NODEDC_1C/llm_normalizer/backend/tests/assistantInvestigationState...

159 lines
4.8 KiB
TypeScript

import { describe, expect, it } from "vitest";
import type { RouteHintSummary } from "../src/types/normalizer";
import type { UnifiedRetrievalResult } from "../src/types/assistant";
import { createEmptyInvestigationState } from "../src/services/investigationState";
import {
buildAssistantInvestigationStateSnapshot,
persistAssistantInvestigationStateSnapshot
} from "../src/services/assistantInvestigationStateRuntimeAdapter";
function buildRouteSummary(): RouteHintSummary {
return {
mode: "deterministic_v2",
message_in_scope: true,
scope_confidence: "high",
planner: {
total_fragments: 1,
in_scope_fragments: 1,
out_of_scope_fragments: 0,
discarded_fragments: 0,
contains_multiple_tasks: false
},
decisions: [
{
fragment_id: "F1",
domain_relevance: "in_scope",
business_scope: "company_specific_accounting",
candidate_labels: ["anomaly_probe"],
decision_flags: {
has_multi_entity_scope: false,
asks_for_chain_explanation: false,
asks_for_ranking_or_top: false,
asks_for_period_summary: false,
asks_for_rule_check: true,
asks_for_anomaly_scan: true,
asks_for_exact_object_trace: false,
asks_for_evidence: true,
mentions_period_close_context: false
},
route: "store_feature_risk",
reason: "test-route"
}
],
fallback: {
type: "none",
message: null
}
};
}
function buildRetrievalResult(): UnifiedRetrievalResult {
return {
fragment_id: "F1",
requirement_ids: ["R1"],
route: "store_feature_risk",
status: "ok",
result_type: "summary",
items: [],
summary: {},
evidence: [],
why_included: [],
selection_reason: [],
risk_factors: [],
business_interpretation: [],
confidence: "medium",
limitations: [],
errors: []
};
}
describe("assistant investigation state runtime adapter", () => {
it("returns null and skips persist when feature disabled", () => {
const snapshot = buildAssistantInvestigationStateSnapshot({
featureEnabled: false,
previousState: createEmptyInvestigationState("asst-1", "2026-04-10T10:00:00.000Z"),
timestamp: "2026-04-10T10:01:00.000Z",
questionId: "msg-1",
userMessage: "проверь 60.01",
routeSummary: buildRouteSummary(),
requirements: [],
coverageReport: {
requirements_total: 0,
requirements_covered: 0,
requirements_uncovered: [],
requirements_partially_covered: [],
clarification_needed_for: [],
out_of_scope_requirements: []
},
retrievalResults: [],
replyType: "factual",
followupApplied: false
});
expect(snapshot).toBeNull();
let persistCalled = false;
const persisted = persistAssistantInvestigationStateSnapshot({
featureEnabled: false,
sessionId: "asst-1",
snapshot: null,
persist: () => {
persistCalled = true;
}
});
expect(persisted).toBe(false);
expect(persistCalled).toBe(false);
});
it("builds snapshot and persists it when feature enabled", () => {
const previous = createEmptyInvestigationState("asst-2", "2026-04-10T10:00:00.000Z");
const snapshot = buildAssistantInvestigationStateSnapshot({
featureEnabled: true,
previousState: previous,
timestamp: "2026-04-10T10:01:00.000Z",
questionId: "msg-2",
userMessage: "проверь счет 60.01 за 2020-07",
routeSummary: buildRouteSummary(),
requirements: [
{
requirement_id: "R1",
source_fragment_id: "F1",
requirement_text: "проверить счет 60.01",
subject_tokens: ["account_60.01"],
status: "covered",
route: "store_feature_risk"
}
],
coverageReport: {
requirements_total: 1,
requirements_covered: 1,
requirements_uncovered: [],
requirements_partially_covered: [],
clarification_needed_for: [],
out_of_scope_requirements: []
},
retrievalResults: [buildRetrievalResult()],
replyType: "factual",
followupApplied: false
});
expect(snapshot).not.toBeNull();
expect(snapshot?.turn_index).toBe(1);
expect(snapshot?.question_id).toBe("msg-2");
let persistedSessionId: string | null = null;
let persistedQuestionId: string | null = null;
const persisted = persistAssistantInvestigationStateSnapshot({
featureEnabled: true,
sessionId: "asst-2",
snapshot: snapshot,
persist: (sessionId, state) => {
persistedSessionId = sessionId;
persistedQuestionId = state.question_id;
}
});
expect(persisted).toBe(true);
expect(persistedSessionId).toBe("asst-2");
expect(persistedQuestionId).toBe("msg-2");
});
});