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
10 changes: 9 additions & 1 deletion __tests__/database-query.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,14 @@ test.each([undefined, 'application/vnd.volcano+json'])(
},
);
const generatedFetch = jest
.fn<(path: string, options: RequestInit, mode: 'anon' | 'session') => Promise<Response>>()
.fn<
(
path: string,
options: RequestInit,
mode: 'anon' | 'session',
timeoutMs?: number,
) => Promise<Response>
>()
.mockResolvedValue(fetchResponse);
const client = { _generatedFetch: generatedFetch };
const date = new Date('2026-09-23T12:00:00.000Z');
Expand Down Expand Up @@ -65,6 +72,7 @@ test.each([undefined, 'application/vnd.volcano+json'])(
headers: expect.any(Headers),
}),
'session',
undefined,
);
const call = generatedFetch.mock.calls[0];
expect(new Headers(call?.[1].headers).get('Content-Type')).toBe(
Expand Down
121 changes: 121 additions & 0 deletions __tests__/sandbox-review.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
/** @jest-environment ./__tests__/node-environment.cjs */
import { inspect } from 'node:util';
import { afterEach, expect, jest, test } from '@jest/globals';
import { VolcanoAuth } from '../src/index.ts';

const id = '11111111-1111-4111-8111-111111111111';
const snapshot = { id, project_id: id, region: 'us-east-1', state: 'running', expires_at: '' };
const command = {
stdout: '',
stderr: '',
exit_code: 0,
timed_out: false,
stdout_truncated: false,
stderr_truncated: false,
session_id: id,
region: 'us-east-1',
duration_ms: 0,
};
const originalFetch = globalThis.fetch;
afterEach(() => {
globalThis.fetch = originalFetch;
jest.restoreAllMocks();
});

test.each([
[60_000, undefined, 180_000],
[60_000, 600, 720_000],
[900_000, 600, 900_000],
])(
'command requests budget provisioning time: %s/%s',
async (timeout, timeoutSeconds, expected) => {
const client = new VolcanoAuth({
apiUrl: 'https://api.test.com',
anonKey: 'anon',
accessToken: 'service',
timeout,
});
const fetchMock = jest.fn<typeof fetch>().mockResolvedValueOnce(Response.json(snapshot));
globalThis.fetch = fetchMock;
const session = await client.sandboxes.get(id);
if (session.data === null) {
throw session.error;
}
const timer = jest.spyOn(globalThis, 'setTimeout');
const options = timeoutSeconds === undefined ? {} : { timeoutSeconds };
fetchMock.mockImplementation(() => Promise.resolve(Response.json(command)));
const result = await session.data.exec('true', options);
expect(result.error).toBeNull();
expect(timer).toHaveBeenLastCalledWith(expect.any(Function), expected);
const executed = await client.sandboxes.exec(id, 'true', {
...options,
preset: 'node22',
region: 'us-east-1',
});
expect(executed.error).toBeNull();
expect(timer).toHaveBeenLastCalledWith(expect.any(Function), expected);
},
);

test.each(['terminating', 'terminated'])('disposal skips %s sessions', async (state) => {
const client = new VolcanoAuth({ anonKey: 'anon', accessToken: 'service' });
const fetchMock = jest
.fn<typeof fetch>()
.mockResolvedValueOnce(Response.json(snapshot))
.mockResolvedValueOnce(Response.json({ ...snapshot, state }));
globalThis.fetch = fetchMock;
const session = await client.sandboxes.get(id);
if (session.data === null) {
throw session.error;
}
const terminated = await session.data.terminate();
expect(terminated.error).toBeNull();
await session.data[Symbol.asyncDispose]();
expect(fetchMock).toHaveBeenCalledTimes(2);
});

test('disposal accepts an already removed session', async () => {
const client = new VolcanoAuth({ anonKey: 'anon', accessToken: 'service' });
globalThis.fetch = jest
.fn<typeof fetch>()
.mockResolvedValueOnce(Response.json(snapshot))
.mockResolvedValueOnce(Response.json({ error: 'not found' }, { status: 404 }));
const session = await client.sandboxes.get(id);
if (session.data === null) {
throw session.error;
}
await expect(session.data[Symbol.asyncDispose]()).resolves.toBeUndefined();
});

test('scoped access credentials remain usable but are absent from routine logs', async () => {
const client = new VolcanoAuth({ anonKey: 'anon', accessToken: 'service' });
globalThis.fetch = jest
.fn<typeof fetch>()
.mockResolvedValueOnce(Response.json(snapshot))
.mockResolvedValueOnce(
Response.json({ url: 'https://session.test', token: 'private-access-token', expires_at: '' }),
);
const session = await client.sandboxes.get(id);
if (session.data === null) {
throw session.error;
}
const access = await session.data.access(8080);
expect(access.data?.token).toBe('private-access-token');
expect(inspect(access)).not.toContain('private-access-token');
expect(JSON.stringify(access)).not.toContain('private-access-token');
expect({ ...access.data }).not.toHaveProperty('token');
});

test('disposal preserves network errors', async () => {
const client = new VolcanoAuth({ anonKey: 'anon', accessToken: 'service' });
const failure = new Error('connection failed');
globalThis.fetch = jest
.fn<typeof fetch>()
.mockResolvedValueOnce(Response.json(snapshot))
.mockRejectedValueOnce(failure);
const session = await client.sandboxes.get(id);
if (session.data === null) {
throw session.error;
}
await expect(session.data[Symbol.asyncDispose]()).rejects.toBe(failure);
});
160 changes: 160 additions & 0 deletions __tests__/sandbox-wire.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
/** @jest-environment ./__tests__/node-environment.cjs */
import { describe, expect, jest, test } from '@jest/globals';
import { commandRequest, pathId, requestOptions } from '../src/sandbox-request.ts';
import {
commandResult,
createRequest,
encodeBytes,
presetsResult,
record,
sessionRequest,
sessionState,
text,
} from '../src/sandbox-wire.ts';

const command = {
stdout: '',
stderr: '',
exit_code: 0,
timed_out: false,
stdout_truncated: false,
stderr_truncated: false,
};

describe('Sandbox wire validation', () => {
test.each([null, undefined, 0, 'text', [], true])('rejects non-record %j', (value) => {
expect(() => record(value)).toThrow('Invalid Sandbox response');
});
test('accepts records and text without coercion', () => {
const value = { field: 'value' };
expect(record(value)).toBe(value);
expect(text('')).toBe('');
expect(() => text(1)).toThrow('Invalid Sandbox text field');
});
test.each([Number.NaN, Number.POSITIVE_INFINITY, '0'])(
'rejects invalid numeric fields %j',
(exit_code) => {
expect(() => commandResult({ ...command, exit_code })).toThrow(
'Invalid Sandbox numeric field',
);
},
);
test('rejects non-boolean flags', () => {
expect(() => commandResult({ ...command, timed_out: 'false' })).toThrow('Invalid Sandbox flag');
});
test.each([
'starting',
'running',
'suspending',
'suspended',
'resuming',
'terminating',
'terminated',
'unknown',
])('preserves session state %s', (state) => {
expect(sessionState(state)).toBe(state);
});
test('rejects unknown session state', () => {
expect(() => sessionState('future')).toThrow('Invalid Sandbox state');
});
test.each([
{ region: 'aws-us-east-1' },
{ region: 'aws-us-east-1', preset: 'node22' as const, sandboxId: 'template' },
])('requires exactly one selector %j', (options) => {
expect(() => createRequest(options)).toThrow('Choose exactly one preset or sandboxId');
});
test.each([{}, { data: {} }, { data: [{ id: 'node22', memory_mb: 1024, regions: {} }] }])(
'rejects malformed catalog %j',
(value) => {
const message =
'data' in value && Array.isArray(value.data)
? 'Invalid Sandbox regions'
: 'Invalid Sandbox preset catalog';
expect(() => presetsResult(value)).toThrow(message);
},
);
test('accepts the file limit and rejects the next byte', () => {
const limit = 8 * 1024 * 1024;
expect(encodeBytes(new Uint8Array(limit))).toHaveLength(Math.ceil(limit / 3) * 4);
expect(() => encodeBytes(new Uint8Array(limit + 1))).toThrow(
'Sandbox files are limited to 8 MiB',
);
});
});

test.each([
'prefix11111111-1111-4111-8111-111111111111',
'11111111-1111-4111-8111-111111111111suffix',
])('rejects a UUID embedded in %s', (id) => {
expect(() => pathId(id)).toThrow('A Sandbox resource ID must be a UUID');
});

test('forwards the caller abort signal and leaves absent signals untouched', () => {
const generated = jest
.fn<
(
mode: 'session',
headers?: Record<string, string>,
responseType?: 'blob',
timeoutMs?: number,
) => object
>()
.mockImplementation(() => ({}));
const client = {
timeout: 60_000,
_completeOAuthExchange: () => Promise.resolve(),
_generatedOptions: generated,
};
const signal = new AbortController().signal;
expect(requestOptions(client, { signal }).signal).toBe(signal);
expect(requestOptions(client)).not.toHaveProperty('signal');
});

test('omits absent optional wire properties', () => {
expect(createRequest({ preset: 'node22', region: 'aws-us-east-1' })).toStrictEqual({
preset: 'node22',
region: 'aws-us-east-1',
});
expect(
createRequest({ sandboxId: 'template', region: 'aws-us-east-1', memoryMB: 2048 }),
).toStrictEqual({ sandbox_id: 'template', region: 'aws-us-east-1', memory_mb: 2048 });
expect(commandRequest('true', {})).toStrictEqual({ command: 'true' });
expect(commandRequest('true', { timeoutSeconds: 5, environment: { A: 'b' } })).toStrictEqual({
command: 'true',
timeout_seconds: 5,
environment: { A: 'b' },
});
expect(sessionRequest({ preset: 'node22', region: 'aws-us-east-1' })).toStrictEqual({
preset: 'node22',
region: 'aws-us-east-1',
});
expect(
sessionRequest({
preset: 'node22',
region: 'aws-us-east-1',
maxDurationSeconds: 300,
idleTimeoutSeconds: 60,
}),
).toStrictEqual({
preset: 'node22',
region: 'aws-us-east-1',
max_duration_seconds: 300,
idle_timeout_seconds: 60,
});
});

test.each([0, 1, 1023, 1025, 2047, 2049, Number.NaN])(
'rejects unsupported memory %s before transport',
(memoryMB) => {
expect(() => createRequest({ preset: 'node22', region: 'us-east-1', memoryMB })).toThrow(
'Sandbox memory must be 1024 or 2048 MB',
);
},
);
test.each([1024, 2048])('accepts catalog memory %s', (memoryMB) => {
expect(createRequest({ preset: 'catalog-preset', region: 'us-east-1', memoryMB })).toEqual({
preset: 'catalog-preset',
region: 'us-east-1',
memory_mb: memoryMB,
});
});
Loading
Loading