@labdigital/commercetools-mock 2.65.1 → 3.0.0-beta.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 (281) hide show
  1. package/README.md +31 -8
  2. package/dist/abstract-BKFcva6S.mjs +1044 -0
  3. package/dist/abstract-BKFcva6S.mjs.map +1 -0
  4. package/dist/config-BcNSzPZz.d.mts +1718 -0
  5. package/dist/index.d.mts +50 -1633
  6. package/dist/index.mjs +3771 -2654
  7. package/dist/index.mjs.map +1 -1
  8. package/dist/storage/sqlite.d.mts +59 -0
  9. package/dist/storage/sqlite.mjs +234 -0
  10. package/dist/storage/sqlite.mjs.map +1 -0
  11. package/package.json +26 -29
  12. package/src/ctMock.ts +125 -136
  13. package/src/helpers.ts +14 -6
  14. package/src/index.ts +5 -0
  15. package/src/lib/masking.ts +4 -5
  16. package/src/lib/product-review-statistics.test.ts +257 -294
  17. package/src/lib/review-statistics.ts +17 -4
  18. package/src/oauth/helpers.ts +7 -4
  19. package/src/oauth/server.test.ts +102 -62
  20. package/src/oauth/server.ts +215 -213
  21. package/src/oauth/store.ts +20 -6
  22. package/src/orderSearch.ts +3 -3
  23. package/src/product-projection-search.ts +38 -20
  24. package/src/product-search-availability.test.ts +31 -52
  25. package/src/product-search.ts +19 -10
  26. package/src/projectAPI.ts +6 -22
  27. package/src/repositories/abstract.ts +182 -48
  28. package/src/repositories/as-associate.test.ts +19 -19
  29. package/src/repositories/associate-role.ts +12 -23
  30. package/src/repositories/attribute-group.test.ts +23 -23
  31. package/src/repositories/attribute-group.ts +6 -4
  32. package/src/repositories/business-unit.test.ts +63 -57
  33. package/src/repositories/business-unit.ts +107 -55
  34. package/src/repositories/cart/actions.ts +96 -65
  35. package/src/repositories/cart/helpers.ts +15 -11
  36. package/src/repositories/cart/index.test.ts +136 -30
  37. package/src/repositories/cart/index.ts +76 -59
  38. package/src/repositories/cart-discount/actions.ts +12 -44
  39. package/src/repositories/cart-discount/index.ts +20 -8
  40. package/src/repositories/category/actions.ts +27 -27
  41. package/src/repositories/category/index.test.ts +13 -9
  42. package/src/repositories/category/index.ts +40 -23
  43. package/src/repositories/channel.test.ts +53 -51
  44. package/src/repositories/channel.ts +12 -22
  45. package/src/repositories/custom-object.ts +34 -25
  46. package/src/repositories/customer/actions.ts +47 -25
  47. package/src/repositories/customer/index.test.ts +11 -11
  48. package/src/repositories/customer/index.ts +65 -35
  49. package/src/repositories/customer-group.test.ts +44 -42
  50. package/src/repositories/customer-group.ts +12 -22
  51. package/src/repositories/discount-code/actions.ts +3 -19
  52. package/src/repositories/discount-code/index.ts +9 -4
  53. package/src/repositories/discount-group/index.ts +8 -3
  54. package/src/repositories/extension.test.ts +27 -27
  55. package/src/repositories/extension.ts +10 -5
  56. package/src/repositories/helpers.ts +126 -47
  57. package/src/repositories/inventory-entry/actions.ts +3 -24
  58. package/src/repositories/inventory-entry/index.ts +19 -11
  59. package/src/repositories/my-customer.ts +13 -12
  60. package/src/repositories/my-order.ts +5 -2
  61. package/src/repositories/order/actions.ts +89 -56
  62. package/src/repositories/order/index.test.ts +36 -31
  63. package/src/repositories/order/index.ts +83 -49
  64. package/src/repositories/order-edit.ts +8 -3
  65. package/src/repositories/payment/actions.ts +64 -44
  66. package/src/repositories/payment/helpers.ts +3 -3
  67. package/src/repositories/payment/index.ts +28 -12
  68. package/src/repositories/product/actions.ts +133 -98
  69. package/src/repositories/product/helpers.ts +29 -16
  70. package/src/repositories/product/index.ts +42 -25
  71. package/src/repositories/product-discount.ts +6 -4
  72. package/src/repositories/product-projection.ts +41 -21
  73. package/src/repositories/product-selection.ts +8 -15
  74. package/src/repositories/product-tailoring.ts +22 -3
  75. package/src/repositories/product-type.ts +45 -4
  76. package/src/repositories/project.ts +57 -13
  77. package/src/repositories/quote/actions.ts +5 -28
  78. package/src/repositories/quote/index.ts +29 -6
  79. package/src/repositories/quote-request/actions.ts +5 -28
  80. package/src/repositories/quote-request/index.test.ts +3 -3
  81. package/src/repositories/quote-request/index.ts +31 -11
  82. package/src/repositories/quote-staged/actions.ts +5 -28
  83. package/src/repositories/quote-staged/index.ts +22 -8
  84. package/src/repositories/recurrence-policy/index.ts +6 -4
  85. package/src/repositories/recurring-order/actions.ts +7 -32
  86. package/src/repositories/recurring-order/index.ts +8 -6
  87. package/src/repositories/review.test.ts +147 -142
  88. package/src/repositories/review.ts +31 -37
  89. package/src/repositories/shipping-method/actions.ts +11 -28
  90. package/src/repositories/shipping-method/index.ts +26 -15
  91. package/src/repositories/shopping-list/actions.ts +21 -31
  92. package/src/repositories/shopping-list/index.ts +44 -22
  93. package/src/repositories/standalone-price.ts +6 -4
  94. package/src/repositories/state.ts +15 -9
  95. package/src/repositories/store.ts +21 -32
  96. package/src/repositories/subscription.test.ts +22 -22
  97. package/src/repositories/subscription.ts +8 -3
  98. package/src/repositories/tax-category/index.ts +8 -3
  99. package/src/repositories/type/actions.ts +21 -3
  100. package/src/repositories/type/index.ts +5 -3
  101. package/src/repositories/zone.test.ts +112 -77
  102. package/src/repositories/zone.ts +5 -3
  103. package/src/schemas/generated/associate-role.ts +13 -0
  104. package/src/schemas/generated/attribute-group.ts +12 -0
  105. package/src/schemas/generated/business-unit.ts +38 -0
  106. package/src/schemas/generated/cart-discount.ts +33 -0
  107. package/src/schemas/generated/cart.ts +61 -0
  108. package/src/schemas/generated/category.ts +25 -0
  109. package/src/schemas/generated/channel.ts +21 -0
  110. package/src/schemas/generated/common.ts +1372 -0
  111. package/src/schemas/generated/custom-object.ts +11 -0
  112. package/src/schemas/generated/customer-group.ts +11 -0
  113. package/src/schemas/generated/customer.ts +47 -0
  114. package/src/schemas/generated/discount-code.ts +25 -0
  115. package/src/schemas/generated/discount-group.ts +13 -0
  116. package/src/schemas/generated/extension.ts +15 -0
  117. package/src/schemas/generated/index.ts +42 -0
  118. package/src/schemas/generated/inventory-entry.ts +20 -0
  119. package/src/schemas/generated/my-quote-request.ts +10 -0
  120. package/src/schemas/generated/order-edit.ts +18 -0
  121. package/src/schemas/generated/order-from-cart.ts +25 -0
  122. package/src/schemas/generated/payment.ts +30 -0
  123. package/src/schemas/generated/product-discount.ts +20 -0
  124. package/src/schemas/generated/product-selection.ts +18 -0
  125. package/src/schemas/generated/product-tailoring.ts +26 -0
  126. package/src/schemas/generated/product-type.ts +12 -0
  127. package/src/schemas/generated/product.ts +37 -0
  128. package/src/schemas/generated/quote-request.ts +19 -0
  129. package/src/schemas/generated/quote.ts +18 -0
  130. package/src/schemas/generated/recurrence-policy.ts +15 -0
  131. package/src/schemas/generated/recurring-order.ts +19 -0
  132. package/src/schemas/generated/review.ts +24 -0
  133. package/src/schemas/generated/shipping-method.ts +24 -0
  134. package/src/schemas/generated/shopping-list.ts +28 -0
  135. package/src/schemas/generated/staged-quote.ts +18 -0
  136. package/src/schemas/generated/standalone-price.ts +32 -0
  137. package/src/schemas/generated/state.ts +20 -0
  138. package/src/schemas/generated/store.ts +23 -0
  139. package/src/schemas/generated/subscription.ts +20 -0
  140. package/src/schemas/generated/tax-category.ts +12 -0
  141. package/src/schemas/generated/type.ts +17 -0
  142. package/src/schemas/generated/zone.ts +12 -0
  143. package/src/schemas/update-request.ts +3 -5
  144. package/src/server.ts +32 -4
  145. package/src/services/abstract.ts +207 -101
  146. package/src/services/as-associate-cart.test.ts +28 -36
  147. package/src/services/as-associate-cart.ts +15 -12
  148. package/src/services/as-associate-order.test.ts +33 -40
  149. package/src/services/as-associate-order.ts +15 -12
  150. package/src/services/as-associate-quote-request.ts +15 -12
  151. package/src/services/as-associate-shopping-list.test.ts +25 -35
  152. package/src/services/as-associate-shopping-list.ts +15 -12
  153. package/src/services/as-associate.test.ts +21 -15
  154. package/src/services/as-associate.ts +23 -22
  155. package/src/services/associate-roles.test.ts +16 -22
  156. package/src/services/associate-roles.ts +2 -2
  157. package/src/services/attribute-group.test.ts +40 -44
  158. package/src/services/attribute-group.ts +2 -2
  159. package/src/services/business-units.test.ts +227 -163
  160. package/src/services/business-units.ts +2 -2
  161. package/src/services/cart-discount.test.ts +253 -187
  162. package/src/services/cart-discount.ts +2 -2
  163. package/src/services/cart.test.ts +833 -832
  164. package/src/services/cart.ts +31 -12
  165. package/src/services/category.test.ts +208 -130
  166. package/src/services/category.ts +2 -2
  167. package/src/services/channel.test.ts +39 -44
  168. package/src/services/channel.ts +2 -2
  169. package/src/services/custom-object.test.ts +103 -79
  170. package/src/services/custom-object.ts +106 -38
  171. package/src/services/customer-group.test.ts +39 -44
  172. package/src/services/customer-group.ts +2 -2
  173. package/src/services/customer.test.ts +357 -292
  174. package/src/services/customer.ts +70 -23
  175. package/src/services/discount-code.test.ts +57 -68
  176. package/src/services/discount-code.ts +2 -2
  177. package/src/services/discount-group.test.ts +111 -134
  178. package/src/services/discount-group.ts +2 -2
  179. package/src/services/draft-validation.test.ts +255 -0
  180. package/src/services/extension.test.ts +39 -44
  181. package/src/services/extension.ts +2 -2
  182. package/src/services/inventory-entry.test.ts +106 -87
  183. package/src/services/inventory-entry.ts +2 -2
  184. package/src/services/my-business-unit.test.ts +82 -112
  185. package/src/services/my-business-unit.ts +25 -19
  186. package/src/services/my-cart.test.ts +46 -41
  187. package/src/services/my-cart.ts +32 -28
  188. package/src/services/my-customer.test.ts +153 -88
  189. package/src/services/my-customer.ts +130 -61
  190. package/src/services/my-order.ts +15 -12
  191. package/src/services/my-payment.test.ts +30 -24
  192. package/src/services/my-payment.ts +2 -2
  193. package/src/services/my-shopping-list.ts +2 -2
  194. package/src/services/order.test.ts +332 -276
  195. package/src/services/order.ts +45 -27
  196. package/src/services/payment.test.ts +31 -29
  197. package/src/services/payment.ts +2 -2
  198. package/src/services/product-discount.test.ts +39 -46
  199. package/src/services/product-discount.ts +2 -2
  200. package/src/services/product-projection.test.ts +176 -166
  201. package/src/services/product-projection.ts +31 -15
  202. package/src/services/product-selection.test.ts +17 -9
  203. package/src/services/product-selection.ts +2 -2
  204. package/src/services/product-type.test.ts +80 -21
  205. package/src/services/product-type.ts +2 -2
  206. package/src/services/product.test.ts +569 -534
  207. package/src/services/product.ts +14 -7
  208. package/src/services/project.test.ts +22 -12
  209. package/src/services/project.ts +28 -13
  210. package/src/services/quote-request.test.ts +36 -39
  211. package/src/services/quote-request.ts +2 -2
  212. package/src/services/quote-staged.ts +2 -2
  213. package/src/services/quote.ts +2 -2
  214. package/src/services/recurrence-policy.test.ts +114 -139
  215. package/src/services/recurrence-policy.ts +2 -2
  216. package/src/services/recurring-order.test.ts +149 -194
  217. package/src/services/recurring-order.ts +2 -2
  218. package/src/services/reviews.test.ts +127 -106
  219. package/src/services/reviews.ts +2 -2
  220. package/src/services/shipping-method.test.ts +96 -125
  221. package/src/services/shipping-method.ts +24 -12
  222. package/src/services/shopping-list.test.ts +183 -141
  223. package/src/services/shopping-list.ts +2 -2
  224. package/src/services/standalone-price.test.ts +60 -46
  225. package/src/services/standalone-price.ts +2 -2
  226. package/src/services/state.test.ts +20 -25
  227. package/src/services/state.ts +2 -2
  228. package/src/services/store.test.ts +26 -45
  229. package/src/services/store.ts +2 -2
  230. package/src/services/subscription.test.ts +39 -44
  231. package/src/services/subscription.ts +2 -2
  232. package/src/services/tax-category.test.ts +33 -36
  233. package/src/services/tax-category.ts +2 -2
  234. package/src/services/type.test.ts +45 -44
  235. package/src/services/type.ts +2 -2
  236. package/src/services/zone.test.ts +40 -44
  237. package/src/services/zone.ts +2 -2
  238. package/src/shipping.ts +41 -11
  239. package/src/storage/abstract.ts +248 -17
  240. package/src/storage/in-memory.ts +147 -290
  241. package/src/storage/sqlite.ts +429 -0
  242. package/src/storage/storage-map.ts +75 -0
  243. package/src/storage/storage.test-helpers.ts +97 -0
  244. package/src/storage/storage.test.ts +802 -0
  245. package/src/testing/associate-role.ts +28 -0
  246. package/src/testing/attribute-group.ts +27 -0
  247. package/src/testing/business-unit.ts +9 -8
  248. package/src/testing/cart-discount.ts +34 -0
  249. package/src/testing/cart.ts +20 -0
  250. package/src/testing/category.ts +25 -0
  251. package/src/testing/channel.ts +23 -0
  252. package/src/testing/custom-object.ts +27 -0
  253. package/src/testing/customer-group.ts +26 -0
  254. package/src/testing/customer.ts +36 -33
  255. package/src/testing/discount-code.ts +29 -0
  256. package/src/testing/discount-group.ts +27 -0
  257. package/src/testing/extension.ts +32 -0
  258. package/src/testing/index.ts +33 -0
  259. package/src/testing/inventory-entry.ts +26 -0
  260. package/src/testing/order.ts +27 -0
  261. package/src/testing/payment.ts +23 -0
  262. package/src/testing/product-discount.ts +33 -0
  263. package/src/testing/product-selection.ts +28 -0
  264. package/src/testing/product-type.ts +27 -0
  265. package/src/testing/product.ts +38 -0
  266. package/src/testing/quote-request.ts +29 -0
  267. package/src/testing/recurrence-policy.ts +33 -0
  268. package/src/testing/recurring-order.ts +32 -0
  269. package/src/testing/review.ts +24 -0
  270. package/src/testing/shipping-method.ts +31 -0
  271. package/src/testing/shopping-list.ts +25 -0
  272. package/src/testing/standalone-price.ts +31 -0
  273. package/src/testing/state.ts +21 -0
  274. package/src/testing/store.ts +26 -0
  275. package/src/testing/subscription.ts +38 -0
  276. package/src/testing/tax-category.ts +27 -0
  277. package/src/testing/type.ts +9 -6
  278. package/src/testing/zone.ts +22 -0
  279. package/src/validate.test.ts +122 -0
  280. package/src/validate.ts +78 -7
  281. package/src/.env +0 -0
@@ -0,0 +1,802 @@
1
+ import type { Category } from "@commercetools/platform-sdk";
2
+ import { afterEach, beforeEach, describe, expect, test } from "vitest";
3
+ import { CommercetoolsError } from "#src/exceptions.ts";
4
+ import type { Writable } from "#src/types.ts";
5
+ import type { AbstractStorage } from "./abstract.ts";
6
+ import {
7
+ createStorage,
8
+ makeCategory,
9
+ makeChannel,
10
+ makeCustomer,
11
+ makeCustomObject,
12
+ makeType,
13
+ storageEngineName,
14
+ } from "./storage.test-helpers.ts";
15
+
16
+ describe(`Storage (${storageEngineName})`, () => {
17
+ let storage: AbstractStorage;
18
+ const projectKey = "test-project";
19
+
20
+ beforeEach(async () => {
21
+ storage = await createStorage();
22
+ });
23
+
24
+ afterEach(() => {
25
+ storage.close();
26
+ });
27
+
28
+ describe("project management", () => {
29
+ test("addProject creates a new project with defaults", async () => {
30
+ const project = await storage.addProject(projectKey);
31
+
32
+ expect(project.key).toBe(projectKey);
33
+ expect(project.version).toBe(1);
34
+ expect(project.countries).toEqual([]);
35
+ expect(project.currencies).toEqual([]);
36
+ expect(project.languages).toEqual([]);
37
+ expect(project.messages.enabled).toBe(false);
38
+ expect(project.carts).toBeDefined();
39
+ expect(project.searchIndexing).toBeDefined();
40
+ });
41
+
42
+ test("addProject returns existing project if already created", async () => {
43
+ const first = await storage.addProject(projectKey);
44
+ const modified = { ...first, name: "Modified" };
45
+ await storage.saveProject(modified);
46
+
47
+ const second = await storage.addProject(projectKey);
48
+ expect(second.name).toBe("Modified");
49
+ });
50
+
51
+ test("getProject returns (or creates) the project", async () => {
52
+ const project = await storage.getProject(projectKey);
53
+ expect(project.key).toBe(projectKey);
54
+ });
55
+
56
+ test("saveProject persists changes", async () => {
57
+ const project = await storage.addProject(projectKey);
58
+ const updated = {
59
+ ...project,
60
+ name: "Updated Name",
61
+ countries: ["US", "DE"],
62
+ };
63
+ await storage.saveProject(updated);
64
+
65
+ const retrieved = await storage.getProject(projectKey);
66
+ expect(retrieved.name).toBe("Updated Name");
67
+ expect(retrieved.countries).toEqual(["US", "DE"]);
68
+ });
69
+
70
+ test("separate project keys are independent", async () => {
71
+ await storage.addProject("project-a");
72
+ await storage.addProject("project-b");
73
+
74
+ const a = await storage.getProject("project-a");
75
+ const b = await storage.getProject("project-b");
76
+
77
+ expect(a.key).toBe("project-a");
78
+ expect(b.key).toBe("project-b");
79
+ });
80
+ });
81
+
82
+ describe("CRUD operations", () => {
83
+ describe("add", () => {
84
+ test("adds a resource and returns a clone", async () => {
85
+ const category = makeCategory();
86
+ const result = await storage.add(projectKey, "category", category);
87
+
88
+ expect(result.id).toBe("cat-1");
89
+ expect(result.name).toEqual({ en: "Test Category" });
90
+
91
+ // Should be a clone, not the same reference
92
+ expect(result).not.toBe(category);
93
+ expect(result).toEqual(category);
94
+ });
95
+
96
+ test("returned clone is independent of stored resource", async () => {
97
+ await storage.add(projectKey, "category", makeCategory());
98
+
99
+ // Mutate the returned value - should not affect stored version
100
+ const returned = await storage.get(projectKey, "category", "cat-1");
101
+ (returned as Writable<Category>).name = { en: "Mutated" };
102
+
103
+ const retrieved = await storage.get(projectKey, "category", "cat-1");
104
+ expect(retrieved?.name).toEqual({ en: "Test Category" });
105
+ });
106
+ });
107
+
108
+ describe("get", () => {
109
+ test("returns resource by id", async () => {
110
+ await storage.add(projectKey, "category", makeCategory());
111
+
112
+ const result = await storage.get(projectKey, "category", "cat-1");
113
+ expect(result).not.toBeNull();
114
+ expect(result?.id).toBe("cat-1");
115
+ });
116
+
117
+ test("returns null for non-existent id", async () => {
118
+ const result = await storage.get(
119
+ projectKey,
120
+ "category",
121
+ "non-existent",
122
+ );
123
+ expect(result).toBeNull();
124
+ });
125
+
126
+ test("returns a clone each time", async () => {
127
+ await storage.add(projectKey, "category", makeCategory());
128
+
129
+ const first = await storage.get(projectKey, "category", "cat-1");
130
+ const second = await storage.get(projectKey, "category", "cat-1");
131
+
132
+ expect(first).toEqual(second);
133
+ expect(first).not.toBe(second);
134
+ });
135
+ });
136
+
137
+ describe("getByKey", () => {
138
+ test("returns resource by key", async () => {
139
+ await storage.add(
140
+ projectKey,
141
+ "category",
142
+ makeCategory({ key: "my-key" }),
143
+ );
144
+
145
+ const result = await storage.getByKey(
146
+ projectKey,
147
+ "category",
148
+ "my-key",
149
+ {},
150
+ );
151
+ expect(result).not.toBeNull();
152
+ expect(result?.id).toBe("cat-1");
153
+ });
154
+
155
+ test("returns null for non-existent key", async () => {
156
+ await storage.add(projectKey, "category", makeCategory());
157
+
158
+ const result = await storage.getByKey(
159
+ projectKey,
160
+ "category",
161
+ "does-not-exist",
162
+ {},
163
+ );
164
+ expect(result).toBeNull();
165
+ });
166
+ });
167
+
168
+ describe("all", () => {
169
+ test("returns empty array when no resources exist", async () => {
170
+ const result = await storage.all(projectKey, "category");
171
+ expect(result).toEqual([]);
172
+ });
173
+
174
+ test("returns all resources of a type", async () => {
175
+ await storage.add(
176
+ projectKey,
177
+ "category",
178
+ makeCategory({ id: "cat-1" }),
179
+ );
180
+ await storage.add(
181
+ projectKey,
182
+ "category",
183
+ makeCategory({ id: "cat-2" }),
184
+ );
185
+ await storage.add(
186
+ projectKey,
187
+ "category",
188
+ makeCategory({ id: "cat-3" }),
189
+ );
190
+
191
+ const result = await storage.all(projectKey, "category");
192
+ expect(result).toHaveLength(3);
193
+ });
194
+
195
+ test("returns clones of all resources", async () => {
196
+ await storage.add(projectKey, "category", makeCategory());
197
+
198
+ const results = await storage.all(projectKey, "category");
199
+ (results[0] as Writable<Category>).name = { en: "Mutated" };
200
+
201
+ const fresh = await storage.all(projectKey, "category");
202
+ expect(fresh[0].name).toEqual({ en: "Test Category" });
203
+ });
204
+
205
+ test("does not return resources from other types", async () => {
206
+ await storage.add(projectKey, "category", makeCategory());
207
+ await storage.add(projectKey, "channel", makeChannel());
208
+
209
+ const categories = await storage.all(projectKey, "category");
210
+ expect(categories).toHaveLength(1);
211
+
212
+ const channels = await storage.all(projectKey, "channel");
213
+ expect(channels).toHaveLength(1);
214
+ });
215
+ });
216
+
217
+ describe("delete", () => {
218
+ test("deletes a resource and returns it", async () => {
219
+ await storage.add(projectKey, "category", makeCategory());
220
+
221
+ const deleted = await storage.delete(
222
+ projectKey,
223
+ "category",
224
+ "cat-1",
225
+ {},
226
+ );
227
+ expect(deleted).not.toBeNull();
228
+ expect(deleted?.id).toBe("cat-1");
229
+
230
+ // Should no longer be retrievable
231
+ const result = await storage.get(projectKey, "category", "cat-1");
232
+ expect(result).toBeNull();
233
+ });
234
+
235
+ test("returns null when deleting non-existent resource", async () => {
236
+ const result = await storage.delete(
237
+ projectKey,
238
+ "category",
239
+ "non-existent",
240
+ {},
241
+ );
242
+ expect(result).toBeNull();
243
+ });
244
+ });
245
+ });
246
+
247
+ describe("query", () => {
248
+ beforeEach(async () => {
249
+ // Add 25 categories for pagination tests
250
+ for (let i = 1; i <= 25; i++) {
251
+ await storage.add(
252
+ projectKey,
253
+ "category",
254
+ makeCategory({
255
+ id: `cat-${i}`,
256
+ name: { en: `Category ${i}` },
257
+ key: `category-${i}`,
258
+ orderHint: `0.${i}`,
259
+ }),
260
+ );
261
+ }
262
+ });
263
+
264
+ test("returns paginated results with default limit of 20", async () => {
265
+ const result = await storage.query(projectKey, "category", {});
266
+
267
+ expect(result.results).toHaveLength(20);
268
+ expect(result.limit).toBe(20);
269
+ expect(result.offset).toBe(0);
270
+ expect(result.count).toBe(20);
271
+ expect(result.total).toBe(25);
272
+ });
273
+
274
+ test("respects custom limit", async () => {
275
+ const result = await storage.query(projectKey, "category", { limit: 5 });
276
+
277
+ expect(result.results).toHaveLength(5);
278
+ expect(result.limit).toBe(5);
279
+ });
280
+
281
+ test("respects offset", async () => {
282
+ const result = await storage.query(projectKey, "category", {
283
+ offset: 20,
284
+ limit: 10,
285
+ });
286
+
287
+ expect(result.results).toHaveLength(5); // 25 - 20 = 5 remaining
288
+ expect(result.offset).toBe(20);
289
+ });
290
+
291
+ test("returns empty results when offset exceeds total", async () => {
292
+ const result = await storage.query(projectKey, "category", {
293
+ offset: 100,
294
+ });
295
+
296
+ expect(result.results).toHaveLength(0);
297
+ expect(result.count).toBe(0);
298
+ expect(result.total).toBe(25);
299
+ });
300
+
301
+ test("filters with where predicate", async () => {
302
+ const result = await storage.query(projectKey, "category", {
303
+ where: 'key = "category-5"',
304
+ });
305
+
306
+ expect(result.results).toHaveLength(1);
307
+ expect(result.results[0].key).toBe("category-5");
308
+ expect(result.count).toBe(1);
309
+ });
310
+
311
+ test("filters with where predicate using variables", async () => {
312
+ const result = await storage.query(projectKey, "category", {
313
+ where: "key = :myKey",
314
+ "var.myKey": "category-3",
315
+ });
316
+
317
+ expect(result.results).toHaveLength(1);
318
+ expect(result.results[0].key).toBe("category-3");
319
+ });
320
+
321
+ test("throws CommercetoolsError on invalid where predicate", async () => {
322
+ await expect(
323
+ storage.query(projectKey, "category", {
324
+ where: "invalid !!! predicate",
325
+ }),
326
+ ).rejects.toThrow(CommercetoolsError);
327
+ });
328
+
329
+ test("returns cloned results", async () => {
330
+ const result = await storage.query(projectKey, "category", { limit: 1 });
331
+ (result.results[0] as Writable<Category>).name = { en: "Mutated" };
332
+
333
+ const fresh = await storage.query(projectKey, "category", { limit: 1 });
334
+ expect(fresh.results[0].name).not.toEqual({ en: "Mutated" });
335
+ });
336
+ });
337
+
338
+ describe("search", () => {
339
+ beforeEach(async () => {
340
+ for (let i = 1; i <= 10; i++) {
341
+ await storage.add(
342
+ projectKey,
343
+ "category",
344
+ makeCategory({
345
+ id: `cat-${i}`,
346
+ key: `category-${i}`,
347
+ name: { en: `Category ${i}` },
348
+ }),
349
+ );
350
+ }
351
+ });
352
+
353
+ test("returns all resources with default pagination", async () => {
354
+ const result = await storage.search(projectKey, "category", {});
355
+
356
+ expect(result.results).toHaveLength(10);
357
+ expect(result.count).toBe(10);
358
+ expect(result.offset).toBe(0);
359
+ expect(result.limit).toBe(20);
360
+ });
361
+
362
+ test("respects limit and offset", async () => {
363
+ const result = await storage.search(projectKey, "category", {
364
+ limit: 3,
365
+ offset: 2,
366
+ });
367
+
368
+ expect(result.results).toHaveLength(3);
369
+ expect(result.offset).toBe(2);
370
+ expect(result.limit).toBe(3);
371
+ });
372
+
373
+ test("filters with where predicate", async () => {
374
+ const result = await storage.search(projectKey, "category", {
375
+ where: 'key = "category-5"',
376
+ });
377
+
378
+ expect(result.results).toHaveLength(1);
379
+ expect(result.count).toBe(1);
380
+ });
381
+
382
+ test("throws on invalid where predicate", async () => {
383
+ await expect(
384
+ storage.search(projectKey, "category", {
385
+ where: "invalid !!! predicate",
386
+ }),
387
+ ).rejects.toThrow(CommercetoolsError);
388
+ });
389
+ });
390
+
391
+ describe("getByResourceIdentifier", () => {
392
+ test("finds resource by id", async () => {
393
+ await storage.add(projectKey, "category", makeCategory());
394
+
395
+ const result = await storage.getByResourceIdentifier(projectKey, {
396
+ typeId: "category",
397
+ id: "cat-1",
398
+ });
399
+
400
+ expect(result.id).toBe("cat-1");
401
+ });
402
+
403
+ test("finds resource by key", async () => {
404
+ await storage.add(
405
+ projectKey,
406
+ "category",
407
+ makeCategory({ key: "my-cat-key" }),
408
+ );
409
+
410
+ const result = await storage.getByResourceIdentifier(projectKey, {
411
+ typeId: "category",
412
+ key: "my-cat-key",
413
+ });
414
+
415
+ expect(result.id).toBe("cat-1");
416
+ });
417
+
418
+ test("throws ReferencedResourceNotFoundError when id not found", async () => {
419
+ try {
420
+ await storage.getByResourceIdentifier(projectKey, {
421
+ typeId: "category",
422
+ id: "non-existent",
423
+ });
424
+ expect.fail("Should have thrown");
425
+ } catch (err) {
426
+ expect(err).toBeInstanceOf(CommercetoolsError);
427
+ expect((err as CommercetoolsError<any>).info.code).toBe(
428
+ "ReferencedResourceNotFound",
429
+ );
430
+ expect((err as CommercetoolsError<any>).info.id).toBe("non-existent");
431
+ }
432
+ });
433
+
434
+ test("throws ReferencedResourceNotFoundError when key not found", async () => {
435
+ try {
436
+ await storage.getByResourceIdentifier(projectKey, {
437
+ typeId: "category",
438
+ key: "non-existent-key",
439
+ });
440
+ expect.fail("Should have thrown");
441
+ } catch (err) {
442
+ expect(err).toBeInstanceOf(CommercetoolsError);
443
+ expect((err as CommercetoolsError<any>).info.code).toBe(
444
+ "ReferencedResourceNotFound",
445
+ );
446
+ expect((err as CommercetoolsError<any>).info.key).toBe(
447
+ "non-existent-key",
448
+ );
449
+ }
450
+ });
451
+
452
+ test("throws InvalidJsonInput when neither id nor key provided", async () => {
453
+ try {
454
+ await storage.getByResourceIdentifier(projectKey, {
455
+ typeId: "category",
456
+ } as any);
457
+ expect.fail("Should have thrown");
458
+ } catch (err) {
459
+ expect(err).toBeInstanceOf(CommercetoolsError);
460
+ expect((err as CommercetoolsError<any>).info.code).toBe(
461
+ "InvalidJsonInput",
462
+ );
463
+ }
464
+ });
465
+ });
466
+
467
+ describe("expand", () => {
468
+ test("returns object unchanged when clause is undefined", async () => {
469
+ const obj = { foo: "bar" };
470
+ const result = await storage.expand(projectKey, obj, undefined);
471
+
472
+ expect(result).toEqual(obj);
473
+ });
474
+
475
+ test("expands a single reference", async () => {
476
+ const typeResource = makeType();
477
+ await storage.add(projectKey, "type", typeResource);
478
+
479
+ const category = makeCategory({
480
+ custom: {
481
+ type: { typeId: "type" as const, id: "type-1" },
482
+ fields: {},
483
+ },
484
+ });
485
+ await storage.add(projectKey, "category", category);
486
+
487
+ const retrieved = await storage.get(projectKey, "category", "cat-1", {
488
+ expand: ["custom.type"],
489
+ });
490
+
491
+ expect(retrieved).not.toBeNull();
492
+ expect((retrieved as any).custom.type.obj).toBeDefined();
493
+ expect((retrieved as any).custom.type.obj.id).toBe("type-1");
494
+ });
495
+
496
+ test("expands array references with wildcard", async () => {
497
+ const cat1 = makeCategory({ id: "cat-a" });
498
+ const cat2 = makeCategory({ id: "cat-b" });
499
+ await storage.add(projectKey, "category", cat1);
500
+ await storage.add(projectKey, "category", cat2);
501
+
502
+ const obj = {
503
+ categories: [
504
+ { typeId: "category", id: "cat-a" },
505
+ { typeId: "category", id: "cat-b" },
506
+ ],
507
+ };
508
+
509
+ const result = await storage.expand(projectKey, obj, ["categories[*]"]);
510
+
511
+ expect((result.categories[0] as any).obj).toBeDefined();
512
+ expect((result.categories[0] as any).obj.id).toBe("cat-a");
513
+ expect((result.categories[1] as any).obj).toBeDefined();
514
+ expect((result.categories[1] as any).obj.id).toBe("cat-b");
515
+ });
516
+
517
+ test("handles missing reference element gracefully", async () => {
518
+ const obj = { name: "test" };
519
+ // Should not throw when trying to expand a non-existent field
520
+ const result = await storage.expand(projectKey, obj, ["nonExistent"]);
521
+ expect(result).toEqual(obj);
522
+ });
523
+
524
+ test("expands multiple clauses", async () => {
525
+ const typeResource = makeType();
526
+ await storage.add(projectKey, "type", typeResource);
527
+
528
+ const channelResource = makeChannel();
529
+ await storage.add(projectKey, "channel", channelResource);
530
+
531
+ const obj = {
532
+ custom: {
533
+ type: { typeId: "type", id: "type-1" },
534
+ fields: {},
535
+ },
536
+ supplyChannel: { typeId: "channel", id: "channel-1" },
537
+ };
538
+
539
+ const result = await storage.expand(projectKey, obj, [
540
+ "custom.type",
541
+ "supplyChannel",
542
+ ]);
543
+
544
+ expect((result.custom.type as any).obj).toBeDefined();
545
+ expect((result.supplyChannel as any).obj).toBeDefined();
546
+ });
547
+
548
+ test("expand with string clause (non-array)", async () => {
549
+ const typeResource = makeType();
550
+ await storage.add(projectKey, "type", typeResource);
551
+
552
+ const obj = {
553
+ custom: {
554
+ type: { typeId: "type", id: "type-1" },
555
+ fields: {},
556
+ },
557
+ };
558
+
559
+ const result = await storage.expand(projectKey, obj, "custom.type");
560
+ expect((result.custom.type as any).obj).toBeDefined();
561
+ expect((result.custom.type as any).obj.id).toBe("type-1");
562
+ });
563
+ });
564
+
565
+ describe("clear", () => {
566
+ test("removes all resources from all projects", async () => {
567
+ await storage.add(projectKey, "category", makeCategory());
568
+ await storage.add(projectKey, "channel", makeChannel());
569
+ await storage.add(
570
+ "other-project",
571
+ "category",
572
+ makeCategory({ id: "cat-2" }),
573
+ );
574
+
575
+ await storage.clear();
576
+
577
+ expect(await storage.all(projectKey, "category")).toHaveLength(0);
578
+ expect(await storage.all(projectKey, "channel")).toHaveLength(0);
579
+ expect(await storage.all("other-project", "category")).toHaveLength(0);
580
+ });
581
+
582
+ test("clear does not affect projects themselves", async () => {
583
+ await storage.addProject(projectKey);
584
+ await storage.add(projectKey, "category", makeCategory());
585
+
586
+ await storage.clear();
587
+
588
+ // Projects still exist, can still add resources after clear
589
+ const project = await storage.getProject(projectKey);
590
+ expect(project.key).toBe(projectKey);
591
+ });
592
+ });
593
+
594
+ describe("cross-project isolation", () => {
595
+ test("resources in different projects are isolated", async () => {
596
+ await storage.add("project-a", "category", makeCategory({ id: "cat-a" }));
597
+ await storage.add("project-b", "category", makeCategory({ id: "cat-b" }));
598
+
599
+ expect(
600
+ await storage.get("project-a", "category", "cat-a"),
601
+ ).not.toBeNull();
602
+ expect(await storage.get("project-a", "category", "cat-b")).toBeNull();
603
+
604
+ expect(
605
+ await storage.get("project-b", "category", "cat-b"),
606
+ ).not.toBeNull();
607
+ expect(await storage.get("project-b", "category", "cat-a")).toBeNull();
608
+ });
609
+
610
+ test("deleting from one project does not affect another", async () => {
611
+ await storage.add("project-a", "category", makeCategory({ id: "cat-1" }));
612
+ await storage.add("project-b", "category", makeCategory({ id: "cat-1" }));
613
+
614
+ await storage.delete("project-a", "category", "cat-1", {});
615
+
616
+ expect(await storage.get("project-a", "category", "cat-1")).toBeNull();
617
+ expect(
618
+ await storage.get("project-b", "category", "cat-1"),
619
+ ).not.toBeNull();
620
+ });
621
+ });
622
+
623
+ describe("multiple resource types", () => {
624
+ test("stores and retrieves different resource types independently", async () => {
625
+ await storage.add(projectKey, "category", makeCategory());
626
+ await storage.add(projectKey, "channel", makeChannel());
627
+ await storage.add(projectKey, "customer", makeCustomer());
628
+
629
+ expect(await storage.get(projectKey, "category", "cat-1")).not.toBeNull();
630
+ expect(
631
+ await storage.get(projectKey, "channel", "channel-1"),
632
+ ).not.toBeNull();
633
+ expect(
634
+ await storage.get(projectKey, "customer", "customer-1"),
635
+ ).not.toBeNull();
636
+
637
+ // Cross-type lookup should return null
638
+ expect(await storage.get(projectKey, "category", "channel-1")).toBeNull();
639
+ });
640
+ });
641
+
642
+ describe("count", () => {
643
+ test("returns 0 for an empty store", async () => {
644
+ const result = await storage.count(projectKey, "category");
645
+ expect(result).toBe(0);
646
+ });
647
+
648
+ test("returns correct count after adding resources", async () => {
649
+ await storage.add(projectKey, "category", makeCategory({ id: "cat-1" }));
650
+ await storage.add(projectKey, "category", makeCategory({ id: "cat-2" }));
651
+ await storage.add(projectKey, "category", makeCategory({ id: "cat-3" }));
652
+
653
+ const result = await storage.count(projectKey, "category");
654
+ expect(result).toBe(3);
655
+ });
656
+
657
+ test("returns correct count after deleting a resource", async () => {
658
+ await storage.add(projectKey, "category", makeCategory({ id: "cat-1" }));
659
+ await storage.add(projectKey, "category", makeCategory({ id: "cat-2" }));
660
+
661
+ await storage.delete(projectKey, "category", "cat-1", {});
662
+
663
+ const result = await storage.count(projectKey, "category");
664
+ expect(result).toBe(1);
665
+ });
666
+
667
+ test("counts are isolated per resource type", async () => {
668
+ await storage.add(projectKey, "category", makeCategory());
669
+ await storage.add(projectKey, "channel", makeChannel());
670
+
671
+ expect(await storage.count(projectKey, "category")).toBe(1);
672
+ expect(await storage.count(projectKey, "channel")).toBe(1);
673
+ });
674
+ });
675
+
676
+ describe("getByContainerAndKey", () => {
677
+ test("finds a custom object by container and key", async () => {
678
+ await storage.add(
679
+ projectKey,
680
+ "key-value-document",
681
+ makeCustomObject({
682
+ id: "co-1",
683
+ container: "my-container",
684
+ key: "my-key",
685
+ }),
686
+ );
687
+
688
+ const result = await storage.getByContainerAndKey(
689
+ projectKey,
690
+ "my-container",
691
+ "my-key",
692
+ );
693
+ expect(result).not.toBeNull();
694
+ expect(result?.id).toBe("co-1");
695
+ expect(result?.container).toBe("my-container");
696
+ expect(result?.key).toBe("my-key");
697
+ });
698
+
699
+ test("returns null when container does not match", async () => {
700
+ await storage.add(
701
+ projectKey,
702
+ "key-value-document",
703
+ makeCustomObject({
704
+ id: "co-1",
705
+ container: "my-container",
706
+ key: "my-key",
707
+ }),
708
+ );
709
+
710
+ const result = await storage.getByContainerAndKey(
711
+ projectKey,
712
+ "other-container",
713
+ "my-key",
714
+ );
715
+ expect(result).toBeNull();
716
+ });
717
+
718
+ test("returns null when key does not match", async () => {
719
+ await storage.add(
720
+ projectKey,
721
+ "key-value-document",
722
+ makeCustomObject({
723
+ id: "co-1",
724
+ container: "my-container",
725
+ key: "my-key",
726
+ }),
727
+ );
728
+
729
+ const result = await storage.getByContainerAndKey(
730
+ projectKey,
731
+ "my-container",
732
+ "other-key",
733
+ );
734
+ expect(result).toBeNull();
735
+ });
736
+
737
+ test("returns null when no custom objects exist", async () => {
738
+ const result = await storage.getByContainerAndKey(
739
+ projectKey,
740
+ "my-container",
741
+ "my-key",
742
+ );
743
+ expect(result).toBeNull();
744
+ });
745
+
746
+ test("returns null after custom object is deleted", async () => {
747
+ await storage.add(
748
+ projectKey,
749
+ "key-value-document",
750
+ makeCustomObject({
751
+ id: "co-1",
752
+ container: "my-container",
753
+ key: "my-key",
754
+ }),
755
+ );
756
+
757
+ await storage.delete(projectKey, "key-value-document", "co-1", {});
758
+
759
+ const result = await storage.getByContainerAndKey(
760
+ projectKey,
761
+ "my-container",
762
+ "my-key",
763
+ );
764
+ expect(result).toBeNull();
765
+ });
766
+
767
+ test("distinguishes between different container/key combinations", async () => {
768
+ await storage.add(
769
+ projectKey,
770
+ "key-value-document",
771
+ makeCustomObject({
772
+ id: "co-1",
773
+ container: "container-a",
774
+ key: "key-1",
775
+ }),
776
+ );
777
+ await storage.add(
778
+ projectKey,
779
+ "key-value-document",
780
+ makeCustomObject({
781
+ id: "co-2",
782
+ container: "container-b",
783
+ key: "key-1",
784
+ }),
785
+ );
786
+
787
+ const resultA = await storage.getByContainerAndKey(
788
+ projectKey,
789
+ "container-a",
790
+ "key-1",
791
+ );
792
+ const resultB = await storage.getByContainerAndKey(
793
+ projectKey,
794
+ "container-b",
795
+ "key-1",
796
+ );
797
+
798
+ expect(resultA?.id).toBe("co-1");
799
+ expect(resultB?.id).toBe("co-2");
800
+ });
801
+ });
802
+ });