import assert from "node:assert/strict"; import { spawnSync } from "node:child_process"; import { fileURLToPath } from "node:url"; import test from "node:test"; import { closeLocalPostgresDataPool, createLocalPostgresDataClient } from "../src/lib/db/local-postgres-client-core.ts"; import { startPostgresFixture } from "./helpers/postgres-fixture.ts"; const runnerPath = fileURLToPath( new URL("../scripts/db-migrate.mjs", import.meta.url), ); function dockerAvailable(): boolean { const probe = spawnSync("docker", ["version", "--format", "{{.Server.Version}}"], { encoding: "utf8", stdio: "ignore", }); return probe.status === 0; } const skipWithoutDocker = dockerAvailable() ? false : "docker unavailable on this host"; function rpcError(error: unknown): string { if (!error || typeof error !== "object") return ""; const value = error as { message?: unknown }; return typeof value.message === "string" ? value.message : ""; } const snapshot = { birth_date: "1998-03-15", birth_place_label: "北京市", latitude: 39.9042, longitude: 116.4074, timezone_id: "Asia/Shanghai", timezone_offset: 8, birth_time_source: "unknown", reported_birth_time: null, active_birth_time: null, birth_time_period: null, uncertainty_before_minutes: null, uncertainty_after_minutes: null, }; const fingerprint = "b".repeat(64); const fullDay = { start_time: "00:00", end_time: "23:59" }; const skillName = "jyotish-birth-time-rectification"; const skillVersion = "9.0.0"; const blocks = { blocks: [ { period: "early_morning", start_time: "04:00", end_time: "07:59", relative_support: 12 }, { period: "morning", start_time: "08:00", end_time: "11:59", relative_support: 41 }, { period: "afternoon", start_time: "12:00", end_time: "17:59", relative_support: 28 }, { period: "evening", start_time: "18:00", end_time: "22:59", relative_support: 11 }, { period: "late_night", start_time: "23:00", end_time: "03:59", relative_support: 8 }, ], }; test("block_scan RPCs are service_role-only and advance a declared period", { 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, migration.stderr); fixture.psqlAs( "identity_runtime", "identity-runtime-test-password", ` insert into identity.users (name, email, email_verified, email_verified_at) values ('Unknown', 'unknown-time@example.com', true, now()); `, ); const userId = fixture.psql( "select id from identity.users where email = 'unknown-time@example.com'", ); fixture.psql(` update public.profiles set birth_date = '1998-03-15', reported_birth_time = null, birth_time_source = 'unknown', latitude = 39.9042, longitude = 116.4074, timezone_offset = 8, birth_time_status = 'reported' where id = '${userId}'; `); fixture.psql(` with provider as ( insert into public.model_providers (code, name, provider_type, encrypted_api_key, enabled) values ('block-scan-test', 'Block Scan Test', 'openai', 'test-ciphertext', true) returning id ), config as ( insert into public.model_configs (model_id) values ('block-scan-default-model') returning id ) insert into public.model_config_versions ( config_id, version, provider_id, label, provider_model, enabled, is_default, status, published_at ) select config.id, 1, provider.id, 'Default', 'gpt-test', true, true, 'published', now() from config cross join provider; `); const service = createLocalPostgresDataClient( fixture.connectionUrl("service_runtime", "service-runtime-test-password"), null, "service_role", ); const opened = await service.rpc("open_agentic_rectification_case", { p_user_id: userId, p_request_id: "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa", p_intent: "homepage", p_session_id: null, p_skill_name: skillName, p_skill_version: skillVersion, p_baseline_profile_fingerprint: fingerprint, p_baseline_birth_snapshot: snapshot, p_candidate_range: fullDay, }); assert.equal(opened.error, null, rpcError(opened.error)); const caseId = String((opened.data as Record).case_id); const staged = await service.rpc("set_agentic_rectification_case_stage", { p_user_id: userId, p_case_id: caseId, p_stage: "block_scan", }); assert.equal(staged.error, null, rpcError(staged.error)); const written = await service.rpc("write_agentic_rectification_block_scan", { p_user_id: userId, p_case_id: caseId, p_block_scan: blocks, }); assert.equal(written.error, null, rpcError(written.error)); const dossier = await service.rpc("get_agentic_rectification_case_dossier", { p_user_id: userId, p_case_id: caseId, }); assert.equal(dossier.error, null, rpcError(dossier.error)); const caseRow = (dossier.data as { case?: { stage?: string; block_scan?: unknown } }).case; assert.equal(caseRow?.stage, "block_scan"); assert.ok(caseRow?.block_scan); const advanced = await service.rpc("advance_agentic_rectification_case_from_block_scan", { p_user_id: userId, p_case_id: caseId, p_start_time: "08:00", p_end_time: "11:59", }); assert.equal(advanced.error, null, rpcError(advanced.error)); assert.equal((advanced.data as { stage?: string }).stage, "block_scan"); assert.deepEqual((advanced.data as { candidate_range?: unknown }).candidate_range, { start_time: "08:00", end_time: "11:59", }); assert.equal( fixture.psql(`select stage from public.agentic_rectification_cases where id = '${caseId}'`), "block_scan", ); const outside = await service.rpc("advance_agentic_rectification_case_from_block_scan", { p_user_id: userId, p_case_id: caseId, p_start_time: "04:50", p_end_time: "05:10", }); assert.match(rpcError(outside.error), /agentic_rectification_invalid_block_window/); const narrowed = await service.rpc("advance_agentic_rectification_case_from_block_scan", { p_user_id: userId, p_case_id: caseId, p_start_time: "08:00", p_end_time: "09:00", }); assert.equal(narrowed.error, null, rpcError(narrowed.error)); assert.equal((narrowed.data as { stage?: string }).stage, "minute"); const invalidPeriod = await service.rpc("advance_agentic_rectification_case_from_block_scan", { p_user_id: userId, p_case_id: caseId, p_start_time: "08:00", p_end_time: "09:00", }); assert.match(rpcError(invalidPeriod.error), /agentic_rectification_not_block_scan/); const widened = await service.rpc("widen_agentic_rectification_case_window", { p_user_id: userId, p_case_id: caseId, p_start_time: "07:30", p_end_time: "09:30", }); assert.equal(widened.error, null, rpcError(widened.error)); assert.deepEqual((widened.data as { candidate_range?: unknown }).candidate_range, { start_time: "07:30", end_time: "09:30", }); assert.equal( fixture.psql(`select adopted_credible_range is null from public.agentic_rectification_cases where id = '${caseId}'`), "t", ); const shrink = await service.rpc("widen_agentic_rectification_case_window", { p_user_id: userId, p_case_id: caseId, p_start_time: "08:00", p_end_time: "09:00", }); assert.match(rpcError(shrink.error), /agentic_rectification_invalid_widen_window/); const privileges = fixture.psql(` select concat_ws(':', has_function_privilege('service_role', 'public.set_agentic_rectification_case_stage(uuid,uuid,text)', 'EXECUTE'), has_function_privilege('anon', 'public.set_agentic_rectification_case_stage(uuid,uuid,text)', 'EXECUTE'), has_function_privilege('authenticated', 'public.write_agentic_rectification_block_scan(uuid,uuid,jsonb)', 'EXECUTE'), has_function_privilege('service_role', 'public.advance_agentic_rectification_case_from_block_scan(uuid,uuid,text,text)', 'EXECUTE'), has_function_privilege('service_role', 'public.widen_agentic_rectification_case_window(uuid,uuid,text,text)', 'EXECUTE'), has_function_privilege('anon', 'public.widen_agentic_rectification_case_window(uuid,uuid,text,text)', 'EXECUTE') ) `); assert.equal(privileges, "t:f:f:t:t:f"); } finally { try { await closeLocalPostgresDataPool( fixture.connectionUrl("service_runtime", "service-runtime-test-password"), ); } finally { fixture.stop(); } } });