804 lines
32 KiB
TypeScript
804 lines
32 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 { loadLatestAgenticRectificationResult } from "../src/lib/rectification-agentic/session.ts";
|
|
import { startPostgresFixture } from "./helpers/postgres-fixture.ts";
|
|
|
|
const runnerPath = fileURLToPath(
|
|
new URL("../scripts/db-migrate.mjs", import.meta.url),
|
|
);
|
|
|
|
function rpcError(error: unknown): string {
|
|
if (!error || typeof error !== "object") return "";
|
|
const value = error as { message?: unknown };
|
|
return typeof value.message === "string" ? value.message : "";
|
|
}
|
|
|
|
function candidateIds(value: unknown): string[] {
|
|
if (!value || typeof value !== "object") return [];
|
|
const candidates = (value as { candidates?: unknown }).candidates;
|
|
if (!Array.isArray(candidates)) return [];
|
|
return candidates.flatMap((candidate) => {
|
|
if (!candidate || typeof candidate !== "object") return [];
|
|
const candidateId = (candidate as { candidate_id?: unknown }).candidate_id;
|
|
return typeof candidateId === "string" ? [candidateId] : [];
|
|
});
|
|
}
|
|
|
|
test("local PostgreSQL applies the reviewed business schema and serves authenticated business calls", 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);
|
|
assert.match(migration.stdout, /applied 20260715000000_account_credits\.sql/);
|
|
assert.match(migration.stdout, /applied 20260721150000_align_conversational_finance_domain\.sql/);
|
|
assert.match(migration.stdout, /applied 20260723010000_restore_conversational_message_history\.sql/);
|
|
assert.match(migration.stdout, /applied 20260723020000_mark_captured_conversational_messages\.sql/);
|
|
assert.match(migration.stdout, /applied 20260728010000_conversational_event_semantics\.sql/);
|
|
assert.match(migration.stdout, /applied 20260728020000_rectification_agent_v5\.sql/);
|
|
assert.match(migration.stdout, /applied 20260804010000_agentic_rectification_candidate_acceptance\.sql/);
|
|
assert.match(migration.stdout, /applied 20260804020000_preserve_reported_birth_time_on_candidate_acceptance\.sql/);
|
|
assert.match(migration.stdout, /applied 20260805010000_reconcile_admin_redemption_audit\.sql/);
|
|
assert.match(migration.stdout, /applied 20260805020000_reconcile_payment_admin_schema\.sql/);
|
|
assert.match(migration.stdout, /applied 20260805030000_reconcile_rectification_v4_conversational_turns\.sql/);
|
|
assert.match(migration.stdout, /applied 20260806000000_personal_reports\.sql/);
|
|
assert.match(migration.stdout, /applied 20260806010000_admin_rbac\.sql/);
|
|
assert.match(migration.stdout, /applied 20260806020000_billing_products_subscriptions\.sql/);
|
|
assert.match(migration.stdout, /applied 20260806030000_settle_order_usage_authorization\.sql/);
|
|
assert.match(migration.stdout, /applied 20260806040000_model_configuration\.sql/);
|
|
assert.match(migration.stdout, /applied 20260806050000_operations_feature_flags\.sql/);
|
|
assert.match(migration.stdout, /applied 20260811010000_consultation_status_service_role_read\.sql/);
|
|
assert.match(migration.stdout, /applied 20260812010000_agentic_rectification_v9_runtime\.sql/);
|
|
assert.match(migration.stdout, /applied 20260813010000_agentic_rectification_v9_agent_api\.sql/);
|
|
assert.match(migration.stdout, /applied 20260813060000_rectification_turn_regeneration\.sql/);
|
|
assert.match(migration.stdout, /applied 20260814020000_rectification_v10_runtime\.sql/);
|
|
assert.match(migration.stdout, /applied 20260814025000_personal_report_document_v2\.sql/);
|
|
assert.match(migration.stdout, /applied 20260814040000_personal_report_jobs_v2\.sql/);
|
|
|
|
assert.equal(
|
|
fixture.psql(`
|
|
select is_nullable || ':' || data_type
|
|
from information_schema.columns
|
|
where table_schema = 'public'
|
|
and table_name = 'birth_time_rectification_turns'
|
|
and column_name = 'user_message'
|
|
`),
|
|
"YES:text",
|
|
);
|
|
assert.equal(
|
|
fixture.psql(`
|
|
select is_nullable || ':' || data_type || ':' || column_default
|
|
from information_schema.columns
|
|
where table_schema = 'public'
|
|
and table_name = 'birth_time_rectification_turns'
|
|
and column_name = 'user_message_captured'
|
|
`),
|
|
"NO:boolean:f",
|
|
);
|
|
assert.equal(
|
|
fixture.psql(`
|
|
select
|
|
has_function_privilege(
|
|
'service_role',
|
|
'public.load_conversational_rectification_case_with_history(uuid, uuid)',
|
|
'execute'
|
|
) || ':' ||
|
|
has_function_privilege(
|
|
'authenticated',
|
|
'public.load_conversational_rectification_case_with_history(uuid, uuid)',
|
|
'execute'
|
|
)
|
|
`),
|
|
"true:f",
|
|
);
|
|
assert.equal(
|
|
fixture.psql(`
|
|
select
|
|
has_table_privilege('service_role', 'public.consultation_requests', 'select') || ':' ||
|
|
has_table_privilege('anon', 'public.consultation_requests', 'select') || ':' ||
|
|
has_table_privilege('authenticated', 'public.consultation_requests', 'select')
|
|
`),
|
|
"true:f:f",
|
|
);
|
|
|
|
assert.equal(
|
|
fixture.psql(`
|
|
select string_agg(tablename, ',' order by tablename)
|
|
from pg_tables
|
|
where schemaname = 'public'
|
|
`),
|
|
[
|
|
"admin_permissions",
|
|
"admin_role_permissions",
|
|
"admin_roles",
|
|
"admin_session_revocations",
|
|
"admin_user_roles",
|
|
"admin_users",
|
|
"agentic_rectification_candidate_decisions",
|
|
"agentic_rectification_candidates",
|
|
"agentic_rectification_case_conversation_summaries",
|
|
"agentic_rectification_cases",
|
|
"agentic_rectification_conversation_focuses",
|
|
"agentic_rectification_evidence",
|
|
"agentic_rectification_open_ledger",
|
|
"agentic_rectification_results",
|
|
"agentic_rectification_run_attempts",
|
|
"agentic_rectification_run_phases",
|
|
"agentic_rectification_skill_run_receipts",
|
|
"agentic_rectification_skill_upgrade_receipts",
|
|
"agentic_rectification_tool_receipts",
|
|
"agentic_rectification_turn_regenerations",
|
|
"agentic_rectification_turns",
|
|
"billing_products",
|
|
"birth_time_rectification_action_receipts",
|
|
"birth_time_rectification_agent_runs",
|
|
"birth_time_rectification_billing",
|
|
"birth_time_rectification_candidate_feature_snapshots",
|
|
"birth_time_rectification_cases",
|
|
"birth_time_rectification_diagnostics",
|
|
"birth_time_rectification_dynamic_state",
|
|
"birth_time_rectification_event_evidence",
|
|
"birth_time_rectification_handoff_attach_receipts",
|
|
"birth_time_rectification_handoff_settlements",
|
|
"birth_time_rectification_pending_evidence",
|
|
"birth_time_rectification_public_messages",
|
|
"birth_time_rectification_question_handoffs",
|
|
"birth_time_rectification_scoring_jobs",
|
|
"birth_time_rectification_turns",
|
|
"birth_time_rectification_v4_actions",
|
|
"birth_time_rectification_v4_candidate_snapshots",
|
|
"birth_time_rectification_v4_cases",
|
|
"birth_time_rectification_v4_event_revisions",
|
|
"birth_time_rectification_v4_events",
|
|
"birth_time_rectification_v4_handoff_attach_receipts",
|
|
"birth_time_rectification_v4_handoff_settlements",
|
|
"birth_time_rectification_v4_handoffs",
|
|
"birth_time_rectification_v4_jobs",
|
|
"birth_time_rectification_v4_turns",
|
|
"chart_profiles",
|
|
"chat_sessions",
|
|
"consultation_requests",
|
|
"credit_request_cancellations",
|
|
"credit_transactions",
|
|
"epay_settings",
|
|
"feature_flags",
|
|
"model_config_versions",
|
|
"model_configs",
|
|
"model_connection_test_evidence",
|
|
"model_providers",
|
|
"model_publish_events",
|
|
"notification_templates",
|
|
"payment_orders",
|
|
"payment_packages",
|
|
"personal_report_jobs",
|
|
"personal_reports",
|
|
"pricing_experiment_events",
|
|
"product_entitlements",
|
|
"profiles",
|
|
"redemption_attempts",
|
|
"redemption_codes",
|
|
"synastry_reports",
|
|
"usage_events",
|
|
"usage_ledger",
|
|
"usage_reservations",
|
|
"user_product_redemptions",
|
|
"user_subscriptions",
|
|
].join(","),
|
|
);
|
|
|
|
fixture.psqlAs(
|
|
"identity_runtime",
|
|
"identity-runtime-test-password",
|
|
`
|
|
insert into identity.users (name, email, email_verified, email_verified_at)
|
|
values ('Local User', 'local-user@example.com', true, now())
|
|
`,
|
|
);
|
|
const userId = fixture.psql(
|
|
"select id from identity.users where email = 'local-user@example.com'",
|
|
);
|
|
assert.equal(
|
|
fixture.psql(`select email from auth.users where id = '${userId}'`),
|
|
"local-user@example.com",
|
|
);
|
|
assert.equal(
|
|
fixture.psql(`select email || ':' || credits from public.profiles where id = '${userId}'`),
|
|
"local-user@example.com:0",
|
|
);
|
|
assert.equal(
|
|
fixture.psqlAs(
|
|
"app_runtime",
|
|
"app-runtime-test-password",
|
|
`set role authenticated;
|
|
select set_config('request.jwt.claim.sub', '${userId}', true);
|
|
select email from public.profiles where id = '${userId}'`,
|
|
),
|
|
`SET\n${userId}\nlocal-user@example.com`,
|
|
);
|
|
fixture.psql(`update public.profiles set birth_date = '1997-08-08' where id = '${userId}'`);
|
|
|
|
const local = createLocalPostgresDataClient(
|
|
fixture.connectionUrl("app_runtime", "app-runtime-test-password"),
|
|
{ id: userId, email: "local-user@example.com" },
|
|
);
|
|
const profile = await local.from("profiles")
|
|
.select("id,email,credits,birth_date,created_at")
|
|
.eq("id", userId)
|
|
.single();
|
|
assert.equal(profile.error, null);
|
|
const { created_at: createdAt, ...profileData } = profile.data as Record<string, unknown>;
|
|
assert.ok(createdAt instanceof Date);
|
|
assert.deepEqual(profileData, {
|
|
id: userId,
|
|
email: "local-user@example.com",
|
|
credits: 0,
|
|
birth_date: "1997-08-08",
|
|
});
|
|
const nonAbandonedProfiles = await local.from("profiles")
|
|
.select("id")
|
|
.neq("email", "not-local-user@example.com")
|
|
.single();
|
|
assert.equal(nonAbandonedProfiles.error, null);
|
|
assert.deepEqual(nonAbandonedProfiles.data, { id: userId });
|
|
const admin = createLocalPostgresDataClient(
|
|
fixture.connectionUrl("service_runtime", "service-runtime-test-password"),
|
|
null,
|
|
"service_role",
|
|
);
|
|
const adminProfile = await admin.from("profiles")
|
|
.select("id")
|
|
.eq("id", userId)
|
|
.single();
|
|
assert.equal(adminProfile.error, null);
|
|
assert.deepEqual(adminProfile.data, { id: userId });
|
|
|
|
const serviceScalar = (sql: string) => {
|
|
const output = fixture.psqlAs(
|
|
"service_runtime",
|
|
"service-runtime-test-password",
|
|
`set role service_role; ${sql}`,
|
|
);
|
|
return output.split("\n").at(-1) ?? "";
|
|
};
|
|
|
|
const terminalCaseId = "44444444-4444-4444-8444-444444444444";
|
|
const terminalSessionId = "55555555-5555-4555-8555-555555555555";
|
|
const terminalRequestId = "77777777-7777-4777-8777-777777777777";
|
|
const skillSha256 = "a".repeat(64);
|
|
const skillSourceCommit = "b".repeat(40);
|
|
fixture.psql(`
|
|
insert into public.chat_sessions (
|
|
id, user_id, title, theme, model_id, messages, session_type, updated_at
|
|
) values (
|
|
'${terminalSessionId}', '${userId}', 'V10 terminal finalize', 'general',
|
|
'test-model', '[]', 'birth_time_rectification', now()
|
|
);
|
|
insert into public.agentic_rectification_cases (
|
|
id, user_id, session_id, status, skill_name, skill_version,
|
|
skill_sha256, skill_source_commit, baseline_profile_fingerprint,
|
|
baseline_birth_snapshot, candidate_range
|
|
) values (
|
|
'${terminalCaseId}', '${userId}', '${terminalSessionId}', 'collecting_evidence',
|
|
'jyotish-birth-time-rectification', '10.0.0', '${skillSha256}',
|
|
'${skillSourceCommit}', 'v10-terminal-fixture', '{}', '{}'
|
|
);
|
|
`);
|
|
|
|
const terminalTurnId = serviceScalar(`
|
|
select result ->> 'turn_id'
|
|
from (
|
|
select public.append_agentic_rectification_turn(
|
|
'${userId}', '${terminalCaseId}', '确认这个时间', null,
|
|
'test-model', null, 'pending', '${terminalRequestId}'
|
|
) as result
|
|
) appended
|
|
`);
|
|
assert.match(terminalTurnId, /^[0-9a-f-]{36}$/);
|
|
assert.equal(
|
|
serviceScalar(`
|
|
select (result ->> 'turn_id') || ':' ||
|
|
(result ->> 'should_execute') || ':' ||
|
|
(result ->> 'idempotent')
|
|
from (
|
|
select public.append_agentic_rectification_turn(
|
|
'${userId}', '${terminalCaseId}', '确认这个时间', null,
|
|
'test-model', null, 'pending', '${terminalRequestId}'
|
|
) as result
|
|
) replayed
|
|
`),
|
|
`${terminalTurnId}:false:true`,
|
|
);
|
|
assert.equal(
|
|
fixture.psql(`
|
|
select count(*)
|
|
from public.agentic_rectification_turns
|
|
where case_id = '${terminalCaseId}' and request_id = '${terminalRequestId}'
|
|
`),
|
|
"1",
|
|
);
|
|
assert.throws(
|
|
() => serviceScalar(`
|
|
select public.append_agentic_rectification_turn(
|
|
'${userId}', '${terminalCaseId}', '不同的请求正文', null,
|
|
'test-model', null, 'pending', '${terminalRequestId}'
|
|
)
|
|
`),
|
|
/agentic_rectification_request_mismatch/,
|
|
);
|
|
for (const legacySignature of [
|
|
"public.append_agentic_rectification_turn(uuid,uuid,text,text,text,text,text)",
|
|
"public.finalize_agentic_rectification_turn(uuid,uuid,uuid,text,text)",
|
|
"public.finalize_agentic_rectification_turn(uuid,uuid,uuid,text,text,uuid)",
|
|
]) {
|
|
assert.equal(
|
|
fixture.psql(`select pg_catalog.has_function_privilege('service_role', '${legacySignature}', 'EXECUTE')`),
|
|
"f",
|
|
);
|
|
}
|
|
|
|
const terminalAttemptId = serviceScalar(`
|
|
select result ->> 'attempt_id'
|
|
from (
|
|
select public.create_agentic_rectification_run_attempt(
|
|
'${userId}', '${terminalCaseId}', '${terminalTurnId}', 1
|
|
) as result
|
|
) claimed
|
|
`);
|
|
fixture.psql(`
|
|
update public.agentic_rectification_cases
|
|
set status = 'closed', completed_at = now(), closed_reason = 'other'
|
|
where id = '${terminalCaseId}';
|
|
`);
|
|
assert.equal(
|
|
serviceScalar(`
|
|
select (result ->> 'turn_id') || ':' ||
|
|
(result ->> 'should_execute') || ':' ||
|
|
(result ->> 'idempotent')
|
|
from (
|
|
select public.append_agentic_rectification_turn(
|
|
'${userId}', '${terminalCaseId}', '确认这个时间', null,
|
|
'test-model', null, 'pending', '${terminalRequestId}'
|
|
) as result
|
|
) replayed
|
|
`),
|
|
`${terminalTurnId}:false:true`,
|
|
);
|
|
assert.throws(
|
|
() => serviceScalar(`
|
|
select public.append_agentic_rectification_turn(
|
|
'${userId}', '${terminalCaseId}', '关闭后新请求', null,
|
|
'test-model', null, 'pending', 'dddddddd-dddd-4ddd-8ddd-dddddddddddd'
|
|
)
|
|
`),
|
|
/agentic_rectification_case_terminal/,
|
|
);
|
|
assert.equal(
|
|
serviceScalar(`
|
|
select (result ->> 'attempt_id') || ':' ||
|
|
(result ->> 'should_execute') || ':' ||
|
|
(result ->> 'already_in_progress') || ':' ||
|
|
(result ->> 'idempotent')
|
|
from (
|
|
select public.create_agentic_rectification_run_attempt(
|
|
'${userId}', '${terminalCaseId}', '${terminalTurnId}', 1
|
|
) as result
|
|
) replayed
|
|
`),
|
|
`${terminalAttemptId}:false:true:true`,
|
|
);
|
|
assert.throws(
|
|
() => serviceScalar(`
|
|
select public.create_agentic_rectification_run_attempt(
|
|
'${userId}', '${terminalCaseId}', '${terminalTurnId}', 2
|
|
)
|
|
`),
|
|
/agentic_rectification_case_terminal/,
|
|
);
|
|
serviceScalar(`
|
|
select public.insert_agentic_rectification_skill_run_receipt(
|
|
'${userId}', '${terminalCaseId}', '${terminalTurnId}', '${terminalAttemptId}',
|
|
'turn', 'jyotish-birth-time-rectification', '10.0.0',
|
|
'${skillSha256}', '${skillSourceCommit}'
|
|
)
|
|
`);
|
|
serviceScalar(`
|
|
select public.insert_agentic_rectification_run_phase(
|
|
'${userId}', '${terminalCaseId}', '${terminalTurnId}',
|
|
'billing.settled', null, 1, '${terminalAttemptId}'
|
|
)
|
|
`);
|
|
serviceScalar(`
|
|
select public.insert_agentic_rectification_run_phase(
|
|
'${userId}', '${terminalCaseId}', '${terminalTurnId}',
|
|
'run.completed', null, 2, '${terminalAttemptId}'
|
|
)
|
|
`);
|
|
assert.equal(
|
|
serviceScalar(`
|
|
select (result ->> 'status') || ':' || (result ->> 'idempotent')
|
|
from (
|
|
select public.finalize_agentic_rectification_run_attempt(
|
|
'${userId}', '${terminalCaseId}', '${terminalTurnId}', '${terminalAttemptId}',
|
|
'completed', null, '{"inputTokens":11,"outputTokens":7}'::jsonb
|
|
) as result
|
|
) finalized
|
|
`),
|
|
"completed:false",
|
|
);
|
|
assert.equal(
|
|
serviceScalar(`
|
|
select (result ->> 'status') || ':' || (result ->> 'idempotent')
|
|
from (
|
|
select public.finalize_agentic_rectification_turn(
|
|
'${userId}', '${terminalCaseId}', '${terminalTurnId}', '${terminalAttemptId}',
|
|
'completed', '已确认并完成', '${terminalAttemptId}'
|
|
) as result
|
|
) finalized
|
|
`),
|
|
"completed:false",
|
|
);
|
|
assert.throws(
|
|
() => serviceScalar(`
|
|
select public.finalize_agentic_rectification_turn(
|
|
'${userId}', '${terminalCaseId}', '${terminalTurnId}', '${terminalAttemptId}',
|
|
'failed', null, null
|
|
)
|
|
`),
|
|
/agentic_rectification_turn_already_completed/,
|
|
);
|
|
assert.equal(
|
|
fixture.psql(`
|
|
select status || ':' || assistant_message || ':' || successful_attempt_id
|
|
from public.agentic_rectification_turns
|
|
where id = '${terminalTurnId}'
|
|
`),
|
|
`completed:已确认并完成:${terminalAttemptId}`,
|
|
);
|
|
|
|
const supersededCaseId = "88888888-8888-4888-8888-888888888888";
|
|
const supersededSessionId = "99999999-9999-4999-8999-999999999999";
|
|
const supersededRequestId = "cccccccc-cccc-4ccc-8ccc-cccccccccccc";
|
|
fixture.psql(`
|
|
insert into public.chat_sessions (
|
|
id, user_id, title, theme, model_id, messages, session_type, updated_at
|
|
) values (
|
|
'${supersededSessionId}', '${userId}', 'V10 superseded attempt', 'general',
|
|
'test-model', '[]', 'birth_time_rectification', now()
|
|
);
|
|
insert into public.agentic_rectification_cases (
|
|
id, user_id, session_id, status, skill_name, skill_version,
|
|
skill_sha256, skill_source_commit, baseline_profile_fingerprint,
|
|
baseline_birth_snapshot, candidate_range
|
|
) values (
|
|
'${supersededCaseId}', '${userId}', '${supersededSessionId}', 'collecting_evidence',
|
|
'jyotish-birth-time-rectification', '10.0.0', '${skillSha256}',
|
|
'${skillSourceCommit}', 'v10-superseded-fixture', '{}', '{}'
|
|
);
|
|
`);
|
|
const supersededTurnId = serviceScalar(`
|
|
select result ->> 'turn_id'
|
|
from (
|
|
select public.append_agentic_rectification_turn(
|
|
'${userId}', '${supersededCaseId}', '继续核对', null,
|
|
'test-model', null, 'pending', '${supersededRequestId}'
|
|
) as result
|
|
) appended
|
|
`);
|
|
const oldAttemptId = serviceScalar(`
|
|
select result ->> 'attempt_id'
|
|
from (
|
|
select public.create_agentic_rectification_run_attempt(
|
|
'${userId}', '${supersededCaseId}', '${supersededTurnId}', 1
|
|
) as result
|
|
) claimed
|
|
`);
|
|
serviceScalar(`
|
|
select public.finalize_agentic_rectification_run_attempt(
|
|
'${userId}', '${supersededCaseId}', '${supersededTurnId}', '${oldAttemptId}',
|
|
'retryable', 'stream_unfinished', '{}'::jsonb
|
|
)
|
|
`);
|
|
const latestAttemptId = serviceScalar(`
|
|
select result ->> 'attempt_id'
|
|
from (
|
|
select public.create_agentic_rectification_run_attempt(
|
|
'${userId}', '${supersededCaseId}', '${supersededTurnId}', 2
|
|
) as result
|
|
) claimed
|
|
`);
|
|
assert.notEqual(latestAttemptId, oldAttemptId);
|
|
assert.throws(
|
|
() => serviceScalar(`
|
|
select public.finalize_agentic_rectification_turn(
|
|
'${userId}', '${supersededCaseId}', '${supersededTurnId}', '${oldAttemptId}',
|
|
'retryable', null, null
|
|
)
|
|
`),
|
|
/agentic_rectification_attempt_superseded/,
|
|
);
|
|
|
|
const sessionId = "11111111-1111-4111-8111-111111111111";
|
|
const inserted = await local.from("chat_sessions").insert({
|
|
id: sessionId,
|
|
user_id: userId,
|
|
title: "Local conversation",
|
|
theme: "general",
|
|
model_id: "test-model",
|
|
messages: [],
|
|
session_type: "consultation",
|
|
rectification_case_id: null,
|
|
updated_at: new Date().toISOString(),
|
|
}).select("id").single();
|
|
assert.equal(inserted.error, null);
|
|
assert.deepEqual(inserted.data, { id: sessionId });
|
|
|
|
const beforeTomorrow = await local
|
|
.from("chat_sessions")
|
|
.select("id")
|
|
.eq("user_id", userId)
|
|
.eq("session_type", "consultation")
|
|
.lte("updated_at", new Date(Date.now() + 86_400_000).toISOString())
|
|
.single();
|
|
assert.equal(beforeTomorrow.error, null);
|
|
assert.deepEqual(beforeTomorrow.data, { id: sessionId });
|
|
|
|
const matchingTitle = await local
|
|
.from("chat_sessions")
|
|
.select("id")
|
|
.eq("user_id", userId)
|
|
.like("title", "Local%")
|
|
.single();
|
|
assert.equal(matchingTitle.error, null);
|
|
assert.deepEqual(matchingTitle.data, { id: sessionId });
|
|
|
|
const injectedLike = await local
|
|
.from("chat_sessions")
|
|
.select("id")
|
|
.eq("user_id", userId)
|
|
.like("title", "Local%' OR true --");
|
|
assert.equal(injectedLike.error, null);
|
|
assert.deepEqual(injectedLike.data, []);
|
|
|
|
const rectificationSessionId = "22222222-2222-4222-8222-222222222222";
|
|
fixture.psql(`
|
|
update public.profiles
|
|
set birth_date = '1997-08-08',
|
|
reported_birth_time = '05:00',
|
|
birth_time_source = 'family_exact',
|
|
uncertainty_before_minutes = 10,
|
|
uncertainty_after_minutes = 10,
|
|
latitude = 36.420487,
|
|
longitude = 114.209936,
|
|
timezone_offset = 8,
|
|
birth_time_status = 'reported'
|
|
where id = '${userId}';
|
|
insert into public.chat_sessions (id, user_id, title, theme, model_id, messages, session_type, updated_at)
|
|
values ('${rectificationSessionId}', '${userId}', 'Rectification', 'general', 'test-model', '[]', 'birth_time_rectification', now());
|
|
insert into public.agentic_rectification_results (
|
|
id, user_id, session_id, engine_result_id, canonical_input_hash, algorithm_version,
|
|
candidate_range, candidates, overall_confidence, selection_allowed, confirmation_allowed,
|
|
representative_time, baseline_birth_date, baseline_reported_birth_time, baseline_birth_time_source,
|
|
baseline_uncertainty_before_minutes, baseline_uncertainty_after_minutes, baseline_latitude,
|
|
baseline_longitude, baseline_timezone_offset
|
|
) values (
|
|
'33333333-3333-4333-8333-333333333333', '${userId}', '${rectificationSessionId}',
|
|
'engine-result-1', 'canonical-hash-1', 'test-v1', '{}',
|
|
'[{"rank":1,"time":"04:55","relative_support":60,"tied_minute_count":1},{"rank":2,"time":"05:07","relative_support":40,"tied_minute_count":1}]',
|
|
'medium', true, false, '04:55', '1997-08-08', '05:00', 'family_exact', 10, 10,
|
|
36.420487, 114.209936, 8
|
|
);
|
|
`);
|
|
const latestCandidate = await loadLatestAgenticRectificationResult(
|
|
admin as never,
|
|
userId,
|
|
rectificationSessionId,
|
|
);
|
|
assert.equal(latestCandidate?.resultId, "33333333-3333-4333-8333-333333333333");
|
|
assert.equal(latestCandidate?.selectionAllowed, true);
|
|
assert.deepEqual(latestCandidate?.candidates.map(({ time, relative_support }) => ({ time, relative_support })), [
|
|
{ time: "04:55", relative_support: 60 },
|
|
{ time: "05:07", relative_support: 40 },
|
|
]);
|
|
assert.equal(
|
|
fixture.psql(`
|
|
select
|
|
not has_function_privilege('service_role', 'public.accept_agentic_rectification_candidate(uuid,uuid,uuid,time without time zone)', 'EXECUTE')
|
|
and has_function_privilege('service_role', 'public.accept_agentic_rectification_candidate_for_case_v2(uuid,uuid,uuid,uuid,uuid)', 'EXECUTE')
|
|
`),
|
|
"t",
|
|
);
|
|
assert.throws(
|
|
() => fixture.psqlAs(
|
|
"service_runtime",
|
|
"service-runtime-test-password",
|
|
`set role service_role;
|
|
select public.accept_agentic_rectification_candidate(
|
|
'${userId}', '${rectificationSessionId}', '33333333-3333-4333-8333-333333333333', '04:55'
|
|
)`,
|
|
),
|
|
/permission denied for function accept_agentic_rectification_candidate/,
|
|
);
|
|
assert.equal(
|
|
fixture.psql(`select coalesce(to_char(active_birth_time, 'HH24:MI'), 'null') || ':' || birth_time_status || ':' || to_char(reported_birth_time, 'HH24:MI') from public.profiles where id = '${userId}'`),
|
|
"null:reported:05:00",
|
|
);
|
|
|
|
const rectificationCaseId = "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa";
|
|
const baselineBirthSnapshot = {
|
|
birth_date: "1997-08-08",
|
|
reported_birth_time: "05:00",
|
|
active_birth_time: null,
|
|
birth_time_source: "family_exact",
|
|
birth_time_period: null,
|
|
uncertainty_before_minutes: 10,
|
|
uncertainty_after_minutes: 10,
|
|
latitude: 36.420487,
|
|
longitude: 114.209936,
|
|
timezone_offset: 8,
|
|
};
|
|
const candidateRange = { start_time: "04:50", end_time: "05:10" };
|
|
fixture.psql(`
|
|
insert into public.agentic_rectification_cases (
|
|
id, user_id, session_id, status, skill_name, skill_version,
|
|
baseline_profile_fingerprint, baseline_birth_snapshot, candidate_range
|
|
) values (
|
|
'${rectificationCaseId}', '${userId}', '${rectificationSessionId}', 'candidate_ready',
|
|
'jyotish-birth-time-rectification', '10.0.0', '${"e".repeat(64)}',
|
|
'${JSON.stringify(baselineBirthSnapshot)}'::jsonb,
|
|
'${JSON.stringify(candidateRange)}'::jsonb
|
|
)
|
|
`);
|
|
|
|
const persisted = await admin.rpc("persist_agentic_rectification_candidate_v2", {
|
|
p_user_id: userId,
|
|
p_case_id: rectificationCaseId,
|
|
p_engine_result_id: "database-local-business-v2",
|
|
p_evidence_ledger_fingerprint: "f".repeat(64),
|
|
p_candidate_range_fingerprint: "1".repeat(64),
|
|
p_skill_version: "10.0.0",
|
|
p_algorithm_version: "database-local-business-v2",
|
|
p_event_contract_version: "rectification-event-contract-v2",
|
|
p_decision_policy_version: "decision-policy-v2",
|
|
p_candidate_range: candidateRange,
|
|
p_candidates: [
|
|
{ rank: 1, time: "04:55", relative_support: 60, tied_minute_count: 1 },
|
|
{ rank: 2, time: "05:07", relative_support: 40, tied_minute_count: 1 },
|
|
],
|
|
p_decision_receipt: {
|
|
display_allowed: true,
|
|
accept_allowed: true,
|
|
confirm_allowed: false,
|
|
representative_time: "04:55",
|
|
overall_confidence: "medium",
|
|
margin_percent: 20,
|
|
},
|
|
p_execution_ledger: [
|
|
{ phase: "candidate.score", status: "completed", engine: "fixture-engine" },
|
|
{ phase: "decision.evaluate", status: "completed", policy: "decision-policy-v2" },
|
|
],
|
|
});
|
|
assert.equal(persisted.error, null, rpcError(persisted.error));
|
|
const persistedRow = persisted.data as Record<string, unknown>;
|
|
const v2ResultId = String(persistedRow.result_id);
|
|
const [firstCandidateId, secondCandidateId] = candidateIds(persistedRow);
|
|
assert.ok(firstCandidateId);
|
|
assert.ok(secondCandidateId);
|
|
|
|
const firstAcceptRequestId = "bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb";
|
|
const firstAccepted = await admin.rpc("accept_agentic_rectification_candidate_for_case_v2", {
|
|
p_user_id: userId,
|
|
p_case_id: rectificationCaseId,
|
|
p_result_id: v2ResultId,
|
|
p_candidate_id: firstCandidateId,
|
|
p_request_id: firstAcceptRequestId,
|
|
});
|
|
assert.equal(firstAccepted.error, null, rpcError(firstAccepted.error));
|
|
assert.deepEqual(
|
|
{
|
|
saved_time: (firstAccepted.data as Record<string, unknown>).saved_time,
|
|
status: (firstAccepted.data as Record<string, unknown>).status,
|
|
case_status: (firstAccepted.data as Record<string, unknown>).case_status,
|
|
idempotent: (firstAccepted.data as Record<string, unknown>).idempotent,
|
|
},
|
|
{ saved_time: "04:55", status: "accepted", case_status: "candidate_accepted", idempotent: false },
|
|
);
|
|
assert.equal(
|
|
fixture.psql(`
|
|
select to_char(active_birth_time, 'HH24:MI') || ':' ||
|
|
to_char(birth_time, 'HH24:MI') || ':' || birth_time_status || ':' ||
|
|
to_char(reported_birth_time, 'HH24:MI')
|
|
from public.profiles where id = '${userId}'
|
|
`),
|
|
"04:55:04:55:accepted:05:00",
|
|
);
|
|
|
|
const firstReplay = await admin.rpc("accept_agentic_rectification_candidate_for_case_v2", {
|
|
p_user_id: userId,
|
|
p_case_id: rectificationCaseId,
|
|
p_result_id: v2ResultId,
|
|
p_candidate_id: firstCandidateId,
|
|
p_request_id: firstAcceptRequestId,
|
|
});
|
|
assert.equal(firstReplay.error, null, rpcError(firstReplay.error));
|
|
assert.equal((firstReplay.data as Record<string, unknown>).idempotent, true);
|
|
|
|
const secondAccepted = await admin.rpc("accept_agentic_rectification_candidate_for_case_v2", {
|
|
p_user_id: userId,
|
|
p_case_id: rectificationCaseId,
|
|
p_result_id: v2ResultId,
|
|
p_candidate_id: secondCandidateId,
|
|
p_request_id: "cccccccc-cccc-4ccc-8ccc-cccccccccccc",
|
|
});
|
|
assert.equal(secondAccepted.error, null, rpcError(secondAccepted.error));
|
|
assert.deepEqual(
|
|
{
|
|
saved_time: (secondAccepted.data as Record<string, unknown>).saved_time,
|
|
status: (secondAccepted.data as Record<string, unknown>).status,
|
|
case_status: (secondAccepted.data as Record<string, unknown>).case_status,
|
|
idempotent: (secondAccepted.data as Record<string, unknown>).idempotent,
|
|
},
|
|
{ saved_time: "05:07", status: "accepted", case_status: "candidate_accepted", idempotent: false },
|
|
);
|
|
assert.equal(
|
|
fixture.psql(`select to_char(active_birth_time, 'HH24:MI') || ':' || to_char(birth_time, 'HH24:MI') || ':' || to_char(reported_birth_time, 'HH24:MI') from public.profiles where id = '${userId}'`),
|
|
"05:07:05:07:05:00",
|
|
);
|
|
|
|
assert.equal(
|
|
fixture.psql(`select invalidated_at is null from public.agentic_rectification_results where id = '33333333-3333-4333-8333-333333333333'`),
|
|
"t",
|
|
);
|
|
fixture.psql(`update public.profiles set reported_birth_time = '05:01' where id = '${userId}'`);
|
|
assert.equal(
|
|
fixture.psql(`select invalidated_at is not null from public.agentic_rectification_results where id = '33333333-3333-4333-8333-333333333333'`),
|
|
"t",
|
|
);
|
|
assert.equal(
|
|
fixture.psql(`select invalidated_at is not null from public.agentic_rectification_results where id = '${v2ResultId}'`),
|
|
"t",
|
|
);
|
|
const expiredAccept = await admin.rpc("accept_agentic_rectification_candidate_for_case_v2", {
|
|
p_user_id: userId,
|
|
p_case_id: rectificationCaseId,
|
|
p_result_id: v2ResultId,
|
|
p_candidate_id: firstCandidateId,
|
|
p_request_id: "dddddddd-dddd-4ddd-8ddd-dddddddddddd",
|
|
});
|
|
assert.match(rpcError(expiredAccept.error), /agentic_rectification_candidate_expired/);
|
|
assert.equal(
|
|
await loadLatestAgenticRectificationResult(admin as never, userId, rectificationSessionId),
|
|
null,
|
|
);
|
|
|
|
fixture.psql(`
|
|
insert into public.redemption_codes (code_hash, code_mask, credits)
|
|
values ('${"a".repeat(64)}', 'JYOTISH-****-TEST', 3)
|
|
`);
|
|
const redeemed = await local.rpc("redeem_code", {
|
|
p_code_hash: "a".repeat(64),
|
|
});
|
|
assert.equal(redeemed.error, null);
|
|
assert.deepEqual(redeemed.data, [{ success: true, credits: 3, awarded_credits: 3, error_code: null }]);
|
|
} finally {
|
|
await closeLocalPostgresDataPools();
|
|
fixture.stop();
|
|
}
|
|
});
|