@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,141 @@
1
+ import { beforeEach, describe, expect, it, vi } from "vitest";
2
+ import { getOrderDocumentsBatch } from "../../src/services/vtex/vtex-orders.js";
3
+ import { formatVtexError } from "../../src/services/vtex/vtex-api.js";
4
+ import { orderDetailsHandler } from "../../src/tools/vtex/order-details.js";
5
+ import { resolveVtexProfileOrSelection } from "../../src/tools/vtex/profile-resolution.js";
6
+ import { formatOrderDetails } from "../../src/utils/format-order-details.js";
7
+ vi.mock("mcp-use/server", () => ({
8
+ object: vi.fn((payload) => ({ kind: "object", payload })),
9
+ error: vi.fn((message) => ({ kind: "error", message }))
10
+ }));
11
+ vi.mock("../../src/tools/vtex/profile-resolution.js", () => ({
12
+ resolveVtexProfileOrSelection: vi.fn()
13
+ }));
14
+ vi.mock("../../src/services/vtex/vtex-orders.js", () => ({
15
+ getOrderDocumentsBatch: vi.fn()
16
+ }));
17
+ vi.mock("../../src/utils/format-order-details.js", () => ({
18
+ formatOrderDetails: vi.fn()
19
+ }));
20
+ vi.mock("../../src/services/vtex/vtex-api.js", () => ({
21
+ formatVtexError: vi.fn(
22
+ (err, fallback) => `formatted:${fallback}:${err instanceof Error ? err.message : String(err)}`
23
+ )
24
+ }));
25
+ describe("order-details tool", () => {
26
+ beforeEach(() => {
27
+ vi.clearAllMocks();
28
+ vi.mocked(resolveVtexProfileOrSelection).mockResolvedValue({
29
+ ok: true,
30
+ value: { profileId: "profile-1" }
31
+ });
32
+ });
33
+ it("deduplicates order ids, delegates batch fetch, and returns formatted payloads", async () => {
34
+ vi.mocked(getOrderDocumentsBatch).mockResolvedValue({
35
+ successful: [
36
+ { document: { orderId: "o-1" }, orderId: "o-1" },
37
+ { document: { orderId: "o-2" }, orderId: "o-2" }
38
+ ],
39
+ failed: [
40
+ {
41
+ orderId: "o-3",
42
+ message: "missing",
43
+ statusCode: 404,
44
+ attempts: 1,
45
+ retryable: false
46
+ }
47
+ ]
48
+ });
49
+ vi.mocked(formatOrderDetails).mockReturnValueOnce({
50
+ id: "o-1",
51
+ status: "ready",
52
+ created: "2026-06-01 10:00",
53
+ total: 100,
54
+ items: [],
55
+ shipping: [],
56
+ payments: []
57
+ }).mockReturnValueOnce({
58
+ id: "o-2",
59
+ status: "invoiced",
60
+ created: "2026-06-01 11:00",
61
+ total: 200,
62
+ items: [],
63
+ shipping: [],
64
+ payments: []
65
+ });
66
+ const result = await orderDetailsHandler({
67
+ profileId: "custom",
68
+ orderIds: ["o-1", "o-1", "o-2", "o-3"],
69
+ reason: "manual-review"
70
+ });
71
+ expect(resolveVtexProfileOrSelection).toHaveBeenCalledWith("custom");
72
+ expect(getOrderDocumentsBatch).toHaveBeenCalledWith(
73
+ "profile-1",
74
+ ["o-1", "o-2", "o-3"],
75
+ {
76
+ reason: "manual-review",
77
+ maxConcurrency: 10,
78
+ maxRetries: 2
79
+ }
80
+ );
81
+ expect(formatOrderDetails).toHaveBeenCalledTimes(2);
82
+ expect(result).toEqual({
83
+ kind: "object",
84
+ payload: {
85
+ metadata: {
86
+ profile_id: "profile-1",
87
+ requested: 3,
88
+ successful: 2,
89
+ failed: 1
90
+ },
91
+ schemas: {
92
+ item: ["id", "ref", "name", "qty", "price", "list"],
93
+ shipping: ["carrier", "estimate", "delivery_date"],
94
+ payment: ["system", "installments", "value"],
95
+ payment_validation: {
96
+ payments_total: "Sum of all payment values",
97
+ payment_delta: "Difference between payments_total and order total",
98
+ payment_mismatch: "True if |payment_delta| > 0.5 (potential issue)"
99
+ }
100
+ },
101
+ orders: [
102
+ {
103
+ id: "o-1",
104
+ status: "ready",
105
+ created: "2026-06-01 10:00",
106
+ total: 100
107
+ },
108
+ {
109
+ id: "o-2",
110
+ status: "invoiced",
111
+ created: "2026-06-01 11:00",
112
+ total: 200
113
+ }
114
+ ],
115
+ failures: [{ order_id: "o-3", message: "missing", status_code: 404 }]
116
+ }
117
+ });
118
+ });
119
+ it("returns profile selection payload unchanged when resolution fails", async () => {
120
+ vi.mocked(resolveVtexProfileOrSelection).mockResolvedValue({
121
+ ok: false,
122
+ response: { selection: true }
123
+ });
124
+ const result = await orderDetailsHandler({ orderIds: ["o-1"] });
125
+ expect(result).toEqual({ selection: true });
126
+ expect(getOrderDocumentsBatch).not.toHaveBeenCalled();
127
+ });
128
+ it("formats batch failures through formatVtexError", async () => {
129
+ vi.mocked(getOrderDocumentsBatch).mockRejectedValue(new Error("boom"));
130
+ const result = await orderDetailsHandler({
131
+ profileId: "custom",
132
+ orderIds: ["o-1"]
133
+ });
134
+ expect(formatVtexError).toHaveBeenCalled();
135
+ expect(result).toEqual({
136
+ kind: "error",
137
+ message: "formatted:Failed to fetch order details batch:boom"
138
+ });
139
+ });
140
+ });
141
+ //# sourceMappingURL=vtex-order-details.tool.test.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../tests/vtex/vtex-order-details.tool.test.ts"],
4
+ "sourcesContent": ["import { beforeEach, describe, expect, it, vi } from \"vitest\";\n\nimport { getOrderDocumentsBatch } from \"../../src/services/vtex/vtex-orders.js\";\nimport { formatVtexError } from \"../../src/services/vtex/vtex-api.js\";\nimport { orderDetailsHandler } from \"../../src/tools/vtex/order-details.js\";\nimport { resolveVtexProfileOrSelection } from \"../../src/tools/vtex/profile-resolution.js\";\nimport { formatOrderDetails } from \"../../src/utils/format-order-details.js\";\n\nvi.mock(\"mcp-use/server\", () => ({\n\tobject: vi.fn((payload: unknown) => ({ kind: \"object\", payload })),\n\terror: vi.fn((message: string) => ({ kind: \"error\", message })),\n}));\n\nvi.mock(\"../../src/tools/vtex/profile-resolution.js\", () => ({\n\tresolveVtexProfileOrSelection: vi.fn(),\n}));\n\nvi.mock(\"../../src/services/vtex/vtex-orders.js\", () => ({\n\tgetOrderDocumentsBatch: vi.fn(),\n}));\n\nvi.mock(\"../../src/utils/format-order-details.js\", () => ({\n\tformatOrderDetails: vi.fn(),\n}));\n\nvi.mock(\"../../src/services/vtex/vtex-api.js\", () => ({\n\tformatVtexError: vi.fn(\n\t\t(err: unknown, fallback: string) =>\n\t\t\t`formatted:${fallback}:${err instanceof Error ? err.message : String(err)}`,\n\t),\n}));\n\ndescribe(\"order-details tool\", () => {\n\tbeforeEach(() => {\n\t\tvi.clearAllMocks();\n\t\tvi.mocked(resolveVtexProfileOrSelection).mockResolvedValue({\n\t\t\tok: true,\n\t\t\tvalue: { profileId: \"profile-1\" },\n\t\t});\n\t});\n\n\tit(\"deduplicates order ids, delegates batch fetch, and returns formatted payloads\", async () => {\n\t\tvi.mocked(getOrderDocumentsBatch).mockResolvedValue({\n\t\t\tsuccessful: [\n\t\t\t\t{ document: { orderId: \"o-1\" }, orderId: \"o-1\" },\n\t\t\t\t{ document: { orderId: \"o-2\" }, orderId: \"o-2\" },\n\t\t\t],\n\t\t\tfailed: [\n\t\t\t\t{\n\t\t\t\t\torderId: \"o-3\",\n\t\t\t\t\tmessage: \"missing\",\n\t\t\t\t\tstatusCode: 404,\n\t\t\t\t\tattempts: 1,\n\t\t\t\t\tretryable: false,\n\t\t\t\t},\n\t\t\t],\n\t\t});\n\t\tvi.mocked(formatOrderDetails)\n\t\t\t.mockReturnValueOnce({\n\t\t\t\tid: \"o-1\",\n\t\t\t\tstatus: \"ready\",\n\t\t\t\tcreated: \"2026-06-01 10:00\",\n\t\t\t\ttotal: 100,\n\t\t\t\titems: [],\n\t\t\t\tshipping: [],\n\t\t\t\tpayments: [],\n\t\t\t} as never)\n\t\t\t.mockReturnValueOnce({\n\t\t\t\tid: \"o-2\",\n\t\t\t\tstatus: \"invoiced\",\n\t\t\t\tcreated: \"2026-06-01 11:00\",\n\t\t\t\ttotal: 200,\n\t\t\t\titems: [],\n\t\t\t\tshipping: [],\n\t\t\t\tpayments: [],\n\t\t\t} as never);\n\n\t\tconst result = await orderDetailsHandler({\n\t\t\tprofileId: \"custom\",\n\t\t\torderIds: [\"o-1\", \"o-1\", \"o-2\", \"o-3\"],\n\t\t\treason: \"manual-review\",\n\t\t});\n\n\t\texpect(resolveVtexProfileOrSelection).toHaveBeenCalledWith(\"custom\");\n\t\texpect(getOrderDocumentsBatch).toHaveBeenCalledWith(\n\t\t\t\"profile-1\",\n\t\t\t[\"o-1\", \"o-2\", \"o-3\"],\n\t\t\t{\n\t\t\t\treason: \"manual-review\",\n\t\t\t\tmaxConcurrency: 10,\n\t\t\t\tmaxRetries: 2,\n\t\t\t},\n\t\t);\n\t\texpect(formatOrderDetails).toHaveBeenCalledTimes(2);\n\t\texpect(result).toEqual({\n\t\t\tkind: \"object\",\n\t\t\tpayload: {\n\t\t\t\tmetadata: {\n\t\t\t\t\tprofile_id: \"profile-1\",\n\t\t\t\t\trequested: 3,\n\t\t\t\t\tsuccessful: 2,\n\t\t\t\t\tfailed: 1,\n\t\t\t\t},\n\t\t\t\tschemas: {\n\t\t\t\t\titem: [\"id\", \"ref\", \"name\", \"qty\", \"price\", \"list\"],\n\t\t\t\t\tshipping: [\"carrier\", \"estimate\", \"delivery_date\"],\n\t\t\t\t\tpayment: [\"system\", \"installments\", \"value\"],\n\t\t\t\t\tpayment_validation: {\n\t\t\t\t\t\tpayments_total: \"Sum of all payment values\",\n\t\t\t\t\t\tpayment_delta: \"Difference between payments_total and order total\",\n\t\t\t\t\t\tpayment_mismatch: \"True if |payment_delta| > 0.5 (potential issue)\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\torders: [\n\t\t\t\t\t{\n\t\t\t\t\t\tid: \"o-1\",\n\t\t\t\t\t\tstatus: \"ready\",\n\t\t\t\t\t\tcreated: \"2026-06-01 10:00\",\n\t\t\t\t\t\ttotal: 100,\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tid: \"o-2\",\n\t\t\t\t\t\tstatus: \"invoiced\",\n\t\t\t\t\t\tcreated: \"2026-06-01 11:00\",\n\t\t\t\t\t\ttotal: 200,\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t\tfailures: [{ order_id: \"o-3\", message: \"missing\", status_code: 404 }],\n\t\t\t},\n\t\t});\n\t});\n\n\tit(\"returns profile selection payload unchanged when resolution fails\", async () => {\n\t\tvi.mocked(resolveVtexProfileOrSelection).mockResolvedValue({\n\t\t\tok: false,\n\t\t\tresponse: { selection: true },\n\t\t} as never);\n\n\t\tconst result = await orderDetailsHandler({ orderIds: [\"o-1\"] });\n\n\t\texpect(result).toEqual({ selection: true });\n\t\texpect(getOrderDocumentsBatch).not.toHaveBeenCalled();\n\t});\n\n\tit(\"formats batch failures through formatVtexError\", async () => {\n\t\tvi.mocked(getOrderDocumentsBatch).mockRejectedValue(new Error(\"boom\"));\n\n\t\tconst result = await orderDetailsHandler({\n\t\t\tprofileId: \"custom\",\n\t\t\torderIds: [\"o-1\"],\n\t\t});\n\n\t\texpect(formatVtexError).toHaveBeenCalled();\n\t\texpect(result).toEqual({\n\t\t\tkind: \"error\",\n\t\t\tmessage: \"formatted:Failed to fetch order details batch:boom\",\n\t\t});\n\t});\n});\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,483 @@
1
+ import { beforeEach, describe, expect, it, vi } from "vitest";
2
+ import { z } from "zod";
3
+ import { addOrderTrackingHandler } from "../../src/tools/vtex/add-order-tracking.js";
4
+ import { cancelOrderHandler } from "../../src/tools/vtex/cancel-order.js";
5
+ import { invoiceOrderHandler } from "../../src/tools/vtex/invoice-order.js";
6
+ import {
7
+ cancelOrder,
8
+ getOrderStateSnapshot,
9
+ invoiceOrder,
10
+ updateOrderTrackingStatus,
11
+ updatePartialInvoiceTracking,
12
+ validateOrderForCancellation,
13
+ validateOrderForInvoice,
14
+ validateOrderForTracking
15
+ } from "../../src/services/vtex/vtex-orders-write.js";
16
+ import {
17
+ buildWriteSuccessResponse,
18
+ handleVtexWriteError,
19
+ requireExplicitConfirmation,
20
+ resolveVtexWriteProfile
21
+ } from "../../src/tools/vtex/write-helpers.js";
22
+ vi.mock("../../src/tools/vtex/write-helpers.js", () => ({
23
+ vtexProfileIdSchemaField: z.string().optional(),
24
+ confirmationSchemaField: z.boolean().optional(),
25
+ confirmationNoteSchemaField: z.string().optional(),
26
+ resolveVtexWriteProfile: vi.fn(),
27
+ requireExplicitConfirmation: vi.fn(),
28
+ buildWriteSuccessResponse: vi.fn((params) => ({
29
+ kind: "success",
30
+ params
31
+ })),
32
+ handleVtexWriteError: vi.fn((err, fallback) => ({
33
+ kind: "error",
34
+ fallback,
35
+ err
36
+ }))
37
+ }));
38
+ vi.mock("../../src/services/vtex/vtex-orders-write.js", () => ({
39
+ validateOrderForCancellation: vi.fn(),
40
+ cancelOrder: vi.fn(),
41
+ getOrderStateSnapshot: vi.fn(),
42
+ validateOrderForInvoice: vi.fn(),
43
+ invoiceOrder: vi.fn(),
44
+ validateOrderForTracking: vi.fn(),
45
+ updatePartialInvoiceTracking: vi.fn(),
46
+ updateOrderTrackingStatus: vi.fn()
47
+ }));
48
+ describe("VTEX order write tools", () => {
49
+ beforeEach(() => {
50
+ vi.clearAllMocks();
51
+ vi.mocked(requireExplicitConfirmation).mockReturnValue(null);
52
+ vi.mocked(resolveVtexWriteProfile).mockResolvedValue({
53
+ ok: true,
54
+ value: { profileId: "profile-1" }
55
+ });
56
+ });
57
+ describe("cancelOrderHandler", () => {
58
+ it("returns confirmation-required payload before any write path", async () => {
59
+ vi.mocked(requireExplicitConfirmation).mockReturnValue({
60
+ confirmation: true
61
+ });
62
+ const result = await cancelOrderHandler({
63
+ profileId: "custom",
64
+ orderId: "order-1",
65
+ reason: "customer request"
66
+ });
67
+ expect(requireExplicitConfirmation).toHaveBeenCalledWith(
68
+ void 0,
69
+ "cancel_order",
70
+ {
71
+ order_id: "order-1"
72
+ }
73
+ );
74
+ expect(result).toEqual({ confirmation: true });
75
+ expect(resolveVtexWriteProfile).not.toHaveBeenCalled();
76
+ expect(validateOrderForCancellation).not.toHaveBeenCalled();
77
+ });
78
+ it("returns profile selection payload when profile resolution fails", async () => {
79
+ vi.mocked(resolveVtexWriteProfile).mockResolvedValue({
80
+ ok: false,
81
+ response: { selection: true }
82
+ });
83
+ const result = await cancelOrderHandler({
84
+ profileId: "custom",
85
+ orderId: "order-1",
86
+ confirmed: true
87
+ });
88
+ expect(result).toEqual({ selection: true });
89
+ expect(validateOrderForCancellation).not.toHaveBeenCalled();
90
+ expect(cancelOrder).not.toHaveBeenCalled();
91
+ });
92
+ it("validates, cancels, and re-fetches the order snapshot", async () => {
93
+ const beforeOrder = { orderId: "order-1", status: "handling" };
94
+ const afterOrder = { orderId: "order-1", status: "canceled" };
95
+ vi.mocked(validateOrderForCancellation).mockResolvedValue(
96
+ beforeOrder
97
+ );
98
+ vi.mocked(getOrderStateSnapshot).mockResolvedValue(afterOrder);
99
+ const result = await cancelOrderHandler({
100
+ profileId: "custom",
101
+ orderId: "order-1",
102
+ confirmed: true,
103
+ reason: "customer request",
104
+ confirmationNote: "User confirmed cancellation"
105
+ });
106
+ expect(validateOrderForCancellation).toHaveBeenCalledWith(
107
+ "profile-1",
108
+ "order-1",
109
+ "customer request"
110
+ );
111
+ expect(cancelOrder).toHaveBeenCalledWith(
112
+ "profile-1",
113
+ "order-1",
114
+ "customer request"
115
+ );
116
+ expect(getOrderStateSnapshot).toHaveBeenCalledWith(
117
+ "profile-1",
118
+ "order-1",
119
+ "customer request"
120
+ );
121
+ expect(
122
+ vi.mocked(validateOrderForCancellation).mock.invocationCallOrder[0]
123
+ ).toBeLessThan(vi.mocked(cancelOrder).mock.invocationCallOrder[0]);
124
+ expect(vi.mocked(cancelOrder).mock.invocationCallOrder[0]).toBeLessThan(
125
+ vi.mocked(getOrderStateSnapshot).mock.invocationCallOrder[0]
126
+ );
127
+ expect(buildWriteSuccessResponse).toHaveBeenCalledWith({
128
+ profileId: "profile-1",
129
+ operation: "cancel_order",
130
+ resourceId: "order-1",
131
+ riskLevel: "high",
132
+ confirmed: true,
133
+ confirmationNote: "User confirmed cancellation",
134
+ message: "Order canceled successfully.",
135
+ before: beforeOrder,
136
+ after: afterOrder,
137
+ details: { order_id: "order-1" }
138
+ });
139
+ expect(result).toEqual({
140
+ kind: "success",
141
+ params: expect.objectContaining({ operation: "cancel_order" })
142
+ });
143
+ });
144
+ it("maps service errors through handleVtexWriteError", async () => {
145
+ vi.mocked(validateOrderForCancellation).mockRejectedValue(
146
+ new Error("boom")
147
+ );
148
+ const result = await cancelOrderHandler({
149
+ profileId: "custom",
150
+ orderId: "order-1",
151
+ confirmed: true
152
+ });
153
+ expect(handleVtexWriteError).toHaveBeenCalledWith(
154
+ expect.any(Error),
155
+ "Failed to cancel order order-1"
156
+ );
157
+ expect(result).toEqual({
158
+ kind: "error",
159
+ fallback: "Failed to cancel order order-1",
160
+ err: expect.any(Error)
161
+ });
162
+ });
163
+ });
164
+ describe("invoiceOrderHandler", () => {
165
+ const invoicePayload = {
166
+ type: "Output",
167
+ issuanceDate: "2026-06-01T12:00:00Z",
168
+ invoiceNumber: "INV-123",
169
+ invoiceValue: "15000",
170
+ items: [
171
+ { id: "sku-1", price: 15e3, quantity: 1, description: "Main item" }
172
+ ],
173
+ invoiceKey: "KEY-1",
174
+ invoiceUrl: "https://example.com/invoice.pdf",
175
+ invoiceOrderNumber: "ERP-9",
176
+ courier: "Andreani",
177
+ trackingNumber: "TRK-1",
178
+ trackingUrl: "https://example.com/track/TRK-1"
179
+ };
180
+ it("returns confirmation-required payload before any write path", async () => {
181
+ vi.mocked(requireExplicitConfirmation).mockReturnValue({
182
+ confirmation: true
183
+ });
184
+ const result = await invoiceOrderHandler({
185
+ profileId: "custom",
186
+ orderId: "order-2",
187
+ ...invoicePayload
188
+ });
189
+ expect(requireExplicitConfirmation).toHaveBeenCalledWith(
190
+ void 0,
191
+ "invoice_order",
192
+ {
193
+ order_id: "order-2"
194
+ }
195
+ );
196
+ expect(result).toEqual({ confirmation: true });
197
+ expect(resolveVtexWriteProfile).not.toHaveBeenCalled();
198
+ expect(validateOrderForInvoice).not.toHaveBeenCalled();
199
+ });
200
+ it("returns profile selection payload when profile resolution fails", async () => {
201
+ vi.mocked(resolveVtexWriteProfile).mockResolvedValue({
202
+ ok: false,
203
+ response: { selection: true }
204
+ });
205
+ const result = await invoiceOrderHandler({
206
+ profileId: "custom",
207
+ orderId: "order-2",
208
+ confirmed: true,
209
+ ...invoicePayload
210
+ });
211
+ expect(result).toEqual({ selection: true });
212
+ expect(validateOrderForInvoice).not.toHaveBeenCalled();
213
+ expect(invoiceOrder).not.toHaveBeenCalled();
214
+ });
215
+ it("validates, invoices, and re-fetches the order snapshot", async () => {
216
+ const beforeOrder = { orderId: "order-2", invoiceNumbers: [] };
217
+ const afterOrder = { orderId: "order-2", invoiceNumbers: ["INV-123"] };
218
+ vi.mocked(validateOrderForInvoice).mockResolvedValue(
219
+ beforeOrder
220
+ );
221
+ vi.mocked(getOrderStateSnapshot).mockResolvedValue(afterOrder);
222
+ const result = await invoiceOrderHandler({
223
+ profileId: "custom",
224
+ orderId: "order-2",
225
+ confirmed: true,
226
+ confirmationNote: "User confirmed invoicing",
227
+ ...invoicePayload
228
+ });
229
+ expect(validateOrderForInvoice).toHaveBeenCalledWith(
230
+ "profile-1",
231
+ "order-2"
232
+ );
233
+ expect(invoiceOrder).toHaveBeenCalledWith(
234
+ "profile-1",
235
+ "order-2",
236
+ invoicePayload
237
+ );
238
+ expect(getOrderStateSnapshot).toHaveBeenCalledWith(
239
+ "profile-1",
240
+ "order-2"
241
+ );
242
+ expect(
243
+ vi.mocked(validateOrderForInvoice).mock.invocationCallOrder[0]
244
+ ).toBeLessThan(vi.mocked(invoiceOrder).mock.invocationCallOrder[0]);
245
+ expect(vi.mocked(invoiceOrder).mock.invocationCallOrder[0]).toBeLessThan(
246
+ vi.mocked(getOrderStateSnapshot).mock.invocationCallOrder[0]
247
+ );
248
+ expect(buildWriteSuccessResponse).toHaveBeenCalledWith({
249
+ profileId: "profile-1",
250
+ operation: "invoice_order",
251
+ resourceId: "order-2",
252
+ riskLevel: "high",
253
+ confirmed: true,
254
+ confirmationNote: "User confirmed invoicing",
255
+ message: "Order invoiced successfully.",
256
+ before: beforeOrder,
257
+ after: afterOrder,
258
+ details: {
259
+ order_id: "order-2",
260
+ invoice_number: "INV-123"
261
+ }
262
+ });
263
+ expect(result).toEqual({
264
+ kind: "success",
265
+ params: expect.objectContaining({ operation: "invoice_order" })
266
+ });
267
+ });
268
+ it("maps service errors through handleVtexWriteError", async () => {
269
+ vi.mocked(validateOrderForInvoice).mockRejectedValue(new Error("boom"));
270
+ const result = await invoiceOrderHandler({
271
+ profileId: "custom",
272
+ orderId: "order-2",
273
+ confirmed: true,
274
+ ...invoicePayload
275
+ });
276
+ expect(handleVtexWriteError).toHaveBeenCalledWith(
277
+ expect.any(Error),
278
+ "Failed to invoice order order-2"
279
+ );
280
+ expect(result).toEqual({
281
+ kind: "error",
282
+ fallback: "Failed to invoice order order-2",
283
+ err: expect.any(Error)
284
+ });
285
+ });
286
+ });
287
+ describe("addOrderTrackingHandler", () => {
288
+ const baseTrackingPayload = {
289
+ invoiceNumber: "INV-999",
290
+ trackingNumber: "TRK-999",
291
+ trackingUrl: "https://example.com/track/TRK-999",
292
+ courier: "OCA",
293
+ dispatchedDate: "2026-06-01T14:00:00Z"
294
+ };
295
+ it("returns confirmation-required payload before any write path", async () => {
296
+ vi.mocked(requireExplicitConfirmation).mockReturnValue({
297
+ confirmation: true
298
+ });
299
+ const result = await addOrderTrackingHandler({
300
+ profileId: "custom",
301
+ orderId: "order-3",
302
+ ...baseTrackingPayload
303
+ });
304
+ expect(requireExplicitConfirmation).toHaveBeenCalledWith(
305
+ void 0,
306
+ "add_order_tracking",
307
+ {
308
+ order_id: "order-3",
309
+ invoice_number: "INV-999"
310
+ }
311
+ );
312
+ expect(result).toEqual({ confirmation: true });
313
+ expect(resolveVtexWriteProfile).not.toHaveBeenCalled();
314
+ expect(validateOrderForTracking).not.toHaveBeenCalled();
315
+ });
316
+ it("returns profile selection payload when profile resolution fails", async () => {
317
+ vi.mocked(resolveVtexWriteProfile).mockResolvedValue({
318
+ ok: false,
319
+ response: { selection: true }
320
+ });
321
+ const result = await addOrderTrackingHandler({
322
+ profileId: "custom",
323
+ orderId: "order-3",
324
+ confirmed: true,
325
+ ...baseTrackingPayload
326
+ });
327
+ expect(result).toEqual({ selection: true });
328
+ expect(validateOrderForTracking).not.toHaveBeenCalled();
329
+ expect(updatePartialInvoiceTracking).not.toHaveBeenCalled();
330
+ });
331
+ it("updates partial tracking and skips status update when no events or delivery state are provided", async () => {
332
+ const beforeOrder = { orderId: "order-3", invoiceNumbers: ["INV-999"] };
333
+ const afterOrder = { orderId: "order-3", invoiceNumbers: ["INV-999"] };
334
+ vi.mocked(validateOrderForTracking).mockResolvedValue(
335
+ beforeOrder
336
+ );
337
+ vi.mocked(getOrderStateSnapshot).mockResolvedValue(afterOrder);
338
+ const result = await addOrderTrackingHandler({
339
+ profileId: "custom",
340
+ orderId: "order-3",
341
+ confirmed: true,
342
+ confirmationNote: "User confirmed tracking update",
343
+ ...baseTrackingPayload
344
+ });
345
+ expect(validateOrderForTracking).toHaveBeenCalledWith(
346
+ "profile-1",
347
+ "order-3",
348
+ "INV-999"
349
+ );
350
+ expect(updatePartialInvoiceTracking).toHaveBeenCalledWith(
351
+ "profile-1",
352
+ "order-3",
353
+ "INV-999",
354
+ {
355
+ trackingNumber: "TRK-999",
356
+ trackingUrl: "https://example.com/track/TRK-999",
357
+ courier: "OCA",
358
+ dispatchedDate: "2026-06-01T14:00:00Z"
359
+ }
360
+ );
361
+ expect(updateOrderTrackingStatus).not.toHaveBeenCalled();
362
+ expect(getOrderStateSnapshot).toHaveBeenCalledWith(
363
+ "profile-1",
364
+ "order-3"
365
+ );
366
+ expect(buildWriteSuccessResponse).toHaveBeenCalledWith({
367
+ profileId: "profile-1",
368
+ operation: "add_order_tracking",
369
+ resourceId: "order-3",
370
+ riskLevel: "high",
371
+ confirmed: true,
372
+ confirmationNote: "User confirmed tracking update",
373
+ message: "Order tracking updated successfully.",
374
+ before: beforeOrder,
375
+ after: afterOrder,
376
+ details: {
377
+ order_id: "order-3",
378
+ invoice_number: "INV-999",
379
+ tracking_number: "TRK-999"
380
+ }
381
+ });
382
+ expect(result).toEqual({
383
+ kind: "success",
384
+ params: expect.objectContaining({ operation: "add_order_tracking" })
385
+ });
386
+ });
387
+ it("updates tracking status when events or delivery state are provided", async () => {
388
+ const beforeOrder = { orderId: "order-3", invoiceNumbers: ["INV-999"] };
389
+ const afterOrder = { orderId: "order-3", invoiceNumbers: ["INV-999"] };
390
+ vi.mocked(validateOrderForTracking).mockResolvedValue(
391
+ beforeOrder
392
+ );
393
+ vi.mocked(getOrderStateSnapshot).mockResolvedValue(afterOrder);
394
+ await addOrderTrackingHandler({
395
+ profileId: "custom",
396
+ orderId: "order-3",
397
+ confirmed: true,
398
+ ...baseTrackingPayload,
399
+ isDelivered: true,
400
+ deliveredDate: "2026-06-02 10:30",
401
+ events: [
402
+ {
403
+ city: "CABA",
404
+ state: "AR-C",
405
+ description: "Delivered",
406
+ date: "2026-06-02 10:30"
407
+ }
408
+ ]
409
+ });
410
+ expect(updateOrderTrackingStatus).toHaveBeenCalledWith(
411
+ "profile-1",
412
+ "order-3",
413
+ "INV-999",
414
+ {
415
+ isDelivered: true,
416
+ deliveredDate: "2026-06-02 10:30",
417
+ events: [
418
+ {
419
+ city: "CABA",
420
+ state: "AR-C",
421
+ description: "Delivered",
422
+ date: "2026-06-02 10:30"
423
+ }
424
+ ]
425
+ }
426
+ );
427
+ expect(
428
+ vi.mocked(updatePartialInvoiceTracking).mock.invocationCallOrder[0]
429
+ ).toBeLessThan(
430
+ vi.mocked(updateOrderTrackingStatus).mock.invocationCallOrder[0]
431
+ );
432
+ });
433
+ it("updates tracking status even when isDelivered is explicitly false", async () => {
434
+ const beforeOrder = { orderId: "order-3", invoiceNumbers: ["INV-999"] };
435
+ const afterOrder = { orderId: "order-3", invoiceNumbers: ["INV-999"] };
436
+ vi.mocked(validateOrderForTracking).mockResolvedValue(
437
+ beforeOrder
438
+ );
439
+ vi.mocked(getOrderStateSnapshot).mockResolvedValue(afterOrder);
440
+ await addOrderTrackingHandler({
441
+ profileId: "custom",
442
+ orderId: "order-3",
443
+ confirmed: true,
444
+ ...baseTrackingPayload,
445
+ isDelivered: false
446
+ });
447
+ expect(updateOrderTrackingStatus).toHaveBeenCalledWith(
448
+ "profile-1",
449
+ "order-3",
450
+ "INV-999",
451
+ {
452
+ isDelivered: false,
453
+ deliveredDate: null,
454
+ events: []
455
+ }
456
+ );
457
+ expect(
458
+ vi.mocked(updatePartialInvoiceTracking).mock.invocationCallOrder[0]
459
+ ).toBeLessThan(
460
+ vi.mocked(updateOrderTrackingStatus).mock.invocationCallOrder[0]
461
+ );
462
+ });
463
+ it("maps service errors through handleVtexWriteError", async () => {
464
+ vi.mocked(validateOrderForTracking).mockRejectedValue(new Error("boom"));
465
+ const result = await addOrderTrackingHandler({
466
+ profileId: "custom",
467
+ orderId: "order-3",
468
+ confirmed: true,
469
+ ...baseTrackingPayload
470
+ });
471
+ expect(handleVtexWriteError).toHaveBeenCalledWith(
472
+ expect.any(Error),
473
+ "Failed to update tracking for order order-3"
474
+ );
475
+ expect(result).toEqual({
476
+ kind: "error",
477
+ fallback: "Failed to update tracking for order order-3",
478
+ err: expect.any(Error)
479
+ });
480
+ });
481
+ });
482
+ });
483
+ //# sourceMappingURL=vtex-order-write-tools.test.js.map