Files
Jyotisha/frontend/tests/rectification-product-domain-route.test.ts
T
Jesse_ChenandClaude Opus 5.5 eded9c724e test(rectification): isolate the open-route domain test from the account-deletion freeze merged on staging; local Docker DB checklist (BUG-1131)
Staging 96adbce3 added refuseWhenAccountDeletionPending to the open route; the
domain test's call counter saw its service RPC. The freeze has its own suite,
so the test mocks it as not frozen; the invalid-source assertion is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017eEAG8HD3mm8gsKXgk8uU8
2026-10-01 08:04:05 +08:00

110 lines
7.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 { parseOpenRectificationCaseRequest } from "../src/lib/rectification-agentic/v9/open-request.ts";
import { openRectificationRequestBody } from "../src/lib/rectification-entry.ts";
const sourceId = "11111111-1111-4111-8111-111111111111";
const requestId = "22222222-2222-4222-8222-222222222222";
test("handoff accepts only a source reference, never a browser domain or birth facts", () => {
const body = openRectificationRequestBody("homepage", null, sourceId);
assert.equal(body.sourceSessionId, sourceId);
assert.ok(parseOpenRectificationCaseRequest(body));
for (const patch of [{ domain: "career" }, { productDomain: "marriage" }, { birth_date: "2000-06-14" }, { scores: [] }, { sourceSessionId: "not-a-uuid" }]) {
assert.equal(parseOpenRectificationCaseRequest({ ...body, ...patch }), null);
}
assert.equal(parseOpenRectificationCaseRequest({ intent: "session", requestId, sessionId: sourceId, sourceSessionId: sourceId }), null);
assert.equal(parseOpenRectificationCaseRequest({ intent: "new", requestId, sourceSessionId: sourceId }), null);
assert.equal(openRectificationRequestBody("session", sourceId, sourceId).sourceSessionId, undefined);
});
// Fictional owned-session/RPC controls exercising the actual POST and case service,
// not a PostgreSQL or engine calibration claim.
test("production open route reads owned consultation themes and fixes new-case targets without changing exact-session opens", () => {
const script = String.raw`
import assert from 'node:assert/strict';
import { mock } from 'node:test';
import { pathToFileURL } from 'node:url';
import { resolveActiveSkillPackage } from './src/lib/skill-package-registry.ts';
import { targetChartsForDomain } from './src/lib/rectification-agentic/core/segment-summary.ts';
const userId='11111111-1111-4111-8111-111111111111';
const sourceId='22222222-2222-4222-8222-222222222222';
const sessionId='33333333-3333-4333-8333-333333333333';
const caseId='44444444-4444-4444-8444-444444444444';
const skill=resolveActiveSkillPackage('jyotish-birth-time-rectification');
let source={id:sourceId,user_id:userId,session_type:'consultation',theme:'marriage'};
let persistedDomain=null; let sourceError=null; let queries=[]; let calls=[];
const response=()=>({case_id:caseId,session_id:sessionId,status:'draft',disposition:'created',should_start_opening:false,skill_version:skill.version});
const accounting={
from(table){assert.equal(table,'profiles'); return {select(){return this},eq(){return this},single:async()=>({error:null,data:{
birth_date:'2000-06-14',birth_place_label:'Synthetic Test City',reported_birth_time:'09:10',birth_time_source:'family_exact',
latitude:40,longitude:-74,timezone_id:'America/New_York',timezone_offset:-4,ayanamsa:'raman'
}})}},
rpc:async(fn,args)=>{
calls.push({fn,args});
if(fn==='get_agentic_rectification_skill_identity')return {error:null,data:{skill_name:skill.name,skill_version:skill.version,skill_sha256:skill.sha256,skill_source_commit:skill.sourceCommit}};
if(fn==='open_agentic_rectification_case')return {error:null,data:response()};
if(fn==='open_agentic_rectification_case_v2'){
if(args.p_intent==='session')assert.ok(!Object.hasOwn(args,'p_product_domain'));
else if(persistedDomain===null)persistedDomain=args.p_product_domain;
return {error:null,data:response()};
}
throw new Error('unexpected RPC '+fn);
}
};
mock.module('server-only',{namedExports:{}});
mock.module('@/lib/supabase/server',{namedExports:{createServerSupabaseClient:async()=>({
auth:{getUser:async()=>({data:{user:{id:userId}},error:null})},
from(table){assert.equal(table,'chat_sessions'); const filters={}; return {
select(columns){assert.equal(columns,'id,user_id,session_type,theme'); return this},
eq(key,value){filters[key]=value; return this},
maybeSingle:async()=>{queries.push(filters); assert.equal(filters.id,sourceId); assert.equal(filters.user_id,userId);
return {data:source,error:sourceError}}
}}
})}});
mock.module('@/lib/supabase/admin',{namedExports:{createAdminSupabaseClient:()=>accounting}});
mock.module('@/lib/product-access',{namedExports:{isProductEnabled:async()=>true}});
// Account-deletion freeze (staging 96adbce3) has its own suite; here the account is not frozen.
mock.module('@/lib/account-deletion-server',{namedExports:{refuseWhenAccountDeletionPending:async()=>null}});
const {POST}=await import(pathToFileURL(process.cwd()+'/src/app/api/rectification/cases/open/route.ts').href);
const post=async(body)=>POST(new Request('https://example.invalid/api/rectification/cases/open',{method:'POST',headers:{'content-type':'application/json'},body:JSON.stringify(body)}));
const body={intent:'homepage',requestId:'55555555-5555-4555-8555-555555555555',sourceSessionId:sourceId};
for(const [theme,domain,targets] of [
['marriage','marriage',['D1','D9']],['career','career',['D1','D10']],['general','general',['D1','D9','D10']],
['wealth','finance',['D1','D9','D10']],['migration','relocation',['D1','D9','D10']],['annual','general',['D1','D9','D10']],
['health','health',['D1','D9','D10']],['education','education',['D1','D9','D10']],['family','family',['D1','D9','D10']],['timing','general',['D1','D9','D10']]
]){
source.theme=theme; persistedDomain=null; calls=[];
const result=await post(body); assert.equal(result.status,200,await result.text());
assert.equal(persistedDomain,domain); assert.deepEqual(targetChartsForDomain(persistedDomain),targets);
assert.equal(calls.filter(c=>c.fn==='open_agentic_rectification_case_v2').length,1);
assert.ok(!calls.some(c=>/initialize_agentic_rectification_domain|focus|billing/.test(c.fn)));
}
source.theme='marriage'; persistedDomain=null; assert.equal((await post(body)).status,200);
source.theme='career'; assert.equal((await post(body)).status,200); assert.equal(persistedDomain,'marriage','new source theme cannot rewrite persisted case domain');
for(const [replacement,status,code] of [
[null,404,'source_session_not_found'],
[{...source,user_id:'66666666-6666-4666-8666-666666666666'},404,'source_session_not_found'],
[{...source,session_type:'birth_time_rectification'},400,'source_session_not_consultation'],
[{...source,theme:'relationship'},400,'source_session_invalid_theme'],
[{...source,theme:'D1'},400,'source_session_invalid_theme']
]){
source=replacement; calls=[]; const result=await post(body); assert.equal(result.status,status); assert.equal((await result.json()).code,code);
assert.equal(calls.length,0,'invalid source must not read profile or open a case');
}
sourceError={message:'synthetic unavailable'}; const unavailable=await post(body); assert.equal(unavailable.status,503); sourceError=null;
calls=[]; queries=[]; persistedDomain=null;
const general=await post({intent:'homepage',requestId:body.requestId}); assert.equal(general.status,200); assert.equal(persistedDomain,'general'); assert.equal(queries.length,0);
calls=[]; queries=[];
const exact=await post({intent:'session',sessionId,requestId:body.requestId}); assert.equal(exact.status,200);
assert.equal(queries.length,0); assert.ok(!Object.hasOwn(calls.find(c=>c.fn==='open_agentic_rectification_case_v2').args,'p_product_domain'));
assert.equal((await post({...body,domain:'career'})).status,400);
`;
const result = spawnSync(process.execPath, ["--experimental-test-module-mocks", "--import", "tsx", "--input-type=module", "--eval", script], {
cwd: fileURLToPath(new URL("../", import.meta.url)), encoding: "utf8", timeout: 60_000,
});
assert.equal(result.status, 0, result.stderr + result.stdout);
});