@labdigital/commercetools-mock 2.45.1 → 2.46.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@labdigital/commercetools-mock",
3
- "version": "2.45.1",
3
+ "version": "2.46.0",
4
4
  "license": "MIT",
5
5
  "author": "Michael van Tellingen",
6
6
  "type": "module",
@@ -13,8 +13,12 @@ describe("Predicate filter", () => {
13
13
  nested: {
14
14
  numberProperty: 1234,
15
15
  objectProperty: {
16
- stringProperty: "foobar",
17
- booleanProperty: true,
16
+ "stringProperty": "foobar",
17
+ "booleanProperty": true,
18
+ "45c652f2-76e8-48fd-ab64-d11ad99d6631": {
19
+ stringProperty: "foobar",
20
+ uuidProperty: "3a57cc78-db08-4cd3-b778-d59b3326c435",
21
+ },
18
22
  },
19
23
  array: [
20
24
  {
@@ -331,6 +335,33 @@ describe("Predicate filter", () => {
331
335
  );
332
336
  expect(() => match(`stringProperty`)).toThrow(PredicateError);
333
337
  });
338
+
339
+ test("uuid as field name", async () => {
340
+ expect(
341
+ match(
342
+ `nested(objectProperty(45c652f2-76e8-48fd-ab64-d11ad99d6631(stringProperty = "foobar")))`,
343
+ ),
344
+ ).toBeTruthy();
345
+
346
+ expect(
347
+ match(
348
+ `nested(objectProperty(3a57cc78-db08-4cd3-b778-d59b3326c435(stringProperty = "foobar")))`,
349
+ ),
350
+ ).toBeFalsy();
351
+ });
352
+
353
+ test("uuid as value", async () => {
354
+ expect(
355
+ match(
356
+ `nested(objectProperty(45c652f2-76e8-48fd-ab64-d11ad99d6631(uuidProperty = "3a57cc78-db08-4cd3-b778-d59b3326c435")))`,
357
+ ),
358
+ ).toBeTruthy();
359
+ expect(
360
+ match(
361
+ `nested(objectProperty(45c652f2-76e8-48fd-ab64-d11ad99d6631(uuidProperty = "45c652f2-76e8-48fd-ab64-d11ad99d6631")))`,
362
+ ),
363
+ ).toBeFalsy();
364
+ });
334
365
  });
335
366
 
336
367
  describe("Report parse errors", () => {
@@ -124,6 +124,12 @@ const getLexer = (value: string) =>
124
124
  .token("IS", /is(?![-_a-z0-9]+)/i)
125
125
  .token("DEFINED", /defined(?![-_a-z0-9]+)/i)
126
126
 
127
+ // Special case for UUID identifiers,
128
+ // since they otherwise would get matched as INT, when starting with a digit
129
+ .token(
130
+ "IDENTIFIER",
131
+ /[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}/,
132
+ )
127
133
  .token("FLOAT", /\d+\.\d+/)
128
134
  .token("INT", /\d+/)
129
135
  .token("VARIABLE", /:([-_A-Za-z0-9]+)/)
@@ -13,6 +13,12 @@ import { validateSearchQuery } from "./lib/searchQueryTypeChecker";
13
13
  import { applyPriceSelector } from "./priceSelector";
14
14
  import type { AbstractStorage } from "./storage";
15
15
 
16
+ interface ProductSearchVariantAvailability {
17
+ isOnStock: boolean;
18
+ availableQuantity: number;
19
+ isOnStockForChannel: string | undefined;
20
+ }
21
+
16
22
  export class ProductSearch {
17
23
  protected _storage: AbstractStorage;
18
24
 
@@ -24,10 +30,32 @@ export class ProductSearch {
24
30
  projectKey: string,
25
31
  params: ProductSearchRequest,
26
32
  ): ProductPagedSearchResponse {
27
- let resources = this._storage
33
+ const availabilityBySku = this._storage
34
+ .all(projectKey, "inventory-entry")
35
+ .reduce((acc, entry) => {
36
+ const existingEntry = acc.get(entry.sku);
37
+
38
+ acc.set(entry.sku, {
39
+ isOnStock: existingEntry?.isOnStock || entry.quantityOnStock > 0,
40
+ availableQuantity:
41
+ existingEntry?.availableQuantity ?? 0 + entry.quantityOnStock,
42
+ // NOTE: This doesn't handle inventory entries for multiple channels,
43
+ // so it doesn't exactly replicate the behavior of the commercetools api.
44
+ isOnStockForChannel:
45
+ existingEntry?.isOnStockForChannel ?? entry.supplyChannel?.id,
46
+ });
47
+
48
+ return acc;
49
+ }, new Map<string, ProductSearchVariantAvailability>());
50
+
51
+ let productResources = this._storage
28
52
  .all(projectKey, "product")
29
53
  .map((r) =>
30
- this.transform(r, params.productProjectionParameters?.staged ?? false),
54
+ this.transformProduct(
55
+ r,
56
+ params.productProjectionParameters?.staged ?? false,
57
+ availabilityBySku,
58
+ ),
31
59
  )
32
60
  .filter((p) => {
33
61
  if (!(params.productProjectionParameters?.staged ?? false)) {
@@ -46,7 +74,7 @@ export class ProductSearch {
46
74
  const matchFunc = parseSearchQuery(params.query);
47
75
 
48
76
  // Filters can modify the output. So clone the resources first.
49
- resources = resources.filter((resource) =>
77
+ productResources = productResources.filter((resource) =>
50
78
  matchFunc(resource, markMatchingVariant),
51
79
  );
52
80
  } catch (err) {
@@ -63,7 +91,7 @@ export class ProductSearch {
63
91
 
64
92
  // Apply the priceSelector
65
93
  if (params.productProjectionParameters) {
66
- applyPriceSelector(resources, {
94
+ applyPriceSelector(productResources, {
67
95
  country: params.productProjectionParameters.priceCountry,
68
96
  channel: params.productProjectionParameters.priceChannel,
69
97
  customerGroup: params.productProjectionParameters.priceCustomerGroup,
@@ -76,7 +104,10 @@ export class ProductSearch {
76
104
 
77
105
  const offset = params.offset || 0;
78
106
  const limit = params.limit || 20;
79
- const productProjectionsResult = resources.slice(offset, offset + limit);
107
+ const productProjectionsResult = productResources.slice(
108
+ offset,
109
+ offset + limit,
110
+ );
80
111
 
81
112
  /**
82
113
  * Do not supply productProjection if productProjectionParameters are not given
@@ -100,7 +131,7 @@ export class ProductSearch {
100
131
  );
101
132
 
102
133
  return {
103
- total: resources.length,
134
+ total: productResources.length,
104
135
  offset: offset,
105
136
  limit: limit,
106
137
  results: results,
@@ -108,7 +139,11 @@ export class ProductSearch {
108
139
  };
109
140
  }
110
141
 
111
- transform(product: Product, staged: boolean): ProductProjection {
142
+ transformProduct(
143
+ product: Product,
144
+ staged: boolean,
145
+ availabilityBySku: Map<string, ProductSearchVariantAvailability>,
146
+ ): ProductProjection {
112
147
  const obj = !staged
113
148
  ? product.masterData.current
114
149
  : product.masterData.staged;
@@ -125,7 +160,12 @@ export class ProductSearch {
125
160
  slug: obj.slug,
126
161
  categories: obj.categories,
127
162
  masterVariant: obj.masterVariant,
128
- variants: obj.variants,
163
+ variants: obj.variants.map((variant) => ({
164
+ ...variant,
165
+ availability: variant.sku
166
+ ? availabilityBySku.get(variant.sku)
167
+ : { isOnStock: false, availableQuantity: 0, isOnStockForChannel: [] },
168
+ })),
129
169
  productType: product.productType,
130
170
  hasStagedChanges: product.masterData.hasStagedChanges,
131
171
  published: product.masterData.published,
@@ -1,3 +1,4 @@
1
+ import type { InvalidOperationError } from "@commercetools/platform-sdk";
1
2
  import {
2
3
  type Cart,
3
4
  type CartDraft,
@@ -27,6 +28,21 @@ export class CartRepository extends AbstractResourceRepository<"cart"> {
27
28
  }
28
29
 
29
30
  create(context: RepositoryContext, draft: CartDraft): Cart {
31
+ if (draft.anonymousId && draft.customerId) {
32
+ throw new CommercetoolsError<InvalidOperationError>({
33
+ code: "InvalidOperation",
34
+ message: "Can set only one of customer OR anonymousId",
35
+ });
36
+ }
37
+
38
+ // Validate that the customer exists
39
+ if (draft.customerId) {
40
+ this._storage.getByResourceIdentifier(context.projectKey, {
41
+ typeId: "customer",
42
+ id: draft.customerId,
43
+ });
44
+ }
45
+
30
46
  const lineItems =
31
47
  draft.lineItems?.map((draftLineItem) =>
32
48
  this.draftLineItemtoLineItem(
@@ -45,6 +61,7 @@ export class CartRepository extends AbstractResourceRepository<"cart"> {
45
61
  : undefined,
46
62
  cartState: "Active",
47
63
  country: draft.country,
64
+ customerId: draft.customerId,
48
65
  customerEmail: draft.customerEmail,
49
66
  customLineItems: [],
50
67
  directDiscounts: [],
@@ -13,6 +13,7 @@ import type {
13
13
  import assert from "assert";
14
14
  import supertest from "supertest";
15
15
  import { afterEach, beforeEach, describe, expect, test } from "vitest";
16
+ import { customerDraftFactory } from "~src/testing/customer";
16
17
  import { CommercetoolsMock } from "../index";
17
18
 
18
19
  describe("Carts Query", () => {
@@ -81,6 +82,30 @@ describe("Carts Query", () => {
81
82
  expect(myCart.custom?.type.id).toBe(myCart.custom?.type.obj?.id);
82
83
  expect(myCart.custom?.type.obj?.description?.en).toBe("Test Type");
83
84
  });
85
+
86
+ test("throw error when anonymousId and customerId are given", async () => {
87
+ const customerId = "400be09e-bfe8-4925-a307-4ef6280b063e";
88
+ const anonymousId = "a99f27d1-7e7e-4592-8d5a-aa5da1adfe24";
89
+ const response = await supertest(ctMock.app).post("/dummy/carts").send({
90
+ currency: "EUR",
91
+ anonymousId,
92
+ customerId,
93
+ });
94
+ expect(response.status).toBe(400);
95
+ expect(response.body.message).toBe(
96
+ "Can set only one of customer OR anonymousId",
97
+ );
98
+ });
99
+
100
+ test("create cart with existing customer", async () => {
101
+ const customer = await customerDraftFactory(ctMock).create();
102
+ const response = await supertest(ctMock.app).post("/dummy/carts").send({
103
+ currency: "EUR",
104
+ customerId: customer.id,
105
+ });
106
+ expect(response.status).toBe(201);
107
+ expect(response.body.customerId).toBe(customer.id);
108
+ });
84
109
  });
85
110
 
86
111
  describe("Cart Update Actions", () => {
@@ -4,60 +4,21 @@ import type {
4
4
  CustomerToken,
5
5
  } from "@commercetools/platform-sdk";
6
6
  import assert from "assert";
7
- import { Factory } from "fishery";
8
7
  import supertest from "supertest";
9
8
  import { afterEach, beforeEach, describe, expect, test } from "vitest";
10
9
  import { hashPassword } from "~src/lib/password";
10
+ import { customerDraftFactory } from "~src/testing/customer";
11
11
  import { CommercetoolsMock, getBaseResourceProperties } from "../index";
12
12
 
13
13
  const ctMock = new CommercetoolsMock();
14
14
 
15
- const customerDraftFactory = Factory.define<
16
- CustomerDraft,
17
- CustomerDraft,
18
- Customer
19
- >(({ onCreate }) => {
20
- onCreate(async (draft) => {
21
- const response = await supertest(ctMock.app)
22
- .post(`/dummy/customers`)
23
- .send(draft);
24
-
25
- return response.body.customer;
26
- });
27
-
28
- return {
29
- email: "customer@example.com",
30
- firstName: "John",
31
- lastName: "Doe",
32
- locale: "nl-NL",
33
- password: "my-secret-pw",
34
- addresses: [
35
- {
36
- firstName: "John",
37
- lastName: "Doe",
38
- streetName: "Street name",
39
- streetNumber: "42",
40
- postalCode: "1234 AB",
41
- city: "Utrecht",
42
- country: "NL",
43
- company: "Lab Digital",
44
- phone: "+31612345678",
45
- email: "customer@example.com",
46
- },
47
- ],
48
- isEmailVerified: false,
49
- stores: [],
50
- authenticationMode: "Password",
51
- };
52
- });
53
-
54
15
  afterEach(() => {
55
16
  ctMock.clear();
56
17
  });
57
18
 
58
19
  describe("Customer create", () => {
59
20
  test("create new customer", async () => {
60
- const draft = customerDraftFactory.build();
21
+ const draft = customerDraftFactory(ctMock).build();
61
22
 
62
23
  const response = await supertest(ctMock.app)
63
24
  .post(`/dummy/customers`)
@@ -109,7 +70,7 @@ describe("Customer create", () => {
109
70
 
110
71
  describe("Customer Update Actions", () => {
111
72
  test("addAddress", async () => {
112
- const customer = await customerDraftFactory.create();
73
+ const customer = await customerDraftFactory(ctMock).create();
113
74
  const response = await supertest(ctMock.app)
114
75
  .post(`/dummy/customers/${customer.id}`)
115
76
  .send({
@@ -134,7 +95,7 @@ describe("Customer Update Actions", () => {
134
95
  });
135
96
 
136
97
  test("removeAddress by ID", async () => {
137
- const customer = await customerDraftFactory.create({
98
+ const customer = await customerDraftFactory(ctMock).create({
138
99
  addresses: [
139
100
  {
140
101
  key: "address-key",
@@ -165,7 +126,7 @@ describe("Customer Update Actions", () => {
165
126
  });
166
127
 
167
128
  test("removeAddress by Key", async () => {
168
- const customer = await customerDraftFactory.create({
129
+ const customer = await customerDraftFactory(ctMock).create({
169
130
  addresses: [
170
131
  {
171
132
  key: "address-key",
@@ -196,7 +157,7 @@ describe("Customer Update Actions", () => {
196
157
  });
197
158
 
198
159
  test("changeAddress by ID", async () => {
199
- const customer = await customerDraftFactory.create({
160
+ const customer = await customerDraftFactory(ctMock).create({
200
161
  addresses: [
201
162
  {
202
163
  key: "address-key",
@@ -248,7 +209,7 @@ describe("Customer Update Actions", () => {
248
209
  });
249
210
 
250
211
  test("addBillingAddressId", async () => {
251
- const customer = await customerDraftFactory.create({
212
+ const customer = await customerDraftFactory(ctMock).create({
252
213
  addresses: [
253
214
  {
254
215
  key: "address-key",
@@ -280,7 +241,7 @@ describe("Customer Update Actions", () => {
280
241
  });
281
242
 
282
243
  test("removeBillingAddressId", async () => {
283
- const customer = await customerDraftFactory.create({
244
+ const customer = await customerDraftFactory(ctMock).create({
284
245
  addresses: [
285
246
  {
286
247
  key: "address-key",
@@ -318,7 +279,7 @@ describe("Customer Update Actions", () => {
318
279
  });
319
280
 
320
281
  test("setDefaultBillingAddress by ID", async () => {
321
- const customer = await customerDraftFactory.create({
282
+ const customer = await customerDraftFactory(ctMock).create({
322
283
  defaultBillingAddress: undefined,
323
284
  defaultShippingAddress: undefined,
324
285
  addresses: [
@@ -356,7 +317,7 @@ describe("Customer Update Actions", () => {
356
317
  });
357
318
 
358
319
  test("addShippingAddressId", async () => {
359
- const customer = await customerDraftFactory.create({
320
+ const customer = await customerDraftFactory(ctMock).create({
360
321
  addresses: [
361
322
  {
362
323
  key: "address-key",
@@ -387,7 +348,7 @@ describe("Customer Update Actions", () => {
387
348
  });
388
349
 
389
350
  test("removeShippingAddressId", async () => {
390
- const customer = await customerDraftFactory.create({
351
+ const customer = await customerDraftFactory(ctMock).create({
391
352
  addresses: [
392
353
  {
393
354
  key: "address-key",
@@ -425,7 +386,7 @@ describe("Customer Update Actions", () => {
425
386
  });
426
387
 
427
388
  test("setDefaultShippingAddress by ID", async () => {
428
- const customer = await customerDraftFactory.create({
389
+ const customer = await customerDraftFactory(ctMock).create({
429
390
  defaultBillingAddress: undefined,
430
391
  defaultShippingAddress: undefined,
431
392
  addresses: [
@@ -2,6 +2,7 @@ import type {
2
2
  Category,
3
3
  CategoryDraft,
4
4
  Image,
5
+ InventoryEntryDraft,
5
6
  PriceDraft,
6
7
  Product,
7
8
  ProductData,
@@ -20,7 +21,14 @@ import type {
20
21
  } from "@commercetools/platform-sdk";
21
22
  import assert from "assert";
22
23
  import supertest from "supertest";
23
- import { beforeAll, beforeEach, describe, expect, test } from "vitest";
24
+ import {
25
+ afterAll,
26
+ beforeAll,
27
+ beforeEach,
28
+ describe,
29
+ expect,
30
+ test,
31
+ } from "vitest";
24
32
  import { CommercetoolsMock } from "../index";
25
33
 
26
34
  const productTypeDraft: ProductTypeDraft = {
@@ -492,6 +500,10 @@ describe("Product update actions", () => {
492
500
  expect(response.status).toBe(201);
493
501
  });
494
502
 
503
+ afterAll(async () => {
504
+ ctMock.clear();
505
+ });
506
+
495
507
  test("setAttribute masterVariant (staged)", async () => {
496
508
  assert(productPublished, "product not created");
497
509
 
@@ -1488,76 +1500,148 @@ describe("Product update actions", () => {
1488
1500
  ?.myCustomField,
1489
1501
  ).toBe("MyRandomValue");
1490
1502
  });
1503
+ });
1491
1504
 
1492
- // Test the general product search implementation
1493
- describe("Product Search - Generic", () => {
1494
- test("Pagination", async () => {
1495
- {
1496
- const body: ProductSearchRequest = {
1497
- productProjectionParameters: {
1498
- storeProjection: "dummy-store",
1499
- localeProjection: ["en-US"],
1500
- priceCurrency: "EUR",
1501
- priceChannel: "dummy-channel",
1502
- expand: ["categories[*]", "categories[*].ancestors[*]"],
1503
- },
1504
- limit: 24,
1505
- };
1506
- const response = await supertest(ctMock.app)
1507
- .post("/dummy/products/search")
1508
- .send(body);
1509
-
1510
- const pagedSearchResponse: ProductPagedSearchResponse = response.body;
1511
- expect(pagedSearchResponse.limit).toBe(24);
1512
- expect(pagedSearchResponse.offset).toBe(0);
1513
- expect(pagedSearchResponse.total).toBeGreaterThan(0);
1514
-
1515
- // Deliberately not supported fow now
1516
- expect(pagedSearchResponse.facets).toEqual([]);
1517
-
1518
- const results: ProductSearchResult[] = pagedSearchResponse.results;
1519
- expect(results).toBeDefined();
1520
- expect(results.length).toBeGreaterThan(0);
1521
-
1522
- // Find product with sku "1337" to be part of the search results
1523
- const productFound = results.find(
1524
- (result) => result?.productProjection?.masterVariant?.sku === "1337",
1525
- );
1526
- expect(productFound).toBeDefined();
1527
-
1528
- const priceCurrencyMatch = results.find((result) =>
1529
- result?.productProjection?.masterVariant?.prices?.find(
1530
- (price) => price?.value?.currencyCode === "EUR",
1531
- ),
1532
- );
1533
- expect(priceCurrencyMatch).toBeDefined();
1534
- }
1535
- {
1536
- const body: ProductSearchRequest = {
1537
- limit: 88,
1538
- offset: 88,
1539
- };
1540
-
1541
- const response = await supertest(ctMock.app)
1542
- .post("/dummy/products/search")
1543
- .send(body);
1544
-
1545
- const pagedSearchResponse: ProductPagedSearchResponse = response.body;
1546
- expect(pagedSearchResponse.limit).toBe(88);
1547
- expect(pagedSearchResponse.offset).toBe(88);
1548
- expect(pagedSearchResponse.total).toBeGreaterThan(0);
1549
-
1550
- // No results, since we start at offset 88
1551
- const results: ProductSearchResult[] = pagedSearchResponse.results;
1552
- expect(results).toBeDefined();
1553
- expect(results.length).toBe(0);
1554
-
1555
- // Product with sku "1337" should not be part of the results
1556
- const productFound = results.find(
1557
- (result) => result?.productProjection?.masterVariant?.sku === "1337",
1558
- );
1559
- expect(productFound).toBeUndefined();
1560
- }
1505
+ // Test the general product search implementation
1506
+ describe("Product Search - Generic", () => {
1507
+ const ctMock = new CommercetoolsMock();
1508
+
1509
+ async function addInventoryEntry(sku: string, quantity: number) {
1510
+ const inventoryEntryDraft: InventoryEntryDraft = {
1511
+ key: `${sku}_stock`,
1512
+ sku,
1513
+ quantityOnStock: quantity,
1514
+ supplyChannel: {
1515
+ typeId: "channel",
1516
+ id: "dummy-inventory-channel",
1517
+ },
1518
+ };
1519
+
1520
+ await supertest(ctMock.app)
1521
+ .post("/dummy/inventory")
1522
+ .send(inventoryEntryDraft);
1523
+ }
1524
+
1525
+ beforeAll(async () => {
1526
+ await beforeAllProductTests(ctMock);
1527
+
1528
+ new Array(24).fill(null).forEach(async () => {
1529
+ const response = await supertest(ctMock.app)
1530
+ .post("/dummy/products")
1531
+ .send(publishedProductDraft);
1532
+
1533
+ expect(response.status).toBe(201);
1561
1534
  });
1562
1535
  });
1536
+
1537
+ test("Pagination", async () => {
1538
+ {
1539
+ const body: ProductSearchRequest = {
1540
+ productProjectionParameters: {
1541
+ storeProjection: "dummy-store",
1542
+ localeProjection: ["en-US"],
1543
+ priceCurrency: "EUR",
1544
+ priceChannel: "dummy-channel",
1545
+ expand: ["categories[*]", "categories[*].ancestors[*]"],
1546
+ },
1547
+ limit: 24,
1548
+ };
1549
+ const response = await supertest(ctMock.app)
1550
+ .post("/dummy/products/search")
1551
+ .send(body);
1552
+
1553
+ const pagedSearchResponse: ProductPagedSearchResponse = response.body;
1554
+ expect(pagedSearchResponse.limit).toBe(24);
1555
+ expect(pagedSearchResponse.offset).toBe(0);
1556
+ expect(pagedSearchResponse.total).toBeGreaterThan(0);
1557
+
1558
+ // Deliberately not supported fow now
1559
+ expect(pagedSearchResponse.facets).toEqual([]);
1560
+
1561
+ const results: ProductSearchResult[] = pagedSearchResponse.results;
1562
+ expect(results).toBeDefined();
1563
+ expect(results.length).toBeGreaterThan(0);
1564
+
1565
+ // Find product with sku "1337" to be part of the search results
1566
+ const productFound = results.find(
1567
+ (result) => result?.productProjection?.masterVariant?.sku === "1337",
1568
+ );
1569
+ expect(productFound).toBeDefined();
1570
+
1571
+ const priceCurrencyMatch = results.find((result) =>
1572
+ result?.productProjection?.masterVariant?.prices?.find(
1573
+ (price) => price?.value?.currencyCode === "EUR",
1574
+ ),
1575
+ );
1576
+ expect(priceCurrencyMatch).toBeDefined();
1577
+ }
1578
+ {
1579
+ const body: ProductSearchRequest = {
1580
+ limit: 88,
1581
+ offset: 88,
1582
+ };
1583
+
1584
+ const response = await supertest(ctMock.app)
1585
+ .post("/dummy/products/search")
1586
+ .send(body);
1587
+
1588
+ const pagedSearchResponse: ProductPagedSearchResponse = response.body;
1589
+ expect(pagedSearchResponse.limit).toBe(88);
1590
+ expect(pagedSearchResponse.offset).toBe(88);
1591
+ expect(pagedSearchResponse.total).toBeGreaterThan(0);
1592
+
1593
+ // No results, since we start at offset 88
1594
+ const results: ProductSearchResult[] = pagedSearchResponse.results;
1595
+ expect(results).toBeDefined();
1596
+ expect(results.length).toBe(0);
1597
+
1598
+ // Product with sku "1337" should not be part of the results
1599
+ const productFound = results.find(
1600
+ (result) => result?.productProjection?.masterVariant?.sku === "1337",
1601
+ );
1602
+ expect(productFound).toBeUndefined();
1603
+ }
1604
+ });
1605
+
1606
+ test("Filter on inventory", async () => {
1607
+ const body: ProductSearchRequest = {
1608
+ query: {
1609
+ exact: {
1610
+ field: "variants.availability.isOnStockForChannel",
1611
+ value: "dummy-inventory-channel",
1612
+ },
1613
+ },
1614
+ productProjectionParameters: {
1615
+ storeProjection: "dummy-store",
1616
+ localeProjection: ["en-US"],
1617
+ priceCurrency: "EUR",
1618
+ priceChannel: "dummy-channel",
1619
+ },
1620
+ limit: 1,
1621
+ };
1622
+
1623
+ const response1 = await supertest(ctMock.app)
1624
+ .post("/dummy/products/search")
1625
+ .send(body);
1626
+
1627
+ const pagedSearchResponse1: ProductPagedSearchResponse = response1.body;
1628
+
1629
+ expect(pagedSearchResponse1.results.length).toBe(0);
1630
+
1631
+ await addInventoryEntry(
1632
+ publishedProductDraft.variants?.[0]?.sku as string,
1633
+ 10,
1634
+ );
1635
+
1636
+ const response2 = await supertest(ctMock.app)
1637
+ .post("/dummy/products/search")
1638
+ .send(body);
1639
+
1640
+ const pagedSearchResponse2: ProductPagedSearchResponse = response2.body;
1641
+
1642
+ const productFound = pagedSearchResponse2.results.find(
1643
+ (result) => result?.productProjection?.masterVariant?.sku === "1337",
1644
+ );
1645
+ expect(productFound).toBeDefined();
1646
+ });
1563
1647
  });