Files
Jyotisha/frontend/tests/rectification-history-open-20260909.test.ts
T
Jesse_ChenandCursor a31a5e2426
Independent Staging Quality Gate / validate (push) Successful in 9m46s
Independent Staging Quality Gate / publish (push) Successful in 32m4s
fix(rectification): open history cases with their bound skill identity (BUG-621)
Session open was sending the live registry version into v2, so every Skill
bump rejected older cases with an unmapped 500 and no error on the clicked row.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-09 19:35:23 +08:00

71 lines
3.8 KiB
TypeScript

import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import test from "node:test";
import { cssDeclarations } from "./css-contract-test-support.ts";
import { homeSurface as page } from "./home-surface.ts";
const read = (relativePath: string) => readFileSync(new URL(relativePath, import.meta.url), "utf8");
const sidebar = read("../src/components/app-sidebar.tsx");
const sidebarRow = read("../src/components/sidebar-session-row.tsx");
const starter = read("../src/components/starter-home.tsx");
const styles = read("../src/app/globals.css");
const pageSource = read("../src/app/page.tsx");
const hook = read("../src/hooks/use-rectification-surface.ts");
const sessions = read("../src/hooks/use-session-management.ts");
const caseService = read("../src/lib/rectification-agentic/v9/case-service.ts");
test("history session open uses the bound skill identity instead of the live registry version", () => {
// 原值:openRectificationCase 对 session 也把 resolveActiveSkillPackage() 交给 v2
// 新值:session 先 v1 取 Case,再读绑定身份,注册表核验后把绑定身份交给 v2
// 决策 1:打开 RPC 不再要求等于当前版本
assert.match(caseService, /request\.intent === "session"/);
assert.match(caseService, /open_agentic_rectification_case"/);
assert.match(caseService, /get_agentic_rectification_skill_identity"/);
assert.match(caseService, /resolveExactSkillPackage\(/);
assert.match(caseService, /skill_identity_missing/);
const homepageOpen = caseService.slice(
caseService.indexOf("export async function openRectificationCase"),
caseService.indexOf("export async function getRectificationEntrySummary"),
);
assert.match(homepageOpen, /intent === "homepage" \|\| request\.intent === "new"/);
assert.match(homepageOpen, /open_agentic_rectification_case_v2/);
});
test("skill_identity_mismatch maps to 409 instead of the 500 fallback", () => {
// 原值:KNOWN_RPC_ERROR_CODES 没有 mismatch,落到 校正服务暂时不可用
// 新值:409 skill_identity_mismatch,文案要求刷新
// 决策 2:修完决策 1 后不应再出现;映射只为不再露 500
assert.match(
caseService,
/"agentic_rectification_skill_identity_mismatch", \{ status: 409, code: "skill_identity_mismatch", message: "该校正绑定的规则版本与当前不一致,请刷新后重试" \}/,
);
});
test("a history-list open failure shows under the clicked row, not on the starter card", () => {
// 原值:rectificationError 只由 starter-home 画;selectSession 不切会话,表现为点了没反应
// 新值:session 失败绑到被点的那一行;首页起始卡不重复同一条
// 决策 3:错误就地显示
assert.match(pageSource, /const \[rectificationErrorSessionId, setRectificationErrorSessionId\]/);
assert.match(
pageSource,
/rectificationError=\{rectificationErrorSessionId \? "" : rectificationError\}/,
);
assert.match(pageSource, /openErrorSessionId=\{rectificationErrorSessionId\}/);
assert.match(
pageSource,
/openErrorMessage=\{rectificationErrorSessionId \? rectificationErrorMessage : ""\}/,
);
assert.match(starter, /rectificationError && !rectificationSurfaceOpen && \(/);
assert.match(sidebar, /openErrorSessionId/);
assert.match(sidebarRow, /error \? \(/);
assert.match(sidebarRow, /className="session-open-error"/);
assert.match(sidebarRow, /role="alert"/);
assert.match(hook, /setRectificationErrorSessionId\(intent === "session" \? exactSessionId : null\)/);
assert.match(sessions, /setRectificationErrorSessionId\(null\)/);
const notice = cssDeclarations(".session-open-error", styles);
assert.match(notice, /color:\s*var\(--color-danger\)/);
assert.match(notice, /font-size:\s*var\(--type-overline\)/);
assert.match(page, /openErrorSessionId=\{rectificationErrorSessionId\}/);
});