@yoryoboy/bi-mcp 1.16.0 → 1.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -0
- package/dist/.tsbuildinfo +1 -1
- package/dist/index.js +179 -6
- package/dist/index.js.map +2 -2
- package/dist/mcp-use.json +3 -6
- package/dist/src/config/__tests__/google-store.test.js +30 -0
- package/dist/src/config/__tests__/google-store.test.js.map +7 -0
- package/dist/src/config/__tests__/mcp-tool-deadline.test.js +64 -0
- package/dist/src/config/__tests__/mcp-tool-deadline.test.js.map +7 -0
- package/dist/src/config/google-store.js +18 -0
- package/dist/src/config/google-store.js.map +2 -2
- package/dist/src/config/mcp-tool-deadline-context.js +34 -0
- package/dist/src/config/mcp-tool-deadline-context.js.map +7 -0
- package/dist/src/config/mcp-tool-deadline.js +67 -0
- package/dist/src/config/mcp-tool-deadline.js.map +7 -0
- package/dist/src/services/mercadolibre/__tests__/mercadolibre-api.test.js +38 -3
- package/dist/src/services/mercadolibre/__tests__/mercadolibre-api.test.js.map +2 -2
- package/dist/src/services/mercadolibre/__tests__/mercadolibre-buyer-reconciliation.test.js +226 -0
- package/dist/src/services/mercadolibre/__tests__/mercadolibre-buyer-reconciliation.test.js.map +7 -0
- package/dist/src/services/mercadolibre/__tests__/mercadolibre-claim-reconciliation.test.js +127 -0
- package/dist/src/services/mercadolibre/__tests__/mercadolibre-claim-reconciliation.test.js.map +7 -0
- package/dist/src/services/mercadolibre/__tests__/mercadolibre-kits.test.js +103 -0
- package/dist/src/services/mercadolibre/__tests__/mercadolibre-kits.test.js.map +7 -0
- package/dist/src/services/mercadolibre/__tests__/mercadolibre-order-billing-info.test.js +582 -0
- package/dist/src/services/mercadolibre/__tests__/mercadolibre-order-billing-info.test.js.map +7 -0
- package/dist/src/services/mercadolibre/__tests__/mercadolibre-orders.test.js +10 -0
- package/dist/src/services/mercadolibre/__tests__/mercadolibre-orders.test.js.map +2 -2
- package/dist/src/services/mercadolibre/__tests__/mercadolibre-packs.test.js +36 -0
- package/dist/src/services/mercadolibre/__tests__/mercadolibre-packs.test.js.map +7 -0
- package/dist/src/services/mercadolibre/__tests__/mercadolibre-payments.test.js +137 -0
- package/dist/src/services/mercadolibre/__tests__/mercadolibre-payments.test.js.map +7 -0
- package/dist/src/services/mercadolibre/__tests__/mercadolibre-product-ads-ad-group-ads.test.js +105 -0
- package/dist/src/services/mercadolibre/__tests__/mercadolibre-product-ads-ad-group-ads.test.js.map +7 -0
- package/dist/src/services/mercadolibre/__tests__/mercadolibre-product-ads-ad-groups.test.js +143 -0
- package/dist/src/services/mercadolibre/__tests__/mercadolibre-product-ads-ad-groups.test.js.map +7 -0
- package/dist/src/services/mercadolibre/__tests__/mercadolibre-product-ads-campaigns.test.js +100 -0
- package/dist/src/services/mercadolibre/__tests__/mercadolibre-product-ads-campaigns.test.js.map +7 -0
- package/dist/src/services/mercadolibre/__tests__/mercadolibre-product-ads.test.js +350 -0
- package/dist/src/services/mercadolibre/__tests__/mercadolibre-product-ads.test.js.map +7 -0
- package/dist/src/services/mercadolibre/__tests__/mercadolibre-return-reconciliation.test.js +228 -0
- package/dist/src/services/mercadolibre/__tests__/mercadolibre-return-reconciliation.test.js.map +7 -0
- package/dist/src/services/mercadolibre/__tests__/mercadolibre-shipping.test.js +58 -0
- package/dist/src/services/mercadolibre/__tests__/mercadolibre-shipping.test.js.map +7 -0
- package/dist/src/services/mercadolibre/mercadolibre-api.js +15 -3
- package/dist/src/services/mercadolibre/mercadolibre-api.js.map +2 -2
- package/dist/src/services/mercadolibre/mercadolibre-billing.js +110 -3
- package/dist/src/services/mercadolibre/mercadolibre-billing.js.map +2 -2
- package/dist/src/services/mercadolibre/mercadolibre-buyer-reconciliation.js +368 -0
- package/dist/src/services/mercadolibre/mercadolibre-buyer-reconciliation.js.map +7 -0
- package/dist/src/services/mercadolibre/mercadolibre-claim-reconciliation.js +399 -0
- package/dist/src/services/mercadolibre/mercadolibre-claim-reconciliation.js.map +7 -0
- package/dist/src/services/mercadolibre/mercadolibre-kits.js +55 -0
- package/dist/src/services/mercadolibre/mercadolibre-kits.js.map +7 -0
- package/dist/src/services/mercadolibre/mercadolibre-order-billing-info.js +392 -0
- package/dist/src/services/mercadolibre/mercadolibre-order-billing-info.js.map +7 -0
- package/dist/src/services/mercadolibre/mercadolibre-orders.js +8 -8
- package/dist/src/services/mercadolibre/mercadolibre-orders.js.map +2 -2
- package/dist/src/services/mercadolibre/mercadolibre-packs.js +21 -0
- package/dist/src/services/mercadolibre/mercadolibre-packs.js.map +7 -0
- package/dist/src/services/mercadolibre/mercadolibre-payments.js +60 -0
- package/dist/src/services/mercadolibre/mercadolibre-payments.js.map +7 -0
- package/dist/src/services/mercadolibre/mercadolibre-product-ads.js +429 -0
- package/dist/src/services/mercadolibre/mercadolibre-product-ads.js.map +7 -0
- package/dist/src/services/mercadolibre/mercadolibre-return-reconciliation.js +436 -0
- package/dist/src/services/mercadolibre/mercadolibre-return-reconciliation.js.map +7 -0
- package/dist/src/services/mercadolibre/mercadolibre-shipping.js +35 -0
- package/dist/src/services/mercadolibre/mercadolibre-shipping.js.map +7 -0
- package/dist/src/services/vtex/__tests__/vtex-api.test.js +67 -0
- package/dist/src/services/vtex/__tests__/vtex-api.test.js.map +7 -0
- package/dist/src/services/vtex/vtex-api.js +6 -0
- package/dist/src/services/vtex/vtex-api.js.map +2 -2
- package/dist/src/tools/config/__tests__/list-profiles.test.js +154 -0
- package/dist/src/tools/config/__tests__/list-profiles.test.js.map +7 -0
- package/dist/src/tools/config/list-profiles.js +39 -3
- package/dist/src/tools/config/list-profiles.js.map +2 -2
- package/dist/src/tools/mercadolibre/__tests__/get-order-billing-info.test.js +339 -0
- package/dist/src/tools/mercadolibre/__tests__/get-order-billing-info.test.js.map +7 -0
- package/dist/src/tools/mercadolibre/__tests__/product-ads-ad-group-ads.test.js +225 -0
- package/dist/src/tools/mercadolibre/__tests__/product-ads-ad-group-ads.test.js.map +7 -0
- package/dist/src/tools/mercadolibre/__tests__/product-ads-ad-groups.test.js +148 -0
- package/dist/src/tools/mercadolibre/__tests__/product-ads-ad-groups.test.js.map +7 -0
- package/dist/src/tools/mercadolibre/__tests__/product-ads-campaigns.test.js +60 -0
- package/dist/src/tools/mercadolibre/__tests__/product-ads-campaigns.test.js.map +7 -0
- package/dist/src/tools/mercadolibre/__tests__/product-ads-helpers.test.js +128 -0
- package/dist/src/tools/mercadolibre/__tests__/product-ads-helpers.test.js.map +7 -0
- package/dist/src/tools/mercadolibre/__tests__/product-ads-tools.test.js +435 -0
- package/dist/src/tools/mercadolibre/__tests__/product-ads-tools.test.js.map +7 -0
- package/dist/src/tools/mercadolibre/__tests__/reconcile-buyer-data.test.js +31 -0
- package/dist/src/tools/mercadolibre/__tests__/reconcile-buyer-data.test.js.map +7 -0
- package/dist/src/tools/mercadolibre/__tests__/reconcile-claim-data.test.js +36 -0
- package/dist/src/tools/mercadolibre/__tests__/reconcile-claim-data.test.js.map +7 -0
- package/dist/src/tools/mercadolibre/__tests__/validate-connection.test.js +205 -0
- package/dist/src/tools/mercadolibre/__tests__/validate-connection.test.js.map +7 -0
- package/dist/src/tools/mercadolibre/get-billing-provisions-by-order.js +313 -0
- package/dist/src/tools/mercadolibre/get-billing-provisions-by-order.js.map +7 -0
- package/dist/src/tools/mercadolibre/get-bundle-context-by-item.js +176 -0
- package/dist/src/tools/mercadolibre/get-bundle-context-by-item.js.map +7 -0
- package/dist/src/tools/mercadolibre/get-full-storage-charges.js +183 -0
- package/dist/src/tools/mercadolibre/get-full-storage-charges.js.map +7 -0
- package/dist/src/tools/mercadolibre/get-order-billing-info.js +189 -0
- package/dist/src/tools/mercadolibre/get-order-billing-info.js.map +7 -0
- package/dist/src/tools/mercadolibre/get-pack-context-by-order.js +150 -0
- package/dist/src/tools/mercadolibre/get-pack-context-by-order.js.map +7 -0
- package/dist/src/tools/mercadolibre/get-payment-details.js +322 -0
- package/dist/src/tools/mercadolibre/get-payment-details.js.map +7 -0
- package/dist/src/tools/mercadolibre/get-product-ads-ad-group-ads.js +144 -0
- package/dist/src/tools/mercadolibre/get-product-ads-ad-group-ads.js.map +7 -0
- package/dist/src/tools/mercadolibre/get-product-ads-advertisers.js +59 -0
- package/dist/src/tools/mercadolibre/get-product-ads-advertisers.js.map +7 -0
- package/dist/src/tools/mercadolibre/get-shipping-quote-by-declared-dimensions.js +104 -0
- package/dist/src/tools/mercadolibre/get-shipping-quote-by-declared-dimensions.js.map +7 -0
- package/dist/src/tools/mercadolibre/index.js +32 -0
- package/dist/src/tools/mercadolibre/index.js.map +2 -2
- package/dist/src/tools/mercadolibre/product-ads-helpers.js +130 -0
- package/dist/src/tools/mercadolibre/product-ads-helpers.js.map +7 -0
- package/dist/src/tools/mercadolibre/reconcile-buyer-data.js +27 -0
- package/dist/src/tools/mercadolibre/reconcile-buyer-data.js.map +7 -0
- package/dist/src/tools/mercadolibre/reconcile-claim-data.js +46 -0
- package/dist/src/tools/mercadolibre/reconcile-claim-data.js.map +7 -0
- package/dist/src/tools/mercadolibre/reconcile-return-data.js +25 -0
- package/dist/src/tools/mercadolibre/reconcile-return-data.js.map +7 -0
- package/dist/src/tools/mercadolibre/search-product-ads-ad-groups.js +162 -0
- package/dist/src/tools/mercadolibre/search-product-ads-ad-groups.js.map +7 -0
- package/dist/src/tools/mercadolibre/search-product-ads-ads.js +216 -0
- package/dist/src/tools/mercadolibre/search-product-ads-ads.js.map +7 -0
- package/dist/src/tools/mercadolibre/search-product-ads-campaigns.js +116 -0
- package/dist/src/tools/mercadolibre/search-product-ads-campaigns.js.map +7 -0
- package/dist/src/tools/mercadolibre/validate-connection.js +40 -12
- package/dist/src/tools/mercadolibre/validate-connection.js.map +2 -2
- package/dist/tests/meli/mercadolibre-billing-provisions.test.js +378 -0
- package/dist/tests/meli/mercadolibre-billing-provisions.test.js.map +2 -2
- package/dist/tests/meli/mercadolibre-order-billing-info-surface.test.js +123 -0
- package/dist/tests/meli/mercadolibre-order-billing-info-surface.test.js.map +7 -0
- package/dist/tests/meli/mercadolibre-payment-details.test.js +356 -0
- package/dist/tests/meli/mercadolibre-payment-details.test.js.map +7 -0
- package/dist/tests/meli/mercadolibre-payments.test.js +62 -0
- package/dist/tests/meli/mercadolibre-payments.test.js.map +7 -0
- package/dist/tests/meli/mercadolibre-product-ads-ad-groups-surface.test.js +90 -0
- package/dist/tests/meli/mercadolibre-product-ads-ad-groups-surface.test.js.map +7 -0
- package/dist/tests/meli/mercadolibre-product-ads-campaigns-surface.test.js +72 -0
- package/dist/tests/meli/mercadolibre-product-ads-campaigns-surface.test.js.map +7 -0
- package/dist/tests/meli/mercadolibre-tool-handlers-pack-kit-shipping.test.js +400 -0
- package/dist/tests/meli/mercadolibre-tool-handlers-pack-kit-shipping.test.js.map +7 -0
- package/dist/tests/meli/mercadolibre-tool-handlers-small.test.js +31 -12
- package/dist/tests/meli/mercadolibre-tool-handlers-small.test.js.map +2 -2
- package/dist/tests/vtex/vtex-commercial-conditions-docs.test.js +1 -1
- package/dist/tests/vtex/vtex-commercial-conditions-docs.test.js.map +1 -1
- package/package.json +1 -1
- package/dist/src/google-ads/report-utils.js +0 -78
- package/dist/src/google-ads/report-utils.js.map +0 -7
- package/dist/src/search-console/search-console-utils.js +0 -275
- package/dist/src/search-console/search-console-utils.js.map +0 -7
- package/dist/src/services/vtex/__tests__/vtex-catalog-write-batch.test.js +0 -165
- package/dist/src/services/vtex/__tests__/vtex-catalog-write-batch.test.js.map +0 -7
- package/dist/src/tools/vtex/__tests__/catalog-admin-batch.test.js +0 -233
- package/dist/src/tools/vtex/__tests__/catalog-admin-batch.test.js.map +0 -7
- package/dist/src/tools/vtex/catalog-admin-batch.js +0 -298
- package/dist/src/tools/vtex/catalog-admin-batch.js.map +0 -7
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { MercadoLibreApiError } from "../../../services/mercadolibre/mercadolibre-api.js";
|
|
4
|
+
const objectMock = vi.fn((value) => ({ kind: "object", value }));
|
|
5
|
+
const errorMock = vi.fn((value) => ({ kind: "error", value }));
|
|
6
|
+
const resolveMercadoLibreProfileOrSelectionMock = vi.fn();
|
|
7
|
+
const resolveMercadoLibreProductAdsContextMock = vi.fn();
|
|
8
|
+
const searchMercadoLibreProductAdsAdGroupsMock = vi.fn();
|
|
9
|
+
vi.mock("mcp-use/server", () => ({ object: objectMock, error: errorMock }));
|
|
10
|
+
vi.mock("../profile-resolution.js", () => ({
|
|
11
|
+
resolveMercadoLibreProfileOrSelection: resolveMercadoLibreProfileOrSelectionMock
|
|
12
|
+
}));
|
|
13
|
+
vi.mock("../product-ads-helpers.js", () => ({
|
|
14
|
+
resolveMercadoLibreProductAdsContext: resolveMercadoLibreProductAdsContextMock,
|
|
15
|
+
mercadolibreProductAdsProfileIdSchemaField: z.string().optional(),
|
|
16
|
+
mercadolibreProductAdsSiteIdSchemaField: z.string().optional(),
|
|
17
|
+
mercadolibreProductAdsAdvertiserIdSchemaField: z.string().optional()
|
|
18
|
+
}));
|
|
19
|
+
vi.mock("../../../services/mercadolibre/mercadolibre-product-ads.js", () => ({
|
|
20
|
+
searchMercadoLibreProductAdsAdGroups: searchMercadoLibreProductAdsAdGroupsMock
|
|
21
|
+
}));
|
|
22
|
+
const toolModule = await import("../search-product-ads-ad-groups.js");
|
|
23
|
+
describe("mercadolibre product ads ad-groups tool", () => {
|
|
24
|
+
const input = {
|
|
25
|
+
profileId: "triplim",
|
|
26
|
+
siteId: "MLA",
|
|
27
|
+
advertiserId: "89015",
|
|
28
|
+
dateFrom: "2026-06-01",
|
|
29
|
+
dateTo: "2026-06-30"
|
|
30
|
+
};
|
|
31
|
+
beforeEach(() => vi.clearAllMocks());
|
|
32
|
+
it("validates calendar dates, date ordering, page mode, and documented filter rows at the schema boundary", () => {
|
|
33
|
+
expect(
|
|
34
|
+
toolModule.meliSearchProductAdsAdGroupsSchema.safeParse({
|
|
35
|
+
...input,
|
|
36
|
+
dateFrom: "2026-06-30",
|
|
37
|
+
dateTo: "2026-06-01",
|
|
38
|
+
mode: "full_universe",
|
|
39
|
+
filters: [{ field: "", value: "active" }]
|
|
40
|
+
}).success
|
|
41
|
+
).toBe(false);
|
|
42
|
+
expect(
|
|
43
|
+
toolModule.meliSearchProductAdsAdGroupsSchema.safeParse({
|
|
44
|
+
...input,
|
|
45
|
+
dateFrom: "2026-02-30"
|
|
46
|
+
}).success
|
|
47
|
+
).toBe(false);
|
|
48
|
+
expect(
|
|
49
|
+
toolModule.meliSearchProductAdsAdGroupsSchema.safeParse({
|
|
50
|
+
...input,
|
|
51
|
+
filters: [{ field: "status", value: "active" }]
|
|
52
|
+
}).success
|
|
53
|
+
).toBe(false);
|
|
54
|
+
});
|
|
55
|
+
it("returns nested ad-group rows, metrics summary, and page continuation metadata", async () => {
|
|
56
|
+
resolveMercadoLibreProfileOrSelectionMock.mockResolvedValueOnce({ ok: true, value: { profileId: "triplim" } });
|
|
57
|
+
const rows = Array.from({ length: 50 }, (_, index) => ({
|
|
58
|
+
id: `ag-${index + 1}`,
|
|
59
|
+
campaign_id: "campaign-1",
|
|
60
|
+
status: "active",
|
|
61
|
+
metadata: { name: `Group ${index + 1}` },
|
|
62
|
+
metrics: { clicks: index + 1 }
|
|
63
|
+
}));
|
|
64
|
+
searchMercadoLibreProductAdsAdGroupsMock.mockResolvedValueOnce({
|
|
65
|
+
paging: { total: 1499, limit: 50, offset: 0 },
|
|
66
|
+
results: rows,
|
|
67
|
+
metricsSummary: { clicks: 1275 }
|
|
68
|
+
});
|
|
69
|
+
const result = await toolModule.meliSearchProductAdsAdGroupsHandler({
|
|
70
|
+
...input,
|
|
71
|
+
metrics: ["clicks"],
|
|
72
|
+
metricsSummary: true,
|
|
73
|
+
sort: "desc",
|
|
74
|
+
sortBy: "clicks",
|
|
75
|
+
filters: [{ field: "statuses", value: "active" }],
|
|
76
|
+
limit: 50
|
|
77
|
+
});
|
|
78
|
+
expect(result).toEqual({
|
|
79
|
+
kind: "object",
|
|
80
|
+
value: expect.objectContaining({
|
|
81
|
+
profile_id: "triplim",
|
|
82
|
+
site_id: "MLA",
|
|
83
|
+
advertiser_id: "89015",
|
|
84
|
+
mode: "page",
|
|
85
|
+
results: rows,
|
|
86
|
+
metrics_summary: { clicks: 1275 },
|
|
87
|
+
metadata: expect.objectContaining({
|
|
88
|
+
entity: "ad_groups",
|
|
89
|
+
fetch_mode: "page",
|
|
90
|
+
total: 1499,
|
|
91
|
+
has_more: true,
|
|
92
|
+
next_offset: 50,
|
|
93
|
+
continuation: "Re-run meli_search_product_ads_ad_groups with mode=page and offset=50, limit=50 to continue."
|
|
94
|
+
})
|
|
95
|
+
})
|
|
96
|
+
});
|
|
97
|
+
expect(searchMercadoLibreProductAdsAdGroupsMock).toHaveBeenCalledWith(
|
|
98
|
+
"triplim",
|
|
99
|
+
expect.objectContaining({ filters: { statuses: "active" }, limit: 50, offset: 0 })
|
|
100
|
+
);
|
|
101
|
+
expect(resolveMercadoLibreProductAdsContextMock).not.toHaveBeenCalled();
|
|
102
|
+
});
|
|
103
|
+
it("returns the empty page state with no continuation", async () => {
|
|
104
|
+
resolveMercadoLibreProfileOrSelectionMock.mockResolvedValueOnce({ ok: true, value: { profileId: "triplim" } });
|
|
105
|
+
searchMercadoLibreProductAdsAdGroupsMock.mockResolvedValueOnce({
|
|
106
|
+
paging: { total: 0, limit: 50, offset: 0 },
|
|
107
|
+
results: []
|
|
108
|
+
});
|
|
109
|
+
await expect(toolModule.meliSearchProductAdsAdGroupsHandler(input)).resolves.toEqual(expect.objectContaining({
|
|
110
|
+
kind: "object",
|
|
111
|
+
value: expect.objectContaining({
|
|
112
|
+
results: [],
|
|
113
|
+
metadata: expect.objectContaining({ total: 0, returned: 0, has_more: false })
|
|
114
|
+
})
|
|
115
|
+
}));
|
|
116
|
+
});
|
|
117
|
+
it("fails closed without a next offset when an upstream page is underfilled but reports more rows", async () => {
|
|
118
|
+
resolveMercadoLibreProfileOrSelectionMock.mockResolvedValueOnce({ ok: true, value: { profileId: "triplim" } });
|
|
119
|
+
searchMercadoLibreProductAdsAdGroupsMock.mockResolvedValueOnce({
|
|
120
|
+
paging: { total: 120, limit: 50, offset: 0 },
|
|
121
|
+
results: [{ id: "ag-1" }]
|
|
122
|
+
});
|
|
123
|
+
const result = await toolModule.meliSearchProductAdsAdGroupsHandler(input);
|
|
124
|
+
expect(result).toEqual(
|
|
125
|
+
expect.objectContaining({
|
|
126
|
+
kind: "object",
|
|
127
|
+
value: expect.objectContaining({
|
|
128
|
+
metadata: expect.objectContaining({
|
|
129
|
+
has_more: false,
|
|
130
|
+
continuation: expect.stringContaining("Do not advance the offset; re-run meli_search_product_ads_ad_groups with mode=page and offset=0, limit=50")
|
|
131
|
+
})
|
|
132
|
+
})
|
|
133
|
+
})
|
|
134
|
+
);
|
|
135
|
+
expect(result.value.metadata).not.toHaveProperty(
|
|
136
|
+
"next_offset"
|
|
137
|
+
);
|
|
138
|
+
});
|
|
139
|
+
it.each([401, 403, 404])("preserves undocumented upstream %s errors through standard handling", async (status) => {
|
|
140
|
+
resolveMercadoLibreProfileOrSelectionMock.mockResolvedValueOnce({ ok: true, value: { profileId: "triplim" } });
|
|
141
|
+
searchMercadoLibreProductAdsAdGroupsMock.mockRejectedValueOnce(new MercadoLibreApiError(`upstream ${status}`, status));
|
|
142
|
+
await expect(toolModule.meliSearchProductAdsAdGroupsHandler(input)).resolves.toEqual({
|
|
143
|
+
kind: "error",
|
|
144
|
+
value: `upstream ${status}`
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
//# sourceMappingURL=product-ads-ad-groups.test.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../../src/tools/mercadolibre/__tests__/product-ads-ad-groups.test.ts"],
|
|
4
|
+
"sourcesContent": ["import { beforeEach, describe, expect, it, vi } from \"vitest\";\nimport { z } from \"zod\";\n\nimport { MercadoLibreApiError } from \"../../../services/mercadolibre/mercadolibre-api.js\";\n\nconst objectMock = vi.fn((value) => ({ kind: \"object\", value }));\nconst errorMock = vi.fn((value) => ({ kind: \"error\", value }));\nconst resolveMercadoLibreProfileOrSelectionMock = vi.fn();\nconst resolveMercadoLibreProductAdsContextMock = vi.fn();\nconst searchMercadoLibreProductAdsAdGroupsMock = vi.fn();\n\nvi.mock(\"mcp-use/server\", () => ({ object: objectMock, error: errorMock }));\nvi.mock(\"../profile-resolution.js\", () => ({\n resolveMercadoLibreProfileOrSelection: resolveMercadoLibreProfileOrSelectionMock,\n}));\nvi.mock(\"../product-ads-helpers.js\", () => ({\n resolveMercadoLibreProductAdsContext: resolveMercadoLibreProductAdsContextMock,\n mercadolibreProductAdsProfileIdSchemaField: z.string().optional(),\n mercadolibreProductAdsSiteIdSchemaField: z.string().optional(),\n mercadolibreProductAdsAdvertiserIdSchemaField: z.string().optional(),\n}));\nvi.mock(\"../../../services/mercadolibre/mercadolibre-product-ads.js\", () => ({\n searchMercadoLibreProductAdsAdGroups: searchMercadoLibreProductAdsAdGroupsMock,\n}));\n\nconst toolModule = await import(\"../search-product-ads-ad-groups.js\");\n\ndescribe(\"mercadolibre product ads ad-groups tool\", () => {\n const input = {\n profileId: \"triplim\",\n siteId: \"MLA\",\n advertiserId: \"89015\",\n dateFrom: \"2026-06-01\",\n dateTo: \"2026-06-30\",\n };\n\n beforeEach(() => vi.clearAllMocks());\n\n it(\"validates calendar dates, date ordering, page mode, and documented filter rows at the schema boundary\", () => {\n expect(\n toolModule.meliSearchProductAdsAdGroupsSchema.safeParse({\n ...input,\n dateFrom: \"2026-06-30\",\n dateTo: \"2026-06-01\",\n mode: \"full_universe\",\n filters: [{ field: \"\", value: \"active\" }],\n }).success\n ).toBe(false);\n\n expect(\n toolModule.meliSearchProductAdsAdGroupsSchema.safeParse({\n ...input,\n dateFrom: \"2026-02-30\",\n }).success\n ).toBe(false);\n\n expect(\n toolModule.meliSearchProductAdsAdGroupsSchema.safeParse({\n ...input,\n filters: [{ field: \"status\", value: \"active\" }],\n }).success\n ).toBe(false);\n });\n\n it(\"returns nested ad-group rows, metrics summary, and page continuation metadata\", async () => {\n resolveMercadoLibreProfileOrSelectionMock.mockResolvedValueOnce({ ok: true, value: { profileId: \"triplim\" } });\n const rows = Array.from({ length: 50 }, (_, index) => ({\n id: `ag-${index + 1}`,\n campaign_id: \"campaign-1\",\n status: \"active\",\n metadata: { name: `Group ${index + 1}` },\n metrics: { clicks: index + 1 },\n }));\n searchMercadoLibreProductAdsAdGroupsMock.mockResolvedValueOnce({\n paging: { total: 1499, limit: 50, offset: 0 },\n results: rows,\n metricsSummary: { clicks: 1275 },\n });\n\n const result = await toolModule.meliSearchProductAdsAdGroupsHandler({\n ...input,\n metrics: [\"clicks\"],\n metricsSummary: true,\n sort: \"desc\",\n sortBy: \"clicks\",\n filters: [{ field: \"statuses\", value: \"active\" }],\n limit: 50,\n } as never);\n\n expect(result).toEqual({\n kind: \"object\",\n value: expect.objectContaining({\n profile_id: \"triplim\",\n site_id: \"MLA\",\n advertiser_id: \"89015\",\n mode: \"page\",\n results: rows,\n metrics_summary: { clicks: 1275 },\n metadata: expect.objectContaining({\n entity: \"ad_groups\",\n fetch_mode: \"page\",\n total: 1499,\n has_more: true,\n next_offset: 50,\n continuation: \"Re-run meli_search_product_ads_ad_groups with mode=page and offset=50, limit=50 to continue.\",\n }),\n }),\n });\n expect(searchMercadoLibreProductAdsAdGroupsMock).toHaveBeenCalledWith(\n \"triplim\",\n expect.objectContaining({ filters: { statuses: \"active\" }, limit: 50, offset: 0 })\n );\n expect(resolveMercadoLibreProductAdsContextMock).not.toHaveBeenCalled();\n });\n\n it(\"returns the empty page state with no continuation\", async () => {\n resolveMercadoLibreProfileOrSelectionMock.mockResolvedValueOnce({ ok: true, value: { profileId: \"triplim\" } });\n searchMercadoLibreProductAdsAdGroupsMock.mockResolvedValueOnce({\n paging: { total: 0, limit: 50, offset: 0 },\n results: [],\n });\n\n await expect(toolModule.meliSearchProductAdsAdGroupsHandler(input as never)).resolves.toEqual(expect.objectContaining({\n kind: \"object\",\n value: expect.objectContaining({\n results: [],\n metadata: expect.objectContaining({ total: 0, returned: 0, has_more: false }),\n }),\n }));\n });\n\n it(\"fails closed without a next offset when an upstream page is underfilled but reports more rows\", async () => {\n resolveMercadoLibreProfileOrSelectionMock.mockResolvedValueOnce({ ok: true, value: { profileId: \"triplim\" } });\n searchMercadoLibreProductAdsAdGroupsMock.mockResolvedValueOnce({\n paging: { total: 120, limit: 50, offset: 0 },\n results: [{ id: \"ag-1\" }],\n });\n\n const result = await toolModule.meliSearchProductAdsAdGroupsHandler(input as never);\n\n expect(result).toEqual(\n expect.objectContaining({\n kind: \"object\",\n value: expect.objectContaining({\n metadata: expect.objectContaining({\n has_more: false,\n continuation: expect.stringContaining(\"Do not advance the offset; re-run meli_search_product_ads_ad_groups with mode=page and offset=0, limit=50\"),\n }),\n }),\n })\n );\n expect((result as unknown as { value: { metadata: Record<string, unknown> } }).value.metadata).not.toHaveProperty(\n \"next_offset\"\n );\n });\n\n it.each([401, 403, 404])(\"preserves undocumented upstream %s errors through standard handling\", async (status) => {\n resolveMercadoLibreProfileOrSelectionMock.mockResolvedValueOnce({ ok: true, value: { profileId: \"triplim\" } });\n searchMercadoLibreProductAdsAdGroupsMock.mockRejectedValueOnce(new MercadoLibreApiError(`upstream ${status}`, status));\n\n await expect(toolModule.meliSearchProductAdsAdGroupsHandler(input as never)).resolves.toEqual({\n kind: \"error\",\n value: `upstream ${status}`,\n });\n });\n});\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { MercadoLibreApiError } from "../../../services/mercadolibre/mercadolibre-api.js";
|
|
4
|
+
const objectMock = vi.fn((value) => ({ kind: "object", value }));
|
|
5
|
+
const errorMock = vi.fn((value) => ({ kind: "error", value }));
|
|
6
|
+
const resolveProfileMock = vi.fn();
|
|
7
|
+
const resolveContextMock = vi.fn();
|
|
8
|
+
const searchCampaignsMock = vi.fn();
|
|
9
|
+
vi.mock("mcp-use/server", () => ({ object: objectMock, error: errorMock }));
|
|
10
|
+
vi.mock("../profile-resolution.js", () => ({ resolveMercadoLibreProfileOrSelection: resolveProfileMock }));
|
|
11
|
+
vi.mock("../product-ads-helpers.js", () => ({
|
|
12
|
+
resolveMercadoLibreProductAdsContext: resolveContextMock,
|
|
13
|
+
mercadolibreProductAdsProfileIdSchemaField: z.string().optional(),
|
|
14
|
+
mercadolibreProductAdsSiteIdSchemaField: z.string().optional(),
|
|
15
|
+
mercadolibreProductAdsAdvertiserIdSchemaField: z.string().optional()
|
|
16
|
+
}));
|
|
17
|
+
vi.mock("../../../services/mercadolibre/mercadolibre-product-ads.js", () => ({
|
|
18
|
+
searchMercadoLibreProductAdsCampaigns: searchCampaignsMock
|
|
19
|
+
}));
|
|
20
|
+
const toolModule = await import("../search-product-ads-campaigns.js");
|
|
21
|
+
describe("mercadolibre product ads campaigns tool", () => {
|
|
22
|
+
const input = { profileId: "triplim", siteId: "MLA", advertiserId: "89015", dateFrom: "2026-06-01", dateTo: "2026-06-30" };
|
|
23
|
+
beforeEach(() => vi.clearAllMocks());
|
|
24
|
+
it("validates real calendar dates, ordering, and page mode", () => {
|
|
25
|
+
expect(toolModule.meliSearchProductAdsCampaignsSchema.safeParse({ ...input, dateFrom: "2026-02-30" }).success).toBe(false);
|
|
26
|
+
expect(toolModule.meliSearchProductAdsCampaignsSchema.safeParse({ ...input, dateFrom: "2026-06-30", dateTo: "2026-06-01" }).success).toBe(false);
|
|
27
|
+
expect(toolModule.meliSearchProductAdsCampaignsSchema.safeParse({ ...input, mode: "all" }).success).toBe(false);
|
|
28
|
+
expect(toolModule.meliSearchProductAdsCampaignsSchema.safeParse({ ...input, limit: 0 }).success).toBe(false);
|
|
29
|
+
});
|
|
30
|
+
it("returns a raw page and campaign-to-ad-groups chaining guidance", async () => {
|
|
31
|
+
resolveProfileMock.mockResolvedValueOnce({ ok: true, value: { profileId: "triplim" } });
|
|
32
|
+
const results = [{ id: "campaign-1", strategy: { type: "manual" }, budget: { amount: 1e3 } }];
|
|
33
|
+
searchCampaignsMock.mockResolvedValueOnce({ paging: { total: 5, limit: 1, offset: 0 }, results });
|
|
34
|
+
await expect(toolModule.meliSearchProductAdsCampaignsHandler({ ...input, limit: 1 })).resolves.toEqual({
|
|
35
|
+
kind: "object",
|
|
36
|
+
value: expect.objectContaining({
|
|
37
|
+
results,
|
|
38
|
+
metadata: expect.objectContaining({ entity: "campaigns", next_offset: 1, ad_groups_hint: expect.stringContaining("campaign id") })
|
|
39
|
+
})
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
it("logs structured campaign discovery observations from the feature boundary", async () => {
|
|
43
|
+
const infoSpy = vi.spyOn(console, "info").mockImplementation(() => void 0);
|
|
44
|
+
resolveProfileMock.mockResolvedValueOnce({ ok: true, value: { profileId: "triplim" } });
|
|
45
|
+
searchCampaignsMock.mockImplementationOnce(async (_profile, _params, options) => {
|
|
46
|
+
options.onObservation({ event: "retry", attempt: 1, elapsed_ms: 250, status: 503, reason: "retryable_error" });
|
|
47
|
+
return { paging: { total: 0, limit: 1, offset: 0 }, results: [] };
|
|
48
|
+
});
|
|
49
|
+
await toolModule.meliSearchProductAdsCampaignsHandler({ ...input, limit: 1 });
|
|
50
|
+
expect(infoSpy).toHaveBeenCalledWith(expect.stringContaining('"event":"mercadolibre.product_ads.campaigns.discovery"'));
|
|
51
|
+
expect(infoSpy).toHaveBeenCalledWith(expect.stringContaining('"reason":"retryable_error"'));
|
|
52
|
+
infoSpy.mockRestore();
|
|
53
|
+
});
|
|
54
|
+
it.each([401, 403, 404])("preserves upstream %s errors", async (status) => {
|
|
55
|
+
resolveProfileMock.mockResolvedValueOnce({ ok: true, value: { profileId: "triplim" } });
|
|
56
|
+
searchCampaignsMock.mockRejectedValueOnce(new MercadoLibreApiError(`upstream ${status}`, status));
|
|
57
|
+
await expect(toolModule.meliSearchProductAdsCampaignsHandler(input)).resolves.toEqual({ kind: "error", value: `upstream ${status}` });
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
//# sourceMappingURL=product-ads-campaigns.test.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../../src/tools/mercadolibre/__tests__/product-ads-campaigns.test.ts"],
|
|
4
|
+
"sourcesContent": ["import { beforeEach, describe, expect, it, vi } from \"vitest\";\nimport { z } from \"zod\";\n\nimport { MercadoLibreApiError } from \"../../../services/mercadolibre/mercadolibre-api.js\";\n\nconst objectMock = vi.fn((value) => ({ kind: \"object\", value }));\nconst errorMock = vi.fn((value) => ({ kind: \"error\", value }));\nconst resolveProfileMock = vi.fn();\nconst resolveContextMock = vi.fn();\nconst searchCampaignsMock = vi.fn();\n\nvi.mock(\"mcp-use/server\", () => ({ object: objectMock, error: errorMock }));\nvi.mock(\"../profile-resolution.js\", () => ({ resolveMercadoLibreProfileOrSelection: resolveProfileMock }));\nvi.mock(\"../product-ads-helpers.js\", () => ({\n resolveMercadoLibreProductAdsContext: resolveContextMock,\n mercadolibreProductAdsProfileIdSchemaField: z.string().optional(),\n mercadolibreProductAdsSiteIdSchemaField: z.string().optional(),\n mercadolibreProductAdsAdvertiserIdSchemaField: z.string().optional(),\n}));\nvi.mock(\"../../../services/mercadolibre/mercadolibre-product-ads.js\", () => ({\n searchMercadoLibreProductAdsCampaigns: searchCampaignsMock,\n}));\n\nconst toolModule = await import(\"../search-product-ads-campaigns.js\");\n\ndescribe(\"mercadolibre product ads campaigns tool\", () => {\n const input = { profileId: \"triplim\", siteId: \"MLA\", advertiserId: \"89015\", dateFrom: \"2026-06-01\", dateTo: \"2026-06-30\" };\n beforeEach(() => vi.clearAllMocks());\n\n it(\"validates real calendar dates, ordering, and page mode\", () => {\n expect(toolModule.meliSearchProductAdsCampaignsSchema.safeParse({ ...input, dateFrom: \"2026-02-30\" }).success).toBe(false);\n expect(toolModule.meliSearchProductAdsCampaignsSchema.safeParse({ ...input, dateFrom: \"2026-06-30\", dateTo: \"2026-06-01\" }).success).toBe(false);\n expect(toolModule.meliSearchProductAdsCampaignsSchema.safeParse({ ...input, mode: \"all\" }).success).toBe(false);\n expect(toolModule.meliSearchProductAdsCampaignsSchema.safeParse({ ...input, limit: 0 }).success).toBe(false);\n });\n\n it(\"returns a raw page and campaign-to-ad-groups chaining guidance\", async () => {\n resolveProfileMock.mockResolvedValueOnce({ ok: true, value: { profileId: \"triplim\" } });\n const results = [{ id: \"campaign-1\", strategy: { type: \"manual\" }, budget: { amount: 1000 } }];\n searchCampaignsMock.mockResolvedValueOnce({ paging: { total: 5, limit: 1, offset: 0 }, results });\n\n await expect(toolModule.meliSearchProductAdsCampaignsHandler({ ...input, limit: 1 } as never)).resolves.toEqual({\n kind: \"object\", value: expect.objectContaining({\n results,\n metadata: expect.objectContaining({ entity: \"campaigns\", next_offset: 1, ad_groups_hint: expect.stringContaining(\"campaign id\") }),\n }),\n });\n });\n\n it(\"logs structured campaign discovery observations from the feature boundary\", async () => {\n const infoSpy = vi.spyOn(console, \"info\").mockImplementation(() => undefined);\n resolveProfileMock.mockResolvedValueOnce({ ok: true, value: { profileId: \"triplim\" } });\n searchCampaignsMock.mockImplementationOnce(async (_profile, _params, options) => {\n options.onObservation({ event: \"retry\", attempt: 1, elapsed_ms: 250, status: 503, reason: \"retryable_error\" });\n return { paging: { total: 0, limit: 1, offset: 0 }, results: [] };\n });\n\n await toolModule.meliSearchProductAdsCampaignsHandler({ ...input, limit: 1 } as never);\n\n expect(infoSpy).toHaveBeenCalledWith(expect.stringContaining('\"event\":\"mercadolibre.product_ads.campaigns.discovery\"'));\n expect(infoSpy).toHaveBeenCalledWith(expect.stringContaining('\"reason\":\"retryable_error\"'));\n infoSpy.mockRestore();\n });\n\n it.each([401, 403, 404])(\"preserves upstream %s errors\", async (status) => {\n resolveProfileMock.mockResolvedValueOnce({ ok: true, value: { profileId: \"triplim\" } });\n searchCampaignsMock.mockRejectedValueOnce(new MercadoLibreApiError(`upstream ${status}`, status));\n await expect(toolModule.meliSearchProductAdsCampaignsHandler(input as never)).resolves.toEqual({ kind: \"error\", value: `upstream ${status}` });\n });\n});\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from "vitest";
|
|
2
|
+
const objectMock = vi.fn((value) => ({ kind: "object", value }));
|
|
3
|
+
const resolveMercadoLibreProfileOrSelectionMock = vi.fn();
|
|
4
|
+
const listMercadoLibreProductAdsAdvertisersMock = vi.fn();
|
|
5
|
+
vi.mock("mcp-use/server", () => ({
|
|
6
|
+
object: objectMock
|
|
7
|
+
}));
|
|
8
|
+
vi.mock("../profile-resolution.js", () => ({
|
|
9
|
+
resolveMercadoLibreProfileOrSelection: resolveMercadoLibreProfileOrSelectionMock
|
|
10
|
+
}));
|
|
11
|
+
vi.mock("../../../services/mercadolibre/mercadolibre-product-ads.js", () => ({
|
|
12
|
+
listMercadoLibreProductAdsAdvertisers: listMercadoLibreProductAdsAdvertisersMock
|
|
13
|
+
}));
|
|
14
|
+
const helpersModule = await import("../product-ads-helpers.js");
|
|
15
|
+
describe("mercadolibre product ads helpers", () => {
|
|
16
|
+
it("passes through shared profile selection responses", async () => {
|
|
17
|
+
resolveMercadoLibreProfileOrSelectionMock.mockResolvedValueOnce({
|
|
18
|
+
ok: false,
|
|
19
|
+
response: { kind: "object", value: { selection_required: true } }
|
|
20
|
+
});
|
|
21
|
+
await expect(
|
|
22
|
+
helpersModule.resolveMercadoLibreProductAdsContext({ profileId: "profile-1" })
|
|
23
|
+
).resolves.toEqual({
|
|
24
|
+
ok: false,
|
|
25
|
+
response: { kind: "object", value: { selection_required: true } }
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
it("auto-resolves a single available advertiser when no context is provided", async () => {
|
|
29
|
+
resolveMercadoLibreProfileOrSelectionMock.mockResolvedValueOnce({
|
|
30
|
+
ok: true,
|
|
31
|
+
value: { profileId: "profile-1" }
|
|
32
|
+
});
|
|
33
|
+
listMercadoLibreProductAdsAdvertisersMock.mockResolvedValueOnce([
|
|
34
|
+
{
|
|
35
|
+
advertiser_id: "adv-1",
|
|
36
|
+
advertiser_site_id: "MLA",
|
|
37
|
+
account_name: "Main account",
|
|
38
|
+
business_name: "Main business",
|
|
39
|
+
status: "active"
|
|
40
|
+
}
|
|
41
|
+
]);
|
|
42
|
+
await expect(helpersModule.resolveMercadoLibreProductAdsContext({ profileId: "profile-1" })).resolves.toEqual(
|
|
43
|
+
{
|
|
44
|
+
ok: true,
|
|
45
|
+
value: {
|
|
46
|
+
profileId: "profile-1",
|
|
47
|
+
siteId: "MLA",
|
|
48
|
+
advertiserId: "adv-1",
|
|
49
|
+
advertiser: {
|
|
50
|
+
advertiser_id: "adv-1",
|
|
51
|
+
advertiser_site_id: "MLA",
|
|
52
|
+
account_name: "Main account",
|
|
53
|
+
business_name: "Main business",
|
|
54
|
+
status: "active"
|
|
55
|
+
},
|
|
56
|
+
availableAdvertisers: [
|
|
57
|
+
{
|
|
58
|
+
advertiser_id: "adv-1",
|
|
59
|
+
advertiser_site_id: "MLA",
|
|
60
|
+
account_name: "Main account",
|
|
61
|
+
business_name: "Main business",
|
|
62
|
+
status: "active"
|
|
63
|
+
}
|
|
64
|
+
],
|
|
65
|
+
resolutionMode: "auto_single_match"
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
);
|
|
69
|
+
});
|
|
70
|
+
it("guides discovery without asking for impossible advertiser pairs when none are available", async () => {
|
|
71
|
+
resolveMercadoLibreProfileOrSelectionMock.mockResolvedValueOnce({
|
|
72
|
+
ok: true,
|
|
73
|
+
value: { profileId: "profile-1" }
|
|
74
|
+
});
|
|
75
|
+
listMercadoLibreProductAdsAdvertisersMock.mockResolvedValueOnce([]);
|
|
76
|
+
const result = await helpersModule.resolveMercadoLibreProductAdsContext({ profileId: "profile-1" });
|
|
77
|
+
expect(result).toEqual({
|
|
78
|
+
ok: false,
|
|
79
|
+
response: {
|
|
80
|
+
kind: "object",
|
|
81
|
+
value: expect.objectContaining({
|
|
82
|
+
selection_required: true,
|
|
83
|
+
reason: "missing_advertiser_context",
|
|
84
|
+
profile_id: "profile-1",
|
|
85
|
+
message: expect.stringContaining("No Product Ads advertisers"),
|
|
86
|
+
resolution_hint: "Re-run advertiser discovery after the profile exposes Product Ads advertisers."
|
|
87
|
+
})
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
if (!result.ok) {
|
|
91
|
+
expect(result.response.value).not.toHaveProperty("required_fields");
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
it("returns a guided selection response when the advertiser context is incomplete", async () => {
|
|
95
|
+
resolveMercadoLibreProfileOrSelectionMock.mockResolvedValueOnce({
|
|
96
|
+
ok: true,
|
|
97
|
+
value: { profileId: "profile-1" }
|
|
98
|
+
});
|
|
99
|
+
listMercadoLibreProductAdsAdvertisersMock.mockResolvedValueOnce([
|
|
100
|
+
{
|
|
101
|
+
advertiser_id: "adv-1",
|
|
102
|
+
advertiser_site_id: "MLA"
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
advertiser_id: "adv-2",
|
|
106
|
+
advertiser_site_id: "MLB"
|
|
107
|
+
}
|
|
108
|
+
]);
|
|
109
|
+
await expect(
|
|
110
|
+
helpersModule.resolveMercadoLibreProductAdsContext({
|
|
111
|
+
profileId: "profile-1",
|
|
112
|
+
siteId: "MLA"
|
|
113
|
+
})
|
|
114
|
+
).resolves.toEqual({
|
|
115
|
+
ok: false,
|
|
116
|
+
response: {
|
|
117
|
+
kind: "object",
|
|
118
|
+
value: expect.objectContaining({
|
|
119
|
+
selection_required: true,
|
|
120
|
+
reason: "incomplete_advertiser_context",
|
|
121
|
+
profile_id: "profile-1",
|
|
122
|
+
resolution_hint: "If you need help choosing a pair, first run the advertiser discovery tool, then pass the selected siteId and advertiserId to the page-mode ads search tool."
|
|
123
|
+
})
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
//# sourceMappingURL=product-ads-helpers.test.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../../src/tools/mercadolibre/__tests__/product-ads-helpers.test.ts"],
|
|
4
|
+
"sourcesContent": ["import { describe, expect, it, vi } from \"vitest\";\n\nconst objectMock = vi.fn((value) => ({ kind: \"object\", value }));\nconst resolveMercadoLibreProfileOrSelectionMock = vi.fn();\nconst listMercadoLibreProductAdsAdvertisersMock = vi.fn();\n\nvi.mock(\"mcp-use/server\", () => ({\n object: objectMock,\n}));\n\nvi.mock(\"../profile-resolution.js\", () => ({\n resolveMercadoLibreProfileOrSelection: resolveMercadoLibreProfileOrSelectionMock,\n}));\n\nvi.mock(\"../../../services/mercadolibre/mercadolibre-product-ads.js\", () => ({\n listMercadoLibreProductAdsAdvertisers: listMercadoLibreProductAdsAdvertisersMock,\n}));\n\nconst helpersModule = await import(\"../product-ads-helpers.js\");\n\ndescribe(\"mercadolibre product ads helpers\", () => {\n it(\"passes through shared profile selection responses\", async () => {\n resolveMercadoLibreProfileOrSelectionMock.mockResolvedValueOnce({\n ok: false,\n response: { kind: \"object\", value: { selection_required: true } },\n });\n\n await expect(\n helpersModule.resolveMercadoLibreProductAdsContext({ profileId: \"profile-1\" })\n ).resolves.toEqual({\n ok: false,\n response: { kind: \"object\", value: { selection_required: true } },\n });\n });\n\n it(\"auto-resolves a single available advertiser when no context is provided\", async () => {\n resolveMercadoLibreProfileOrSelectionMock.mockResolvedValueOnce({\n ok: true,\n value: { profileId: \"profile-1\" },\n });\n listMercadoLibreProductAdsAdvertisersMock.mockResolvedValueOnce([\n {\n advertiser_id: \"adv-1\",\n advertiser_site_id: \"MLA\",\n account_name: \"Main account\",\n business_name: \"Main business\",\n status: \"active\",\n },\n ]);\n\n await expect(helpersModule.resolveMercadoLibreProductAdsContext({ profileId: \"profile-1\" })).resolves.toEqual(\n {\n ok: true,\n value: {\n profileId: \"profile-1\",\n siteId: \"MLA\",\n advertiserId: \"adv-1\",\n advertiser: {\n advertiser_id: \"adv-1\",\n advertiser_site_id: \"MLA\",\n account_name: \"Main account\",\n business_name: \"Main business\",\n status: \"active\",\n },\n availableAdvertisers: [\n {\n advertiser_id: \"adv-1\",\n advertiser_site_id: \"MLA\",\n account_name: \"Main account\",\n business_name: \"Main business\",\n status: \"active\",\n },\n ],\n resolutionMode: \"auto_single_match\",\n },\n }\n );\n });\n\n it(\"guides discovery without asking for impossible advertiser pairs when none are available\", async () => {\n resolveMercadoLibreProfileOrSelectionMock.mockResolvedValueOnce({\n ok: true,\n value: { profileId: \"profile-1\" },\n });\n listMercadoLibreProductAdsAdvertisersMock.mockResolvedValueOnce([]);\n\n const result = await helpersModule.resolveMercadoLibreProductAdsContext({ profileId: \"profile-1\" });\n\n expect(result).toEqual({\n ok: false,\n response: {\n kind: \"object\",\n value: expect.objectContaining({\n selection_required: true,\n reason: \"missing_advertiser_context\",\n profile_id: \"profile-1\",\n message: expect.stringContaining(\"No Product Ads advertisers\"),\n resolution_hint:\n \"Re-run advertiser discovery after the profile exposes Product Ads advertisers.\",\n }),\n },\n });\n if (!result.ok) {\n expect(result.response.value).not.toHaveProperty(\"required_fields\");\n }\n });\n\n it(\"returns a guided selection response when the advertiser context is incomplete\", async () => {\n resolveMercadoLibreProfileOrSelectionMock.mockResolvedValueOnce({\n ok: true,\n value: { profileId: \"profile-1\" },\n });\n listMercadoLibreProductAdsAdvertisersMock.mockResolvedValueOnce([\n {\n advertiser_id: \"adv-1\",\n advertiser_site_id: \"MLA\",\n },\n {\n advertiser_id: \"adv-2\",\n advertiser_site_id: \"MLB\",\n },\n ]);\n\n await expect(\n helpersModule.resolveMercadoLibreProductAdsContext({\n profileId: \"profile-1\",\n siteId: \"MLA\",\n })\n ).resolves.toEqual({\n ok: false,\n response: {\n kind: \"object\",\n value: expect.objectContaining({\n selection_required: true,\n reason: \"incomplete_advertiser_context\",\n profile_id: \"profile-1\",\n resolution_hint:\n \"If you need help choosing a pair, first run the advertiser discovery tool, then pass the selected siteId and advertiserId to the page-mode ads search tool.\",\n }),\n },\n });\n });\n});\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|