@cimplify/sdk 0.44.14 → 0.44.16

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 (75) hide show
  1. package/dist/advanced.d.mts +1 -1
  2. package/dist/advanced.d.ts +1 -1
  3. package/dist/advanced.js +21 -21
  4. package/dist/advanced.mjs +2 -2
  5. package/dist/{chunk-6TVQQYQ5.mjs → chunk-6AZZ6AOF.mjs} +21 -1
  6. package/dist/{chunk-EZK2FXTM.mjs → chunk-7NML226B.mjs} +1 -1
  7. package/dist/{chunk-BAWKQRIB.js → chunk-CFD4J32N.js} +21 -21
  8. package/dist/{chunk-OMAP327U.js → chunk-MBGQP6FZ.js} +62 -61
  9. package/dist/{chunk-V2O5AO7I.js → chunk-MNIKXG7W.js} +26 -0
  10. package/dist/{chunk-3WD6U5RD.mjs → chunk-UXLQ7WXI.mjs} +4 -3
  11. package/dist/chunk-XL4CX75J.js +9370 -0
  12. package/dist/chunk-ZLEFMQC6.mjs +9318 -0
  13. package/dist/cli/commands/deploy.mjs +9 -1
  14. package/dist/{client-lvLvB1_K.d.ts → client-D6ZisKco.d.ts} +7 -0
  15. package/dist/{client-CkGlXZRd.d.mts → client-Dh-bHyhU.d.mts} +7 -0
  16. package/dist/index.d.mts +1 -1
  17. package/dist/index.d.ts +1 -1
  18. package/dist/index.js +106 -106
  19. package/dist/index.mjs +3 -3
  20. package/dist/mock/cli.mjs +2 -1
  21. package/dist/mock/library.js +2 -1
  22. package/dist/mock/library.mjs +2 -1
  23. package/dist/mock/msw.js +2 -1
  24. package/dist/mock/msw.mjs +2 -1
  25. package/dist/react.d.mts +1 -1
  26. package/dist/react.d.ts +1 -1
  27. package/dist/react.js +80 -80
  28. package/dist/react.mjs +3 -3
  29. package/dist/server-CWpUlLOQ.d.mts +1003 -0
  30. package/dist/server-CWpUlLOQ.d.ts +1003 -0
  31. package/dist/server.d.mts +2 -2
  32. package/dist/server.d.ts +2 -2
  33. package/dist/server.js +5 -5
  34. package/dist/server.mjs +3 -3
  35. package/dist/testing/suite.d.mts +60 -0
  36. package/dist/testing/suite.d.ts +60 -0
  37. package/dist/testing/suite.js +169 -0
  38. package/dist/testing/suite.mjs +165 -0
  39. package/dist/testing.d.mts +1359 -0
  40. package/dist/testing.d.ts +1359 -0
  41. package/dist/testing.js +213 -0
  42. package/dist/testing.mjs +4 -0
  43. package/dist/utils.js +28 -28
  44. package/dist/utils.mjs +1 -1
  45. package/package.json +18 -3
  46. package/templates/storefront-bakery/__tests__/brand.test.ts +4 -0
  47. package/templates/storefront-bakery/__tests__/cart-flow.test.ts +4 -0
  48. package/templates/storefront-bakery/__tests__/contract.test.ts +4 -0
  49. package/templates/storefront-bakery/package.json +11 -3
  50. package/templates/storefront-bakery/vitest.config.ts +9 -0
  51. package/templates/storefront-fashion/__tests__/brand.test.ts +4 -0
  52. package/templates/storefront-fashion/__tests__/cart-flow.test.ts +4 -0
  53. package/templates/storefront-fashion/__tests__/contract.test.ts +4 -0
  54. package/templates/storefront-fashion/package.json +11 -3
  55. package/templates/storefront-fashion/vitest.config.ts +9 -0
  56. package/templates/storefront-grocery/__tests__/brand.test.ts +4 -0
  57. package/templates/storefront-grocery/__tests__/cart-flow.test.ts +4 -0
  58. package/templates/storefront-grocery/__tests__/contract.test.ts +4 -0
  59. package/templates/storefront-grocery/package.json +11 -3
  60. package/templates/storefront-grocery/vitest.config.ts +9 -0
  61. package/templates/storefront-restaurant/__tests__/brand.test.ts +4 -0
  62. package/templates/storefront-restaurant/__tests__/cart-flow.test.ts +4 -0
  63. package/templates/storefront-restaurant/__tests__/contract.test.ts +4 -0
  64. package/templates/storefront-restaurant/package.json +11 -3
  65. package/templates/storefront-restaurant/vitest.config.ts +9 -0
  66. package/templates/storefront-retail/__tests__/brand.test.ts +4 -0
  67. package/templates/storefront-retail/__tests__/cart-flow.test.ts +4 -0
  68. package/templates/storefront-retail/__tests__/contract.test.ts +4 -0
  69. package/templates/storefront-retail/package.json +11 -3
  70. package/templates/storefront-retail/vitest.config.ts +9 -0
  71. package/templates/storefront-services/__tests__/brand.test.ts +4 -0
  72. package/templates/storefront-services/__tests__/cart-flow.test.ts +4 -0
  73. package/templates/storefront-services/__tests__/contract.test.ts +4 -0
  74. package/templates/storefront-services/package.json +11 -3
  75. package/templates/storefront-services/vitest.config.ts +9 -0
package/dist/server.d.mts CHANGED
@@ -1,5 +1,5 @@
1
- import { C as CimplifyClient } from './client-CkGlXZRd.mjs';
2
- export { aN as Result } from './client-CkGlXZRd.mjs';
1
+ import { C as CimplifyClient } from './client-Dh-bHyhU.mjs';
2
+ export { aN as Result } from './client-Dh-bHyhU.mjs';
3
3
  export { am as Category, h as CimplifyError, ao as Collection, U as Product, a7 as ProductWithDetails } from './payment-DA1i5hhk.mjs';
4
4
 
5
5
  interface ServerClientOptions {
package/dist/server.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { C as CimplifyClient } from './client-lvLvB1_K.js';
2
- export { aN as Result } from './client-lvLvB1_K.js';
1
+ import { C as CimplifyClient } from './client-D6ZisKco.js';
2
+ export { aN as Result } from './client-D6ZisKco.js';
3
3
  export { am as Category, h as CimplifyError, ao as Collection, U as Product, a7 as ProductWithDetails } from './payment-DA1i5hhk.js';
4
4
 
5
5
  interface ServerClientOptions {
package/dist/server.js CHANGED
@@ -1,8 +1,8 @@
1
1
  'use strict';
2
2
 
3
- var chunkOMAP327U_js = require('./chunk-OMAP327U.js');
4
- require('./chunk-BAWKQRIB.js');
5
- var chunkV2O5AO7I_js = require('./chunk-V2O5AO7I.js');
3
+ var chunkMBGQP6FZ_js = require('./chunk-MBGQP6FZ.js');
4
+ require('./chunk-CFD4J32N.js');
5
+ var chunkMNIKXG7W_js = require('./chunk-MNIKXG7W.js');
6
6
  var react = require('react');
7
7
 
8
8
  var DEFAULT_DEV_URL = "http://127.0.0.1:8787";
@@ -18,7 +18,7 @@ function readEnv(...keys) {
18
18
  var getServerClient = react.cache((opts = {}) => {
19
19
  const baseUrl = opts.apiUrl ?? readEnv("CIMPLIFY_API_URL", "NEXT_PUBLIC_CIMPLIFY_API_URL") ?? DEFAULT_DEV_URL;
20
20
  const publicKey = opts.secretKey ?? readEnv("CIMPLIFY_SECRET_KEY", "NEXT_PUBLIC_CIMPLIFY_PUBLIC_KEY") ?? "mock-dev";
21
- const client = chunkOMAP327U_js.createCimplifyClient({
21
+ const client = chunkMBGQP6FZ_js.createCimplifyClient({
22
22
  baseUrl,
23
23
  publicKey,
24
24
  suppressPublicKeyWarning: true
@@ -93,7 +93,7 @@ async function revalidateByTag(tag) {
93
93
 
94
94
  Object.defineProperty(exports, "CimplifyError", {
95
95
  enumerable: true,
96
- get: function () { return chunkV2O5AO7I_js.CimplifyError; }
96
+ get: function () { return chunkMNIKXG7W_js.CimplifyError; }
97
97
  });
98
98
  exports.getServerClient = getServerClient;
99
99
  exports.revalidateBusiness = revalidateBusiness;
package/dist/server.mjs CHANGED
@@ -1,6 +1,6 @@
1
- import { createCimplifyClient } from './chunk-3WD6U5RD.mjs';
2
- import './chunk-EZK2FXTM.mjs';
3
- export { CimplifyError } from './chunk-6TVQQYQ5.mjs';
1
+ import { createCimplifyClient } from './chunk-UXLQ7WXI.mjs';
2
+ import './chunk-7NML226B.mjs';
3
+ export { CimplifyError } from './chunk-6AZZ6AOF.mjs';
4
4
  import { cache } from 'react';
5
5
 
6
6
  var DEFAULT_DEV_URL = "http://127.0.0.1:8787";
@@ -0,0 +1,60 @@
1
+ import { C as CreateAppOptions } from '../server-CWpUlLOQ.mjs';
2
+ import 'hono';
3
+
4
+ /**
5
+ * Pre-baked test suites for scaffolded storefronts.
6
+ *
7
+ * Templates' `__tests__/*.test.ts` files are now ~5 lines each — they
8
+ * import their `brand` and call `createBrandSuite` / `createCartFlowSuite`
9
+ * / `createContractSuite`. One bug fix in this file → every template
10
+ * picks it up on `bun update @cimplify/sdk`.
11
+ *
12
+ * // __tests__/brand.test.ts in any template:
13
+ * import { createBrandSuite } from "@cimplify/sdk/testing/suite";
14
+ * import { brand } from "../lib/brand";
15
+ * createBrandSuite({ brand });
16
+ *
17
+ * // __tests__/cart-flow.test.ts:
18
+ * import { createCartFlowSuite } from "@cimplify/sdk/testing/suite";
19
+ * import { brand } from "../lib/brand";
20
+ * createCartFlowSuite({ seed: brand.mock.seed, businessId: brand.mock.businessId });
21
+ *
22
+ * // __tests__/contract.test.ts:
23
+ * import { createContractSuite } from "@cimplify/sdk/testing/suite";
24
+ * import { brand } from "../lib/brand";
25
+ * createContractSuite({ seed: brand.mock.seed });
26
+ */
27
+
28
+ /**
29
+ * Brand sanity suite. Catches: missing required fields, placeholder
30
+ * strings shipped to prod, invalid email/url/locale/currency formats,
31
+ * mismatched seed/businessId pairing.
32
+ */
33
+ declare function createBrandSuite(opts: {
34
+ brand: unknown;
35
+ label?: string;
36
+ }): void;
37
+ interface CartFlowOptions extends CreateAppOptions {
38
+ /** Expected business ID — asserts cart responses match the merchant. */
39
+ businessId?: string;
40
+ /** Override the describe() label. */
41
+ label?: string;
42
+ }
43
+ /**
44
+ * Cart flow suite. Boots the in-process mock, exercises add/dedupe/remove
45
+ * with shape assertions on every response. Catches: SDK ↔ mock contract
46
+ * drift, missing fields on cart items, add-to-cart not persisting,
47
+ * variant/payload silently dropped.
48
+ */
49
+ declare function createCartFlowSuite(opts?: CartFlowOptions): void;
50
+ interface ContractOptions extends CreateAppOptions {
51
+ label?: string;
52
+ }
53
+ /**
54
+ * Shape contract suite. Exercises the schemas against actual mock
55
+ * traffic. Catches: outbound payloads dropping required fields,
56
+ * inbound responses missing fields the storefront depends on.
57
+ */
58
+ declare function createContractSuite(opts?: ContractOptions): void;
59
+
60
+ export { createBrandSuite, createCartFlowSuite, createContractSuite };
@@ -0,0 +1,60 @@
1
+ import { C as CreateAppOptions } from '../server-CWpUlLOQ.js';
2
+ import 'hono';
3
+
4
+ /**
5
+ * Pre-baked test suites for scaffolded storefronts.
6
+ *
7
+ * Templates' `__tests__/*.test.ts` files are now ~5 lines each — they
8
+ * import their `brand` and call `createBrandSuite` / `createCartFlowSuite`
9
+ * / `createContractSuite`. One bug fix in this file → every template
10
+ * picks it up on `bun update @cimplify/sdk`.
11
+ *
12
+ * // __tests__/brand.test.ts in any template:
13
+ * import { createBrandSuite } from "@cimplify/sdk/testing/suite";
14
+ * import { brand } from "../lib/brand";
15
+ * createBrandSuite({ brand });
16
+ *
17
+ * // __tests__/cart-flow.test.ts:
18
+ * import { createCartFlowSuite } from "@cimplify/sdk/testing/suite";
19
+ * import { brand } from "../lib/brand";
20
+ * createCartFlowSuite({ seed: brand.mock.seed, businessId: brand.mock.businessId });
21
+ *
22
+ * // __tests__/contract.test.ts:
23
+ * import { createContractSuite } from "@cimplify/sdk/testing/suite";
24
+ * import { brand } from "../lib/brand";
25
+ * createContractSuite({ seed: brand.mock.seed });
26
+ */
27
+
28
+ /**
29
+ * Brand sanity suite. Catches: missing required fields, placeholder
30
+ * strings shipped to prod, invalid email/url/locale/currency formats,
31
+ * mismatched seed/businessId pairing.
32
+ */
33
+ declare function createBrandSuite(opts: {
34
+ brand: unknown;
35
+ label?: string;
36
+ }): void;
37
+ interface CartFlowOptions extends CreateAppOptions {
38
+ /** Expected business ID — asserts cart responses match the merchant. */
39
+ businessId?: string;
40
+ /** Override the describe() label. */
41
+ label?: string;
42
+ }
43
+ /**
44
+ * Cart flow suite. Boots the in-process mock, exercises add/dedupe/remove
45
+ * with shape assertions on every response. Catches: SDK ↔ mock contract
46
+ * drift, missing fields on cart items, add-to-cart not persisting,
47
+ * variant/payload silently dropped.
48
+ */
49
+ declare function createCartFlowSuite(opts?: CartFlowOptions): void;
50
+ interface ContractOptions extends CreateAppOptions {
51
+ label?: string;
52
+ }
53
+ /**
54
+ * Shape contract suite. Exercises the schemas against actual mock
55
+ * traffic. Catches: outbound payloads dropping required fields,
56
+ * inbound responses missing fields the storefront depends on.
57
+ */
58
+ declare function createContractSuite(opts?: ContractOptions): void;
59
+
60
+ export { createBrandSuite, createCartFlowSuite, createContractSuite };
@@ -0,0 +1,169 @@
1
+ 'use strict';
2
+
3
+ var chunkXL4CX75J_js = require('../chunk-XL4CX75J.js');
4
+ require('../chunk-MBGQP6FZ.js');
5
+ require('../chunk-CFD4J32N.js');
6
+ require('../chunk-MNIKXG7W.js');
7
+ var vitest = require('vitest');
8
+
9
+ var PLACEHOLDER_PHRASES = [
10
+ "lorem ipsum",
11
+ "todo",
12
+ "fixme",
13
+ "placeholder",
14
+ "your business name",
15
+ "your store"
16
+ ];
17
+ function createBrandSuite(opts) {
18
+ vitest.describe(opts.label ?? "brand schema", () => {
19
+ vitest.it("conforms to the Cimplify brand contract", () => {
20
+ vitest.expect(() => chunkXL4CX75J_js.assertBrand(opts.brand)).not.toThrow();
21
+ });
22
+ vitest.it("declares a known mock seed", () => {
23
+ const b = opts.brand;
24
+ vitest.expect(chunkXL4CX75J_js.SeedNameSchema.safeParse(b.mock?.seed).success).toBe(true);
25
+ });
26
+ vitest.it("has no placeholder copy left in", () => {
27
+ const flat = JSON.stringify(opts.brand).toLowerCase();
28
+ for (const phrase of PLACEHOLDER_PHRASES) {
29
+ vitest.expect(flat, `brand contains placeholder phrase "${phrase}"`).not.toContain(phrase);
30
+ }
31
+ });
32
+ vitest.it("uses the bus_ businessId convention", () => {
33
+ const b = opts.brand;
34
+ vitest.expect(b.mock?.businessId).toMatch(/^bus_/);
35
+ });
36
+ vitest.it("zod-parses cleanly", () => {
37
+ const result = chunkXL4CX75J_js.BrandSchema.safeParse(opts.brand);
38
+ if (!result.success) {
39
+ throw new Error(
40
+ "Brand schema violations:\n" + result.error.issues.map((i) => ` \u2022 brand.${i.path.join(".")}: ${i.message}`).join("\n")
41
+ );
42
+ }
43
+ vitest.expect(result.success).toBe(true);
44
+ });
45
+ });
46
+ }
47
+ function createCartFlowSuite(opts = {}) {
48
+ vitest.describe(opts.label ?? "cart flow against in-process mock", () => {
49
+ let h;
50
+ vitest.beforeEach(() => {
51
+ h = chunkXL4CX75J_js.createTestClient(opts);
52
+ });
53
+ vitest.afterEach(() => h.dispose());
54
+ vitest.it("starts with an empty cart matching the canonical shape", async () => {
55
+ const res = await h.client.cart.get();
56
+ vitest.expect(res.ok).toBe(true);
57
+ if (!res.ok) return;
58
+ vitest.expect(() => chunkXL4CX75J_js.assertCart(res.value)).not.toThrow();
59
+ vitest.expect(res.value.items).toHaveLength(0);
60
+ });
61
+ vitest.it("adds the first product, persists it, and returns a shape-valid cart", async () => {
62
+ const { product, variantId } = await chunkXL4CX75J_js.fixtures.addFirstProduct(h.client);
63
+ vitest.expect(product.id).toBeTruthy();
64
+ const get = await h.client.cart.get();
65
+ vitest.expect(get.ok).toBe(true);
66
+ if (!get.ok) return;
67
+ vitest.expect(() => chunkXL4CX75J_js.assertCart(get.value)).not.toThrow();
68
+ vitest.expect(get.value.items).toHaveLength(1);
69
+ vitest.expect(get.value.items[0]?.item_id).toBe(product.id);
70
+ if (variantId) {
71
+ vitest.expect(get.value.items[0]?.variant_id).toBe(variantId);
72
+ const variantInfo = get.value.items[0]?.variant_info;
73
+ vitest.expect(variantInfo?.display_attributes).toBeDefined();
74
+ }
75
+ vitest.expect(parseFloat(String(get.value.pricing.subtotal))).toBeGreaterThan(0);
76
+ });
77
+ vitest.it("dedupes by line_key when adding the same product twice", async () => {
78
+ await chunkXL4CX75J_js.fixtures.addFirstProduct(h.client);
79
+ await chunkXL4CX75J_js.fixtures.addFirstProduct(h.client);
80
+ const res = await h.client.cart.get();
81
+ vitest.expect(res.ok).toBe(true);
82
+ if (!res.ok) return;
83
+ vitest.expect(res.value.items).toHaveLength(1);
84
+ vitest.expect(res.value.items[0]?.quantity).toBe(2);
85
+ });
86
+ vitest.it("removes items and zeroes the subtotal", async () => {
87
+ await chunkXL4CX75J_js.fixtures.addFirstProduct(h.client);
88
+ const before = await h.client.cart.get();
89
+ if (!before.ok) throw before.error;
90
+ const itemId = before.value.items[0]?.id;
91
+ vitest.expect(itemId).toBeTruthy();
92
+ const removed = await h.client.cart.removeItem(itemId);
93
+ vitest.expect(removed.ok).toBe(true);
94
+ const after = await h.client.cart.get();
95
+ if (!after.ok) throw after.error;
96
+ vitest.expect(after.value.items).toHaveLength(0);
97
+ vitest.expect(parseFloat(String(after.value.pricing.subtotal))).toBe(0);
98
+ });
99
+ if (opts.businessId) {
100
+ vitest.it("returns the brand's businessId on cart responses", async () => {
101
+ const res = await h.client.cart.get();
102
+ if (!res.ok) throw res.error;
103
+ vitest.expect(res.value.business_id).toBe(opts.businessId);
104
+ });
105
+ }
106
+ });
107
+ }
108
+ function createContractSuite(opts = {}) {
109
+ vitest.describe(opts.label ?? "SDK \u2194 mock contract", () => {
110
+ let h;
111
+ vitest.beforeEach(() => {
112
+ h = chunkXL4CX75J_js.createTestClient(opts);
113
+ });
114
+ vitest.afterEach(() => h.dispose());
115
+ vitest.it("AddItemPayload schema accepts a minimal valid body", () => {
116
+ const result = chunkXL4CX75J_js.AddItemPayloadSchema.safeParse({ item_id: "prod_x", quantity: 1 });
117
+ vitest.expect(result.success).toBe(true);
118
+ });
119
+ vitest.it("AddItemPayload schema rejects negative quantity", () => {
120
+ const result = chunkXL4CX75J_js.AddItemPayloadSchema.safeParse({ item_id: "prod_x", quantity: -1 });
121
+ vitest.expect(result.success).toBe(false);
122
+ });
123
+ vitest.it("AddItemPayload schema rejects empty item_id", () => {
124
+ const result = chunkXL4CX75J_js.AddItemPayloadSchema.safeParse({ item_id: "", quantity: 1 });
125
+ vitest.expect(result.success).toBe(false);
126
+ });
127
+ vitest.it("Cart line items returned by the mock match CartItemSchema", async () => {
128
+ const { product } = await chunkXL4CX75J_js.fixtures.addFirstProduct(h.client);
129
+ const get = await h.client.cart.get();
130
+ if (!get.ok) throw get.error;
131
+ vitest.expect(get.value.items.length).toBeGreaterThan(0);
132
+ for (const item of get.value.items) {
133
+ const result = chunkXL4CX75J_js.CartItemSchema.safeParse(item);
134
+ if (!result.success) {
135
+ const issues = result.error.issues.map((i) => ` \u2022 ${i.path.join(".")}: ${i.message}`).join("\n");
136
+ throw new Error(`Cart item shape mismatch for ${product.name}:
137
+ ${issues}`);
138
+ }
139
+ vitest.expect(result.success).toBe(true);
140
+ }
141
+ });
142
+ vitest.it("CheckoutResponse from the mock includes bill_token", async () => {
143
+ await chunkXL4CX75J_js.fixtures.addFirstProduct(h.client);
144
+ const cartRes = await h.client.cart.get();
145
+ if (!cartRes.ok) throw cartRes.error;
146
+ const checkout = await h.client.checkout.process({
147
+ cart_id: cartRes.value.id,
148
+ customer: chunkXL4CX75J_js.fixtures.customer(),
149
+ order_type: "delivery",
150
+ payment_method: "mobile_money",
151
+ mobile_money_details: { phone_number: "+233244000000", provider: "mtn" }
152
+ });
153
+ vitest.expect(checkout.ok).toBe(true);
154
+ if (!checkout.ok) return;
155
+ const result = chunkXL4CX75J_js.CheckoutResponseSchema.safeParse(checkout.value);
156
+ if (!result.success) {
157
+ const issues = result.error.issues.map((i) => ` \u2022 ${i.path.join(".")}: ${i.message}`).join("\n");
158
+ throw new Error(`CheckoutResponse shape mismatch:
159
+ ${issues}`);
160
+ }
161
+ vitest.expect(checkout.value.bill_token).toBeTruthy();
162
+ vitest.expect(checkout.value.order_id).toBeTruthy();
163
+ });
164
+ });
165
+ }
166
+
167
+ exports.createBrandSuite = createBrandSuite;
168
+ exports.createCartFlowSuite = createCartFlowSuite;
169
+ exports.createContractSuite = createContractSuite;
@@ -0,0 +1,165 @@
1
+ import { assertBrand, SeedNameSchema, BrandSchema, createTestClient, assertCart, fixtures, AddItemPayloadSchema, CartItemSchema, CheckoutResponseSchema } from '../chunk-ZLEFMQC6.mjs';
2
+ import '../chunk-UXLQ7WXI.mjs';
3
+ import '../chunk-7NML226B.mjs';
4
+ import '../chunk-6AZZ6AOF.mjs';
5
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
6
+
7
+ var PLACEHOLDER_PHRASES = [
8
+ "lorem ipsum",
9
+ "todo",
10
+ "fixme",
11
+ "placeholder",
12
+ "your business name",
13
+ "your store"
14
+ ];
15
+ function createBrandSuite(opts) {
16
+ describe(opts.label ?? "brand schema", () => {
17
+ it("conforms to the Cimplify brand contract", () => {
18
+ expect(() => assertBrand(opts.brand)).not.toThrow();
19
+ });
20
+ it("declares a known mock seed", () => {
21
+ const b = opts.brand;
22
+ expect(SeedNameSchema.safeParse(b.mock?.seed).success).toBe(true);
23
+ });
24
+ it("has no placeholder copy left in", () => {
25
+ const flat = JSON.stringify(opts.brand).toLowerCase();
26
+ for (const phrase of PLACEHOLDER_PHRASES) {
27
+ expect(flat, `brand contains placeholder phrase "${phrase}"`).not.toContain(phrase);
28
+ }
29
+ });
30
+ it("uses the bus_ businessId convention", () => {
31
+ const b = opts.brand;
32
+ expect(b.mock?.businessId).toMatch(/^bus_/);
33
+ });
34
+ it("zod-parses cleanly", () => {
35
+ const result = BrandSchema.safeParse(opts.brand);
36
+ if (!result.success) {
37
+ throw new Error(
38
+ "Brand schema violations:\n" + result.error.issues.map((i) => ` \u2022 brand.${i.path.join(".")}: ${i.message}`).join("\n")
39
+ );
40
+ }
41
+ expect(result.success).toBe(true);
42
+ });
43
+ });
44
+ }
45
+ function createCartFlowSuite(opts = {}) {
46
+ describe(opts.label ?? "cart flow against in-process mock", () => {
47
+ let h;
48
+ beforeEach(() => {
49
+ h = createTestClient(opts);
50
+ });
51
+ afterEach(() => h.dispose());
52
+ it("starts with an empty cart matching the canonical shape", async () => {
53
+ const res = await h.client.cart.get();
54
+ expect(res.ok).toBe(true);
55
+ if (!res.ok) return;
56
+ expect(() => assertCart(res.value)).not.toThrow();
57
+ expect(res.value.items).toHaveLength(0);
58
+ });
59
+ it("adds the first product, persists it, and returns a shape-valid cart", async () => {
60
+ const { product, variantId } = await fixtures.addFirstProduct(h.client);
61
+ expect(product.id).toBeTruthy();
62
+ const get = await h.client.cart.get();
63
+ expect(get.ok).toBe(true);
64
+ if (!get.ok) return;
65
+ expect(() => assertCart(get.value)).not.toThrow();
66
+ expect(get.value.items).toHaveLength(1);
67
+ expect(get.value.items[0]?.item_id).toBe(product.id);
68
+ if (variantId) {
69
+ expect(get.value.items[0]?.variant_id).toBe(variantId);
70
+ const variantInfo = get.value.items[0]?.variant_info;
71
+ expect(variantInfo?.display_attributes).toBeDefined();
72
+ }
73
+ expect(parseFloat(String(get.value.pricing.subtotal))).toBeGreaterThan(0);
74
+ });
75
+ it("dedupes by line_key when adding the same product twice", async () => {
76
+ await fixtures.addFirstProduct(h.client);
77
+ await fixtures.addFirstProduct(h.client);
78
+ const res = await h.client.cart.get();
79
+ expect(res.ok).toBe(true);
80
+ if (!res.ok) return;
81
+ expect(res.value.items).toHaveLength(1);
82
+ expect(res.value.items[0]?.quantity).toBe(2);
83
+ });
84
+ it("removes items and zeroes the subtotal", async () => {
85
+ await fixtures.addFirstProduct(h.client);
86
+ const before = await h.client.cart.get();
87
+ if (!before.ok) throw before.error;
88
+ const itemId = before.value.items[0]?.id;
89
+ expect(itemId).toBeTruthy();
90
+ const removed = await h.client.cart.removeItem(itemId);
91
+ expect(removed.ok).toBe(true);
92
+ const after = await h.client.cart.get();
93
+ if (!after.ok) throw after.error;
94
+ expect(after.value.items).toHaveLength(0);
95
+ expect(parseFloat(String(after.value.pricing.subtotal))).toBe(0);
96
+ });
97
+ if (opts.businessId) {
98
+ it("returns the brand's businessId on cart responses", async () => {
99
+ const res = await h.client.cart.get();
100
+ if (!res.ok) throw res.error;
101
+ expect(res.value.business_id).toBe(opts.businessId);
102
+ });
103
+ }
104
+ });
105
+ }
106
+ function createContractSuite(opts = {}) {
107
+ describe(opts.label ?? "SDK \u2194 mock contract", () => {
108
+ let h;
109
+ beforeEach(() => {
110
+ h = createTestClient(opts);
111
+ });
112
+ afterEach(() => h.dispose());
113
+ it("AddItemPayload schema accepts a minimal valid body", () => {
114
+ const result = AddItemPayloadSchema.safeParse({ item_id: "prod_x", quantity: 1 });
115
+ expect(result.success).toBe(true);
116
+ });
117
+ it("AddItemPayload schema rejects negative quantity", () => {
118
+ const result = AddItemPayloadSchema.safeParse({ item_id: "prod_x", quantity: -1 });
119
+ expect(result.success).toBe(false);
120
+ });
121
+ it("AddItemPayload schema rejects empty item_id", () => {
122
+ const result = AddItemPayloadSchema.safeParse({ item_id: "", quantity: 1 });
123
+ expect(result.success).toBe(false);
124
+ });
125
+ it("Cart line items returned by the mock match CartItemSchema", async () => {
126
+ const { product } = await fixtures.addFirstProduct(h.client);
127
+ const get = await h.client.cart.get();
128
+ if (!get.ok) throw get.error;
129
+ expect(get.value.items.length).toBeGreaterThan(0);
130
+ for (const item of get.value.items) {
131
+ const result = CartItemSchema.safeParse(item);
132
+ if (!result.success) {
133
+ const issues = result.error.issues.map((i) => ` \u2022 ${i.path.join(".")}: ${i.message}`).join("\n");
134
+ throw new Error(`Cart item shape mismatch for ${product.name}:
135
+ ${issues}`);
136
+ }
137
+ expect(result.success).toBe(true);
138
+ }
139
+ });
140
+ it("CheckoutResponse from the mock includes bill_token", async () => {
141
+ await fixtures.addFirstProduct(h.client);
142
+ const cartRes = await h.client.cart.get();
143
+ if (!cartRes.ok) throw cartRes.error;
144
+ const checkout = await h.client.checkout.process({
145
+ cart_id: cartRes.value.id,
146
+ customer: fixtures.customer(),
147
+ order_type: "delivery",
148
+ payment_method: "mobile_money",
149
+ mobile_money_details: { phone_number: "+233244000000", provider: "mtn" }
150
+ });
151
+ expect(checkout.ok).toBe(true);
152
+ if (!checkout.ok) return;
153
+ const result = CheckoutResponseSchema.safeParse(checkout.value);
154
+ if (!result.success) {
155
+ const issues = result.error.issues.map((i) => ` \u2022 ${i.path.join(".")}: ${i.message}`).join("\n");
156
+ throw new Error(`CheckoutResponse shape mismatch:
157
+ ${issues}`);
158
+ }
159
+ expect(checkout.value.bill_token).toBeTruthy();
160
+ expect(checkout.value.order_id).toBeTruthy();
161
+ });
162
+ });
163
+ }
164
+
165
+ export { createBrandSuite, createCartFlowSuite, createContractSuite };