@yoryoboy/bi-mcp 1.10.0 → 1.13.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 +4 -5
- package/dist/.tsbuildinfo +1 -1
- package/dist/index.js +398 -8
- package/dist/index.js.map +2 -2
- package/dist/mcp-use.json +2 -2
- package/dist/src/config/__tests__/meta.test.js +35 -0
- package/dist/src/config/__tests__/meta.test.js.map +7 -0
- package/dist/src/config/mercadolibre-profile-store.js +25 -0
- package/dist/src/config/mercadolibre-profile-store.js.map +2 -2
- package/dist/src/config/meta-store.js +109 -0
- package/dist/src/config/meta-store.js.map +7 -0
- package/dist/src/config/meta.js +0 -2
- package/dist/src/config/meta.js.map +2 -2
- package/dist/src/meta/__tests__/meta-utils.test.js +155 -0
- package/dist/src/meta/__tests__/meta-utils.test.js.map +7 -0
- package/dist/src/services/mercadolibre/__tests__/mercadolibre-api.test.js +265 -0
- package/dist/src/services/mercadolibre/__tests__/mercadolibre-api.test.js.map +7 -0
- package/dist/src/services/mercadolibre/__tests__/mercadolibre-items.test.js +311 -0
- package/dist/src/services/mercadolibre/__tests__/mercadolibre-items.test.js.map +7 -0
- package/dist/src/services/mercadolibre/__tests__/mercadolibre-orders.test.js +220 -0
- package/dist/src/services/mercadolibre/__tests__/mercadolibre-orders.test.js.map +7 -0
- package/dist/src/services/meta/__tests__/meta-ads.test.js +126 -0
- package/dist/src/services/meta/__tests__/meta-ads.test.js.map +7 -0
- package/dist/src/services/meta/__tests__/meta-api.test.js +70 -0
- package/dist/src/services/meta/__tests__/meta-api.test.js.map +7 -0
- package/dist/src/services/meta/meta-ads.js +94 -40
- package/dist/src/services/meta/meta-ads.js.map +2 -2
- package/dist/src/services/meta/meta-api.js +8 -6
- package/dist/src/services/meta/meta-api.js.map +2 -2
- package/dist/src/services/vtex/__tests__/vtex-catalog-write-batch.test.js +165 -0
- package/dist/src/services/vtex/__tests__/vtex-catalog-write-batch.test.js.map +7 -0
- package/dist/src/services/vtex/__tests__/vtex-catalog-write-eans.test.js +92 -0
- package/dist/src/services/vtex/__tests__/vtex-catalog-write-eans.test.js.map +7 -0
- package/dist/src/services/vtex/__tests__/vtex-catalog-write-products.test.js +41 -0
- package/dist/src/services/vtex/__tests__/vtex-catalog-write-products.test.js.map +7 -0
- package/dist/src/services/vtex/__tests__/vtex-catalog.test.js +80 -0
- package/dist/src/services/vtex/__tests__/vtex-catalog.test.js.map +7 -0
- package/dist/src/services/vtex/__tests__/vtex-orders-write-http.test.js +85 -0
- package/dist/src/services/vtex/__tests__/vtex-orders-write-http.test.js.map +7 -0
- package/dist/src/services/vtex/__tests__/vtex-orders-write-state-validation.test.js +251 -0
- package/dist/src/services/vtex/__tests__/vtex-orders-write-state-validation.test.js.map +7 -0
- package/dist/src/services/vtex/__tests__/vtex-write.test.js +111 -0
- package/dist/src/services/vtex/__tests__/vtex-write.test.js.map +7 -0
- package/dist/src/services/vtex/vtex-catalog-write.js +194 -1
- package/dist/src/services/vtex/vtex-catalog-write.js.map +2 -2
- package/dist/src/services/vtex/vtex-catalog.js +118 -1
- package/dist/src/services/vtex/vtex-catalog.js.map +2 -2
- package/dist/src/services/vtex/vtex-pricing-write.js +5 -0
- package/dist/src/services/vtex/vtex-pricing-write.js.map +2 -2
- package/dist/src/services/vtex/vtex-write.js +67 -1
- package/dist/src/services/vtex/vtex-write.js.map +2 -2
- package/dist/src/tools/config/list-profiles.js +85 -11
- package/dist/src/tools/config/list-profiles.js.map +2 -2
- package/dist/src/tools/index.js.map +2 -2
- package/dist/src/tools/mercadolibre/__tests__/helpers.test.js +37 -0
- package/dist/src/tools/mercadolibre/__tests__/helpers.test.js.map +7 -0
- package/dist/src/tools/mercadolibre/__tests__/profile-resolution.test.js +30 -0
- package/dist/src/tools/mercadolibre/__tests__/profile-resolution.test.js.map +7 -0
- package/dist/src/tools/mercadolibre/profile-resolution.js +4 -50
- package/dist/src/tools/mercadolibre/profile-resolution.js.map +2 -2
- package/dist/src/tools/meta/__tests__/pagination.test.js +133 -0
- package/dist/src/tools/meta/__tests__/pagination.test.js.map +7 -0
- package/dist/src/tools/meta/__tests__/profile-access.test.js +262 -0
- package/dist/src/tools/meta/__tests__/profile-access.test.js.map +7 -0
- package/dist/src/tools/meta/__tests__/read-tools.test.js +722 -0
- package/dist/src/tools/meta/__tests__/read-tools.test.js.map +7 -0
- package/dist/src/tools/meta/__tests__/schemas.test.js +103 -0
- package/dist/src/tools/meta/__tests__/schemas.test.js.map +7 -0
- package/dist/src/tools/meta/account-overview.js +37 -19
- package/dist/src/tools/meta/account-overview.js.map +2 -2
- package/dist/src/tools/meta/ad-account-info.js +31 -6
- package/dist/src/tools/meta/ad-account-info.js.map +2 -2
- package/dist/src/tools/meta/ads-performance.js +35 -21
- package/dist/src/tools/meta/ads-performance.js.map +2 -2
- package/dist/src/tools/meta/campaign-performance.js +35 -18
- package/dist/src/tools/meta/campaign-performance.js.map +2 -2
- package/dist/src/tools/meta/list-accessible-ad-accounts.js +39 -10
- package/dist/src/tools/meta/list-accessible-ad-accounts.js.map +2 -2
- package/dist/src/tools/meta/list-accessible-businesses.js +37 -13
- package/dist/src/tools/meta/list-accessible-businesses.js.map +2 -2
- package/dist/src/tools/meta/placement-mix.js +37 -22
- package/dist/src/tools/meta/placement-mix.js.map +2 -2
- package/dist/src/tools/meta/profile-access.js +215 -0
- package/dist/src/tools/meta/profile-access.js.map +7 -0
- package/dist/src/tools/meta/schema-helpers.js +19 -0
- package/dist/src/tools/meta/schema-helpers.js.map +7 -0
- package/dist/src/tools/meta/time-series.js +40 -23
- package/dist/src/tools/meta/time-series.js.map +2 -2
- package/dist/src/tools/vtex/__tests__/catalog-admin-batch.test.js +233 -0
- package/dist/src/tools/vtex/__tests__/catalog-admin-batch.test.js.map +7 -0
- package/dist/src/tools/vtex/__tests__/catalog-admin-categories.test.js +649 -0
- package/dist/src/tools/vtex/__tests__/catalog-admin-categories.test.js.map +7 -0
- package/dist/src/tools/vtex/__tests__/catalog-admin-product-specifications.test.js +339 -0
- package/dist/src/tools/vtex/__tests__/catalog-admin-product-specifications.test.js.map +7 -0
- package/dist/src/tools/vtex/__tests__/catalog-admin-products-skus.test.js +235 -0
- package/dist/src/tools/vtex/__tests__/catalog-admin-products-skus.test.js.map +7 -0
- package/dist/src/tools/vtex/__tests__/catalog-navigation-reads.test.js +372 -0
- package/dist/src/tools/vtex/__tests__/catalog-navigation-reads.test.js.map +7 -0
- package/dist/src/tools/vtex/__tests__/catalog-navigation-surface.test.js +57 -0
- package/dist/src/tools/vtex/__tests__/catalog-navigation-surface.test.js.map +7 -0
- package/dist/src/tools/vtex/__tests__/write-helpers.test.js +97 -0
- package/dist/src/tools/vtex/__tests__/write-helpers.test.js.map +7 -0
- package/dist/src/tools/vtex/activate-sku.js +1 -48
- package/dist/src/tools/vtex/activate-sku.js.map +2 -2
- package/dist/src/tools/vtex/associate-specification.js +3 -54
- package/dist/src/tools/vtex/associate-specification.js.map +2 -2
- package/dist/src/tools/vtex/attach-catalog-image.js +3 -57
- package/dist/src/tools/vtex/attach-catalog-image.js.map +2 -2
- package/dist/src/tools/vtex/catalog-admin-batch.js +298 -0
- package/dist/src/tools/vtex/catalog-admin-batch.js.map +7 -0
- package/dist/src/tools/vtex/catalog-admin-categories.js +542 -0
- package/dist/src/tools/vtex/catalog-admin-categories.js.map +7 -0
- package/dist/src/tools/vtex/catalog-admin-product-specifications.js +275 -0
- package/dist/src/tools/vtex/catalog-admin-product-specifications.js.map +7 -0
- package/dist/src/tools/vtex/catalog-admin-products-skus.js +475 -0
- package/dist/src/tools/vtex/catalog-admin-products-skus.js.map +7 -0
- package/dist/src/tools/vtex/catalog-navigation-reads.js +430 -0
- package/dist/src/tools/vtex/catalog-navigation-reads.js.map +7 -0
- package/dist/src/tools/vtex/create-brand.js +1 -64
- package/dist/src/tools/vtex/create-brand.js.map +2 -2
- package/dist/src/tools/vtex/create-category.js +1 -76
- package/dist/src/tools/vtex/create-category.js.map +2 -2
- package/dist/src/tools/vtex/create-product-with-sku.js +3 -114
- package/dist/src/tools/vtex/create-product-with-sku.js.map +2 -2
- package/dist/src/tools/vtex/create-specification-value.js +3 -47
- package/dist/src/tools/vtex/create-specification-value.js.map +2 -2
- package/dist/src/tools/vtex/create-specification.js +1 -80
- package/dist/src/tools/vtex/create-specification.js.map +2 -2
- package/dist/src/tools/vtex/deactivate-sku.js +1 -48
- package/dist/src/tools/vtex/deactivate-sku.js.map +2 -2
- package/dist/src/tools/vtex/delete-all-product-specifications.js +9 -0
- package/dist/src/tools/vtex/delete-all-product-specifications.js.map +7 -0
- package/dist/src/tools/vtex/delete-all-sku-specifications.js +9 -0
- package/dist/src/tools/vtex/delete-all-sku-specifications.js.map +7 -0
- package/dist/src/tools/vtex/delete-brand.js +9 -0
- package/dist/src/tools/vtex/delete-brand.js.map +7 -0
- package/dist/src/tools/vtex/delete-catalog-image.js +9 -0
- package/dist/src/tools/vtex/delete-catalog-image.js.map +7 -0
- package/dist/src/tools/vtex/delete-product-specification.js +9 -0
- package/dist/src/tools/vtex/delete-product-specification.js.map +7 -0
- package/dist/src/tools/vtex/delete-sku-price.js +55 -0
- package/dist/src/tools/vtex/delete-sku-price.js.map +7 -0
- package/dist/src/tools/vtex/delete-sku-specification.js +9 -0
- package/dist/src/tools/vtex/delete-sku-specification.js.map +7 -0
- package/dist/src/tools/vtex/get-brand.js +6 -0
- package/dist/src/tools/vtex/get-brand.js.map +7 -0
- package/dist/src/tools/vtex/get-category-tree.js +6 -0
- package/dist/src/tools/vtex/get-category-tree.js.map +7 -0
- package/dist/src/tools/vtex/get-category.js +6 -0
- package/dist/src/tools/vtex/get-category.js.map +7 -0
- package/dist/src/tools/vtex/get-product-specifications.js +9 -0
- package/dist/src/tools/vtex/get-product-specifications.js.map +7 -0
- package/dist/src/tools/vtex/get-product.js +6 -0
- package/dist/src/tools/vtex/get-product.js.map +7 -0
- package/dist/src/tools/vtex/get-sku.js +6 -0
- package/dist/src/tools/vtex/get-sku.js.map +7 -0
- package/dist/src/tools/vtex/index.js +23 -1
- package/dist/src/tools/vtex/index.js.map +2 -2
- package/dist/src/tools/vtex/list-brands.js +6 -0
- package/dist/src/tools/vtex/list-brands.js.map +7 -0
- package/dist/src/tools/vtex/list-categories.js +6 -0
- package/dist/src/tools/vtex/list-categories.js.map +7 -0
- package/dist/src/tools/vtex/list-products-by-category.js +6 -0
- package/dist/src/tools/vtex/list-products-by-category.js.map +7 -0
- package/dist/src/tools/vtex/list-products.js +6 -0
- package/dist/src/tools/vtex/list-products.js.map +7 -0
- package/dist/src/tools/vtex/list-skus-by-product.js +6 -0
- package/dist/src/tools/vtex/list-skus-by-product.js.map +7 -0
- package/dist/src/tools/vtex/list-specification-groups.js +9 -0
- package/dist/src/tools/vtex/list-specification-groups.js.map +7 -0
- package/dist/src/tools/vtex/move-category.js +6 -0
- package/dist/src/tools/vtex/move-category.js.map +7 -0
- package/dist/src/tools/vtex/profile-resolution.js +4 -51
- package/dist/src/tools/vtex/profile-resolution.js.map +2 -2
- package/dist/src/tools/vtex/update-brand.js +6 -0
- package/dist/src/tools/vtex/update-brand.js.map +7 -0
- package/dist/src/tools/vtex/update-category.js +6 -0
- package/dist/src/tools/vtex/update-category.js.map +7 -0
- package/dist/src/tools/vtex/update-product-basic-fields.js +3 -65
- package/dist/src/tools/vtex/update-product-basic-fields.js.map +2 -2
- package/dist/src/tools/vtex/update-sku-basic-fields.js +1 -87
- package/dist/src/tools/vtex/update-sku-basic-fields.js.map +2 -2
- package/dist/src/tools/vtex/update-sku-price.js +21 -1
- package/dist/src/tools/vtex/update-sku-price.js.map +2 -2
- package/dist/src/tools/vtex/write-helpers.js +104 -14
- package/dist/src/tools/vtex/write-helpers.js.map +2 -2
- package/dist/src/utils/provider-profile-selection.js +117 -0
- package/dist/src/utils/provider-profile-selection.js.map +7 -0
- package/dist/tests/meli/mercadolibre-tool-handlers-medium-batch4.test.js +678 -0
- package/dist/tests/meli/mercadolibre-tool-handlers-medium-batch4.test.js.map +7 -0
- package/dist/tests/meli/mercadolibre-tool-handlers-monster-batch5.test.js +564 -0
- package/dist/tests/meli/mercadolibre-tool-handlers-monster-batch5.test.js.map +7 -0
- package/dist/tests/meli/mercadolibre-tool-handlers-monster-batch6.test.js +387 -0
- package/dist/tests/meli/mercadolibre-tool-handlers-monster-batch6.test.js.map +7 -0
- package/dist/tests/meli/mercadolibre-tool-handlers-monster-batch7.test.js +368 -0
- package/dist/tests/meli/mercadolibre-tool-handlers-monster-batch7.test.js.map +7 -0
- package/dist/tests/meli/mercadolibre-tool-handlers-small.test.js +626 -0
- package/dist/tests/meli/mercadolibre-tool-handlers-small.test.js.map +7 -0
- package/dist/tests/meli/mercadolibre-tool-handlers-write-batch8.test.js +480 -0
- package/dist/tests/meli/mercadolibre-tool-handlers-write-batch8.test.js.map +7 -0
- package/dist/tests/setup.js +2 -0
- package/dist/tests/setup.js.map +7 -0
- package/dist/tests/smoke/test-harness.test.js +7 -0
- package/dist/tests/smoke/test-harness.test.js.map +7 -0
- package/dist/tests/vtex/read-only-utils.test.js +161 -0
- package/dist/tests/vtex/read-only-utils.test.js.map +7 -0
- package/dist/tests/vtex/vtex-catalog-admin-docs.test.js +24 -0
- package/dist/tests/vtex/vtex-catalog-admin-docs.test.js.map +7 -0
- package/dist/tests/vtex/vtex-catalog-admin-surface.test.js +30 -0
- package/dist/tests/vtex/vtex-catalog-admin-surface.test.js.map +7 -0
- package/dist/tests/vtex/vtex-catalog-write-tools.test.js +712 -0
- package/dist/tests/vtex/vtex-catalog-write-tools.test.js.map +7 -0
- package/dist/tests/vtex/vtex-catalog.service.test.js +51 -0
- package/dist/tests/vtex/vtex-catalog.service.test.js.map +7 -0
- package/dist/tests/vtex/vtex-inventory-tools.test.js +201 -0
- package/dist/tests/vtex/vtex-inventory-tools.test.js.map +7 -0
- package/dist/tests/vtex/vtex-logistics.service.test.js +134 -0
- package/dist/tests/vtex/vtex-logistics.service.test.js.map +7 -0
- package/dist/tests/vtex/vtex-order-details.tool.test.js +141 -0
- package/dist/tests/vtex/vtex-order-details.tool.test.js.map +7 -0
- package/dist/tests/vtex/vtex-order-write-tools.test.js +483 -0
- package/dist/tests/vtex/vtex-order-write-tools.test.js.map +7 -0
- package/dist/tests/vtex/vtex-orders-summary.tool.test.js +185 -0
- package/dist/tests/vtex/vtex-orders-summary.tool.test.js.map +7 -0
- package/dist/tests/vtex/vtex-orders.service.test.js +120 -0
- package/dist/tests/vtex/vtex-orders.service.test.js.map +7 -0
- package/dist/tests/vtex/vtex-pricing-write-tools.test.js +202 -0
- package/dist/tests/vtex/vtex-pricing-write-tools.test.js.map +7 -0
- package/dist/tests/vtex/vtex-pricing-write.service.test.js +106 -0
- package/dist/tests/vtex/vtex-pricing-write.service.test.js.map +7 -0
- package/dist/tests/vtex/vtex-pricing.service.test.js +88 -0
- package/dist/tests/vtex/vtex-pricing.service.test.js.map +7 -0
- package/dist/tests/vtex/vtex-small-tools.test.js +190 -0
- package/dist/tests/vtex/vtex-small-tools.test.js.map +7 -0
- package/dist/tests/vtex/vtex-write-simple-tools.test.js +647 -0
- package/dist/tests/vtex/vtex-write-simple-tools.test.js.map +7 -0
- package/dist/vitest.config.js +15 -0
- package/dist/vitest.config.js.map +7 -0
- package/package.json +6 -2
|
@@ -0,0 +1,647 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { activateSkuHandler } from "../../src/tools/vtex/activate-sku.js";
|
|
4
|
+
import { deactivateSkuHandler } from "../../src/tools/vtex/deactivate-sku.js";
|
|
5
|
+
import { toggleUnlimitedQuantityHandler } from "../../src/tools/vtex/toggle-unlimited-quantity.js";
|
|
6
|
+
import { updateInventoryHandler } from "../../src/tools/vtex/update-inventory.js";
|
|
7
|
+
import { updateLeadTimeHandler } from "../../src/tools/vtex/update-lead-time.js";
|
|
8
|
+
import { deleteSkuPriceHandler } from "../../src/tools/vtex/delete-sku-price.js";
|
|
9
|
+
import { updateSkuPriceHandler } from "../../src/tools/vtex/update-sku-price.js";
|
|
10
|
+
import {
|
|
11
|
+
updateSkuBasicFields
|
|
12
|
+
} from "../../src/services/vtex/vtex-catalog-write.js";
|
|
13
|
+
import {
|
|
14
|
+
getInventoryByWarehouse,
|
|
15
|
+
updateInventoryLeadTime,
|
|
16
|
+
updateInventoryQuantity
|
|
17
|
+
} from "../../src/services/vtex/vtex-logistics.js";
|
|
18
|
+
import { getSkuPrice } from "../../src/services/vtex/vtex-pricing.js";
|
|
19
|
+
import {
|
|
20
|
+
deleteSkuPrice,
|
|
21
|
+
updateSkuPrice
|
|
22
|
+
} from "../../src/services/vtex/vtex-pricing-write.js";
|
|
23
|
+
import {
|
|
24
|
+
buildWriteSuccessResponse,
|
|
25
|
+
handleVtexWriteError,
|
|
26
|
+
requireExplicitConfirmation,
|
|
27
|
+
resolveVtexWriteProfile
|
|
28
|
+
} from "../../src/tools/vtex/write-helpers.js";
|
|
29
|
+
vi.mock("../../src/tools/vtex/write-helpers.js", () => ({
|
|
30
|
+
vtexProfileIdSchemaField: z.string().optional(),
|
|
31
|
+
confirmationSchemaField: z.boolean().optional(),
|
|
32
|
+
confirmationNoteSchemaField: z.string().optional(),
|
|
33
|
+
resolveVtexWriteProfile: vi.fn(),
|
|
34
|
+
buildWriteSuccessResponse: vi.fn((params) => ({
|
|
35
|
+
kind: "success",
|
|
36
|
+
params
|
|
37
|
+
})),
|
|
38
|
+
requireExplicitConfirmation: vi.fn(),
|
|
39
|
+
handleVtexWriteError: vi.fn((err, fallback) => ({
|
|
40
|
+
kind: "error",
|
|
41
|
+
fallback,
|
|
42
|
+
err
|
|
43
|
+
}))
|
|
44
|
+
}));
|
|
45
|
+
vi.mock("../../src/services/vtex/vtex-catalog-write.js", () => ({
|
|
46
|
+
updateSkuBasicFields: vi.fn()
|
|
47
|
+
}));
|
|
48
|
+
vi.mock("../../src/services/vtex/vtex-logistics.js", () => ({
|
|
49
|
+
getInventoryByWarehouse: vi.fn(),
|
|
50
|
+
updateInventoryQuantity: vi.fn(),
|
|
51
|
+
updateInventoryLeadTime: vi.fn()
|
|
52
|
+
}));
|
|
53
|
+
vi.mock("../../src/services/vtex/vtex-pricing.js", () => ({
|
|
54
|
+
getSkuPrice: vi.fn()
|
|
55
|
+
}));
|
|
56
|
+
vi.mock("../../src/services/vtex/vtex-pricing-write.js", () => ({
|
|
57
|
+
updateSkuPrice: vi.fn(),
|
|
58
|
+
deleteSkuPrice: vi.fn()
|
|
59
|
+
}));
|
|
60
|
+
describe("VTEX write simple tools", () => {
|
|
61
|
+
beforeEach(() => {
|
|
62
|
+
vi.clearAllMocks();
|
|
63
|
+
vi.mocked(requireExplicitConfirmation).mockReturnValue(null);
|
|
64
|
+
vi.mocked(resolveVtexWriteProfile).mockResolvedValue({
|
|
65
|
+
ok: true,
|
|
66
|
+
value: { profileId: "profile-1" }
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
describe("activateSkuHandler", () => {
|
|
70
|
+
it("activates a sku using the fetched sku snapshot", async () => {
|
|
71
|
+
const beforeSku = { Id: "sku-1", IsActive: false, Name: "Before" };
|
|
72
|
+
const afterSku = { Id: "sku-1", IsActive: true, Name: "Before" };
|
|
73
|
+
vi.mocked(updateSkuBasicFields).mockResolvedValue({
|
|
74
|
+
before: beforeSku,
|
|
75
|
+
payload: { IsActive: true, Name: "Before" },
|
|
76
|
+
after: afterSku
|
|
77
|
+
});
|
|
78
|
+
const result = await activateSkuHandler({
|
|
79
|
+
profileId: "custom",
|
|
80
|
+
skuId: "sku-1"
|
|
81
|
+
});
|
|
82
|
+
expect(resolveVtexWriteProfile).toHaveBeenCalledWith("custom");
|
|
83
|
+
expect(updateSkuBasicFields).toHaveBeenCalledWith("profile-1", "sku-1", {
|
|
84
|
+
isActive: true
|
|
85
|
+
});
|
|
86
|
+
expect(buildWriteSuccessResponse).toHaveBeenCalledWith({
|
|
87
|
+
profileId: "profile-1",
|
|
88
|
+
operation: "activate_sku",
|
|
89
|
+
resourceId: "sku-1",
|
|
90
|
+
riskLevel: "medium",
|
|
91
|
+
message: "SKU activated successfully.",
|
|
92
|
+
before: beforeSku,
|
|
93
|
+
after: afterSku,
|
|
94
|
+
details: { sku_id: "sku-1" }
|
|
95
|
+
});
|
|
96
|
+
expect(result).toEqual({
|
|
97
|
+
kind: "success",
|
|
98
|
+
params: expect.objectContaining({ operation: "activate_sku" })
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
it("returns profile selection payload without mutating anything", async () => {
|
|
102
|
+
vi.mocked(resolveVtexWriteProfile).mockResolvedValue({
|
|
103
|
+
ok: false,
|
|
104
|
+
response: { selection: true }
|
|
105
|
+
});
|
|
106
|
+
const result = await activateSkuHandler({ skuId: "sku-1" });
|
|
107
|
+
expect(result).toEqual({ selection: true });
|
|
108
|
+
expect(updateSkuBasicFields).not.toHaveBeenCalled();
|
|
109
|
+
});
|
|
110
|
+
it("maps service errors through handleVtexWriteError", async () => {
|
|
111
|
+
vi.mocked(updateSkuBasicFields).mockRejectedValue(new Error("boom"));
|
|
112
|
+
const result = await activateSkuHandler({
|
|
113
|
+
profileId: "custom",
|
|
114
|
+
skuId: "sku-1"
|
|
115
|
+
});
|
|
116
|
+
expect(handleVtexWriteError).toHaveBeenCalledWith(
|
|
117
|
+
expect.any(Error),
|
|
118
|
+
"Failed to activate SKU sku-1"
|
|
119
|
+
);
|
|
120
|
+
expect(result).toEqual({
|
|
121
|
+
kind: "error",
|
|
122
|
+
fallback: "Failed to activate SKU sku-1",
|
|
123
|
+
err: expect.any(Error)
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
describe("deactivateSkuHandler", () => {
|
|
128
|
+
it("deactivates a sku using the fetched sku snapshot", async () => {
|
|
129
|
+
const beforeSku = { Id: "sku-2", IsActive: true, Name: "Before" };
|
|
130
|
+
const afterSku = { Id: "sku-2", IsActive: false, Name: "Before" };
|
|
131
|
+
vi.mocked(updateSkuBasicFields).mockResolvedValue({
|
|
132
|
+
before: beforeSku,
|
|
133
|
+
payload: { IsActive: false, Name: "Before" },
|
|
134
|
+
after: afterSku
|
|
135
|
+
});
|
|
136
|
+
const result = await deactivateSkuHandler({
|
|
137
|
+
profileId: "custom",
|
|
138
|
+
skuId: "sku-2"
|
|
139
|
+
});
|
|
140
|
+
expect(updateSkuBasicFields).toHaveBeenCalledWith("profile-1", "sku-2", {
|
|
141
|
+
isActive: false
|
|
142
|
+
});
|
|
143
|
+
expect(buildWriteSuccessResponse).toHaveBeenCalledWith({
|
|
144
|
+
profileId: "profile-1",
|
|
145
|
+
operation: "deactivate_sku",
|
|
146
|
+
resourceId: "sku-2",
|
|
147
|
+
riskLevel: "medium",
|
|
148
|
+
message: "SKU deactivated successfully.",
|
|
149
|
+
before: beforeSku,
|
|
150
|
+
after: afterSku,
|
|
151
|
+
details: { sku_id: "sku-2" }
|
|
152
|
+
});
|
|
153
|
+
expect(result).toEqual({
|
|
154
|
+
kind: "success",
|
|
155
|
+
params: expect.objectContaining({ operation: "deactivate_sku" })
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
it("returns profile selection payload without mutating anything", async () => {
|
|
159
|
+
vi.mocked(resolveVtexWriteProfile).mockResolvedValue({
|
|
160
|
+
ok: false,
|
|
161
|
+
response: { selection: true }
|
|
162
|
+
});
|
|
163
|
+
const result = await deactivateSkuHandler({ skuId: "sku-2" });
|
|
164
|
+
expect(result).toEqual({ selection: true });
|
|
165
|
+
expect(updateSkuBasicFields).not.toHaveBeenCalled();
|
|
166
|
+
});
|
|
167
|
+
it("maps service errors through handleVtexWriteError", async () => {
|
|
168
|
+
vi.mocked(updateSkuBasicFields).mockRejectedValue(new Error("boom"));
|
|
169
|
+
const result = await deactivateSkuHandler({
|
|
170
|
+
profileId: "custom",
|
|
171
|
+
skuId: "sku-2"
|
|
172
|
+
});
|
|
173
|
+
expect(handleVtexWriteError).toHaveBeenCalledWith(
|
|
174
|
+
expect.any(Error),
|
|
175
|
+
"Failed to deactivate SKU sku-2"
|
|
176
|
+
);
|
|
177
|
+
expect(result).toEqual({
|
|
178
|
+
kind: "error",
|
|
179
|
+
fallback: "Failed to deactivate SKU sku-2",
|
|
180
|
+
err: expect.any(Error)
|
|
181
|
+
});
|
|
182
|
+
});
|
|
183
|
+
});
|
|
184
|
+
describe("toggleUnlimitedQuantityHandler", () => {
|
|
185
|
+
it("reuses the current total quantity when toggling unlimited mode", async () => {
|
|
186
|
+
const beforeInventory = [
|
|
187
|
+
{
|
|
188
|
+
skuId: "sku-3",
|
|
189
|
+
warehouseId: "wh-1",
|
|
190
|
+
totalQuantity: 9,
|
|
191
|
+
isUnlimited: false
|
|
192
|
+
}
|
|
193
|
+
];
|
|
194
|
+
const afterInventory = [
|
|
195
|
+
{
|
|
196
|
+
skuId: "sku-3",
|
|
197
|
+
warehouseId: "wh-1",
|
|
198
|
+
totalQuantity: 9,
|
|
199
|
+
isUnlimited: true
|
|
200
|
+
}
|
|
201
|
+
];
|
|
202
|
+
vi.mocked(getInventoryByWarehouse).mockResolvedValueOnce(beforeInventory).mockResolvedValueOnce(afterInventory);
|
|
203
|
+
const result = await toggleUnlimitedQuantityHandler({
|
|
204
|
+
profileId: "custom",
|
|
205
|
+
skuId: "sku-3",
|
|
206
|
+
warehouseId: "wh-1",
|
|
207
|
+
unlimitedQuantity: true
|
|
208
|
+
});
|
|
209
|
+
expect(updateInventoryQuantity).toHaveBeenCalledWith(
|
|
210
|
+
"profile-1",
|
|
211
|
+
"sku-3",
|
|
212
|
+
"wh-1",
|
|
213
|
+
{ quantity: 9, unlimitedQuantity: true }
|
|
214
|
+
);
|
|
215
|
+
expect(buildWriteSuccessResponse).toHaveBeenCalledWith({
|
|
216
|
+
profileId: "profile-1",
|
|
217
|
+
operation: "toggle_unlimited_quantity",
|
|
218
|
+
resourceId: "sku-3",
|
|
219
|
+
riskLevel: "low",
|
|
220
|
+
message: "Unlimited quantity mode updated successfully.",
|
|
221
|
+
before: beforeInventory[0],
|
|
222
|
+
after: afterInventory[0],
|
|
223
|
+
details: {
|
|
224
|
+
sku_id: "sku-3",
|
|
225
|
+
warehouse_id: "wh-1",
|
|
226
|
+
unlimited_quantity: true
|
|
227
|
+
}
|
|
228
|
+
});
|
|
229
|
+
expect(result).toEqual({
|
|
230
|
+
kind: "success",
|
|
231
|
+
params: expect.objectContaining({
|
|
232
|
+
operation: "toggle_unlimited_quantity"
|
|
233
|
+
})
|
|
234
|
+
});
|
|
235
|
+
});
|
|
236
|
+
it("returns profile selection payload without mutating anything", async () => {
|
|
237
|
+
vi.mocked(resolveVtexWriteProfile).mockResolvedValue({
|
|
238
|
+
ok: false,
|
|
239
|
+
response: { selection: true }
|
|
240
|
+
});
|
|
241
|
+
const result = await toggleUnlimitedQuantityHandler({
|
|
242
|
+
skuId: "sku-3",
|
|
243
|
+
warehouseId: "wh-1",
|
|
244
|
+
unlimitedQuantity: true
|
|
245
|
+
});
|
|
246
|
+
expect(result).toEqual({ selection: true });
|
|
247
|
+
expect(getInventoryByWarehouse).not.toHaveBeenCalled();
|
|
248
|
+
expect(updateInventoryQuantity).not.toHaveBeenCalled();
|
|
249
|
+
});
|
|
250
|
+
it("maps missing inventory snapshots through handleVtexWriteError", async () => {
|
|
251
|
+
vi.mocked(getInventoryByWarehouse).mockResolvedValueOnce([]);
|
|
252
|
+
const result = await toggleUnlimitedQuantityHandler({
|
|
253
|
+
profileId: "custom",
|
|
254
|
+
skuId: "sku-3",
|
|
255
|
+
warehouseId: "wh-1",
|
|
256
|
+
unlimitedQuantity: true
|
|
257
|
+
});
|
|
258
|
+
expect(updateInventoryQuantity).not.toHaveBeenCalled();
|
|
259
|
+
expect(handleVtexWriteError).toHaveBeenCalledWith(
|
|
260
|
+
expect.any(Error),
|
|
261
|
+
"Failed to update unlimited quantity mode for SKU sku-3 in wh-1"
|
|
262
|
+
);
|
|
263
|
+
expect(result).toEqual({
|
|
264
|
+
kind: "error",
|
|
265
|
+
fallback: "Failed to update unlimited quantity mode for SKU sku-3 in wh-1",
|
|
266
|
+
err: expect.any(Error)
|
|
267
|
+
});
|
|
268
|
+
});
|
|
269
|
+
it("maps service errors through handleVtexWriteError", async () => {
|
|
270
|
+
vi.mocked(getInventoryByWarehouse).mockRejectedValue(new Error("boom"));
|
|
271
|
+
const result = await toggleUnlimitedQuantityHandler({
|
|
272
|
+
profileId: "custom",
|
|
273
|
+
skuId: "sku-3",
|
|
274
|
+
warehouseId: "wh-1",
|
|
275
|
+
unlimitedQuantity: true
|
|
276
|
+
});
|
|
277
|
+
expect(handleVtexWriteError).toHaveBeenCalledWith(
|
|
278
|
+
expect.any(Error),
|
|
279
|
+
"Failed to update unlimited quantity mode for SKU sku-3 in wh-1"
|
|
280
|
+
);
|
|
281
|
+
expect(result).toEqual({
|
|
282
|
+
kind: "error",
|
|
283
|
+
fallback: "Failed to update unlimited quantity mode for SKU sku-3 in wh-1",
|
|
284
|
+
err: expect.any(Error)
|
|
285
|
+
});
|
|
286
|
+
});
|
|
287
|
+
});
|
|
288
|
+
describe("updateLeadTimeHandler", () => {
|
|
289
|
+
it("updates lead time and returns before/after snapshots", async () => {
|
|
290
|
+
const beforeInventory = [
|
|
291
|
+
{ skuId: "sku-4", warehouseId: "wh-2", leadTime: "1.00:00:00" }
|
|
292
|
+
];
|
|
293
|
+
const afterInventory = [
|
|
294
|
+
{ skuId: "sku-4", warehouseId: "wh-2", leadTime: "2.00:00:00" }
|
|
295
|
+
];
|
|
296
|
+
vi.mocked(getInventoryByWarehouse).mockResolvedValueOnce(beforeInventory).mockResolvedValueOnce(afterInventory);
|
|
297
|
+
const result = await updateLeadTimeHandler({
|
|
298
|
+
profileId: "custom",
|
|
299
|
+
skuId: "sku-4",
|
|
300
|
+
warehouseId: "wh-2",
|
|
301
|
+
leadTime: "2.00:00:00"
|
|
302
|
+
});
|
|
303
|
+
expect(updateInventoryLeadTime).toHaveBeenCalledWith(
|
|
304
|
+
"profile-1",
|
|
305
|
+
"sku-4",
|
|
306
|
+
"wh-2",
|
|
307
|
+
{ leadTime: "2.00:00:00" }
|
|
308
|
+
);
|
|
309
|
+
expect(buildWriteSuccessResponse).toHaveBeenCalledWith({
|
|
310
|
+
profileId: "profile-1",
|
|
311
|
+
operation: "update_lead_time",
|
|
312
|
+
resourceId: "sku-4",
|
|
313
|
+
riskLevel: "low",
|
|
314
|
+
message: "Lead time updated successfully.",
|
|
315
|
+
before: beforeInventory[0],
|
|
316
|
+
after: afterInventory[0],
|
|
317
|
+
details: {
|
|
318
|
+
sku_id: "sku-4",
|
|
319
|
+
warehouse_id: "wh-2",
|
|
320
|
+
lead_time: "2.00:00:00"
|
|
321
|
+
}
|
|
322
|
+
});
|
|
323
|
+
expect(result).toEqual({
|
|
324
|
+
kind: "success",
|
|
325
|
+
params: expect.objectContaining({ operation: "update_lead_time" })
|
|
326
|
+
});
|
|
327
|
+
});
|
|
328
|
+
it("returns profile selection payload without mutating anything", async () => {
|
|
329
|
+
vi.mocked(resolveVtexWriteProfile).mockResolvedValue({
|
|
330
|
+
ok: false,
|
|
331
|
+
response: { selection: true }
|
|
332
|
+
});
|
|
333
|
+
const result = await updateLeadTimeHandler({
|
|
334
|
+
skuId: "sku-4",
|
|
335
|
+
warehouseId: "wh-2",
|
|
336
|
+
leadTime: "2.00:00:00"
|
|
337
|
+
});
|
|
338
|
+
expect(result).toEqual({ selection: true });
|
|
339
|
+
expect(getInventoryByWarehouse).not.toHaveBeenCalled();
|
|
340
|
+
expect(updateInventoryLeadTime).not.toHaveBeenCalled();
|
|
341
|
+
});
|
|
342
|
+
it("maps service errors through handleVtexWriteError", async () => {
|
|
343
|
+
vi.mocked(getInventoryByWarehouse).mockRejectedValue(new Error("boom"));
|
|
344
|
+
const result = await updateLeadTimeHandler({
|
|
345
|
+
profileId: "custom",
|
|
346
|
+
skuId: "sku-4",
|
|
347
|
+
warehouseId: "wh-2",
|
|
348
|
+
leadTime: "2.00:00:00"
|
|
349
|
+
});
|
|
350
|
+
expect(handleVtexWriteError).toHaveBeenCalledWith(
|
|
351
|
+
expect.any(Error),
|
|
352
|
+
"Failed to update lead time for SKU sku-4 in wh-2"
|
|
353
|
+
);
|
|
354
|
+
expect(result).toEqual({
|
|
355
|
+
kind: "error",
|
|
356
|
+
fallback: "Failed to update lead time for SKU sku-4 in wh-2",
|
|
357
|
+
err: expect.any(Error)
|
|
358
|
+
});
|
|
359
|
+
});
|
|
360
|
+
});
|
|
361
|
+
describe("updateInventoryHandler", () => {
|
|
362
|
+
it("updates inventory with forwarded quantity and sync timestamp", async () => {
|
|
363
|
+
const beforeInventory = [
|
|
364
|
+
{ skuId: "sku-5", warehouseId: "wh-3", totalQuantity: 4 }
|
|
365
|
+
];
|
|
366
|
+
const afterInventory = [
|
|
367
|
+
{ skuId: "sku-5", warehouseId: "wh-3", totalQuantity: 12 }
|
|
368
|
+
];
|
|
369
|
+
vi.mocked(getInventoryByWarehouse).mockResolvedValueOnce(beforeInventory).mockResolvedValueOnce(afterInventory);
|
|
370
|
+
const result = await updateInventoryHandler({
|
|
371
|
+
profileId: "custom",
|
|
372
|
+
skuId: "sku-5",
|
|
373
|
+
warehouseId: "wh-3",
|
|
374
|
+
quantity: 12,
|
|
375
|
+
unlimitedQuantity: false,
|
|
376
|
+
dateUtcOnBalanceSystem: "2026-06-01T10:00:00Z"
|
|
377
|
+
});
|
|
378
|
+
expect(updateInventoryQuantity).toHaveBeenCalledWith(
|
|
379
|
+
"profile-1",
|
|
380
|
+
"sku-5",
|
|
381
|
+
"wh-3",
|
|
382
|
+
{
|
|
383
|
+
quantity: 12,
|
|
384
|
+
unlimitedQuantity: false,
|
|
385
|
+
dateUtcOnBalanceSystem: "2026-06-01T10:00:00Z"
|
|
386
|
+
}
|
|
387
|
+
);
|
|
388
|
+
expect(buildWriteSuccessResponse).toHaveBeenCalledWith({
|
|
389
|
+
profileId: "profile-1",
|
|
390
|
+
operation: "update_inventory",
|
|
391
|
+
resourceId: "sku-5",
|
|
392
|
+
riskLevel: "low",
|
|
393
|
+
message: "Inventory quantity updated successfully.",
|
|
394
|
+
before: beforeInventory[0],
|
|
395
|
+
after: afterInventory[0],
|
|
396
|
+
details: {
|
|
397
|
+
sku_id: "sku-5",
|
|
398
|
+
warehouse_id: "wh-3",
|
|
399
|
+
quantity: 12,
|
|
400
|
+
unlimited_quantity: false
|
|
401
|
+
}
|
|
402
|
+
});
|
|
403
|
+
expect(result).toEqual({
|
|
404
|
+
kind: "success",
|
|
405
|
+
params: expect.objectContaining({ operation: "update_inventory" })
|
|
406
|
+
});
|
|
407
|
+
});
|
|
408
|
+
it("returns profile selection payload without mutating anything", async () => {
|
|
409
|
+
vi.mocked(resolveVtexWriteProfile).mockResolvedValue({
|
|
410
|
+
ok: false,
|
|
411
|
+
response: { selection: true }
|
|
412
|
+
});
|
|
413
|
+
const result = await updateInventoryHandler({
|
|
414
|
+
skuId: "sku-5",
|
|
415
|
+
warehouseId: "wh-3",
|
|
416
|
+
quantity: 12,
|
|
417
|
+
unlimitedQuantity: false
|
|
418
|
+
});
|
|
419
|
+
expect(result).toEqual({ selection: true });
|
|
420
|
+
expect(getInventoryByWarehouse).not.toHaveBeenCalled();
|
|
421
|
+
expect(updateInventoryQuantity).not.toHaveBeenCalled();
|
|
422
|
+
});
|
|
423
|
+
it("maps service errors through handleVtexWriteError", async () => {
|
|
424
|
+
vi.mocked(getInventoryByWarehouse).mockRejectedValue(new Error("boom"));
|
|
425
|
+
const result = await updateInventoryHandler({
|
|
426
|
+
profileId: "custom",
|
|
427
|
+
skuId: "sku-5",
|
|
428
|
+
warehouseId: "wh-3",
|
|
429
|
+
quantity: 12,
|
|
430
|
+
unlimitedQuantity: false
|
|
431
|
+
});
|
|
432
|
+
expect(handleVtexWriteError).toHaveBeenCalledWith(
|
|
433
|
+
expect.any(Error),
|
|
434
|
+
"Failed to update inventory quantity for SKU sku-5 in wh-3"
|
|
435
|
+
);
|
|
436
|
+
expect(result).toEqual({
|
|
437
|
+
kind: "error",
|
|
438
|
+
fallback: "Failed to update inventory quantity for SKU sku-5 in wh-3",
|
|
439
|
+
err: expect.any(Error)
|
|
440
|
+
});
|
|
441
|
+
});
|
|
442
|
+
});
|
|
443
|
+
describe("deleteSkuPriceHandler", () => {
|
|
444
|
+
it("requires explicit confirmation before deleting pricing", async () => {
|
|
445
|
+
vi.mocked(requireExplicitConfirmation).mockReturnValue({ confirmation_required: true });
|
|
446
|
+
const result = await deleteSkuPriceHandler({
|
|
447
|
+
profileId: "custom",
|
|
448
|
+
skuId: "sku-7"
|
|
449
|
+
});
|
|
450
|
+
expect(requireExplicitConfirmation).toHaveBeenCalledWith(void 0, "delete_sku_price", {
|
|
451
|
+
sku_id: "sku-7"
|
|
452
|
+
});
|
|
453
|
+
expect(result).toEqual({ confirmation_required: true });
|
|
454
|
+
expect(resolveVtexWriteProfile).not.toHaveBeenCalled();
|
|
455
|
+
expect(deleteSkuPrice).not.toHaveBeenCalled();
|
|
456
|
+
});
|
|
457
|
+
it("deletes pricing and returns the minimal success contract", async () => {
|
|
458
|
+
const result = await deleteSkuPriceHandler({
|
|
459
|
+
profileId: "custom",
|
|
460
|
+
skuId: "sku-7",
|
|
461
|
+
confirmed: true,
|
|
462
|
+
confirmationNote: "User confirmed deleting the SKU price record."
|
|
463
|
+
});
|
|
464
|
+
expect(resolveVtexWriteProfile).toHaveBeenCalledWith("custom");
|
|
465
|
+
expect(deleteSkuPrice).toHaveBeenCalledWith("profile-1", "sku-7");
|
|
466
|
+
expect(result).toEqual({
|
|
467
|
+
content: [
|
|
468
|
+
{
|
|
469
|
+
text: expect.stringContaining('"operation": "delete_sku_price"'),
|
|
470
|
+
type: "text"
|
|
471
|
+
}
|
|
472
|
+
],
|
|
473
|
+
structuredContent: {
|
|
474
|
+
success: true,
|
|
475
|
+
profile_id: "profile-1",
|
|
476
|
+
operation: "delete_sku_price",
|
|
477
|
+
resource_id: "sku-7",
|
|
478
|
+
risk_level: "high",
|
|
479
|
+
confirmed: true,
|
|
480
|
+
message: "SKU price delete request accepted by VTEX.",
|
|
481
|
+
warnings: [
|
|
482
|
+
"Price reads may remain stale for several minutes after a successful delete while VTEX propagates the change."
|
|
483
|
+
]
|
|
484
|
+
},
|
|
485
|
+
_meta: {
|
|
486
|
+
mimeType: "application/json"
|
|
487
|
+
}
|
|
488
|
+
});
|
|
489
|
+
expect(buildWriteSuccessResponse).not.toHaveBeenCalled();
|
|
490
|
+
});
|
|
491
|
+
it("maps service errors through handleVtexWriteError", async () => {
|
|
492
|
+
vi.mocked(deleteSkuPrice).mockRejectedValue(new Error("boom"));
|
|
493
|
+
const result = await deleteSkuPriceHandler({
|
|
494
|
+
profileId: "custom",
|
|
495
|
+
skuId: "sku-7",
|
|
496
|
+
confirmed: true
|
|
497
|
+
});
|
|
498
|
+
expect(handleVtexWriteError).toHaveBeenCalledWith(
|
|
499
|
+
expect.any(Error),
|
|
500
|
+
"Failed to delete price for SKU sku-7"
|
|
501
|
+
);
|
|
502
|
+
expect(result).toEqual({
|
|
503
|
+
kind: "error",
|
|
504
|
+
fallback: "Failed to delete price for SKU sku-7",
|
|
505
|
+
err: expect.any(Error)
|
|
506
|
+
});
|
|
507
|
+
});
|
|
508
|
+
});
|
|
509
|
+
describe("updateSkuPriceHandler", () => {
|
|
510
|
+
it("updates sku pricing and re-fetches before/after price snapshots", async () => {
|
|
511
|
+
const beforePrice = { itemId: "sku-6", basePrice: 100, costPrice: 80 };
|
|
512
|
+
const afterPrice = { itemId: "sku-6", basePrice: 120, costPrice: 80 };
|
|
513
|
+
vi.mocked(getSkuPrice).mockResolvedValueOnce(beforePrice).mockResolvedValueOnce(afterPrice);
|
|
514
|
+
const fixedPrices = [
|
|
515
|
+
{
|
|
516
|
+
tradePolicyId: "1",
|
|
517
|
+
value: 110,
|
|
518
|
+
listPrice: 130,
|
|
519
|
+
minQuantity: 2,
|
|
520
|
+
dateRange: {
|
|
521
|
+
from: "2026-06-01T00:00:00Z",
|
|
522
|
+
to: "2026-06-30T00:00:00Z"
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
];
|
|
526
|
+
const result = await updateSkuPriceHandler({
|
|
527
|
+
profileId: "custom",
|
|
528
|
+
skuId: "sku-6",
|
|
529
|
+
basePrice: 120,
|
|
530
|
+
costPrice: 80,
|
|
531
|
+
listPrice: 140,
|
|
532
|
+
fixedPrices
|
|
533
|
+
});
|
|
534
|
+
expect(updateSkuPrice).toHaveBeenCalledWith("profile-1", "sku-6", {
|
|
535
|
+
basePrice: 120,
|
|
536
|
+
costPrice: 80,
|
|
537
|
+
markup: void 0,
|
|
538
|
+
listPrice: 140,
|
|
539
|
+
fixedPrices
|
|
540
|
+
});
|
|
541
|
+
expect(buildWriteSuccessResponse).toHaveBeenCalledWith({
|
|
542
|
+
profileId: "profile-1",
|
|
543
|
+
operation: "update_sku_price",
|
|
544
|
+
resourceId: "sku-6",
|
|
545
|
+
riskLevel: "low",
|
|
546
|
+
message: "SKU price updated successfully.",
|
|
547
|
+
before: beforePrice,
|
|
548
|
+
after: afterPrice,
|
|
549
|
+
details: { sku_id: "sku-6" }
|
|
550
|
+
});
|
|
551
|
+
expect(result).toEqual({
|
|
552
|
+
kind: "success",
|
|
553
|
+
params: expect.objectContaining({ operation: "update_sku_price" })
|
|
554
|
+
});
|
|
555
|
+
});
|
|
556
|
+
it("does not swallow unrelated 404 price lookup failures", async () => {
|
|
557
|
+
const generic404Error = {
|
|
558
|
+
isAxiosError: true,
|
|
559
|
+
response: { status: 404, data: { message: "SKU pricing endpoint unavailable" } },
|
|
560
|
+
message: "Request failed with status code 404"
|
|
561
|
+
};
|
|
562
|
+
vi.mocked(getSkuPrice).mockRejectedValue(generic404Error);
|
|
563
|
+
const result = await updateSkuPriceHandler({
|
|
564
|
+
profileId: "custom",
|
|
565
|
+
skuId: "sku-6",
|
|
566
|
+
basePrice: 120
|
|
567
|
+
});
|
|
568
|
+
expect(updateSkuPrice).not.toHaveBeenCalled();
|
|
569
|
+
expect(handleVtexWriteError).toHaveBeenCalledWith(
|
|
570
|
+
generic404Error,
|
|
571
|
+
"Failed to update price for SKU sku-6"
|
|
572
|
+
);
|
|
573
|
+
expect(result).toEqual({
|
|
574
|
+
kind: "error",
|
|
575
|
+
fallback: "Failed to update price for SKU sku-6",
|
|
576
|
+
err: generic404Error
|
|
577
|
+
});
|
|
578
|
+
});
|
|
579
|
+
it("creates missing pricing when the seed SKU has no price snapshot yet", async () => {
|
|
580
|
+
const missingPriceError = {
|
|
581
|
+
isAxiosError: true,
|
|
582
|
+
response: { status: 404, data: { message: "Price not found" } },
|
|
583
|
+
message: "Request failed with status code 404"
|
|
584
|
+
};
|
|
585
|
+
const afterPrice = { itemId: "sku-6", basePrice: 1e4, listPrice: 1e4 };
|
|
586
|
+
vi.mocked(getSkuPrice).mockRejectedValueOnce(missingPriceError).mockResolvedValueOnce(afterPrice);
|
|
587
|
+
const result = await updateSkuPriceHandler({
|
|
588
|
+
profileId: "custom",
|
|
589
|
+
skuId: "sku-6",
|
|
590
|
+
basePrice: 100,
|
|
591
|
+
listPrice: 100
|
|
592
|
+
});
|
|
593
|
+
expect(updateSkuPrice).toHaveBeenCalledWith("profile-1", "sku-6", {
|
|
594
|
+
basePrice: 100,
|
|
595
|
+
costPrice: void 0,
|
|
596
|
+
markup: void 0,
|
|
597
|
+
listPrice: 100,
|
|
598
|
+
fixedPrices: void 0
|
|
599
|
+
});
|
|
600
|
+
expect(buildWriteSuccessResponse).toHaveBeenCalledWith({
|
|
601
|
+
profileId: "profile-1",
|
|
602
|
+
operation: "update_sku_price",
|
|
603
|
+
resourceId: "sku-6",
|
|
604
|
+
riskLevel: "low",
|
|
605
|
+
message: "SKU price updated successfully.",
|
|
606
|
+
before: null,
|
|
607
|
+
after: afterPrice,
|
|
608
|
+
details: { sku_id: "sku-6" }
|
|
609
|
+
});
|
|
610
|
+
expect(result).toEqual({
|
|
611
|
+
kind: "success",
|
|
612
|
+
params: expect.objectContaining({ operation: "update_sku_price" })
|
|
613
|
+
});
|
|
614
|
+
});
|
|
615
|
+
it("returns profile selection payload without mutating anything", async () => {
|
|
616
|
+
vi.mocked(resolveVtexWriteProfile).mockResolvedValue({
|
|
617
|
+
ok: false,
|
|
618
|
+
response: { selection: true }
|
|
619
|
+
});
|
|
620
|
+
const result = await updateSkuPriceHandler({
|
|
621
|
+
skuId: "sku-6",
|
|
622
|
+
basePrice: 120
|
|
623
|
+
});
|
|
624
|
+
expect(result).toEqual({ selection: true });
|
|
625
|
+
expect(getSkuPrice).not.toHaveBeenCalled();
|
|
626
|
+
expect(updateSkuPrice).not.toHaveBeenCalled();
|
|
627
|
+
});
|
|
628
|
+
it("maps service errors through handleVtexWriteError", async () => {
|
|
629
|
+
vi.mocked(getSkuPrice).mockRejectedValue(new Error("boom"));
|
|
630
|
+
const result = await updateSkuPriceHandler({
|
|
631
|
+
profileId: "custom",
|
|
632
|
+
skuId: "sku-6",
|
|
633
|
+
basePrice: 120
|
|
634
|
+
});
|
|
635
|
+
expect(handleVtexWriteError).toHaveBeenCalledWith(
|
|
636
|
+
expect.any(Error),
|
|
637
|
+
"Failed to update price for SKU sku-6"
|
|
638
|
+
);
|
|
639
|
+
expect(result).toEqual({
|
|
640
|
+
kind: "error",
|
|
641
|
+
fallback: "Failed to update price for SKU sku-6",
|
|
642
|
+
err: expect.any(Error)
|
|
643
|
+
});
|
|
644
|
+
});
|
|
645
|
+
});
|
|
646
|
+
});
|
|
647
|
+
//# sourceMappingURL=vtex-write-simple-tools.test.js.map
|