Files
electric-cherry/tests/unit/provisioning-response.test.ts

97 lines
3.2 KiB
TypeScript

import { afterEach, beforeEach, expect, it, vi } from 'vitest';
import {
queueCreateServer,
destroyServer,
requestLifecycle
} from '../../src/lib/server/provisioning/index.js';
import { kick, kickProvisioning } from '../../src/lib/server/pipeline/worker.js';
import { POST as create } from '../../src/routes/api/servers/+server.js';
import { DELETE as destroy } from '../../src/routes/api/servers/[name]/+server.js';
import { POST as lifecycle } from '../../src/routes/api/provisioning/+server.js';
vi.mock('../../src/lib/server/provisioning/index.js', () => ({
queueCreateServer: vi.fn(),
destroyServer: vi.fn(),
requestLifecycle: vi.fn()
}));
vi.mock('../../src/lib/server/pipeline/worker.js', () => ({
kick: vi.fn(async () => {}),
kickProvisioning: vi.fn(async () => {})
}));
vi.mock('../../src/lib/server/fleet/snapshot.js', () => ({
fleetSnapshot: vi.fn(),
serverSnapshot: vi.fn()
}));
vi.mock('../../src/lib/server/provisioning/history.js', () => ({ deploymentListPage: vi.fn() }));
beforeEach(() => {
vi.clearAllMocks();
vi.useFakeTimers({ toFake: ['setImmediate'] });
});
afterEach(() => vi.useRealTimers());
const request = (body: unknown) =>
new Request('http://localhost/api/servers', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body)
});
const routes = [
{
name: 'create',
queue: queueCreateServer,
invoke: () =>
create({ request: request({ game: 't5', gameMode: 'zombie', mapSlug: 'theater' }) })
},
{
name: 'destroy',
queue: destroyServer,
invoke: () => destroy({ params: { name: 'server' } })
},
{
name: 'restart',
queue: requestLifecycle,
invoke: () => lifecycle({ request: request({ server: 'server', action: 'restart' }) })
}
];
it.each(routes)(
'returns the queued $name job before starting provisioning or unrelated observers',
async ({ queue, invoke }) => {
vi.mocked(queue).mockReturnValue({
ok: true,
name: 'server',
port: 28960,
deploymentId: 'job',
warnings: []
});
const response = await invoke();
expect(response.status).toBe(202);
expect(await response.json()).toMatchObject({ ok: true, deploymentId: 'job' });
expect(queue).toHaveBeenCalledTimes(1);
expect(kickProvisioning).not.toHaveBeenCalled();
expect(kick).not.toHaveBeenCalled();
await vi.runAllTimersAsync();
expect(kickProvisioning).toHaveBeenCalledTimes(1);
expect(kick).not.toHaveBeenCalled();
}
);
it.each(routes)('does not wake workers when $name admission fails', async ({ queue, invoke }) => {
vi.mocked(queue).mockReturnValue({
ok: false,
kind: 'conflict',
message: 'Busy',
error: 'Busy',
status: 409,
warnings: []
});
const response = await invoke();
expect(response.status).toBe(409);
expect(await response.json()).toMatchObject({ error: 'Busy' });
await vi.runAllTimersAsync();
expect(kickProvisioning).not.toHaveBeenCalled();
expect(kick).not.toHaveBeenCalled();
});