142 lines
6.8 KiB
TypeScript
142 lines
6.8 KiB
TypeScript
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);
|
||
});
|
||
});
|