NODEDC_1C/llm_normalizer/backend/tests/lifecycleRuntime.test.ts

163 lines
5.3 KiB
TypeScript

import { describe, expect, it } from "vitest";
import type { CandidateEvidenceItem, ProblemUnit } from "../src/types/stage2ProblemUnits";
import { enrichProblemUnitLifecycle, rankLifecycleProblemUnits, resolveLifecycle } from "../src/services/lifecycleRuntime";
function buildBaseProblemUnit(input: {
id: string;
type: ProblemUnit["problem_unit_type"];
expectedState?: string;
actualState?: string;
failedEdge?: string;
impactCloseRisk?: boolean;
severity?: number;
confidence?: number;
}): ProblemUnit {
const severityScore = input.severity ?? 0.62;
const confidenceScore = input.confidence ?? 0.58;
const severityGrade: ProblemUnit["severity"]["grade"] = severityScore >= 0.7 ? "high" : severityScore >= 0.4 ? "medium" : "low";
const confidenceGrade: ProblemUnit["confidence"]["grade"] =
confidenceScore >= 0.75 ? "high" : confidenceScore >= 0.45 ? "medium" : "low";
return {
schema_version: "problem_unit_v0_1",
problem_unit_id: input.id,
problem_unit_type: input.type,
title: "Synthetic unit",
mechanism_summary: "Synthetic mechanism",
business_defect_class: "broken_lifecycle",
severity: {
score: Number(severityScore.toFixed(2)),
grade: severityGrade
},
confidence: {
score: Number(confidenceScore.toFixed(2)),
grade: confidenceGrade
},
affected_entities: ["Document:DOC-1"],
affected_documents: ["Document:DOC-1"],
affected_postings: [],
affected_accounts: ["51", "60"],
affected_counterparties: [],
affected_contracts: [],
...(input.expectedState
? {
expected_state: input.expectedState
}
: {}),
...(input.actualState
? {
actual_state: input.actualState
}
: {}),
...(input.failedEdge
? {
failed_expected_edge: input.failedEdge
}
: {}),
...(input.impactCloseRisk
? {
period_impact: {
is_period_sensitive: true,
impact_class: "close_risk" as const
}
}
: {}),
evidence_pack: ["cand-1"],
entity_backlinks: [{ entity: "Document", id: "DOC-1" }],
snapshot_limitations: []
};
}
function buildCandidate(input: {
id: string;
anomalies?: string[];
relations?: string[];
confidence?: "high" | "medium" | "low";
}): CandidateEvidenceItem {
return {
schema_version: "candidate_evidence_v0_1",
candidate_id: input.id,
route: "hybrid_store_plus_live",
source_ref: {
schema_version: "evidence_source_ref_v1",
namespace: "snapshot_2020",
entity: "Document",
id: "DOC-1",
period: "2020-06",
canonical_ref: "evidence_source_ref_v1|snapshot_2020|document|doc-1|2020-06"
},
relation_pattern_hits: input.relations ?? [],
anomaly_patterns: input.anomalies ?? [],
entity_backlinks: [{ entity: "Document", id: "DOC-1" }],
confidence_hint: input.confidence ?? "medium"
};
}
describe("lifecycle runtime stage3", () => {
it("resolves stale missing transition for bank settlement chains", () => {
const unit = buildBaseProblemUnit({
id: "pu-bank-1",
type: "broken_chain_segment",
expectedState: "settlement_closed",
failedEdge: "payment_to_settlement"
});
const candidates = [
buildCandidate({
id: "cand-1",
relations: ["payment_to_settlement", "statement_to_document"],
anomalies: ["broken_lifecycle", "missing_link", "no_continuation"]
})
];
const resolution = resolveLifecycle({ unit, candidates });
expect(resolution.lifecycle_domain).toBe("bank_settlement");
expect(resolution.missing_transitions.length).toBeGreaterThan(0);
expect(resolution.detected_defects[0]).toBe("stale_active_state");
});
it("classifies cross-branch conflicts for VAT flow", () => {
const unit = buildBaseProblemUnit({
id: "pu-vat-1",
type: "cross_branch_inconsistency_cluster"
});
const candidates = [
buildCandidate({
id: "cand-1",
anomalies: ["cross_branch_inconsistency", "vat_chain_conflict"],
relations: ["invoice_to_vat"]
})
];
const enriched = enrichProblemUnitLifecycle({ unit, candidates });
expect(enriched.lifecycle_domain).toBe("vat_flow");
expect(enriched.lifecycle_defect_type).toBe("cross_branch_state_conflict");
expect(typeof enriched.business_lifecycle_interpretation).toBe("string");
});
it("sorts lifecycle-ranked units by lifecycle severity and ranking score", () => {
const stale = enrichProblemUnitLifecycle({
unit: buildBaseProblemUnit({
id: "pu-stale",
type: "period_risk_cluster",
impactCloseRisk: true,
expectedState: "close_completed"
}),
candidates: [buildCandidate({ id: "cand-stale", anomalies: ["period_close_risk", "missing_link", "no_continuation"] })]
});
const generic = enrichProblemUnitLifecycle({
unit: buildBaseProblemUnit({
id: "pu-generic",
type: "document_conflict",
severity: 0.4,
confidence: 0.45
}),
candidates: [buildCandidate({ id: "cand-generic", anomalies: ["anomaly_signal"] })]
});
const ranked = rankLifecycleProblemUnits([generic, stale]);
expect(ranked[0].problem_unit_id).toBe("pu-stale");
expect((ranked[0].lifecycle_ranking_score ?? 0) >= (ranked[1].lifecycle_ranking_score ?? 0)).toBe(true);
});
});