import { describe, expect, it } from "vitest"; import { buildDeepAnalysisProcessedLogDetails } from "../src/services/assistantMessageLogAssembler"; function baseInput() { return { sessionId: "asst-1", messageId: "msg-1", userMessage: "проверь 60.01", normalizerOutput: { schema_version: "normalized_query_v2_0_2" }, executionPlan: [{ fragment_id: "F1", route: "hybrid_store_plus_live", should_execute: true }], resolvedExecutionState: { executable: 1 }, routes: [{ fragment_id: "F1", route: "hybrid_store_plus_live" }], retrievalCalls: [{ route: "hybrid_store_plus_live" }], retrievalResultsRaw: [{ status: "ok" }], retrievalResultsNormalized: [{ status: "ok" }], requirementsExtracted: [{ requirement_id: "R1" }], coverageReport: { requirements_total: 1, requirements_covered: 1, requirements_uncovered: [], requirements_partially_covered: [], clarification_needed_for: [], out_of_scope_requirements: [] }, groundingCheck: { status: "grounded", route_subject_match: true, missing_requirements: [], reasons: [], why_included_summary: ["signal"], selection_reason_summary: ["ranked"] }, replyType: "factual", droppedIntentSegments: [], questionTypeClass: "factual_lookup", companyAnchors: { companies: ["demo"] }, runtimeAnalysisContext: { active: true, as_of_date: "2020-07-31", period_from: null, period_to: null, source: "eval_analysis_date", snapshot_mode: "auto" as const }, businessScopeResolution: { business_scope_raw: ["company_specific_accounting"], business_scope_resolved: ["company_specific_accounting"], company_grounding_applied: true, scope_resolution_reason: ["resolved"] }, temporalGuard: { raw_time_anchor: "2020-07", raw_time_scope: "month", resolved_time_anchor: "2020-07", resolved_primary_period: { from: "2020-07-01", to: "2020-07-31", granularity: "day" }, effective_primary_period: { from: "2020-07-01", to: "2020-07-31", granularity: "day" }, temporal_guard_input: "2020-07", temporal_alignment_status: "aligned", temporal_resolution_source: "analysis_context", temporal_guard_basis: "analysis_context", temporal_guard_applied: true, temporal_guard_outcome: "pass" }, polarityAudit: { raw_numeric_tokens: ["60.01"], classified_numeric_tokens: [{ token: "60.01" }], rejected_as_non_accounts: [], resolved_account_anchors: ["60.01"] }, claimAnchorAudit: { settlement_role: "supplier", settlement_role_resolution_reason: ["account_60_detected"], polarity_resolution_status: "resolved" }, targetedEvidenceAudit: { targeted_evidence_hit_rate: 1 }, evidenceAdmissibilityGateAudit: { admissible_evidence_count: 1 }, rbpLiveRouteAudit: null, faLiveRouteAudit: null, groundedAnswerEligibilityGuard: { eligibility_time_basis: "analysis_context", eligible: true }, followupStateUsage: null, compositionDebug: { problem_centric_answer_applied: true, problem_units_used_count: 1, problem_answer_mode: "stage3_lifecycle_aware_v1", problem_unit_ids_used: ["pu-1"], fallback_type: "none" }, outcomeClassV1: "FULLY_ANSWERED", assistantOrchestrationContractsV1: { query_frame: {}, execution_plan: {}, evidence_bundle: {}, coverage: {} }, answerStructureV11: { schema_version: "answer_structure_v1_1" }, investigationStateSnapshot: { status: "active" }, assistantReply: [ "Коротко: Признак проблемы подтвержден частично.", "Что именно проверено:\n- Проверен учетный контур.", "Что найдено:\n- Есть признак разрыва цепочки.", "Что пока не доказано:\n- Не хватает части подтверждений.", "Что проверить первым:\n- Уточнить период." ].join("\n\n"), traceId: "trace-1" }; } describe("assistant message log assembler", () => { it("builds deep analysis log details and resolves full coverage status", () => { const details = buildDeepAnalysisProcessedLogDetails(baseInput()); expect(details.session_id).toBe("asst-1"); expect(details.coverage_status).toBe("full"); expect(details.analysis_context).toMatchObject({ as_of_date: "2020-07-31" }); expect(details.problem_unit_ids_used).toEqual(["pu-1"]); expect(details.reply_type).toBe("factual"); expect((details.answer_contract_stage4_v1 as { is_stage4_shape?: boolean } | undefined)?.is_stage4_shape).toBe(true); }); it("marks partial coverage and omits optional sections when empty", () => { const input = baseInput(); input.coverageReport.requirements_covered = 0; input.coverageReport.requirements_uncovered = ["R1"]; input.runtimeAnalysisContext.active = false; input.followupStateUsage = null; input.compositionDebug.problem_unit_ids_used = []; const details = buildDeepAnalysisProcessedLogDetails(input); expect(details.coverage_status).toBe("partial_or_limited"); expect(details.analysis_context).toBeNull(); expect(Object.prototype.hasOwnProperty.call(details, "followup_state_usage")).toBe(false); expect(Object.prototype.hasOwnProperty.call(details, "problem_unit_ids_used")).toBe(false); }); });