Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions src/Sandbox.js
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ class Sandbox {
if (options.policy !== undefined) body.policy = options.policy
if (options.ports !== undefined) body.ports = options.ports

const url = `${creds.apiHost}/api/v1/namespaces/${creds.namespace}/sandbox`
const url = `${creds.apiHost}/api/v1/namespaces/${creds.namespace}/sandboxes`
const payload = await apiRequest('POST', url, creds.apiKey, body)

const sandboxId = payload.sandboxId
Expand Down Expand Up @@ -134,7 +134,7 @@ class Sandbox {
static async get(sandboxId, options = {}) {
console.warn('[aio-lib-sandbox] alpha — APIs may change without notice')
const creds = resolveCredentials(options)
const url = `${creds.apiHost}/api/v1/namespaces/${creds.namespace}/sandbox/${sandboxId}`
const url = `${creds.apiHost}/api/v1/namespaces/${creds.namespace}/sandboxes/${sandboxId}`
const payload = await apiRequest('GET', url, creds.apiKey)

return new Sandbox({
Expand Down Expand Up @@ -493,7 +493,7 @@ class Sandbox {
*/
async destroy() {
const base = this.managementEndpoint || this.apiHost
const url = `${base}/api/v1/namespaces/${this.namespace}/sandbox/${this.id}`
const url = `${base}/api/v1/namespaces/${this.namespace}/sandboxes/${this.id}`
this.ws?.beginIntentionalClose()

let payload
Expand Down
2 changes: 1 addition & 1 deletion src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ function normalizeApiHost (host) {
function buildWebSocketEndpoint (apiHost, namespace, sandboxId) {
const url = new URL(apiHost)
url.protocol = url.protocol === 'http:' ? 'ws:' : 'wss:'
url.pathname = `/ws/v1/namespaces/${namespace}/sandbox/${sandboxId}/exec`
url.pathname = `/api/v1/namespaces/${namespace}/sandboxes/${sandboxId}/exec`
url.search = ''
return url.toString()
}
Expand Down
46 changes: 39 additions & 7 deletions test/Sandbox.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ class FakeWebSocket extends EventEmitter {

const BASE_OPTIONS = {
id: 'sb-test',
endpoint: 'wss://runtime.example.net/ws/v1/namespaces/ns/sandbox/sb-test/exec',
endpoint: 'wss://runtime.example.net/api/v1/namespaces/ns/sandboxes/sb-test/exec',
status: 'ready',
namespace: 'ns',
apiHost: 'https://runtime.example.net',
Expand Down Expand Up @@ -200,7 +200,7 @@ describe('Sandbox', () => {
ok: true,
json: () => Promise.resolve({
sandboxId: 'sb-new',
wsEndpoint: 'wss://runtime.example.net/ws/v1/namespaces/ns/sandbox/sb-new/exec',
wsEndpoint: 'wss://runtime.example.net/api/v1/namespaces/ns/sandboxes/sb-new/exec',
status: 'ready',
token: 'tok-new',
maxLifetime: 3600,
Expand Down Expand Up @@ -231,7 +231,7 @@ describe('Sandbox', () => {
[3000, 'https://sb-new-3000.preview.example.net']
]))
expect(mockFetch).toHaveBeenCalledWith(
'https://runtime.example.net/api/v1/namespaces/ns/sandbox',
'https://runtime.example.net/api/v1/namespaces/ns/sandboxes',
expect.objectContaining({ method: 'POST' })
)
})
Expand All @@ -241,7 +241,7 @@ describe('Sandbox', () => {
ok: true,
json: () => Promise.resolve({
sandboxId: 'sb-pol',
wsEndpoint: 'wss://runtime.example.net/ws/v1/namespaces/ns/sandbox/sb-pol/exec',
wsEndpoint: 'wss://runtime.example.net/api/v1/namespaces/ns/sandboxes/sb-pol/exec',
status: 'ready',
token: 'tok-pol',
maxLifetime: 3600
Expand Down Expand Up @@ -272,7 +272,7 @@ describe('Sandbox', () => {
ok: true,
json: () => Promise.resolve({
sandboxId: 'sb-ports',
wsEndpoint: 'wss://runtime.example.net/ws/v1/namespaces/ns/sandbox/sb-ports/exec',
wsEndpoint: 'wss://runtime.example.net/api/v1/namespaces/ns/sandboxes/sb-ports/exec',
status: 'ready',
token: 'tok-ports',
maxLifetime: 3600,
Expand Down Expand Up @@ -312,7 +312,7 @@ describe('Sandbox', () => {
ok: true,
json: () => Promise.resolve({
sandboxId: 'sb-env',
wsEndpoint: 'wss://runtime.example.net/ws/v1/namespaces/ns/sandbox/sb-env/exec',
wsEndpoint: 'wss://runtime.example.net/api/v1/namespaces/ns/sandboxes/sb-env/exec',
status: 'ready',
token: 'tok-env',
maxLifetime: 3600
Expand Down Expand Up @@ -408,6 +408,38 @@ describe('Sandbox', () => {
Sandbox.get('sb-x', { apiHost: 'https://runtime.example.net', namespace: 'ns', auth: 'bad' })
).rejects.toThrow(SandboxUnauthorizedError)
})

test('throws SandboxTimeoutError on 504', async () => {
global.fetch = jest.fn().mockResolvedValue({
ok: false,
status: 504,
text: () => Promise.resolve('gateway timeout')
})

await expect(
Sandbox.get('sb-slow', { apiHost: 'https://runtime.example.net', namespace: 'ns', auth: 'key' })
).rejects.toThrow(SandboxTimeoutError)
})

test('throws SandboxClientError on unexpected API status', async () => {
global.fetch = jest.fn().mockResolvedValue({
ok: false,
status: 500,
text: () => Promise.resolve('server error')
})

await expect(
Sandbox.get('sb-error', { apiHost: 'https://runtime.example.net', namespace: 'ns', auth: 'key' })
).rejects.toThrow(SandboxClientError)
})

test('wraps fetch failures in SandboxClientError', async () => {
global.fetch = jest.fn().mockRejectedValue(new Error('network unavailable'))

await expect(
Sandbox.get('sb-network', { apiHost: 'https://runtime.example.net', namespace: 'ns', auth: 'key' })
).rejects.toThrow(SandboxClientError)
})
})

// -------------------------------------------------------------------------
Expand Down Expand Up @@ -844,7 +876,7 @@ describe('Sandbox', () => {
expect(result.status).toBe('destroyed')
expect(sandbox.status).toBe('destroyed')
expect(mockFetch).toHaveBeenCalledWith(
expect.stringContaining('/sandbox/sb-test'),
expect.stringContaining('/sandboxes/sb-test'),
expect.objectContaining({ method: 'DELETE' })
)
})
Expand Down
Loading