import assert from "node:assert/strict"; import { readFileSync } from "node:fs"; import test from "node:test"; import { PersonalReportJobServiceError, createSupabasePersonalReportJobService, type PersonalReportJobQueryResult, } from "../src/lib/personal-report-job-service-core.ts"; import type { PersonalReportRecord } from "../src/lib/personal-report-service-core.ts"; const USER_A = "11111111-1111-4111-8111-111111111111"; const USER_B = "22222222-2222-4222-8222-222222222222"; const REQUEST_A = "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa"; const REQUEST_B = "bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb"; const REQUEST_C = "cccccccc-cccc-4ccc-8ccc-cccccccccccc"; const FINGERPRINT_A = "a".repeat(64); const FINGERPRINT_B = "b".repeat(64); const WRONG_TOKEN = "ffffffff-ffff-4fff-8fff-ffffffffffff"; const READY_DOCUMENT = JSON.parse(readFileSync( new URL("../../tests/fixtures/personal_report_document.v2.json", import.meta.url), "utf8", )) as Record; const REPORT_ID = String(READY_DOCUMENT.reportId); const PROVENANCE = READY_DOCUMENT.provenance as Record; type Row = Record; type Filter = | Readonly<{ kind: "eq"; column: string; value: unknown }> | Readonly<{ kind: "gt"; column: string; value: unknown }> | Readonly<{ kind: "in"; column: string; values: readonly unknown[] }>; type Mutation = | Readonly<{ kind: "insert"; row: Readonly> }> | Readonly<{ kind: "update"; values: Readonly> }> | Readonly<{ kind: "delete"; exactCount: boolean }>; function uuidFor(value: number): string { return `00000000-0000-4000-8000-${String(value).padStart(12, "0")}`; } function result(data: unknown, count?: number): PersonalReportJobQueryResult { return { data, error: null, ...(count === undefined ? {} : { count }) }; } class MemoryQueryBuilder implements PromiseLike { private mutation: Mutation | null = null; private readonly filters: Filter[] = []; private rowLimit: number | null = null; private cardinality: "many" | "single" | "maybeSingle" = "many"; constructor(private readonly backend: MemoryBackend) {} select() { return this; } insert(row: Readonly>) { this.mutation = { kind: "insert", row }; return this; } update(values: Readonly>) { this.mutation = { kind: "update", values }; return this; } delete(options?: Readonly<{ count?: string }>) { this.mutation = { kind: "delete", exactCount: options?.count === "exact" }; return this; } eq(column: string, value: unknown) { this.filters.push({ kind: "eq", column, value }); return this; } gt(column: string, value: unknown) { this.filters.push({ kind: "gt", column, value }); return this; } in(column: string, values: readonly unknown[]) { this.filters.push({ kind: "in", column, values }); return this; } limit(value: number) { this.rowLimit = value; return this; } single() { this.cardinality = "single"; return this.execute(); } maybeSingle() { this.cardinality = "maybeSingle"; return this.execute(); } then( onfulfilled?: ((value: PersonalReportJobQueryResult) => TResult1 | PromiseLike) | null, onrejected?: ((reason: unknown) => TResult2 | PromiseLike) | null, ): PromiseLike { return this.execute().then(onfulfilled, onrejected); } private matches(row: Row): boolean { return this.filters.every((filter) => { if (filter.kind === "eq") return row[filter.column] === filter.value; if (filter.kind === "in") return filter.values.includes(row[filter.column]); return String(row[filter.column]) > String(filter.value); }); } private cardinal(data: Row[]): PersonalReportJobQueryResult { if (this.cardinality === "many") return result(data); if (data.length > 1 || (this.cardinality === "single" && data.length !== 1)) { return { data: null, error: { code: "PGRST116", message: "unexpected row count" } }; } return result(data[0] ?? null); } private async execute(): Promise { if (!this.mutation) { const rows = this.backend.rows.filter((row) => this.matches(row)); return this.cardinal(this.rowLimit === null ? rows : rows.slice(0, this.rowLimit)); } if (this.mutation.kind === "insert") { const incoming = this.mutation.row; const sameRequest = this.backend.rows.some((row) => row.user_id === incoming.user_id && row.request_id === incoming.request_id); const active = this.backend.rows.some((row) => row.user_id === incoming.user_id && ["queued", "running", "suspended", "retrying"].includes(String(row.status))); if (sameRequest || active) { return { data: null, error: { code: "23505", message: "personal_report_jobs unique constraint" }, }; } const at = this.backend.now.toISOString(); const row: Row = { id: uuidFor(++this.backend.sequence), user_id: incoming.user_id, request_id: incoming.request_id, request_fingerprint: incoming.request_fingerprint, status: incoming.status ?? "queued", attempt_count: incoming.attempt_count ?? 0, max_attempts: incoming.max_attempts ?? 3, lease_token: null, lease_owner: null, lease_acquired_at: null, lease_expires_at: null, heartbeat_at: null, next_attempt_at: null, progress_phase: incoming.progress_phase ?? "queued", progress_percent: incoming.progress_percent ?? 0, last_error_code: null, last_error_at: null, started_at: null, finished_at: null, created_at: at, updated_at: at, }; this.backend.rows.push(row); return this.cardinal([row]); } const matches = this.backend.rows.filter((row) => this.matches(row)); if (this.mutation.kind === "delete") { this.backend.rows = this.backend.rows.filter((row) => !this.matches(row)); return result(null, this.mutation.exactCount ? matches.length : undefined); } for (const row of matches) { if (["ready", "failed", "cancelled"].includes(String(row.status))) { return { data: null, error: { code: "55000", message: "personal_report_job_terminal_immutable" }, }; } Object.assign(row, this.mutation.values, { updated_at: this.backend.now.toISOString() }); } return this.cardinal(matches); } } class MemoryBackend { rows: Row[] = []; sequence = 0; tokenSequence = 100; lastCompletionArgs: Readonly> | null = null; lastFailureArgs: Readonly> | null = null; constructor(public now = new Date("2026-08-14T03:00:00.000Z")) {} from(table: string) { assert.equal(table, "personal_report_jobs"); return new MemoryQueryBuilder(this); } async rpc( functionName: string, args: Readonly> = {}, ): Promise { if (functionName === "claim_personal_report_job") { const requestedId = args.p_job_id; const runnable = this.rows.find((row) => (requestedId === null || requestedId === undefined || row.id === requestedId) && ["queued", "retrying"].includes(String(row.status)) && (row.next_attempt_at === null || Date.parse(String(row.next_attempt_at)) <= this.now.getTime()) && Number(row.attempt_count) < Number(row.max_attempts)); if (!runnable) return result([]); const at = this.now.toISOString(); runnable.status = "running"; runnable.attempt_count = Number(runnable.attempt_count) + 1; runnable.lease_token = uuidFor(++this.tokenSequence); runnable.lease_owner = String(args.p_worker_id); runnable.lease_acquired_at = at; runnable.heartbeat_at = at; runnable.lease_expires_at = new Date( this.now.getTime() + Number(args.p_lease_seconds) * 1000, ).toISOString(); runnable.next_attempt_at = null; runnable.progress_phase = Number(runnable.attempt_count) === 1 ? "starting" : "retry_starting"; runnable.started_at ??= at; runnable.updated_at = at; return result([runnable]); } if (functionName === "heartbeat_personal_report_job") { const row = this.rows.find((candidate) => candidate.id === args.p_job_id && candidate.status === "running" && candidate.lease_token === args.p_lease_token && Date.parse(String(candidate.lease_expires_at)) > this.now.getTime()); if (!row) return result([]); row.heartbeat_at = this.now.toISOString(); row.lease_expires_at = new Date( this.now.getTime() + Number(args.p_lease_seconds) * 1000, ).toISOString(); row.updated_at = this.now.toISOString(); return result([row]); } if (functionName === "complete_personal_report_job") { this.lastCompletionArgs = args; const row = this.rows.find((candidate) => candidate.id === args.p_job_id && candidate.status === "running" && candidate.lease_token === args.p_lease_token && candidate.user_id === args.p_user_id && candidate.request_id === args.p_request_id && candidate.request_fingerprint === args.p_request_fingerprint && Date.parse(String(candidate.lease_expires_at)) > this.now.getTime()); if (!row) { return { data: null, error: { message: "personal_report_job_lease_lost" } }; } const at = this.now.toISOString(); row.status = "ready"; row.lease_token = null; row.lease_owner = null; row.lease_acquired_at = null; row.lease_expires_at = null; row.heartbeat_at = at; row.next_attempt_at = null; row.progress_phase = "ready"; row.progress_percent = 100; row.last_error_code = null; row.last_error_at = null; row.finished_at = at; row.updated_at = at; return result([row]); } if (functionName === "fail_personal_report_job") { this.lastFailureArgs = args; const row = this.rows.find((candidate) => candidate.id === args.p_job_id && candidate.status === "running" && candidate.lease_token === args.p_lease_token && candidate.user_id === args.p_user_id && candidate.request_id === args.p_request_id && candidate.request_fingerprint === args.p_request_fingerprint && Date.parse(String(candidate.lease_expires_at)) > this.now.getTime()); if (!row) { return { data: null, error: { message: "personal_report_job_lease_lost" } }; } const at = this.now.toISOString(); row.status = "failed"; row.lease_token = null; row.lease_owner = null; row.lease_acquired_at = null; row.lease_expires_at = null; row.heartbeat_at = at; row.next_attempt_at = null; row.progress_phase = "failed"; row.last_error_code = args.p_error_code; row.last_error_at = at; row.finished_at = at; row.updated_at = at; return result([row]); } if (functionName === "recover_expired_personal_report_jobs") { let retryingCount = 0; let failedCount = 0; const limit = Number(args.p_limit); const expired = this.rows .filter((row) => row.status === "running" && Date.parse(String(row.lease_expires_at)) <= this.now.getTime()) .slice(0, limit); for (const row of expired) { const at = this.now.toISOString(); const retry = Number(row.attempt_count) < Number(row.max_attempts); row.status = retry ? "retrying" : "failed"; row.lease_token = null; row.lease_owner = null; row.lease_acquired_at = null; row.lease_expires_at = null; row.heartbeat_at = null; row.next_attempt_at = retry ? at : null; row.progress_phase = retry ? "retry_wait" : "failed"; row.last_error_code = retry ? "lease_expired" : "lease_expired_attempts_exhausted"; row.last_error_at = at; row.finished_at = retry ? null : at; row.updated_at = at; if (retry) retryingCount += 1; else failedCount += 1; } return result([{ retrying_count: retryingCount, failed_count: failedCount }]); } return { data: null, error: { message: "unknown rpc" } }; } } function fixture() { const backend = new MemoryBackend(); const service = createSupabasePersonalReportJobService(backend, { now: () => new Date(backend.now), }); return { backend, service }; } function reportRecord(): PersonalReportRecord { return { id: REPORT_ID, userId: USER_A, sessionId: null, chartProfileId: null, requestId: REQUEST_A, requestFingerprint: FINGERPRINT_A, reportType: "personal_full", status: "generating", schemaVersion: "report_document.v2", presentationMode: "default", depth: "standard", requestedThemes: ["career"], reportDocument: null, calculationHash: null, evidenceHash: null, skillName: String(PROVENANCE.skillName), skillVersion: String(PROVENANCE.skillVersion), skillSourceCommit: String(PROVENANCE.skillSourceCommit), skillSnapshotSha256: String(PROVENANCE.skillSnapshotSha256), failureCode: null, createdAt: "2026-08-14T03:00:00.000Z", updatedAt: "2026-08-14T03:00:00.000Z", completedAt: null, }; } async function expectCode( promise: Promise, code: PersonalReportJobServiceError["code"], ): Promise { await assert.rejects( promise, (error) => error instanceof PersonalReportJobServiceError && error.code === code, ); } test("enqueue replays the same request fingerprint and reports a changed fingerprint conflict", async () => { const { backend, service } = fixture(); const input = { userId: USER_A, requestId: REQUEST_A, requestFingerprint: FINGERPRINT_A, }; const created = await service.enqueue(input); assert.equal(created.kind, "created"); const replayed = await service.enqueue(input); assert.equal(replayed.kind, "replayed"); assert.equal(replayed.job.id, created.job.id); const conflict = await service.enqueue({ ...input, requestFingerprint: FINGERPRINT_B }); assert.equal(conflict.kind, "request_conflict"); assert.equal(conflict.job.id, created.job.id); assert.equal(backend.rows.length, 1); assert.deepEqual( Object.keys(backend.rows[0]).filter((key) => /payload|prompt|birth|report_document|model/i.test(key)), [], ); }); test("enqueue preserves one active job per user while allowing another owner", async () => { const { service } = fixture(); const first = await service.enqueue({ userId: USER_A, requestId: REQUEST_A, requestFingerprint: FINGERPRINT_A, }); const blocked = await service.enqueue({ userId: USER_A, requestId: REQUEST_B, requestFingerprint: FINGERPRINT_B, }); const otherOwner = await service.enqueue({ userId: USER_B, requestId: REQUEST_B, requestFingerprint: FINGERPRINT_B, }); assert.equal(first.kind, "created"); assert.equal(blocked.kind, "active_job_exists"); assert.equal(blocked.job.id, first.job.id); assert.equal(otherOwner.kind, "created"); }); test("claim and heartbeat require the exact live lease token", async () => { const { backend, service } = fixture(); const enqueued = await service.enqueue({ userId: USER_A, requestId: REQUEST_A, requestFingerprint: FINGERPRINT_A, }); assert.equal(enqueued.kind, "created"); const claimed = await service.claimLease({ workerId: "worker-a", leaseSeconds: 30, jobId: enqueued.job.id, }); assert.ok(claimed?.leaseToken); assert.equal(claimed.status, "running"); assert.equal(claimed.attemptCount, 1); await expectCode(service.heartbeatLease({ jobId: claimed.id, leaseToken: WRONG_TOKEN, leaseSeconds: 60, }), "lease_lost"); backend.now = new Date("2026-08-14T03:00:10.000Z"); const heartbeat = await service.heartbeatLease({ jobId: claimed.id, leaseToken: claimed.leaseToken, leaseSeconds: 60, }); assert.equal(heartbeat.heartbeatAt, backend.now.toISOString()); assert.equal(heartbeat.leaseExpiresAt, "2026-08-14T03:01:10.000Z"); await expectCode(service.updateProgress({ jobId: claimed.id, leaseToken: WRONG_TOKEN, phase: "building_evidence", percent: 40, }), "lease_lost"); const progressed = await service.updateProgress({ jobId: claimed.id, leaseToken: claimed.leaseToken, phase: "building_evidence", percent: 40, }); assert.equal(progressed.progressPhase, "building_evidence"); assert.equal(progressed.progressPercent, 40); }); test("atomic completion binds the canonical v2 document to the exact live lease", async () => { const { backend, service } = fixture(); const enqueued = await service.enqueue({ userId: USER_A, requestId: REQUEST_A, requestFingerprint: FINGERPRINT_A, }); assert.equal(enqueued.kind, "created"); const claimed = await service.claimLease({ workerId: "worker-a", jobId: enqueued.job.id }); assert.ok(claimed?.leaseToken); const completed = await service.completeReportReady({ jobId: claimed.id, leaseToken: claimed.leaseToken, report: reportRecord(), document: READY_DOCUMENT, }); assert.equal(completed.status, "ready"); assert.equal(completed.progressPercent, 100); assert.equal(backend.lastCompletionArgs?.p_schema_version, "report_document.v2"); assert.equal(backend.lastCompletionArgs?.p_report_id, REPORT_ID); assert.deepEqual(backend.lastCompletionArgs?.p_report_document, READY_DOCUMENT); assert.equal(backend.lastCompletionArgs?.p_evidence_hash, PROVENANCE.evidenceHash); }); test("atomic failure binds the report failure to the exact live lease", async () => { const { backend, service } = fixture(); const enqueued = await service.enqueue({ userId: USER_A, requestId: REQUEST_A, requestFingerprint: FINGERPRINT_A, }); assert.equal(enqueued.kind, "created"); const claimed = await service.claimLease({ workerId: "worker-a", jobId: enqueued.job.id }); assert.ok(claimed?.leaseToken); const failed = await service.completeReportFailed({ jobId: claimed.id, leaseToken: claimed.leaseToken, report: reportRecord(), errorCode: "report_schema_invalid", failureCode: "report_schema_invalid", }); assert.equal(failed.status, "failed"); assert.equal(failed.lastErrorCode, "report_schema_invalid"); assert.equal(backend.lastFailureArgs?.p_report_id, REPORT_ID); assert.equal(backend.lastFailureArgs?.p_failure_code, "report_schema_invalid"); }); test("atomic failure rejects an expired lease without changing the job", async () => { const { backend, service } = fixture(); const enqueued = await service.enqueue({ userId: USER_A, requestId: REQUEST_A, requestFingerprint: FINGERPRINT_A, }); assert.equal(enqueued.kind, "created"); const claimed = await service.claimLease({ workerId: "worker-a", leaseSeconds: 5, jobId: enqueued.job.id, }); assert.ok(claimed?.leaseToken); backend.now = new Date("2026-08-14T03:00:06.000Z"); await expectCode(service.completeReportFailed({ jobId: claimed.id, leaseToken: claimed.leaseToken, report: reportRecord(), errorCode: "report_schema_invalid", failureCode: "report_schema_invalid", }), "lease_lost"); assert.equal((await service.getOwnedById(USER_A, claimed.id))?.status, "running"); }); test("atomic completion rejects an expired lease without changing the job", async () => { const { backend, service } = fixture(); const enqueued = await service.enqueue({ userId: USER_A, requestId: REQUEST_A, requestFingerprint: FINGERPRINT_A, }); assert.equal(enqueued.kind, "created"); const claimed = await service.claimLease({ workerId: "worker-a", leaseSeconds: 5, jobId: enqueued.job.id, }); assert.ok(claimed?.leaseToken); backend.now = new Date("2026-08-14T03:00:06.000Z"); await expectCode(service.completeReportReady({ jobId: claimed.id, leaseToken: claimed.leaseToken, report: reportRecord(), document: READY_DOCUMENT, }), "lease_lost"); assert.equal((await service.getOwnedById(USER_A, claimed.id))?.status, "running"); }); test("ready is terminal and owner reads/deletes remain owner-scoped", async () => { const { service } = fixture(); const enqueued = await service.enqueue({ userId: USER_A, requestId: REQUEST_A, requestFingerprint: FINGERPRINT_A, }); assert.equal(enqueued.kind, "created"); const claimed = await service.claimLease({ workerId: "worker-a", jobId: enqueued.job.id }); assert.ok(claimed?.leaseToken); const ready = await service.markReady({ jobId: claimed.id, leaseToken: claimed.leaseToken }); assert.equal(ready.status, "ready"); assert.equal(ready.progressPercent, 100); await expectCode(service.markFailed({ jobId: ready.id, leaseToken: claimed.leaseToken, errorCode: "late_failure", }), "terminal_immutable"); await expectCode(service.cancel({ jobId: ready.id }), "terminal_immutable"); assert.equal(await service.getOwnedById(USER_B, ready.id), null); assert.equal((await service.getOwnedById(USER_A, ready.id))?.status, "ready"); assert.equal(await service.deleteOwned(USER_B, ready.id), false); assert.equal(await service.deleteOwned(USER_A, ready.id), true); assert.equal(await service.getOwnedById(USER_A, ready.id), null); }); test("retry is bounded and exhaustion preserves the live lease for atomic report failure", async () => { const { service } = fixture(); const enqueued = await service.enqueue({ userId: USER_A, requestId: REQUEST_A, requestFingerprint: FINGERPRINT_A, maxAttempts: 2, }); assert.equal(enqueued.kind, "created"); const first = await service.claimLease({ workerId: "worker-a", jobId: enqueued.job.id }); assert.ok(first?.leaseToken); const scheduled = await service.scheduleRetry({ jobId: first.id, leaseToken: first.leaseToken, errorCode: "model_unavailable", }); assert.equal(scheduled.kind, "scheduled"); assert.equal(scheduled.job.status, "retrying"); const second = await service.claimLease({ workerId: "worker-b", jobId: first.id }); assert.ok(second?.leaseToken); assert.equal(second.attemptCount, 2); const exhausted = await service.scheduleRetry({ jobId: second.id, leaseToken: second.leaseToken, errorCode: "model_unavailable", }); assert.equal(exhausted.kind, "exhausted"); assert.equal(exhausted.job.status, "running"); assert.equal(exhausted.job.leaseToken, second.leaseToken); assert.equal(await service.claimLease({ workerId: "worker-c", jobId: second.id }), null); }); test("expired lease recovery retries within budget and fails at the bound", async () => { const { backend, service } = fixture(); const enqueued = await service.enqueue({ userId: USER_A, requestId: REQUEST_C, requestFingerprint: FINGERPRINT_A, maxAttempts: 2, }); assert.equal(enqueued.kind, "created"); const first = await service.claimLease({ workerId: "worker-a", leaseSeconds: 5, jobId: enqueued.job.id, }); assert.ok(first); backend.now = new Date("2026-08-14T03:00:06.000Z"); assert.deepEqual( await service.recoverExpiredLeases(1), { retryingCount: 1, failedCount: 0 }, ); const retrying = await service.getOwnedById(USER_A, first.id); assert.equal(retrying?.status, "retrying"); assert.equal(retrying?.lastErrorCode, "lease_expired"); const second = await service.claimLease({ workerId: "worker-b", leaseSeconds: 5, jobId: first.id, }); assert.ok(second); assert.equal(second.attemptCount, 2); backend.now = new Date("2026-08-14T03:00:12.000Z"); assert.deepEqual( await service.recoverExpiredLeases(1), { retryingCount: 0, failedCount: 1 }, ); const failed = await service.getOwnedById(USER_A, first.id); assert.equal(failed?.status, "failed"); assert.equal(failed?.lastErrorCode, "lease_expired_attempts_exhausted"); }); test("suspend and cancel enforce valid state and identifier shapes", async () => { const { service } = fixture(); const enqueued = await service.enqueue({ userId: USER_A, requestId: REQUEST_A, requestFingerprint: FINGERPRINT_A, }); assert.equal(enqueued.kind, "created"); const suspended = await service.suspend({ jobId: enqueued.job.id }); assert.equal(suspended.status, "suspended"); const cancelled = await service.cancel({ jobId: suspended.id }); assert.equal(cancelled.status, "cancelled"); await expectCode(service.enqueue({ userId: "not-a-uuid", requestId: REQUEST_B, requestFingerprint: FINGERPRINT_B, }), "invalid_request"); await expectCode(service.heartbeatLease({ jobId: enqueued.job.id, leaseToken: "not-a-token", }), "invalid_request"); }); test("service source exposes only operational job columns", () => { const source = readFileSync( new URL("../src/lib/personal-report-job-service-core.ts", import.meta.url), "utf8", ); assert.doesNotMatch(source, /console\.(?:log|info|warn|error)/); const columns = source.match(/const JOB_COLUMNS = \[([\s\S]*?)\]\.join/)?.[1] ?? ""; assert.doesNotMatch(columns, /birth|prompt|report_document|payload|model/i); assert.match(source, /claim_personal_report_job/); assert.match(source, /heartbeat_personal_report_job/); assert.match(source, /complete_personal_report_job/); assert.match(source, /fail_personal_report_job/); assert.match(source, /recover_expired_personal_report_jobs/); });