396 lines
20 KiB
TypeScript
396 lines
20 KiB
TypeScript
import { afterEach, describe, expect, it, vi } from "vitest";
|
||
|
||
const { executeAddressMcpQueryMock } = vi.hoisted(() => ({
|
||
executeAddressMcpQueryMock: vi.fn()
|
||
}));
|
||
|
||
vi.mock("../src/services/addressMcpClient", async () => {
|
||
const actual = await vi.importActual<typeof import("../src/services/addressMcpClient")>(
|
||
"../src/services/addressMcpClient"
|
||
);
|
||
return {
|
||
...actual,
|
||
executeAddressMcpQuery: executeAddressMcpQueryMock
|
||
};
|
||
});
|
||
|
||
import { AddressQueryService } from "../src/services/addressQueryService";
|
||
import { runAddressDecomposeStage } from "../src/services/address_runtime/decomposeStage";
|
||
|
||
afterEach(() => {
|
||
executeAddressMcpQueryMock.mockReset();
|
||
vi.restoreAllMocks();
|
||
});
|
||
|
||
describe("inventory profitability selected-object regressions", () => {
|
||
const followupContext = {
|
||
previous_intent: "inventory_on_hand_as_of_date" as const,
|
||
previous_filters: {
|
||
organization: "ООО \\Альтернатива Плюс\\",
|
||
as_of_date: "2020-05-31",
|
||
period_from: "2020-05-01",
|
||
period_to: "2020-05-31"
|
||
},
|
||
previous_anchor_type: "unknown" as const,
|
||
previous_anchor_value: null
|
||
};
|
||
|
||
const selectedObjectProfitabilityMessage =
|
||
'По выбранному объекту "Четки Пост (84*117)": а сколько денег мы заработали с продажжи этих четок';
|
||
|
||
it("routes selected-object profitability wording into an item profitability intent", () => {
|
||
const result = runAddressDecomposeStage(selectedObjectProfitabilityMessage, followupContext);
|
||
|
||
expect(result).not.toBeNull();
|
||
expect(result?.intent.intent).toBe("inventory_profitability_for_item");
|
||
expect(result?.intent.intent).not.toBe("customer_revenue_and_payments");
|
||
expect(result?.filters.extracted_filters.item).toBe("Четки Пост (84*117)");
|
||
expect(result?.filters.extracted_filters.period_from).toBe("2020-05-01");
|
||
expect(result?.filters.extracted_filters.period_to).toBe("2020-05-31");
|
||
});
|
||
|
||
it("answers selected-object profitability with a bounded document spread instead of a recipe gap", async () => {
|
||
executeAddressMcpQueryMock.mockResolvedValueOnce({
|
||
fetched_rows: 2,
|
||
matched_rows: 2,
|
||
raw_rows: [
|
||
{
|
||
Period: "2020-05-10T00:00:00Z",
|
||
Registrator: "Поступление товаров и услуг 000000001 от 10.05.2020",
|
||
AccountDt: "41.01",
|
||
AccountKt: "60.01",
|
||
Amount: 500,
|
||
Quantity: 10,
|
||
SubcontoDt1: "Четки Пост (84*117)",
|
||
SubcontoDt3: "Основной склад",
|
||
Counterparty: "ООО \\Поставщик\\",
|
||
Organization: "ООО \\Альтернатива Плюс\\"
|
||
},
|
||
{
|
||
Period: "2020-05-20T00:00:00Z",
|
||
Registrator: "Реализация товаров и услуг 000000017 от 20.05.2020",
|
||
AccountDt: "62.01",
|
||
AccountKt: "41.01",
|
||
Amount: 900,
|
||
Quantity: 10,
|
||
SubcontoKt1: "Четки Пост (84*117)",
|
||
SubcontoKt3: "Основной склад",
|
||
Counterparty: "ИП Покупатель",
|
||
Organization: "ООО \\Альтернатива Плюс\\"
|
||
}
|
||
],
|
||
rows: [],
|
||
error: null
|
||
});
|
||
|
||
const service = new AddressQueryService();
|
||
const result = await service.tryHandle(selectedObjectProfitabilityMessage, {
|
||
followupContext
|
||
});
|
||
|
||
expect(result?.handled).toBe(true);
|
||
expect(result?.response_type).toBe("FACTUAL_SUMMARY");
|
||
expect(result?.debug.detected_intent).toBe("inventory_profitability_for_item");
|
||
expect(result?.debug.selected_recipe).toBe("address_inventory_profitability_for_item_v1");
|
||
expect(result?.debug.capability_id).toBe("inventory_inventory_profitability_for_item");
|
||
expect(result?.debug.mcp_call_status).toBe("matched_non_empty");
|
||
expect(result?.debug.extracted_filters?.item).toBe("Четки Пост (84*117)");
|
||
const reply = String(result?.reply_text ?? "");
|
||
expect(reply).toContain("Четки Пост (84*117)");
|
||
expect(reply).toContain("900");
|
||
expect(reply).toContain("500");
|
||
expect(reply).toContain("400");
|
||
expect(reply).toContain("Маржинальность");
|
||
expect(reply).toContain("не чистая прибыль компании");
|
||
expect(executeAddressMcpQueryMock).toHaveBeenCalledTimes(1);
|
||
});
|
||
|
||
it("asks for a period before ranking nomenclature by margin", async () => {
|
||
const service = new AddressQueryService();
|
||
const result = await service.tryHandle(
|
||
"\u041a\u0430\u043a\u0430\u044f \u043d\u043e\u043c\u0435\u043a\u043b\u0430\u0442\u0443\u0440\u0430 \u0442\u043e\u0432\u0430\u0440\u0430 \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u0430 \u0441 \u0432\u044b\u0441\u043e\u043a\u043e\u0439 \u043f\u0440\u0438\u0431\u044b\u043b\u044c\u044e \u043a\u0430\u043a\u0430\u044f \u0441 \u043d\u0438\u0437\u043a\u043e\u0439"
|
||
);
|
||
|
||
expect(result?.handled).toBe(true);
|
||
expect(result?.response_type).toBe("LIMITED_WITH_REASON");
|
||
expect(result?.debug.detected_intent).toBe("inventory_margin_ranking_for_nomenclature");
|
||
expect(result?.debug.selected_recipe).toBe("address_inventory_margin_ranking_for_nomenclature_v1");
|
||
expect(result?.debug.capability_id).toBe("inventory_inventory_margin_ranking_for_nomenclature");
|
||
expect(result?.debug.missing_required_filters).toEqual(["period_from", "period_to"]);
|
||
const reply = String(result?.reply_text ?? "");
|
||
expect(reply).toContain("\u043d\u0443\u0436\u0435\u043d \u043f\u0435\u0440\u0438\u043e\u0434");
|
||
expect(reply).toContain("\u0441\u0435\u0431\u0435\u0441\u0442\u043e\u0438\u043c\u043e\u0441\u0442\u044c \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438");
|
||
expect(reply).not.toContain("\u041e\u0421");
|
||
expect(reply).not.toContain("\u0430\u043c\u043e\u0440\u0442\u0438\u0437");
|
||
expect(reply).not.toContain("\u0437\u0430\u043a\u0443\u043f\u043e\u0447\u043d\u044b\u0439/\u0441\u0435\u0431\u0435\u0441\u0442\u043e\u0438\u043c\u043e\u0441\u0442\u043d\u044b\u0439 \u0441\u043b\u0435\u0434");
|
||
expect(reply).not.toContain("settlement");
|
||
expect(executeAddressMcpQueryMock).not.toHaveBeenCalled();
|
||
});
|
||
|
||
it("gives a useful accounting limited answer when sales exist but cost is missing", async () => {
|
||
executeAddressMcpQueryMock.mockResolvedValueOnce({
|
||
fetched_rows: 1,
|
||
matched_rows: 1,
|
||
raw_rows: [
|
||
{
|
||
Period: "2020-05-20T00:00:00Z",
|
||
Registrator: "Sales document 1",
|
||
AccountDt: "62.01",
|
||
AccountKt: "41.01",
|
||
Amount: 1500,
|
||
Quantity: 10,
|
||
SubcontoKt1: "Item A",
|
||
Organization: "OOO Alternative Plus"
|
||
}
|
||
],
|
||
rows: [],
|
||
error: null
|
||
});
|
||
|
||
const service = new AddressQueryService();
|
||
const result = await service.tryHandle(
|
||
"\u041a\u0430\u043a\u0430\u044f \u043d\u043e\u043c\u0435\u043a\u043b\u0430\u0442\u0443\u0440\u0430 \u0442\u043e\u0432\u0430\u0440\u0430 \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u0430 \u0441 \u0432\u044b\u0441\u043e\u043a\u043e\u0439 \u043f\u0440\u0438\u0431\u044b\u043b\u044c\u044e \u043a\u0430\u043a\u0430\u044f \u0441 \u043d\u0438\u0437\u043a\u043e\u0439 \u0437\u0430 \u043c\u0430\u0439 2020"
|
||
);
|
||
|
||
expect(result?.handled).toBe(true);
|
||
expect(result?.response_type).toBe("FACTUAL_SUMMARY");
|
||
expect(result?.debug.detected_intent).toBe("inventory_margin_ranking_for_nomenclature");
|
||
expect(result?.debug.mcp_call_status).toBe("matched_non_empty");
|
||
const reply = String(result?.reply_text ?? "");
|
||
expect(reply).toContain("\u0440\u0435\u0439\u0442\u0438\u043d\u0433 \u043f\u0440\u0438\u0431\u044b\u043b\u044c\u043d\u043e\u0441\u0442\u0438 \u043d\u043e\u043c\u0435\u043d\u043a\u043b\u0430\u0442\u0443\u0440\u044b \u043f\u043e\u0441\u0442\u0440\u043e\u0438\u0442\u044c \u043d\u0435\u043b\u044c\u0437\u044f");
|
||
expect(reply).toContain("\u0415\u0441\u0442\u044c \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f");
|
||
expect(reply).toContain("\u0441\u0435\u0431\u0435\u0441\u0442\u043e\u0438\u043c\u043e\u0441\u0442\u0438 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438");
|
||
expect(reply).toContain("\u0447\u0435\u0441\u0442\u043d\u043e \u043f\u043e\u0441\u0447\u0438\u0442\u0430\u0442\u044c \u043d\u0435\u043b\u044c\u0437\u044f");
|
||
expect(reply).toContain("\u0427\u0442\u043e \u043c\u043e\u0436\u043d\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0434\u0430\u043b\u044c\u0448\u0435");
|
||
expect(reply).toContain("90.01 / 90.02");
|
||
expect(reply).not.toContain("\u041e\u0421");
|
||
expect(reply).not.toContain("\u0430\u043c\u043e\u0440\u0442\u0438\u0437");
|
||
expect(reply).not.toContain("\u0437\u0430\u043a\u0443\u043f\u043e\u0447\u043d\u044b\u0439/\u0441\u0435\u0431\u0435\u0441\u0442\u043e\u0438\u043c\u043e\u0441\u0442\u043d\u044b\u0439 \u0441\u043b\u0435\u0434");
|
||
expect(executeAddressMcpQueryMock).toHaveBeenCalledTimes(1);
|
||
});
|
||
|
||
it("does not offer to show realizations when only cost base exists in the period", async () => {
|
||
executeAddressMcpQueryMock.mockResolvedValueOnce({
|
||
fetched_rows: 1,
|
||
matched_rows: 1,
|
||
raw_rows: [
|
||
{
|
||
Period: "2017-09-12T00:00:00Z",
|
||
Registrator: "Purchase document 1",
|
||
AccountDt: "41.01",
|
||
AccountKt: "60.01",
|
||
Amount: 700,
|
||
Quantity: 2,
|
||
SubcontoDt1: "Item C",
|
||
Organization: "OOO Alternative Plus"
|
||
}
|
||
],
|
||
rows: [],
|
||
error: null
|
||
});
|
||
|
||
const service = new AddressQueryService();
|
||
const result = await service.tryHandle(
|
||
"\u041a\u0430\u043a\u0430\u044f \u043d\u043e\u043c\u0435\u043a\u043b\u0430\u0442\u0443\u0440\u0430 \u0442\u043e\u0432\u0430\u0440\u0430 \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u0430 \u0441 \u0432\u044b\u0441\u043e\u043a\u043e\u0439 \u043f\u0440\u0438\u0431\u044b\u043b\u044c\u044e \u043a\u0430\u043a\u0430\u044f \u0441 \u043d\u0438\u0437\u043a\u043e\u0439 \u0437\u0430 \u0441\u0435\u043d\u0442\u044f\u0431\u0440\u044c 2017"
|
||
);
|
||
|
||
expect(result?.handled).toBe(true);
|
||
expect(result?.response_type).toBe("FACTUAL_SUMMARY");
|
||
const reply = String(result?.reply_text ?? "");
|
||
expect(reply).toContain("\u0441\u0435\u0431\u0435\u0441\u0442\u043e\u0438\u043c\u043e\u0441\u0442\u043d\u0430\u044f \u0431\u0430\u0437\u0430");
|
||
expect(reply).toContain("\u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u043f\u043e \u043d\u0435\u0439 \u0432 \u043f\u0435\u0440\u0438\u043e\u0434\u0435 \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u043e");
|
||
expect(reply).toContain("\u043f\u043e\u043a\u0430\u0437\u0430\u0442\u044c \u043d\u0430\u0439\u0434\u0435\u043d\u043d\u044b\u0435 \u0441\u0442\u0440\u043e\u043a\u0438 \u0441\u0435\u0431\u0435\u0441\u0442\u043e\u0438\u043c\u043e\u0441\u0442\u043d\u043e\u0439 \u0431\u0430\u0437\u044b");
|
||
expect(reply).not.toContain("\u043f\u043e\u043a\u0430\u0437\u0430\u0442\u044c \u043d\u0430\u0439\u0434\u0435\u043d\u043d\u044b\u0435 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438");
|
||
expect(reply).not.toContain("\u0441\u0435\u0431\u0435\u0441\u0442\u043e\u0438\u043c\u043e\u0441\u0442\u043d\u0430\u044f/\u0437\u0430\u043a\u0443\u043f\u043e\u0447\u043d\u0430\u044f");
|
||
expect(executeAddressMcpQueryMock).toHaveBeenCalledTimes(1);
|
||
});
|
||
|
||
it("keeps cost-base line drilldown inside the nomenclature margin route", () => {
|
||
const marginFollowupContext = {
|
||
previous_intent: "inventory_margin_ranking_for_nomenclature" as const,
|
||
target_intent: "inventory_margin_ranking_for_nomenclature" as const,
|
||
root_intent: "inventory_margin_ranking_for_nomenclature" as const,
|
||
previous_filters: {
|
||
organization: "OOO Alternative Plus",
|
||
period_from: "2017-09-01",
|
||
period_to: "2017-09-30"
|
||
},
|
||
previous_anchor_type: "unknown" as const,
|
||
previous_anchor_value: null
|
||
};
|
||
|
||
const result = runAddressDecomposeStage(
|
||
"\u043f\u043e\u043a\u0430\u0436\u0438 \u043d\u0430\u0439\u0434\u0435\u043d\u043d\u044b\u0435 \u0441\u0442\u0440\u043e\u043a\u0438 \u0441\u0435\u0431\u0435\u0441\u0442\u043e\u0438\u043c\u043e\u0441\u0442\u043d\u043e\u0439 \u0431\u0430\u0437\u044b",
|
||
marginFollowupContext
|
||
);
|
||
|
||
expect(result).not.toBeNull();
|
||
expect(result?.intent.intent).toBe("inventory_margin_ranking_for_nomenclature");
|
||
expect(result?.filters.extracted_filters.period_from).toBe("2017-09-01");
|
||
expect(result?.filters.extracted_filters.period_to).toBe("2017-09-30");
|
||
expect(result?.filters.missing_required_filters).toEqual([]);
|
||
expect(result?.intent.reasons).toContain("intent_adjusted_to_inventory_margin_ranking_followup_context");
|
||
});
|
||
|
||
it("does not pivot margin follow-up account-41 correction into a balance snapshot", () => {
|
||
const marginFollowupContext = {
|
||
previous_intent: "inventory_margin_ranking_for_nomenclature" as const,
|
||
target_intent: "inventory_margin_ranking_for_nomenclature" as const,
|
||
root_intent: "inventory_margin_ranking_for_nomenclature" as const,
|
||
previous_filters: {
|
||
organization: "OOO Alternative Plus",
|
||
period_from: "2017-09-01",
|
||
period_to: "2017-09-30"
|
||
},
|
||
previous_anchor_type: "unknown" as const,
|
||
previous_anchor_value: null
|
||
};
|
||
|
||
for (const message of [
|
||
"\u0430\u043d\u0430\u043b\u0438\u0437 \u043f\u043e 41 \u0441\u0447\u0435\u0442\u0443 \u0430 \u043d\u0435 01",
|
||
"\u0432\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u044c \u0430\u043d\u0430\u043b\u0438\u0437 \u043f\u043e \u0441\u0447\u0435\u0442\u0443 41 \u0432\u043c\u0435\u0441\u0442\u043e \u0441\u0447\u0435\u0442\u0430 01"
|
||
]) {
|
||
const result = runAddressDecomposeStage(message, marginFollowupContext);
|
||
|
||
expect(result).not.toBeNull();
|
||
expect(result?.intent.intent).toBe("inventory_margin_ranking_for_nomenclature");
|
||
expect(result?.intent.intent).not.toBe("account_balance_snapshot");
|
||
expect(result?.filters.extracted_filters.period_from).toBe("2017-09-01");
|
||
expect(result?.filters.extracted_filters.period_to).toBe("2017-09-30");
|
||
expect(result?.filters.missing_required_filters).toEqual([]);
|
||
expect(result?.intent.reasons).toContain("intent_adjusted_to_inventory_margin_ranking_followup_context");
|
||
}
|
||
});
|
||
|
||
it("keeps carried period when executing margin account-41 correction", async () => {
|
||
executeAddressMcpQueryMock.mockResolvedValueOnce({
|
||
fetched_rows: 2,
|
||
matched_rows: 2,
|
||
raw_rows: [
|
||
{
|
||
Period: "2017-02-10T00:00:00Z",
|
||
Registrator: "Purchase document 2017",
|
||
AccountDt: "41.01",
|
||
AccountKt: "60.01",
|
||
Amount: 500,
|
||
Quantity: 1,
|
||
SubcontoDt1: "Item A",
|
||
Organization: "OOO Alternative Plus"
|
||
},
|
||
{
|
||
Period: "2017-03-10T00:00:00Z",
|
||
Registrator: "Sales document 2017",
|
||
AccountDt: "62.01",
|
||
AccountKt: "41.01",
|
||
Amount: 900,
|
||
Quantity: 1,
|
||
SubcontoKt1: "Item A",
|
||
Organization: "OOO Alternative Plus"
|
||
}
|
||
],
|
||
rows: [],
|
||
error: null
|
||
});
|
||
const marginFollowupContext = {
|
||
previous_intent: "inventory_margin_ranking_for_nomenclature" as const,
|
||
target_intent: "inventory_margin_ranking_for_nomenclature" as const,
|
||
root_intent: "inventory_margin_ranking_for_nomenclature" as const,
|
||
previous_filters: {
|
||
organization: "OOO Alternative Plus",
|
||
period_from: "2017-01-01",
|
||
period_to: "2017-12-31"
|
||
},
|
||
previous_anchor_type: "organization" as const,
|
||
previous_anchor_value: "OOO Alternative Plus"
|
||
};
|
||
|
||
const service = new AddressQueryService();
|
||
const result = await service.tryHandle(
|
||
"\u0430\u043d\u0430\u043b\u0438\u0437 \u043f\u043e 41 \u0441\u0447\u0435\u0442\u0443 \u0430 \u043d\u0435 01",
|
||
{ followupContext: marginFollowupContext }
|
||
);
|
||
|
||
expect(result?.handled).toBe(true);
|
||
expect(result?.debug.detected_intent).toBe("inventory_margin_ranking_for_nomenclature");
|
||
expect(result?.debug.selected_recipe).toBe("address_inventory_margin_ranking_for_nomenclature_v1");
|
||
expect(result?.debug.capability_id).toBe("inventory_inventory_margin_ranking_for_nomenclature");
|
||
expect(result?.debug.extracted_filters?.period_from).toBe("2017-01-01");
|
||
expect(result?.debug.extracted_filters?.period_to).toBe("2017-12-31");
|
||
expect(result?.debug.missing_required_filters).toEqual([]);
|
||
expect(result?.debug.mcp_call_status).toBe("matched_non_empty");
|
||
expect(executeAddressMcpQueryMock).toHaveBeenCalledTimes(1);
|
||
});
|
||
|
||
it("answers period-scoped nomenclature margin ranking with high and low gross-margin buckets", async () => {
|
||
executeAddressMcpQueryMock.mockResolvedValueOnce({
|
||
fetched_rows: 4,
|
||
matched_rows: 4,
|
||
raw_rows: [
|
||
{
|
||
Period: "2020-01-10T00:00:00Z",
|
||
Registrator: "Purchase document 1",
|
||
AccountDt: "41.01",
|
||
AccountKt: "60.01",
|
||
Amount: 500,
|
||
Quantity: 10,
|
||
SubcontoDt1: "Item A",
|
||
Organization: "OOO Alternative Plus"
|
||
},
|
||
{
|
||
Period: "2020-02-10T00:00:00Z",
|
||
Registrator: "Sales document 1",
|
||
AccountDt: "62.01",
|
||
AccountKt: "41.01",
|
||
Amount: 1500,
|
||
Quantity: 10,
|
||
SubcontoKt1: "Item A",
|
||
Organization: "OOO Alternative Plus"
|
||
},
|
||
{
|
||
Period: "2020-03-10T00:00:00Z",
|
||
Registrator: "Purchase document 2",
|
||
AccountDt: "41.01",
|
||
AccountKt: "60.01",
|
||
Amount: 1000,
|
||
Quantity: 5,
|
||
SubcontoDt1: "Item B",
|
||
Organization: "OOO Alternative Plus"
|
||
},
|
||
{
|
||
Period: "2020-04-10T00:00:00Z",
|
||
Registrator: "Sales document 2",
|
||
AccountDt: "62.01",
|
||
AccountKt: "41.01",
|
||
Amount: 900,
|
||
Quantity: 5,
|
||
SubcontoKt1: "Item B",
|
||
Organization: "OOO Alternative Plus"
|
||
}
|
||
],
|
||
rows: [],
|
||
error: null
|
||
});
|
||
|
||
const service = new AddressQueryService();
|
||
const result = await service.tryHandle(
|
||
"\u041a\u0430\u043a\u0430\u044f \u043d\u043e\u043c\u0435\u043a\u043b\u0430\u0442\u0443\u0440\u0430 \u0442\u043e\u0432\u0430\u0440\u0430 \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u0430 \u0441 \u0432\u044b\u0441\u043e\u043a\u043e\u0439 \u043f\u0440\u0438\u0431\u044b\u043b\u044c\u044e \u043a\u0430\u043a\u0430\u044f \u0441 \u043d\u0438\u0437\u043a\u043e\u0439 \u0437\u0430 2020 \u0433\u043e\u0434"
|
||
);
|
||
|
||
expect(result?.handled).toBe(true);
|
||
expect(result?.response_type).toBe("FACTUAL_SUMMARY");
|
||
expect(result?.debug.detected_intent).toBe("inventory_margin_ranking_for_nomenclature");
|
||
expect(result?.debug.selected_recipe).toBe("address_inventory_margin_ranking_for_nomenclature_v1");
|
||
expect(result?.debug.capability_id).toBe("inventory_inventory_margin_ranking_for_nomenclature");
|
||
expect(result?.debug.mcp_call_status).toBe("matched_non_empty");
|
||
const reply = String(result?.reply_text ?? "");
|
||
expect(reply).toContain("\u0412\u044b\u0441\u043e\u043a\u0430\u044f \u0432\u0430\u043b\u043e\u0432\u0430\u044f \u043c\u0430\u0440\u0436\u0438\u043d\u0430\u043b\u044c\u043d\u043e\u0441\u0442\u044c");
|
||
expect(reply).toContain("\u041d\u0438\u0437\u043a\u0430\u044f \u0438\u043b\u0438 \u043e\u0442\u0440\u0438\u0446\u0430\u0442\u0435\u043b\u044c\u043d\u0430\u044f");
|
||
expect(reply).toContain("Item A");
|
||
expect(reply).toContain("Item B");
|
||
expect(reply).toContain("\u043d\u0435 \u043f\u043e\u043a\u0430\u0437\u0430\u0442\u0435\u043b\u044c \u0447\u0438\u0441\u0442\u043e\u0439 \u043f\u0440\u0438\u0431\u044b\u043b\u0438");
|
||
expect(reply).not.toContain("\u041e\u0421");
|
||
expect(reply).not.toContain("\u0430\u043c\u043e\u0440\u0442\u0438\u0437");
|
||
expect(executeAddressMcpQueryMock).toHaveBeenCalledTimes(1);
|
||
});
|
||
});
|