@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.
Files changed (239) hide show
  1. package/README.md +4 -5
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/index.js +398 -8
  4. package/dist/index.js.map +2 -2
  5. package/dist/mcp-use.json +2 -2
  6. package/dist/src/config/__tests__/meta.test.js +35 -0
  7. package/dist/src/config/__tests__/meta.test.js.map +7 -0
  8. package/dist/src/config/mercadolibre-profile-store.js +25 -0
  9. package/dist/src/config/mercadolibre-profile-store.js.map +2 -2
  10. package/dist/src/config/meta-store.js +109 -0
  11. package/dist/src/config/meta-store.js.map +7 -0
  12. package/dist/src/config/meta.js +0 -2
  13. package/dist/src/config/meta.js.map +2 -2
  14. package/dist/src/meta/__tests__/meta-utils.test.js +155 -0
  15. package/dist/src/meta/__tests__/meta-utils.test.js.map +7 -0
  16. package/dist/src/services/mercadolibre/__tests__/mercadolibre-api.test.js +265 -0
  17. package/dist/src/services/mercadolibre/__tests__/mercadolibre-api.test.js.map +7 -0
  18. package/dist/src/services/mercadolibre/__tests__/mercadolibre-items.test.js +311 -0
  19. package/dist/src/services/mercadolibre/__tests__/mercadolibre-items.test.js.map +7 -0
  20. package/dist/src/services/mercadolibre/__tests__/mercadolibre-orders.test.js +220 -0
  21. package/dist/src/services/mercadolibre/__tests__/mercadolibre-orders.test.js.map +7 -0
  22. package/dist/src/services/meta/__tests__/meta-ads.test.js +126 -0
  23. package/dist/src/services/meta/__tests__/meta-ads.test.js.map +7 -0
  24. package/dist/src/services/meta/__tests__/meta-api.test.js +70 -0
  25. package/dist/src/services/meta/__tests__/meta-api.test.js.map +7 -0
  26. package/dist/src/services/meta/meta-ads.js +94 -40
  27. package/dist/src/services/meta/meta-ads.js.map +2 -2
  28. package/dist/src/services/meta/meta-api.js +8 -6
  29. package/dist/src/services/meta/meta-api.js.map +2 -2
  30. package/dist/src/services/vtex/__tests__/vtex-catalog-write-batch.test.js +165 -0
  31. package/dist/src/services/vtex/__tests__/vtex-catalog-write-batch.test.js.map +7 -0
  32. package/dist/src/services/vtex/__tests__/vtex-catalog-write-eans.test.js +92 -0
  33. package/dist/src/services/vtex/__tests__/vtex-catalog-write-eans.test.js.map +7 -0
  34. package/dist/src/services/vtex/__tests__/vtex-catalog-write-products.test.js +41 -0
  35. package/dist/src/services/vtex/__tests__/vtex-catalog-write-products.test.js.map +7 -0
  36. package/dist/src/services/vtex/__tests__/vtex-catalog.test.js +80 -0
  37. package/dist/src/services/vtex/__tests__/vtex-catalog.test.js.map +7 -0
  38. package/dist/src/services/vtex/__tests__/vtex-orders-write-http.test.js +85 -0
  39. package/dist/src/services/vtex/__tests__/vtex-orders-write-http.test.js.map +7 -0
  40. package/dist/src/services/vtex/__tests__/vtex-orders-write-state-validation.test.js +251 -0
  41. package/dist/src/services/vtex/__tests__/vtex-orders-write-state-validation.test.js.map +7 -0
  42. package/dist/src/services/vtex/__tests__/vtex-write.test.js +111 -0
  43. package/dist/src/services/vtex/__tests__/vtex-write.test.js.map +7 -0
  44. package/dist/src/services/vtex/vtex-catalog-write.js +194 -1
  45. package/dist/src/services/vtex/vtex-catalog-write.js.map +2 -2
  46. package/dist/src/services/vtex/vtex-catalog.js +118 -1
  47. package/dist/src/services/vtex/vtex-catalog.js.map +2 -2
  48. package/dist/src/services/vtex/vtex-pricing-write.js +5 -0
  49. package/dist/src/services/vtex/vtex-pricing-write.js.map +2 -2
  50. package/dist/src/services/vtex/vtex-write.js +67 -1
  51. package/dist/src/services/vtex/vtex-write.js.map +2 -2
  52. package/dist/src/tools/config/list-profiles.js +85 -11
  53. package/dist/src/tools/config/list-profiles.js.map +2 -2
  54. package/dist/src/tools/index.js.map +2 -2
  55. package/dist/src/tools/mercadolibre/__tests__/helpers.test.js +37 -0
  56. package/dist/src/tools/mercadolibre/__tests__/helpers.test.js.map +7 -0
  57. package/dist/src/tools/mercadolibre/__tests__/profile-resolution.test.js +30 -0
  58. package/dist/src/tools/mercadolibre/__tests__/profile-resolution.test.js.map +7 -0
  59. package/dist/src/tools/mercadolibre/profile-resolution.js +4 -50
  60. package/dist/src/tools/mercadolibre/profile-resolution.js.map +2 -2
  61. package/dist/src/tools/meta/__tests__/pagination.test.js +133 -0
  62. package/dist/src/tools/meta/__tests__/pagination.test.js.map +7 -0
  63. package/dist/src/tools/meta/__tests__/profile-access.test.js +262 -0
  64. package/dist/src/tools/meta/__tests__/profile-access.test.js.map +7 -0
  65. package/dist/src/tools/meta/__tests__/read-tools.test.js +722 -0
  66. package/dist/src/tools/meta/__tests__/read-tools.test.js.map +7 -0
  67. package/dist/src/tools/meta/__tests__/schemas.test.js +103 -0
  68. package/dist/src/tools/meta/__tests__/schemas.test.js.map +7 -0
  69. package/dist/src/tools/meta/account-overview.js +37 -19
  70. package/dist/src/tools/meta/account-overview.js.map +2 -2
  71. package/dist/src/tools/meta/ad-account-info.js +31 -6
  72. package/dist/src/tools/meta/ad-account-info.js.map +2 -2
  73. package/dist/src/tools/meta/ads-performance.js +35 -21
  74. package/dist/src/tools/meta/ads-performance.js.map +2 -2
  75. package/dist/src/tools/meta/campaign-performance.js +35 -18
  76. package/dist/src/tools/meta/campaign-performance.js.map +2 -2
  77. package/dist/src/tools/meta/list-accessible-ad-accounts.js +39 -10
  78. package/dist/src/tools/meta/list-accessible-ad-accounts.js.map +2 -2
  79. package/dist/src/tools/meta/list-accessible-businesses.js +37 -13
  80. package/dist/src/tools/meta/list-accessible-businesses.js.map +2 -2
  81. package/dist/src/tools/meta/placement-mix.js +37 -22
  82. package/dist/src/tools/meta/placement-mix.js.map +2 -2
  83. package/dist/src/tools/meta/profile-access.js +215 -0
  84. package/dist/src/tools/meta/profile-access.js.map +7 -0
  85. package/dist/src/tools/meta/schema-helpers.js +19 -0
  86. package/dist/src/tools/meta/schema-helpers.js.map +7 -0
  87. package/dist/src/tools/meta/time-series.js +40 -23
  88. package/dist/src/tools/meta/time-series.js.map +2 -2
  89. package/dist/src/tools/vtex/__tests__/catalog-admin-batch.test.js +233 -0
  90. package/dist/src/tools/vtex/__tests__/catalog-admin-batch.test.js.map +7 -0
  91. package/dist/src/tools/vtex/__tests__/catalog-admin-categories.test.js +649 -0
  92. package/dist/src/tools/vtex/__tests__/catalog-admin-categories.test.js.map +7 -0
  93. package/dist/src/tools/vtex/__tests__/catalog-admin-product-specifications.test.js +339 -0
  94. package/dist/src/tools/vtex/__tests__/catalog-admin-product-specifications.test.js.map +7 -0
  95. package/dist/src/tools/vtex/__tests__/catalog-admin-products-skus.test.js +235 -0
  96. package/dist/src/tools/vtex/__tests__/catalog-admin-products-skus.test.js.map +7 -0
  97. package/dist/src/tools/vtex/__tests__/catalog-navigation-reads.test.js +372 -0
  98. package/dist/src/tools/vtex/__tests__/catalog-navigation-reads.test.js.map +7 -0
  99. package/dist/src/tools/vtex/__tests__/catalog-navigation-surface.test.js +57 -0
  100. package/dist/src/tools/vtex/__tests__/catalog-navigation-surface.test.js.map +7 -0
  101. package/dist/src/tools/vtex/__tests__/write-helpers.test.js +97 -0
  102. package/dist/src/tools/vtex/__tests__/write-helpers.test.js.map +7 -0
  103. package/dist/src/tools/vtex/activate-sku.js +1 -48
  104. package/dist/src/tools/vtex/activate-sku.js.map +2 -2
  105. package/dist/src/tools/vtex/associate-specification.js +3 -54
  106. package/dist/src/tools/vtex/associate-specification.js.map +2 -2
  107. package/dist/src/tools/vtex/attach-catalog-image.js +3 -57
  108. package/dist/src/tools/vtex/attach-catalog-image.js.map +2 -2
  109. package/dist/src/tools/vtex/catalog-admin-batch.js +298 -0
  110. package/dist/src/tools/vtex/catalog-admin-batch.js.map +7 -0
  111. package/dist/src/tools/vtex/catalog-admin-categories.js +542 -0
  112. package/dist/src/tools/vtex/catalog-admin-categories.js.map +7 -0
  113. package/dist/src/tools/vtex/catalog-admin-product-specifications.js +275 -0
  114. package/dist/src/tools/vtex/catalog-admin-product-specifications.js.map +7 -0
  115. package/dist/src/tools/vtex/catalog-admin-products-skus.js +475 -0
  116. package/dist/src/tools/vtex/catalog-admin-products-skus.js.map +7 -0
  117. package/dist/src/tools/vtex/catalog-navigation-reads.js +430 -0
  118. package/dist/src/tools/vtex/catalog-navigation-reads.js.map +7 -0
  119. package/dist/src/tools/vtex/create-brand.js +1 -64
  120. package/dist/src/tools/vtex/create-brand.js.map +2 -2
  121. package/dist/src/tools/vtex/create-category.js +1 -76
  122. package/dist/src/tools/vtex/create-category.js.map +2 -2
  123. package/dist/src/tools/vtex/create-product-with-sku.js +3 -114
  124. package/dist/src/tools/vtex/create-product-with-sku.js.map +2 -2
  125. package/dist/src/tools/vtex/create-specification-value.js +3 -47
  126. package/dist/src/tools/vtex/create-specification-value.js.map +2 -2
  127. package/dist/src/tools/vtex/create-specification.js +1 -80
  128. package/dist/src/tools/vtex/create-specification.js.map +2 -2
  129. package/dist/src/tools/vtex/deactivate-sku.js +1 -48
  130. package/dist/src/tools/vtex/deactivate-sku.js.map +2 -2
  131. package/dist/src/tools/vtex/delete-all-product-specifications.js +9 -0
  132. package/dist/src/tools/vtex/delete-all-product-specifications.js.map +7 -0
  133. package/dist/src/tools/vtex/delete-all-sku-specifications.js +9 -0
  134. package/dist/src/tools/vtex/delete-all-sku-specifications.js.map +7 -0
  135. package/dist/src/tools/vtex/delete-brand.js +9 -0
  136. package/dist/src/tools/vtex/delete-brand.js.map +7 -0
  137. package/dist/src/tools/vtex/delete-catalog-image.js +9 -0
  138. package/dist/src/tools/vtex/delete-catalog-image.js.map +7 -0
  139. package/dist/src/tools/vtex/delete-product-specification.js +9 -0
  140. package/dist/src/tools/vtex/delete-product-specification.js.map +7 -0
  141. package/dist/src/tools/vtex/delete-sku-price.js +55 -0
  142. package/dist/src/tools/vtex/delete-sku-price.js.map +7 -0
  143. package/dist/src/tools/vtex/delete-sku-specification.js +9 -0
  144. package/dist/src/tools/vtex/delete-sku-specification.js.map +7 -0
  145. package/dist/src/tools/vtex/get-brand.js +6 -0
  146. package/dist/src/tools/vtex/get-brand.js.map +7 -0
  147. package/dist/src/tools/vtex/get-category-tree.js +6 -0
  148. package/dist/src/tools/vtex/get-category-tree.js.map +7 -0
  149. package/dist/src/tools/vtex/get-category.js +6 -0
  150. package/dist/src/tools/vtex/get-category.js.map +7 -0
  151. package/dist/src/tools/vtex/get-product-specifications.js +9 -0
  152. package/dist/src/tools/vtex/get-product-specifications.js.map +7 -0
  153. package/dist/src/tools/vtex/get-product.js +6 -0
  154. package/dist/src/tools/vtex/get-product.js.map +7 -0
  155. package/dist/src/tools/vtex/get-sku.js +6 -0
  156. package/dist/src/tools/vtex/get-sku.js.map +7 -0
  157. package/dist/src/tools/vtex/index.js +23 -1
  158. package/dist/src/tools/vtex/index.js.map +2 -2
  159. package/dist/src/tools/vtex/list-brands.js +6 -0
  160. package/dist/src/tools/vtex/list-brands.js.map +7 -0
  161. package/dist/src/tools/vtex/list-categories.js +6 -0
  162. package/dist/src/tools/vtex/list-categories.js.map +7 -0
  163. package/dist/src/tools/vtex/list-products-by-category.js +6 -0
  164. package/dist/src/tools/vtex/list-products-by-category.js.map +7 -0
  165. package/dist/src/tools/vtex/list-products.js +6 -0
  166. package/dist/src/tools/vtex/list-products.js.map +7 -0
  167. package/dist/src/tools/vtex/list-skus-by-product.js +6 -0
  168. package/dist/src/tools/vtex/list-skus-by-product.js.map +7 -0
  169. package/dist/src/tools/vtex/list-specification-groups.js +9 -0
  170. package/dist/src/tools/vtex/list-specification-groups.js.map +7 -0
  171. package/dist/src/tools/vtex/move-category.js +6 -0
  172. package/dist/src/tools/vtex/move-category.js.map +7 -0
  173. package/dist/src/tools/vtex/profile-resolution.js +4 -51
  174. package/dist/src/tools/vtex/profile-resolution.js.map +2 -2
  175. package/dist/src/tools/vtex/update-brand.js +6 -0
  176. package/dist/src/tools/vtex/update-brand.js.map +7 -0
  177. package/dist/src/tools/vtex/update-category.js +6 -0
  178. package/dist/src/tools/vtex/update-category.js.map +7 -0
  179. package/dist/src/tools/vtex/update-product-basic-fields.js +3 -65
  180. package/dist/src/tools/vtex/update-product-basic-fields.js.map +2 -2
  181. package/dist/src/tools/vtex/update-sku-basic-fields.js +1 -87
  182. package/dist/src/tools/vtex/update-sku-basic-fields.js.map +2 -2
  183. package/dist/src/tools/vtex/update-sku-price.js +21 -1
  184. package/dist/src/tools/vtex/update-sku-price.js.map +2 -2
  185. package/dist/src/tools/vtex/write-helpers.js +104 -14
  186. package/dist/src/tools/vtex/write-helpers.js.map +2 -2
  187. package/dist/src/utils/provider-profile-selection.js +117 -0
  188. package/dist/src/utils/provider-profile-selection.js.map +7 -0
  189. package/dist/tests/meli/mercadolibre-tool-handlers-medium-batch4.test.js +678 -0
  190. package/dist/tests/meli/mercadolibre-tool-handlers-medium-batch4.test.js.map +7 -0
  191. package/dist/tests/meli/mercadolibre-tool-handlers-monster-batch5.test.js +564 -0
  192. package/dist/tests/meli/mercadolibre-tool-handlers-monster-batch5.test.js.map +7 -0
  193. package/dist/tests/meli/mercadolibre-tool-handlers-monster-batch6.test.js +387 -0
  194. package/dist/tests/meli/mercadolibre-tool-handlers-monster-batch6.test.js.map +7 -0
  195. package/dist/tests/meli/mercadolibre-tool-handlers-monster-batch7.test.js +368 -0
  196. package/dist/tests/meli/mercadolibre-tool-handlers-monster-batch7.test.js.map +7 -0
  197. package/dist/tests/meli/mercadolibre-tool-handlers-small.test.js +626 -0
  198. package/dist/tests/meli/mercadolibre-tool-handlers-small.test.js.map +7 -0
  199. package/dist/tests/meli/mercadolibre-tool-handlers-write-batch8.test.js +480 -0
  200. package/dist/tests/meli/mercadolibre-tool-handlers-write-batch8.test.js.map +7 -0
  201. package/dist/tests/setup.js +2 -0
  202. package/dist/tests/setup.js.map +7 -0
  203. package/dist/tests/smoke/test-harness.test.js +7 -0
  204. package/dist/tests/smoke/test-harness.test.js.map +7 -0
  205. package/dist/tests/vtex/read-only-utils.test.js +161 -0
  206. package/dist/tests/vtex/read-only-utils.test.js.map +7 -0
  207. package/dist/tests/vtex/vtex-catalog-admin-docs.test.js +24 -0
  208. package/dist/tests/vtex/vtex-catalog-admin-docs.test.js.map +7 -0
  209. package/dist/tests/vtex/vtex-catalog-admin-surface.test.js +30 -0
  210. package/dist/tests/vtex/vtex-catalog-admin-surface.test.js.map +7 -0
  211. package/dist/tests/vtex/vtex-catalog-write-tools.test.js +712 -0
  212. package/dist/tests/vtex/vtex-catalog-write-tools.test.js.map +7 -0
  213. package/dist/tests/vtex/vtex-catalog.service.test.js +51 -0
  214. package/dist/tests/vtex/vtex-catalog.service.test.js.map +7 -0
  215. package/dist/tests/vtex/vtex-inventory-tools.test.js +201 -0
  216. package/dist/tests/vtex/vtex-inventory-tools.test.js.map +7 -0
  217. package/dist/tests/vtex/vtex-logistics.service.test.js +134 -0
  218. package/dist/tests/vtex/vtex-logistics.service.test.js.map +7 -0
  219. package/dist/tests/vtex/vtex-order-details.tool.test.js +141 -0
  220. package/dist/tests/vtex/vtex-order-details.tool.test.js.map +7 -0
  221. package/dist/tests/vtex/vtex-order-write-tools.test.js +483 -0
  222. package/dist/tests/vtex/vtex-order-write-tools.test.js.map +7 -0
  223. package/dist/tests/vtex/vtex-orders-summary.tool.test.js +185 -0
  224. package/dist/tests/vtex/vtex-orders-summary.tool.test.js.map +7 -0
  225. package/dist/tests/vtex/vtex-orders.service.test.js +120 -0
  226. package/dist/tests/vtex/vtex-orders.service.test.js.map +7 -0
  227. package/dist/tests/vtex/vtex-pricing-write-tools.test.js +202 -0
  228. package/dist/tests/vtex/vtex-pricing-write-tools.test.js.map +7 -0
  229. package/dist/tests/vtex/vtex-pricing-write.service.test.js +106 -0
  230. package/dist/tests/vtex/vtex-pricing-write.service.test.js.map +7 -0
  231. package/dist/tests/vtex/vtex-pricing.service.test.js +88 -0
  232. package/dist/tests/vtex/vtex-pricing.service.test.js.map +7 -0
  233. package/dist/tests/vtex/vtex-small-tools.test.js +190 -0
  234. package/dist/tests/vtex/vtex-small-tools.test.js.map +7 -0
  235. package/dist/tests/vtex/vtex-write-simple-tools.test.js +647 -0
  236. package/dist/tests/vtex/vtex-write-simple-tools.test.js.map +7 -0
  237. package/dist/vitest.config.js +15 -0
  238. package/dist/vitest.config.js.map +7 -0
  239. package/package.json +6 -2
@@ -0,0 +1,712 @@
1
+ import { beforeEach, describe, expect, it, vi } from "vitest";
2
+ import { z } from "zod";
3
+ import { associateSpecificationHandler } from "../../src/tools/vtex/associate-specification.js";
4
+ import { createBrandHandler } from "../../src/tools/vtex/create-brand.js";
5
+ import { createCategoryHandler } from "../../src/tools/vtex/create-category.js";
6
+ import { createProductHandler } from "../../src/tools/vtex/create-product.js";
7
+ import { createSkuHandler } from "../../src/tools/vtex/create-sku.js";
8
+ import {
9
+ createSpecificationHandler,
10
+ createSpecificationSchema
11
+ } from "../../src/tools/vtex/create-specification.js";
12
+ import { createSpecificationValueHandler } from "../../src/tools/vtex/create-specification-value.js";
13
+ import {
14
+ associateProductSpecification,
15
+ associateSkuSpecification,
16
+ createBrand,
17
+ createCategory,
18
+ createProduct,
19
+ createSku,
20
+ createSkuEan,
21
+ createSpecification,
22
+ createSpecificationValue
23
+ } from "../../src/services/vtex/vtex-catalog-write.js";
24
+ import {
25
+ buildWriteSuccessResponse,
26
+ handleVtexWriteError,
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
+ handleVtexWriteError: vi.fn((err, fallback) => ({
39
+ kind: "error",
40
+ fallback,
41
+ err
42
+ }))
43
+ }));
44
+ vi.mock("../../src/services/vtex/vtex-catalog-write.js", () => ({
45
+ createBrand: vi.fn(),
46
+ createCategory: vi.fn(),
47
+ createProduct: vi.fn(),
48
+ createSku: vi.fn(),
49
+ createSkuEan: vi.fn(),
50
+ createSpecification: vi.fn(),
51
+ createSpecificationValue: vi.fn(),
52
+ associateProductSpecification: vi.fn(),
53
+ associateSkuSpecification: vi.fn()
54
+ }));
55
+ describe("VTEX catalog/specification write tools", () => {
56
+ beforeEach(() => {
57
+ vi.clearAllMocks();
58
+ vi.mocked(resolveVtexWriteProfile).mockResolvedValue({
59
+ ok: true,
60
+ value: { profileId: "profile-1" }
61
+ });
62
+ });
63
+ describe("createBrandHandler", () => {
64
+ it("applies default payload values before creating a brand", async () => {
65
+ const createdBrand = { Id: 10, Name: "Nike", Active: true };
66
+ vi.mocked(createBrand).mockResolvedValue(createdBrand);
67
+ const result = await createBrandHandler({
68
+ profileId: "custom",
69
+ id: 10,
70
+ name: "Nike",
71
+ linkId: "nike"
72
+ });
73
+ expect(createBrand).toHaveBeenCalledWith("profile-1", {
74
+ Id: 10,
75
+ Name: "Nike",
76
+ Text: "Nike",
77
+ Keywords: "",
78
+ SiteTitle: "Nike",
79
+ Active: true,
80
+ MenuHome: false,
81
+ LinkId: "nike",
82
+ Score: null,
83
+ AdWordsRemarketingCode: null,
84
+ LomadeeCampaignCode: null
85
+ });
86
+ expect(buildWriteSuccessResponse).toHaveBeenCalledWith({
87
+ profileId: "profile-1",
88
+ operation: "create_brand",
89
+ resourceId: "10",
90
+ riskLevel: "low",
91
+ message: "Brand created successfully.",
92
+ before: null,
93
+ after: createdBrand
94
+ });
95
+ expect(result).toEqual({
96
+ kind: "success",
97
+ params: expect.objectContaining({ operation: "create_brand" })
98
+ });
99
+ });
100
+ it("returns profile selection payload when profile resolution fails", async () => {
101
+ vi.mocked(resolveVtexWriteProfile).mockResolvedValue({
102
+ ok: false,
103
+ response: { selection: true }
104
+ });
105
+ const result = await createBrandHandler({ id: 10, name: "Nike" });
106
+ expect(result).toEqual({ selection: true });
107
+ expect(createBrand).not.toHaveBeenCalled();
108
+ });
109
+ it("maps service errors through handleVtexWriteError", async () => {
110
+ vi.mocked(createBrand).mockRejectedValue(new Error("boom"));
111
+ const result = await createBrandHandler({
112
+ profileId: "custom",
113
+ id: 10,
114
+ name: "Nike"
115
+ });
116
+ expect(handleVtexWriteError).toHaveBeenCalledWith(
117
+ expect.any(Error),
118
+ "Failed to create brand in VTEX"
119
+ );
120
+ expect(result).toEqual({
121
+ kind: "error",
122
+ fallback: "Failed to create brand in VTEX",
123
+ err: expect.any(Error)
124
+ });
125
+ });
126
+ });
127
+ describe("createCategoryHandler", () => {
128
+ it("forwards the expected VTEX category payload with defaults", async () => {
129
+ const createdCategory = { Id: 20, Name: "Shoes" };
130
+ vi.mocked(createCategory).mockResolvedValue(createdCategory);
131
+ const result = await createCategoryHandler({
132
+ profileId: "custom",
133
+ name: "Shoes",
134
+ title: "Running Shoes",
135
+ description: "Category description",
136
+ fatherCategoryId: null,
137
+ globalCategoryId: 123,
138
+ stockKeepingUnitSelectionMode: "SPECIFICATION"
139
+ });
140
+ expect(createCategory).toHaveBeenCalledWith("profile-1", {
141
+ Id: void 0,
142
+ Name: "Shoes",
143
+ Keywords: "",
144
+ Title: "Running Shoes",
145
+ Description: "Category description",
146
+ AdWordsRemarketingCode: null,
147
+ LomadeeCampaignCode: null,
148
+ FatherCategoryId: null,
149
+ GlobalCategoryId: 123,
150
+ ShowInStoreFront: true,
151
+ IsActive: true,
152
+ ActiveStoreFrontLink: true,
153
+ ShowBrandFilter: true,
154
+ Score: null,
155
+ StockKeepingUnitSelectionMode: "SPECIFICATION"
156
+ });
157
+ expect(buildWriteSuccessResponse).toHaveBeenCalledWith({
158
+ profileId: "profile-1",
159
+ operation: "create_category",
160
+ resourceId: "20",
161
+ riskLevel: "low",
162
+ message: "Category created successfully.",
163
+ before: null,
164
+ after: createdCategory
165
+ });
166
+ expect(result).toEqual({
167
+ kind: "success",
168
+ params: expect.objectContaining({ operation: "create_category" })
169
+ });
170
+ });
171
+ it("returns profile selection payload when profile resolution fails", async () => {
172
+ vi.mocked(resolveVtexWriteProfile).mockResolvedValue({
173
+ ok: false,
174
+ response: { selection: true }
175
+ });
176
+ const result = await createCategoryHandler({
177
+ name: "Shoes",
178
+ title: "Running Shoes",
179
+ description: "Category description",
180
+ globalCategoryId: 123,
181
+ stockKeepingUnitSelectionMode: "SPECIFICATION"
182
+ });
183
+ expect(result).toEqual({ selection: true });
184
+ expect(createCategory).not.toHaveBeenCalled();
185
+ });
186
+ it("maps service errors through handleVtexWriteError", async () => {
187
+ vi.mocked(createCategory).mockRejectedValue(new Error("boom"));
188
+ const result = await createCategoryHandler({
189
+ profileId: "custom",
190
+ name: "Shoes",
191
+ title: "Running Shoes",
192
+ description: "Category description",
193
+ globalCategoryId: 123,
194
+ stockKeepingUnitSelectionMode: "SPECIFICATION"
195
+ });
196
+ expect(handleVtexWriteError).toHaveBeenCalledWith(
197
+ expect.any(Error),
198
+ "Failed to create category in VTEX"
199
+ );
200
+ expect(result).toEqual({
201
+ kind: "error",
202
+ fallback: "Failed to create category in VTEX",
203
+ err: expect.any(Error)
204
+ });
205
+ });
206
+ });
207
+ describe("createProductHandler", () => {
208
+ it("forwards the expected VTEX product payload and builds a success response", async () => {
209
+ const createdProduct = { Id: 55, Name: "Running Shoe" };
210
+ vi.mocked(createProduct).mockResolvedValue(createdProduct);
211
+ const result = await createProductHandler({
212
+ profileId: "custom",
213
+ name: "Running Shoe",
214
+ categoryPath: "Mens/Shoes/Running",
215
+ brandName: "Acme",
216
+ departmentId: 7,
217
+ refId: "RUN-001",
218
+ title: "Running Shoe",
219
+ description: "Main description",
220
+ descriptionShort: "Short description",
221
+ linkId: "running-shoe",
222
+ releaseDate: "2026-06-01T00:00:00Z",
223
+ keyWords: "running,shoe",
224
+ isVisible: true,
225
+ isActive: false,
226
+ taxCode: "TX-1",
227
+ metaTagDescription: "Meta description",
228
+ supplierId: 91,
229
+ showWithoutStock: true,
230
+ score: 12
231
+ });
232
+ expect(createProduct).toHaveBeenCalledWith("profile-1", {
233
+ Name: "Running Shoe",
234
+ CategoryPath: "Mens/Shoes/Running",
235
+ BrandName: "Acme",
236
+ DepartmentId: 7,
237
+ RefId: "RUN-001",
238
+ Title: "Running Shoe",
239
+ Description: "Main description",
240
+ DescriptionShort: "Short description",
241
+ LinkId: "running-shoe",
242
+ ReleaseDate: "2026-06-01T00:00:00Z",
243
+ KeyWords: "running,shoe",
244
+ IsVisible: true,
245
+ IsActive: false,
246
+ TaxCode: "TX-1",
247
+ MetaTagDescription: "Meta description",
248
+ SupplierId: 91,
249
+ ShowWithoutStock: true,
250
+ Score: 12
251
+ });
252
+ expect(buildWriteSuccessResponse).toHaveBeenCalledWith({
253
+ profileId: "profile-1",
254
+ operation: "create_product",
255
+ resourceId: "55",
256
+ riskLevel: "low",
257
+ message: "Product created successfully.",
258
+ before: null,
259
+ after: createdProduct,
260
+ details: {
261
+ product_id: "55"
262
+ }
263
+ });
264
+ expect(result).toEqual({
265
+ kind: "success",
266
+ params: expect.objectContaining({ operation: "create_product" })
267
+ });
268
+ });
269
+ it("returns profile selection payload when profile resolution fails", async () => {
270
+ vi.mocked(resolveVtexWriteProfile).mockResolvedValue({
271
+ ok: false,
272
+ response: { selection: true }
273
+ });
274
+ const result = await createProductHandler({
275
+ name: "Running Shoe",
276
+ categoryId: 10,
277
+ brandId: 20
278
+ });
279
+ expect(result).toEqual({ selection: true });
280
+ expect(createProduct).not.toHaveBeenCalled();
281
+ });
282
+ it("maps service errors through handleVtexWriteError", async () => {
283
+ vi.mocked(createProduct).mockRejectedValue(new Error("boom"));
284
+ const result = await createProductHandler({
285
+ profileId: "custom",
286
+ name: "Running Shoe",
287
+ categoryId: 10,
288
+ brandId: 20
289
+ });
290
+ expect(handleVtexWriteError).toHaveBeenCalledWith(
291
+ expect.any(Error),
292
+ "Failed to create product in VTEX"
293
+ );
294
+ expect(result).toEqual({
295
+ kind: "error",
296
+ fallback: "Failed to create product in VTEX",
297
+ err: expect.any(Error)
298
+ });
299
+ });
300
+ });
301
+ describe("createSkuHandler", () => {
302
+ const skuPayload = {
303
+ profileId: "custom",
304
+ productId: 99,
305
+ name: "Running Shoe / Black / 42",
306
+ refId: "RUN-001-42",
307
+ packagedHeight: 10,
308
+ packagedLength: 20,
309
+ packagedWidth: 30,
310
+ packagedWeightKg: 1.5,
311
+ height: 9,
312
+ length: 19,
313
+ width: 29,
314
+ weightKg: 1.25,
315
+ measurementUnit: "un",
316
+ unitMultiplier: 1,
317
+ manufacturerCode: "ACME-42",
318
+ commercialConditionId: 12
319
+ };
320
+ it("forwards the expected VTEX SKU payload with default flags", async () => {
321
+ const createdSku = { Id: 77, Name: skuPayload.name };
322
+ vi.mocked(createSku).mockResolvedValue(createdSku);
323
+ const result = await createSkuHandler(skuPayload);
324
+ expect(createSku).toHaveBeenCalledWith("profile-1", {
325
+ ProductId: 99,
326
+ Name: "Running Shoe / Black / 42",
327
+ RefId: "RUN-001-42",
328
+ PackagedHeight: 10,
329
+ PackagedLength: 20,
330
+ PackagedWidth: 30,
331
+ PackagedWeightKg: 1.5,
332
+ ActivateIfPossible: true,
333
+ IsActive: false,
334
+ Height: 9,
335
+ Length: 19,
336
+ Width: 29,
337
+ WeightKg: 1.25,
338
+ MeasurementUnit: "un",
339
+ UnitMultiplier: 1,
340
+ ManufacturerCode: "ACME-42",
341
+ CommercialConditionId: 12
342
+ });
343
+ expect(createSkuEan).not.toHaveBeenCalled();
344
+ expect(buildWriteSuccessResponse).toHaveBeenCalledWith({
345
+ profileId: "profile-1",
346
+ operation: "create_sku",
347
+ resourceId: "77",
348
+ riskLevel: "low",
349
+ message: "SKU created successfully.",
350
+ before: null,
351
+ after: createdSku,
352
+ details: {
353
+ product_id: 99,
354
+ sku_id: "77"
355
+ }
356
+ });
357
+ expect(result).toEqual({
358
+ kind: "success",
359
+ params: expect.objectContaining({ operation: "create_sku" })
360
+ });
361
+ });
362
+ it("creates the sku then attaches the EAN when provided", async () => {
363
+ const createdSku = { Id: 78, Name: skuPayload.name };
364
+ vi.mocked(createSku).mockResolvedValue(createdSku);
365
+ await createSkuHandler({
366
+ ...skuPayload,
367
+ ean: " 7791234567890 ",
368
+ isActive: true,
369
+ activateIfPossible: false
370
+ });
371
+ expect(createSku).toHaveBeenCalledWith("profile-1", {
372
+ ProductId: 99,
373
+ Name: "Running Shoe / Black / 42",
374
+ RefId: "RUN-001-42",
375
+ PackagedHeight: 10,
376
+ PackagedLength: 20,
377
+ PackagedWidth: 30,
378
+ PackagedWeightKg: 1.5,
379
+ ActivateIfPossible: false,
380
+ IsActive: true,
381
+ Height: 9,
382
+ Length: 19,
383
+ Width: 29,
384
+ WeightKg: 1.25,
385
+ MeasurementUnit: "un",
386
+ UnitMultiplier: 1,
387
+ ManufacturerCode: "ACME-42",
388
+ CommercialConditionId: 12
389
+ });
390
+ expect(createSkuEan).toHaveBeenCalledWith(
391
+ "profile-1",
392
+ "78",
393
+ "7791234567890"
394
+ );
395
+ expect(
396
+ vi.mocked(createSku).mock.invocationCallOrder[0]
397
+ ).toBeLessThan(vi.mocked(createSkuEan).mock.invocationCallOrder[0]);
398
+ expect(buildWriteSuccessResponse).toHaveBeenCalledWith(
399
+ expect.objectContaining({
400
+ after: expect.objectContaining({ Eans: ["7791234567890"] })
401
+ })
402
+ );
403
+ });
404
+ it("skips EAN attachment when EAN is missing or blank", async () => {
405
+ const createdSku = { Id: 79, Name: skuPayload.name };
406
+ vi.mocked(createSku).mockResolvedValue(createdSku);
407
+ await createSkuHandler({
408
+ ...skuPayload,
409
+ ean: " "
410
+ });
411
+ expect(createSkuEan).not.toHaveBeenCalled();
412
+ });
413
+ it("returns profile selection payload when profile resolution fails", async () => {
414
+ vi.mocked(resolveVtexWriteProfile).mockResolvedValue({
415
+ ok: false,
416
+ response: { selection: true }
417
+ });
418
+ const result = await createSkuHandler({
419
+ productId: 99,
420
+ name: "Running Shoe / Black / 42",
421
+ refId: "RUN-001-42",
422
+ packagedHeight: 10,
423
+ packagedLength: 20,
424
+ packagedWidth: 30,
425
+ packagedWeightKg: 1.5
426
+ });
427
+ expect(result).toEqual({ selection: true });
428
+ expect(createSku).not.toHaveBeenCalled();
429
+ expect(createSkuEan).not.toHaveBeenCalled();
430
+ });
431
+ it("maps service errors through handleVtexWriteError", async () => {
432
+ vi.mocked(createSku).mockRejectedValue(new Error("boom"));
433
+ const result = await createSkuHandler(skuPayload);
434
+ expect(handleVtexWriteError).toHaveBeenCalledWith(
435
+ expect.any(Error),
436
+ "Failed to create SKU in VTEX"
437
+ );
438
+ expect(result).toEqual({
439
+ kind: "error",
440
+ fallback: "Failed to create SKU in VTEX",
441
+ err: expect.any(Error)
442
+ });
443
+ });
444
+ });
445
+ describe("createSpecificationHandler", () => {
446
+ it("schema converts fieldTypeId from string to number", () => {
447
+ const parsed = createSpecificationSchema.parse({
448
+ fieldTypeId: "4",
449
+ fieldGroupId: 12,
450
+ name: "Color"
451
+ });
452
+ expect(parsed.fieldTypeId).toBe(4);
453
+ });
454
+ it("fills default fields before creating a specification", async () => {
455
+ const createdSpecification = { Id: 30, Name: "Color" };
456
+ vi.mocked(createSpecification).mockResolvedValue(
457
+ createdSpecification
458
+ );
459
+ const result = await createSpecificationHandler({
460
+ profileId: "custom",
461
+ fieldTypeId: 4,
462
+ fieldGroupId: 12,
463
+ name: "Color"
464
+ });
465
+ expect(createSpecification).toHaveBeenCalledWith("profile-1", {
466
+ FieldTypeId: 4,
467
+ FieldGroupId: 12,
468
+ Name: "Color",
469
+ CategoryId: void 0,
470
+ Description: null,
471
+ Position: 0,
472
+ IsFilter: false,
473
+ IsRequired: false,
474
+ IsOnProductDetails: true,
475
+ IsStockKeepingUnit: false,
476
+ IsWizard: false,
477
+ IsActive: true,
478
+ IsTopMenuLinkActive: false,
479
+ IsSideMenuLinkActive: false,
480
+ DefaultValue: ""
481
+ });
482
+ expect(buildWriteSuccessResponse).toHaveBeenCalledWith({
483
+ profileId: "profile-1",
484
+ operation: "create_specification",
485
+ resourceId: "30",
486
+ riskLevel: "low",
487
+ message: "Specification created successfully.",
488
+ before: null,
489
+ after: createdSpecification
490
+ });
491
+ expect(result).toEqual({
492
+ kind: "success",
493
+ params: expect.objectContaining({ operation: "create_specification" })
494
+ });
495
+ });
496
+ it("returns profile selection payload when profile resolution fails", async () => {
497
+ vi.mocked(resolveVtexWriteProfile).mockResolvedValue({
498
+ ok: false,
499
+ response: { selection: true }
500
+ });
501
+ const result = await createSpecificationHandler({
502
+ fieldTypeId: 4,
503
+ fieldGroupId: 12,
504
+ name: "Color"
505
+ });
506
+ expect(result).toEqual({ selection: true });
507
+ expect(createSpecification).not.toHaveBeenCalled();
508
+ });
509
+ it("maps service errors through handleVtexWriteError", async () => {
510
+ vi.mocked(createSpecification).mockRejectedValue(new Error("boom"));
511
+ const result = await createSpecificationHandler({
512
+ profileId: "custom",
513
+ fieldTypeId: 4,
514
+ fieldGroupId: 12,
515
+ name: "Color"
516
+ });
517
+ expect(handleVtexWriteError).toHaveBeenCalledWith(
518
+ expect.any(Error),
519
+ "Failed to create specification in VTEX"
520
+ );
521
+ expect(result).toEqual({
522
+ kind: "error",
523
+ fallback: "Failed to create specification in VTEX",
524
+ err: expect.any(Error)
525
+ });
526
+ });
527
+ });
528
+ describe("createSpecificationValueHandler", () => {
529
+ it("fills default fields before creating a specification value", async () => {
530
+ const createdValue = { FieldValueId: 40, Name: "Red" };
531
+ vi.mocked(createSpecificationValue).mockResolvedValue(
532
+ createdValue
533
+ );
534
+ const result = await createSpecificationValueHandler({
535
+ profileId: "custom",
536
+ fieldId: 30,
537
+ name: "Red"
538
+ });
539
+ expect(createSpecificationValue).toHaveBeenCalledWith("profile-1", {
540
+ FieldId: 30,
541
+ Name: "Red",
542
+ IsActive: true,
543
+ Position: 0,
544
+ Text: null
545
+ });
546
+ expect(buildWriteSuccessResponse).toHaveBeenCalledWith({
547
+ profileId: "profile-1",
548
+ operation: "create_specification_value",
549
+ resourceId: "40",
550
+ riskLevel: "low",
551
+ message: "Specification value created successfully.",
552
+ before: null,
553
+ after: createdValue
554
+ });
555
+ expect(result).toEqual({
556
+ kind: "success",
557
+ params: expect.objectContaining({
558
+ operation: "create_specification_value"
559
+ })
560
+ });
561
+ });
562
+ it("returns profile selection payload when profile resolution fails", async () => {
563
+ vi.mocked(resolveVtexWriteProfile).mockResolvedValue({
564
+ ok: false,
565
+ response: { selection: true }
566
+ });
567
+ const result = await createSpecificationValueHandler({
568
+ fieldId: 30,
569
+ name: "Red"
570
+ });
571
+ expect(result).toEqual({ selection: true });
572
+ expect(createSpecificationValue).not.toHaveBeenCalled();
573
+ });
574
+ it("maps service errors through handleVtexWriteError", async () => {
575
+ vi.mocked(createSpecificationValue).mockRejectedValue(new Error("boom"));
576
+ const result = await createSpecificationValueHandler({
577
+ profileId: "custom",
578
+ fieldId: 30,
579
+ name: "Red"
580
+ });
581
+ expect(handleVtexWriteError).toHaveBeenCalledWith(
582
+ expect.any(Error),
583
+ "Failed to create specification value in VTEX"
584
+ );
585
+ expect(result).toEqual({
586
+ kind: "error",
587
+ fallback: "Failed to create specification value in VTEX",
588
+ err: expect.any(Error)
589
+ });
590
+ });
591
+ });
592
+ describe("associateSpecificationHandler", () => {
593
+ it("routes product associations to the product service and shapes optional payload fields", async () => {
594
+ const association = { success: true, id: "prod-1" };
595
+ vi.mocked(associateProductSpecification).mockResolvedValue(
596
+ association
597
+ );
598
+ const result = await associateSpecificationHandler({
599
+ profileId: "custom",
600
+ entityType: "product",
601
+ resourceId: "prod-1",
602
+ fieldId: 30,
603
+ fieldValueId: 40
604
+ });
605
+ expect(associateProductSpecification).toHaveBeenCalledWith(
606
+ "profile-1",
607
+ "prod-1",
608
+ {
609
+ FieldId: 30,
610
+ FieldValueId: 40
611
+ }
612
+ );
613
+ expect(associateSkuSpecification).not.toHaveBeenCalled();
614
+ expect(buildWriteSuccessResponse).toHaveBeenCalledWith({
615
+ profileId: "profile-1",
616
+ operation: "associate_specification",
617
+ resourceId: "prod-1",
618
+ riskLevel: "low",
619
+ message: "Specification associated successfully.",
620
+ before: null,
621
+ after: association,
622
+ details: { entity_type: "product" }
623
+ });
624
+ expect(result).toEqual({
625
+ kind: "success",
626
+ params: expect.objectContaining({
627
+ operation: "associate_specification"
628
+ })
629
+ });
630
+ });
631
+ it("routes sku associations to the sku service and keeps free-text payloads", async () => {
632
+ const association = { success: true, id: "sku-1" };
633
+ vi.mocked(associateSkuSpecification).mockResolvedValue(
634
+ association
635
+ );
636
+ await associateSpecificationHandler({
637
+ profileId: "custom",
638
+ entityType: "sku",
639
+ resourceId: "sku-1",
640
+ fieldId: 30,
641
+ text: "Blue"
642
+ });
643
+ expect(associateSkuSpecification).toHaveBeenCalledWith(
644
+ "profile-1",
645
+ "sku-1",
646
+ {
647
+ FieldId: 30,
648
+ Text: "Blue"
649
+ }
650
+ );
651
+ expect(associateProductSpecification).not.toHaveBeenCalled();
652
+ });
653
+ it("keeps both FieldValueId and Text when both are provided", async () => {
654
+ const association = { success: true, id: "sku-2" };
655
+ vi.mocked(associateSkuSpecification).mockResolvedValue(
656
+ association
657
+ );
658
+ await associateSpecificationHandler({
659
+ profileId: "custom",
660
+ entityType: "sku",
661
+ resourceId: "sku-2",
662
+ fieldId: 31,
663
+ fieldValueId: 41,
664
+ text: "Navy"
665
+ });
666
+ expect(associateSkuSpecification).toHaveBeenCalledWith(
667
+ "profile-1",
668
+ "sku-2",
669
+ {
670
+ FieldId: 31,
671
+ FieldValueId: 41,
672
+ Text: "Navy"
673
+ }
674
+ );
675
+ });
676
+ it("returns profile selection payload when profile resolution fails", async () => {
677
+ vi.mocked(resolveVtexWriteProfile).mockResolvedValue({
678
+ ok: false,
679
+ response: { selection: true }
680
+ });
681
+ const result = await associateSpecificationHandler({
682
+ entityType: "product",
683
+ resourceId: "prod-1",
684
+ fieldId: 30
685
+ });
686
+ expect(result).toEqual({ selection: true });
687
+ expect(associateProductSpecification).not.toHaveBeenCalled();
688
+ expect(associateSkuSpecification).not.toHaveBeenCalled();
689
+ });
690
+ it("maps service errors through handleVtexWriteError", async () => {
691
+ vi.mocked(associateProductSpecification).mockRejectedValue(
692
+ new Error("boom")
693
+ );
694
+ const result = await associateSpecificationHandler({
695
+ profileId: "custom",
696
+ entityType: "product",
697
+ resourceId: "prod-1",
698
+ fieldId: 30
699
+ });
700
+ expect(handleVtexWriteError).toHaveBeenCalledWith(
701
+ expect.any(Error),
702
+ "Failed to associate specification to product prod-1"
703
+ );
704
+ expect(result).toEqual({
705
+ kind: "error",
706
+ fallback: "Failed to associate specification to product prod-1",
707
+ err: expect.any(Error)
708
+ });
709
+ });
710
+ });
711
+ });
712
+ //# sourceMappingURL=vtex-catalog-write-tools.test.js.map