Этап 4 / Волна 10: корректировка settlement-кейса — доменная фиксация синтеза, честное покрытие, удержание фокуса / Этап 4 / Волна 11: бизнес-якоря, доменное заземление и устранение утечки дебага
This commit is contained in:
@@ -0,0 +1,127 @@
|
||||
import fs from "fs";
|
||||
import os from "os";
|
||||
import path from "path";
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
const MCP_FLAG = "FEATURE_ASSISTANT_MCP_RUNTIME_V1";
|
||||
const MCP_PROXY = "ASSISTANT_MCP_PROXY_URL";
|
||||
const MCP_CHANNEL = "ASSISTANT_MCP_CHANNEL";
|
||||
const ORIGINAL_ENV = {
|
||||
[MCP_FLAG]: process.env[MCP_FLAG],
|
||||
[MCP_PROXY]: process.env[MCP_PROXY],
|
||||
[MCP_CHANNEL]: process.env[MCP_CHANNEL]
|
||||
};
|
||||
const TEMP_DIRS: string[] = [];
|
||||
|
||||
function restoreEnv(): void {
|
||||
for (const key of [MCP_FLAG, MCP_PROXY, MCP_CHANNEL] as const) {
|
||||
const original = ORIGINAL_ENV[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(): string {
|
||||
const root = fs.mkdtempSync(path.join(os.tmpdir(), "assistant-mcp-bridge-"));
|
||||
TEMP_DIRS.push(root);
|
||||
return root;
|
||||
}
|
||||
|
||||
describe.sequential("assistant MCP runtime bridge", () => {
|
||||
afterEach(() => {
|
||||
vi.unstubAllGlobals();
|
||||
restoreEnv();
|
||||
cleanupTempDirs();
|
||||
vi.resetModules();
|
||||
});
|
||||
|
||||
it("does not call MCP when runtime flag is disabled", async () => {
|
||||
process.env[MCP_FLAG] = "0";
|
||||
const fetchMock = vi.fn();
|
||||
vi.stubGlobal("fetch", fetchMock);
|
||||
|
||||
const { AssistantDataLayer } = await import("../src/services/assistantDataLayer");
|
||||
const dataLayer = new AssistantDataLayer(createSnapshotRoot());
|
||||
const result = await dataLayer.executeRouteRuntime("hybrid_store_plus_live", "Почему по счету 60.01 долг остался?");
|
||||
|
||||
expect(fetchMock).not.toHaveBeenCalled();
|
||||
expect(result.summary.live_mcp).toBeUndefined();
|
||||
});
|
||||
|
||||
it("uses MCP live probe for hybrid route when runtime flag is enabled", async () => {
|
||||
process.env[MCP_FLAG] = "1";
|
||||
process.env[MCP_PROXY] = "http://127.0.0.1:6003";
|
||||
process.env[MCP_CHANNEL] = "default";
|
||||
|
||||
const payload = JSON.stringify({
|
||||
success: true,
|
||||
data: [
|
||||
{
|
||||
Период: "2026-03-01T00:00:00",
|
||||
Регистратор: "Списание с расчетного счета 0001",
|
||||
СчетДт: "60.01",
|
||||
СчетКт: "51",
|
||||
Сумма: 15000
|
||||
},
|
||||
{
|
||||
Период: "2026-03-02T00:00:00",
|
||||
Регистратор: "Операция бухгалтерская 0002",
|
||||
СчетДт: "91.02",
|
||||
СчетКт: "51",
|
||||
Сумма: 900
|
||||
}
|
||||
]
|
||||
});
|
||||
const fetchMock = vi.fn(async () => new Response(payload, { status: 200 }));
|
||||
vi.stubGlobal("fetch", fetchMock);
|
||||
|
||||
const { AssistantDataLayer } = await import("../src/services/assistantDataLayer");
|
||||
const dataLayer = new AssistantDataLayer(createSnapshotRoot());
|
||||
const result = await dataLayer.executeRouteRuntime("hybrid_store_plus_live", "Проверь 60.01 и 60.02: оплата есть, долг остался");
|
||||
|
||||
expect(fetchMock).toHaveBeenCalledTimes(1);
|
||||
expect(result.status).toBe("ok");
|
||||
expect(result.items.length).toBeGreaterThan(0);
|
||||
|
||||
const summary = result.summary as Record<string, unknown>;
|
||||
const liveSummary = summary.live_mcp as Record<string, unknown>;
|
||||
expect(liveSummary.status).toBe("ok");
|
||||
expect(liveSummary.channel).toBe("default");
|
||||
|
||||
const firstItem = result.items[0] as Record<string, unknown>;
|
||||
expect(firstItem.source_layer).toBe("mcp_live_probe");
|
||||
});
|
||||
|
||||
it("keeps snapshot fallback when MCP responds with error", async () => {
|
||||
process.env[MCP_FLAG] = "1";
|
||||
process.env[MCP_PROXY] = "http://127.0.0.1:6003";
|
||||
process.env[MCP_CHANNEL] = "default";
|
||||
|
||||
const payload = JSON.stringify({
|
||||
success: false,
|
||||
data: null,
|
||||
error: "channel_not_connected"
|
||||
});
|
||||
const fetchMock = vi.fn(async () => new Response(payload, { status: 200 }));
|
||||
vi.stubGlobal("fetch", fetchMock);
|
||||
|
||||
const { AssistantDataLayer } = await import("../src/services/assistantDataLayer");
|
||||
const dataLayer = new AssistantDataLayer(createSnapshotRoot());
|
||||
const result = await dataLayer.executeRouteRuntime("hybrid_store_plus_live", "Проверь 60.01 остаток");
|
||||
|
||||
expect(fetchMock).toHaveBeenCalledTimes(1);
|
||||
const summary = result.summary as Record<string, unknown>;
|
||||
const liveSummary = summary.live_mcp as Record<string, unknown>;
|
||||
expect(liveSummary.status).toBe("error");
|
||||
expect(result.limitations.some((item) => item.includes("Live MCP"))).toBe(true);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user