The self-hosted client was typed as a full Supabase client, so an old scoring poll called .single() on a plain promise and failed. The new DataClient only lists methods the adapter implements. Co-Authored-By: Grok (agent loop)
158 lines
5.9 KiB
TypeScript
158 lines
5.9 KiB
TypeScript
import assert from "node:assert/strict";
|
|
import { spawnSync } from "node:child_process";
|
|
import { fileURLToPath } from "node:url";
|
|
import test from "node:test";
|
|
|
|
import {
|
|
closeLocalPostgresDataPools,
|
|
createLocalPostgresDataClient,
|
|
} from "../src/lib/db/local-postgres-client-core.ts";
|
|
import type { DataClient } from "../src/lib/db/data-client.ts";
|
|
import { birthTimeScoringAlgorithmVersion } from "../src/lib/birth-time-scoring-job.ts";
|
|
import { createSupabaseScoringJobStore } from "../src/lib/birth-time-scoring-job-store.ts";
|
|
import { startPostgresFixture } from "./helpers/postgres-fixture.ts";
|
|
|
|
const runnerPath = fileURLToPath(new URL("../scripts/db-migrate.mjs", import.meta.url));
|
|
|
|
function dockerAvailable(): boolean {
|
|
return spawnSync("docker", ["version", "--format", "{{.Server.Version}}"], {
|
|
encoding: "utf8",
|
|
stdio: "ignore",
|
|
}).status === 0;
|
|
}
|
|
|
|
const skipWithoutDocker = dockerAvailable() ? false : "docker unavailable on this host";
|
|
|
|
const ids = {
|
|
user: "e1111111-1111-4111-8111-111111111111",
|
|
caseId: "e2222222-2222-4222-8222-222222222222",
|
|
job: "e3333333-3333-4333-8333-333333333333",
|
|
shapeJob: "e4444444-4444-4444-8444-444444444444",
|
|
};
|
|
|
|
const fingerprint = "claim-shape-fingerprint";
|
|
|
|
function sqlLiteral(value: string): string {
|
|
return `'${value.replaceAll("'", "''")}'`;
|
|
}
|
|
|
|
const identity = {
|
|
userId: ids.user,
|
|
caseId: ids.caseId,
|
|
jobId: ids.job,
|
|
evidenceFingerprint: fingerprint,
|
|
algorithmVersion: birthTimeScoringAlgorithmVersion,
|
|
now: "2026-10-15T00:00:00.000Z",
|
|
};
|
|
|
|
test("claimScoringJob reads the first row when rpc returns an array", async () => {
|
|
const rpc = async () => ({
|
|
data: [{ claim_state: "claimed", algorithm_version: birthTimeScoringAlgorithmVersion }],
|
|
error: null,
|
|
});
|
|
const store = createSupabaseScoringJobStore({ rpc } as unknown as DataClient, async () => null);
|
|
const claim = await store.claimScoringJob(identity);
|
|
assert.deepEqual(claim, {
|
|
kind: "claimed",
|
|
algorithmVersion: birthTimeScoringAlgorithmVersion,
|
|
});
|
|
});
|
|
|
|
test("claimScoringJob also accepts the single-object rpc shape", async () => {
|
|
const rpc = async () => ({
|
|
data: { claim_state: "processing", algorithm_version: birthTimeScoringAlgorithmVersion },
|
|
error: null,
|
|
});
|
|
const store = createSupabaseScoringJobStore({ rpc } as unknown as DataClient, async () => null);
|
|
const claim = await store.claimScoringJob(identity);
|
|
assert.deepEqual(claim, {
|
|
kind: "processing",
|
|
algorithmVersion: birthTimeScoringAlgorithmVersion,
|
|
});
|
|
});
|
|
|
|
test("claim_birth_time_scoring_job returns an array and claimScoringJob accepts it", { skip: skipWithoutDocker }, async () => {
|
|
const fixture = startPostgresFixture();
|
|
const schemaUrl = fixture.connectionUrl("schema_owner", "schema-owner-test-password");
|
|
try {
|
|
const migration = spawnSync(process.execPath, [runnerPath], {
|
|
encoding: "utf8",
|
|
env: { ...process.env, SCHEMA_DATABASE_URL: schemaUrl },
|
|
});
|
|
assert.equal(
|
|
migration.status,
|
|
0,
|
|
JSON.stringify({
|
|
status: migration.status,
|
|
stderr: migration.stderr,
|
|
stdoutTail: String(migration.stdout).slice(-1500),
|
|
}),
|
|
);
|
|
|
|
fixture.psqlAs(
|
|
"identity_runtime",
|
|
"identity-runtime-test-password",
|
|
`insert into identity.users (id, name, email, email_verified, email_verified_at)
|
|
values (${sqlLiteral(ids.user)}, 'Claim Shape', 'claim-shape@example.com', true, now());`,
|
|
);
|
|
fixture.psql(`
|
|
insert into public.birth_time_rectification_cases (
|
|
id, user_id, status, reported_date, source
|
|
) values (
|
|
${sqlLiteral(ids.caseId)}, ${sqlLiteral(ids.user)}, 'rectifying', '1990-01-01', 'family_exact'
|
|
);
|
|
insert into public.birth_time_rectification_scoring_jobs (
|
|
id, case_id, user_id, evidence_fingerprint, algorithm_version, status, expires_at
|
|
) values (
|
|
${sqlLiteral(ids.job)}, ${sqlLiteral(ids.caseId)}, ${sqlLiteral(ids.user)},
|
|
${sqlLiteral(fingerprint)}, ${sqlLiteral(birthTimeScoringAlgorithmVersion)},
|
|
'pending', '2026-10-15T01:00:00Z'
|
|
), (
|
|
${sqlLiteral(ids.shapeJob)}, ${sqlLiteral(ids.caseId)}, ${sqlLiteral(ids.user)},
|
|
${sqlLiteral(`${fingerprint}-shape`)}, ${sqlLiteral(birthTimeScoringAlgorithmVersion)},
|
|
'pending', '2026-10-15T01:00:00Z'
|
|
);
|
|
update public.birth_time_rectification_cases
|
|
set journey_protocol = 'legacy-guided-v1',
|
|
turn_state = ${sqlLiteral(JSON.stringify({
|
|
nextAction: { kind: "score_pending", jobId: ids.shapeJob },
|
|
}))}::jsonb
|
|
where id = ${sqlLiteral(ids.caseId)};
|
|
`);
|
|
|
|
const client = createLocalPostgresDataClient(
|
|
fixture.connectionUrl("service_runtime", "service-runtime-test-password"),
|
|
null,
|
|
"service_role",
|
|
);
|
|
const direct = await client.rpc("claim_birth_time_scoring_job", {
|
|
p_user_id: ids.user,
|
|
p_case_id: ids.caseId,
|
|
p_job_id: ids.shapeJob,
|
|
p_evidence_fingerprint: `${fingerprint}-shape`,
|
|
p_algorithm_version: birthTimeScoringAlgorithmVersion,
|
|
p_now: identity.now,
|
|
});
|
|
assert.equal(direct.error, null, direct.error?.message);
|
|
assert.equal(Array.isArray(direct.data), true);
|
|
assert.equal(direct.data[0]?.claim_state, "claimed");
|
|
|
|
fixture.psql(`
|
|
update public.birth_time_rectification_cases
|
|
set turn_state = ${sqlLiteral(JSON.stringify({
|
|
nextAction: { kind: "score_pending", jobId: ids.job },
|
|
}))}::jsonb
|
|
where id = ${sqlLiteral(ids.caseId)};
|
|
`);
|
|
const store = createSupabaseScoringJobStore(client, async () => null);
|
|
const claim = await store.claimScoringJob(identity);
|
|
assert.deepEqual(claim, {
|
|
kind: "claimed",
|
|
algorithmVersion: birthTimeScoringAlgorithmVersion,
|
|
});
|
|
} finally {
|
|
await closeLocalPostgresDataPools();
|
|
fixture.stop();
|
|
}
|
|
});
|