@adobe/aio-lib-sandbox 0.1.0-alpha.7 → 0.1.0-alpha.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -0
- package/package.json +1 -1
- package/src/Sandbox.js +31 -5
- package/src/constants.js +4 -1
- package/src/errors.js +5 -1
- package/src/index.js +8 -2
- package/src/utils.js +2 -2
- package/src/ws.js +12 -0
- package/test/Sandbox.test.js +142 -2
package/README.md
CHANGED
|
@@ -74,12 +74,19 @@ const { Sandbox } = require('@adobe/aio-lib-sandbox')
|
|
|
74
74
|
const sandbox = await Sandbox.create({
|
|
75
75
|
name: 'my-sandbox',
|
|
76
76
|
type: 'cpu:default',
|
|
77
|
+
idleTimeout: 900,
|
|
77
78
|
maxLifetime: 3600,
|
|
78
79
|
ports: [3000, 8080],
|
|
79
80
|
envs: { API_KEY: 'your-api-key' }
|
|
80
81
|
})
|
|
81
82
|
```
|
|
82
83
|
|
|
84
|
+
#### Sandbox lifetime model
|
|
85
|
+
|
|
86
|
+
A sandbox is always deleted when `maxLifetime` has elapsed. It will also be deleted after the `idleTimeout` has elapsed, if there has been no activity.
|
|
87
|
+
|
|
88
|
+
To keep a sandbox alive, send at least one command or check the status every `idleTimeout` seconds.
|
|
89
|
+
|
|
83
90
|
### Get Status
|
|
84
91
|
|
|
85
92
|
```js
|
package/package.json
CHANGED
package/src/Sandbox.js
CHANGED
|
@@ -23,7 +23,7 @@ const {
|
|
|
23
23
|
normalizeSize,
|
|
24
24
|
apiRequest
|
|
25
25
|
} = require('./utils')
|
|
26
|
-
const { SANDBOX_SIZES } = require('./constants')
|
|
26
|
+
const { SANDBOX_SIZES, PROTOCOL_VERSION, API_PREFIX } = require('./constants')
|
|
27
27
|
const { SandboxSocket } = require('./ws')
|
|
28
28
|
|
|
29
29
|
/**
|
|
@@ -42,7 +42,9 @@ class Sandbox {
|
|
|
42
42
|
this.status = options.status
|
|
43
43
|
this.cluster = options.cluster
|
|
44
44
|
this.region = options.region
|
|
45
|
+
this.idleTimeout = options.idleTimeout
|
|
45
46
|
this.maxLifetime = options.maxLifetime
|
|
47
|
+
this.protocolVersion = options.protocolVersion || PROTOCOL_VERSION
|
|
46
48
|
|
|
47
49
|
this.namespace = options.namespace
|
|
48
50
|
this.apiHost = options.apiHost
|
|
@@ -70,7 +72,10 @@ class Sandbox {
|
|
|
70
72
|
* @param {string} [options.name] sandbox display name
|
|
71
73
|
* @param {string} [options.type] sandbox type (default: `'cpu:default'`)
|
|
72
74
|
* @param {string|object} [options.size] sandbox size tier (name or spec object)
|
|
73
|
-
* @param {number} [options.
|
|
75
|
+
* @param {number} [options.idleTimeout] seconds of inactivity before the sandbox is terminated
|
|
76
|
+
* (default: 900, max: 10800). The idle timer resets on every WebSocket message or status-check
|
|
77
|
+
* request.
|
|
78
|
+
* @param {number} [options.maxLifetime] maximum lifetime in seconds (default: 3600, max: 10800)
|
|
74
79
|
* @param {number[]} [options.ports] TCP ports to expose via preview URLs (default: `[]`)
|
|
75
80
|
* @param {object} [options.envs] environment variables to inject into the sandbox
|
|
76
81
|
* @param {object} [options.policy] network policy (e.g. egress allowlist)
|
|
@@ -84,6 +89,7 @@ class Sandbox {
|
|
|
84
89
|
name: options.name,
|
|
85
90
|
size: normalizeSize(options.size),
|
|
86
91
|
type: options.type || 'cpu:default',
|
|
92
|
+
idleTimeout: options.idleTimeout || 900,
|
|
87
93
|
maxLifetime: options.maxLifetime || 3600
|
|
88
94
|
}
|
|
89
95
|
|
|
@@ -93,7 +99,7 @@ class Sandbox {
|
|
|
93
99
|
if (options.policy !== undefined) body.policy = options.policy
|
|
94
100
|
if (options.ports !== undefined) body.ports = options.ports
|
|
95
101
|
|
|
96
|
-
const url = `${creds.apiHost}/
|
|
102
|
+
const url = `${creds.apiHost}${API_PREFIX}/namespaces/${creds.namespace}/sandboxes`
|
|
97
103
|
const payload = await apiRequest('POST', url, creds.apiKey, body)
|
|
98
104
|
|
|
99
105
|
const sandboxId = payload.sandboxId
|
|
@@ -105,7 +111,9 @@ class Sandbox {
|
|
|
105
111
|
status: payload.status,
|
|
106
112
|
cluster: payload.cluster,
|
|
107
113
|
region: payload.region,
|
|
114
|
+
idleTimeout: payload.idleTimeout,
|
|
108
115
|
maxLifetime: payload.maxLifetime,
|
|
116
|
+
protocolVersion: payload.protocolVersion || PROTOCOL_VERSION,
|
|
109
117
|
previewUrls: parsePreviewUrls(payload.previewUrls),
|
|
110
118
|
managementEndpoint: payload.managementEndpoint || null,
|
|
111
119
|
namespace: creds.namespace,
|
|
@@ -124,17 +132,23 @@ class Sandbox {
|
|
|
124
132
|
* Credentials are read from the environment automatically.
|
|
125
133
|
* Any value passed explicitly in `options` overrides the environment.
|
|
126
134
|
*
|
|
135
|
+
* Pass the management endpoint so the request is sent to the correct host;
|
|
136
|
+
* falls back to `options.apiHost` when omitted.
|
|
137
|
+
*
|
|
127
138
|
* @param {string} sandboxId the sandbox ID to look up
|
|
128
139
|
* @param {object} [options] credential overrides
|
|
129
140
|
* @param {string} [options.apiHost] Runtime API host
|
|
130
141
|
* @param {string} [options.namespace] Runtime namespace
|
|
131
142
|
* @param {string} [options.auth] Runtime API key
|
|
143
|
+
* @param {string} [options.managementEndpoint] per-sandbox management endpoint returned by
|
|
144
|
+
* `Sandbox.create()`. Falls back to `apiHost` otherwise.
|
|
132
145
|
* @returns {Promise<Sandbox>} sandbox instance with `status` populated (not WebSocket-connected)
|
|
133
146
|
*/
|
|
134
147
|
static async get (sandboxId, options = {}) {
|
|
135
148
|
console.warn('[aio-lib-sandbox] alpha — APIs may change without notice')
|
|
136
149
|
const creds = resolveCredentials(options)
|
|
137
|
-
const
|
|
150
|
+
const base = options.managementEndpoint || creds.apiHost
|
|
151
|
+
const url = `${base}${API_PREFIX}/namespaces/${creds.namespace}/sandboxes/${sandboxId}`
|
|
138
152
|
const payload = await apiRequest('GET', url, creds.apiKey)
|
|
139
153
|
|
|
140
154
|
return new Sandbox({
|
|
@@ -143,7 +157,10 @@ class Sandbox {
|
|
|
143
157
|
status: payload.status,
|
|
144
158
|
cluster: payload.cluster,
|
|
145
159
|
region: payload.region,
|
|
160
|
+
idleTimeout: payload.idleTimeout,
|
|
146
161
|
maxLifetime: payload.maxLifetime,
|
|
162
|
+
protocolVersion: payload.protocolVersion || PROTOCOL_VERSION,
|
|
163
|
+
managementEndpoint: payload.managementEndpoint || options.managementEndpoint || null,
|
|
147
164
|
previewUrls: parsePreviewUrls(payload.previewUrls),
|
|
148
165
|
namespace: creds.namespace,
|
|
149
166
|
apiHost: creds.apiHost,
|
|
@@ -161,6 +178,15 @@ class Sandbox {
|
|
|
161
178
|
return SANDBOX_SIZES
|
|
162
179
|
}
|
|
163
180
|
|
|
181
|
+
/**
|
|
182
|
+
* Sandbox wire protocol major bundled with this SDK.
|
|
183
|
+
*
|
|
184
|
+
* @type {string}
|
|
185
|
+
*/
|
|
186
|
+
static get protocolVersion () {
|
|
187
|
+
return PROTOCOL_VERSION
|
|
188
|
+
}
|
|
189
|
+
|
|
164
190
|
/**
|
|
165
191
|
* Exposes `resolveCredentials` as a static helper (useful for testing).
|
|
166
192
|
*
|
|
@@ -493,7 +519,7 @@ class Sandbox {
|
|
|
493
519
|
*/
|
|
494
520
|
async destroy () {
|
|
495
521
|
const base = this.managementEndpoint || this.apiHost
|
|
496
|
-
const url = `${base}/
|
|
522
|
+
const url = `${base}${API_PREFIX}/namespaces/${this.namespace}/sandboxes/${this.id}`
|
|
497
523
|
this.ws?.beginIntentionalClose()
|
|
498
524
|
|
|
499
525
|
let payload
|
package/src/constants.js
CHANGED
|
@@ -16,4 +16,7 @@ const SANDBOX_SIZES = Object.freeze({
|
|
|
16
16
|
XLARGE: { cpu: '8000m', memory: '32Gi', gpu: 1 }
|
|
17
17
|
})
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
const PROTOCOL_VERSION = '1'
|
|
20
|
+
const API_PREFIX = `/api/v${PROTOCOL_VERSION}`
|
|
21
|
+
|
|
22
|
+
module.exports = { SANDBOX_SIZES, PROTOCOL_VERSION, API_PREFIX }
|
package/src/errors.js
CHANGED
|
@@ -28,6 +28,8 @@ class SandboxWebSocketError extends SandboxSDKError {}
|
|
|
28
28
|
class SandboxCommandNotFoundError extends SandboxSDKError {}
|
|
29
29
|
class SandboxPortNotProvisionedError extends SandboxSDKError {}
|
|
30
30
|
class SandboxInvalidPortError extends SandboxClientError {}
|
|
31
|
+
class ProtocolVersionMismatchError extends SandboxClientError {}
|
|
32
|
+
class SandboxMalformedFrameError extends SandboxClientError {}
|
|
31
33
|
|
|
32
34
|
module.exports = {
|
|
33
35
|
SandboxSDKError,
|
|
@@ -39,5 +41,7 @@ module.exports = {
|
|
|
39
41
|
SandboxWebSocketError,
|
|
40
42
|
SandboxCommandNotFoundError,
|
|
41
43
|
SandboxPortNotProvisionedError,
|
|
42
|
-
SandboxInvalidPortError
|
|
44
|
+
SandboxInvalidPortError,
|
|
45
|
+
ProtocolVersionMismatchError,
|
|
46
|
+
SandboxMalformedFrameError
|
|
43
47
|
}
|
package/src/index.js
CHANGED
|
@@ -10,6 +10,7 @@ governing permissions and limitations under the License.
|
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
const Sandbox = require('./Sandbox')
|
|
13
|
+
const { PROTOCOL_VERSION } = require('./constants')
|
|
13
14
|
const {
|
|
14
15
|
SandboxSDKError,
|
|
15
16
|
SandboxInitializationError,
|
|
@@ -20,11 +21,14 @@ const {
|
|
|
20
21
|
SandboxWebSocketError,
|
|
21
22
|
SandboxCommandNotFoundError,
|
|
22
23
|
SandboxPortNotProvisionedError,
|
|
23
|
-
SandboxInvalidPortError
|
|
24
|
+
SandboxInvalidPortError,
|
|
25
|
+
ProtocolVersionMismatchError,
|
|
26
|
+
SandboxMalformedFrameError
|
|
24
27
|
} = require('./errors')
|
|
25
28
|
|
|
26
29
|
module.exports = {
|
|
27
30
|
Sandbox,
|
|
31
|
+
SANDBOX_PROTOCOL_VERSION: PROTOCOL_VERSION,
|
|
28
32
|
SandboxSDKError,
|
|
29
33
|
SandboxInitializationError,
|
|
30
34
|
SandboxClientError,
|
|
@@ -34,5 +38,7 @@ module.exports = {
|
|
|
34
38
|
SandboxWebSocketError,
|
|
35
39
|
SandboxCommandNotFoundError,
|
|
36
40
|
SandboxPortNotProvisionedError,
|
|
37
|
-
SandboxInvalidPortError
|
|
41
|
+
SandboxInvalidPortError,
|
|
42
|
+
ProtocolVersionMismatchError,
|
|
43
|
+
SandboxMalformedFrameError
|
|
38
44
|
}
|
package/src/utils.js
CHANGED
|
@@ -16,7 +16,7 @@ const {
|
|
|
16
16
|
SandboxUnauthorizedError,
|
|
17
17
|
SandboxTimeoutError
|
|
18
18
|
} = require('./errors')
|
|
19
|
-
const { SANDBOX_SIZES } = require('./constants')
|
|
19
|
+
const { SANDBOX_SIZES, API_PREFIX } = require('./constants')
|
|
20
20
|
|
|
21
21
|
/**
|
|
22
22
|
* Builds a Basic authorization header from a Runtime API key.
|
|
@@ -72,7 +72,7 @@ function normalizeApiHost (host) {
|
|
|
72
72
|
function buildWebSocketEndpoint (apiHost, namespace, sandboxId) {
|
|
73
73
|
const url = new URL(apiHost)
|
|
74
74
|
url.protocol = url.protocol === 'http:' ? 'ws:' : 'wss:'
|
|
75
|
-
url.pathname =
|
|
75
|
+
url.pathname = `${API_PREFIX}/namespaces/${namespace}/sandboxes/${sandboxId}/exec`
|
|
76
76
|
url.search = ''
|
|
77
77
|
return url.toString()
|
|
78
78
|
}
|
package/src/ws.js
CHANGED
|
@@ -13,6 +13,8 @@ const WebSocket = require('ws')
|
|
|
13
13
|
const {
|
|
14
14
|
SandboxClientError,
|
|
15
15
|
SandboxCommandNotFoundError,
|
|
16
|
+
ProtocolVersionMismatchError,
|
|
17
|
+
SandboxMalformedFrameError,
|
|
16
18
|
SandboxUnauthorizedError,
|
|
17
19
|
SandboxWebSocketError
|
|
18
20
|
} = require('./errors')
|
|
@@ -355,6 +357,16 @@ class SandboxSocket {
|
|
|
355
357
|
`Sandbox '${this.id}' rejected the WebSocket authentication token`
|
|
356
358
|
)
|
|
357
359
|
}
|
|
360
|
+
if (code === 4003) {
|
|
361
|
+
return new ProtocolVersionMismatchError(
|
|
362
|
+
`Sandbox '${this.id}' WebSocket protocol version does not match this SDK`
|
|
363
|
+
)
|
|
364
|
+
}
|
|
365
|
+
if (code === 4004) {
|
|
366
|
+
return new SandboxMalformedFrameError(
|
|
367
|
+
`Sandbox '${this.id}' rejected a malformed WebSocket frame`
|
|
368
|
+
)
|
|
369
|
+
}
|
|
358
370
|
return new SandboxWebSocketError(
|
|
359
371
|
`Sandbox '${this.id}' WebSocket closed with code ${code}`
|
|
360
372
|
)
|
package/test/Sandbox.test.js
CHANGED
|
@@ -17,11 +17,13 @@ const {
|
|
|
17
17
|
SandboxCommandNotFoundError,
|
|
18
18
|
SandboxInitializationError,
|
|
19
19
|
SandboxNotFoundError,
|
|
20
|
+
ProtocolVersionMismatchError,
|
|
20
21
|
SandboxPortNotProvisionedError,
|
|
21
22
|
SandboxInvalidPortError,
|
|
22
23
|
SandboxTimeoutError,
|
|
23
24
|
SandboxUnauthorizedError,
|
|
24
|
-
SandboxWebSocketError
|
|
25
|
+
SandboxWebSocketError,
|
|
26
|
+
SandboxMalformedFrameError
|
|
25
27
|
} = require('../src/errors')
|
|
26
28
|
|
|
27
29
|
jest.mock('ws')
|
|
@@ -199,6 +201,12 @@ describe('Sandbox', () => {
|
|
|
199
201
|
})
|
|
200
202
|
})
|
|
201
203
|
|
|
204
|
+
describe('protocolVersion', () => {
|
|
205
|
+
test('exposes the bundled sandbox protocol major', () => {
|
|
206
|
+
expect(Sandbox.protocolVersion).toBe('1')
|
|
207
|
+
})
|
|
208
|
+
})
|
|
209
|
+
|
|
202
210
|
// -------------------------------------------------------------------------
|
|
203
211
|
// Static factories
|
|
204
212
|
// -------------------------------------------------------------------------
|
|
@@ -213,6 +221,7 @@ describe('Sandbox', () => {
|
|
|
213
221
|
status: 'ready',
|
|
214
222
|
token: 'tok-new',
|
|
215
223
|
maxLifetime: 3600,
|
|
224
|
+
protocolVersion: '1',
|
|
216
225
|
previewUrls: {
|
|
217
226
|
3000: 'https://sb-new-3000.preview.example.net'
|
|
218
227
|
}
|
|
@@ -236,6 +245,7 @@ describe('Sandbox', () => {
|
|
|
236
245
|
|
|
237
246
|
expect(sandbox.id).toBe('sb-new')
|
|
238
247
|
expect(sandbox.status).toBe('ready')
|
|
248
|
+
expect(sandbox.protocolVersion).toBe('1')
|
|
239
249
|
expect(sandbox.previewUrls).toEqual(new Map([
|
|
240
250
|
[3000, 'https://sb-new-3000.preview.example.net']
|
|
241
251
|
]))
|
|
@@ -342,6 +352,98 @@ describe('Sandbox', () => {
|
|
|
342
352
|
await expect(Sandbox.create({ name: 'no-creds' })).rejects.toThrow(SandboxInitializationError)
|
|
343
353
|
})
|
|
344
354
|
|
|
355
|
+
test('sends default idleTimeout 900 and maxLifetime 3600 when not specified', async () => {
|
|
356
|
+
const mockFetch = jest.fn().mockResolvedValue({
|
|
357
|
+
ok: true,
|
|
358
|
+
json: () => Promise.resolve({
|
|
359
|
+
sandboxId: 'sb-defaults',
|
|
360
|
+
wsEndpoint: 'wss://runtime.example.net/api/v1/namespaces/ns/sandboxes/sb-defaults/exec',
|
|
361
|
+
status: 'ready',
|
|
362
|
+
token: 'tok-defaults',
|
|
363
|
+
idleTimeout: 900,
|
|
364
|
+
maxLifetime: 3600
|
|
365
|
+
})
|
|
366
|
+
})
|
|
367
|
+
global.fetch = mockFetch
|
|
368
|
+
|
|
369
|
+
const createPromise = Sandbox.create({
|
|
370
|
+
name: 'defaults-sandbox',
|
|
371
|
+
apiHost: 'https://runtime.example.net',
|
|
372
|
+
namespace: 'ns',
|
|
373
|
+
auth: 'uuid:key'
|
|
374
|
+
})
|
|
375
|
+
|
|
376
|
+
await new Promise(resolve => setImmediate(resolve))
|
|
377
|
+
sockets[0].open()
|
|
378
|
+
sockets[0].message({ type: 'auth.ok', sandboxId: 'sb-defaults' })
|
|
379
|
+
await createPromise
|
|
380
|
+
|
|
381
|
+
const body = JSON.parse(mockFetch.mock.calls[0][1].body)
|
|
382
|
+
expect(body.idleTimeout).toBe(900)
|
|
383
|
+
expect(body.maxLifetime).toBe(3600)
|
|
384
|
+
})
|
|
385
|
+
|
|
386
|
+
test('forwards explicit idleTimeout in the request body', async () => {
|
|
387
|
+
const mockFetch = jest.fn().mockResolvedValue({
|
|
388
|
+
ok: true,
|
|
389
|
+
json: () => Promise.resolve({
|
|
390
|
+
sandboxId: 'sb-idle',
|
|
391
|
+
wsEndpoint: 'wss://runtime.example.net/api/v1/namespaces/ns/sandboxes/sb-idle/exec',
|
|
392
|
+
status: 'ready',
|
|
393
|
+
token: 'tok-idle',
|
|
394
|
+
idleTimeout: 1800,
|
|
395
|
+
maxLifetime: 3600
|
|
396
|
+
})
|
|
397
|
+
})
|
|
398
|
+
global.fetch = mockFetch
|
|
399
|
+
|
|
400
|
+
const createPromise = Sandbox.create({
|
|
401
|
+
name: 'idle-sandbox',
|
|
402
|
+
apiHost: 'https://runtime.example.net',
|
|
403
|
+
namespace: 'ns',
|
|
404
|
+
auth: 'uuid:key',
|
|
405
|
+
idleTimeout: 1800
|
|
406
|
+
})
|
|
407
|
+
|
|
408
|
+
await new Promise(resolve => setImmediate(resolve))
|
|
409
|
+
sockets[0].open()
|
|
410
|
+
sockets[0].message({ type: 'auth.ok', sandboxId: 'sb-idle' })
|
|
411
|
+
await createPromise
|
|
412
|
+
|
|
413
|
+
const body = JSON.parse(mockFetch.mock.calls[0][1].body)
|
|
414
|
+
expect(body.idleTimeout).toBe(1800)
|
|
415
|
+
})
|
|
416
|
+
|
|
417
|
+
test('stores idleTimeout from the create response on the instance', async () => {
|
|
418
|
+
const mockFetch = jest.fn().mockResolvedValue({
|
|
419
|
+
ok: true,
|
|
420
|
+
json: () => Promise.resolve({
|
|
421
|
+
sandboxId: 'sb-store',
|
|
422
|
+
wsEndpoint: 'wss://runtime.example.net/api/v1/namespaces/ns/sandboxes/sb-store/exec',
|
|
423
|
+
status: 'ready',
|
|
424
|
+
token: 'tok-store',
|
|
425
|
+
idleTimeout: 1800,
|
|
426
|
+
maxLifetime: 3600
|
|
427
|
+
})
|
|
428
|
+
})
|
|
429
|
+
global.fetch = mockFetch
|
|
430
|
+
|
|
431
|
+
const createPromise = Sandbox.create({
|
|
432
|
+
name: 'store-sandbox',
|
|
433
|
+
apiHost: 'https://runtime.example.net',
|
|
434
|
+
namespace: 'ns',
|
|
435
|
+
auth: 'uuid:key',
|
|
436
|
+
idleTimeout: 1800
|
|
437
|
+
})
|
|
438
|
+
|
|
439
|
+
await new Promise(resolve => setImmediate(resolve))
|
|
440
|
+
sockets[0].open()
|
|
441
|
+
sockets[0].message({ type: 'auth.ok', sandboxId: 'sb-store' })
|
|
442
|
+
const sandbox = await createPromise
|
|
443
|
+
|
|
444
|
+
expect(sandbox.idleTimeout).toBe(1800)
|
|
445
|
+
})
|
|
446
|
+
|
|
345
447
|
test('falls back to buildWebSocketEndpoint when wsEndpoint absent', async () => {
|
|
346
448
|
const mockFetch = jest.fn().mockResolvedValue({
|
|
347
449
|
ok: true,
|
|
@@ -379,7 +481,8 @@ describe('Sandbox', () => {
|
|
|
379
481
|
sandboxId: 'sb-get',
|
|
380
482
|
status: 'running',
|
|
381
483
|
cluster: 'cluster-b',
|
|
382
|
-
region: 'va6'
|
|
484
|
+
region: 'va6',
|
|
485
|
+
protocolVersion: '1'
|
|
383
486
|
})
|
|
384
487
|
})
|
|
385
488
|
|
|
@@ -392,6 +495,27 @@ describe('Sandbox', () => {
|
|
|
392
495
|
expect(sandbox.id).toBe('sb-get')
|
|
393
496
|
expect(sandbox.status).toBe('running')
|
|
394
497
|
expect(sandbox.cluster).toBe('cluster-b')
|
|
498
|
+
expect(sandbox.protocolVersion).toBe('1')
|
|
499
|
+
})
|
|
500
|
+
|
|
501
|
+
test('stores idleTimeout from the get response on the instance', async () => {
|
|
502
|
+
global.fetch = jest.fn().mockResolvedValue({
|
|
503
|
+
ok: true,
|
|
504
|
+
json: () => Promise.resolve({
|
|
505
|
+
sandboxId: 'sb-get-idle',
|
|
506
|
+
status: 'running',
|
|
507
|
+
idleTimeout: 1200,
|
|
508
|
+
maxLifetime: 3600
|
|
509
|
+
})
|
|
510
|
+
})
|
|
511
|
+
|
|
512
|
+
const sandbox = await Sandbox.get('sb-get-idle', {
|
|
513
|
+
apiHost: 'https://runtime.example.net',
|
|
514
|
+
namespace: 'ns',
|
|
515
|
+
auth: 'uuid:key'
|
|
516
|
+
})
|
|
517
|
+
|
|
518
|
+
expect(sandbox.idleTimeout).toBe(1200)
|
|
395
519
|
})
|
|
396
520
|
|
|
397
521
|
test('throws SandboxNotFoundError on 404', async () => {
|
|
@@ -490,6 +614,22 @@ describe('Sandbox', () => {
|
|
|
490
614
|
await expect(p).rejects.toThrow(SandboxUnauthorizedError)
|
|
491
615
|
})
|
|
492
616
|
|
|
617
|
+
test('rejects on protocol mismatch close code 4003 with ProtocolVersionMismatchError', async () => {
|
|
618
|
+
const sandbox = new Sandbox(BASE_OPTIONS)
|
|
619
|
+
const p = sandbox.connect()
|
|
620
|
+
sockets[0].open()
|
|
621
|
+
sockets[0].closeWith(4003)
|
|
622
|
+
await expect(p).rejects.toThrow(ProtocolVersionMismatchError)
|
|
623
|
+
})
|
|
624
|
+
|
|
625
|
+
test('rejects on malformed frame close code 4004 with SandboxMalformedFrameError', async () => {
|
|
626
|
+
const sandbox = new Sandbox(BASE_OPTIONS)
|
|
627
|
+
const p = sandbox.connect()
|
|
628
|
+
sockets[0].open()
|
|
629
|
+
sockets[0].closeWith(4004)
|
|
630
|
+
await expect(p).rejects.toThrow(SandboxMalformedFrameError)
|
|
631
|
+
})
|
|
632
|
+
|
|
493
633
|
test('rejects on unexpected socket close', async () => {
|
|
494
634
|
const sandbox = new Sandbox(BASE_OPTIONS)
|
|
495
635
|
const p = sandbox.connect()
|