feat: add durable asset synchronization worker

This commit is contained in:
jb
2026-09-19 22:15:23 -04:00
parent 2675ea8114
commit fe7d218195
36 changed files with 2218 additions and 189 deletions
+75
View File
@@ -0,0 +1,75 @@
import { describe, expect, test } from 'bun:test';
import { mkdtemp, readFile, rm } from 'node:fs/promises';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { parseSyncEnqueueBody } from '../src/lib/client/sync-queue';
import { workerEnvironment } from '../scripts/run-sync-worker';
describe('T07 worker runtime boundary', () => {
test('bundled worker starts with inert restricted configuration and has no unresolved aliases', async () => {
const directory = await mkdtemp(join(tmpdir(), 'eve-sync-worker-'));
try {
const output = join(directory, 'sync-worker.js');
const built = await Bun.build({ entrypoints: ['workers/sync.ts'], target: 'bun', outdir: directory, naming: 'sync-worker.js', minify: false });
expect(built.success).toBe(true);
const source = await readFile(output, 'utf8');
expect(source.includes('$lib/')).toBe(false);
const env: Record<string, string | undefined> = { PATH: process.env.PATH, HOME: process.env.HOME, CONVEX_URL: 'http://127.0.0.1:9', CONVEX_SYNC_WORKER_SECRET: 'inert-worker-secret', TOKEN_ENCRYPTION_KEY: Buffer.alloc(32).toString('base64'), EVE_CLIENT_ID: 'inert-client', EVE_CALLBACK_URL: 'http://localhost/unused-worker-callback', ESI_USER_AGENT: 'EVE-Intelligence/test', SYNC_WORKER_STARTUP_CHECK: '1' };
for (const name of ['CONVEX_BRIDGE_SECRET', 'CONVEX_SELF_HOSTED_ADMIN_KEY', 'APP_AUTH_PRIVATE_KEY', 'APP_AUTH_JWKS', 'TYPESAFE_API_KEY']) delete env[name];
const child = Bun.spawn(['bun', output], { env, stdout: 'pipe', stderr: 'pipe' });
const code = await child.exited;
const outputText = await new Response(child.stdout).text() + await new Response(child.stderr).text();
expect(code).toBe(0);
expect(outputText).toBe('');
} finally { await rm(directory, { recursive: true, force: true }); }
});
test('client preserves structured all-skipped and partial enqueue outcomes on HTTP errors', async () => {
expect(parseSyncEnqueueBody({ queued: 0, jobs: [], skipped: [{ characterId: 1, reason: 'queue_full' }], partial: true })).toMatchObject({ queued: 0, skipped: [{ characterId: 1, reason: 'queue_full' }] });
expect(parseSyncEnqueueBody({ queued: 1, jobs: [{ id: 'job', characterId: 2, status: 'queued', attempts: 0, dueAt: 1, createdAt: 1, updatedAt: 1, leaseExpiresAt: null, cancelRequested: false }], skipped: [{ characterId: 3, reason: 'enqueue_failed' }], partial: true })).toMatchObject({ queued: 1, jobs: [{ id: 'job', characterId: 2 }], skipped: [{ characterId: 3, reason: 'enqueue_failed' }] });
const originalFetch = globalThis.fetch;
try {
globalThis.fetch = (async () => Response.json({ queued: 0, jobs: [], skipped: [{ characterId: 1, reason: 'queue_full' }], partial: true }, { status: 429 })) as unknown as typeof fetch;
const allSkipped = await (await import('../src/lib/client/sync-queue')).enqueueSync();
expect(allSkipped.response.status).toBe(429); expect(allSkipped.body).toMatchObject({ jobs: [], skipped: [{ reason: 'queue_full' }] });
globalThis.fetch = (async () => Response.json({ queued: 1, jobs: [{ id: 'job', characterId: 2, status: 'queued', attempts: 0, dueAt: 1, createdAt: 1, updatedAt: 1, leaseExpiresAt: null, cancelRequested: false }], skipped: [{ characterId: 3, reason: 'enqueue_failed' }], partial: true }, { status: 503 })) as unknown as typeof fetch;
const partial = await (await import('../src/lib/client/sync-queue')).enqueueSync();
expect(partial.response.status).toBe(503); expect(partial.body).toMatchObject({ jobs: [{ id: 'job' }], skipped: [{ reason: 'enqueue_failed' }] });
} finally { globalThis.fetch = originalFetch; }
});
test('missing status is surfaced and status polling forwards cancellation', async () => {
const originalFetch = globalThis.fetch;
try {
globalThis.fetch = (async () => new Response(null, { status: 404 })) as unknown as typeof fetch;
expect(await (await import('../src/lib/client/sync-queue')).syncJobStatus('missing')).toBeNull();
globalThis.fetch = (() => new Promise<Response>(() => {})) as unknown as typeof fetch;
await expect((await import('../src/lib/client/sync-queue')).syncJobStatus('never', undefined, 5)).rejects.toThrow('timed out');
const controller = new AbortController();
globalThis.fetch = ((_input: RequestInfo | URL, init?: RequestInit) => new Promise<Response>((_resolve, reject) => {
init?.signal?.addEventListener('abort', () => reject(new DOMException('aborted', 'AbortError')), { once: true });
})) as unknown as typeof fetch;
const pending = (await import('../src/lib/client/sync-queue')).syncJobStatus('running', controller.signal);
controller.abort();
await expect(pending).rejects.toMatchObject({ name: 'AbortError' });
} finally { globalThis.fetch = originalFetch; }
});
test('worker child environment drops Infisical bootstrap credentials and ambient web keys', () => {
const child = workerEnvironment({ PATH: '/bin', CONVEX_URL: 'http://inert', CONVEX_SYNC_WORKER_SECRET: 'worker-only', TOKEN_ENCRYPTION_KEY: 'inert-key', EVE_CLIENT_ID: 'inert-client', EVE_CLIENT_SECRET: 'inert-eve-secret', INFISICAL_TOKEN: 'inert-token', INFISICAL_UNIVERSAL_AUTH_CLIENT_ID: 'inert-client-id', INFISICAL_UNIVERSAL_AUTH_CLIENT_SECRET: 'inert-client-secret', CONVEX_BRIDGE_SECRET: 'inert-bridge', APP_AUTH_PRIVATE_KEY: 'inert-jwt', TYPESAFE_API_KEY: 'inert-jev' });
expect(child).toEqual({ PATH: '/bin', CONVEX_URL: 'http://inert', CONVEX_SYNC_WORKER_SECRET: 'worker-only', TOKEN_ENCRYPTION_KEY: 'inert-key', EVE_CLIENT_ID: 'inert-client', EVE_CLIENT_SECRET: 'inert-eve-secret' });
expect(Object.keys(child).some((key) => key.startsWith('INFISICAL_'))).toBe(false);
});
test('direct worker launcher refuses general bridge/admin/JWT/JEV credentials', async () => {
for (const name of ['CONVEX_BRIDGE_SECRET', 'CONVEX_SELF_HOSTED_ADMIN_KEY', 'APP_AUTH_PRIVATE_KEY', 'APP_AUTH_JWKS', 'TYPESAFE_API_KEY']) {
const forbidden = `inert-${name}`;
const env = { PATH: process.env.PATH, HOME: process.env.HOME, CONVEX_SYNC_WORKER_SECRET: 'inert-worker-secret', [name]: forbidden };
const child = Bun.spawn(['bun', 'scripts/run-sync-worker.ts'], { env, stdout: 'pipe', stderr: 'pipe' });
const code = await child.exited;
const output = await new Response(child.stdout).text() + await new Response(child.stderr).text();
expect(code).not.toBe(0);
expect(output).not.toContain(forbidden);
}
});
});