openreader/packages/compute-worker/tests/api/routes.test.ts
Richard R 358a56cc9a refactor: implement modular monorepo packages
- Extract database schema and migrations into @openreader/database

- Extract startup and orchestration scripts into @openreader/bootstrap

- Move compute-worker into packages/compute-worker

- Remove runtime dependency on drizzle-kit

- Update Dockerfile to deploy isolated packages without merging node_modules
2026-06-13 13:51:18 -06:00

298 lines
9.1 KiB
TypeScript

import { afterEach, beforeEach, describe, expect, test } from 'vitest';
import { createComputeWorkerApp } from '../../src/api/app';
import { FakeControlPlane } from '../fixtures/fake-control-plane';
const AUTH = { authorization: 'Bearer test-token' };
describe('compute worker API routes', () => {
let fake: FakeControlPlane;
let runtime: Awaited<ReturnType<typeof createComputeWorkerApp>>;
beforeEach(async () => {
fake = new FakeControlPlane();
runtime = await createComputeWorkerApp({
workerToken: 'test-token',
disableWorkers: true,
routeDeps: fake.deps,
});
});
afterEach(async () => {
await runtime.close();
});
test('allows unauthenticated health checks but protects operation routes', async () => {
const live = await runtime.app.inject({ method: 'GET', url: '/health/live' });
expect(live.statusCode).toBe(200);
const protectedRoute = await runtime.app.inject({ method: 'GET', url: '/v1/operations/op-1' });
expect(protectedRoute.statusCode).toBe(401);
});
test('validates operation creation body and returns 400 for invalid payload', async () => {
const response = await runtime.app.inject({
method: 'POST',
url: '/v1/pdf-layout/operations',
headers: AUTH,
payload: {
documentId: '',
namespace: null,
documentObjectKey: 'openreader/doc.pdf',
},
});
expect(response.statusCode).toBe(400);
expect(response.json()).toMatchObject({ error: 'Bad Request' });
});
test('creates operation and fetches state by op id', async () => {
const documentId = 'c'.repeat(64);
const create = await runtime.app.inject({
method: 'POST',
url: '/v1/pdf-layout/operations',
headers: AUTH,
payload: {
documentId,
namespace: null,
documentObjectKey: `openreader/${documentId}.pdf`,
},
});
expect(create.statusCode).toBe(202);
const created = create.json();
expect(created).toMatchObject({
subject: { kind: 'pdf_layout', documentId, namespace: null },
status: 'queued',
});
expect(created).not.toHaveProperty('opKey');
expect(created).not.toHaveProperty('jobId');
const fetch = await runtime.app.inject({
method: 'GET',
url: `/v1/operations/${created.opId}`,
headers: AUTH,
});
expect(fetch.statusCode).toBe(200);
expect(fetch.json()).toMatchObject({ opId: created.opId, status: 'queued' });
});
test('reuses idempotent PDF requests and replaces them only with an explicit token', async () => {
const documentId = 'e'.repeat(64);
const payload = {
documentId,
namespace: null,
documentObjectKey: `openreader/${documentId}.pdf`,
};
const create = (body: typeof payload & { replaceToken?: string }) => runtime.app.inject({
method: 'POST',
url: '/v1/pdf-layout/operations',
headers: AUTH,
payload: body,
});
const initial = (await create(payload)).json();
const reused = (await create(payload)).json();
const replacement = (await create({ ...payload, replaceToken: 'replace-1' })).json();
expect(reused.opId).toBe(initial.opId);
expect(replacement.opId).not.toBe(initial.opId);
expect(replacement.subject).toEqual(initial.subject);
});
test('creates whisper alignment operations without exposing internal keys', async () => {
const response = await runtime.app.inject({
method: 'POST',
url: '/v1/whisper-align/operations',
headers: AUTH,
payload: {
text: 'Canonical worker text',
audioObjectKey: 'openreader/audio.mp3',
lang: 'en',
},
});
expect(response.statusCode).toBe(202);
expect(response.json()).toMatchObject({
subject: { kind: 'whisper_align' },
status: 'queued',
});
expect(response.json()).not.toHaveProperty('opKey');
});
test('resolves the current PDF artifact and operation without exposing parser identity', async () => {
const documentId = 'a'.repeat(64);
const create = await runtime.app.inject({
method: 'POST',
url: '/v1/pdf-layout/operations',
headers: AUTH,
payload: {
documentId,
namespace: null,
documentObjectKey: `openreader/${documentId}.pdf`,
},
});
expect(create.statusCode).toBe(202);
const artifactKey = `openreader/documents_v1/parsed_v2/${documentId}/pp-doclayoutv3-onnx%40800%2Bpdfjs%404.8.69.json`;
fake.seedArtifact(artifactKey);
const resolve = await runtime.app.inject({
method: 'POST',
url: '/v1/pdf-layout/resolve',
headers: AUTH,
payload: {
documentId,
namespace: null,
documentObjectKey: `openreader/${documentId}.pdf`,
},
});
expect(resolve.statusCode).toBe(200);
expect(resolve.json()).toMatchObject({
artifact: { objectKey: artifactKey },
operation: {
subject: { kind: 'pdf_layout', documentId, namespace: null },
},
});
expect(resolve.json().operation).not.toHaveProperty('opKey');
});
test('resolves a current PDF artifact after operation state has expired', async () => {
const documentId = 'd'.repeat(64);
const artifactKey = `openreader/documents_v1/parsed_v2/${documentId}/pp-doclayoutv3-onnx%40800%2Bpdfjs%404.8.69.json`;
fake.seedArtifact(artifactKey);
const resolve = await runtime.app.inject({
method: 'POST',
url: '/v1/pdf-layout/resolve',
headers: AUTH,
payload: {
documentId,
namespace: null,
documentObjectKey: `openreader/${documentId}.pdf`,
},
});
expect(resolve.statusCode).toBe(200);
expect(resolve.json()).toEqual({
artifact: { objectKey: artifactKey },
operation: null,
});
});
test('returns not found for unknown operation and event stream lookups', async () => {
const opResponse = await runtime.app.inject({
method: 'GET',
url: '/v1/operations/missing',
headers: AUTH,
});
expect(opResponse.statusCode).toBe(404);
const eventsResponse = await runtime.app.inject({
method: 'GET',
url: '/v1/operations/missing/events',
headers: AUTH,
});
expect(eventsResponse.statusCode).toBe(404);
});
test('streams initial SSE snapshot for terminal operation and honors cursor id', async () => {
fake.seedState({
opId: 'op-terminal',
opKey: 'k-terminal',
kind: 'pdf_layout',
jobId: 'job-op-terminal',
status: 'succeeded',
queuedAt: 1000,
updatedAt: 2000,
result: { parsedObjectKey: 'openreader/parsed.json' },
});
const stream = await runtime.app.inject({
method: 'GET',
url: '/v1/operations/op-terminal/events?sinceEventId=7',
headers: AUTH,
});
expect(stream.statusCode).toBe(200);
expect(stream.headers['content-type']).toContain('text/event-stream');
expect(stream.body).toContain('event: snapshot');
expect(stream.body).toContain('id: 7');
expect(stream.body).toContain('"status":"succeeded"');
});
test('marks stale running whisper and pdf ops failed during request-time orphan recovery but leaves queued ops on the conservative path', async () => {
const now = Date.now();
fake.seedState({
opId: 'op-stale-whisper-running',
opKey: 'k-stale-whisper-running',
kind: 'whisper_align',
jobId: 'job-op-stale-whisper-running',
status: 'running',
queuedAt: 1,
updatedAt: now - 40_000,
});
fake.seedState({
opId: 'op-stale-whisper-queued',
opKey: 'k-stale-whisper-queued',
kind: 'whisper_align',
jobId: 'job-op-stale-whisper-queued',
status: 'queued',
queuedAt: 1,
updatedAt: now - 40_000,
});
fake.seedState({
opId: 'op-stale-pdf-running',
opKey: 'k-stale-pdf-running',
kind: 'pdf_layout',
jobId: 'job-op-stale-pdf-running',
status: 'running',
queuedAt: 1,
updatedAt: now - 310_000,
});
fake.seedState({
opId: 'op-stale-pdf-queued',
opKey: 'k-stale-pdf-queued',
kind: 'pdf_layout',
jobId: 'job-op-stale-pdf-queued',
status: 'queued',
queuedAt: 1,
updatedAt: now - 310_000,
});
// GET /v1/operations/:opId resolves via getOpState(), which first awaits the shared
// orphanRecoveryPromise path through ensureOrphanedOpRecovery().
const fetch = await runtime.app.inject({
method: 'GET',
url: '/v1/operations/op-stale-whisper-running',
headers: AUTH,
});
expect(fetch.statusCode).toBe(200);
expect(fetch.json()).toMatchObject({
opId: 'op-stale-whisper-running',
status: 'failed',
error: {
code: 'WORKER_ORPHANED_OP',
},
});
expect(fake.getState('op-stale-whisper-running')).toMatchObject({
status: 'failed',
error: {
code: 'WORKER_ORPHANED_OP',
},
});
expect(fake.getState('op-stale-whisper-queued')).toMatchObject({
status: 'queued',
});
expect(fake.getState('op-stale-pdf-running')).toMatchObject({
status: 'failed',
error: {
code: 'WORKER_ORPHANED_OP',
},
});
expect(fake.getState('op-stale-pdf-queued')).toMatchObject({
status: 'queued',
});
});
});