Files
Jyotisha/frontend/tests/database-synastry-report-chat.test.ts
T
jesse-uxandCursor 3956519f33 feat(synastry): open a follow-up chat from one stored report section
The chat stays bound to that section's saved calculation, checks each chart fact before answering, and bills like an ordinary turn.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-19 21:44:25 +08:00

132 lines
6.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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));
const userId = "83000000-0000-4000-8000-000000000083";
const otherUserId = "83000000-0000-4000-8000-000000000183";
const personId = "83000000-0000-4000-8000-000000000283";
const partnerId = "83000000-0000-4000-8000-000000000383";
const reportId = "83000000-0000-4000-8000-000000000483";
const otherReportId = "83000000-0000-4000-8000-000000000583";
const sessionId = "83000000-0000-4000-8000-000000000683";
const requestId = "83000000-0000-4000-8000-000000000783";
const otherRequestId = "83000000-0000-4000-8000-000000000883";
const fingerprint = "a".repeat(64);
const skillHash = "d".repeat(64);
function dockerAvailable(): boolean {
return spawnSync("docker", ["version", "--format", "{{.Server.Version}}"], { encoding: "utf8", stdio: "ignore" }).status === 0;
}
function errorText(error: unknown): string {
if (!error || typeof error !== "object") return String(error);
const stderr = "stderr" in error ? String((error as { stderr?: unknown }).stderr ?? "") : "";
const stdout = "stdout" in error ? String((error as { stdout?: unknown }).stdout ?? "") : "";
return `${stderr}\n${stdout}\n${String(error)}`;
}
test("synastry chat binds an owned section, rejects another report, and stays readable after delete", {
skip: dockerAvailable() ? false : "docker unavailable on this host",
}, () => {
const fixture = startPostgresFixture();
try {
const migration = spawnSync(process.execPath, [runnerPath], {
encoding: "utf8",
env: { ...process.env, SCHEMA_DATABASE_URL: fixture.connectionUrl("schema_owner", "schema-owner-test-password") },
});
assert.equal(migration.status, 0, `${migration.stdout}${migration.stderr}`);
fixture.psql(`
insert into identity.users (id, name, email, email_verified, email_verified_at)
values
('${userId}', 'Fictional Synastry Chat', '${userId}@example.invalid', true, now()),
('${otherUserId}', 'Fictional Other', '${otherUserId}@example.invalid', true, now());
update public.profiles set credits = 10 where id = '${userId}';
insert into public.chart_profiles (id, user_id, role, profile)
values
('${personId}', '${userId}', 'other', '{}'::jsonb),
('${partnerId}', '${userId}', 'other', '{}'::jsonb);
`);
const insertReport = (id: string, owner: string, request: string, person: string, partner: string) => `
insert into public.personal_reports (
id, user_id, chart_profile_id, partner_chart_profile_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,
subject_low, subject_high, fingerprint_low, fingerprint_high, synastry_rules_version
) values (
'${id}', '${owner}', '${person}', '${partner}', '${request}', '${fingerprint}',
'synastry_full', 'generating', 'synastry_report.v1', 'default', array[]::text[], 'standard',
'jyotish-full-synastry-reading', '1.0.0', '${"e".repeat(40)}', '${skillHash}',
'${person}', '${partner}', '${fingerprint}', '${"b".repeat(64)}', 'bde5d615:marriage_match_product_v1'
);
`;
fixture.psql(insertReport(reportId, userId, requestId, personId, partnerId));
fixture.psql(`
update public.personal_reports
set status = 'ready', completed_at = now(), report_document = '{"names":{"a":"甲","b":"乙"}}'::jsonb,
calculation_hash = '${skillHash}', evidence_hash = '${skillHash}'
where id = '${reportId}';
update public.personal_report_jobs
set status = 'cancelled', finished_at = now()
where user_id = '${userId}' and status = 'queued';
`);
fixture.psql(insertReport(otherReportId, otherUserId, otherRequestId, personId, partnerId));
fixture.psql(`
insert into public.chat_sessions (
id, user_id, title, theme, messages, session_type,
synastry_report_id, synastry_section, synastry_partner_subject_id,
chart_profile_id, chart_profile_name, chart_profile_role
) values (
'${sessionId}', '${userId}', '甲 × 乙 · 合盘追问', 'marriage', '[]'::jsonb, 'synastry_report',
'${reportId}', 'climate', '${partnerId}',
'${personId}', '甲 × 乙', 'other'
);
`);
assert.throws(() => fixture.psql(`
insert into public.chat_sessions (
id, user_id, title, theme, messages, session_type,
synastry_report_id, synastry_section, synastry_partner_subject_id
) values (
'83000000-0000-4000-8000-000000000983', '${userId}', '越权', 'marriage', '[]'::jsonb, 'synastry_report',
'${otherReportId}', 'climate', '${partnerId}'
);
`), (error: unknown) => /synastry_report_not_owned/.test(errorText(error)));
assert.throws(() => fixture.psql(`
insert into public.chat_sessions (
id, user_id, title, theme, messages, session_type,
synastry_report_id, synastry_section, synastry_partner_subject_id
) values (
'83000000-0000-4000-8000-000000001083', '${userId}', '非法节', 'marriage', '[]'::jsonb, 'synastry_report',
'${reportId}', 'practices', '${partnerId}'
);
`), (error: unknown) => /synastry_section_invalid/.test(errorText(error)));
const reserved = fixture.psql(`
select reason from public.reserve_consultation_usage(
'${userId}', 'chat-83', '${sessionId}', 'model-a', 1
);
`);
assert.equal(reserved.includes("session_missing"), false);
fixture.psql(`delete from public.personal_reports where id = '${reportId}';`);
assert.equal(fixture.psql(`
select (synastry_report_id is null)::text || '|' || synastry_section || '|' || session_type
from public.chat_sessions where id = '${sessionId}';
`), "true|climate|synastry_report");
assert.equal(fixture.psql(`
select reason from public.reserve_consultation_usage(
'${userId}', 'chat-83-after-delete', '${sessionId}', 'model-a', 1
);
`), "session_missing");
} finally {
fixture.stop();
}
});