@labdigital/commercetools-mock 2.51.0 → 2.53.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 (42) hide show
  1. package/dist/index.d.ts +34 -23
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +140 -13
  4. package/dist/index.js.map +1 -1
  5. package/package.json +1 -1
  6. package/src/lib/product-review-statistics.test.ts +349 -0
  7. package/src/lib/productSearchFilter.test.ts +77 -0
  8. package/src/lib/review-statistics.ts +58 -0
  9. package/src/product-projection-search.ts +17 -2
  10. package/src/product-search-availability.test.ts +242 -0
  11. package/src/product-search.ts +22 -4
  12. package/src/repositories/as-associate.test.ts +126 -0
  13. package/src/repositories/attribute-group.test.ts +221 -0
  14. package/src/repositories/business-unit.test.ts +425 -0
  15. package/src/repositories/business-unit.ts +57 -1
  16. package/src/repositories/channel.test.ts +374 -0
  17. package/src/repositories/customer-group.test.ts +262 -0
  18. package/src/repositories/extension.test.ts +306 -0
  19. package/src/repositories/index.test.ts +17 -0
  20. package/src/repositories/product/index.ts +22 -1
  21. package/src/repositories/product-projection.ts +8 -2
  22. package/src/repositories/review.test.ts +636 -0
  23. package/src/repositories/review.ts +145 -4
  24. package/src/repositories/subscription.test.ts +207 -0
  25. package/src/repositories/zone.test.ts +278 -0
  26. package/src/services/as-associate-cart.test.ts +58 -0
  27. package/src/services/as-associate.test.ts +34 -0
  28. package/src/services/attribute-group.test.ts +114 -0
  29. package/src/services/channel.test.ts +90 -0
  30. package/src/services/customer-group.test.ts +85 -0
  31. package/src/services/discount-code.test.ts +120 -0
  32. package/src/services/extension.test.ts +130 -0
  33. package/src/services/my-business-unit.test.ts +113 -0
  34. package/src/services/my-business-unit.ts +6 -0
  35. package/src/services/my-customer.test.ts +24 -0
  36. package/src/services/order.test.ts +18 -0
  37. package/src/services/product-discount.test.ts +146 -0
  38. package/src/services/project.test.ts +17 -0
  39. package/src/services/reviews.test.ts +230 -0
  40. package/src/services/subscription.test.ts +151 -0
  41. package/src/services/type.test.ts +127 -0
  42. package/src/services/zone.test.ts +117 -0
@@ -0,0 +1,242 @@
1
+ import type {
2
+ InventoryEntryDraft,
3
+ ProductDraft,
4
+ ProductPagedSearchResponse,
5
+ ProductSearchRequest,
6
+ } from "@commercetools/platform-sdk";
7
+ import supertest from "supertest";
8
+ import { afterEach, beforeEach, describe, expect, test } from "vitest";
9
+ import { CommercetoolsMock } from "./index";
10
+
11
+ describe("Product Search - Availability Filtering", () => {
12
+ const ctMock = new CommercetoolsMock();
13
+ let productId: string;
14
+
15
+ beforeEach(async () => {
16
+ // Create a product type first
17
+ const productTypeDraft = {
18
+ name: "Test Product Type",
19
+ key: "test-type",
20
+ description: "Test Product Type",
21
+ };
22
+
23
+ await supertest(ctMock.app)
24
+ .post("/dummy/product-types")
25
+ .send(productTypeDraft);
26
+
27
+ // Create a test product
28
+ const productDraft: ProductDraft = {
29
+ name: { "en-US": "Test Product" },
30
+ productType: {
31
+ typeId: "product-type",
32
+ key: "test-type",
33
+ },
34
+ slug: { "en-US": "test-product" },
35
+ masterVariant: {
36
+ sku: "TEST-SKU-001",
37
+ },
38
+ variants: [
39
+ {
40
+ sku: "TEST-SKU-002",
41
+ },
42
+ ],
43
+ };
44
+
45
+ const productResponse = await supertest(ctMock.app)
46
+ .post("/dummy/products")
47
+ .send(productDraft);
48
+
49
+ productId = productResponse.body.id;
50
+
51
+ // Publish the product
52
+ await supertest(ctMock.app)
53
+ .post(`/dummy/products/${productId}`)
54
+ .send({
55
+ version: productResponse.body.version,
56
+ actions: [{ action: "publish" }],
57
+ });
58
+ });
59
+
60
+ afterEach(() => {
61
+ ctMock.clear();
62
+ });
63
+
64
+ async function createInventoryEntry(
65
+ sku: string,
66
+ quantityOnStock: number,
67
+ channelId?: string,
68
+ ) {
69
+ const inventoryEntry: InventoryEntryDraft = {
70
+ sku,
71
+ quantityOnStock,
72
+ ...(channelId && {
73
+ supplyChannel: {
74
+ typeId: "channel",
75
+ id: channelId,
76
+ },
77
+ }),
78
+ };
79
+
80
+ await supertest(ctMock.app).post("/dummy/inventory").send(inventoryEntry);
81
+ }
82
+
83
+ async function searchProducts(
84
+ query?: any,
85
+ ): Promise<ProductPagedSearchResponse> {
86
+ const searchRequest: ProductSearchRequest = {
87
+ ...(query && { query }),
88
+ productProjectionParameters: {
89
+ staged: false,
90
+ },
91
+ };
92
+
93
+ const response = await supertest(ctMock.app)
94
+ .post("/dummy/products/search")
95
+ .send(searchRequest);
96
+
97
+ return response.body;
98
+ }
99
+
100
+ test("should filter products by variants.availability.isOnStock = true", async () => {
101
+ // Create inventory with stock for one variant
102
+ await createInventoryEntry("TEST-SKU-001", 10);
103
+
104
+ const result = await searchProducts({
105
+ exact: {
106
+ field: "variants.availability.isOnStock",
107
+ value: true,
108
+ },
109
+ });
110
+
111
+ expect(result.results).toHaveLength(1);
112
+ expect(
113
+ result.results[0].productProjection?.masterVariant?.availability
114
+ ?.isOnStock,
115
+ ).toBe(true);
116
+ });
117
+
118
+ test("should filter products by variants.availability.isOnStock = false", async () => {
119
+ // Create inventory with zero stock
120
+ await createInventoryEntry("TEST-SKU-001", 0);
121
+
122
+ const result = await searchProducts({
123
+ exact: {
124
+ field: "variants.availability.isOnStock",
125
+ value: false,
126
+ },
127
+ });
128
+
129
+ // Should find the product because it's not on stock
130
+ expect(result.results).toHaveLength(1);
131
+ expect(
132
+ result.results[0].productProjection?.masterVariant?.availability
133
+ ?.isOnStock,
134
+ ).toBe(false);
135
+ });
136
+
137
+ test("should filter products by variants.availability.isOnStockForChannel", async () => {
138
+ const channelId = "test-channel-1";
139
+
140
+ // Create inventory for specific channel
141
+ await createInventoryEntry("TEST-SKU-001", 5, channelId);
142
+
143
+ const result = await searchProducts({
144
+ exact: {
145
+ field: "variants.availability.isOnStockForChannel",
146
+ value: channelId,
147
+ },
148
+ });
149
+
150
+ expect(result.results).toHaveLength(1);
151
+ expect(
152
+ (result.results[0].productProjection?.masterVariant?.availability as any)
153
+ ?.isOnStockForChannel,
154
+ ).toBe(channelId);
155
+ });
156
+
157
+ test("should not find products when filtering by non-matching channel", async () => {
158
+ const channelId = "test-channel-1";
159
+ const otherChannelId = "test-channel-2";
160
+
161
+ // Create inventory for specific channel
162
+ await createInventoryEntry("TEST-SKU-001", 5, channelId);
163
+
164
+ const result = await searchProducts({
165
+ exact: {
166
+ field: "variants.availability.isOnStockForChannel",
167
+ value: otherChannelId,
168
+ },
169
+ });
170
+
171
+ expect(result.results).toHaveLength(0);
172
+ });
173
+
174
+ test("should handle products without inventory entries", async () => {
175
+ // Don't create any inventory entries
176
+
177
+ const result = await searchProducts({
178
+ exact: {
179
+ field: "variants.availability.isOnStock",
180
+ value: false,
181
+ },
182
+ });
183
+
184
+ // Should find the product because it has no stock
185
+ expect(result.results).toHaveLength(1);
186
+ expect(
187
+ result.results[0].productProjection?.masterVariant?.availability
188
+ ?.isOnStock,
189
+ ).toBe(false);
190
+ });
191
+
192
+ test("should work with OR queries for availability", async () => {
193
+ // Create inventory with stock
194
+ await createInventoryEntry("TEST-SKU-001", 10);
195
+
196
+ const result = await searchProducts({
197
+ or: [
198
+ {
199
+ exact: {
200
+ field: "variants.availability.isOnStock",
201
+ value: true,
202
+ },
203
+ },
204
+ {
205
+ exact: {
206
+ field: "variants.availability.isOnStock",
207
+ value: false,
208
+ },
209
+ },
210
+ ],
211
+ });
212
+
213
+ // Should find the product regardless of stock status
214
+ expect(result.results).toHaveLength(1);
215
+ });
216
+
217
+ test("should work with AND queries combining availability and other fields", async () => {
218
+ await createInventoryEntry("TEST-SKU-001", 10);
219
+
220
+ const result = await searchProducts({
221
+ and: [
222
+ {
223
+ exact: {
224
+ field: "variants.availability.isOnStock",
225
+ value: true,
226
+ },
227
+ },
228
+ {
229
+ exact: {
230
+ field: "variants.sku",
231
+ value: "TEST-SKU-001",
232
+ },
233
+ },
234
+ ],
235
+ });
236
+
237
+ expect(result.results).toHaveLength(1);
238
+ expect(result.results[0].productProjection?.masterVariant?.sku).toBe(
239
+ "TEST-SKU-001",
240
+ );
241
+ });
242
+ });
@@ -148,6 +148,23 @@ export class ProductSearch {
148
148
  ? product.masterData.current
149
149
  : product.masterData.staged;
150
150
 
151
+ const getVariantAvailability = (sku?: string) => {
152
+ if (!sku) {
153
+ return {
154
+ isOnStock: false,
155
+ availableQuantity: 0,
156
+ isOnStockForChannel: undefined,
157
+ };
158
+ }
159
+ return (
160
+ availabilityBySku.get(sku) || {
161
+ isOnStock: false,
162
+ availableQuantity: 0,
163
+ isOnStockForChannel: undefined,
164
+ }
165
+ );
166
+ };
167
+
151
168
  return {
152
169
  id: product.id,
153
170
  createdAt: product.createdAt,
@@ -159,12 +176,13 @@ export class ProductSearch {
159
176
  metaDescription: obj.metaDescription,
160
177
  slug: obj.slug,
161
178
  categories: obj.categories,
162
- masterVariant: obj.masterVariant,
179
+ masterVariant: {
180
+ ...obj.masterVariant,
181
+ availability: getVariantAvailability(obj.masterVariant.sku),
182
+ },
163
183
  variants: obj.variants.map((variant) => ({
164
184
  ...variant,
165
- availability: variant.sku
166
- ? availabilityBySku.get(variant.sku)
167
- : { isOnStock: false, availableQuantity: 0, isOnStockForChannel: [] },
185
+ availability: getVariantAvailability(variant.sku),
168
186
  })),
169
187
  productType: product.productType,
170
188
  hasStagedChanges: product.masterData.hasStagedChanges,
@@ -0,0 +1,126 @@
1
+ import { describe, expect, test } from "vitest";
2
+ import type { Config } from "~src/config";
3
+ import { InMemoryStorage } from "~src/storage";
4
+ import {
5
+ AsAssociateCartRepository,
6
+ AsAssociateOrderRepository,
7
+ AsAssociateQuoteRequestRepository,
8
+ } from "./as-associate";
9
+ import { CustomerRepository } from "./customer";
10
+
11
+ describe("As Associate Repositories", () => {
12
+ const storage = new InMemoryStorage();
13
+ const config: Config = { storage, strict: false };
14
+
15
+ test("AsAssociateCartRepository can create and retrieve carts", () => {
16
+ const repository = new AsAssociateCartRepository(config);
17
+ const ctx = { projectKey: "test-project" };
18
+
19
+ const cartDraft = {
20
+ currency: "EUR",
21
+ inventoryMode: "None" as const,
22
+ taxMode: "Platform" as const,
23
+ taxRoundingMode: "HalfEven" as const,
24
+ taxCalculationMode: "UnitPriceLevel" as const,
25
+ };
26
+
27
+ const cart = repository.create(ctx, cartDraft);
28
+ expect(cart.id).toBeDefined();
29
+ expect(cart.version).toBe(1);
30
+ expect(cart.totalPrice.currencyCode).toBe("EUR");
31
+
32
+ // Test query
33
+ const result = repository.query(ctx);
34
+ expect(result.count).toBe(1);
35
+ expect(result.results[0].id).toBe(cart.id);
36
+
37
+ // Test get
38
+ const retrieved = repository.get(ctx, cart.id);
39
+ expect(retrieved).toBeDefined();
40
+ expect(retrieved?.id).toBe(cart.id);
41
+ });
42
+
43
+ test("AsAssociateOrderRepository can create and retrieve orders", () => {
44
+ const repository = new AsAssociateOrderRepository(config);
45
+ const ctx = { projectKey: "test-project" };
46
+
47
+ // First create a cart to create an order from
48
+ const cartRepository = new AsAssociateCartRepository(config);
49
+ const cartDraft = {
50
+ currency: "EUR",
51
+ inventoryMode: "None" as const,
52
+ taxMode: "Platform" as const,
53
+ taxRoundingMode: "HalfEven" as const,
54
+ taxCalculationMode: "UnitPriceLevel" as const,
55
+ };
56
+ const cart = cartRepository.create(ctx, cartDraft);
57
+
58
+ const orderDraft = {
59
+ cart: {
60
+ id: cart.id,
61
+ typeId: "cart" as const,
62
+ },
63
+ version: cart.version,
64
+ };
65
+
66
+ const order = repository.create(ctx, orderDraft);
67
+ expect(order.id).toBeDefined();
68
+ expect(order.version).toBe(1);
69
+ expect(order.cart?.id).toBe(cart.id);
70
+
71
+ // Test query
72
+ const result = repository.query(ctx);
73
+ expect(result.count).toBe(1);
74
+ expect(result.results[0].id).toBe(order.id);
75
+
76
+ // Test get
77
+ const retrieved = repository.get(ctx, order.id);
78
+ expect(retrieved).toBeDefined();
79
+ expect(retrieved?.id).toBe(order.id);
80
+ });
81
+
82
+ test("AsAssociateQuoteRequestRepository can create and retrieve quote requests", () => {
83
+ const repository = new AsAssociateQuoteRequestRepository(config);
84
+ const ctx = { projectKey: "test-project" };
85
+
86
+ // Create a customer using the customer repository
87
+ const customerRepository = new CustomerRepository(config);
88
+ const customer = customerRepository.create(ctx, {
89
+ email: "test@example.com",
90
+ password: "password123",
91
+ firstName: "John",
92
+ lastName: "Doe",
93
+ });
94
+
95
+ // First create a cart to create a quote request from
96
+ const cartRepository = new AsAssociateCartRepository(config);
97
+ const cartDraft = {
98
+ currency: "EUR",
99
+ customerId: customer.id,
100
+ };
101
+ const cart = cartRepository.create(ctx, cartDraft);
102
+
103
+ const quoteRequestDraft = {
104
+ cart: {
105
+ id: cart.id,
106
+ typeId: "cart" as const,
107
+ },
108
+ cartVersion: cart.version,
109
+ };
110
+
111
+ const quoteRequest = repository.create(ctx, quoteRequestDraft);
112
+ expect(quoteRequest.id).toBeDefined();
113
+ expect(quoteRequest.version).toBe(1);
114
+ expect(quoteRequest.cart?.id).toBe(cart.id);
115
+
116
+ // Test query
117
+ const result = repository.query(ctx);
118
+ expect(result.count).toBe(1);
119
+ expect(result.results[0].id).toBe(quoteRequest.id);
120
+
121
+ // Test get
122
+ const retrieved = repository.get(ctx, quoteRequest.id);
123
+ expect(retrieved).toBeDefined();
124
+ expect(retrieved?.id).toBe(quoteRequest.id);
125
+ });
126
+ });
@@ -0,0 +1,221 @@
1
+ import type {
2
+ AttributeGroupChangeNameAction,
3
+ AttributeGroupDraft,
4
+ AttributeGroupSetAttributesAction,
5
+ AttributeGroupSetDescriptionAction,
6
+ AttributeGroupSetKeyAction,
7
+ } from "@commercetools/platform-sdk";
8
+ import { describe, expect, test } from "vitest";
9
+ import type { Config } from "~src/config";
10
+ import { InMemoryStorage } from "~src/storage";
11
+ import { AttributeGroupRepository } from "./attribute-group";
12
+
13
+ describe("AttributeGroup Repository", () => {
14
+ const storage = new InMemoryStorage();
15
+ const config: Config = { storage, strict: false };
16
+ const repository = new AttributeGroupRepository(config);
17
+
18
+ test("create attribute group", () => {
19
+ const draft: AttributeGroupDraft = {
20
+ name: { "en-US": "Size Attributes", "de-DE": "Größenattribute" },
21
+ description: { "en-US": "Attributes related to product size" },
22
+ key: "size-attributes",
23
+ attributes: [
24
+ {
25
+ key: "size",
26
+ },
27
+ {
28
+ key: "weight",
29
+ },
30
+ ],
31
+ };
32
+
33
+ const ctx = { projectKey: "dummy" };
34
+ const result = repository.create(ctx, draft);
35
+
36
+ expect(result.id).toBeDefined();
37
+ expect(result.version).toBe(1);
38
+ expect(result.name).toEqual(draft.name);
39
+ expect(result.description).toEqual(draft.description);
40
+ expect(result.key).toBe(draft.key);
41
+ expect(result.attributes).toEqual(draft.attributes);
42
+
43
+ // Test that the attribute group is stored
44
+ const items = repository.query(ctx);
45
+ expect(items.count).toBe(1);
46
+ expect(items.results[0].id).toBe(result.id);
47
+ });
48
+
49
+ test("create attribute group with minimal data", () => {
50
+ const draft: AttributeGroupDraft = {
51
+ name: { "en-US": "Minimal Attributes" },
52
+ attributes: [],
53
+ };
54
+
55
+ const ctx = { projectKey: "dummy" };
56
+ const result = repository.create(ctx, draft);
57
+
58
+ expect(result.id).toBeDefined();
59
+ expect(result.name).toEqual(draft.name);
60
+ expect(result.description).toBeUndefined();
61
+ expect(result.key).toBeUndefined();
62
+ expect(result.attributes).toEqual([]);
63
+ });
64
+
65
+ test("update attribute group - changeName", () => {
66
+ const draft: AttributeGroupDraft = {
67
+ name: { "en-US": "Original Name" },
68
+ key: "test-attributes",
69
+ attributes: [],
70
+ };
71
+
72
+ const ctx = { projectKey: "dummy" };
73
+ const attributeGroup = repository.create(ctx, draft);
74
+
75
+ const result = repository.processUpdateActions(
76
+ ctx,
77
+ attributeGroup,
78
+ attributeGroup.version,
79
+ [
80
+ {
81
+ action: "changeName",
82
+ name: { "en-US": "Updated Name", "de-DE": "Aktualisierter Name" },
83
+ } as AttributeGroupChangeNameAction,
84
+ ],
85
+ );
86
+
87
+ expect(result.name).toEqual({
88
+ "en-US": "Updated Name",
89
+ "de-DE": "Aktualisierter Name",
90
+ });
91
+ expect(result.version).toBe(attributeGroup.version + 1);
92
+ });
93
+
94
+ test("update attribute group - setDescription", () => {
95
+ const draft: AttributeGroupDraft = {
96
+ name: { "en-US": "Test Attributes" },
97
+ key: "test-attributes-2",
98
+ attributes: [],
99
+ };
100
+
101
+ const ctx = { projectKey: "dummy" };
102
+ const attributeGroup = repository.create(ctx, draft);
103
+
104
+ const result = repository.processUpdateActions(
105
+ ctx,
106
+ attributeGroup,
107
+ attributeGroup.version,
108
+ [
109
+ {
110
+ action: "setDescription",
111
+ description: {
112
+ "en-US": "New description",
113
+ "de-DE": "Neue Beschreibung",
114
+ },
115
+ } as AttributeGroupSetDescriptionAction,
116
+ ],
117
+ );
118
+
119
+ expect(result.description).toEqual({
120
+ "en-US": "New description",
121
+ "de-DE": "Neue Beschreibung",
122
+ });
123
+ expect(result.version).toBe(attributeGroup.version + 1);
124
+ });
125
+
126
+ test("update attribute group - setKey", () => {
127
+ const draft: AttributeGroupDraft = {
128
+ name: { "en-US": "Key Test Attributes" },
129
+ key: "original-key",
130
+ attributes: [],
131
+ };
132
+
133
+ const ctx = { projectKey: "dummy" };
134
+ const attributeGroup = repository.create(ctx, draft);
135
+
136
+ const result = repository.processUpdateActions(
137
+ ctx,
138
+ attributeGroup,
139
+ attributeGroup.version,
140
+ [
141
+ {
142
+ action: "setKey",
143
+ key: "updated-key",
144
+ } as AttributeGroupSetKeyAction,
145
+ ],
146
+ );
147
+
148
+ expect(result.key).toBe("updated-key");
149
+ expect(result.version).toBe(attributeGroup.version + 1);
150
+ });
151
+
152
+ test("update attribute group - setAttributes", () => {
153
+ const draft: AttributeGroupDraft = {
154
+ name: { "en-US": "Attributes Test" },
155
+ key: "attributes-test",
156
+ attributes: [
157
+ {
158
+ key: "original-attribute",
159
+ },
160
+ ],
161
+ };
162
+
163
+ const ctx = { projectKey: "dummy" };
164
+ const attributeGroup = repository.create(ctx, draft);
165
+
166
+ const result = repository.processUpdateActions(
167
+ ctx,
168
+ attributeGroup,
169
+ attributeGroup.version,
170
+ [
171
+ {
172
+ action: "setAttributes",
173
+ attributes: [
174
+ {
175
+ key: "new-attribute-1",
176
+ },
177
+ {
178
+ key: "new-attribute-2",
179
+ },
180
+ ],
181
+ } as AttributeGroupSetAttributesAction,
182
+ ],
183
+ );
184
+
185
+ expect(result.attributes).toEqual([
186
+ { key: "new-attribute-1" },
187
+ { key: "new-attribute-2" },
188
+ ]);
189
+ expect(result.version).toBe(attributeGroup.version + 1);
190
+ });
191
+
192
+ test("get and delete attribute group", () => {
193
+ const draft: AttributeGroupDraft = {
194
+ name: { "en-US": "Delete Test Attributes" },
195
+ key: "delete-test",
196
+ attributes: [],
197
+ };
198
+
199
+ const ctx = { projectKey: "dummy" };
200
+ const attributeGroup = repository.create(ctx, draft);
201
+
202
+ // Test get
203
+ const retrieved = repository.get(ctx, attributeGroup.id);
204
+ expect(retrieved).toBeDefined();
205
+ expect(retrieved?.id).toBe(attributeGroup.id);
206
+
207
+ // Test getByKey
208
+ const retrievedByKey = repository.getByKey(ctx, attributeGroup.key!);
209
+ expect(retrievedByKey).toBeDefined();
210
+ expect(retrievedByKey?.key).toBe(attributeGroup.key);
211
+
212
+ // Test delete
213
+ const deleted = repository.delete(ctx, attributeGroup.id);
214
+ expect(deleted).toBeDefined();
215
+ expect(deleted?.id).toBe(attributeGroup.id);
216
+
217
+ // Verify it's deleted
218
+ const notFound = repository.get(ctx, attributeGroup.id);
219
+ expect(notFound).toBeNull();
220
+ });
221
+ });