import fs from "fs"; import os from "os"; import path from "path"; import { afterEach, describe, expect, it, vi } from "vitest"; const TEMP_DIRS: string[] = []; const FLAG_KEYS = [ "FEATURE_ASSISTANT_BROAD_GUARD_V1", "FEATURE_ASSISTANT_MIN_EVIDENCE_GATE_V1" ] as const; const ORIGINAL_FLAGS: Record = Object.fromEntries(FLAG_KEYS.map((key) => [key, process.env[key]])); function restoreFlags(): void { for (const key of FLAG_KEYS) { const original = ORIGINAL_FLAGS[key]; if (original === undefined) { delete process.env[key]; } else { process.env[key] = original; } } } function cleanupTempDirs(): void { for (const dir of TEMP_DIRS.splice(0)) { fs.rmSync(dir, { recursive: true, force: true }); } } function createSnapshotRoot(keyFields: Array>): string { const root = fs.mkdtempSync(path.join(os.tmpdir(), "assistant-wave11-recovery-")); TEMP_DIRS.push(root); const write = (fileName: string, records: Array>) => { fs.writeFileSync(path.resolve(root, fileName), JSON.stringify({ records }, null, 2), "utf-8"); }; write("09_samples_key_fields_Recorder_Ref_Supplier_Buyer_Responsible.json", keyFields); write("03_snapshot_fragment_problem_cases.json", []); write("07_samples_DocumentJournals.json", []); write("08_samples_NDS_registers.json", []); write("04_samples_SpisanieSRaschetnogoScheta.json", []); write("05_samples_RealizaciyaTovarovUslug.json", []); write("06_samples_PostuplenieTovarovUslug.json", []); return root; } function buildDrilldownAnchorRecord(): Record { return { source_entity: "Document", source_id: "DOC-SETTLE-4", display_name: "Оплата по счету 4", unknown_link_count: 0, attributes: { Number: "4", Date: "2020-07-07T00:00:00", Amount: 276873.6, Description: "Оплата по счету 4 от 07.07.20", Account: "62.02" }, links: [ { relation: "document_has_counterparty", target_entity: "Counterparty", target_id: "CP-4", source_field: "Counterparty" } ] }; } function buildSettlementRecoveryRecord(): Record { return { source_entity: "Document", source_id: "DOC-SETTLE-RECOVERY-1", display_name: "Payment settlement tail", unknown_link_count: 1, attributes: { Period: "2020-07-15T00:00:00", Description: "payment linked to contract and buyer with unresolved closure" }, links: [ { relation: "document_has_counterparty", target_entity: "Counterparty", target_id: "CP-RECOVERY", source_field: "Counterparty" }, { relation: "document_refers_to_document", target_entity: "Document", target_id: "DOC-CHAIN-1", source_field: "Recorder" } ] }; } describe.sequential("wave11 data-layer recovery", () => { afterEach(() => { cleanupTempDirs(); restoreFlags(); vi.resetModules(); }); it("settlement_object_trace_with_number_date_amount_must_not_require_guid_by_default", async () => { const { AssistantDataLayer } = await import("../src/services/assistantDataLayer"); const dataLayer = new AssistantDataLayer(createSnapshotRoot([buildDrilldownAnchorRecord()])); const result = dataLayer.executeRoute( "live_mcp_drilldown", "Оплата по счету № 4 от 07.07.20 на 276 873,60 пришла 13 июля по счету 62.02." ); expect(result.status).toBe("ok"); const summary = result.summary as Record; expect(summary.reason).toBe("business_anchor_trace"); expect(summary.reason).not.toBe("guid_not_provided"); expect(Array.isArray(result.items)).toBe(true); expect(result.items.length).toBeGreaterThan(0); }); it("broad_settlement_query_must_not_drop_all_retrieval_due_to_strict_purity_if_in_scope", async () => { process.env.FEATURE_ASSISTANT_BROAD_GUARD_V1 = "0"; process.env.FEATURE_ASSISTANT_MIN_EVIDENCE_GATE_V1 = "0"; vi.resetModules(); const { AssistantDataLayer } = await import("../src/services/assistantDataLayer"); const dataLayer = new AssistantDataLayer(createSnapshotRoot([buildSettlementRecoveryRecord()])); const result = dataLayer.executeRoute( "hybrid_store_plus_live", "Почему по поставщику деньги ушли, а долг остался по счетам 60.01/62.02 в июле?" ); expect(result.status).toBe("ok"); expect(result.items.length).toBeGreaterThan(0); const summary = result.summary as Record; const guard = (summary.domain_purity_guard ?? {}) as Record; expect(Number(guard.source_selection_allowed ?? 0)).toBeGreaterThan(0); expect(Boolean(guard.settlement_source_recovery) || Boolean(guard.settlement_narrowing_recovery)).toBe(true); }); });