128 lines
4.4 KiB
TypeScript
128 lines
4.4 KiB
TypeScript
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);
|
|
});
|
|
});
|