NODEDC_1C/llm_normalizer/backend/tests/assistantMcpDiscoveryRespon...

142 lines
6.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { describe, expect, it } from "vitest";
import { buildAssistantMcpDiscoveryResponseCandidate } from "../src/services/assistantMcpDiscoveryResponseCandidate";
function entryPoint(overrides: Record<string, unknown> = {}) {
return {
schema_version: "assistant_mcp_discovery_runtime_entry_point_v1",
policy_owner: "assistantMcpDiscoveryRuntimeEntryPoint",
entry_status: "bridge_executed",
hot_runtime_wired: false,
discovery_attempted: true,
turn_input: { adapter_status: "ready" },
bridge: {
bridge_status: "answer_draft_ready",
user_facing_response_allowed: true,
business_fact_answer_allowed: true,
requires_user_clarification: false,
answer_draft: {
answer_mode: "confirmed_with_bounded_inference",
headline: "По данным 1С есть подтвержденная активность; длительность можно оценивать только как вывод.",
confirmed_lines: ["1C activity rows were found for counterparty SVK"],
inference_lines: ["Business activity duration may be inferred from first and latest confirmed 1C activity rows"],
unknown_lines: ["Legal registration date is not proven by this MCP discovery pilot"],
limitation_lines: ["query_documents was skipped internally", "MCP fetch window was limited"],
next_step_line: null
}
},
reason_codes: ["runtime_entry_point_bridge_executed"],
...overrides
} as any;
}
describe("assistant MCP discovery response candidate", () => {
it("builds a Russian guarded candidate from a confirmed discovery draft", () => {
const candidate = buildAssistantMcpDiscoveryResponseCandidate(entryPoint());
expect(candidate.candidate_status).toBe("ready_for_guarded_use");
expect(candidate.hot_runtime_wired).toBe(false);
expect(candidate.reply_type).toBe("partial_coverage");
expect(candidate.eligible_for_future_hot_runtime).toBe(true);
expect(candidate.reply_text).toContain("Коротко:");
expect(candidate.reply_text).toContain("В 1С найдены строки активности по контрагенту SVK.");
expect(candidate.reply_text).toContain("Юридическая дата регистрации этим поиском не подтверждена.");
expect(candidate.reply_text).not.toContain("query_documents");
expect(candidate.reply_text).not.toContain("primitive");
});
it("localizes value-flow evidence without leaking pilot mechanics", () => {
const candidate = buildAssistantMcpDiscoveryResponseCandidate(
entryPoint({
bridge: {
bridge_status: "answer_draft_ready",
user_facing_response_allowed: true,
business_fact_answer_allowed: true,
requires_user_clarification: false,
answer_draft: {
answer_mode: "confirmed_with_bounded_inference",
headline: "По данным 1С найдены строки денежных движений; сумму можно называть только в рамках проверенного периода и найденных строк.",
confirmed_lines: [
"1C value-flow rows were found for counterparty SVK",
"По найденным строкам денежных движений в 1С по контрагенту SVK за период 2020 сумма составляет 3 750 руб."
],
inference_lines: ["Counterparty value-flow total was calculated from confirmed 1C movement rows"],
unknown_lines: ["Full turnover outside the checked period is not proven by this MCP discovery pilot"],
limitation_lines: ["pilot_value_flow_uses_query_movements_and_derives_aggregate"],
next_step_line: null
}
}
})
);
expect(candidate.candidate_status).toBe("ready_for_guarded_use");
expect(candidate.reply_text).toContain("В 1С найдены строки денежных движений по контрагенту SVK.");
expect(candidate.reply_text).toContain("3 750 руб.");
expect(candidate.reply_text).toContain("Полный оборот вне проверенного периода этим поиском не подтвержден.");
expect(candidate.reply_text).not.toContain("pilot_");
expect(candidate.reply_text).not.toContain("query_movements");
});
it("returns not applicable when discovery was skipped for an exact supported route", () => {
const candidate = buildAssistantMcpDiscoveryResponseCandidate({
schema_version: "assistant_mcp_discovery_runtime_entry_point_v1",
policy_owner: "assistantMcpDiscoveryRuntimeEntryPoint",
entry_status: "skipped_not_applicable",
hot_runtime_wired: false,
discovery_attempted: false,
turn_input: { adapter_status: "not_applicable" },
bridge: null,
reason_codes: []
} as any);
expect(candidate.candidate_status).toBe("not_applicable");
expect(candidate.reply_text).toBeNull();
expect(candidate.eligible_for_future_hot_runtime).toBe(false);
});
it("creates a clarification candidate from a clarification bridge", () => {
const candidate = buildAssistantMcpDiscoveryResponseCandidate(
entryPoint({
bridge: {
bridge_status: "needs_clarification",
user_facing_response_allowed: true,
business_fact_answer_allowed: false,
requires_user_clarification: true,
answer_draft: {
answer_mode: "needs_clarification",
headline: "Нужно уточнить контекст перед поиском в 1С.",
confirmed_lines: [],
inference_lines: [],
unknown_lines: [],
limitation_lines: [],
next_step_line: "Уточните контрагента, период или организацию."
}
}
})
);
expect(candidate.candidate_status).toBe("clarification_candidate");
expect(candidate.reply_type).toBe("clarification_required");
expect(candidate.reply_text).toContain("Нужно уточнить контекст");
expect(candidate.eligible_for_future_hot_runtime).toBe(true);
});
it("does not expose unsupported bridge output as a future hot candidate", () => {
const candidate = buildAssistantMcpDiscoveryResponseCandidate(
entryPoint({
bridge: {
bridge_status: "unsupported",
user_facing_response_allowed: true,
business_fact_answer_allowed: false,
requires_user_clarification: false,
answer_draft: null
}
})
);
expect(candidate.candidate_status).toBe("unsupported");
expect(candidate.reply_type).toBe("no_grounded_answer");
expect(candidate.reply_text).toBeNull();
expect(candidate.eligible_for_future_hot_runtime).toBe(false);
});
});