import assert from "node:assert/strict"; import { spawnSync } from "node:child_process"; import { fileURLToPath } from "node:url"; import test from "node:test"; 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 USER_A = "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa"; const USER_B = "bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb"; const REPORT_ID = "cccccccc-cccc-4ccc-8ccc-cccccccccccc"; const REQUEST_ID = "dddddddd-dddd-4ddd-8ddd-dddddddddddd"; const HASH = "1111111111111111111111111111111111111111111111111111111111111111"; const COMMIT = "2222222222222222222222222222222222222222"; const SKILL_NAME = "jyotish-personal-report"; const SKILL_VERSION = "1.0.0"; function sqlLiteral(value: string): string { return `'${value.replaceAll("'", "''")}'`; } function selectAsAuthenticated(userId: string, sql: string): string { return ` set role authenticated; select set_config('request.jwt.claim.sub', '${userId}', true); ${sql} `; } function serviceSql(sql: string): string { return `set role service_role;\n${sql}`; } function documentFor(reportId: string, summary: string): string { return JSON.stringify({ schemaVersion: "report_document.v2", reportId, provenance: { calculationHash: HASH, evidenceHash: HASH, skillName: SKILL_NAME, skillVersion: SKILL_VERSION, skillSourceCommit: COMMIT, skillSnapshotSha256: HASH, }, executiveSummary: { summary }, }); } test("personal_reports.card_summary is nullable, owner-read, service-written, and length-capped", { skip: skipWithoutDocker }, () => { 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, migration.stderr); assert.match(migration.stdout, /applied 20260907010000_personal_report_card_summary\.sql/); fixture.psql(` insert into identity.users (id, name, email, email_verified, email_verified_at) values ('${USER_A}', 'Card Summary User A', 'card-a@example.com', true, now()), ('${USER_B}', 'Card Summary User B', 'card-b@example.com', true, now()); insert into public.personal_reports ( id, user_id, request_id, request_fingerprint, report_type, status, schema_version, presentation_mode, requested_themes, depth, skill_name, skill_version, skill_source_commit, skill_snapshot_sha256 ) values ( '${REPORT_ID}', '${USER_A}', '${REQUEST_ID}', '${HASH}', 'personal_full', 'generating', 'report_document.v2', 'default', array['career']::text[], 'standard', '${SKILL_NAME}', '${SKILL_VERSION}', '${COMMIT}', '${HASH}' ); `); assert.equal( fixture.psql(` select (card_summary is null)::text from public.personal_reports where id = '${REPORT_ID}' `), "true", ); assert.throws( () => fixture.psqlAs( "app_runtime", "app-runtime-test-password", selectAsAuthenticated(USER_A, ` update public.personal_reports set card_summary = 'owner write' where id = '${REPORT_ID}' `), ), /permission denied for table personal_reports/, ); const jobId = fixture.psql(` select id from public.personal_report_jobs where user_id = '${USER_A}' and request_id = '${REQUEST_ID}' `); assert.match(jobId, /^[0-9a-f-]{36}$/); const claim = fixture.psqlAs( "service_runtime", "service-runtime-test-password", `set role service_role; select lease_token::text from public.claim_personal_report_job('card-summary-worker', 60, '${jobId}');`, ); const leaseToken = claim.split("\n").at(-1)!; assert.match(leaseToken, /^[0-9a-f-]{36}$/); assert.equal( fixture.psqlAs( "service_runtime", "service-runtime-test-password", serviceSql(` select status from public.complete_personal_report_job( '${jobId}', '${leaseToken}', '${USER_A}', '${REPORT_ID}', '${REQUEST_ID}', '${HASH}', 'report_document.v2', ${sqlLiteral(documentFor(REPORT_ID, "事业方向保持观察"))}::jsonb, '${HASH}', '${HASH}', '${SKILL_NAME}', '${SKILL_VERSION}', '${COMMIT}', '${HASH}' ); `), ), "SET\nready", ); assert.equal( fixture.psql(`select card_summary from public.personal_reports where id = '${REPORT_ID}'`), "事业方向保持观察", ); assert.equal( fixture.psqlAs( "app_runtime", "app-runtime-test-password", selectAsAuthenticated(USER_A, ` select card_summary from public.personal_reports where id = '${REPORT_ID}' `), ), `SET\n${USER_A}\n事业方向保持观察`, ); assert.equal( fixture.psqlAs( "app_runtime", "app-runtime-test-password", selectAsAuthenticated(USER_B, ` select count(*) from public.personal_reports where id = '${REPORT_ID}' `), ), `SET\n${USER_B}\n0`, ); fixture.psqlAs( "service_runtime", "service-runtime-test-password", serviceSql(` update public.personal_reports set card_summary = '${"测".repeat(500)}' where id = '${REPORT_ID}' `), ); assert.equal( fixture.psql(` select char_length(card_summary) from public.personal_reports where id = '${REPORT_ID}' `), "500", ); assert.throws( () => fixture.psqlAs( "service_runtime", "service-runtime-test-password", serviceSql(` update public.personal_reports set card_summary = '${"测".repeat(501)}' where id = '${REPORT_ID}' `), ), /personal_reports_card_summary_length_check/, ); } finally { fixture.stop(); } });