From 68d869607f5420c976e6b813069f8340c8b0dc1e Mon Sep 17 00:00:00 2001 From: Stevche Radevski Date: Thu, 14 Mar 2024 22:01:50 +0100 Subject: [PATCH] chore: Use module test runner in all modules (#6706) I replaced the custom test setup with the test runner. This should make migrating to mikroorm 6 a lot easier once we do it. There are few more modules to be done, but I thought the PR is super big already so we can tackle them separately. Note that there are no or very little test changes, it is mostly the setup around the tests. --- .../services/auth-user/index.spec.ts | 440 +- .../services/module/auth-user.spec.ts | 452 +- .../services/module/providers.spec.ts | 87 +- .../providers/username-password.spec.ts | 252 +- packages/auth/integration-tests/setup-env.js | 6 - packages/auth/integration-tests/setup.js | 3 - .../auth/integration-tests/utils/config.ts | 6 - .../auth/integration-tests/utils/database.ts | 18 - .../utils/get-init-module-config.ts | 42 - .../auth/integration-tests/utils/index.ts | 2 - packages/auth/jest.config.js | 2 - .../services/cart-module/index.spec.ts | 4406 ++++---- packages/cart/integration-tests/setup-env.js | 6 - packages/cart/integration-tests/setup.js | 3 - .../cart/integration-tests/utils/config.ts | 6 - .../cart/integration-tests/utils/database.ts | 18 - .../utils/get-init-module-config.ts | 33 - .../cart/integration-tests/utils/index.ts | 2 - packages/cart/jest.config.js | 2 - .../services/customer-module/index.spec.ts | 2178 ++-- .../customer/integration-tests/setup-env.js | 6 - packages/customer/integration-tests/setup.js | 3 - .../integration-tests/utils/config.ts | 6 - .../integration-tests/utils/database.ts | 18 - .../utils/get-init-module-config.ts | 33 - .../customer/integration-tests/utils/index.ts | 2 - packages/customer/jest.config.js | 2 - .../services/money-amount/index.spec.ts | 353 - .../services/price-list-rule/index.spec.ts | 243 - .../services/price-list/index.spec.ts | 230 - .../services/price-rule/index.spec.ts | 331 - .../index.spec.ts | 289 - .../services/price-set/index.spec.ts | 392 - .../pricing-module/calculate-price.spec.ts | 3809 ++++--- .../pricing-module/money-amount.spec.ts | 759 +- .../pricing-module/price-list-rule.spec.ts | 598 +- .../pricing-module/price-list.spec.ts | 1632 ++- .../pricing-module/price-rule.spec.ts | 594 +- .../price-set-money-amount-rules.spec.ts | 495 +- .../services/pricing-module/price-set.spec.ts | 1348 ++- .../services/pricing-module/rule-type.spec.ts | 476 +- .../services/rule-type/index.spec.ts | 274 - .../pricing/integration-tests/setup-env.js | 6 - packages/pricing/integration-tests/setup.js | 3 - .../pricing/integration-tests/utils/config.ts | 6 - .../integration-tests/utils/database.ts | 18 - .../utils/get-init-module-config.ts | 31 - .../pricing/integration-tests/utils/index.ts | 1 - packages/pricing/jest.config.js | 2 - .../__fixtures__/event-bus/index.ts | 43 - .../integration-tests/__tests__/module.ts | 482 - .../services/product-category/index.ts | 1560 ++- .../services/product-collection/index.ts | 697 +- .../product-categories.spec.ts | 1136 +- .../product-collections.spec.ts | 837 +- .../product-options.spec.ts | 588 +- .../product-tags.spec.ts | 549 +- .../product-types.spec.ts | 486 +- .../product-variants.spec.ts | 314 +- .../product-module-service/products.spec.ts | 1576 ++- .../services/product-option/index.ts | 684 +- .../__tests__/services/product-tag/index.ts | 584 +- .../__tests__/services/product-type/index.ts | 523 +- .../services/product-variant/index.ts | 562 +- .../__tests__/services/product/index.ts | 1190 ++- .../product/integration-tests/setup-env.js | 6 - packages/product/integration-tests/setup.js | 22 - .../product/integration-tests/utils/config.ts | 6 - .../integration-tests/utils/database.ts | 95 - .../utils/get-init-module-config.ts | 40 - .../product/integration-tests/utils/index.ts | 2 - packages/product/jest.config.js | 2 - .../src/services/product-module-service.ts | 1 - .../promotion-module/campaign.spec.ts | 800 +- .../promotion-module/compute-actions.spec.ts | 9434 ++++++++--------- .../promotion-module/promotion.spec.ts | 2820 +++-- .../promotion-module/register-usage.spec.ts | 344 +- .../services/promotion/index.spec.ts | 111 +- .../promotion/integration-tests/setup-env.js | 6 - packages/promotion/integration-tests/setup.js | 3 - .../integration-tests/utils/config.ts | 6 - .../integration-tests/utils/database.ts | 18 - .../utils/get-init-module-config.ts | 33 - .../integration-tests/utils/index.ts | 2 - packages/promotion/jest.config.js | 2 - .../__tests__/region-module.spec.ts | 663 +- .../region/integration-tests/setup-env.js | 8 - packages/region/integration-tests/setup.js | 3 - .../region/integration-tests/utils/config.ts | 6 - .../integration-tests/utils/database.ts | 18 - .../utils/get-init-module-config.ts | 33 - .../region/integration-tests/utils/index.ts | 3 - packages/region/jest.config.js | 2 - .../__tests__/services/module/invite.spec.ts | 453 +- .../__tests__/services/module/user.spec.ts | 430 +- packages/user/integration-tests/setup-env.js | 6 - packages/user/integration-tests/setup.js | 3 - .../user/integration-tests/utils/config.ts | 6 - .../user/integration-tests/utils/database.ts | 18 - .../utils/get-init-module-config.ts | 37 - .../user/integration-tests/utils/index.ts | 2 - packages/user/jest.config.js | 2 - 102 files changed, 21700 insertions(+), 25481 deletions(-) delete mode 100644 packages/auth/integration-tests/setup-env.js delete mode 100644 packages/auth/integration-tests/setup.js delete mode 100644 packages/auth/integration-tests/utils/config.ts delete mode 100644 packages/auth/integration-tests/utils/database.ts delete mode 100644 packages/auth/integration-tests/utils/get-init-module-config.ts delete mode 100644 packages/auth/integration-tests/utils/index.ts delete mode 100644 packages/cart/integration-tests/setup-env.js delete mode 100644 packages/cart/integration-tests/setup.js delete mode 100644 packages/cart/integration-tests/utils/config.ts delete mode 100644 packages/cart/integration-tests/utils/database.ts delete mode 100644 packages/cart/integration-tests/utils/get-init-module-config.ts delete mode 100644 packages/cart/integration-tests/utils/index.ts delete mode 100644 packages/customer/integration-tests/setup-env.js delete mode 100644 packages/customer/integration-tests/setup.js delete mode 100644 packages/customer/integration-tests/utils/config.ts delete mode 100644 packages/customer/integration-tests/utils/database.ts delete mode 100644 packages/customer/integration-tests/utils/get-init-module-config.ts delete mode 100644 packages/customer/integration-tests/utils/index.ts delete mode 100644 packages/pricing/integration-tests/__tests__/services/money-amount/index.spec.ts delete mode 100644 packages/pricing/integration-tests/__tests__/services/price-list-rule/index.spec.ts delete mode 100644 packages/pricing/integration-tests/__tests__/services/price-list/index.spec.ts delete mode 100644 packages/pricing/integration-tests/__tests__/services/price-rule/index.spec.ts delete mode 100644 packages/pricing/integration-tests/__tests__/services/price-set-money-amonut-rules/index.spec.ts delete mode 100644 packages/pricing/integration-tests/__tests__/services/price-set/index.spec.ts delete mode 100644 packages/pricing/integration-tests/__tests__/services/rule-type/index.spec.ts delete mode 100644 packages/pricing/integration-tests/setup-env.js delete mode 100644 packages/pricing/integration-tests/setup.js delete mode 100644 packages/pricing/integration-tests/utils/config.ts delete mode 100644 packages/pricing/integration-tests/utils/database.ts delete mode 100644 packages/pricing/integration-tests/utils/get-init-module-config.ts delete mode 100644 packages/pricing/integration-tests/utils/index.ts delete mode 100644 packages/product/integration-tests/__fixtures__/event-bus/index.ts delete mode 100644 packages/product/integration-tests/__tests__/module.ts delete mode 100644 packages/product/integration-tests/setup-env.js delete mode 100644 packages/product/integration-tests/setup.js delete mode 100644 packages/product/integration-tests/utils/config.ts delete mode 100644 packages/product/integration-tests/utils/database.ts delete mode 100644 packages/product/integration-tests/utils/get-init-module-config.ts delete mode 100644 packages/product/integration-tests/utils/index.ts delete mode 100644 packages/promotion/integration-tests/setup-env.js delete mode 100644 packages/promotion/integration-tests/setup.js delete mode 100644 packages/promotion/integration-tests/utils/config.ts delete mode 100644 packages/promotion/integration-tests/utils/database.ts delete mode 100644 packages/promotion/integration-tests/utils/get-init-module-config.ts delete mode 100644 packages/promotion/integration-tests/utils/index.ts delete mode 100644 packages/region/integration-tests/setup-env.js delete mode 100644 packages/region/integration-tests/setup.js delete mode 100644 packages/region/integration-tests/utils/config.ts delete mode 100644 packages/region/integration-tests/utils/database.ts delete mode 100644 packages/region/integration-tests/utils/get-init-module-config.ts delete mode 100644 packages/region/integration-tests/utils/index.ts delete mode 100644 packages/user/integration-tests/setup-env.js delete mode 100644 packages/user/integration-tests/setup.js delete mode 100644 packages/user/integration-tests/utils/config.ts delete mode 100644 packages/user/integration-tests/utils/database.ts delete mode 100644 packages/user/integration-tests/utils/get-init-module-config.ts delete mode 100644 packages/user/integration-tests/utils/index.ts diff --git a/packages/auth/integration-tests/__tests__/services/auth-user/index.spec.ts b/packages/auth/integration-tests/__tests__/services/auth-user/index.spec.ts index 3aae9c7e48..2960932c22 100644 --- a/packages/auth/integration-tests/__tests__/services/auth-user/index.spec.ts +++ b/packages/auth/integration-tests/__tests__/services/auth-user/index.spec.ts @@ -1,245 +1,231 @@ -import { AuthUserService } from "@services" -import ContainerLoader from "../../../../src/loaders/container" -import { MikroOrmWrapper } from "../../../utils" -import { SqlEntityManager } from "@mikro-orm/postgresql" -import { asValue } from "awilix" import { createAuthUsers } from "../../../__fixtures__/auth-user" -import { createMedusaContainer } from "@medusajs/utils" +import { moduleIntegrationTestRunner, SuiteOptions } from "medusa-test-utils" +import { Modules } from "@medusajs/modules-sdk" +import { IAuthModuleService } from "@medusajs/types" jest.setTimeout(30000) -describe("AuthUser Service", () => { - let service: AuthUserService - let testManager: SqlEntityManager - let repositoryManager: SqlEntityManager - - beforeEach(async () => { - await MikroOrmWrapper.setupDatabase() - repositoryManager = await MikroOrmWrapper.forkManager() - testManager = await MikroOrmWrapper.forkManager() - - const container = createMedusaContainer() - container.register("manager", asValue(repositoryManager)) - - await ContainerLoader({ container }) - - service = container.resolve("authUserService") - - await createAuthUsers(testManager) - }) - - afterEach(async () => { - await MikroOrmWrapper.clearDatabase() - }) - - describe("list", () => { - it("should list authUsers", async () => { - const authUsers = await service.list() - const serialized = JSON.parse(JSON.stringify(authUsers)) - - expect(serialized).toEqual([ - expect.objectContaining({ - provider: "store", - }), - expect.objectContaining({ - provider: "manual", - }), - expect.objectContaining({ - provider: "manual", - }), - ]) - }) - - it("should list authUsers by id", async () => { - const authUsers = await service.list({ - id: ["test-id"], +moduleIntegrationTestRunner({ + moduleName: Modules.AUTH, + testSuite: ({ + MikroOrmWrapper, + service, + }: SuiteOptions) => { + describe("AuthUser Service", () => { + beforeEach(async () => { + await createAuthUsers(MikroOrmWrapper.forkManager()) }) - expect(authUsers).toEqual([ - expect.objectContaining({ - id: "test-id", - }), - ]) - }) + describe("list", () => { + it("should list authUsers", async () => { + const authUsers = await service.list() + const serialized = JSON.parse(JSON.stringify(authUsers)) - it("should list authUsers by provider_id", async () => { - const authUsers = await service.list({ - provider: "manual", - }) - - const serialized = JSON.parse(JSON.stringify(authUsers)) - - expect(serialized).toEqual([ - expect.objectContaining({ - id: "test-id", - }), - expect.objectContaining({ - id: "test-id-1", - }), - ]) - }) - }) - - describe("listAndCount", () => { - it("should list authUsers", async () => { - const [authUsers, count] = await service.listAndCount() - const serialized = JSON.parse(JSON.stringify(authUsers)) - - expect(count).toEqual(3) - expect(serialized).toEqual([ - expect.objectContaining({ - provider: "store", - }), - expect.objectContaining({ - provider: "manual", - }), - expect.objectContaining({ - provider: "manual", - }), - ]) - }) - - it("should listAndCount authUsers by provider_id", async () => { - const [authUsers, count] = await service.listAndCount({ - provider: "manual", - }) - - expect(count).toEqual(2) - expect(authUsers).toEqual([ - expect.objectContaining({ - id: "test-id", - }), - expect.objectContaining({ - id: "test-id-1", - }), - ]) - }) - }) - - describe("retrieve", () => { - const id = "test-id" - - it("should return an authUser for the given id", async () => { - const authUser = await service.retrieve(id) - - expect(authUser).toEqual( - expect.objectContaining({ - id, + expect(serialized).toEqual([ + expect.objectContaining({ + provider: "store", + }), + expect.objectContaining({ + provider: "manual", + }), + expect.objectContaining({ + provider: "manual", + }), + ]) }) - ) - }) - it("should return authUser based on config select param", async () => { - const authUser = await service.retrieve(id, { - select: ["id"], - }) + it("should list authUsers by id", async () => { + const authUsers = await service.list({ + id: ["test-id"], + }) - const serialized = JSON.parse(JSON.stringify(authUser)) - - expect(serialized).toEqual({ - id, - }) - }) - - it("should throw an error when an authUser with the given id does not exist", async () => { - let error - - try { - await service.retrieve("does-not-exist") - } catch (e) { - error = e - } - - expect(error.message).toEqual( - "AuthUser with id: does-not-exist was not found" - ) - }) - - it("should throw an error when a authUserId is not provided", async () => { - let error - - try { - await service.retrieve(undefined as unknown as string) - } catch (e) { - error = e - } - - expect(error.message).toEqual("authUser - id must be defined") - }) - }) - - describe("delete", () => { - it("should delete the authUsers given an id successfully", async () => { - const id = "test-id" - - await service.delete([id]) - - const authUsers = await service.list({ - id: [id], - }) - - expect(authUsers).toHaveLength(0) - }) - }) - - describe("update", () => { - it("should throw an error when a id does not exist", async () => { - let error - - try { - await service.update([ - { - id: "does-not-exist", - }, - ]) - } catch (e) { - error = e - } - - expect(error.message).toEqual( - 'AuthUser with id "does-not-exist" not found' - ) - }) - - it("should update authUser", async () => { - const id = "test-id" - - await service.update([ - { - id, - provider_metadata: { email: "test@email.com" }, - }, - ]) - - const [authUser] = await service.list({ id: [id] }) - expect(authUser).toEqual( - expect.objectContaining({ - provider_metadata: { email: "test@email.com" }, + expect(authUsers).toEqual([ + expect.objectContaining({ + id: "test-id", + }), + ]) }) - ) - }) - }) - describe("create", () => { - it("should create a authUser successfully", async () => { - await service.create([ - { - id: "test", - provider: "manual", - entity_id: "test", - scope: "store", - }, - ]) + it("should list authUsers by provider_id", async () => { + const authUsers = await service.list({ + provider: "manual", + }) - const [authUser] = await service.list({ - id: ["test"], + const serialized = JSON.parse(JSON.stringify(authUsers)) + + expect(serialized).toEqual([ + expect.objectContaining({ + id: "test-id", + }), + expect.objectContaining({ + id: "test-id-1", + }), + ]) + }) }) - expect(authUser).toEqual( - expect.objectContaining({ - id: "test", + describe("listAndCount", () => { + it("should list authUsers", async () => { + const [authUsers, count] = await service.listAndCount() + const serialized = JSON.parse(JSON.stringify(authUsers)) + + expect(count).toEqual(3) + expect(serialized).toEqual([ + expect.objectContaining({ + provider: "store", + }), + expect.objectContaining({ + provider: "manual", + }), + expect.objectContaining({ + provider: "manual", + }), + ]) }) - ) + + it("should listAndCount authUsers by provider_id", async () => { + const [authUsers, count] = await service.listAndCount({ + provider: "manual", + }) + + expect(count).toEqual(2) + expect(authUsers).toEqual([ + expect.objectContaining({ + id: "test-id", + }), + expect.objectContaining({ + id: "test-id-1", + }), + ]) + }) + }) + + describe("retrieve", () => { + const id = "test-id" + + it("should return an authUser for the given id", async () => { + const authUser = await service.retrieve(id) + + expect(authUser).toEqual( + expect.objectContaining({ + id, + }) + ) + }) + + it("should return authUser based on config select param", async () => { + const authUser = await service.retrieve(id, { + select: ["id"], + }) + + const serialized = JSON.parse(JSON.stringify(authUser)) + + expect(serialized).toEqual({ + id, + }) + }) + + it("should throw an error when an authUser with the given id does not exist", async () => { + let error + + try { + await service.retrieve("does-not-exist") + } catch (e) { + error = e + } + + expect(error.message).toEqual( + "AuthUser with id: does-not-exist was not found" + ) + }) + + it("should throw an error when a authUserId is not provided", async () => { + let error + + try { + await service.retrieve(undefined as unknown as string) + } catch (e) { + error = e + } + + expect(error.message).toEqual("authUser - id must be defined") + }) + }) + + describe("delete", () => { + it("should delete the authUsers given an id successfully", async () => { + const id = "test-id" + + await service.delete([id]) + + const authUsers = await service.list({ + id: [id], + }) + + expect(authUsers).toHaveLength(0) + }) + }) + + describe("update", () => { + it("should throw an error when a id does not exist", async () => { + let error + + try { + await service.update([ + { + id: "does-not-exist", + }, + ]) + } catch (e) { + error = e + } + + expect(error.message).toEqual( + 'AuthUser with id "does-not-exist" not found' + ) + }) + + it("should update authUser", async () => { + const id = "test-id" + + await service.update([ + { + id, + provider_metadata: { email: "test@email.com" }, + }, + ]) + + const [authUser] = await service.list({ id: [id] }) + expect(authUser).toEqual( + expect.objectContaining({ + provider_metadata: { email: "test@email.com" }, + }) + ) + }) + }) + + describe("create", () => { + it("should create a authUser successfully", async () => { + await service.create([ + { + id: "test", + provider: "manual", + entity_id: "test", + scope: "store", + }, + ]) + + const [authUser] = await service.list({ + id: ["test"], + }) + + expect(authUser).toEqual( + expect.objectContaining({ + id: "test", + }) + ) + }) + }) }) - }) + }, }) diff --git a/packages/auth/integration-tests/__tests__/services/module/auth-user.spec.ts b/packages/auth/integration-tests/__tests__/services/module/auth-user.spec.ts index 263ee6268f..176311b4ba 100644 --- a/packages/auth/integration-tests/__tests__/services/module/auth-user.spec.ts +++ b/packages/auth/integration-tests/__tests__/services/module/auth-user.spec.ts @@ -1,257 +1,237 @@ import { IAuthModuleService } from "@medusajs/types" -import { MikroOrmWrapper } from "../../../utils" import { Modules } from "@medusajs/modules-sdk" -import { SqlEntityManager } from "@mikro-orm/postgresql" import { createAuthUsers } from "../../../__fixtures__/auth-user" -import { getInitModuleConfig } from "../../../utils/get-init-module-config" -import { initModules } from "medusa-test-utils" +import { moduleIntegrationTestRunner, SuiteOptions } from "medusa-test-utils" jest.setTimeout(30000) -describe("AuthModuleService - AuthUser", () => { - let service: IAuthModuleService - let testManager: SqlEntityManager - let shutdownFunc: () => Promise - - beforeAll(async () => { - const initModulesConfig = getInitModuleConfig() - - const { medusaApp, shutdown } = await initModules(initModulesConfig) - - service = medusaApp.modules[Modules.AUTH] - - shutdownFunc = shutdown - }) - - beforeEach(async () => { - await MikroOrmWrapper.setupDatabase() - testManager = MikroOrmWrapper.forkManager() - - await createAuthUsers(testManager) - }) - - afterEach(async () => { - await MikroOrmWrapper.clearDatabase() - }) - - afterAll(async () => { - await shutdownFunc() - }) - - describe("listAuthUsers", () => { - it("should list authUsers", async () => { - const authUsers = await service.list() - - expect(authUsers).toEqual([ - expect.objectContaining({ - provider: "store", - }), - expect.objectContaining({ - provider: "manual", - }), - expect.objectContaining({ - provider: "manual", - }), - ]) - }) - - it("should list authUsers by id", async () => { - const authUsers = await service.list({ - id: ["test-id"], +moduleIntegrationTestRunner({ + moduleName: Modules.AUTH, + testSuite: ({ + MikroOrmWrapper, + service, + }: SuiteOptions) => { + describe("AuthModuleService - AuthUser", () => { + beforeEach(async () => { + await createAuthUsers(MikroOrmWrapper.forkManager()) }) - expect(authUsers).toEqual([ - expect.objectContaining({ - id: "test-id", - }), - ]) - }) + describe("listAuthUsers", () => { + it("should list authUsers", async () => { + const authUsers = await service.list() - it("should list authUsers by provider", async () => { - const authUsers = await service.list({ - provider: "manual", - }) - - expect(authUsers).toEqual([ - expect.objectContaining({ - id: "test-id", - }), - expect.objectContaining({ - id: "test-id-1", - }), - ]) - }) - }) - - describe("listAndCountAuthUsers", () => { - it("should list and count authUsers", async () => { - const [authUsers, count] = await service.listAndCount() - - expect(count).toEqual(3) - expect(authUsers).toEqual([ - expect.objectContaining({ - provider: "store", - }), - expect.objectContaining({ - provider: "manual", - }), - expect.objectContaining({ - provider: "manual", - }), - ]) - }) - - it("should listAndCount authUsers by provider_id", async () => { - const [authUsers, count] = await service.listAndCount({ - provider: "manual", - }) - - expect(count).toEqual(2) - expect(authUsers).toEqual([ - expect.objectContaining({ - id: "test-id", - }), - expect.objectContaining({ - id: "test-id-1", - }), - ]) - }) - }) - - describe("retrieveAuthUser", () => { - const id = "test-id" - - it("should return an authUser for the given id", async () => { - const authUser = await service.retrieve(id) - - expect(authUser).toEqual( - expect.objectContaining({ - id, + expect(authUsers).toEqual([ + expect.objectContaining({ + provider: "store", + }), + expect.objectContaining({ + provider: "manual", + }), + expect.objectContaining({ + provider: "manual", + }), + ]) }) - ) - }) - it("should throw an error when an authUser with the given id does not exist", async () => { - let error + it("should list authUsers by id", async () => { + const authUsers = await service.list({ + id: ["test-id"], + }) - try { - await service.retrieve("does-not-exist") - } catch (e) { - error = e - } - - expect(error.message).toEqual( - "AuthUser with id: does-not-exist was not found" - ) - }) - - it("should not return an authUser with password hash", async () => { - const authUser = await service.retrieve("test-id-1") - - expect(authUser).toEqual( - expect.objectContaining({ - id: "test-id-1", + expect(authUsers).toEqual([ + expect.objectContaining({ + id: "test-id", + }), + ]) }) - ) - expect(authUser["password_hash"]).toEqual(undefined) - }) - it("should throw an error when a authUserId is not provided", async () => { - let error + it("should list authUsers by provider", async () => { + const authUsers = await service.list({ + provider: "manual", + }) - try { - await service.retrieve(undefined as unknown as string) - } catch (e) { - error = e - } - - expect(error.message).toEqual("authUser - id must be defined") - }) - - it("should return authUser based on config select param", async () => { - const authUser = await service.retrieve(id, { - select: ["id"], - }) - - expect(authUser).toEqual({ - id, - }) - }) - }) - - describe("deleteAuthUser", () => { - const id = "test-id" - - it("should delete the authUsers given an id successfully", async () => { - await service.delete([id]) - - const authUsers = await service.list({ - id: [id], - }) - - expect(authUsers).toHaveLength(0) - }) - }) - - describe("updateAuthUser", () => { - const id = "test-id" - - it("should throw an error when a id does not exist", async () => { - let error - - try { - await service.update([ - { - id: "does-not-exist", - }, - ]) - } catch (e) { - error = e - } - - expect(error.message).toEqual( - 'AuthUser with id "does-not-exist" not found' - ) - }) - - it("should update authUser", async () => { - await service.update([ - { - id, - provider_metadata: { email: "test@email.com" }, - }, - ]) - - const [authUser] = await service.list({ id: [id] }) - expect(authUser).toEqual( - expect.objectContaining({ - provider_metadata: { email: "test@email.com" }, + expect(authUsers).toEqual([ + expect.objectContaining({ + id: "test-id", + }), + expect.objectContaining({ + id: "test-id-1", + }), + ]) }) - ) - }) - }) - - describe("createAuthUser", () => { - it("should create a authUser successfully", async () => { - await service.create([ - { - id: "test", - provider: "manual", - entity_id: "test", - scope: "store", - }, - ]) - - const [authUser, count] = await service.listAndCount({ - id: ["test"], }) - expect(count).toEqual(1) - expect(authUser[0]).toEqual( - expect.objectContaining({ - id: "test", + describe("listAndCountAuthUsers", () => { + it("should list and count authUsers", async () => { + const [authUsers, count] = await service.listAndCount() + + expect(count).toEqual(3) + expect(authUsers).toEqual([ + expect.objectContaining({ + provider: "store", + }), + expect.objectContaining({ + provider: "manual", + }), + expect.objectContaining({ + provider: "manual", + }), + ]) }) - ) + + it("should listAndCount authUsers by provider_id", async () => { + const [authUsers, count] = await service.listAndCount({ + provider: "manual", + }) + + expect(count).toEqual(2) + expect(authUsers).toEqual([ + expect.objectContaining({ + id: "test-id", + }), + expect.objectContaining({ + id: "test-id-1", + }), + ]) + }) + }) + + describe("retrieveAuthUser", () => { + const id = "test-id" + + it("should return an authUser for the given id", async () => { + const authUser = await service.retrieve(id) + + expect(authUser).toEqual( + expect.objectContaining({ + id, + }) + ) + }) + + it("should throw an error when an authUser with the given id does not exist", async () => { + let error + + try { + await service.retrieve("does-not-exist") + } catch (e) { + error = e + } + + expect(error.message).toEqual( + "AuthUser with id: does-not-exist was not found" + ) + }) + + it("should not return an authUser with password hash", async () => { + const authUser = await service.retrieve("test-id-1") + + expect(authUser).toEqual( + expect.objectContaining({ + id: "test-id-1", + }) + ) + expect(authUser["password_hash"]).toEqual(undefined) + }) + + it("should throw an error when a authUserId is not provided", async () => { + let error + + try { + await service.retrieve(undefined as unknown as string) + } catch (e) { + error = e + } + + expect(error.message).toEqual("authUser - id must be defined") + }) + + it("should return authUser based on config select param", async () => { + const authUser = await service.retrieve(id, { + select: ["id"], + }) + + expect(authUser).toEqual({ + id, + }) + }) + }) + + describe("deleteAuthUser", () => { + const id = "test-id" + + it("should delete the authUsers given an id successfully", async () => { + await service.delete([id]) + + const authUsers = await service.list({ + id: [id], + }) + + expect(authUsers).toHaveLength(0) + }) + }) + + describe("updateAuthUser", () => { + const id = "test-id" + + it("should throw an error when a id does not exist", async () => { + let error + + try { + await service.update([ + { + id: "does-not-exist", + }, + ]) + } catch (e) { + error = e + } + + expect(error.message).toEqual( + 'AuthUser with id "does-not-exist" not found' + ) + }) + + it("should update authUser", async () => { + await service.update([ + { + id, + provider_metadata: { email: "test@email.com" }, + }, + ]) + + const [authUser] = await service.list({ id: [id] }) + expect(authUser).toEqual( + expect.objectContaining({ + provider_metadata: { email: "test@email.com" }, + }) + ) + }) + }) + + describe("createAuthUser", () => { + it("should create a authUser successfully", async () => { + await service.create([ + { + id: "test", + provider: "manual", + entity_id: "test", + scope: "store", + }, + ]) + + const [authUser, count] = await service.listAndCount({ + id: ["test"], + }) + + expect(count).toEqual(1) + expect(authUser[0]).toEqual( + expect.objectContaining({ + id: "test", + }) + ) + }) + }) }) - }) + }, }) diff --git a/packages/auth/integration-tests/__tests__/services/module/providers.spec.ts b/packages/auth/integration-tests/__tests__/services/module/providers.spec.ts index 2be6a351f2..0f05fa96e0 100644 --- a/packages/auth/integration-tests/__tests__/services/module/providers.spec.ts +++ b/packages/auth/integration-tests/__tests__/services/module/providers.spec.ts @@ -1,68 +1,41 @@ import { MedusaModule, Modules } from "@medusajs/modules-sdk" import { IAuthModuleService } from "@medusajs/types" -import { MikroOrmWrapper } from "../../../utils" -import { SqlEntityManager } from "@mikro-orm/postgresql" -import { getInitModuleConfig } from "../../../utils/get-init-module-config" -import { initModules } from "medusa-test-utils" +import { moduleIntegrationTestRunner, SuiteOptions } from "medusa-test-utils" jest.setTimeout(30000) -describe("AuthModuleService - AuthProvider", () => { - let service: IAuthModuleService - let testManager: SqlEntityManager - let shutdownFunc: () => Promise +moduleIntegrationTestRunner({ + moduleName: Modules.AUTH, + testSuite: ({ + MikroOrmWrapper, + service, + }: SuiteOptions) => { + describe("AuthModuleService - AuthProvider", () => { + describe("authenticate", () => { + it("authenticate validates that a provider is registered in container", async () => { + const { success, error } = await service.authenticate( + "notRegistered", + {} as any + ) - beforeAll(async () => { - const initModulesConfig = getInitModuleConfig() + expect(success).toBe(false) + expect(error).toEqual( + "AuthenticationProvider: notRegistered wasn't registered in the module. Have you configured your options correctly?" + ) + }) - const { medusaApp, shutdown } = await initModules(initModulesConfig) + it("fails to authenticate using a valid provider with an invalid scope", async () => { + const { success, error } = await service.authenticate("emailpass", { + authScope: "non-existing", + } as any) - service = medusaApp.modules[Modules.AUTH] - - shutdownFunc = shutdown - }) - - beforeEach(async () => { - await MikroOrmWrapper.setupDatabase() - testManager = MikroOrmWrapper.forkManager() - - if (service.__hooks?.onApplicationStart) { - await service.__hooks.onApplicationStart() - } - }) - - afterEach(async () => { - await MikroOrmWrapper.clearDatabase() - MedusaModule.clearInstances() - }) - - afterAll(async () => { - await shutdownFunc() - }) - - describe("authenticate", () => { - it("authenticate validates that a provider is registered in container", async () => { - const { success, error } = await service.authenticate( - "notRegistered", - {} as any - ) - - expect(success).toBe(false) - expect(error).toEqual( - "AuthenticationProvider: notRegistered wasn't registered in the module. Have you configured your options correctly?" - ) + expect(success).toBe(false) + expect(error).toEqual( + `Scope "non-existing" is not valid for provider emailpass` + ) + }) + }) }) - - it("fails to authenticate using a valid provider with an invalid scope", async () => { - const { success, error } = await service.authenticate("emailpass", { - authScope: "non-existing", - } as any) - - expect(success).toBe(false) - expect(error).toEqual( - `Scope "non-existing" is not valid for provider emailpass` - ) - }) - }) + }, }) diff --git a/packages/auth/integration-tests/__tests__/services/providers/username-password.spec.ts b/packages/auth/integration-tests/__tests__/services/providers/username-password.spec.ts index ee24625214..297d8445fe 100644 --- a/packages/auth/integration-tests/__tests__/services/providers/username-password.spec.ts +++ b/packages/auth/integration-tests/__tests__/services/providers/username-password.spec.ts @@ -1,149 +1,133 @@ import { MedusaModule, Modules } from "@medusajs/modules-sdk" import { IAuthModuleService } from "@medusajs/types" -import { MikroOrmWrapper } from "../../../utils" import Scrypt from "scrypt-kdf" -import { SqlEntityManager } from "@mikro-orm/postgresql" import { createAuthUsers } from "../../../__fixtures__/auth-user" -import { getInitModuleConfig } from "../../../utils/get-init-module-config" -import { initModules } from "medusa-test-utils" +import { moduleIntegrationTestRunner, SuiteOptions } from "medusa-test-utils" jest.setTimeout(30000) -const seedDefaultData = async (testManager) => { - await createAuthUsers(testManager) +const seedDefaultData = async (manager) => { + await createAuthUsers(manager) } -describe("AuthModuleService - AuthProvider", () => { - let service: IAuthModuleService - let testManager: SqlEntityManager - let shutdownFunc: () => Promise - - beforeAll(async () => { - const initModulesConfig = getInitModuleConfig() - - const { medusaApp, shutdown } = await initModules(initModulesConfig) - - service = medusaApp.modules[Modules.AUTH] - - shutdownFunc = shutdown - }) - - afterAll(async () => { - await shutdownFunc() - }) - - beforeEach(async () => { - await MikroOrmWrapper.setupDatabase() - testManager = MikroOrmWrapper.forkManager() - - if (service.__hooks?.onApplicationStart) { - await service.__hooks.onApplicationStart() - } - }) - - afterEach(async () => { - await MikroOrmWrapper.clearDatabase() - MedusaModule.clearInstances() - }) - - describe("authenticate", () => { - it("authenticate validates that a provider is registered in container", async () => { - const password = "supersecret" - const email = "test@test.com" - const passwordHash = ( - await Scrypt.kdf(password, { logN: 15, r: 8, p: 1 }) - ).toString("base64") - - await seedDefaultData(testManager) - await createAuthUsers(testManager, [ - // Add authenticated user - { - provider: "emailpass", - entity_id: email, - scope: "store", - provider_metadata: { - password: passwordHash, - }, +moduleIntegrationTestRunner({ + moduleName: Modules.AUTH, + moduleOptions: { + providers: [ + { + name: "emailpass", + scopes: { + admin: {}, + store: {}, }, - ]) + }, + ], + }, + testSuite: ({ + MikroOrmWrapper, + service, + }: SuiteOptions) => { + describe("AuthModuleService - AuthProvider", () => { + describe("authenticate", () => { + it("authenticate validates that a provider is registered in container", async () => { + const password = "supersecret" + const email = "test@test.com" + const passwordHash = ( + await Scrypt.kdf(password, { logN: 15, r: 8, p: 1 }) + ).toString("base64") - const res = await service.authenticate("emailpass", { - body: { - email: "test@test.com", - password: password, - }, - authScope: "store", - } as any) + await seedDefaultData(MikroOrmWrapper.forkManager()) + await createAuthUsers(MikroOrmWrapper.forkManager(), [ + // Add authenticated user + { + provider: "emailpass", + entity_id: email, + scope: "store", + provider_metadata: { + password: passwordHash, + }, + }, + ]) - expect(res).toEqual({ - success: true, - authUser: expect.objectContaining({ - entity_id: email, - provider_metadata: {}, - }), + const res = await service.authenticate("emailpass", { + body: { + email: "test@test.com", + password: password, + }, + authScope: "store", + } as any) + + expect(res).toEqual({ + success: true, + authUser: expect.objectContaining({ + entity_id: email, + provider_metadata: {}, + }), + }) + }) + + it("fails when no password is given", async () => { + await seedDefaultData(MikroOrmWrapper.forkManager()) + + const res = await service.authenticate("emailpass", { + body: { email: "test@test.com" }, + authScope: "store", + } as any) + + expect(res).toEqual({ + success: false, + error: "Password should be a string", + }) + }) + + it("fails when no email is given", async () => { + await seedDefaultData(MikroOrmWrapper.forkManager()) + + const res = await service.authenticate("emailpass", { + body: { password: "supersecret" }, + authScope: "store", + } as any) + + expect(res).toEqual({ + success: false, + error: "Email should be a string", + }) + }) + + it("fails with an invalid password", async () => { + const password = "supersecret" + const email = "test@test.com" + const passwordHash = ( + await Scrypt.kdf(password, { logN: 15, r: 8, p: 1 }) + ).toString("base64") + + await seedDefaultData(MikroOrmWrapper.forkManager()) + await createAuthUsers(MikroOrmWrapper.forkManager(), [ + // Add authenticated user + { + provider: "emailpass", + scope: "store", + entity_id: email, + provider_metadata: { + password_hash: passwordHash, + }, + }, + ]) + + const res = await service.authenticate("emailpass", { + body: { + email: "test@test.com", + password: "password", + }, + authScope: "store", + } as any) + + expect(res).toEqual({ + success: false, + error: "Invalid email or password", + }) + }) }) }) - - it("fails when no password is given", async () => { - await seedDefaultData(testManager) - - const res = await service.authenticate("emailpass", { - body: { email: "test@test.com" }, - authScope: "store", - } as any) - - expect(res).toEqual({ - success: false, - error: "Password should be a string", - }) - }) - - it("fails when no email is given", async () => { - await seedDefaultData(testManager) - - const res = await service.authenticate("emailpass", { - body: { password: "supersecret" }, - authScope: "store", - } as any) - - expect(res).toEqual({ - success: false, - error: "Email should be a string", - }) - }) - - it("fails with an invalid password", async () => { - const password = "supersecret" - const email = "test@test.com" - const passwordHash = ( - await Scrypt.kdf(password, { logN: 15, r: 8, p: 1 }) - ).toString("base64") - - await seedDefaultData(testManager) - await createAuthUsers(testManager, [ - // Add authenticated user - { - provider: "emailpass", - scope: "store", - entity_id: email, - provider_metadata: { - password_hash: passwordHash, - }, - }, - ]) - - const res = await service.authenticate("emailpass", { - body: { - email: "test@test.com", - password: "password", - }, - authScope: "store", - } as any) - - expect(res).toEqual({ - success: false, - error: "Invalid email or password", - }) - }) - }) + }, }) diff --git a/packages/auth/integration-tests/setup-env.js b/packages/auth/integration-tests/setup-env.js deleted file mode 100644 index 90c24b7129..0000000000 --- a/packages/auth/integration-tests/setup-env.js +++ /dev/null @@ -1,6 +0,0 @@ -if (typeof process.env.DB_TEMP_NAME === "undefined") { - const tempName = parseInt(process.env.JEST_WORKER_ID || "1") - process.env.DB_TEMP_NAME = `medusa-auth-integration-${tempName}` -} - -process.env.MEDUSA_AUTH_DB_SCHEMA = "public" diff --git a/packages/auth/integration-tests/setup.js b/packages/auth/integration-tests/setup.js deleted file mode 100644 index 43f99aab4a..0000000000 --- a/packages/auth/integration-tests/setup.js +++ /dev/null @@ -1,3 +0,0 @@ -import { JestUtils } from "medusa-test-utils" - -JestUtils.afterAllHookDropDatabase() diff --git a/packages/auth/integration-tests/utils/config.ts b/packages/auth/integration-tests/utils/config.ts deleted file mode 100644 index 1558119f53..0000000000 --- a/packages/auth/integration-tests/utils/config.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { ModuleServiceInitializeOptions } from "@medusajs/types" - -export const databaseOptions: ModuleServiceInitializeOptions["database"] = { - schema: "public", - clientUrl: "medusa-auth-test", -} diff --git a/packages/auth/integration-tests/utils/database.ts b/packages/auth/integration-tests/utils/database.ts deleted file mode 100644 index 2b708a0eb7..0000000000 --- a/packages/auth/integration-tests/utils/database.ts +++ /dev/null @@ -1,18 +0,0 @@ -import * as AuthModels from "@models" - -import { TestDatabaseUtils } from "medusa-test-utils" - -const pathToMigrations = "../../src/migrations" -const mikroOrmEntities = AuthModels as unknown as any[] - -export const MikroOrmWrapper = TestDatabaseUtils.getMikroOrmWrapper({ - mikroOrmEntities, - pathToMigrations, -}) - -export const MikroOrmConfig = TestDatabaseUtils.getMikroOrmConfig({ - mikroOrmEntities, - pathToMigrations, -}) - -export const DB_URL = TestDatabaseUtils.getDatabaseURL() diff --git a/packages/auth/integration-tests/utils/get-init-module-config.ts b/packages/auth/integration-tests/utils/get-init-module-config.ts deleted file mode 100644 index b05055e67d..0000000000 --- a/packages/auth/integration-tests/utils/get-init-module-config.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { Modules, ModulesDefinition } from "@medusajs/modules-sdk" - -import { DB_URL } from "./database" - -export function getInitModuleConfig() { - const moduleOptions = { - defaultAdapterOptions: { - database: { - clientUrl: DB_URL, - schema: process.env.MEDUSA_AUTH_DB_SCHEMA, - }, - }, - providers: [ - { - name: "emailpass", - scopes: { - admin: {}, - store: {}, - }, - }, - ], - } - - const injectedDependencies = {} - - const modulesConfig_ = { - [Modules.AUTH]: { - definition: ModulesDefinition[Modules.AUTH], - options: moduleOptions, - }, - } - - return { - injectedDependencies, - modulesConfig: modulesConfig_, - databaseConfig: { - clientUrl: DB_URL, - schema: process.env.MEDUSA_AUTH_DB_SCHEMA, - }, - joinerConfig: [], - } -} diff --git a/packages/auth/integration-tests/utils/index.ts b/packages/auth/integration-tests/utils/index.ts deleted file mode 100644 index 5ca5d1bdc0..0000000000 --- a/packages/auth/integration-tests/utils/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from "./config" -export * from "./database" diff --git a/packages/auth/jest.config.js b/packages/auth/jest.config.js index 18fbb3dd05..82b23a96dc 100644 --- a/packages/auth/jest.config.js +++ b/packages/auth/jest.config.js @@ -18,6 +18,4 @@ module.exports = { testEnvironment: `node`, moduleFileExtensions: [`js`, `ts`], modulePathIgnorePatterns: ["dist/"], - setupFiles: ["/integration-tests/setup-env.js"], - setupFilesAfterEnv: ["/integration-tests/setup.js"], } diff --git a/packages/cart/integration-tests/__tests__/services/cart-module/index.spec.ts b/packages/cart/integration-tests/__tests__/services/cart-module/index.spec.ts index 78574be8ff..841751720c 100644 --- a/packages/cart/integration-tests/__tests__/services/cart-module/index.spec.ts +++ b/packages/cart/integration-tests/__tests__/services/cart-module/index.spec.ts @@ -1,2335 +1,2345 @@ import { Modules } from "@medusajs/modules-sdk" import { ICartModuleService } from "@medusajs/types" import { CheckConstraintViolationException } from "@mikro-orm/core" -import { initModules } from "medusa-test-utils" -import { MikroOrmWrapper } from "../../../utils" -import { getInitModuleConfig } from "../../../utils/get-init-module-config" +import { moduleIntegrationTestRunner, SuiteOptions } from "medusa-test-utils" jest.setTimeout(50000) -describe("Cart Module Service", () => { - let service: ICartModuleService - let shutdownFunc: () => Promise +moduleIntegrationTestRunner({ + moduleName: Modules.CART, + testSuite: ({ + MikroOrmWrapper, + service, + }: SuiteOptions) => { + describe("Cart Module Service", () => { + describe("create", () => { + it("should throw an error when required params are not passed", async () => { + const error = await service + .create([ + { + email: "test@email.com", + } as any, + ]) + .catch((e) => e) - beforeAll(async () => { - const initModulesConfig = getInitModuleConfig() - - const { medusaApp, shutdown } = await initModules(initModulesConfig) - - service = medusaApp.modules[Modules.CART] - - shutdownFunc = shutdown - }) - - afterAll(async () => { - await shutdownFunc() - }) - - beforeEach(async () => { - await MikroOrmWrapper.setupDatabase() - }) - - afterEach(async () => { - await MikroOrmWrapper.clearDatabase() - }) - - describe("create", () => { - it("should throw an error when required params are not passed", async () => { - const error = await service - .create([ - { - email: "test@email.com", - } as any, - ]) - .catch((e) => e) - - expect(error.message).toContain( - "Value for Cart.currency_code is required, 'undefined' found" - ) - }) - - it("should create a cart successfully", async () => { - const [createdCart] = await service.create([ - { - currency_code: "eur", - }, - ]) - - const [cart] = await service.list({ id: [createdCart.id] }) - - expect(cart).toEqual( - expect.objectContaining({ - id: createdCart.id, - currency_code: "eur", + expect(error.message).toContain( + "Value for Cart.currency_code is required, 'undefined' found" + ) }) - ) - }) - it("should create a cart with billing + shipping address successfully", async () => { - const [createdCart] = await service.create([ - { - currency_code: "eur", - billing_address: { - first_name: "John", - last_name: "Doe", - }, - shipping_address: { - first_name: "John", - last_name: "Doe", - }, - }, - ]) - - const [cart] = await service.list( - { id: [createdCart.id] }, - { relations: ["billing_address", "shipping_address"] } - ) - - expect(cart).toEqual( - expect.objectContaining({ - id: createdCart.id, - currency_code: "eur", - billing_address: expect.objectContaining({ - first_name: "John", - last_name: "Doe", - }), - shipping_address: expect.objectContaining({ - first_name: "John", - last_name: "Doe", - }), - }) - ) - }) - - it("should create a cart with billing id + shipping id successfully", async () => { - const [createdAddress] = await service.createAddresses([ - { - first_name: "John", - last_name: "Doe", - }, - ]) - - const [createdCart] = await service.create([ - { - currency_code: "eur", - billing_address_id: createdAddress.id, - shipping_address_id: createdAddress.id, - }, - ]) - - expect(createdCart).toEqual( - expect.objectContaining({ - id: createdCart.id, - currency_code: "eur", - billing_address: expect.objectContaining({ - id: createdAddress.id, - first_name: "John", - last_name: "Doe", - }), - shipping_address: expect.objectContaining({ - id: createdAddress.id, - first_name: "John", - last_name: "Doe", - }), - }) - ) - }) - - it("should create a cart with items", async () => { - const createdCart = await service.create({ - currency_code: "eur", - items: [ - { - title: "test", - quantity: 1, - unit_price: 100, - }, - ], - }) - - const cart = await service.retrieve(createdCart.id, { - relations: ["items"], - }) - - expect(cart).toEqual( - expect.objectContaining({ - id: createdCart.id, - currency_code: "eur", - items: expect.arrayContaining([ - expect.objectContaining({ - title: "test", - unit_price: 100, - }), - ]), - }) - ) - }) - - it("should create multiple carts with items", async () => { - const createdCarts = await service.create([ - { - currency_code: "eur", - items: [ + it("should create a cart successfully", async () => { + const [createdCart] = await service.create([ { + currency_code: "eur", + }, + ]) + + const [cart] = await service.list({ id: [createdCart.id] }) + + expect(cart).toEqual( + expect.objectContaining({ + id: createdCart.id, + currency_code: "eur", + }) + ) + }) + + it("should create a cart with billing + shipping address successfully", async () => { + const [createdCart] = await service.create([ + { + currency_code: "eur", + billing_address: { + first_name: "John", + last_name: "Doe", + }, + shipping_address: { + first_name: "John", + last_name: "Doe", + }, + }, + ]) + + const [cart] = await service.list( + { id: [createdCart.id] }, + { relations: ["billing_address", "shipping_address"] } + ) + + expect(cart).toEqual( + expect.objectContaining({ + id: createdCart.id, + currency_code: "eur", + billing_address: expect.objectContaining({ + first_name: "John", + last_name: "Doe", + }), + shipping_address: expect.objectContaining({ + first_name: "John", + last_name: "Doe", + }), + }) + ) + }) + + it("should create a cart with billing id + shipping id successfully", async () => { + const [createdAddress] = await service.createAddresses([ + { + first_name: "John", + last_name: "Doe", + }, + ]) + + const [createdCart] = await service.create([ + { + currency_code: "eur", + billing_address_id: createdAddress.id, + shipping_address_id: createdAddress.id, + }, + ]) + + expect(createdCart).toEqual( + expect.objectContaining({ + id: createdCart.id, + currency_code: "eur", + billing_address: expect.objectContaining({ + id: createdAddress.id, + first_name: "John", + last_name: "Doe", + }), + shipping_address: expect.objectContaining({ + id: createdAddress.id, + first_name: "John", + last_name: "Doe", + }), + }) + ) + }) + + it("should create a cart with items", async () => { + const createdCart = await service.create({ + currency_code: "eur", + items: [ + { + title: "test", + quantity: 1, + unit_price: 100, + }, + ], + }) + + const cart = await service.retrieve(createdCart.id, { + relations: ["items"], + }) + + expect(cart).toEqual( + expect.objectContaining({ + id: createdCart.id, + currency_code: "eur", + items: expect.arrayContaining([ + expect.objectContaining({ + title: "test", + unit_price: 100, + }), + ]), + }) + ) + }) + + it("should create multiple carts with items", async () => { + const createdCarts = await service.create([ + { + currency_code: "eur", + items: [ + { + title: "test", + quantity: 1, + unit_price: 100, + }, + ], + }, + { + currency_code: "usd", + items: [ + { + title: "test-2", + quantity: 2, + unit_price: 200, + }, + ], + }, + ]) + + const carts = await service.list( + { id: createdCarts.map((c) => c.id) }, + { + relations: ["items"], + } + ) + + expect(carts).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + currency_code: "eur", + items: expect.arrayContaining([ + expect.objectContaining({ + title: "test", + unit_price: 100, + }), + ]), + }), + expect.objectContaining({ + currency_code: "usd", + items: expect.arrayContaining([ + expect.objectContaining({ + title: "test-2", + unit_price: 200, + }), + ]), + }), + ]) + ) + }) + }) + + describe("update", () => { + it("should throw an error if cart does not exist", async () => { + const error = await service + .update([ + { + id: "none-existing", + }, + ]) + .catch((e) => e) + + expect(error.message).toContain( + 'Cart with id "none-existing" not found' + ) + }) + + it("should update a cart successfully", async () => { + const [createdCart] = await service.create([ + { + currency_code: "eur", + }, + ]) + + const [updatedCart] = await service.update([ + { + id: createdCart.id, + email: "test@email.com", + }, + ]) + + const [cart] = await service.list({ id: [createdCart.id] }) + + expect(cart).toEqual( + expect.objectContaining({ + id: createdCart.id, + currency_code: "eur", + email: updatedCart.email, + }) + ) + }) + + it("should update a cart with selector successfully", async () => { + const [createdCart] = await service.create([ + { + currency_code: "eur", + }, + ]) + + const [updatedCart] = await service.update( + { id: createdCart.id }, + { + email: "test@email.com", + } + ) + + const [cart] = await service.list({ id: [createdCart.id] }) + + expect(cart).toEqual( + expect.objectContaining({ + id: createdCart.id, + currency_code: "eur", + email: updatedCart.email, + }) + ) + }) + + it("should update a cart with id successfully", async () => { + const [createdCart] = await service.create([ + { + currency_code: "eur", + }, + ]) + + const updatedCart = await service.update(createdCart.id, { + email: "test@email.com", + }) + + const [cart] = await service.list({ id: [createdCart.id] }) + + expect(cart).toEqual( + expect.objectContaining({ + id: createdCart.id, + currency_code: "eur", + email: updatedCart.email, + }) + ) + }) + }) + + describe("delete", () => { + it("should delete a cart successfully", async () => { + const [createdCart] = await service.create([ + { + currency_code: "eur", + }, + ]) + + await service.delete([createdCart.id]) + + const carts = await service.list({ id: [createdCart.id] }) + + expect(carts.length).toEqual(0) + }) + }) + + describe("createAddresses", () => { + it("should create an address successfully", async () => { + const [createdAddress] = await service.createAddresses([ + { + first_name: "John", + }, + ]) + + const [address] = await service.listAddresses({ + id: [createdAddress.id!], + }) + + expect(address).toEqual( + expect.objectContaining({ + id: createdAddress.id, + first_name: "John", + }) + ) + }) + }) + + describe("updateAddresses", () => { + it("should update an address successfully", async () => { + const [createdAddress] = await service.createAddresses([ + { + first_name: "John", + }, + ]) + + const [updatedAddress] = await service.updateAddresses([ + { id: createdAddress.id!, first_name: "Jane" }, + ]) + + expect(updatedAddress).toEqual( + expect.objectContaining({ + id: createdAddress.id, + first_name: "Jane", + }) + ) + }) + }) + + describe("deleteAddresses", () => { + it("should delete an address successfully", async () => { + const [createdAddress] = await service.createAddresses([ + { + first_name: "John", + }, + ]) + + await service.deleteAddresses([createdAddress.id!]) + + const [address] = await service.listAddresses({ + id: [createdAddress.id!], + }) + + expect(address).toBe(undefined) + }) + }) + + describe("addLineItems", () => { + it("should add a line item to cart succesfully", async () => { + const [createdCart] = await service.create([ + { + currency_code: "eur", + }, + ]) + + const items = await service.addLineItems(createdCart.id, [ + { + quantity: 1, + unit_price: 100, + title: "test", + }, + ]) + + const cart = await service.retrieve(createdCart.id, { + relations: ["items"], + }) + + expect(items[0]).toEqual( + expect.objectContaining({ title: "test", quantity: 1, unit_price: 100, - }, - ], - }, - { - currency_code: "usd", - items: [ + }) + ) + expect(cart.items?.length).toBe(1) + }) + + it("should add multiple line items to cart succesfully", async () => { + const [createdCart] = await service.create([ + { + currency_code: "eur", + }, + ]) + + await service.addLineItems([ + { + quantity: 1, + unit_price: 100, + title: "test", + cart_id: createdCart.id, + }, { - title: "test-2", quantity: 2, unit_price: 200, + title: "test-2", + cart_id: createdCart.id, }, - ], - }, - ]) + ]) - const carts = await service.list( - { id: createdCarts.map((c) => c.id) }, - { - relations: ["items"], - } - ) + const cart = await service.retrieve(createdCart.id, { + relations: ["items"], + }) - expect(carts).toEqual( - expect.arrayContaining([ - expect.objectContaining({ - currency_code: "eur", - items: expect.arrayContaining([ + expect(cart.items).toEqual( + expect.arrayContaining([ expect.objectContaining({ title: "test", + quantity: 1, unit_price: 100, }), - ]), - }), - expect.objectContaining({ - currency_code: "usd", - items: expect.arrayContaining([ expect.objectContaining({ title: "test-2", + quantity: 2, unit_price: 200, }), - ]), - }), - ]) - ) - }) - }) + ]) + ) - describe("update", () => { - it("should throw an error if cart does not exist", async () => { - const error = await service - .update([ - { - id: "none-existing", - }, - ]) - .catch((e) => e) - - expect(error.message).toContain('Cart with id "none-existing" not found') - }) - - it("should update a cart successfully", async () => { - const [createdCart] = await service.create([ - { - currency_code: "eur", - }, - ]) - - const [updatedCart] = await service.update([ - { - id: createdCart.id, - email: "test@email.com", - }, - ]) - - const [cart] = await service.list({ id: [createdCart.id] }) - - expect(cart).toEqual( - expect.objectContaining({ - id: createdCart.id, - currency_code: "eur", - email: updatedCart.email, + expect(cart.items?.length).toBe(2) }) - ) - }) - it("should update a cart with selector successfully", async () => { - const [createdCart] = await service.create([ - { - currency_code: "eur", - }, - ]) + it("should add multiple line items to multiple carts succesfully", async () => { + let [eurCart] = await service.create([ + { + currency_code: "eur", + }, + ]) - const [updatedCart] = await service.update( - { id: createdCart.id }, - { - email: "test@email.com", - } - ) + let [usdCart] = await service.create([ + { + currency_code: "usd", + }, + ]) - const [cart] = await service.list({ id: [createdCart.id] }) + const items = await service.addLineItems([ + { + cart_id: eurCart.id, + quantity: 1, + unit_price: 100, + title: "test", + }, + { + cart_id: usdCart.id, + quantity: 1, + unit_price: 100, + title: "test", + }, + ]) - expect(cart).toEqual( - expect.objectContaining({ - id: createdCart.id, - currency_code: "eur", - email: updatedCart.email, + const carts = await service.list( + { id: [eurCart.id, usdCart.id] }, + { relations: ["items"] } + ) + + eurCart = carts.find((c) => c.currency_code === "eur")! + usdCart = carts.find((c) => c.currency_code === "usd")! + + const eurItems = items.filter((i) => i.cart_id === eurCart.id) + const usdItems = items.filter((i) => i.cart_id === usdCart.id) + + expect(eurCart.items![0].id).toBe(eurItems[0].id) + expect(usdCart.items![0].id).toBe(usdItems[0].id) + + expect(eurCart.items?.length).toBe(1) + expect(usdCart.items?.length).toBe(1) }) - ) - }) - it("should update a cart with id successfully", async () => { - const [createdCart] = await service.create([ - { - currency_code: "eur", - }, - ]) + it("should throw if cart does not exist", async () => { + const error = await service + .addLineItems("foo", [ + { + quantity: 1, + unit_price: 100, + title: "test", + tax_lines: [], + }, + ]) + .catch((e) => e) - const updatedCart = await service.update(createdCart.id, { - email: "test@email.com", - }) - - const [cart] = await service.list({ id: [createdCart.id] }) - - expect(cart).toEqual( - expect.objectContaining({ - id: createdCart.id, - currency_code: "eur", - email: updatedCart.email, + expect(error.message).toContain("Cart with id: foo was not found") }) - ) - }) - }) - describe("delete", () => { - it("should delete a cart successfully", async () => { - const [createdCart] = await service.create([ - { - currency_code: "eur", - }, - ]) + it("should throw an error when required params are not passed adding to a single cart", async () => { + const [createdCart] = await service.create([ + { + currency_code: "eur", + }, + ]) - await service.delete([createdCart.id]) + const error = await service + .addLineItems(createdCart.id, [ + { + unit_price: 10, + title: "test", + }, + ] as any) + .catch((e) => e) - const carts = await service.list({ id: [createdCart.id] }) - - expect(carts.length).toEqual(0) - }) - }) - - describe("createAddresses", () => { - it("should create an address successfully", async () => { - const [createdAddress] = await service.createAddresses([ - { - first_name: "John", - }, - ]) - - const [address] = await service.listAddresses({ - id: [createdAddress.id!], - }) - - expect(address).toEqual( - expect.objectContaining({ - id: createdAddress.id, - first_name: "John", + expect(error.message).toContain( + "Value for LineItem.quantity is required, 'undefined' found" + ) }) - ) - }) - }) - describe("updateAddresses", () => { - it("should update an address successfully", async () => { - const [createdAddress] = await service.createAddresses([ - { - first_name: "John", - }, - ]) + it("should throw a generic error when required params are not passed using bulk add method", async () => { + const [createdCart] = await service.create([ + { + currency_code: "eur", + }, + ]) - const [updatedAddress] = await service.updateAddresses([ - { id: createdAddress.id!, first_name: "Jane" }, - ]) + const error = await service + .addLineItems([ + { + cart_id: createdCart.id, + unit_price: 10, + title: "test", + }, + ] as any) + .catch((e) => e) - expect(updatedAddress).toEqual( - expect.objectContaining({ - id: createdAddress.id, - first_name: "Jane", + expect(error.message).toContain( + "Value for LineItem.quantity is required, 'undefined' found" + ) }) - ) - }) - }) - - describe("deleteAddresses", () => { - it("should delete an address successfully", async () => { - const [createdAddress] = await service.createAddresses([ - { - first_name: "John", - }, - ]) - - await service.deleteAddresses([createdAddress.id!]) - - const [address] = await service.listAddresses({ - id: [createdAddress.id!], }) - expect(address).toBe(undefined) - }) - }) + describe("updateLineItems", () => { + it("should update a line item in cart succesfully with selector approach", async () => { + const [createdCart] = await service.create([ + { + currency_code: "eur", + }, + ]) - describe("addLineItems", () => { - it("should add a line item to cart succesfully", async () => { - const [createdCart] = await service.create([ - { - currency_code: "eur", - }, - ]) + const [item] = await service.addLineItems(createdCart.id, [ + { + quantity: 1, + unit_price: 100, + title: "test", + tax_lines: [], + }, + ]) - const items = await service.addLineItems(createdCart.id, [ - { - quantity: 1, - unit_price: 100, - title: "test", - }, - ]) + expect(item.title).toBe("test") - const cart = await service.retrieve(createdCart.id, { - relations: ["items"], - }) + const [updatedItem] = await service.updateLineItems( + { cart_id: createdCart.id }, + { + title: "test2", + } + ) - expect(items[0]).toEqual( - expect.objectContaining({ - title: "test", - quantity: 1, - unit_price: 100, + expect(updatedItem.title).toBe("test2") }) - ) - expect(cart.items?.length).toBe(1) - }) - it("should add multiple line items to cart succesfully", async () => { - const [createdCart] = await service.create([ - { - currency_code: "eur", - }, - ]) - - await service.addLineItems([ - { - quantity: 1, - unit_price: 100, - title: "test", - cart_id: createdCart.id, - }, - { - quantity: 2, - unit_price: 200, - title: "test-2", - cart_id: createdCart.id, - }, - ]) - - const cart = await service.retrieve(createdCart.id, { - relations: ["items"], - }) - - expect(cart.items).toEqual( - expect.arrayContaining([ - expect.objectContaining({ - title: "test", - quantity: 1, - unit_price: 100, - }), - expect.objectContaining({ - title: "test-2", - quantity: 2, - unit_price: 200, - }), - ]) - ) - - expect(cart.items?.length).toBe(2) - }) - - it("should add multiple line items to multiple carts succesfully", async () => { - let [eurCart] = await service.create([ - { - currency_code: "eur", - }, - ]) - - let [usdCart] = await service.create([ - { - currency_code: "usd", - }, - ]) - - const items = await service.addLineItems([ - { - cart_id: eurCart.id, - quantity: 1, - unit_price: 100, - title: "test", - }, - { - cart_id: usdCart.id, - quantity: 1, - unit_price: 100, - title: "test", - }, - ]) - - const carts = await service.list( - { id: [eurCart.id, usdCart.id] }, - { relations: ["items"] } - ) - - eurCart = carts.find((c) => c.currency_code === "eur")! - usdCart = carts.find((c) => c.currency_code === "usd")! - - const eurItems = items.filter((i) => i.cart_id === eurCart.id) - const usdItems = items.filter((i) => i.cart_id === usdCart.id) - - expect(eurCart.items![0].id).toBe(eurItems[0].id) - expect(usdCart.items![0].id).toBe(usdItems[0].id) - - expect(eurCart.items?.length).toBe(1) - expect(usdCart.items?.length).toBe(1) - }) - - it("should throw if cart does not exist", async () => { - const error = await service - .addLineItems("foo", [ - { - quantity: 1, - unit_price: 100, - title: "test", - tax_lines: [], - }, - ]) - .catch((e) => e) - - expect(error.message).toContain("Cart with id: foo was not found") - }) - - it("should throw an error when required params are not passed adding to a single cart", async () => { - const [createdCart] = await service.create([ - { - currency_code: "eur", - }, - ]) - - const error = await service - .addLineItems(createdCart.id, [ - { - unit_price: 10, - title: "test", - }, - ] as any) - .catch((e) => e) - - expect(error.message).toContain( - "Value for LineItem.quantity is required, 'undefined' found" - ) - }) - - it("should throw a generic error when required params are not passed using bulk add method", async () => { - const [createdCart] = await service.create([ - { - currency_code: "eur", - }, - ]) - - const error = await service - .addLineItems([ - { - cart_id: createdCart.id, - unit_price: 10, - title: "test", - }, - ] as any) - .catch((e) => e) - - expect(error.message).toContain( - "Value for LineItem.quantity is required, 'undefined' found" - ) - }) - }) - - describe("updateLineItems", () => { - it("should update a line item in cart succesfully with selector approach", async () => { - const [createdCart] = await service.create([ - { - currency_code: "eur", - }, - ]) - - const [item] = await service.addLineItems(createdCart.id, [ - { - quantity: 1, - unit_price: 100, - title: "test", - tax_lines: [], - }, - ]) - - expect(item.title).toBe("test") - - const [updatedItem] = await service.updateLineItems( - { cart_id: createdCart.id }, - { - title: "test2", - } - ) - - expect(updatedItem.title).toBe("test2") - }) - - it("should update a line item in cart succesfully with id approach", async () => { - const [createdCart] = await service.create([ - { - currency_code: "eur", - }, - ]) - - const [item] = await service.addLineItems(createdCart.id, [ - { - quantity: 1, - unit_price: 100, - title: "test", - tax_lines: [], - }, - ]) - - expect(item.title).toBe("test") - - const updatedItem = await service.updateLineItems(item.id, { - title: "test2", - }) - - expect(updatedItem.title).toBe("test2") - }) - - it("should update line items in carts succesfully with multi-selector approach", async () => { - const [createdCart] = await service.create([ - { - currency_code: "eur", - }, - ]) - - const items = await service.addLineItems(createdCart.id, [ - { - quantity: 1, - unit_price: 100, - title: "test", - }, - { - quantity: 2, - unit_price: 200, - title: "other-test", - }, - ]) - - expect(items).toEqual( - expect.arrayContaining([ - expect.objectContaining({ - title: "test", - quantity: 1, - unit_price: 100, - }), - expect.objectContaining({ - title: "other-test", - quantity: 2, - unit_price: 200, - }), - ]) - ) - - const itemOne = items.find((i) => i.title === "test") - const itemTwo = items.find((i) => i.title === "other-test") - - const updatedItems = await service.updateLineItems([ - { - selector: { cart_id: createdCart.id }, - data: { - title: "changed-test", - }, - }, - { - selector: { id: itemTwo!.id }, - data: { - title: "changed-other-test", - }, - }, - ]) - - expect(updatedItems).toEqual( - expect.arrayContaining([ - expect.objectContaining({ - title: "changed-test", - quantity: 1, - unit_price: 100, - }), - expect.objectContaining({ - title: "changed-other-test", - quantity: 2, - unit_price: 200, - }), - ]) - ) - }) - }) - - describe("removeLineItems", () => { - it("should remove a line item succesfully", async () => { - const [createdCart] = await service.create([ - { - currency_code: "eur", - }, - ]) - - const [item] = await service.addLineItems(createdCart.id, [ - { - quantity: 1, - unit_price: 100, - title: "test", - tax_lines: [], - }, - ]) - - expect(item.title).toBe("test") - - await service.softDeleteLineItems([item.id]) - - const cart = await service.retrieve(createdCart.id, { - relations: ["items"], - }) - - expect(cart.items?.length).toBe(0) - }) - - it("should remove multiple line items succesfully", async () => { - const [createdCart] = await service.create([ - { - currency_code: "eur", - }, - ]) - - const [item, item2] = await service.addLineItems(createdCart.id, [ - { - quantity: 1, - unit_price: 100, - title: "test", - }, - { - quantity: 1, - unit_price: 100, - title: "test-2", - }, - ]) - - await service.softDeleteLineItems([item.id, item2.id]) - - const cart = await service.retrieve(createdCart.id, { - relations: ["items"], - }) - - expect(cart.items?.length).toBe(0) - }) - }) - - describe("addShippingMethods", () => { - it("should add a shipping method to cart succesfully", async () => { - const [createdCart] = await service.create([ - { - currency_code: "eur", - }, - ]) - - const [method] = await service.addShippingMethods(createdCart.id, [ - { - amount: 100, - name: "Test", - }, - ]) - - const cart = await service.retrieve(createdCart.id, { - relations: ["shipping_methods"], - }) - - expect(method.id).toBe(cart.shipping_methods![0].id) - }) - - it("should throw when amount is negative", async () => { - const [createdCart] = await service.create([ - { - currency_code: "eur", - }, - ]) - - const error = await service - .addShippingMethods(createdCart.id, [ - { - amount: -100, - name: "Test", - }, - ]) - .catch((e) => e) - - expect(error.name).toBe(CheckConstraintViolationException.name) - }) - - it("should add multiple shipping methods to multiple carts succesfully", async () => { - let [eurCart] = await service.create([ - { - currency_code: "eur", - }, - ]) - - let [usdCart] = await service.create([ - { - currency_code: "usd", - }, - ]) - - const methods = await service.addShippingMethods([ - { - cart_id: eurCart.id, - amount: 100, - name: "Test One", - }, - { - cart_id: usdCart.id, - amount: 100, - name: "Test One", - }, - ]) - - const carts = await service.list( - { id: [eurCart.id, usdCart.id] }, - { relations: ["shipping_methods"] } - ) - - eurCart = carts.find((c) => c.currency_code === "eur")! - usdCart = carts.find((c) => c.currency_code === "usd")! - - const eurMethods = methods.filter((m) => m.cart_id === eurCart.id) - const usdMethods = methods.filter((m) => m.cart_id === usdCart.id) - - expect(eurCart.shipping_methods![0].id).toBe(eurMethods[0].id) - expect(usdCart.shipping_methods![0].id).toBe(usdMethods[0].id) - - expect(eurCart.shipping_methods?.length).toBe(1) - expect(usdCart.shipping_methods?.length).toBe(1) - }) - }) - - describe("removeShippingMethods", () => { - it("should remove a line item succesfully", async () => { - const [createdCart] = await service.create([ - { - currency_code: "eur", - }, - ]) - - const [method] = await service.addShippingMethods(createdCart.id, [ - { - amount: 100, - name: "test", - }, - ]) - - expect(method.id).not.toBe(null) - - await service.softDeleteShippingMethods([method.id]) - - const cart = await service.retrieve(createdCart.id, { - relations: ["shipping_methods"], - }) - - expect(cart.shipping_methods?.length).toBe(0) - }) - }) - - describe("setLineItemAdjustments", () => { - it("should set line item adjustments for a cart", async () => { - const [createdCart] = await service.create([ - { - currency_code: "eur", - }, - ]) - - const [itemOne] = await service.addLineItems(createdCart.id, [ - { - quantity: 1, - unit_price: 100, - title: "test", - }, - ]) - - const [itemTwo] = await service.addLineItems(createdCart.id, [ - { - quantity: 2, - unit_price: 200, - title: "test-2", - }, - ]) - - const adjustments = await service.setLineItemAdjustments(createdCart.id, [ - { - item_id: itemOne.id, - amount: 100, - code: "FREE", - }, - { - item_id: itemTwo.id, - amount: 200, - code: "FREE-2", - }, - ]) - - expect(adjustments).toEqual( - expect.arrayContaining([ - expect.objectContaining({ - item_id: itemOne.id, - amount: 100, - code: "FREE", - }), - expect.objectContaining({ - item_id: itemTwo.id, - amount: 200, - code: "FREE-2", - }), - ]) - ) - }) - - it("should replace line item adjustments for a cart", async () => { - const [createdCart] = await service.create([ - { - currency_code: "eur", - }, - ]) - - const [itemOne] = await service.addLineItems(createdCart.id, [ - { - quantity: 1, - unit_price: 100, - title: "test", - }, - ]) - - const adjustments = await service.setLineItemAdjustments(createdCart.id, [ - { - item_id: itemOne.id, - amount: 100, - code: "FREE", - }, - ]) - - expect(adjustments).toEqual( - expect.arrayContaining([ - expect.objectContaining({ - item_id: itemOne.id, - amount: 100, - code: "FREE", - }), - ]) - ) - - await service.setLineItemAdjustments(createdCart.id, [ - { - item_id: itemOne.id, - amount: 50, - code: "50%", - }, - ]) - - const cart = await service.retrieve(createdCart.id, { - relations: ["items.adjustments"], - }) - - expect(cart.items).toEqual( - expect.arrayContaining([ - expect.objectContaining({ - id: itemOne.id, - adjustments: expect.arrayContaining([ + it("should update a line item in cart succesfully with id approach", async () => { + const [createdCart] = await service.create([ + { + currency_code: "eur", + }, + ]) + + const [item] = await service.addLineItems(createdCart.id, [ + { + quantity: 1, + unit_price: 100, + title: "test", + tax_lines: [], + }, + ]) + + expect(item.title).toBe("test") + + const updatedItem = await service.updateLineItems(item.id, { + title: "test2", + }) + + expect(updatedItem.title).toBe("test2") + }) + + it("should update line items in carts succesfully with multi-selector approach", async () => { + const [createdCart] = await service.create([ + { + currency_code: "eur", + }, + ]) + + const items = await service.addLineItems(createdCart.id, [ + { + quantity: 1, + unit_price: 100, + title: "test", + }, + { + quantity: 2, + unit_price: 200, + title: "other-test", + }, + ]) + + expect(items).toEqual( + expect.arrayContaining([ expect.objectContaining({ - item_id: itemOne.id, - amount: 50, - code: "50%", + title: "test", + quantity: 1, + unit_price: 100, }), - ]), - }), - ]) - ) - - expect(cart.items?.length).toBe(1) - expect(cart.items?.[0].adjustments?.length).toBe(1) - }) - - it("should remove all line item adjustments for a cart", async () => { - const [createdCart] = await service.create([ - { - currency_code: "eur", - }, - ]) - - const [itemOne] = await service.addLineItems(createdCart.id, [ - { - quantity: 1, - unit_price: 100, - title: "test", - }, - ]) - - const adjustments = await service.setLineItemAdjustments(createdCart.id, [ - { - item_id: itemOne.id, - amount: 100, - code: "FREE", - }, - ]) - - expect(adjustments).toEqual( - expect.arrayContaining([ - expect.objectContaining({ - item_id: itemOne.id, - amount: 100, - code: "FREE", - }), - ]) - ) - - await service.setLineItemAdjustments(createdCart.id, []) - - const cart = await service.retrieve(createdCart.id, { - relations: ["items.adjustments"], - }) - - expect(cart.items).toEqual( - expect.arrayContaining([ - expect.objectContaining({ - id: itemOne.id, - adjustments: [], - }), - ]) - ) - - expect(cart.items?.length).toBe(1) - expect(cart.items?.[0].adjustments?.length).toBe(0) - }) - - it("should update line item adjustments for a cart", async () => { - const [createdCart] = await service.create([ - { - currency_code: "eur", - }, - ]) - - const [itemOne] = await service.addLineItems(createdCart.id, [ - { - quantity: 1, - unit_price: 100, - title: "test", - }, - ]) - - const adjustments = await service.setLineItemAdjustments(createdCart.id, [ - { - item_id: itemOne.id, - amount: 100, - code: "FREE", - }, - ]) - - expect(adjustments).toEqual( - expect.arrayContaining([ - expect.objectContaining({ - item_id: itemOne.id, - amount: 100, - code: "FREE", - }), - ]) - ) - - await service.setLineItemAdjustments(createdCart.id, [ - { - id: adjustments[0].id, - item_id: itemOne.id, - amount: 50, - code: "50%", - }, - ]) - - const cart = await service.retrieve(createdCart.id, { - relations: ["items.adjustments"], - }) - - expect(cart.items).toEqual( - expect.arrayContaining([ - expect.objectContaining({ - id: itemOne.id, - adjustments: [ expect.objectContaining({ - id: adjustments[0].id, - item_id: itemOne.id, - amount: 50, - code: "50%", + title: "other-test", + quantity: 2, + unit_price: 200, }), - ], - }), - ]) - ) + ]) + ) - expect(cart.items?.length).toBe(1) - expect(cart.items?.[0].adjustments?.length).toBe(1) - }) - }) + const itemOne = items.find((i) => i.title === "test") + const itemTwo = items.find((i) => i.title === "other-test") - describe("addLineItemAdjustments", () => { - it("should add line item adjustments for items in a cart", async () => { - const [createdCart] = await service.create([ - { - currency_code: "eur", - }, - ]) + const updatedItems = await service.updateLineItems([ + { + selector: { cart_id: createdCart.id }, + data: { + title: "changed-test", + }, + }, + { + selector: { id: itemTwo!.id }, + data: { + title: "changed-other-test", + }, + }, + ]) - const [itemOne] = await service.addLineItems(createdCart.id, [ - { - quantity: 1, - unit_price: 100, - title: "test", - }, - ]) + expect(updatedItems).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + title: "changed-test", + quantity: 1, + unit_price: 100, + }), + expect.objectContaining({ + title: "changed-other-test", + quantity: 2, + unit_price: 200, + }), + ]) + ) + }) + }) - const adjustments = await service.addLineItemAdjustments(createdCart.id, [ - { - item_id: itemOne.id, - amount: 100, - code: "FREE", - }, - ]) + describe("removeLineItems", () => { + it("should remove a line item succesfully", async () => { + const [createdCart] = await service.create([ + { + currency_code: "eur", + }, + ]) - expect(adjustments).toEqual( - expect.arrayContaining([ - expect.objectContaining({ - item_id: itemOne.id, - amount: 100, - code: "FREE", - }), - ]) - ) - }) + const [item] = await service.addLineItems(createdCart.id, [ + { + quantity: 1, + unit_price: 100, + title: "test", + tax_lines: [], + }, + ]) - it("should add multiple line item adjustments for multiple line items", async () => { - const [createdCart] = await service.create([ - { - currency_code: "eur", - }, - ]) + expect(item.title).toBe("test") - const [itemOne] = await service.addLineItems(createdCart.id, [ - { - quantity: 1, - unit_price: 100, - title: "test", - }, - ]) - const [itemTwo] = await service.addLineItems(createdCart.id, [ - { - quantity: 2, - unit_price: 200, - title: "test-2", - }, - ]) + await service.softDeleteLineItems([item.id]) - const adjustments = await service.addLineItemAdjustments(createdCart.id, [ - { - item_id: itemOne.id, - amount: 100, - code: "FREE", - }, - { - item_id: itemTwo.id, - amount: 150, - code: "CODE-2", - }, - ]) + const cart = await service.retrieve(createdCart.id, { + relations: ["items"], + }) - expect(adjustments).toEqual( - expect.arrayContaining([ - expect.objectContaining({ - item_id: itemOne.id, - amount: 100, - code: "FREE", - }), - expect.objectContaining({ - item_id: itemTwo.id, - amount: 150, - code: "CODE-2", - }), - ]) - ) - }) + expect(cart.items?.length).toBe(0) + }) - it("should add line item adjustments for line items on multiple carts", async () => { - const [cartOne] = await service.create([ - { - currency_code: "eur", - }, - ]) - const [cartTwo] = await service.create([ - { - currency_code: "usd", - }, - ]) + it("should remove multiple line items succesfully", async () => { + const [createdCart] = await service.create([ + { + currency_code: "eur", + }, + ]) - const [itemOne] = await service.addLineItems(cartOne.id, [ - { - quantity: 1, - unit_price: 100, - title: "test", - }, - ]) - const [itemTwo] = await service.addLineItems(cartTwo.id, [ - { - quantity: 2, - unit_price: 200, - title: "test-2", - }, - ]) + const [item, item2] = await service.addLineItems(createdCart.id, [ + { + quantity: 1, + unit_price: 100, + title: "test", + }, + { + quantity: 1, + unit_price: 100, + title: "test-2", + }, + ]) - await service.addLineItemAdjustments([ - // item from cart one - { - item_id: itemOne.id, - amount: 100, - code: "FREE", - }, - // item from cart two - { - item_id: itemTwo.id, - amount: 150, - code: "CODE-2", - }, - ]) + await service.softDeleteLineItems([item.id, item2.id]) - const cartOneItems = await service.listLineItems( - { cart_id: cartOne.id }, - { relations: ["adjustments"] } - ) - const cartTwoItems = await service.listLineItems( - { cart_id: cartTwo.id }, - { relations: ["adjustments"] } - ) + const cart = await service.retrieve(createdCart.id, { + relations: ["items"], + }) - expect(cartOneItems).toEqual( - expect.arrayContaining([ - expect.objectContaining({ - adjustments: expect.arrayContaining([ + expect(cart.items?.length).toBe(0) + }) + }) + + describe("addShippingMethods", () => { + it("should add a shipping method to cart succesfully", async () => { + const [createdCart] = await service.create([ + { + currency_code: "eur", + }, + ]) + + const [method] = await service.addShippingMethods(createdCart.id, [ + { + amount: 100, + name: "Test", + }, + ]) + + const cart = await service.retrieve(createdCart.id, { + relations: ["shipping_methods"], + }) + + expect(method.id).toBe(cart.shipping_methods![0].id) + }) + + it("should throw when amount is negative", async () => { + const [createdCart] = await service.create([ + { + currency_code: "eur", + }, + ]) + + const error = await service + .addShippingMethods(createdCart.id, [ + { + amount: -100, + name: "Test", + }, + ]) + .catch((e) => e) + + expect(error.name).toBe(CheckConstraintViolationException.name) + }) + + it("should add multiple shipping methods to multiple carts succesfully", async () => { + let [eurCart] = await service.create([ + { + currency_code: "eur", + }, + ]) + + let [usdCart] = await service.create([ + { + currency_code: "usd", + }, + ]) + + const methods = await service.addShippingMethods([ + { + cart_id: eurCart.id, + amount: 100, + name: "Test One", + }, + { + cart_id: usdCart.id, + amount: 100, + name: "Test One", + }, + ]) + + const carts = await service.list( + { id: [eurCart.id, usdCart.id] }, + { relations: ["shipping_methods"] } + ) + + eurCart = carts.find((c) => c.currency_code === "eur")! + usdCart = carts.find((c) => c.currency_code === "usd")! + + const eurMethods = methods.filter((m) => m.cart_id === eurCart.id) + const usdMethods = methods.filter((m) => m.cart_id === usdCart.id) + + expect(eurCart.shipping_methods![0].id).toBe(eurMethods[0].id) + expect(usdCart.shipping_methods![0].id).toBe(usdMethods[0].id) + + expect(eurCart.shipping_methods?.length).toBe(1) + expect(usdCart.shipping_methods?.length).toBe(1) + }) + }) + + describe("removeShippingMethods", () => { + it("should remove a line item succesfully", async () => { + const [createdCart] = await service.create([ + { + currency_code: "eur", + }, + ]) + + const [method] = await service.addShippingMethods(createdCart.id, [ + { + amount: 100, + name: "test", + }, + ]) + + expect(method.id).not.toBe(null) + + await service.softDeleteShippingMethods([method.id]) + + const cart = await service.retrieve(createdCart.id, { + relations: ["shipping_methods"], + }) + + expect(cart.shipping_methods?.length).toBe(0) + }) + }) + + describe("setLineItemAdjustments", () => { + it("should set line item adjustments for a cart", async () => { + const [createdCart] = await service.create([ + { + currency_code: "eur", + }, + ]) + + const [itemOne] = await service.addLineItems(createdCart.id, [ + { + quantity: 1, + unit_price: 100, + title: "test", + }, + ]) + + const [itemTwo] = await service.addLineItems(createdCart.id, [ + { + quantity: 2, + unit_price: 200, + title: "test-2", + }, + ]) + + const adjustments = await service.setLineItemAdjustments( + createdCart.id, + [ + { + item_id: itemOne.id, + amount: 100, + code: "FREE", + }, + { + item_id: itemTwo.id, + amount: 200, + code: "FREE-2", + }, + ] + ) + + expect(adjustments).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + item_id: itemOne.id, + amount: 100, + code: "FREE", + }), + expect.objectContaining({ + item_id: itemTwo.id, + amount: 200, + code: "FREE-2", + }), + ]) + ) + }) + + it("should replace line item adjustments for a cart", async () => { + const [createdCart] = await service.create([ + { + currency_code: "eur", + }, + ]) + + const [itemOne] = await service.addLineItems(createdCart.id, [ + { + quantity: 1, + unit_price: 100, + title: "test", + }, + ]) + + const adjustments = await service.setLineItemAdjustments( + createdCart.id, + [ + { + item_id: itemOne.id, + amount: 100, + code: "FREE", + }, + ] + ) + + expect(adjustments).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + item_id: itemOne.id, + amount: 100, + code: "FREE", + }), + ]) + ) + + await service.setLineItemAdjustments(createdCart.id, [ + { + item_id: itemOne.id, + amount: 50, + code: "50%", + }, + ]) + + const cart = await service.retrieve(createdCart.id, { + relations: ["items.adjustments"], + }) + + expect(cart.items).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + id: itemOne.id, + adjustments: expect.arrayContaining([ + expect.objectContaining({ + item_id: itemOne.id, + amount: 50, + code: "50%", + }), + ]), + }), + ]) + ) + + expect(cart.items?.length).toBe(1) + expect(cart.items?.[0].adjustments?.length).toBe(1) + }) + + it("should remove all line item adjustments for a cart", async () => { + const [createdCart] = await service.create([ + { + currency_code: "eur", + }, + ]) + + const [itemOne] = await service.addLineItems(createdCart.id, [ + { + quantity: 1, + unit_price: 100, + title: "test", + }, + ]) + + const adjustments = await service.setLineItemAdjustments( + createdCart.id, + [ + { + item_id: itemOne.id, + amount: 100, + code: "FREE", + }, + ] + ) + + expect(adjustments).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + item_id: itemOne.id, + amount: 100, + code: "FREE", + }), + ]) + ) + + await service.setLineItemAdjustments(createdCart.id, []) + + const cart = await service.retrieve(createdCart.id, { + relations: ["items.adjustments"], + }) + + expect(cart.items).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + id: itemOne.id, + adjustments: [], + }), + ]) + ) + + expect(cart.items?.length).toBe(1) + expect(cart.items?.[0].adjustments?.length).toBe(0) + }) + + it("should update line item adjustments for a cart", async () => { + const [createdCart] = await service.create([ + { + currency_code: "eur", + }, + ]) + + const [itemOne] = await service.addLineItems(createdCart.id, [ + { + quantity: 1, + unit_price: 100, + title: "test", + }, + ]) + + const adjustments = await service.setLineItemAdjustments( + createdCart.id, + [ + { + item_id: itemOne.id, + amount: 100, + code: "FREE", + }, + ] + ) + + expect(adjustments).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + item_id: itemOne.id, + amount: 100, + code: "FREE", + }), + ]) + ) + + await service.setLineItemAdjustments(createdCart.id, [ + { + id: adjustments[0].id, + item_id: itemOne.id, + amount: 50, + code: "50%", + }, + ]) + + const cart = await service.retrieve(createdCart.id, { + relations: ["items.adjustments"], + }) + + expect(cart.items).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + id: itemOne.id, + adjustments: [ + expect.objectContaining({ + id: adjustments[0].id, + item_id: itemOne.id, + amount: 50, + code: "50%", + }), + ], + }), + ]) + ) + + expect(cart.items?.length).toBe(1) + expect(cart.items?.[0].adjustments?.length).toBe(1) + }) + }) + + describe("addLineItemAdjustments", () => { + it("should add line item adjustments for items in a cart", async () => { + const [createdCart] = await service.create([ + { + currency_code: "eur", + }, + ]) + + const [itemOne] = await service.addLineItems(createdCart.id, [ + { + quantity: 1, + unit_price: 100, + title: "test", + }, + ]) + + const adjustments = await service.addLineItemAdjustments( + createdCart.id, + [ + { + item_id: itemOne.id, + amount: 100, + code: "FREE", + }, + ] + ) + + expect(adjustments).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + item_id: itemOne.id, + amount: 100, + code: "FREE", + }), + ]) + ) + }) + + it("should add multiple line item adjustments for multiple line items", async () => { + const [createdCart] = await service.create([ + { + currency_code: "eur", + }, + ]) + + const [itemOne] = await service.addLineItems(createdCart.id, [ + { + quantity: 1, + unit_price: 100, + title: "test", + }, + ]) + const [itemTwo] = await service.addLineItems(createdCart.id, [ + { + quantity: 2, + unit_price: 200, + title: "test-2", + }, + ]) + + const adjustments = await service.addLineItemAdjustments( + createdCart.id, + [ + { + item_id: itemOne.id, + amount: 100, + code: "FREE", + }, + { + item_id: itemTwo.id, + amount: 150, + code: "CODE-2", + }, + ] + ) + + expect(adjustments).toEqual( + expect.arrayContaining([ expect.objectContaining({ item_id: itemOne.id, amount: 100, code: "FREE", }), - ]), - }), - ]) - ) - expect(cartTwoItems).toEqual( - expect.arrayContaining([ - expect.objectContaining({ - adjustments: expect.arrayContaining([ expect.objectContaining({ item_id: itemTwo.id, amount: 150, code: "CODE-2", }), - ]), - }), - ]) - ) - }) - }) + ]) + ) + }) - describe("removeLineItemAdjustments", () => { - it("should remove a line item succesfully", async () => { - const [createdCart] = await service.create([ - { - currency_code: "eur", - }, - ]) + it("should add line item adjustments for line items on multiple carts", async () => { + const [cartOne] = await service.create([ + { + currency_code: "eur", + }, + ]) + const [cartTwo] = await service.create([ + { + currency_code: "usd", + }, + ]) - const [item] = await service.addLineItems(createdCart.id, [ - { - quantity: 1, - unit_price: 100, - title: "test", - }, - ]) + const [itemOne] = await service.addLineItems(cartOne.id, [ + { + quantity: 1, + unit_price: 100, + title: "test", + }, + ]) + const [itemTwo] = await service.addLineItems(cartTwo.id, [ + { + quantity: 2, + unit_price: 200, + title: "test-2", + }, + ]) - const [adjustment] = await service.addLineItemAdjustments( - createdCart.id, - [ - { + await service.addLineItemAdjustments([ + // item from cart one + { + item_id: itemOne.id, + amount: 100, + code: "FREE", + }, + // item from cart two + { + item_id: itemTwo.id, + amount: 150, + code: "CODE-2", + }, + ]) + + const cartOneItems = await service.listLineItems( + { cart_id: cartOne.id }, + { relations: ["adjustments"] } + ) + const cartTwoItems = await service.listLineItems( + { cart_id: cartTwo.id }, + { relations: ["adjustments"] } + ) + + expect(cartOneItems).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + adjustments: expect.arrayContaining([ + expect.objectContaining({ + item_id: itemOne.id, + amount: 100, + code: "FREE", + }), + ]), + }), + ]) + ) + expect(cartTwoItems).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + adjustments: expect.arrayContaining([ + expect.objectContaining({ + item_id: itemTwo.id, + amount: 150, + code: "CODE-2", + }), + ]), + }), + ]) + ) + }) + }) + + describe("removeLineItemAdjustments", () => { + it("should remove a line item succesfully", async () => { + const [createdCart] = await service.create([ + { + currency_code: "eur", + }, + ]) + + const [item] = await service.addLineItems(createdCart.id, [ + { + quantity: 1, + unit_price: 100, + title: "test", + }, + ]) + + const [adjustment] = await service.addLineItemAdjustments( + createdCart.id, + [ + { + item_id: item.id, + amount: 50, + }, + ] + ) + + expect(adjustment.item_id).toBe(item.id) + + await service.softDeleteLineItemAdjustments([adjustment.id]) + + const adjustments = await service.listLineItemAdjustments({ item_id: item.id, - amount: 50, - }, - ] - ) + }) - expect(adjustment.item_id).toBe(item.id) - - await service.softDeleteLineItemAdjustments([adjustment.id]) - - const adjustments = await service.listLineItemAdjustments({ - item_id: item.id, + expect(adjustments?.length).toBe(0) + }) }) - expect(adjustments?.length).toBe(0) - }) - }) + describe("setShippingMethodAdjustments", () => { + it("should set shipping method adjustments for a cart", async () => { + const [createdCart] = await service.create([ + { + currency_code: "eur", + }, + ]) - describe("setShippingMethodAdjustments", () => { - it("should set shipping method adjustments for a cart", async () => { - const [createdCart] = await service.create([ - { - currency_code: "eur", - }, - ]) + const [shippingMethodOne] = await service.addShippingMethods( + createdCart.id, + [ + { + amount: 100, + name: "test", + }, + ] + ) - const [shippingMethodOne] = await service.addShippingMethods( - createdCart.id, - [ - { - amount: 100, - name: "test", - }, - ] - ) + const [shippingMethodTwo] = await service.addShippingMethods( + createdCart.id, + [ + { + amount: 200, + name: "test-2", + }, + ] + ) - const [shippingMethodTwo] = await service.addShippingMethods( - createdCart.id, - [ - { - amount: 200, - name: "test-2", - }, - ] - ) - - const adjustments = await service.setShippingMethodAdjustments( - createdCart.id, - [ - { - shipping_method_id: shippingMethodOne.id, - amount: 100, - code: "FREE", - }, - { - shipping_method_id: shippingMethodTwo.id, - amount: 200, - code: "FREE-2", - }, - ] - ) - - expect(adjustments).toEqual( - expect.arrayContaining([ - expect.objectContaining({ - shipping_method_id: shippingMethodOne.id, - amount: 100, - code: "FREE", - }), - expect.objectContaining({ - shipping_method_id: shippingMethodTwo.id, - amount: 200, - code: "FREE-2", - }), - ]) - ) - }) - - it("should replace shipping method adjustments for a cart", async () => { - const [createdCart] = await service.create([ - { - currency_code: "eur", - }, - ]) - - const [shippingMethodOne] = await service.addShippingMethods( - createdCart.id, - [ - { - amount: 100, - name: "test", - }, - ] - ) - - const adjustments = await service.setShippingMethodAdjustments( - createdCart.id, - [ - { - shipping_method_id: shippingMethodOne.id, - amount: 100, - code: "FREE", - }, - ] - ) - - expect(adjustments).toEqual( - expect.arrayContaining([ - expect.objectContaining({ - shipping_method_id: shippingMethodOne.id, - amount: 100, - code: "FREE", - }), - ]) - ) - - await service.setShippingMethodAdjustments(createdCart.id, [ - { - shipping_method_id: shippingMethodOne.id, - amount: 50, - code: "50%", - }, - ]) - - const cart = await service.retrieve(createdCart.id, { - relations: ["shipping_methods.adjustments"], - }) - - expect(cart.shipping_methods).toEqual( - expect.arrayContaining([ - expect.objectContaining({ - id: shippingMethodOne.id, - cart_id: createdCart.id, - adjustments: expect.arrayContaining([ - expect.objectContaining({ + const adjustments = await service.setShippingMethodAdjustments( + createdCart.id, + [ + { shipping_method_id: shippingMethodOne.id, - amount: 50, - code: "50%", - }), - ]), - }), - ]) - ) + amount: 100, + code: "FREE", + }, + { + shipping_method_id: shippingMethodTwo.id, + amount: 200, + code: "FREE-2", + }, + ] + ) - expect(cart.shipping_methods?.length).toBe(1) - expect(cart.shipping_methods?.[0].adjustments?.length).toBe(1) - }) - - it("should remove all shipping method adjustments for a cart", async () => { - const [createdCart] = await service.create([ - { - currency_code: "eur", - }, - ]) - - const [shippingMethodOne] = await service.addShippingMethods( - createdCart.id, - [ - { - amount: 100, - name: "test", - }, - ] - ) - - const adjustments = await service.setShippingMethodAdjustments( - createdCart.id, - [ - { - shipping_method_id: shippingMethodOne.id, - amount: 100, - code: "FREE", - }, - ] - ) - - expect(adjustments).toEqual( - expect.arrayContaining([ - expect.objectContaining({ - shipping_method_id: shippingMethodOne.id, - amount: 100, - code: "FREE", - }), - ]) - ) - - await service.setShippingMethodAdjustments(createdCart.id, []) - - const cart = await service.retrieve(createdCart.id, { - relations: ["shipping_methods.adjustments"], - }) - - expect(cart.shipping_methods).toEqual( - expect.arrayContaining([ - expect.objectContaining({ - id: shippingMethodOne.id, - adjustments: [], - }), - ]) - ) - - expect(cart.shipping_methods?.length).toBe(1) - expect(cart.shipping_methods?.[0].adjustments?.length).toBe(0) - }) - - it("should update shipping method adjustments for a cart", async () => { - const [createdCart] = await service.create([ - { - currency_code: "eur", - }, - ]) - - const [shippingMethodOne] = await service.addShippingMethods( - createdCart.id, - [ - { - amount: 100, - name: "test", - }, - ] - ) - - const adjustments = await service.setShippingMethodAdjustments( - createdCart.id, - [ - { - shipping_method_id: shippingMethodOne.id, - amount: 100, - code: "FREE", - }, - ] - ) - - expect(adjustments).toEqual( - expect.arrayContaining([ - expect.objectContaining({ - shipping_method_id: shippingMethodOne.id, - amount: 100, - code: "FREE", - }), - ]) - ) - - await service.setShippingMethodAdjustments(createdCart.id, [ - { - id: adjustments[0].id, - amount: 50, - code: "50%", - }, - ]) - - const cart = await service.retrieve(createdCart.id, { - relations: ["shipping_methods.adjustments"], - }) - - expect(cart.shipping_methods).toEqual( - expect.arrayContaining([ - expect.objectContaining({ - id: shippingMethodOne.id, - adjustments: [ - expect.objectContaining({ - id: adjustments[0].id, - shipping_method_id: shippingMethodOne.id, - amount: 50, - code: "50%", - }), - ], - }), - ]) - ) - - expect(cart.shipping_methods?.length).toBe(1) - expect(cart.shipping_methods?.[0].adjustments?.length).toBe(1) - }) - }) - - describe("addShippingMethodAdjustments", () => { - it("should add shipping method adjustments in a cart", async () => { - const [createdCart] = await service.create([ - { - currency_code: "eur", - }, - ]) - - const [shippingMethodOne] = await service.addShippingMethods( - createdCart.id, - [ - { - amount: 100, - name: "test", - }, - ] - ) - - const adjustments = await service.addShippingMethodAdjustments( - createdCart.id, - [ - { - shipping_method_id: shippingMethodOne.id, - amount: 100, - code: "FREE", - }, - ] - ) - - expect(adjustments).toEqual( - expect.arrayContaining([ - expect.objectContaining({ - shipping_method_id: shippingMethodOne.id, - amount: 100, - code: "FREE", - }), - ]) - ) - }) - - it("should add multiple shipping method adjustments for multiple shipping methods", async () => { - const [createdCart] = await service.create([ - { - currency_code: "eur", - }, - ]) - - const [shippingMethodOne] = await service.addShippingMethods( - createdCart.id, - [ - { - amount: 100, - name: "test", - }, - ] - ) - const [shippingMethodTwo] = await service.addShippingMethods( - createdCart.id, - [ - { - amount: 200, - name: "test-2", - }, - ] - ) - - const adjustments = await service.addShippingMethodAdjustments( - createdCart.id, - [ - { - shipping_method_id: shippingMethodOne.id, - amount: 100, - code: "FREE", - }, - { - shipping_method_id: shippingMethodTwo.id, - amount: 150, - code: "CODE-2", - }, - ] - ) - - expect(adjustments).toEqual( - expect.arrayContaining([ - expect.objectContaining({ - shipping_method_id: shippingMethodOne.id, - amount: 100, - code: "FREE", - }), - expect.objectContaining({ - shipping_method_id: shippingMethodTwo.id, - amount: 150, - code: "CODE-2", - }), - ]) - ) - }) - - it("should add shipping method adjustments for shipping methods on multiple carts", async () => { - const [cartOne] = await service.create([ - { - currency_code: "eur", - }, - ]) - const [cartTwo] = await service.create([ - { - currency_code: "usd", - }, - ]) - - const [shippingMethodOne] = await service.addShippingMethods(cartOne.id, [ - { - amount: 100, - name: "test", - }, - ]) - const [shippingMethodTwo] = await service.addShippingMethods(cartTwo.id, [ - { - amount: 200, - name: "test-2", - }, - ]) - - await service.addShippingMethodAdjustments([ - // item from cart one - { - shipping_method_id: shippingMethodOne.id, - amount: 100, - code: "FREE", - }, - // item from cart two - { - shipping_method_id: shippingMethodTwo.id, - amount: 150, - code: "CODE-2", - }, - ]) - - const cartOneMethods = await service.listShippingMethods( - { cart_id: cartOne.id }, - { relations: ["adjustments"] } - ) - - const cartTwoMethods = await service.listShippingMethods( - { cart_id: cartTwo.id }, - { relations: ["adjustments"] } - ) - - expect(cartOneMethods).toEqual( - expect.arrayContaining([ - expect.objectContaining({ - adjustments: expect.arrayContaining([ + expect(adjustments).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + shipping_method_id: shippingMethodOne.id, + amount: 100, + code: "FREE", + }), + expect.objectContaining({ + shipping_method_id: shippingMethodTwo.id, + amount: 200, + code: "FREE-2", + }), + ]) + ) + }) + + it("should replace shipping method adjustments for a cart", async () => { + const [createdCart] = await service.create([ + { + currency_code: "eur", + }, + ]) + + const [shippingMethodOne] = await service.addShippingMethods( + createdCart.id, + [ + { + amount: 100, + name: "test", + }, + ] + ) + + const adjustments = await service.setShippingMethodAdjustments( + createdCart.id, + [ + { + shipping_method_id: shippingMethodOne.id, + amount: 100, + code: "FREE", + }, + ] + ) + + expect(adjustments).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + shipping_method_id: shippingMethodOne.id, + amount: 100, + code: "FREE", + }), + ]) + ) + + await service.setShippingMethodAdjustments(createdCart.id, [ + { + shipping_method_id: shippingMethodOne.id, + amount: 50, + code: "50%", + }, + ]) + + const cart = await service.retrieve(createdCart.id, { + relations: ["shipping_methods.adjustments"], + }) + + expect(cart.shipping_methods).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + id: shippingMethodOne.id, + cart_id: createdCart.id, + adjustments: expect.arrayContaining([ + expect.objectContaining({ + shipping_method_id: shippingMethodOne.id, + amount: 50, + code: "50%", + }), + ]), + }), + ]) + ) + + expect(cart.shipping_methods?.length).toBe(1) + expect(cart.shipping_methods?.[0].adjustments?.length).toBe(1) + }) + + it("should remove all shipping method adjustments for a cart", async () => { + const [createdCart] = await service.create([ + { + currency_code: "eur", + }, + ]) + + const [shippingMethodOne] = await service.addShippingMethods( + createdCart.id, + [ + { + amount: 100, + name: "test", + }, + ] + ) + + const adjustments = await service.setShippingMethodAdjustments( + createdCart.id, + [ + { + shipping_method_id: shippingMethodOne.id, + amount: 100, + code: "FREE", + }, + ] + ) + + expect(adjustments).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + shipping_method_id: shippingMethodOne.id, + amount: 100, + code: "FREE", + }), + ]) + ) + + await service.setShippingMethodAdjustments(createdCart.id, []) + + const cart = await service.retrieve(createdCart.id, { + relations: ["shipping_methods.adjustments"], + }) + + expect(cart.shipping_methods).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + id: shippingMethodOne.id, + adjustments: [], + }), + ]) + ) + + expect(cart.shipping_methods?.length).toBe(1) + expect(cart.shipping_methods?.[0].adjustments?.length).toBe(0) + }) + + it("should update shipping method adjustments for a cart", async () => { + const [createdCart] = await service.create([ + { + currency_code: "eur", + }, + ]) + + const [shippingMethodOne] = await service.addShippingMethods( + createdCart.id, + [ + { + amount: 100, + name: "test", + }, + ] + ) + + const adjustments = await service.setShippingMethodAdjustments( + createdCart.id, + [ + { + shipping_method_id: shippingMethodOne.id, + amount: 100, + code: "FREE", + }, + ] + ) + + expect(adjustments).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + shipping_method_id: shippingMethodOne.id, + amount: 100, + code: "FREE", + }), + ]) + ) + + await service.setShippingMethodAdjustments(createdCart.id, [ + { + id: adjustments[0].id, + amount: 50, + code: "50%", + }, + ]) + + const cart = await service.retrieve(createdCart.id, { + relations: ["shipping_methods.adjustments"], + }) + + expect(cart.shipping_methods).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + id: shippingMethodOne.id, + adjustments: [ + expect.objectContaining({ + id: adjustments[0].id, + shipping_method_id: shippingMethodOne.id, + amount: 50, + code: "50%", + }), + ], + }), + ]) + ) + + expect(cart.shipping_methods?.length).toBe(1) + expect(cart.shipping_methods?.[0].adjustments?.length).toBe(1) + }) + }) + + describe("addShippingMethodAdjustments", () => { + it("should add shipping method adjustments in a cart", async () => { + const [createdCart] = await service.create([ + { + currency_code: "eur", + }, + ]) + + const [shippingMethodOne] = await service.addShippingMethods( + createdCart.id, + [ + { + amount: 100, + name: "test", + }, + ] + ) + + const adjustments = await service.addShippingMethodAdjustments( + createdCart.id, + [ + { + shipping_method_id: shippingMethodOne.id, + amount: 100, + code: "FREE", + }, + ] + ) + + expect(adjustments).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + shipping_method_id: shippingMethodOne.id, + amount: 100, + code: "FREE", + }), + ]) + ) + }) + + it("should add multiple shipping method adjustments for multiple shipping methods", async () => { + const [createdCart] = await service.create([ + { + currency_code: "eur", + }, + ]) + + const [shippingMethodOne] = await service.addShippingMethods( + createdCart.id, + [ + { + amount: 100, + name: "test", + }, + ] + ) + const [shippingMethodTwo] = await service.addShippingMethods( + createdCart.id, + [ + { + amount: 200, + name: "test-2", + }, + ] + ) + + const adjustments = await service.addShippingMethodAdjustments( + createdCart.id, + [ + { + shipping_method_id: shippingMethodOne.id, + amount: 100, + code: "FREE", + }, + { + shipping_method_id: shippingMethodTwo.id, + amount: 150, + code: "CODE-2", + }, + ] + ) + + expect(adjustments).toEqual( + expect.arrayContaining([ expect.objectContaining({ shipping_method_id: shippingMethodOne.id, amount: 100, code: "FREE", }), - ]), - }), - ]) - ) - expect(cartTwoMethods).toEqual( - expect.arrayContaining([ - expect.objectContaining({ - adjustments: expect.arrayContaining([ expect.objectContaining({ shipping_method_id: shippingMethodTwo.id, amount: 150, code: "CODE-2", }), - ]), - }), - ]) - ) - }) + ]) + ) + }) - it("should throw if shipping method is not associated with cart", async () => { - const [cartOne] = await service.create([ - { - currency_code: "eur", - }, - ]) + it("should add shipping method adjustments for shipping methods on multiple carts", async () => { + const [cartOne] = await service.create([ + { + currency_code: "eur", + }, + ]) + const [cartTwo] = await service.create([ + { + currency_code: "usd", + }, + ]) - const [cartTwo] = await service.create([ - { - currency_code: "eur", - }, - ]) + const [shippingMethodOne] = await service.addShippingMethods( + cartOne.id, + [ + { + amount: 100, + name: "test", + }, + ] + ) + const [shippingMethodTwo] = await service.addShippingMethods( + cartTwo.id, + [ + { + amount: 200, + name: "test-2", + }, + ] + ) - const [shippingMethodOne] = await service.addShippingMethods(cartOne.id, [ - { - amount: 100, - name: "test", - }, - ]) + await service.addShippingMethodAdjustments([ + // item from cart one + { + shipping_method_id: shippingMethodOne.id, + amount: 100, + code: "FREE", + }, + // item from cart two + { + shipping_method_id: shippingMethodTwo.id, + amount: 150, + code: "CODE-2", + }, + ]) - const error = await service - .addShippingMethodAdjustments(cartTwo.id, [ - { - shipping_method_id: shippingMethodOne.id, - amount: 100, - code: "FREE", - }, - ]) - .catch((e) => e) + const cartOneMethods = await service.listShippingMethods( + { cart_id: cartOne.id }, + { relations: ["adjustments"] } + ) - expect(error.message).toBe( - `Shipping method with id ${shippingMethodOne.id} does not exist on cart with id ${cartTwo.id}` - ) - }) - }) + const cartTwoMethods = await service.listShippingMethods( + { cart_id: cartTwo.id }, + { relations: ["adjustments"] } + ) - describe("removeShippingMethodAdjustments", () => { - it("should remove a shipping method succesfully", async () => { - const [createdCart] = await service.create([ - { - currency_code: "eur", - }, - ]) + expect(cartOneMethods).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + adjustments: expect.arrayContaining([ + expect.objectContaining({ + shipping_method_id: shippingMethodOne.id, + amount: 100, + code: "FREE", + }), + ]), + }), + ]) + ) + expect(cartTwoMethods).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + adjustments: expect.arrayContaining([ + expect.objectContaining({ + shipping_method_id: shippingMethodTwo.id, + amount: 150, + code: "CODE-2", + }), + ]), + }), + ]) + ) + }) - const [method] = await service.addShippingMethods(createdCart.id, [ - { - amount: 100, - name: "test", - }, - ]) + it("should throw if shipping method is not associated with cart", async () => { + const [cartOne] = await service.create([ + { + currency_code: "eur", + }, + ]) - const [adjustment] = await service.addShippingMethodAdjustments( - createdCart.id, - [ - { + const [cartTwo] = await service.create([ + { + currency_code: "eur", + }, + ]) + + const [shippingMethodOne] = await service.addShippingMethods( + cartOne.id, + [ + { + amount: 100, + name: "test", + }, + ] + ) + + const error = await service + .addShippingMethodAdjustments(cartTwo.id, [ + { + shipping_method_id: shippingMethodOne.id, + amount: 100, + code: "FREE", + }, + ]) + .catch((e) => e) + + expect(error.message).toBe( + `Shipping method with id ${shippingMethodOne.id} does not exist on cart with id ${cartTwo.id}` + ) + }) + }) + + describe("removeShippingMethodAdjustments", () => { + it("should remove a shipping method succesfully", async () => { + const [createdCart] = await service.create([ + { + currency_code: "eur", + }, + ]) + + const [method] = await service.addShippingMethods(createdCart.id, [ + { + amount: 100, + name: "test", + }, + ]) + + const [adjustment] = await service.addShippingMethodAdjustments( + createdCart.id, + [ + { + shipping_method_id: method.id, + amount: 50, + code: "50%", + }, + ] + ) + + expect(adjustment.shipping_method_id).toBe(method.id) + + await service.softDeleteShippingMethodAdjustments([adjustment.id]) + + const adjustments = await service.listShippingMethodAdjustments({ shipping_method_id: method.id, - amount: 50, - code: "50%", - }, - ] - ) + }) - expect(adjustment.shipping_method_id).toBe(method.id) - - await service.softDeleteShippingMethodAdjustments([adjustment.id]) - - const adjustments = await service.listShippingMethodAdjustments({ - shipping_method_id: method.id, + expect(adjustments?.length).toBe(0) + }) }) - expect(adjustments?.length).toBe(0) - }) - }) + describe("setLineItemTaxLines", () => { + it("should set line item tax lines for a cart", async () => { + const [createdCart] = await service.create([ + { + currency_code: "eur", + }, + ]) - describe("setLineItemTaxLines", () => { - it("should set line item tax lines for a cart", async () => { - const [createdCart] = await service.create([ - { - currency_code: "eur", - }, - ]) + const [itemOne] = await service.addLineItems(createdCart.id, [ + { + quantity: 1, + unit_price: 100, + title: "test", + }, + ]) - const [itemOne] = await service.addLineItems(createdCart.id, [ - { - quantity: 1, - unit_price: 100, - title: "test", - }, - ]) + const [itemTwo] = await service.addLineItems(createdCart.id, [ + { + quantity: 2, + unit_price: 200, + title: "test-2", + }, + ]) - const [itemTwo] = await service.addLineItems(createdCart.id, [ - { - quantity: 2, - unit_price: 200, - title: "test-2", - }, - ]) + const taxLines = await service.setLineItemTaxLines(createdCart.id, [ + { + item_id: itemOne.id, + rate: 20, + code: "TX", + }, + { + item_id: itemTwo.id, + rate: 20, + code: "TX", + }, + ]) - const taxLines = await service.setLineItemTaxLines(createdCart.id, [ - { - item_id: itemOne.id, - rate: 20, - code: "TX", - }, - { - item_id: itemTwo.id, - rate: 20, - code: "TX", - }, - ]) - - expect(taxLines).toEqual( - expect.arrayContaining([ - expect.objectContaining({ - item_id: itemOne.id, - rate: 20, - code: "TX", - }), - expect.objectContaining({ - item_id: itemTwo.id, - rate: 20, - code: "TX", - }), - ]) - ) - }) - - it("should replace line item tax lines for a cart", async () => { - const [createdCart] = await service.create([ - { - currency_code: "eur", - }, - ]) - - const [itemOne] = await service.addLineItems(createdCart.id, [ - { - quantity: 1, - unit_price: 100, - title: "test", - }, - ]) - - const taxLines = await service.setLineItemTaxLines(createdCart.id, [ - { - item_id: itemOne.id, - rate: 20, - code: "TX", - }, - ]) - - expect(taxLines).toEqual( - expect.arrayContaining([ - expect.objectContaining({ - item_id: itemOne.id, - rate: 20, - code: "TX", - }), - ]) - ) - - await service.setLineItemTaxLines(createdCart.id, [ - { - item_id: itemOne.id, - rate: 25, - code: "TX-2", - }, - ]) - - const cart = await service.retrieve(createdCart.id, { - relations: ["items.tax_lines"], - }) - - expect(cart.items).toEqual( - expect.arrayContaining([ - expect.objectContaining({ - id: itemOne.id, - tax_lines: expect.arrayContaining([ - expect.objectContaining({ - item_id: itemOne.id, - rate: 25, - code: "TX-2", - }), - ]), - }), - ]) - ) - - expect(cart.items?.length).toBe(1) - expect(cart.items?.[0].tax_lines?.length).toBe(1) - }) - - it("should remove all line item tax lines for a cart", async () => { - const [createdCart] = await service.create([ - { - currency_code: "eur", - }, - ]) - - const [itemOne] = await service.addLineItems(createdCart.id, [ - { - quantity: 1, - unit_price: 100, - title: "test", - }, - ]) - - const taxLines = await service.setLineItemTaxLines(createdCart.id, [ - { - item_id: itemOne.id, - rate: 20, - code: "TX", - }, - ]) - - expect(taxLines).toEqual( - expect.arrayContaining([ - expect.objectContaining({ - item_id: itemOne.id, - rate: 20, - code: "TX", - }), - ]) - ) - - await service.setLineItemTaxLines(createdCart.id, []) - - const cart = await service.retrieve(createdCart.id, { - relations: ["items.tax_lines"], - }) - - expect(cart.items).toEqual( - expect.arrayContaining([ - expect.objectContaining({ - id: itemOne.id, - tax_lines: [], - }), - ]) - ) - - expect(cart.items?.length).toBe(1) - expect(cart.items?.[0].tax_lines?.length).toBe(0) - }) - - it("should update line item tax lines for a cart", async () => { - const [createdCart] = await service.create([ - { - currency_code: "eur", - }, - ]) - - const [itemOne] = await service.addLineItems(createdCart.id, [ - { - quantity: 1, - unit_price: 100, - title: "test", - }, - ]) - - const taxLines = await service.setLineItemTaxLines(createdCart.id, [ - { - item_id: itemOne.id, - rate: 20, - code: "TX", - }, - ]) - - expect(taxLines).toEqual( - expect.arrayContaining([ - expect.objectContaining({ - item_id: itemOne.id, - rate: 20, - code: "TX", - }), - ]) - ) - - await service.setLineItemTaxLines(createdCart.id, [ - { - id: taxLines[0].id, - item_id: itemOne.id, - rate: 25, - code: "TX", - }, - ]) - - const cart = await service.retrieve(createdCart.id, { - relations: ["items.tax_lines"], - }) - - expect(cart.items).toEqual( - expect.arrayContaining([ - expect.objectContaining({ - id: itemOne.id, - tax_lines: [ - expect.objectContaining({ - id: taxLines[0].id, - item_id: itemOne.id, - rate: 25, - code: "TX", - }), - ], - }), - ]) - ) - - expect(cart.items?.length).toBe(1) - expect(cart.items?.[0].tax_lines?.length).toBe(1) - }) - - it("should remove, update, and create line item tax lines for a cart", async () => { - const [createdCart] = await service.create([ - { - currency_code: "eur", - }, - ]) - - const [itemOne] = await service.addLineItems(createdCart.id, [ - { - quantity: 1, - unit_price: 100, - title: "test", - }, - ]) - - const taxLines = await service.setLineItemTaxLines(createdCart.id, [ - { - item_id: itemOne.id, - rate: 20, - code: "TX", - }, - { - item_id: itemOne.id, - rate: 25, - code: "TX", - }, - ]) - - expect(taxLines).toEqual( - expect.arrayContaining([ - expect.objectContaining({ - item_id: itemOne.id, - rate: 20, - code: "TX", - }), - expect.objectContaining({ - item_id: itemOne.id, - rate: 25, - code: "TX", - }), - ]) - ) - - const taxLine = taxLines.find((tx) => tx.item_id === itemOne.id) - - await service.setLineItemTaxLines(createdCart.id, [ - // update - { - id: taxLine.id, - rate: 40, - code: "TX", - }, - // create - { - item_id: itemOne.id, - rate: 25, - code: "TX-2", - }, - // remove: should remove the initial tax line for itemOne - ]) - - const cart = await service.retrieve(createdCart.id, { - relations: ["items.tax_lines"], - }) - - expect(cart.items).toEqual( - expect.arrayContaining([ - expect.objectContaining({ - id: itemOne.id, - tax_lines: [ - expect.objectContaining({ - id: taxLine!.id, - item_id: itemOne.id, - rate: 40, - code: "TX", - }), - expect.objectContaining({ - item_id: itemOne.id, - rate: 25, - code: "TX-2", - }), - ], - }), - ]) - ) - - expect(cart.items?.length).toBe(1) - expect(cart.items?.[0].tax_lines?.length).toBe(2) - }) - }) - - describe("addLineItemAdjustments", () => { - it("should add line item tax lines for items in a cart", async () => { - const [createdCart] = await service.create([ - { - currency_code: "eur", - }, - ]) - - const [itemOne] = await service.addLineItems(createdCart.id, [ - { - quantity: 1, - unit_price: 100, - title: "test", - }, - ]) - - const taxLines = await service.addLineItemTaxLines(createdCart.id, [ - { - item_id: itemOne.id, - rate: 20, - code: "TX", - }, - ]) - - expect(taxLines).toEqual( - expect.arrayContaining([ - expect.objectContaining({ - item_id: itemOne.id, - rate: 20, - code: "TX", - }), - ]) - ) - }) - - it("should add multiple line item tax lines for multiple line items", async () => { - const [createdCart] = await service.create([ - { - currency_code: "eur", - }, - ]) - - const [itemOne] = await service.addLineItems(createdCart.id, [ - { - quantity: 1, - unit_price: 100, - title: "test", - }, - ]) - const [itemTwo] = await service.addLineItems(createdCart.id, [ - { - quantity: 2, - unit_price: 200, - title: "test-2", - }, - ]) - - const taxLines = await service.addLineItemTaxLines(createdCart.id, [ - { - item_id: itemOne.id, - rate: 20, - code: "TX", - }, - { - item_id: itemTwo.id, - rate: 20, - code: "TX", - }, - ]) - - expect(taxLines).toEqual( - expect.arrayContaining([ - expect.objectContaining({ - item_id: itemOne.id, - rate: 20, - code: "TX", - }), - expect.objectContaining({ - item_id: itemTwo.id, - rate: 20, - code: "TX", - }), - ]) - ) - }) - - it("should add line item tax lines for line items on multiple carts", async () => { - const [cartOne] = await service.create([ - { - currency_code: "eur", - }, - ]) - const [cartTwo] = await service.create([ - { - currency_code: "usd", - }, - ]) - - const [itemOne] = await service.addLineItems(cartOne.id, [ - { - quantity: 1, - unit_price: 100, - title: "test", - }, - ]) - const [itemTwo] = await service.addLineItems(cartTwo.id, [ - { - quantity: 2, - unit_price: 200, - title: "test-2", - }, - ]) - - await service.addLineItemTaxLines([ - // item from cart one - { - item_id: itemOne.id, - rate: 20, - code: "TX", - }, - // item from cart two - { - item_id: itemTwo.id, - rate: 25, - code: "TX-2", - }, - ]) - - const cartOneItems = await service.listLineItems( - { cart_id: cartOne.id }, - { relations: ["tax_lines"] } - ) - const cartTwoItems = await service.listLineItems( - { cart_id: cartTwo.id }, - { relations: ["tax_lines"] } - ) - - expect(cartOneItems).toEqual( - expect.arrayContaining([ - expect.objectContaining({ - tax_lines: expect.arrayContaining([ + expect(taxLines).toEqual( + expect.arrayContaining([ expect.objectContaining({ item_id: itemOne.id, rate: 20, code: "TX", }), - ]), - }), - ]) - ) - expect(cartTwoItems).toEqual( - expect.arrayContaining([ - expect.objectContaining({ - tax_lines: expect.arrayContaining([ expect.objectContaining({ item_id: itemTwo.id, - rate: 25, - code: "TX-2", + rate: 20, + code: "TX", }), - ]), - }), - ]) - ) - }) - }) + ]) + ) + }) - describe("removeLineItemAdjustments", () => { - it("should remove line item tax line succesfully", async () => { - const [createdCart] = await service.create([ - { - currency_code: "eur", - }, - ]) + it("should replace line item tax lines for a cart", async () => { + const [createdCart] = await service.create([ + { + currency_code: "eur", + }, + ]) - const [item] = await service.addLineItems(createdCart.id, [ - { - quantity: 1, - unit_price: 100, - title: "test", - }, - ]) + const [itemOne] = await service.addLineItems(createdCart.id, [ + { + quantity: 1, + unit_price: 100, + title: "test", + }, + ]) - const [taxLine] = await service.addLineItemTaxLines(createdCart.id, [ - { - item_id: item.id, - rate: 20, - code: "TX", - }, - ]) + const taxLines = await service.setLineItemTaxLines(createdCart.id, [ + { + item_id: itemOne.id, + rate: 20, + code: "TX", + }, + ]) - expect(taxLine.item_id).toBe(item.id) + expect(taxLines).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + item_id: itemOne.id, + rate: 20, + code: "TX", + }), + ]) + ) - await service.softDeleteLineItemTaxLines([taxLine.id]) + await service.setLineItemTaxLines(createdCart.id, [ + { + item_id: itemOne.id, + rate: 25, + code: "TX-2", + }, + ]) - const taxLines = await service.listLineItemTaxLines({ - item_id: item.id, + const cart = await service.retrieve(createdCart.id, { + relations: ["items.tax_lines"], + }) + + expect(cart.items).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + id: itemOne.id, + tax_lines: expect.arrayContaining([ + expect.objectContaining({ + item_id: itemOne.id, + rate: 25, + code: "TX-2", + }), + ]), + }), + ]) + ) + + expect(cart.items?.length).toBe(1) + expect(cart.items?.[0].tax_lines?.length).toBe(1) + }) + + it("should remove all line item tax lines for a cart", async () => { + const [createdCart] = await service.create([ + { + currency_code: "eur", + }, + ]) + + const [itemOne] = await service.addLineItems(createdCart.id, [ + { + quantity: 1, + unit_price: 100, + title: "test", + }, + ]) + + const taxLines = await service.setLineItemTaxLines(createdCart.id, [ + { + item_id: itemOne.id, + rate: 20, + code: "TX", + }, + ]) + + expect(taxLines).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + item_id: itemOne.id, + rate: 20, + code: "TX", + }), + ]) + ) + + await service.setLineItemTaxLines(createdCart.id, []) + + const cart = await service.retrieve(createdCart.id, { + relations: ["items.tax_lines"], + }) + + expect(cart.items).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + id: itemOne.id, + tax_lines: [], + }), + ]) + ) + + expect(cart.items?.length).toBe(1) + expect(cart.items?.[0].tax_lines?.length).toBe(0) + }) + + it("should update line item tax lines for a cart", async () => { + const [createdCart] = await service.create([ + { + currency_code: "eur", + }, + ]) + + const [itemOne] = await service.addLineItems(createdCart.id, [ + { + quantity: 1, + unit_price: 100, + title: "test", + }, + ]) + + const taxLines = await service.setLineItemTaxLines(createdCart.id, [ + { + item_id: itemOne.id, + rate: 20, + code: "TX", + }, + ]) + + expect(taxLines).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + item_id: itemOne.id, + rate: 20, + code: "TX", + }), + ]) + ) + + await service.setLineItemTaxLines(createdCart.id, [ + { + id: taxLines[0].id, + item_id: itemOne.id, + rate: 25, + code: "TX", + }, + ]) + + const cart = await service.retrieve(createdCart.id, { + relations: ["items.tax_lines"], + }) + + expect(cart.items).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + id: itemOne.id, + tax_lines: [ + expect.objectContaining({ + id: taxLines[0].id, + item_id: itemOne.id, + rate: 25, + code: "TX", + }), + ], + }), + ]) + ) + + expect(cart.items?.length).toBe(1) + expect(cart.items?.[0].tax_lines?.length).toBe(1) + }) + + it("should remove, update, and create line item tax lines for a cart", async () => { + const [createdCart] = await service.create([ + { + currency_code: "eur", + }, + ]) + + const [itemOne] = await service.addLineItems(createdCart.id, [ + { + quantity: 1, + unit_price: 100, + title: "test", + }, + ]) + + const taxLines = await service.setLineItemTaxLines(createdCart.id, [ + { + item_id: itemOne.id, + rate: 20, + code: "TX", + }, + { + item_id: itemOne.id, + rate: 25, + code: "TX", + }, + ]) + + expect(taxLines).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + item_id: itemOne.id, + rate: 20, + code: "TX", + }), + expect.objectContaining({ + item_id: itemOne.id, + rate: 25, + code: "TX", + }), + ]) + ) + + const taxLine = taxLines.find((tx) => tx.item_id === itemOne.id) + + await service.setLineItemTaxLines(createdCart.id, [ + // update + { + id: taxLine.id, + rate: 40, + code: "TX", + }, + // create + { + item_id: itemOne.id, + rate: 25, + code: "TX-2", + }, + // remove: should remove the initial tax line for itemOne + ]) + + const cart = await service.retrieve(createdCart.id, { + relations: ["items.tax_lines"], + }) + + expect(cart.items).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + id: itemOne.id, + tax_lines: [ + expect.objectContaining({ + id: taxLine!.id, + item_id: itemOne.id, + rate: 40, + code: "TX", + }), + expect.objectContaining({ + item_id: itemOne.id, + rate: 25, + code: "TX-2", + }), + ], + }), + ]) + ) + + expect(cart.items?.length).toBe(1) + expect(cart.items?.[0].tax_lines?.length).toBe(2) + }) }) - expect(taxLines?.length).toBe(0) + describe("addLineItemAdjustments", () => { + it("should add line item tax lines for items in a cart", async () => { + const [createdCart] = await service.create([ + { + currency_code: "eur", + }, + ]) + + const [itemOne] = await service.addLineItems(createdCart.id, [ + { + quantity: 1, + unit_price: 100, + title: "test", + }, + ]) + + const taxLines = await service.addLineItemTaxLines(createdCart.id, [ + { + item_id: itemOne.id, + rate: 20, + code: "TX", + }, + ]) + + expect(taxLines).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + item_id: itemOne.id, + rate: 20, + code: "TX", + }), + ]) + ) + }) + + it("should add multiple line item tax lines for multiple line items", async () => { + const [createdCart] = await service.create([ + { + currency_code: "eur", + }, + ]) + + const [itemOne] = await service.addLineItems(createdCart.id, [ + { + quantity: 1, + unit_price: 100, + title: "test", + }, + ]) + const [itemTwo] = await service.addLineItems(createdCart.id, [ + { + quantity: 2, + unit_price: 200, + title: "test-2", + }, + ]) + + const taxLines = await service.addLineItemTaxLines(createdCart.id, [ + { + item_id: itemOne.id, + rate: 20, + code: "TX", + }, + { + item_id: itemTwo.id, + rate: 20, + code: "TX", + }, + ]) + + expect(taxLines).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + item_id: itemOne.id, + rate: 20, + code: "TX", + }), + expect.objectContaining({ + item_id: itemTwo.id, + rate: 20, + code: "TX", + }), + ]) + ) + }) + + it("should add line item tax lines for line items on multiple carts", async () => { + const [cartOne] = await service.create([ + { + currency_code: "eur", + }, + ]) + const [cartTwo] = await service.create([ + { + currency_code: "usd", + }, + ]) + + const [itemOne] = await service.addLineItems(cartOne.id, [ + { + quantity: 1, + unit_price: 100, + title: "test", + }, + ]) + const [itemTwo] = await service.addLineItems(cartTwo.id, [ + { + quantity: 2, + unit_price: 200, + title: "test-2", + }, + ]) + + await service.addLineItemTaxLines([ + // item from cart one + { + item_id: itemOne.id, + rate: 20, + code: "TX", + }, + // item from cart two + { + item_id: itemTwo.id, + rate: 25, + code: "TX-2", + }, + ]) + + const cartOneItems = await service.listLineItems( + { cart_id: cartOne.id }, + { relations: ["tax_lines"] } + ) + const cartTwoItems = await service.listLineItems( + { cart_id: cartTwo.id }, + { relations: ["tax_lines"] } + ) + + expect(cartOneItems).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + tax_lines: expect.arrayContaining([ + expect.objectContaining({ + item_id: itemOne.id, + rate: 20, + code: "TX", + }), + ]), + }), + ]) + ) + expect(cartTwoItems).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + tax_lines: expect.arrayContaining([ + expect.objectContaining({ + item_id: itemTwo.id, + rate: 25, + code: "TX-2", + }), + ]), + }), + ]) + ) + }) + }) + + describe("removeLineItemAdjustments", () => { + it("should remove line item tax line succesfully", async () => { + const [createdCart] = await service.create([ + { + currency_code: "eur", + }, + ]) + + const [item] = await service.addLineItems(createdCart.id, [ + { + quantity: 1, + unit_price: 100, + title: "test", + }, + ]) + + const [taxLine] = await service.addLineItemTaxLines(createdCart.id, [ + { + item_id: item.id, + rate: 20, + code: "TX", + }, + ]) + + expect(taxLine.item_id).toBe(item.id) + + await service.softDeleteLineItemTaxLines([taxLine.id]) + + const taxLines = await service.listLineItemTaxLines({ + item_id: item.id, + }) + + expect(taxLines?.length).toBe(0) + }) + }) }) - }) + }, }) diff --git a/packages/cart/integration-tests/setup-env.js b/packages/cart/integration-tests/setup-env.js deleted file mode 100644 index a66b6d712e..0000000000 --- a/packages/cart/integration-tests/setup-env.js +++ /dev/null @@ -1,6 +0,0 @@ -if (typeof process.env.DB_TEMP_NAME === "undefined") { - const tempName = parseInt(process.env.JEST_WORKER_ID || "1") - process.env.DB_TEMP_NAME = `medusa-cart-integration-${tempName}` -} - -process.env.MEDUSA_CART_DB_SCHEMA = "public" diff --git a/packages/cart/integration-tests/setup.js b/packages/cart/integration-tests/setup.js deleted file mode 100644 index 43f99aab4a..0000000000 --- a/packages/cart/integration-tests/setup.js +++ /dev/null @@ -1,3 +0,0 @@ -import { JestUtils } from "medusa-test-utils" - -JestUtils.afterAllHookDropDatabase() diff --git a/packages/cart/integration-tests/utils/config.ts b/packages/cart/integration-tests/utils/config.ts deleted file mode 100644 index 96eb67ff9f..0000000000 --- a/packages/cart/integration-tests/utils/config.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { ModuleServiceInitializeOptions } from "@medusajs/types" - -export const databaseOptions: ModuleServiceInitializeOptions["database"] = { - schema: "public", - clientUrl: "medusa-cart-test", -} diff --git a/packages/cart/integration-tests/utils/database.ts b/packages/cart/integration-tests/utils/database.ts deleted file mode 100644 index 1fc3ae99c0..0000000000 --- a/packages/cart/integration-tests/utils/database.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { TestDatabaseUtils } from "medusa-test-utils" - -import * as CartModels from "@models" - -const pathToMigrations = "../../src/migrations" -const mikroOrmEntities = CartModels as unknown as any[] - -export const MikroOrmWrapper = TestDatabaseUtils.getMikroOrmWrapper({ - mikroOrmEntities, - pathToMigrations, -}) - -export const MikroOrmConfig = TestDatabaseUtils.getMikroOrmConfig({ - mikroOrmEntities, - pathToMigrations, -}) - -export const DB_URL = TestDatabaseUtils.getDatabaseURL() diff --git a/packages/cart/integration-tests/utils/get-init-module-config.ts b/packages/cart/integration-tests/utils/get-init-module-config.ts deleted file mode 100644 index 574ac50a5d..0000000000 --- a/packages/cart/integration-tests/utils/get-init-module-config.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { Modules, ModulesDefinition } from "@medusajs/modules-sdk" - -import { DB_URL } from "./database" - -export function getInitModuleConfig() { - const moduleOptions = { - defaultAdapterOptions: { - database: { - clientUrl: DB_URL, - schema: process.env.MEDUSA_CART_DB_SCHEMA, - }, - }, - } - - const injectedDependencies = {} - - const modulesConfig_ = { - [Modules.CART]: { - definition: ModulesDefinition[Modules.CART], - options: moduleOptions, - }, - } - - return { - injectedDependencies, - modulesConfig: modulesConfig_, - databaseConfig: { - clientUrl: DB_URL, - schema: process.env.MEDUSA_CART_DB_SCHEMA, - }, - joinerConfig: [], - } -} diff --git a/packages/cart/integration-tests/utils/index.ts b/packages/cart/integration-tests/utils/index.ts deleted file mode 100644 index 5ca5d1bdc0..0000000000 --- a/packages/cart/integration-tests/utils/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from "./config" -export * from "./database" diff --git a/packages/cart/jest.config.js b/packages/cart/jest.config.js index 7117eaa088..07c381bf59 100644 --- a/packages/cart/jest.config.js +++ b/packages/cart/jest.config.js @@ -18,6 +18,4 @@ module.exports = { testEnvironment: `node`, moduleFileExtensions: [`js`, `ts`], modulePathIgnorePatterns: ["dist/"], - setupFiles: ["/integration-tests/setup-env.js"], - setupFilesAfterEnv: ["/integration-tests/setup.js"], } diff --git a/packages/customer/integration-tests/__tests__/services/customer-module/index.spec.ts b/packages/customer/integration-tests/__tests__/services/customer-module/index.spec.ts index 49f676475b..76d41be33b 100644 --- a/packages/customer/integration-tests/__tests__/services/customer-module/index.spec.ts +++ b/packages/customer/integration-tests/__tests__/services/customer-module/index.spec.ts @@ -1,1157 +1,1201 @@ import { ICustomerModuleService } from "@medusajs/types" -import { MikroOrmWrapper } from "../../../utils" +import { moduleIntegrationTestRunner, SuiteOptions } from "medusa-test-utils" import { Modules } from "@medusajs/modules-sdk" -import { getInitModuleConfig } from "../../../utils/get-init-module-config" -import { initModules } from "medusa-test-utils" jest.setTimeout(30000) -describe("Customer Module Service", () => { - let service: ICustomerModuleService - let shutdownFunc: () => Promise - - beforeAll(async () => { - const initModulesConfig = getInitModuleConfig() - - const { medusaApp, shutdown } = await initModules(initModulesConfig) - - service = medusaApp.modules[Modules.CUSTOMER] - - shutdownFunc = shutdown - }) - - afterAll(async () => { - await shutdownFunc() - }) - - beforeEach(async () => { - await MikroOrmWrapper.setupDatabase() - }) - - afterEach(async () => { - await MikroOrmWrapper.clearDatabase() - }) - - describe("create", () => { - it("should create a single customer", async () => { - const customerData = { - company_name: "Acme Corp", - first_name: "John", - last_name: "Doe", - email: "john.doe@acmecorp.com", - phone: "123456789", - created_by: "admin", - metadata: { membership: "gold" }, - } - const customer = await service.create(customerData) - - expect(customer).toEqual( - expect.objectContaining({ - id: expect.any(String), - company_name: "Acme Corp", - first_name: "John", - last_name: "Doe", - email: "john.doe@acmecorp.com", - phone: "123456789", - created_by: "admin", - metadata: expect.objectContaining({ membership: "gold" }), - }) - ) - }) - - it("should create address", async () => { - const customerData = { - company_name: "Acme Corp", - first_name: "John", - last_name: "Doe", - addresses: [ - { - address_1: "Testvej 1", - address_2: "Testvej 2", - city: "Testby", - country_code: "DK", - province: "Test", - postal_code: "8000", - phone: "123456789", - metadata: { membership: "gold" }, - is_default_shipping: true, - }, - ], - } - const customer = await service.create(customerData) - - const [address] = await service.listAddresses({ - customer_id: customer.id, - }) - - expect(address).toEqual( - expect.objectContaining({ - id: expect.any(String), - address_1: "Testvej 1", - address_2: "Testvej 2", - city: "Testby", - country_code: "DK", - province: "Test", - postal_code: "8000", - phone: "123456789", - metadata: expect.objectContaining({ membership: "gold" }), - is_default_shipping: true, - }) - ) - }) - - it("should fail to create two default shipping", async () => { - const customerData = { - company_name: "Acme Corp", - first_name: "John", - last_name: "Doe", - addresses: [ - { - address_1: "Testvej 1", - address_2: "Testvej 2", - city: "Testby", - country_code: "DK", - province: "Test", - postal_code: "8000", - phone: "123456789", - metadata: { membership: "gold" }, - is_default_shipping: true, - }, - { - address_1: "Test Ave 1", - address_2: "Test Ave 2", - city: "Testville", - country_code: "US", - is_default_shipping: true, - }, - ], - } - await expect(service.create(customerData)).rejects.toThrow( - "A default shipping address already exists" - ) - }) - - it("should create multiple customers", async () => { - const customersData = [ - { - company_name: "Acme Corp", - first_name: "John", - last_name: "Doe", - email: "john.doe@acmecorp.com", - phone: "123456789", - created_by: "admin", - metadata: { membership: "gold" }, - }, - { - first_name: "Jane", - last_name: "Smith", - email: "jane.smith@example.com", - phone: "987654321", - metadata: { membership: "silver" }, - }, - ] - const customer = await service.create(customersData) - - expect(customer).toEqual( - expect.arrayContaining([ - expect.objectContaining({ - id: expect.any(String), +moduleIntegrationTestRunner({ + moduleName: Modules.CUSTOMER, + testSuite: ({ + MikroOrmWrapper, + service, + }: SuiteOptions) => { + describe("Customer Module Service", () => { + describe("create", () => { + it("should create a single customer", async () => { + const customerData = { company_name: "Acme Corp", first_name: "John", last_name: "Doe", email: "john.doe@acmecorp.com", phone: "123456789", created_by: "admin", - metadata: expect.objectContaining({ membership: "gold" }), - }), - expect.objectContaining({ - id: expect.any(String), - first_name: "Jane", - last_name: "Smith", - email: "jane.smith@example.com", - phone: "987654321", - metadata: expect.objectContaining({ membership: "silver" }), - }), - ]) - ) - }) - }) + metadata: { membership: "gold" }, + } + const customer = await service.create(customerData) - describe("createCustomerGroup", () => { - it("should create a single customer group", async () => { - const group = await service.createCustomerGroup({ - name: "VIP Customers", - metadata: { priority: "high" }, - created_by: "admin", - }) - - expect(group).toEqual( - expect.objectContaining({ - id: expect.any(String), - name: "VIP Customers", - metadata: expect.objectContaining({ priority: "high" }), - created_by: "admin", + expect(customer).toEqual( + expect.objectContaining({ + id: expect.any(String), + company_name: "Acme Corp", + first_name: "John", + last_name: "Doe", + email: "john.doe@acmecorp.com", + phone: "123456789", + created_by: "admin", + metadata: expect.objectContaining({ membership: "gold" }), + }) + ) }) - ) - }) - it("should create multiple customer groups", async () => { - const groups = await service.createCustomerGroup([ - { - name: "VIP Customers", - metadata: { priority: "high" }, - created_by: "admin", - }, - { - name: "Regular Customers", - metadata: { discount: "10%" }, - created_by: "staff", - }, - ]) - - expect(groups).toEqual( - expect.arrayContaining([ - expect.objectContaining({ - id: expect.any(String), - name: "VIP Customers", - metadata: expect.objectContaining({ priority: "high" }), - created_by: "admin", - }), - expect.objectContaining({ - id: expect.any(String), - name: "Regular Customers", - metadata: expect.objectContaining({ discount: "10%" }), - created_by: "staff", - }), - ]) - ) - }) - }) - - describe("list", () => { - it("should list all customers when no filters are applied", async () => { - await service.create([ - { first_name: "John", last_name: "Doe", email: "john.doe@example.com" }, - { - first_name: "Jane", - last_name: "Smith", - email: "jane.smith@example.com", - }, - ]) - - const customers = await service.list() - - expect(customers.length).toBeGreaterThanOrEqual(2) - expect(customers).toEqual( - expect.arrayContaining([ - expect.objectContaining({ + it("should create address", async () => { + const customerData = { + company_name: "Acme Corp", first_name: "John", last_name: "Doe", - email: "john.doe@example.com", - }), - expect.objectContaining({ - first_name: "Jane", - last_name: "Smith", - email: "jane.smith@example.com", - }), - ]) - ) - }) + addresses: [ + { + address_1: "Testvej 1", + address_2: "Testvej 2", + city: "Testby", + country_code: "DK", + province: "Test", + postal_code: "8000", + phone: "123456789", + metadata: { membership: "gold" }, + is_default_shipping: true, + }, + ], + } + const customer = await service.create(customerData) - it("should list customers filtered by a specific email", async () => { - await service.create([ - { - first_name: "John", - last_name: "Doe", - email: "unique.email@example.com", - }, - { - first_name: "Jane", - last_name: "Smith", - email: "jane.smith@example.com", - }, - ]) - - const filter = { email: "unique.email@example.com" } - const customers = await service.list(filter) - - expect(customers.length).toBe(1) - expect(customers[0]).toEqual( - expect.objectContaining({ - first_name: "John", - last_name: "Doe", - email: "unique.email@example.com", - }) - ) - }) - - it("should list customers by a specific customer group", async () => { - const vipGroup = await service.createCustomerGroup({ name: "VIP" }) - - const [john] = await service.create([ - { first_name: "John", last_name: "Doe", email: "john.doe@example.com" }, - { - first_name: "Jane", - last_name: "Smith", - email: "jane.smith@example.com", - }, - ]) - - await service.addCustomerToGroup({ - customer_id: john.id, - customer_group_id: vipGroup.id, - }) - - const filter = { groups: vipGroup.id } - const customers = await service.list(filter) - - expect(customers).toEqual( - expect.arrayContaining([ - expect.objectContaining({ - first_name: "John", - last_name: "Doe", - email: "john.doe@example.com", - }), - ]) - ) - expect(customers).not.toEqual( - expect.arrayContaining([ - expect.objectContaining({ - first_name: "Jane", - last_name: "Smith", - email: "jane.smith@example.com", - }), - ]) - ) - }) - }) - - describe("addCustomerToGroup", () => { - it("should add a single customer to a customer group", async () => { - const [customer] = await service.create([ - { first_name: "John", last_name: "Doe", email: "john.doe@example.com" }, - ]) - const [group] = await service.createCustomerGroup([{ name: "VIP" }]) - - const result = await service.addCustomerToGroup({ - customer_id: customer.id, - customer_group_id: group.id, - }) - - expect(result).toEqual( - expect.objectContaining({ id: expect.any(String) }) - ) - - // Additional validation (optional): retrieve the customer and check if the group is assigned - const updatedCustomer = await service.retrieve(customer.id, { - relations: ["groups"], - }) - expect(updatedCustomer.groups).toContainEqual( - expect.objectContaining({ id: group.id }) - ) - }) - - it("should add multiple customers to customer groups", async () => { - const customers = await service.create([ - { first_name: "John", last_name: "Doe", email: "john.doe@example.com" }, - { - first_name: "Jane", - last_name: "Smith", - email: "jane.smith@example.com", - }, - ]) - const groups = await service.createCustomerGroup([ - { name: "VIP" }, - { name: "Regular" }, - ]) - - const pairs = customers.map((customer, index) => ({ - customer_id: customer.id, - customer_group_id: groups[index % groups.length].id, - })) - - const results = await service.addCustomerToGroup(pairs) - - expect(results).toEqual( - expect.arrayContaining([ - expect.objectContaining({ id: expect.any(String) }), - expect.objectContaining({ id: expect.any(String) }), - ]) - ) - - for (const customer of customers) { - const updatedCustomer = await service.retrieve(customer.id, { - relations: ["groups"], - }) - expect(updatedCustomer.groups).toContainEqual( - expect.objectContaining({ - id: groups[customers.indexOf(customer) % groups.length].id, + const [address] = await service.listAddresses({ + customer_id: customer.id, }) - ) - } - }) - }) - describe("update", () => { - it("should update a single customer", async () => { - const [customer] = await service.create([ - { first_name: "John", last_name: "Doe", email: "john.doe@example.com" }, - ]) - - const updateData = { first_name: "Jonathan" } - const updatedCustomer = await service.update(customer.id, updateData) - - expect(updatedCustomer).toEqual( - expect.objectContaining({ id: customer.id, first_name: "Jonathan" }) - ) - }) - - it("should update multiple customers by IDs", async () => { - const customers = await service.create([ - { first_name: "John", last_name: "Doe", email: "john.doe@example.com" }, - { - first_name: "Jane", - last_name: "Smith", - email: "jane.smith@example.com", - }, - ]) - - const updateData = { last_name: "Updated" } - const customerIds = customers.map((customer) => customer.id) - const updatedCustomers = await service.update(customerIds, updateData) - - updatedCustomers.forEach((updatedCustomer) => { - expect(updatedCustomer).toEqual( - expect.objectContaining({ last_name: "Updated" }) - ) - }) - }) - - it("should update customers using a selector", async () => { - await service.create([ - { first_name: "John", last_name: "Doe", email: "john.doe@example.com" }, - { first_name: "Jane", last_name: "Doe", email: "jane.doe@example.com" }, - ]) - - const selector = { last_name: "Doe" } - const updateData = { last_name: "Updated" } - const updatedCustomers = await service.update(selector, updateData) - - updatedCustomers.forEach((updatedCustomer) => { - expect(updatedCustomer).toEqual( - expect.objectContaining({ last_name: "Updated" }) - ) - }) - }) - }) - - describe("delete", () => { - it("should delete a single customer", async () => { - const [customer] = await service.create([ - { first_name: "John", last_name: "Doe", email: "john.doe@example.com" }, - ]) - - await service.delete(customer.id) - - await expect(service.retrieve(customer.id)).rejects.toThrow( - `Customer with id: ${customer.id} was not found` - ) - }) - - it("should delete multiple customers by IDs", async () => { - const customers = await service.create([ - { first_name: "John", last_name: "Doe", email: "john.doe@example.com" }, - { - first_name: "Jane", - last_name: "Smith", - email: "jane.smith@example.com", - }, - ]) - - const customerIds = customers.map((customer) => customer.id) - await service.delete(customerIds) - - for (const customer of customers) { - await expect(service.retrieve(customer.id)).rejects.toThrow( - `Customer with id: ${customer.id} was not found` - ) - } - }) - - it("should delete customers using a selector", async () => { - await service.create([ - { first_name: "John", last_name: "Doe", email: "john.doe@example.com" }, - { first_name: "Jane", last_name: "Doe", email: "jane.doe@example.com" }, - ]) - - const selector = { last_name: "Doe" } - await service.delete(selector) - - const remainingCustomers = await service.list({ last_name: "Doe" }) - expect(remainingCustomers.length).toBe(0) - }) - - it("should cascade address relationship when deleting customer", async () => { - // Creating a customer and an address - const customer = await service.create({ - first_name: "John", - last_name: "Doe", - }) - await service.addAddresses({ - customer_id: customer.id, - first_name: "John", - last_name: "Doe", - postal_code: "10001", - country_code: "US", - }) - - // verify that the address was added - const customerWithAddress = await service.retrieve(customer.id, { - relations: ["addresses"], - }) - expect(customerWithAddress.addresses?.length).toBe(1) - - await service.delete(customer.id) - - const res = await service.listAddresses({ - customer_id: customer.id, - }) - expect(res.length).toBe(0) - }) - - it("should cascade relationship when deleting customer", async () => { - // Creating a customer and a group - const customer = await service.create({ - first_name: "John", - last_name: "Doe", - }) - const group = await service.createCustomerGroup({ name: "VIP" }) - - // Adding the customer to the groups - await service.addCustomerToGroup({ - customer_id: customer.id, - customer_group_id: group.id, - }) - - await service.delete(customer.id) - - const res = await service.listCustomerGroupCustomers({ - customer_id: customer.id, - customer_group_id: group.id, - }) - expect(res.length).toBe(0) - }) - }) - - describe("deleteCustomerGroup", () => { - it("should delete a single customer group", async () => { - const [group] = await service.createCustomerGroup([{ name: "VIP" }]) - await service.deleteCustomerGroups(group.id) - - await expect( - service.retrieveCustomerGroup(group.id) - ).rejects.toThrowError(`CustomerGroup with id: ${group.id} was not found`) - }) - - it("should delete multiple customer groups by IDs", async () => { - const groups = await service.createCustomerGroup([ - { name: "VIP" }, - { name: "Regular" }, - ]) - - const groupIds = groups.map((group) => group.id) - await service.deleteCustomerGroups(groupIds) - - for (const group of groups) { - await expect( - service.retrieveCustomerGroup(group.id) - ).rejects.toThrowError( - `CustomerGroup with id: ${group.id} was not found` - ) - } - }) - - it("should delete customer groups using a selector", async () => { - await service.createCustomerGroup([{ name: "VIP" }, { name: "Regular" }]) - - const selector = { name: "VIP" } - await service.deleteCustomerGroups(selector) - - const remainingGroups = await service.listCustomerGroups({ name: "VIP" }) - expect(remainingGroups.length).toBe(0) - }) - - it("should cascade relationship when deleting customer group", async () => { - // Creating a customer and a group - const customer = await service.create({ - first_name: "John", - last_name: "Doe", - }) - const group = await service.createCustomerGroup({ name: "VIP" }) - - // Adding the customer to the groups - await service.addCustomerToGroup({ - customer_id: customer.id, - customer_group_id: group.id, - }) - - await service.deleteCustomerGroups(group.id) - - const res = await service.listCustomerGroupCustomers({ - customer_id: customer.id, - customer_group_id: group.id, - }) - expect(res.length).toBe(0) - }) - }) - - describe("addAddresses", () => { - it("should add a single address to a customer", async () => { - const customer = await service.create({ - first_name: "John", - last_name: "Doe", - }) - const address = await service.addAddresses({ - customer_id: customer.id, - first_name: "John", - last_name: "Doe", - postal_code: "10001", - country_code: "US", - }) - const [customerWithAddress] = await service.list( - { id: customer.id }, - { relations: ["addresses"] } - ) - - expect(customerWithAddress.addresses).toEqual([ - expect.objectContaining({ id: address.id }), - ]) - }) - - it("should add multiple addresses to a customer", async () => { - const customer = await service.create({ - first_name: "John", - last_name: "Doe", - }) - const addresses = await service.addAddresses([ - { - customer_id: customer.id, - first_name: "John", - last_name: "Doe", - postal_code: "10001", - country_code: "US", - }, - { - customer_id: customer.id, - first_name: "John", - last_name: "Doe", - postal_code: "10002", - country_code: "US", - }, - ]) - const [customerWithAddresses] = await service.list( - { id: customer.id }, - { relations: ["addresses"] } - ) - - expect(customerWithAddresses.addresses).toEqual( - expect.arrayContaining([ - expect.objectContaining({ id: addresses[0].id }), - expect.objectContaining({ id: addresses[1].id }), - ]) - ) - }) - - it("should only be possible to add one default shipping address per customer", async () => { - const customer = await service.create({ - first_name: "John", - last_name: "Doe", - }) - await service.addAddresses({ - customer_id: customer.id, - first_name: "John", - last_name: "Doe", - postal_code: "10001", - country_code: "US", - is_default_shipping: true, - }) - await service.addAddresses({ - customer_id: customer.id, - first_name: "John", - last_name: "Doe", - postal_code: "10001", - country_code: "US", - is_default_shipping: false, - }) - - await expect( - service.addAddresses({ - customer_id: customer.id, - first_name: "John", - last_name: "Doe", - postal_code: "10002", - country_code: "US", - is_default_shipping: true, + expect(address).toEqual( + expect.objectContaining({ + id: expect.any(String), + address_1: "Testvej 1", + address_2: "Testvej 2", + city: "Testby", + country_code: "DK", + province: "Test", + postal_code: "8000", + phone: "123456789", + metadata: expect.objectContaining({ membership: "gold" }), + is_default_shipping: true, + }) + ) }) - ).rejects.toThrow("A default shipping address already exists") - }) - it("should only be possible to add one default billing address per customer", async () => { - const customer = await service.create({ - first_name: "John", - last_name: "Doe", - }) - await service.addAddresses({ - customer_id: customer.id, - first_name: "John", - last_name: "Doe", - postal_code: "10001", - country_code: "US", - is_default_billing: true, - }) - await service.addAddresses({ - customer_id: customer.id, - first_name: "John", - last_name: "Doe", - postal_code: "10001", - country_code: "US", - is_default_billing: false, - }) - - await expect( - service.addAddresses({ - customer_id: customer.id, - first_name: "John", - last_name: "Doe", - postal_code: "10002", - country_code: "US", - is_default_billing: true, + it("should fail to create two default shipping", async () => { + const customerData = { + company_name: "Acme Corp", + first_name: "John", + last_name: "Doe", + addresses: [ + { + address_1: "Testvej 1", + address_2: "Testvej 2", + city: "Testby", + country_code: "DK", + province: "Test", + postal_code: "8000", + phone: "123456789", + metadata: { membership: "gold" }, + is_default_shipping: true, + }, + { + address_1: "Test Ave 1", + address_2: "Test Ave 2", + city: "Testville", + country_code: "US", + is_default_shipping: true, + }, + ], + } + await expect(service.create(customerData)).rejects.toThrow( + "A default shipping address already exists" + ) }) - ).rejects.toThrow("A default billing address already exists") - }) - }) - describe("updateAddresses", () => { - it("should update a single address", async () => { - const customer = await service.create({ - first_name: "John", - last_name: "Doe", - }) - const address = await service.addAddresses({ - customer_id: customer.id, - address_name: "Home", - address_1: "123 Main St", + it("should create multiple customers", async () => { + const customersData = [ + { + company_name: "Acme Corp", + first_name: "John", + last_name: "Doe", + email: "john.doe@acmecorp.com", + phone: "123456789", + created_by: "admin", + metadata: { membership: "gold" }, + }, + { + first_name: "Jane", + last_name: "Smith", + email: "jane.smith@example.com", + phone: "987654321", + metadata: { membership: "silver" }, + }, + ] + const customer = await service.create(customersData) + + expect(customer).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + id: expect.any(String), + company_name: "Acme Corp", + first_name: "John", + last_name: "Doe", + email: "john.doe@acmecorp.com", + phone: "123456789", + created_by: "admin", + metadata: expect.objectContaining({ membership: "gold" }), + }), + expect.objectContaining({ + id: expect.any(String), + first_name: "Jane", + last_name: "Smith", + email: "jane.smith@example.com", + phone: "987654321", + metadata: expect.objectContaining({ membership: "silver" }), + }), + ]) + ) + }) }) - await service.updateAddresses(address.id, { - address_name: "Work", - address_1: "456 Main St", + describe("createCustomerGroup", () => { + it("should create a single customer group", async () => { + const group = await service.createCustomerGroup({ + name: "VIP Customers", + metadata: { priority: "high" }, + created_by: "admin", + }) + + expect(group).toEqual( + expect.objectContaining({ + id: expect.any(String), + name: "VIP Customers", + metadata: expect.objectContaining({ priority: "high" }), + created_by: "admin", + }) + ) + }) + + it("should create multiple customer groups", async () => { + const groups = await service.createCustomerGroup([ + { + name: "VIP Customers", + metadata: { priority: "high" }, + created_by: "admin", + }, + { + name: "Regular Customers", + metadata: { discount: "10%" }, + created_by: "staff", + }, + ]) + + expect(groups).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + id: expect.any(String), + name: "VIP Customers", + metadata: expect.objectContaining({ priority: "high" }), + created_by: "admin", + }), + expect.objectContaining({ + id: expect.any(String), + name: "Regular Customers", + metadata: expect.objectContaining({ discount: "10%" }), + created_by: "staff", + }), + ]) + ) + }) }) - const updatedCustomer = await service.retrieve(customer.id, { - select: ["id"], - relations: ["addresses"], + describe("list", () => { + it("should list all customers when no filters are applied", async () => { + await service.create([ + { + first_name: "John", + last_name: "Doe", + email: "john.doe@example.com", + }, + { + first_name: "Jane", + last_name: "Smith", + email: "jane.smith@example.com", + }, + ]) + + const customers = await service.list() + + expect(customers.length).toBeGreaterThanOrEqual(2) + expect(customers).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + first_name: "John", + last_name: "Doe", + email: "john.doe@example.com", + }), + expect.objectContaining({ + first_name: "Jane", + last_name: "Smith", + email: "jane.smith@example.com", + }), + ]) + ) + }) + + it("should list customers filtered by a specific email", async () => { + await service.create([ + { + first_name: "John", + last_name: "Doe", + email: "unique.email@example.com", + }, + { + first_name: "Jane", + last_name: "Smith", + email: "jane.smith@example.com", + }, + ]) + + const filter = { email: "unique.email@example.com" } + const customers = await service.list(filter) + + expect(customers.length).toBe(1) + expect(customers[0]).toEqual( + expect.objectContaining({ + first_name: "John", + last_name: "Doe", + email: "unique.email@example.com", + }) + ) + }) + + it("should list customers by a specific customer group", async () => { + const vipGroup = await service.createCustomerGroup({ name: "VIP" }) + + const [john] = await service.create([ + { + first_name: "John", + last_name: "Doe", + email: "john.doe@example.com", + }, + { + first_name: "Jane", + last_name: "Smith", + email: "jane.smith@example.com", + }, + ]) + + await service.addCustomerToGroup({ + customer_id: john.id, + customer_group_id: vipGroup.id, + }) + + const filter = { groups: vipGroup.id } + const customers = await service.list(filter) + + expect(customers).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + first_name: "John", + last_name: "Doe", + email: "john.doe@example.com", + }), + ]) + ) + expect(customers).not.toEqual( + expect.arrayContaining([ + expect.objectContaining({ + first_name: "Jane", + last_name: "Smith", + email: "jane.smith@example.com", + }), + ]) + ) + }) }) - expect(updatedCustomer.addresses).toEqual([ - expect.objectContaining({ - id: address.id, - address_name: "Work", - address_1: "456 Main St", - }), - ]) - }) + describe("addCustomerToGroup", () => { + it("should add a single customer to a customer group", async () => { + const [customer] = await service.create([ + { + first_name: "John", + last_name: "Doe", + email: "john.doe@example.com", + }, + ]) + const [group] = await service.createCustomerGroup([{ name: "VIP" }]) - it("should update multiple addresses", async () => { - const customer = await service.create({ - first_name: "John", - last_name: "Doe", - }) - const address1 = await service.addAddresses({ - customer_id: customer.id, - address_name: "Home", - address_1: "123 Main St", - }) - const address2 = await service.addAddresses({ - customer_id: customer.id, - address_name: "Work", - address_1: "456 Main St", + const result = await service.addCustomerToGroup({ + customer_id: customer.id, + customer_group_id: group.id, + }) + + expect(result).toEqual( + expect.objectContaining({ id: expect.any(String) }) + ) + + // Additional validation (optional): retrieve the customer and check if the group is assigned + const updatedCustomer = await service.retrieve(customer.id, { + relations: ["groups"], + }) + expect(updatedCustomer.groups).toContainEqual( + expect.objectContaining({ id: group.id }) + ) + }) + + it("should add multiple customers to customer groups", async () => { + const customers = await service.create([ + { + first_name: "John", + last_name: "Doe", + email: "john.doe@example.com", + }, + { + first_name: "Jane", + last_name: "Smith", + email: "jane.smith@example.com", + }, + ]) + const groups = await service.createCustomerGroup([ + { name: "VIP" }, + { name: "Regular" }, + ]) + + const pairs = customers.map((customer, index) => ({ + customer_id: customer.id, + customer_group_id: groups[index % groups.length].id, + })) + + const results = await service.addCustomerToGroup(pairs) + + expect(results).toEqual( + expect.arrayContaining([ + expect.objectContaining({ id: expect.any(String) }), + expect.objectContaining({ id: expect.any(String) }), + ]) + ) + + for (const customer of customers) { + const updatedCustomer = await service.retrieve(customer.id, { + relations: ["groups"], + }) + expect(updatedCustomer.groups).toContainEqual( + expect.objectContaining({ + id: groups[customers.indexOf(customer) % groups.length].id, + }) + ) + } + }) }) - await service.updateAddresses( - { customer_id: customer.id }, - { - address_name: "Under Construction", - } - ) + describe("update", () => { + it("should update a single customer", async () => { + const [customer] = await service.create([ + { + first_name: "John", + last_name: "Doe", + email: "john.doe@example.com", + }, + ]) - const updatedCustomer = await service.retrieve(customer.id, { - select: ["id"], - relations: ["addresses"], + const updateData = { first_name: "Jonathan" } + const updatedCustomer = await service.update(customer.id, updateData) + + expect(updatedCustomer).toEqual( + expect.objectContaining({ id: customer.id, first_name: "Jonathan" }) + ) + }) + + it("should update multiple customers by IDs", async () => { + const customers = await service.create([ + { + first_name: "John", + last_name: "Doe", + email: "john.doe@example.com", + }, + { + first_name: "Jane", + last_name: "Smith", + email: "jane.smith@example.com", + }, + ]) + + const updateData = { last_name: "Updated" } + const customerIds = customers.map((customer) => customer.id) + const updatedCustomers = await service.update(customerIds, updateData) + + updatedCustomers.forEach((updatedCustomer) => { + expect(updatedCustomer).toEqual( + expect.objectContaining({ last_name: "Updated" }) + ) + }) + }) + + it("should update customers using a selector", async () => { + await service.create([ + { + first_name: "John", + last_name: "Doe", + email: "john.doe@example.com", + }, + { + first_name: "Jane", + last_name: "Doe", + email: "jane.doe@example.com", + }, + ]) + + const selector = { last_name: "Doe" } + const updateData = { last_name: "Updated" } + const updatedCustomers = await service.update(selector, updateData) + + updatedCustomers.forEach((updatedCustomer) => { + expect(updatedCustomer).toEqual( + expect.objectContaining({ last_name: "Updated" }) + ) + }) + }) }) - expect(updatedCustomer.addresses).toEqual( - expect.arrayContaining([ - expect.objectContaining({ - id: address1.id, - address_name: "Under Construction", - }), - expect.objectContaining({ - id: address2.id, - address_name: "Under Construction", - }), - ]) - ) - }) + describe("delete", () => { + it("should delete a single customer", async () => { + const [customer] = await service.create([ + { + first_name: "John", + last_name: "Doe", + email: "john.doe@example.com", + }, + ]) - it("should update multiple addresses with ids", async () => { - const customer = await service.create({ - first_name: "John", - last_name: "Doe", - }) - const [address1, address2] = await service.addAddresses([ - { - customer_id: customer.id, - address_name: "Home", - address_1: "123 Main St", - }, - { - customer_id: customer.id, - address_name: "Work", - address_1: "456 Main St", - }, - ]) + await service.delete(customer.id) - await service.updateAddresses([address1.id, address2.id], { - address_name: "Under Construction", + await expect(service.retrieve(customer.id)).rejects.toThrow( + `Customer with id: ${customer.id} was not found` + ) + }) + + it("should delete multiple customers by IDs", async () => { + const customers = await service.create([ + { + first_name: "John", + last_name: "Doe", + email: "john.doe@example.com", + }, + { + first_name: "Jane", + last_name: "Smith", + email: "jane.smith@example.com", + }, + ]) + + const customerIds = customers.map((customer) => customer.id) + await service.delete(customerIds) + + for (const customer of customers) { + await expect(service.retrieve(customer.id)).rejects.toThrow( + `Customer with id: ${customer.id} was not found` + ) + } + }) + + it("should delete customers using a selector", async () => { + await service.create([ + { + first_name: "John", + last_name: "Doe", + email: "john.doe@example.com", + }, + { + first_name: "Jane", + last_name: "Doe", + email: "jane.doe@example.com", + }, + ]) + + const selector = { last_name: "Doe" } + await service.delete(selector) + + const remainingCustomers = await service.list({ last_name: "Doe" }) + expect(remainingCustomers.length).toBe(0) + }) + + it("should cascade address relationship when deleting customer", async () => { + // Creating a customer and an address + const customer = await service.create({ + first_name: "John", + last_name: "Doe", + }) + await service.addAddresses({ + customer_id: customer.id, + first_name: "John", + last_name: "Doe", + postal_code: "10001", + country_code: "US", + }) + + // verify that the address was added + const customerWithAddress = await service.retrieve(customer.id, { + relations: ["addresses"], + }) + expect(customerWithAddress.addresses?.length).toBe(1) + + await service.delete(customer.id) + + const res = await service.listAddresses({ + customer_id: customer.id, + }) + expect(res.length).toBe(0) + }) + + it("should cascade relationship when deleting customer", async () => { + // Creating a customer and a group + const customer = await service.create({ + first_name: "John", + last_name: "Doe", + }) + const group = await service.createCustomerGroup({ name: "VIP" }) + + // Adding the customer to the groups + await service.addCustomerToGroup({ + customer_id: customer.id, + customer_group_id: group.id, + }) + + await service.delete(customer.id) + + const res = await service.listCustomerGroupCustomers({ + customer_id: customer.id, + customer_group_id: group.id, + }) + expect(res.length).toBe(0) + }) }) - const updatedCustomer = await service.retrieve(customer.id, { - select: ["id"], - relations: ["addresses"], + describe("deleteCustomerGroup", () => { + it("should delete a single customer group", async () => { + const [group] = await service.createCustomerGroup([{ name: "VIP" }]) + await service.deleteCustomerGroups(group.id) + + await expect( + service.retrieveCustomerGroup(group.id) + ).rejects.toThrowError( + `CustomerGroup with id: ${group.id} was not found` + ) + }) + + it("should delete multiple customer groups by IDs", async () => { + const groups = await service.createCustomerGroup([ + { name: "VIP" }, + { name: "Regular" }, + ]) + + const groupIds = groups.map((group) => group.id) + await service.deleteCustomerGroups(groupIds) + + for (const group of groups) { + await expect( + service.retrieveCustomerGroup(group.id) + ).rejects.toThrowError( + `CustomerGroup with id: ${group.id} was not found` + ) + } + }) + + it("should delete customer groups using a selector", async () => { + await service.createCustomerGroup([ + { name: "VIP" }, + { name: "Regular" }, + ]) + + const selector = { name: "VIP" } + await service.deleteCustomerGroups(selector) + + const remainingGroups = await service.listCustomerGroups({ + name: "VIP", + }) + expect(remainingGroups.length).toBe(0) + }) + + it("should cascade relationship when deleting customer group", async () => { + // Creating a customer and a group + const customer = await service.create({ + first_name: "John", + last_name: "Doe", + }) + const group = await service.createCustomerGroup({ name: "VIP" }) + + // Adding the customer to the groups + await service.addCustomerToGroup({ + customer_id: customer.id, + customer_group_id: group.id, + }) + + await service.deleteCustomerGroups(group.id) + + const res = await service.listCustomerGroupCustomers({ + customer_id: customer.id, + customer_group_id: group.id, + }) + expect(res.length).toBe(0) + }) }) - expect(updatedCustomer.addresses).toEqual( - expect.arrayContaining([ - expect.objectContaining({ - id: address1.id, - address_name: "Under Construction", - }), - expect.objectContaining({ - id: address2.id, - address_name: "Under Construction", - }), - ]) - ) - }) + describe("addAddresses", () => { + it("should add a single address to a customer", async () => { + const customer = await service.create({ + first_name: "John", + last_name: "Doe", + }) + const address = await service.addAddresses({ + customer_id: customer.id, + first_name: "John", + last_name: "Doe", + postal_code: "10001", + country_code: "US", + }) + const [customerWithAddress] = await service.list( + { id: customer.id }, + { relations: ["addresses"] } + ) - it("should fail when updating address to a default shipping address when one already exists", async () => { - const customer = await service.create({ - first_name: "John", - last_name: "Doe", - addresses: [ - { - address_name: "Home", - address_1: "123 Main St", + expect(customerWithAddress.addresses).toEqual([ + expect.objectContaining({ id: address.id }), + ]) + }) + + it("should add multiple addresses to a customer", async () => { + const customer = await service.create({ + first_name: "John", + last_name: "Doe", + }) + const addresses = await service.addAddresses([ + { + customer_id: customer.id, + first_name: "John", + last_name: "Doe", + postal_code: "10001", + country_code: "US", + }, + { + customer_id: customer.id, + first_name: "John", + last_name: "Doe", + postal_code: "10002", + country_code: "US", + }, + ]) + const [customerWithAddresses] = await service.list( + { id: customer.id }, + { relations: ["addresses"] } + ) + + expect(customerWithAddresses.addresses).toEqual( + expect.arrayContaining([ + expect.objectContaining({ id: addresses[0].id }), + expect.objectContaining({ id: addresses[1].id }), + ]) + ) + }) + + it("should only be possible to add one default shipping address per customer", async () => { + const customer = await service.create({ + first_name: "John", + last_name: "Doe", + }) + await service.addAddresses({ + customer_id: customer.id, + first_name: "John", + last_name: "Doe", + postal_code: "10001", + country_code: "US", is_default_shipping: true, - }, - ], - }) - const address = await service.addAddresses({ - customer_id: customer.id, - address_name: "Work", - address_1: "456 Main St", + }) + await service.addAddresses({ + customer_id: customer.id, + first_name: "John", + last_name: "Doe", + postal_code: "10001", + country_code: "US", + is_default_shipping: false, + }) + + await expect( + service.addAddresses({ + customer_id: customer.id, + first_name: "John", + last_name: "Doe", + postal_code: "10002", + country_code: "US", + is_default_shipping: true, + }) + ).rejects.toThrow("A default shipping address already exists") + }) + + it("should only be possible to add one default billing address per customer", async () => { + const customer = await service.create({ + first_name: "John", + last_name: "Doe", + }) + await service.addAddresses({ + customer_id: customer.id, + first_name: "John", + last_name: "Doe", + postal_code: "10001", + country_code: "US", + is_default_billing: true, + }) + await service.addAddresses({ + customer_id: customer.id, + first_name: "John", + last_name: "Doe", + postal_code: "10001", + country_code: "US", + is_default_billing: false, + }) + + await expect( + service.addAddresses({ + customer_id: customer.id, + first_name: "John", + last_name: "Doe", + postal_code: "10002", + country_code: "US", + is_default_billing: true, + }) + ).rejects.toThrow("A default billing address already exists") + }) }) - await expect( - service.updateAddresses(address.id, { is_default_shipping: true }) - ).rejects.toThrow("A default shipping address already exists") - }) - }) - - describe("listAddresses", () => { - it("should list all addresses for a customer", async () => { - const customer = await service.create({ - first_name: "John", - last_name: "Doe", - }) - const [address1, address2] = await service.addAddresses([ - { - customer_id: customer.id, - address_name: "Home", - address_1: "123 Main St", - }, - { - customer_id: customer.id, - address_name: "Work", - - address_1: "456 Main St", - }, - ]) - - const addresses = await service.listAddresses({ - customer_id: customer.id, - }) - - expect(addresses).toEqual( - expect.arrayContaining([ - expect.objectContaining({ - id: address1.id, + describe("updateAddresses", () => { + it("should update a single address", async () => { + const customer = await service.create({ + first_name: "John", + last_name: "Doe", + }) + const address = await service.addAddresses({ + customer_id: customer.id, address_name: "Home", address_1: "123 Main St", - }), - expect.objectContaining({ - id: address2.id, + }) + + await service.updateAddresses(address.id, { address_name: "Work", address_1: "456 Main St", - }), - ]) - ) - }) - }) + }) - describe("removeCustomerFromGroup", () => { - it("should remove a single customer from a group", async () => { - // Creating a customer and a group - const [customer] = await service.create([ - { first_name: "John", last_name: "Doe", email: "john.doe@example.com" }, - ]) - const [group] = await service.createCustomerGroup([{ name: "VIP" }]) + const updatedCustomer = await service.retrieve(customer.id, { + select: ["id"], + relations: ["addresses"], + }) - // Adding the customer to the group - await service.addCustomerToGroup({ - customer_id: customer.id, - customer_group_id: group.id, + expect(updatedCustomer.addresses).toEqual([ + expect.objectContaining({ + id: address.id, + address_name: "Work", + address_1: "456 Main St", + }), + ]) + }) + + it("should update multiple addresses", async () => { + const customer = await service.create({ + first_name: "John", + last_name: "Doe", + }) + const address1 = await service.addAddresses({ + customer_id: customer.id, + address_name: "Home", + address_1: "123 Main St", + }) + const address2 = await service.addAddresses({ + customer_id: customer.id, + address_name: "Work", + address_1: "456 Main St", + }) + + await service.updateAddresses( + { customer_id: customer.id }, + { + address_name: "Under Construction", + } + ) + + const updatedCustomer = await service.retrieve(customer.id, { + select: ["id"], + relations: ["addresses"], + }) + + expect(updatedCustomer.addresses).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + id: address1.id, + address_name: "Under Construction", + }), + expect.objectContaining({ + id: address2.id, + address_name: "Under Construction", + }), + ]) + ) + }) + + it("should update multiple addresses with ids", async () => { + const customer = await service.create({ + first_name: "John", + last_name: "Doe", + }) + const [address1, address2] = await service.addAddresses([ + { + customer_id: customer.id, + address_name: "Home", + address_1: "123 Main St", + }, + { + customer_id: customer.id, + address_name: "Work", + address_1: "456 Main St", + }, + ]) + + await service.updateAddresses([address1.id, address2.id], { + address_name: "Under Construction", + }) + + const updatedCustomer = await service.retrieve(customer.id, { + select: ["id"], + relations: ["addresses"], + }) + + expect(updatedCustomer.addresses).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + id: address1.id, + address_name: "Under Construction", + }), + expect.objectContaining({ + id: address2.id, + address_name: "Under Construction", + }), + ]) + ) + }) + + it("should fail when updating address to a default shipping address when one already exists", async () => { + const customer = await service.create({ + first_name: "John", + last_name: "Doe", + addresses: [ + { + address_name: "Home", + address_1: "123 Main St", + is_default_shipping: true, + }, + ], + }) + const address = await service.addAddresses({ + customer_id: customer.id, + address_name: "Work", + address_1: "456 Main St", + }) + + await expect( + service.updateAddresses(address.id, { is_default_shipping: true }) + ).rejects.toThrow("A default shipping address already exists") + }) }) - const [customerInGroup] = await service.list( - { id: customer.id }, - { relations: ["groups"] } - ) - expect(customerInGroup.groups).toEqual([ - expect.objectContaining({ id: group.id }), - ]) + describe("listAddresses", () => { + it("should list all addresses for a customer", async () => { + const customer = await service.create({ + first_name: "John", + last_name: "Doe", + }) + const [address1, address2] = await service.addAddresses([ + { + customer_id: customer.id, + address_name: "Home", + address_1: "123 Main St", + }, + { + customer_id: customer.id, + address_name: "Work", - // Removing the customer from the group - await service.removeCustomerFromGroup({ - customer_id: customer.id, - customer_group_id: group.id, + address_1: "456 Main St", + }, + ]) + + const addresses = await service.listAddresses({ + customer_id: customer.id, + }) + + expect(addresses).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + id: address1.id, + address_name: "Home", + address_1: "123 Main St", + }), + expect.objectContaining({ + id: address2.id, + address_name: "Work", + address_1: "456 Main St", + }), + ]) + ) + }) }) - const [updatedCustomer] = await service.list( - { id: customer.id }, - { relations: ["groups"] } - ) - expect(updatedCustomer.groups).toEqual([]) - }) + describe("removeCustomerFromGroup", () => { + it("should remove a single customer from a group", async () => { + // Creating a customer and a group + const [customer] = await service.create([ + { + first_name: "John", + last_name: "Doe", + email: "john.doe@example.com", + }, + ]) + const [group] = await service.createCustomerGroup([{ name: "VIP" }]) - it("should remove multiple customers from groups", async () => { - // Creating multiple customers and groups - const customers = await service.create([ - { first_name: "John", last_name: "Doe", email: "john.doe@example.com" }, - { - first_name: "Jane", - last_name: "Smith", - email: "jane.smith@example.com", - }, - ]) - const groups = await service.createCustomerGroup([ - { name: "VIP" }, - { name: "Regular" }, - ]) + // Adding the customer to the group + await service.addCustomerToGroup({ + customer_id: customer.id, + customer_group_id: group.id, + }) - // Adding customers to groups - const pairsToAdd = [ - { customer_id: customers[0].id, customer_group_id: groups[0].id }, - { customer_id: customers[1].id, customer_group_id: groups[1].id }, - ] - await service.addCustomerToGroup(pairsToAdd) + const [customerInGroup] = await service.list( + { id: customer.id }, + { relations: ["groups"] } + ) + expect(customerInGroup.groups).toEqual([ + expect.objectContaining({ id: group.id }), + ]) - // Removing customers from groups - const pairsToRemove = [ - { customer_id: customers[0].id, customer_group_id: groups[0].id }, - { customer_id: customers[1].id, customer_group_id: groups[1].id }, - ] - await service.removeCustomerFromGroup(pairsToRemove) + // Removing the customer from the group + await service.removeCustomerFromGroup({ + customer_id: customer.id, + customer_group_id: group.id, + }) - // Verification for each customer - for (const pair of pairsToRemove) { - const [updatedCustomer] = await service.list( - { id: pair.customer_id }, - { relations: ["groups"] } - ) - expect(updatedCustomer.groups).not.toContainEqual( - expect.objectContaining({ id: pair.customer_group_id }) - ) - } - }) - }) + const [updatedCustomer] = await service.list( + { id: customer.id }, + { relations: ["groups"] } + ) + expect(updatedCustomer.groups).toEqual([]) + }) - describe("softDelete", () => { - it("should soft delete a single customer", async () => { - const [customer] = await service.create([ - { first_name: "John", last_name: "Doe" }, - ]) - await service.softDelete([customer.id]) + it("should remove multiple customers from groups", async () => { + // Creating multiple customers and groups + const customers = await service.create([ + { + first_name: "John", + last_name: "Doe", + email: "john.doe@example.com", + }, + { + first_name: "Jane", + last_name: "Smith", + email: "jane.smith@example.com", + }, + ]) + const groups = await service.createCustomerGroup([ + { name: "VIP" }, + { name: "Regular" }, + ]) - const res = await service.list({ id: customer.id }) - expect(res.length).toBe(0) + // Adding customers to groups + const pairsToAdd = [ + { customer_id: customers[0].id, customer_group_id: groups[0].id }, + { customer_id: customers[1].id, customer_group_id: groups[1].id }, + ] + await service.addCustomerToGroup(pairsToAdd) - const deletedCustomer = await service.retrieve(customer.id, { - withDeleted: true, + // Removing customers from groups + const pairsToRemove = [ + { customer_id: customers[0].id, customer_group_id: groups[0].id }, + { customer_id: customers[1].id, customer_group_id: groups[1].id }, + ] + await service.removeCustomerFromGroup(pairsToRemove) + + // Verification for each customer + for (const pair of pairsToRemove) { + const [updatedCustomer] = await service.list( + { id: pair.customer_id }, + { relations: ["groups"] } + ) + expect(updatedCustomer.groups).not.toContainEqual( + expect.objectContaining({ id: pair.customer_group_id }) + ) + } + }) }) - expect(deletedCustomer.deleted_at).not.toBeNull() - }) + describe("softDelete", () => { + it("should soft delete a single customer", async () => { + const [customer] = await service.create([ + { first_name: "John", last_name: "Doe" }, + ]) + await service.softDelete([customer.id]) - it("should soft delete multiple customers", async () => { - const customers = await service.create([ - { first_name: "John", last_name: "Doe" }, - { first_name: "Jane", last_name: "Smith" }, - ]) - const customerIds = customers.map((customer) => customer.id) - await service.softDelete(customerIds) + const res = await service.list({ id: customer.id }) + expect(res.length).toBe(0) - const res = await service.list({ id: customerIds }) - expect(res.length).toBe(0) + const deletedCustomer = await service.retrieve(customer.id, { + withDeleted: true, + }) - const deletedCustomers = await service.list( - { id: customerIds }, - { withDeleted: true } - ) - expect(deletedCustomers.length).toBe(2) - }) + expect(deletedCustomer.deleted_at).not.toBeNull() + }) - it("should remove customer in group relation", async () => { - // Creating a customer and a group - const customer = await service.create({ - first_name: "John", - last_name: "Doe", - }) - const group = await service.createCustomerGroup({ name: "VIP" }) + it("should soft delete multiple customers", async () => { + const customers = await service.create([ + { first_name: "John", last_name: "Doe" }, + { first_name: "Jane", last_name: "Smith" }, + ]) + const customerIds = customers.map((customer) => customer.id) + await service.softDelete(customerIds) - // Adding the customer to the group - await service.addCustomerToGroup({ - customer_id: customer.id, - customer_group_id: group.id, + const res = await service.list({ id: customerIds }) + expect(res.length).toBe(0) + + const deletedCustomers = await service.list( + { id: customerIds }, + { withDeleted: true } + ) + expect(deletedCustomers.length).toBe(2) + }) + + it("should remove customer in group relation", async () => { + // Creating a customer and a group + const customer = await service.create({ + first_name: "John", + last_name: "Doe", + }) + const group = await service.createCustomerGroup({ name: "VIP" }) + + // Adding the customer to the group + await service.addCustomerToGroup({ + customer_id: customer.id, + customer_group_id: group.id, + }) + + await service.softDelete([customer.id]) + + const resGroup = await service.retrieveCustomerGroup(group.id, { + relations: ["customers"], + }) + expect(resGroup.customers?.length).toBe(0) + }) }) - await service.softDelete([customer.id]) + describe("restore", () => { + it("should restore a single customer", async () => { + const [customer] = await service.create([ + { first_name: "John", last_name: "Doe" }, + ]) + await service.softDelete([customer.id]) - const resGroup = await service.retrieveCustomerGroup(group.id, { - relations: ["customers"], - }) - expect(resGroup.customers?.length).toBe(0) - }) - }) + const res = await service.list({ id: customer.id }) + expect(res.length).toBe(0) - describe("restore", () => { - it("should restore a single customer", async () => { - const [customer] = await service.create([ - { first_name: "John", last_name: "Doe" }, - ]) - await service.softDelete([customer.id]) + await service.restore([customer.id]) - const res = await service.list({ id: customer.id }) - expect(res.length).toBe(0) + const restoredCustomer = await service.retrieve(customer.id, { + withDeleted: true, + }) + expect(restoredCustomer.deleted_at).toBeNull() + }) - await service.restore([customer.id]) + it("should restore multiple customers", async () => { + const customers = await service.create([ + { first_name: "John", last_name: "Doe" }, + { first_name: "Jane", last_name: "Smith" }, + ]) + const customerIds = customers.map((customer) => customer.id) + await service.softDelete(customerIds) - const restoredCustomer = await service.retrieve(customer.id, { - withDeleted: true, - }) - expect(restoredCustomer.deleted_at).toBeNull() - }) + const res = await service.list({ id: customerIds }) + expect(res.length).toBe(0) - it("should restore multiple customers", async () => { - const customers = await service.create([ - { first_name: "John", last_name: "Doe" }, - { first_name: "Jane", last_name: "Smith" }, - ]) - const customerIds = customers.map((customer) => customer.id) - await service.softDelete(customerIds) + await service.restore(customerIds) - const res = await service.list({ id: customerIds }) - expect(res.length).toBe(0) - - await service.restore(customerIds) - - const restoredCustomers = await service.list( - { id: customerIds }, - { withDeleted: true } - ) - expect(restoredCustomers.length).toBe(2) - }) - }) - - describe("softDeleteCustomerGroup", () => { - it("should soft delete a single customer group", async () => { - const [group] = await service.createCustomerGroup([{ name: "VIP" }]) - await service.softDeleteCustomerGroups([group.id]) - - const res = await service.listCustomerGroups({ id: group.id }) - expect(res.length).toBe(0) - - const deletedGroup = await service.retrieveCustomerGroup(group.id, { - withDeleted: true, + const restoredCustomers = await service.list( + { id: customerIds }, + { withDeleted: true } + ) + expect(restoredCustomers.length).toBe(2) + }) }) - expect(deletedGroup.deleted_at).not.toBeNull() - }) + describe("softDeleteCustomerGroup", () => { + it("should soft delete a single customer group", async () => { + const [group] = await service.createCustomerGroup([{ name: "VIP" }]) + await service.softDeleteCustomerGroups([group.id]) - it("should soft delete multiple customer groups", async () => { - const groups = await service.createCustomerGroup([ - { name: "VIP" }, - { name: "Regular" }, - ]) - const groupIds = groups.map((group) => group.id) - await service.softDeleteCustomerGroups(groupIds) + const res = await service.listCustomerGroups({ id: group.id }) + expect(res.length).toBe(0) - const res = await service.listCustomerGroups({ id: groupIds }) - expect(res.length).toBe(0) + const deletedGroup = await service.retrieveCustomerGroup(group.id, { + withDeleted: true, + }) - const deletedGroups = await service.listCustomerGroups( - { id: groupIds }, - { withDeleted: true } - ) - expect(deletedGroups.length).toBe(2) - }) - }) + expect(deletedGroup.deleted_at).not.toBeNull() + }) - describe("restoreCustomerGroup", () => { - it("should restore a single customer group", async () => { - const [group] = await service.createCustomerGroup([{ name: "VIP" }]) - await service.softDeleteCustomerGroups([group.id]) + it("should soft delete multiple customer groups", async () => { + const groups = await service.createCustomerGroup([ + { name: "VIP" }, + { name: "Regular" }, + ]) + const groupIds = groups.map((group) => group.id) + await service.softDeleteCustomerGroups(groupIds) - const res = await service.listCustomerGroups({ id: group.id }) - expect(res.length).toBe(0) + const res = await service.listCustomerGroups({ id: groupIds }) + expect(res.length).toBe(0) - await service.restoreCustomerGroups([group.id]) - - const restoredGroup = await service.retrieveCustomerGroup(group.id, { - withDeleted: true, + const deletedGroups = await service.listCustomerGroups( + { id: groupIds }, + { withDeleted: true } + ) + expect(deletedGroups.length).toBe(2) + }) + }) + + describe("restoreCustomerGroup", () => { + it("should restore a single customer group", async () => { + const [group] = await service.createCustomerGroup([{ name: "VIP" }]) + await service.softDeleteCustomerGroups([group.id]) + + const res = await service.listCustomerGroups({ id: group.id }) + expect(res.length).toBe(0) + + await service.restoreCustomerGroups([group.id]) + + const restoredGroup = await service.retrieveCustomerGroup(group.id, { + withDeleted: true, + }) + expect(restoredGroup.deleted_at).toBeNull() + }) + + it("should restore multiple customer groups", async () => { + const groups = await service.createCustomerGroup([ + { name: "VIP" }, + { name: "Regular" }, + ]) + const groupIds = groups.map((group) => group.id) + await service.softDeleteCustomerGroups(groupIds) + + const res = await service.listCustomerGroups({ id: groupIds }) + expect(res.length).toBe(0) + + await service.restoreCustomerGroups(groupIds) + + const restoredGroups = await service.listCustomerGroups( + { id: groupIds }, + { withDeleted: true } + ) + expect(restoredGroups.length).toBe(2) + }) }) - expect(restoredGroup.deleted_at).toBeNull() }) - - it("should restore multiple customer groups", async () => { - const groups = await service.createCustomerGroup([ - { name: "VIP" }, - { name: "Regular" }, - ]) - const groupIds = groups.map((group) => group.id) - await service.softDeleteCustomerGroups(groupIds) - - const res = await service.listCustomerGroups({ id: groupIds }) - expect(res.length).toBe(0) - - await service.restoreCustomerGroups(groupIds) - - const restoredGroups = await service.listCustomerGroups( - { id: groupIds }, - { withDeleted: true } - ) - expect(restoredGroups.length).toBe(2) - }) - }) + }, }) diff --git a/packages/customer/integration-tests/setup-env.js b/packages/customer/integration-tests/setup-env.js deleted file mode 100644 index cc51317fb0..0000000000 --- a/packages/customer/integration-tests/setup-env.js +++ /dev/null @@ -1,6 +0,0 @@ -if (typeof process.env.DB_TEMP_NAME === "undefined") { - const tempName = parseInt(process.env.JEST_WORKER_ID || "1") - process.env.DB_TEMP_NAME = `medusa-customer-integration-${tempName}` -} - -process.env.MEDUSA_CUSTOMER_DB_SCHEMA = "public" diff --git a/packages/customer/integration-tests/setup.js b/packages/customer/integration-tests/setup.js deleted file mode 100644 index 43f99aab4a..0000000000 --- a/packages/customer/integration-tests/setup.js +++ /dev/null @@ -1,3 +0,0 @@ -import { JestUtils } from "medusa-test-utils" - -JestUtils.afterAllHookDropDatabase() diff --git a/packages/customer/integration-tests/utils/config.ts b/packages/customer/integration-tests/utils/config.ts deleted file mode 100644 index d5530a25f8..0000000000 --- a/packages/customer/integration-tests/utils/config.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { ModuleServiceInitializeOptions } from "@medusajs/types" - -export const databaseOptions: ModuleServiceInitializeOptions["database"] = { - schema: "public", - clientUrl: "medusa-customer-test", -} diff --git a/packages/customer/integration-tests/utils/database.ts b/packages/customer/integration-tests/utils/database.ts deleted file mode 100644 index 284863d2c7..0000000000 --- a/packages/customer/integration-tests/utils/database.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { TestDatabaseUtils } from "medusa-test-utils" - -import * as Models from "@models" - -const pathToMigrations = "../../src/migrations" -const mikroOrmEntities = Models as unknown as any[] - -export const MikroOrmWrapper = TestDatabaseUtils.getMikroOrmWrapper({ - mikroOrmEntities, - pathToMigrations, -}) - -export const MikroOrmConfig = TestDatabaseUtils.getMikroOrmConfig({ - mikroOrmEntities, - pathToMigrations, -}) - -export const DB_URL = TestDatabaseUtils.getDatabaseURL() diff --git a/packages/customer/integration-tests/utils/get-init-module-config.ts b/packages/customer/integration-tests/utils/get-init-module-config.ts deleted file mode 100644 index c1f92f6e86..0000000000 --- a/packages/customer/integration-tests/utils/get-init-module-config.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { Modules, ModulesDefinition } from "@medusajs/modules-sdk" - -import { DB_URL } from "./database" - -export function getInitModuleConfig() { - const moduleOptions = { - defaultAdapterOptions: { - database: { - clientUrl: DB_URL, - schema: process.env.MEDUSA_CUSTOMER_DB_SCHEMA, - }, - }, - } - - const injectedDependencies = {} - - const modulesConfig_ = { - [Modules.CUSTOMER]: { - definition: ModulesDefinition[Modules.CUSTOMER], - options: moduleOptions, - }, - } - - return { - injectedDependencies, - modulesConfig: modulesConfig_, - databaseConfig: { - clientUrl: DB_URL, - schema: process.env.MEDUSA_CUSTOMER_DB_SCHEMA, - }, - joinerConfig: [], - } -} diff --git a/packages/customer/integration-tests/utils/index.ts b/packages/customer/integration-tests/utils/index.ts deleted file mode 100644 index 5ca5d1bdc0..0000000000 --- a/packages/customer/integration-tests/utils/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from "./config" -export * from "./database" diff --git a/packages/customer/jest.config.js b/packages/customer/jest.config.js index 860ba90a49..f2e9f17237 100644 --- a/packages/customer/jest.config.js +++ b/packages/customer/jest.config.js @@ -16,6 +16,4 @@ module.exports = { testEnvironment: `node`, moduleFileExtensions: [`js`, `ts`], modulePathIgnorePatterns: ["dist/"], - setupFiles: ["/integration-tests/setup-env.js"], - setupFilesAfterEnv: ["/integration-tests/setup.js"], } diff --git a/packages/pricing/integration-tests/__tests__/services/money-amount/index.spec.ts b/packages/pricing/integration-tests/__tests__/services/money-amount/index.spec.ts deleted file mode 100644 index f82842b196..0000000000 --- a/packages/pricing/integration-tests/__tests__/services/money-amount/index.spec.ts +++ /dev/null @@ -1,353 +0,0 @@ -import { SqlEntityManager } from "@mikro-orm/postgresql" - -import { MoneyAmount } from "@models" -import { MoneyAmountService } from "@services" - -import { createMedusaContainer } from "@medusajs/utils" -import { asValue } from "awilix" -import ContainerLoader from "../../../../src/loaders/container" -import { createMoneyAmounts } from "../../../__fixtures__/money-amount" -import { MikroOrmWrapper } from "../../../utils" - -jest.setTimeout(30000) - -describe("MoneyAmount Service", () => { - let service: MoneyAmountService - let testManager: SqlEntityManager - let repositoryManager: SqlEntityManager - let data!: MoneyAmount[] - - beforeEach(async () => { - await MikroOrmWrapper.setupDatabase() - repositoryManager = await MikroOrmWrapper.forkManager() - - const container = createMedusaContainer() - container.register("manager", asValue(repositoryManager)) - - await ContainerLoader({ container }) - - service = container.resolve("moneyAmountService") - - testManager = await MikroOrmWrapper.forkManager() - data = await createMoneyAmounts(testManager) - }) - - afterEach(async () => { - await MikroOrmWrapper.clearDatabase() - }) - - describe("list", () => { - it("should list all moneyAmounts", async () => { - const moneyAmountsResult = await service.list() - - expect(JSON.parse(JSON.stringify(moneyAmountsResult))).toEqual( - expect.arrayContaining([ - expect.objectContaining({ - id: "money-amount-USD", - amount: 500, - }), - expect.objectContaining({ - id: "money-amount-EUR", - amount: 400, - }), - expect.objectContaining({ - id: "money-amount-CAD", - amount: 600, - }), - ]) - ) - }) - - it("should list moneyAmounts by id", async () => { - const moneyAmountsResult = await service.list({ - id: ["money-amount-USD"], - }) - - expect(JSON.parse(JSON.stringify(moneyAmountsResult))).toEqual([ - expect.objectContaining({ - id: "money-amount-USD", - }), - ]) - }) - - it("should list moneyAmounts with relations and selects", async () => { - const moneyAmountsResult = await service.list( - { - id: ["money-amount-USD"], - }, - { - select: ["id", "min_quantity", "currency_code", "amount"], - } - ) - - const serialized = JSON.parse(JSON.stringify(moneyAmountsResult)) - - expect(serialized).toEqual([ - { - id: "money-amount-USD", - amount: 500, - min_quantity: "1", - currency_code: "USD", - }, - ]) - }) - - it("should list moneyAmounts scoped by currency_code", async () => { - const moneyAmountsResult = await service.list( - { - currency_code: ["USD"], - }, - { - select: ["id", "min_quantity", "currency_code", "amount"], - } - ) - - const serialized = JSON.parse(JSON.stringify(moneyAmountsResult)) - - expect(serialized).toEqual([ - { - id: "money-amount-USD", - min_quantity: "1", - currency_code: "USD", - amount: 500, - }, - ]) - }) - }) - - describe("listAndCount", () => { - it("should return moneyAmounts and count", async () => { - const [moneyAmountsResult, count] = await service.listAndCount() - - expect(count).toEqual(3) - expect(JSON.parse(JSON.stringify(moneyAmountsResult))).toEqual( - expect.arrayContaining([ - expect.objectContaining({ - id: "money-amount-USD", - }), - expect.objectContaining({ - id: "money-amount-EUR", - }), - expect.objectContaining({ - id: "money-amount-CAD", - }), - ]) - ) - }) - - it("should return moneyAmounts and count when filtered", async () => { - const [moneyAmountsResult, count] = await service.listAndCount({ - id: ["money-amount-USD"], - }) - - expect(count).toEqual(1) - expect(moneyAmountsResult).toEqual([ - expect.objectContaining({ - id: "money-amount-USD", - }), - ]) - }) - - it("list moneyAmounts with relations and selects", async () => { - const [moneyAmountsResult, count] = await service.listAndCount( - { - id: ["money-amount-USD"], - }, - { - select: ["id", "min_quantity", "currency_code", "amount"], - } - ) - - const serialized = JSON.parse(JSON.stringify(moneyAmountsResult)) - - expect(count).toEqual(1) - expect(serialized).toEqual([ - { - id: "money-amount-USD", - amount: 500, - min_quantity: "1", - currency_code: "USD", - }, - ]) - }) - - it("should return moneyAmounts and count when using skip and take", async () => { - const [moneyAmountsResult, count] = await service.listAndCount( - {}, - { skip: 1, take: 1 } - ) - - expect(count).toEqual(3) - expect(moneyAmountsResult).toEqual([ - expect.objectContaining({ - id: "money-amount-EUR", - }), - ]) - }) - - it("should return requested fields", async () => { - const [moneyAmountsResult, count] = await service.listAndCount( - {}, - { - take: 1, - select: ["id", "amount"], - } - ) - - const serialized = JSON.parse(JSON.stringify(moneyAmountsResult)) - - expect(count).toEqual(3) - expect(serialized).toEqual([ - { - id: "money-amount-CAD", - amount: 600, - }, - ]) - }) - }) - - describe("retrieve", () => { - const id = "money-amount-USD" - const amount = 500 - - it("should return moneyAmount for the given id", async () => { - const moneyAmount = await service.retrieve(id) - - expect(moneyAmount).toEqual( - expect.objectContaining({ - id, - }) - ) - }) - - it("should throw an error when moneyAmount with id does not exist", async () => { - let error - - try { - await service.retrieve("does-not-exist") - } catch (e) { - error = e - } - - expect(error.message).toEqual( - "MoneyAmount with id: does-not-exist was not found" - ) - }) - - it("should throw an error when a id is not provided", async () => { - let error - - try { - await service.retrieve(undefined as unknown as string) - } catch (e) { - error = e - } - - expect(error.message).toEqual("moneyAmount - id must be defined") - }) - - it("should return moneyAmount based on config select param", async () => { - const moneyAmount = await service.retrieve(id, { - select: ["id", "amount"], - }) - - const serialized = JSON.parse(JSON.stringify(moneyAmount)) - - expect(serialized).toEqual({ - id, - amount, - }) - }) - }) - - describe("delete", () => { - const id = "money-amount-USD" - - it("should delete the moneyAmounts given an id successfully", async () => { - await service.delete([id]) - - const moneyAmounts = await service.list({ - id: [id], - }) - - expect(moneyAmounts).toHaveLength(0) - }) - }) - - describe("update", () => { - const id = "money-amount-USD" - - it("should update the amount of the moneyAmount successfully", async () => { - await service.update([ - { - id, - amount: 700, - }, - ]) - - const moneyAmount = JSON.parse(JSON.stringify(await service.retrieve(id))) - - expect(moneyAmount.amount).toEqual(700) - }) - - it("should update the currency of the moneyAmount successfully", async () => { - await service.update([ - { - id, - currency_code: "EUR", - }, - ]) - - const moneyAmount = await service.retrieve(id) - - expect(moneyAmount.currency_code).toEqual("EUR") - }) - - it("should throw an error when a id does not exist", async () => { - let error - - try { - await service.update([ - { - id: "does-not-exist", - amount: 666, - }, - ]) - } catch (e) { - error = e - } - - expect(error.message).toEqual( - 'MoneyAmount with id "does-not-exist" not found' - ) - }) - }) - - describe("create", () => { - it("should create a moneyAmount successfully", async () => { - await service.create([ - { - id: "money-amount-TESM", - currency_code: "USD", - amount: 333, - min_quantity: 1, - max_quantity: 4, - }, - ]) - - const [moneyAmount] = await service.list({ - id: ["money-amount-TESM"], - }) - - expect(JSON.parse(JSON.stringify(moneyAmount))).toEqual( - expect.objectContaining({ - id: "money-amount-TESM", - currency_code: "USD", - amount: 333, - min_quantity: "1", - max_quantity: "4", - }) - ) - }) - }) -}) diff --git a/packages/pricing/integration-tests/__tests__/services/price-list-rule/index.spec.ts b/packages/pricing/integration-tests/__tests__/services/price-list-rule/index.spec.ts deleted file mode 100644 index 3e953563ea..0000000000 --- a/packages/pricing/integration-tests/__tests__/services/price-list-rule/index.spec.ts +++ /dev/null @@ -1,243 +0,0 @@ -import { SqlEntityManager } from "@mikro-orm/postgresql" -import { PriceListRuleService } from "@services" - -import { createPriceLists } from "../../../__fixtures__/price-list" -import { createPriceListRules } from "../../../__fixtures__/price-list-rules" -import { createRuleTypes } from "../../../__fixtures__/rule-type" -import { MikroOrmWrapper } from "../../../utils" -import { createMedusaContainer } from "@medusajs/utils" -import { asValue } from "awilix" -import ContainerLoader from "../../../../src/loaders/container" - -jest.setTimeout(30000) - -describe("PriceListRule Service", () => { - let service: PriceListRuleService - let testManager: SqlEntityManager - let repositoryManager: SqlEntityManager - - beforeEach(async () => { - await MikroOrmWrapper.setupDatabase() - repositoryManager = await MikroOrmWrapper.forkManager() - - const container = createMedusaContainer() - container.register("manager", asValue(repositoryManager)) - - await ContainerLoader({ container }) - - service = container.resolve("priceListRuleService") - - testManager = await MikroOrmWrapper.forkManager() - await createRuleTypes(testManager) - await createPriceLists(testManager) - await createPriceListRules(testManager) - }) - - afterEach(async () => { - await MikroOrmWrapper.clearDatabase() - }) - - describe("list", () => { - it("should list all priceListRules", async () => { - const priceListRuleResult = await service.list() - - expect(priceListRuleResult).toEqual([ - expect.objectContaining({ - id: "price-list-rule-1", - }), - expect.objectContaining({ - id: "price-list-rule-2", - }), - ]) - }) - - it("should list priceListRules scoped by priceListRule id", async () => { - const priceListRuleResult = await service.list({ - id: ["price-list-rule-1"], - }) - - expect(priceListRuleResult).toEqual([ - expect.objectContaining({ - id: "price-list-rule-1", - }), - ]) - }) - }) - - describe("listAndCount", () => { - it("should return pricelistrules and count", async () => { - const [priceListRuleResult, count] = await service.listAndCount() - - expect(count).toEqual(2) - expect(priceListRuleResult).toEqual([ - expect.objectContaining({ - id: "price-list-rule-1", - }), - expect.objectContaining({ - id: "price-list-rule-2", - }), - ]) - }) - - it("should return pricelistrules and count when filtered", async () => { - const [priceListRuleResult, count] = await service.listAndCount({ - id: ["price-list-rule-1"], - }) - - expect(count).toEqual(1) - expect(priceListRuleResult).toEqual([ - expect.objectContaining({ - id: "price-list-rule-1", - }), - ]) - }) - - it("should return pricelistrules and count when using skip and take", async () => { - const [priceListRuleResult, count] = await service.listAndCount( - {}, - { skip: 1, take: 1 } - ) - - expect(count).toEqual(2) - expect(priceListRuleResult).toEqual([ - expect.objectContaining({ - id: "price-list-rule-2", - }), - ]) - }) - - it("should return requested fields", async () => { - const [priceListRuleResult, count] = await service.listAndCount( - {}, - { - take: 1, - select: ["id"], - } - ) - - const serialized = JSON.parse(JSON.stringify(priceListRuleResult)) - - expect(count).toEqual(2) - expect(serialized).toEqual([ - { - id: "price-list-rule-1", - }, - ]) - }) - }) - - describe("retrieve", () => { - const id = "price-list-rule-1" - - it("should return priceList for the given id", async () => { - const priceListRuleResult = await service.retrieve(id) - - expect(priceListRuleResult).toEqual( - expect.objectContaining({ - id, - }) - ) - }) - - it("should throw an error when priceListRule with id does not exist", async () => { - let error - - try { - await service.retrieve("does-not-exist") - } catch (e) { - error = e - } - - expect(error.message).toEqual( - "PriceListRule with id: does-not-exist was not found" - ) - }) - - it("should throw an error when a id is not provided", async () => { - let error - - try { - await service.retrieve(undefined as unknown as string) - } catch (e) { - error = e - } - - expect(error.message).toEqual("priceListRule - id must be defined") - }) - }) - - describe("delete", () => { - const id = "price-list-rule-1" - - it("should delete the pricelists given an id successfully", async () => { - await service.delete([id]) - - const priceListResult = await service.list({ - id: [id], - }) - - expect(priceListResult).toHaveLength(0) - }) - }) - - describe("update", () => { - const id = "price-list-rule-2" - - it("should update the value of the priceListRule successfully", async () => { - await service.update([ - { - id, - price_list_id: "price-list-1", - }, - ]) - - const priceList = await service.retrieve(id, { - relations: ["price_list"], - }) - - expect(priceList.price_list.id).toEqual("price-list-1") - }) - - it("should throw an error when a id does not exist", async () => { - let error - - try { - await service.update([ - { - id: "does-not-exist", - }, - ]) - } catch (e) { - error = e - } - - expect(error.message).toEqual( - 'PriceListRule with id "does-not-exist" not found' - ) - }) - }) - - describe("create", () => { - it("should create a priceListRule successfully", async () => { - const [created] = await service.create([ - { - price_list_id: "price-list-2", - rule_type_id: "rule-type-1", - }, - ]) - - const [priceListRule] = await service.list( - { - id: [created.id], - }, - { - relations: ["price_list", "rule_type"], - select: ["price_list.id", "rule_type.id"], - } - ) - - expect(priceListRule.price_list.id).toEqual("price-list-2") - expect(priceListRule.rule_type.id).toEqual("rule-type-1") - }) - }) -}) diff --git a/packages/pricing/integration-tests/__tests__/services/price-list/index.spec.ts b/packages/pricing/integration-tests/__tests__/services/price-list/index.spec.ts deleted file mode 100644 index 026bc3186b..0000000000 --- a/packages/pricing/integration-tests/__tests__/services/price-list/index.spec.ts +++ /dev/null @@ -1,230 +0,0 @@ -import { MikroOrmWrapper } from "../../../utils" -import { PriceListService } from "@services" -import { SqlEntityManager } from "@mikro-orm/postgresql" -import { createPriceLists } from "../../../__fixtures__/price-list" -import { createMedusaContainer } from "@medusajs/utils" -import { asValue } from "awilix" -import ContainerLoader from "../../../../src/loaders/container" - -jest.setTimeout(30000) - -describe("PriceList Service", () => { - let service: PriceListService - let testManager: SqlEntityManager - let repositoryManager: SqlEntityManager - - beforeEach(async () => { - await MikroOrmWrapper.setupDatabase() - repositoryManager = await MikroOrmWrapper.forkManager() - - const container = createMedusaContainer() - container.register("manager", asValue(repositoryManager)) - - await ContainerLoader({ container }) - - service = container.resolve("priceListService") - - testManager = await MikroOrmWrapper.forkManager() - await createPriceLists(testManager) - }) - - afterEach(async () => { - await MikroOrmWrapper.clearDatabase() - }) - - describe("list", () => { - it("should return list priceLists", async () => { - const priceListResult = await service.list() - - expect(priceListResult).toEqual([ - expect.objectContaining({ - id: "price-list-1", - }), - expect.objectContaining({ - id: "price-list-2", - }), - ]) - }) - - it("should list pricelists by id", async () => { - const priceListResult = await service.list({ - id: ["price-list-1"], - }) - - expect(priceListResult).toEqual([ - expect.objectContaining({ - id: "price-list-1", - }), - ]) - }) - }) - - describe("listAndCount", () => { - it("should return pricelists and count", async () => { - const [priceListResult, count] = await service.listAndCount() - - expect(count).toEqual(2) - expect(priceListResult).toEqual([ - expect.objectContaining({ - id: "price-list-1", - }), - expect.objectContaining({ - id: "price-list-2", - }), - ]) - }) - - it("should return pricelists and count when filtered", async () => { - const [priceListResult, count] = await service.listAndCount({ - id: ["price-list-1"], - }) - - expect(count).toEqual(1) - expect(priceListResult).toEqual([ - expect.objectContaining({ - id: "price-list-1", - }), - ]) - }) - - it("should return pricelists and count when using skip and take", async () => { - const [priceListResult, count] = await service.listAndCount( - {}, - { skip: 1, take: 1 } - ) - - expect(count).toEqual(2) - expect(priceListResult).toEqual([ - expect.objectContaining({ - id: "price-list-2", - }), - ]) - }) - - it("should return requested fields", async () => { - const [priceListResult, count] = await service.listAndCount( - {}, - { - take: 1, - select: ["id"], - } - ) - - const serialized = JSON.parse(JSON.stringify(priceListResult)) - - expect(count).toEqual(2) - expect(serialized).toEqual([ - { - id: "price-list-1", - }, - ]) - }) - }) - - describe("retrieve", () => { - const id = "price-list-1" - - it("should return priceList for the given id", async () => { - const priceListResult = await service.retrieve(id) - - expect(priceListResult).toEqual( - expect.objectContaining({ - id, - }) - ) - }) - - it("should throw an error when priceList with id does not exist", async () => { - let error - - try { - await service.retrieve("does-not-exist") - } catch (e) { - error = e - } - - expect(error.message).toEqual( - "PriceList with id: does-not-exist was not found" - ) - }) - - it("should throw an error when a id is not provided", async () => { - let error - - try { - await service.retrieve(undefined as unknown as string) - } catch (e) { - error = e - } - - expect(error.message).toEqual("priceList - id must be defined") - }) - }) - - describe("delete", () => { - const id = "price-list-1" - - it("should delete the pricelists given an id successfully", async () => { - await service.delete([id]) - - const priceListResult = await service.list({ - id: [id], - }) - - expect(priceListResult).toHaveLength(0) - }) - }) - - describe("update", () => { - const id = "price-list-2" - - it("should update the starts_at date of the priceList successfully", async () => { - const updateDate = new Date() - await service.update([ - { - id, - starts_at: updateDate, - }, - ]) - - const priceList = await service.retrieve(id) - - expect(priceList.starts_at).toEqual(updateDate) - }) - - it("should throw an error when a id does not exist", async () => { - let error - - try { - await service.update([ - { - id: "does-not-exist", - }, - ]) - } catch (e) { - error = e - } - - expect(error.message).toEqual( - 'PriceList with id "does-not-exist" not found' - ) - }) - }) - - describe("create", () => { - it("should create a priceList successfully", async () => { - const [created] = await service.create([ - { - title: "test", - description: "test", - }, - ]) - - const [priceList] = await service.list({ - id: [created.id], - }) - - expect(priceList.title).toEqual("test") - }) - }) -}) diff --git a/packages/pricing/integration-tests/__tests__/services/price-rule/index.spec.ts b/packages/pricing/integration-tests/__tests__/services/price-rule/index.spec.ts deleted file mode 100644 index 62176a3be5..0000000000 --- a/packages/pricing/integration-tests/__tests__/services/price-rule/index.spec.ts +++ /dev/null @@ -1,331 +0,0 @@ -import { PriceSetMoneyAmount } from "@models" - -import { CreatePriceRuleDTO } from "@medusajs/types" -import { SqlEntityManager } from "@mikro-orm/postgresql" -import { PriceRuleService } from "@services" -import { createMoneyAmounts } from "../../../__fixtures__/money-amount" -import { createPriceRules } from "../../../__fixtures__/price-rule" -import { createPriceSets } from "../../../__fixtures__/price-set" -import { createPriceSetMoneyAmounts } from "../../../__fixtures__/price-set-money-amount" -import { createPriceSetMoneyAmountRules } from "../../../__fixtures__/price-set-money-amount-rules" -import { createRuleTypes } from "../../../__fixtures__/rule-type" -import { MikroOrmWrapper } from "../../../utils" -import { createMedusaContainer } from "@medusajs/utils" -import { asValue } from "awilix" -import ContainerLoader from "../../../../src/loaders/container" - -jest.setTimeout(30000) - -describe("PriceRule Service", () => { - let service: PriceRuleService - let testManager: SqlEntityManager - let repositoryManager: SqlEntityManager - - beforeEach(async () => { - await MikroOrmWrapper.setupDatabase() - repositoryManager = await MikroOrmWrapper.forkManager() - testManager = await MikroOrmWrapper.forkManager() - - const container = createMedusaContainer() - container.register("manager", asValue(repositoryManager)) - - await ContainerLoader({ container }) - - service = container.resolve("priceRuleService") - - await createMoneyAmounts(testManager) - await createPriceSets(testManager) - await createRuleTypes(testManager) - await createPriceSetMoneyAmounts(testManager) - await createPriceSetMoneyAmountRules(testManager) - await createPriceRules(testManager) - }) - - afterEach(async () => { - await MikroOrmWrapper.clearDatabase() - }) - - describe("list", () => { - it("should list priceRules", async () => { - const priceRuleResult = await service.list() - const serialized = JSON.parse(JSON.stringify(priceRuleResult)) - - expect(serialized).toEqual([ - expect.objectContaining({ - id: "price-rule-1", - }), - expect.objectContaining({ - id: "price-rule-2", - }), - ]) - }) - - it("should list priceRules by id", async () => { - const priceRuleResult = await service.list({ - id: ["price-rule-1"], - }) - - expect(priceRuleResult).toEqual([ - expect.objectContaining({ - id: "price-rule-1", - }), - ]) - }) - - it("should list priceRules with relations and selects", async () => { - const priceRulesResult = await service.list( - { - id: ["price-rule-1"], - }, - { - select: ["id", "price_set.id"], - relations: ["price_set"], - } - ) - - const serialized = JSON.parse(JSON.stringify(priceRulesResult)) - - expect(serialized).toEqual([ - { - id: "price-rule-1", - price_set: { - id: "price-set-1", - }, - }, - ]) - }) - - describe("listAndCount", () => { - it("should return priceRules and count", async () => { - const [priceRulesResult, count] = await service.listAndCount() - - expect(count).toEqual(2) - expect(priceRulesResult).toEqual([ - expect.objectContaining({ - id: "price-rule-1", - }), - expect.objectContaining({ - id: "price-rule-2", - }), - ]) - }) - - it("should return priceRules and count when filtered", async () => { - const [priceRulesResult, count] = await service.listAndCount({ - id: ["price-rule-1"], - }) - - expect(count).toEqual(1) - expect(priceRulesResult).toEqual([ - expect.objectContaining({ - id: "price-rule-1", - }), - ]) - }) - - it("should list priceRules with relations and selects", async () => { - const [priceRulesResult, count] = await service.listAndCount( - { - id: ["price-rule-1"], - }, - { - select: ["id", "price_set.id"], - relations: ["price_set"], - } - ) - - const serialized = JSON.parse(JSON.stringify(priceRulesResult)) - - expect(count).toEqual(1) - expect(serialized).toEqual([ - { - id: "price-rule-1", - price_set: { - id: "price-set-1", - }, - }, - ]) - }) - - it("should return priceRules and count when using skip and take", async () => { - const [priceRulesResult, count] = await service.listAndCount( - {}, - { skip: 1, take: 1 } - ) - - expect(count).toEqual(2) - expect(priceRulesResult).toEqual([ - expect.objectContaining({ - id: "price-rule-2", - }), - ]) - }) - - it("should return requested fields", async () => { - const [priceRulesResult, count] = await service.listAndCount( - {}, - { - take: 1, - select: ["id"], - } - ) - - const serialized = JSON.parse(JSON.stringify(priceRulesResult)) - - expect(count).toEqual(2) - expect(serialized).toEqual([ - { - id: "price-rule-1", - }, - ]) - }) - }) - - describe("retrieve", () => { - const id = "price-rule-1" - - it("should return priceRule for the given id", async () => { - const priceRule = await service.retrieve(id) - - expect(priceRule).toEqual( - expect.objectContaining({ - id, - }) - ) - }) - - it("should throw an error when priceRule with id does not exist", async () => { - let error - - try { - await service.retrieve("does-not-exist") - } catch (e) { - error = e - } - - expect(error.message).toEqual( - "PriceRule with id: does-not-exist was not found" - ) - }) - - it("should throw an error when a id is not provided", async () => { - let error - - try { - await service.retrieve(undefined as unknown as string) - } catch (e) { - error = e - } - - expect(error.message).toEqual("priceRule - id must be defined") - }) - - it("should return priceRule based on config select param", async () => { - const priceRule = await service.retrieve(id, { - select: ["id"], - }) - - const serialized = JSON.parse(JSON.stringify(priceRule)) - - expect(serialized).toEqual({ - id, - }) - }) - }) - - describe("delete", () => { - const id = "price-set-1" - - it("should delete the priceRules given an id successfully", async () => { - await service.delete([id]) - - const priceRules = await service.list({ - id: [id], - }) - - expect(priceRules).toHaveLength(0) - }) - }) - - describe("update", () => { - const id = "price-set-1" - - it("should throw an error when a id does not exist", async () => { - let error - - try { - await service.update([ - { - id: "does-not-exist", - }, - ]) - } catch (e) { - error = e - } - - expect(error.message).toEqual( - 'PriceRule with id "does-not-exist" not found' - ) - }) - }) - - describe("create", () => { - it("should throw an error when a id does not exist", async () => { - let error - - try { - await service.update([ - { - random: "does-not-exist", - } as any, - ]) - } catch (e) { - error = e - } - - expect(error.message).toEqual('PriceRule with id "" not found') - }) - - it("should create a priceRule successfully", async () => { - const [ma] = await createMoneyAmounts(testManager, [ - { - amount: 100, - currency_code: "EUR", - }, - ]) - - const psma: PriceSetMoneyAmount = testManager.create( - PriceSetMoneyAmount, - { - price_set: "price-set-1", - money_amount: ma.id, - title: "test", - } - ) - - await testManager.persist(psma).flush() - - await service.create([ - { - id: "price-rule-new", - price_set_id: "price-set-1", - rule_type_id: "rule-type-1", - value: "region_1", - price_list_id: "test", - price_set_money_amount_id: psma.id, - } as unknown as CreatePriceRuleDTO, - ]) - - const [pricerule] = await service.list({ - id: ["price-rule-new"], - }) - - expect(pricerule).toEqual( - expect.objectContaining({ - id: "price-rule-new", - } as unknown as CreatePriceRuleDTO) - ) - }) - }) - }) -}) diff --git a/packages/pricing/integration-tests/__tests__/services/price-set-money-amonut-rules/index.spec.ts b/packages/pricing/integration-tests/__tests__/services/price-set-money-amonut-rules/index.spec.ts deleted file mode 100644 index 8f5848a1d0..0000000000 --- a/packages/pricing/integration-tests/__tests__/services/price-set-money-amonut-rules/index.spec.ts +++ /dev/null @@ -1,289 +0,0 @@ -import { SqlEntityManager } from "@mikro-orm/postgresql" -import { PriceSetMoneyAmountRulesService } from "@services" - -import { seedPriceData } from "../../../__fixtures__/seed-price-data" -import { MikroOrmWrapper } from "../../../utils" -import { createMedusaContainer } from "@medusajs/utils" -import { asValue } from "awilix" -import ContainerLoader from "../../../../src/loaders/container" - -jest.setTimeout(30000) - -describe("PriceSetMoneyAmountRules Service", () => { - let service: PriceSetMoneyAmountRulesService - let testManager: SqlEntityManager - let repositoryManager: SqlEntityManager - - beforeEach(async () => { - await MikroOrmWrapper.setupDatabase() - repositoryManager = await MikroOrmWrapper.forkManager() - - const container = createMedusaContainer() - container.register("manager", asValue(repositoryManager)) - - await ContainerLoader({ container }) - - service = container.resolve("priceSetMoneyAmountRulesService") - - testManager = await MikroOrmWrapper.forkManager() - - await seedPriceData(testManager) - }) - - afterEach(async () => { - await MikroOrmWrapper.clearDatabase() - }) - - describe("list", () => { - it("should list psmar records", async () => { - const priceSetMoneyAmountRulesResult = await service.list() - - expect(priceSetMoneyAmountRulesResult).toEqual([ - expect.objectContaining({ - id: "psmar-1", - }), - expect.objectContaining({ - id: "psmar-2", - }), - expect.objectContaining({ - id: "psmar-3", - }), - ]) - }) - - it("should list psmar record by id", async () => { - const priceSetMoneyAmountRulesResult = await service.list({ - id: ["psmar-1"], - }) - - expect(priceSetMoneyAmountRulesResult).toEqual([ - expect.objectContaining({ - id: "psmar-1", - }), - ]) - }) - }) - - describe("listAndCount", () => { - it("should return psmar records and count", async () => { - const [priceSetMoneyAmountRulesResult, count] = - await service.listAndCount() - - expect(count).toEqual(3) - expect(priceSetMoneyAmountRulesResult).toEqual([ - expect.objectContaining({ - id: "psmar-1", - }), - expect.objectContaining({ - id: "psmar-2", - }), - expect.objectContaining({ - id: "psmar-3", - }), - ]) - }) - - it("should return psmar records and count when filtered", async () => { - const [priceSetMoneyAmountRulesResult, count] = - await service.listAndCount({ - id: ["psmar-1"], - }) - - expect(count).toEqual(1) - expect(priceSetMoneyAmountRulesResult).toEqual([ - expect.objectContaining({ - id: "psmar-1", - }), - ]) - }) - - it("should return psmar and count when using skip and take", async () => { - const [priceSetMoneyAmountRulesResult, count] = - await service.listAndCount({}, { skip: 1, take: 1 }) - - expect(count).toEqual(3) - expect(priceSetMoneyAmountRulesResult).toEqual([ - expect.objectContaining({ - id: "psmar-2", - }), - ]) - }) - - it("should return requested fields", async () => { - const [priceSetMoneyAmountRulesResult, count] = - await service.listAndCount( - {}, - { - take: 1, - select: ["value"], - } - ) - - const serialized = JSON.parse( - JSON.stringify(priceSetMoneyAmountRulesResult) - ) - - expect(count).toEqual(3) - expect(serialized).toEqual([ - { - id: "psmar-1", - value: "EUR", - }, - ]) - }) - }) - - describe("retrieve", () => { - it("should return priceSetMoneyAmountRules for the given id", async () => { - const priceSetMoneyAmountRules = await service.retrieve("psmar-1") - - expect(priceSetMoneyAmountRules).toEqual( - expect.objectContaining({ - id: "psmar-1", - }) - ) - }) - - it("should throw an error when priceSetMoneyAmountRules with id does not exist", async () => { - let error - - try { - await service.retrieve("does-not-exist") - } catch (e) { - error = e - } - - expect(error.message).toEqual( - "PriceSetMoneyAmountRules with id: does-not-exist was not found" - ) - }) - - it("should throw an error when an id is not provided", async () => { - let error - - try { - await service.retrieve(undefined as unknown as string) - } catch (e) { - error = e - } - - expect(error.message).toEqual( - "priceSetMoneyAmountRules - id must be defined" - ) - }) - - it("should return priceSetMoneyAmountRules based on config select param", async () => { - const priceSetMoneyAmountRulesResult = await service.retrieve("psmar-1", { - select: ["value"], - }) - - const serialized = JSON.parse( - JSON.stringify(priceSetMoneyAmountRulesResult) - ) - - expect(serialized).toEqual({ - value: "EUR", - id: "psmar-1", - }) - }) - }) - - describe("delete", () => { - const id = "psmar-1" - - it("should delete the priceSetMoneyAmountRules given an id successfully", async () => { - await service.delete([id]) - - const priceSetMoneyAmountRules = await service.list({ - id: [id], - }) - - expect(priceSetMoneyAmountRules).toHaveLength(0) - }) - }) - - describe("update", () => { - const id = "psmar-1" - - it("should update the value of the priceSetMoneyAmountRules successfully", async () => { - await service.update([ - { - id, - value: "New value", - price_set_money_amount: "price-set-money-amount-CAD", - rule_type: "rule-type-2", - }, - ]) - - const psmar = await service.retrieve(id, { - relations: ["price_set_money_amount", "rule_type"], - }) - - expect(psmar).toEqual( - expect.objectContaining({ - id, - value: "New value", - price_set_money_amount: expect.objectContaining({ - id: "price-set-money-amount-CAD", - }), - rule_type: expect.objectContaining({ - id: "rule-type-2", - }), - }) - ) - }) - - it("should throw an error when a id does not exist", async () => { - let error - - try { - await service.update([ - { - id: "does-not-exist", - value: "random value", - }, - ]) - } catch (e) { - error = e - } - - expect(error.message).toEqual( - 'PriceSetMoneyAmountRules with id "does-not-exist" not found' - ) - }) - }) - - describe("create", () => { - it("should create a priceSetMoneyAmountRules successfully", async () => { - const created = await service.create([ - { - price_set_money_amount: "price-set-money-amount-EUR", - rule_type: "rule-type-2", - value: "New priceSetMoneyAmountRule", - }, - ]) - - const [priceSetMoneyAmountRules] = await service.list( - { - id: [created[0]?.id], - }, - { - relations: ["price_set_money_amount", "rule_type"], - } - ) - - expect(priceSetMoneyAmountRules).toEqual( - expect.objectContaining({ - id: created[0]?.id, - value: "New priceSetMoneyAmountRule", - price_set_money_amount: expect.objectContaining({ - id: "price-set-money-amount-EUR", - }), - rule_type: expect.objectContaining({ - id: "rule-type-2", - }), - }) - ) - }) - }) -}) diff --git a/packages/pricing/integration-tests/__tests__/services/price-set/index.spec.ts b/packages/pricing/integration-tests/__tests__/services/price-set/index.spec.ts deleted file mode 100644 index 5efa4277cc..0000000000 --- a/packages/pricing/integration-tests/__tests__/services/price-set/index.spec.ts +++ /dev/null @@ -1,392 +0,0 @@ -import { CreatePriceSetDTO } from "@medusajs/types" -import { SqlEntityManager } from "@mikro-orm/postgresql" -import { MoneyAmount, PriceSet } from "@models" -import { PriceSetService } from "@services" - -import { createMoneyAmounts } from "../../../__fixtures__/money-amount" -import { createPriceSets } from "../../../__fixtures__/price-set" -import { MikroOrmWrapper } from "../../../utils" -import { createMedusaContainer } from "@medusajs/utils" -import { asValue } from "awilix" -import ContainerLoader from "../../../../src/loaders/container" - -jest.setTimeout(30000) - -describe("PriceSet Service", () => { - let service: PriceSetService - let testManager: SqlEntityManager - let repositoryManager: SqlEntityManager - let data!: PriceSet[] - let moneyAmountsData!: MoneyAmount[] - - const moneyAmountsInputData = [ - { - id: "money-amount-USD", - currency_code: "USD", - amount: 500, - min_quantity: 1, - max_quantity: 10, - }, - ] - - const priceSetInputData = [ - { - id: "price-set-1", - prices: [ - { - id: "money-amount-USD", - currency_code: "EUR", - amount: 100, - }, - ], - }, - { - id: "price-set-2", - prices: [], - }, - { - id: "price-set-3", - prices: [], - }, - ] - - beforeEach(async () => { - await MikroOrmWrapper.setupDatabase() - repositoryManager = await MikroOrmWrapper.forkManager() - testManager = await MikroOrmWrapper.forkManager() - - const container = createMedusaContainer() - container.register("manager", asValue(repositoryManager)) - - await ContainerLoader({ container }) - - service = container.resolve("priceSetService") - - moneyAmountsData = await createMoneyAmounts( - testManager, - moneyAmountsInputData - ) - - data = await createPriceSets(testManager, priceSetInputData) - }) - - afterEach(async () => { - await MikroOrmWrapper.clearDatabase() - }) - - describe("list", () => { - it("should list priceSets", async () => { - const priceSetsResult = await service.list() - const serialized = JSON.parse(JSON.stringify(priceSetsResult)) - - expect(serialized).toEqual([ - expect.objectContaining({ - id: "price-set-1", - }), - expect.objectContaining({ - id: "price-set-2", - }), - expect.objectContaining({ - id: "price-set-3", - }), - ]) - }) - - it("should list priceSets by id", async () => { - const priceSetsResult = await service.list({ - id: ["price-set-1"], - }) - - expect(priceSetsResult).toEqual([ - expect.objectContaining({ - id: "price-set-1", - }), - ]) - }) - - it("should list priceSets with relations and selects", async () => { - const priceSetsResult = await service.list( - { - id: ["price-set-1"], - }, - { - select: ["id", "money_amounts.id"], - relations: ["money_amounts"], - } - ) - - const serialized = JSON.parse(JSON.stringify(priceSetsResult)) - - expect(serialized).toEqual([ - { - id: "price-set-1", - money_amounts: [ - expect.objectContaining({ - id: "money-amount-USD", - }), - ], - }, - ]) - }) - - it("should scope priceSets with currency_code of money amounts", async () => { - const priceSetsResult = await service.list( - { - money_amounts: { - currency_code: ["USD"], - }, - }, - { - select: ["id", "money_amounts.id"], - relations: ["money_amounts"], - } - ) - - const serialized = JSON.parse(JSON.stringify(priceSetsResult)) - - expect(serialized).toEqual([ - { - id: "price-set-1", - money_amounts: [ - expect.objectContaining({ - id: "money-amount-USD", - }), - ], - }, - ]) - }) - }) - - it("should not return price sets if money amounts with a currency code dont exist", async () => { - const priceSetsResult = await service.list( - { - money_amounts: { - currency_code: ["DOESNOTEXIST"], - }, - }, - { - select: ["id", "money_amounts.id"], - relations: ["money_amounts"], - } - ) - - const serialized = JSON.parse(JSON.stringify(priceSetsResult)) - - expect(serialized).toEqual([]) - }) - - describe("listAndCount", () => { - it("should return priceSets and count", async () => { - const [priceSetsResult, count] = await service.listAndCount() - - expect(count).toEqual(3) - expect(priceSetsResult).toEqual([ - expect.objectContaining({ - id: "price-set-1", - }), - expect.objectContaining({ - id: "price-set-2", - }), - expect.objectContaining({ - id: "price-set-3", - }), - ]) - }) - - it("should return priceSets and count when filtered", async () => { - const [priceSetsResult, count] = await service.listAndCount({ - id: ["price-set-1"], - }) - - expect(count).toEqual(1) - expect(priceSetsResult).toEqual([ - expect.objectContaining({ - id: "price-set-1", - }), - ]) - }) - - it("should list priceSets with relations and selects", async () => { - const [priceSetsResult, count] = await service.listAndCount( - { - id: ["price-set-1"], - }, - { - select: ["id", "min_quantity", "money_amounts.id"], - relations: ["money_amounts"], - } - ) - - const serialized = JSON.parse(JSON.stringify(priceSetsResult)) - - expect(count).toEqual(1) - expect(serialized).toEqual([ - { - id: "price-set-1", - money_amounts: [ - expect.objectContaining({ - id: "money-amount-USD", - }), - ], - }, - ]) - }) - - it("should return priceSets and count when using skip and take", async () => { - const [priceSetsResult, count] = await service.listAndCount( - {}, - { skip: 1, take: 1 } - ) - - expect(count).toEqual(3) - expect(priceSetsResult).toEqual([ - expect.objectContaining({ - id: "price-set-2", - }), - ]) - }) - - it("should return requested fields", async () => { - const [priceSetsResult, count] = await service.listAndCount( - {}, - { - take: 1, - select: ["id"], - } - ) - - const serialized = JSON.parse(JSON.stringify(priceSetsResult)) - - expect(count).toEqual(3) - expect(serialized).toEqual([ - { - id: "price-set-1", - }, - ]) - }) - }) - - describe("retrieve", () => { - const id = "price-set-1" - - it("should return priceSet for the given id", async () => { - const priceSet = await service.retrieve(id) - - expect(priceSet).toEqual( - expect.objectContaining({ - id, - }) - ) - }) - - it("should throw an error when priceSet with id does not exist", async () => { - let error - - try { - await service.retrieve("does-not-exist") - } catch (e) { - error = e - } - - expect(error.message).toEqual( - "PriceSet with id: does-not-exist was not found" - ) - }) - - it("should throw an error when a id is not provided", async () => { - let error - - try { - await service.retrieve(undefined as unknown as string) - } catch (e) { - error = e - } - - expect(error.message).toEqual("priceSet - id must be defined") - }) - - it("should return priceSet based on config select param", async () => { - const priceSet = await service.retrieve(id, { - select: ["id"], - }) - - const serialized = JSON.parse(JSON.stringify(priceSet)) - - expect(serialized).toEqual({ - id, - }) - }) - }) - - describe("delete", () => { - const id = "price-set-1" - - it("should delete the priceSets given an id successfully", async () => { - await service.delete([id]) - - const priceSets = await service.list({ - id: [id], - }) - - expect(priceSets).toHaveLength(0) - }) - }) - - describe("update", () => { - const id = "price-set-1" - - it("should throw an error when a id does not exist", async () => { - let error - - try { - await service.update([ - { - id: "does-not-exist", - }, - ]) - } catch (e) { - error = e - } - - expect(error.message).toEqual( - 'PriceSet with id "does-not-exist" not found' - ) - }) - }) - - describe("create", () => { - it("should throw an error when a id does not exist", async () => { - let error - - try { - await service.update([ - { - random: "does-not-exist", - } as any, - ]) - } catch (e) { - error = e - } - - expect(error.message).toEqual('PriceSet with id "" not found') - }) - - it("should create a priceSet successfully", async () => { - await service.create([ - { - id: "price-set-new", - } as unknown as CreatePriceSetDTO, - ]) - - const [priceSet] = await service.list({ - id: ["price-set-new"], - }) - - expect(priceSet).toEqual( - expect.objectContaining({ - id: "price-set-new", - } as unknown as CreatePriceSetDTO) - ) - }) - }) -}) diff --git a/packages/pricing/integration-tests/__tests__/services/pricing-module/calculate-price.spec.ts b/packages/pricing/integration-tests/__tests__/services/pricing-module/calculate-price.spec.ts index bf5f6607c6..a18fe8bfb6 100644 --- a/packages/pricing/integration-tests/__tests__/services/pricing-module/calculate-price.spec.ts +++ b/packages/pricing/integration-tests/__tests__/services/pricing-module/calculate-price.spec.ts @@ -5,13 +5,9 @@ import { PricingTypes, } from "@medusajs/types" import { PriceListType } from "@medusajs/utils" -import { SqlEntityManager } from "@mikro-orm/postgresql" -import { PriceSet } from "@models" import { seedPriceData } from "../../../__fixtures__/seed-price-data" -import { MikroOrmWrapper } from "../../../utils" -import { getInitModuleConfig } from "../../../utils/get-init-module-config" -import { initModules } from "medusa-test-utils" import { Modules } from "@medusajs/modules-sdk" +import { moduleIntegrationTestRunner, SuiteOptions } from "medusa-test-utils" jest.setTimeout(30000) @@ -52,1941 +48,1926 @@ const createPriceLists = async ( ]) } -describe("PricingModule Service - Calculate Price", () => { - let service: IPricingModuleService - let testManager: SqlEntityManager - let repositoryManager: SqlEntityManager - let data!: PriceSet[] - let shutdownFunc: () => Promise - - beforeAll(async () => { - const initModulesConfig = getInitModuleConfig() - - const { medusaApp, shutdown } = await initModules(initModulesConfig) - - service = medusaApp.modules[Modules.PRICING] - - shutdownFunc = shutdown - }) - - afterAll(async () => { - await shutdownFunc() - }) - - beforeEach(async () => { - await MikroOrmWrapper.setupDatabase() - repositoryManager = MikroOrmWrapper.forkManager() - testManager = MikroOrmWrapper.forkManager() - }) - - afterEach(async () => { - await MikroOrmWrapper.clearDatabase() - }) - - describe("calculatePrices", () => { - beforeEach(async () => { - const moneyAmountsData = [ - { - id: "money-amount-PLN", - currency_code: "PLN", - amount: 1000, - min_quantity: 1, - max_quantity: 10, - }, - { - id: "money-amount-company_id-EUR", - currency_code: "EUR", - amount: 500, - min_quantity: 1, - max_quantity: 10, - }, - { - id: "money-amount-company_id-PLN", - currency_code: "PLN", - amount: 400, - min_quantity: 1, - max_quantity: 5, - }, - { - id: "money-amount-region_id-PLN", - currency_code: "PLN", - amount: 300, - min_quantity: 1, - max_quantity: 4, - }, - { - id: "money-amount-region_id+company_id-PLN", - currency_code: "PLN", - amount: 999, - min_quantity: 1, - max_quantity: 10, - }, - { - id: "money-amount-region_id-PLN-5-qty", - currency_code: "PLN", - amount: 250, - min_quantity: 4, - max_quantity: 10, - }, - { - id: "money-amount-region_id-PL-EUR", - currency_code: "EUR", - amount: 200, - min_quantity: 1, - max_quantity: 3, - }, - { - id: "money-amount-region_id-PL-EUR-4-qty", - currency_code: "EUR", - amount: 50, - min_quantity: 4, - max_quantity: 10, - }, - { - id: "money-amount-region_id-PL-EUR-customer-group", - currency_code: "EUR", - amount: 100, - min_quantity: 1, - max_quantity: 3, - }, - ] - - const priceSetsData = [ - { - id: "price-set-EUR", - }, - { - id: "price-set-PLN", - }, - ] as unknown as CreatePriceSetDTO[] - - const priceSetMoneyAmountsData = [ - { - id: "psma-PLN", - title: "psma PLN", - price_set: "price-set-PLN", - money_amount: "money-amount-PLN", - rules_count: 0, - }, - { - id: "psma-company_id-EUR", - title: "psma EUR - company_id", - price_set: "price-set-EUR", - money_amount: "money-amount-company_id-EUR", - rules_count: 1, - }, - { - id: "psma-company_id-PLN", - title: "psma PLN - company_id", - price_set: "price-set-PLN", - money_amount: "money-amount-company_id-PLN", - rules_count: 1, - }, - { - id: "psma-region_id-PLN", - title: "psma PLN - region_id", - price_set: "price-set-PLN", - money_amount: "money-amount-region_id-PLN", - rules_count: 1, - }, - { - id: "psma-region_id+company_id-PLN", - title: "psma region_id + company_id", - price_set: "price-set-PLN", - money_amount: "money-amount-region_id+company_id-PLN", - rules_count: 2, - }, - { - id: "psma-region_id-PLN-5-qty", - title: "psma PLN - region_id 5 qty", - price_set: "price-set-PLN", - money_amount: "money-amount-region_id-PLN-5-qty", - rules_count: 1, - }, - { - id: "psma-region_id_company_id-PL-EUR", - title: "psma PLN - region_id PL with EUR currency", - price_set: "price-set-PLN", - money_amount: "money-amount-region_id-PL-EUR", - rules_count: 2, - }, - { - id: "psma-region_id_company_id-PL-EUR-4-qty", - title: "psma PLN - region_id PL with EUR currency for quantity 4", - price_set: "price-set-PLN", - money_amount: "money-amount-region_id-PL-EUR-4-qty", - rules_count: 2, - }, - { - id: "psma-region_id_company_id-PL-EUR-customer-group", - title: "psma PLN - region_id PL with EUR currency for customer group", - price_set: "price-set-PLN", - money_amount: "money-amount-region_id-PL-EUR-customer-group", - rules_count: 3, - }, - ] - - const ruleTypesData = [ - { - id: "rule-type-company_id", - name: "rule type company id", - rule_attribute: "company_id", - default_priority: 2, - }, - { - id: "rule-type-region_id", - name: "rule type region id", - rule_attribute: "region_id", - default_priority: 1, - }, - { - id: "rule-type-customer_group_id", - name: "rule type customer group id", - rule_attribute: "customer_group_id", - default_priority: 3, - }, - ] - - const priceRuleData = [ - { - id: "price-rule-company_id-EUR", - price_set_id: "price-set-EUR", - rule_type_id: "rule-type-company_id", - value: "EUR", - price_list_id: "test", - price_set_money_amount_id: "psma-company_id-EUR", - }, - { - id: "price-rule-company_id-PLN", - price_set_id: "price-set-PLN", - rule_type_id: "rule-type-company_id", - value: "medusa-company-id", - price_list_id: "test", - price_set_money_amount_id: "psma-company_id-PLN", - }, - { - id: "price-rule-region_id-PLN", - price_set_id: "price-set-PLN", - rule_type_id: "rule-type-region_id", - value: "PL", - price_list_id: "test", - price_set_money_amount_id: "psma-region_id-PLN", - }, - { - id: "price-rule-region_id+company_id-PL", - price_set_id: "price-set-PLN", - rule_type_id: "rule-type-region_id", - value: "PL", - price_list_id: "test", - price_set_money_amount_id: "psma-region_id+company_id-PLN", - }, - { - id: "price-rule-region_id+company_id-medusa-company-id", - price_set_id: "price-set-PLN", - rule_type_id: "rule-type-company_id", - value: "medusa-company-id", - price_list_id: "test", - price_set_money_amount_id: "psma-region_id+company_id-PLN", - }, - { - id: "price-rule-region_id-PLN-5-qty", - price_set_id: "price-set-PLN", - rule_type_id: "rule-type-region_id", - value: "PL", - price_list_id: "test", - price_set_money_amount_id: "psma-region_id-PLN-5-qty", - }, - { - id: "price-rule-region_id-company_id-PL", - price_set_id: "price-set-PLN", - rule_type_id: "rule-type-region_id", - value: "PL", - price_list_id: "test", - price_set_money_amount_id: "psma-region_id_company_id-PL-EUR", - }, - { - id: "price-rule-region_id-company_id-PLN", - price_set_id: "price-set-PLN", - rule_type_id: "rule-type-company_id", - value: "medusa-company-id", - price_list_id: "test", - price_set_money_amount_id: "psma-region_id_company_id-PL-EUR", - }, - { - id: "price-rule-region_id-company_id-PL-4-qty", - price_set_id: "price-set-PLN", - rule_type_id: "rule-type-region_id", - value: "PL", - price_list_id: "test", - price_set_money_amount_id: "psma-region_id_company_id-PL-EUR-4-qty", - }, - { - id: "price-rule-region_id-company_id-PLN-4-qty", - price_set_id: "price-set-PLN", - rule_type_id: "rule-type-company_id", - value: "medusa-company-id", - price_list_id: "test", - price_set_money_amount_id: "psma-region_id_company_id-PL-EUR-4-qty", - }, - { - id: "price-rule-region_id-currency_customer_group_code-PL", - price_set_id: "price-set-PLN", - rule_type_id: "rule-type-region_id", - value: "PL", - price_list_id: "test", - price_set_money_amount_id: - "psma-region_id_company_id-PL-EUR-customer-group", - }, - { - id: "price-rule-region_id-currency_customer_group_code-PLN", - price_set_id: "price-set-PLN", - rule_type_id: "rule-type-company_id", - value: "medusa-company-id", - price_list_id: "test", - price_set_money_amount_id: - "psma-region_id_company_id-PL-EUR-customer-group", - }, - { - id: "price-rule-region_id-currency_customer_group_code-test_customer_group", - price_set_id: "price-set-PLN", - rule_type_id: "rule-type-customer_group_id", - value: "test-customer-group", - price_list_id: "test", - price_set_money_amount_id: - "psma-region_id_company_id-PL-EUR-customer-group", - }, - ] as unknown as CreatePriceRuleDTO[] - - await seedPriceData(MikroOrmWrapper.forkManager(), { - moneyAmountsData, - priceSetsData, - priceSetMoneyAmountsData, - priceSetMoneyAmountRulesData: [], - priceRuleData, - ruleTypesData, - }) - }) - - it("should throw an error when currency code is not set", async () => { - let result = service.calculatePrices( - { id: ["price-set-EUR", "price-set-PLN"] }, - {} - ) - - expect(result).rejects.toThrow( - "Method calculatePrices requires currency_code in the pricing context" - ) - - result = service.calculatePrices( - { id: ["price-set-EUR", "price-set-PLN"] }, - { context: { region_id: "DE" } } - ) - - expect(result).rejects.toThrow( - "Method calculatePrices requires currency_code in the pricing context" - ) - }) - - it("should return filled prices when 1 context is present and price is setup for PLN", async () => { - const priceSetsResult = await service.calculatePrices( - { id: ["price-set-EUR", "price-set-PLN"] }, - { - context: { currency_code: "PLN" }, - } - ) - - expect(priceSetsResult).toEqual([ - { - id: "price-set-EUR", - is_calculated_price_price_list: false, - calculated_amount: null, - is_original_price_price_list: false, - original_amount: null, - currency_code: null, - calculated_price: { - money_amount_id: null, - price_list_id: null, - price_list_type: null, - min_quantity: null, - max_quantity: null, - }, - original_price: { - money_amount_id: null, - price_list_id: null, - price_list_type: null, - min_quantity: null, - max_quantity: null, - }, - }, - { - id: "price-set-PLN", - is_calculated_price_price_list: false, - calculated_amount: 1000, - is_original_price_price_list: false, - original_amount: 1000, - currency_code: "PLN", - calculated_price: { - money_amount_id: "money-amount-PLN", - price_list_id: null, - price_list_type: null, - min_quantity: 1, - max_quantity: 10, - }, - original_price: { - money_amount_id: "money-amount-PLN", - price_list_id: null, - price_list_type: null, - min_quantity: 1, - max_quantity: 10, - }, - }, - ]) - }) - - it("should return filled prices when 1 context is present and price is setup for PLN region_id", async () => { - const priceSetsResult = await service.calculatePrices( - { id: ["price-set-EUR", "price-set-PLN"] }, - { - context: { currency_code: "PLN", region_id: "PL" }, - } - ) - - expect(priceSetsResult).toEqual([ - { - id: "price-set-EUR", - is_calculated_price_price_list: false, - calculated_amount: null, - is_original_price_price_list: false, - original_amount: null, - currency_code: null, - calculated_price: { - money_amount_id: null, - price_list_id: null, - price_list_type: null, - min_quantity: null, - max_quantity: null, - }, - original_price: { - money_amount_id: null, - price_list_id: null, - price_list_type: null, - min_quantity: null, - max_quantity: null, - }, - }, - { - id: "price-set-PLN", - is_calculated_price_price_list: false, - calculated_amount: 300, - is_original_price_price_list: false, - original_amount: 300, - currency_code: "PLN", - calculated_price: { - money_amount_id: "money-amount-region_id-PLN", - price_list_id: null, - price_list_type: null, - min_quantity: 1, - max_quantity: 4, - }, - original_price: { - money_amount_id: "money-amount-region_id-PLN", - price_list_id: null, - price_list_type: null, - min_quantity: 1, - max_quantity: 4, - }, - }, - ]) - }) - - it("should return filled prices when 1 context is present and price is setup for PLN currency_code", async () => { - const priceSetsResult = await service.calculatePrices( - { id: ["price-set-EUR", "price-set-PLN"] }, - { - context: { currency_code: "PLN" }, - } - ) - - expect(priceSetsResult).toEqual([ - { - id: "price-set-EUR", - is_calculated_price_price_list: false, - calculated_amount: null, - is_original_price_price_list: false, - original_amount: null, - currency_code: null, - calculated_price: { - money_amount_id: null, - price_list_id: null, - price_list_type: null, - min_quantity: null, - max_quantity: null, - }, - original_price: { - money_amount_id: null, - price_list_id: null, - price_list_type: null, - min_quantity: null, - max_quantity: null, - }, - }, - { - id: "price-set-PLN", - is_calculated_price_price_list: false, - calculated_amount: 1000, - is_original_price_price_list: false, - original_amount: 1000, - currency_code: "PLN", - calculated_price: { - money_amount_id: "money-amount-PLN", - price_list_id: null, - price_list_type: null, - min_quantity: 1, - max_quantity: 10, - }, - original_price: { - money_amount_id: "money-amount-PLN", - price_list_id: null, - price_list_type: null, - min_quantity: 1, - max_quantity: 10, - }, - }, - ]) - }) - - it("should return null prices when 1 context is present and price is NOT setup", async () => { - const priceSetsResult = await service.calculatePrices( - { id: ["price-set-EUR", "price-set-PLN"] }, - { - context: { currency_code: "EUR", does_not_exist: "EUR" }, - } - ) - - expect(priceSetsResult).toEqual([ - { - id: "price-set-EUR", - is_calculated_price_price_list: false, - calculated_amount: null, - is_original_price_price_list: false, - original_amount: null, - currency_code: null, - calculated_price: { - money_amount_id: null, - price_list_id: null, - price_list_type: null, - min_quantity: null, - max_quantity: null, - }, - original_price: { - money_amount_id: null, - price_list_id: null, - price_list_type: null, - min_quantity: null, - max_quantity: null, - }, - }, - { - id: "price-set-PLN", - is_calculated_price_price_list: false, - calculated_amount: null, - is_original_price_price_list: false, - original_amount: null, - currency_code: null, - calculated_price: { - money_amount_id: null, - price_list_id: null, - price_list_type: null, - min_quantity: null, - max_quantity: null, - }, - original_price: { - money_amount_id: null, - price_list_id: null, - price_list_type: null, - min_quantity: null, - max_quantity: null, - }, - }, - ]) - }) - - it("should return filled prices when 2 contexts are present and price is setup", async () => { - const priceSetsResult = await service.calculatePrices( - { id: ["price-set-EUR", "price-set-PLN"] }, - { - context: { currency_code: "PLN", region_id: "PL" }, - } - ) - - expect(priceSetsResult).toEqual([ - { - id: "price-set-EUR", - is_calculated_price_price_list: false, - calculated_amount: null, - is_original_price_price_list: false, - original_amount: null, - currency_code: null, - calculated_price: { - money_amount_id: null, - price_list_id: null, - price_list_type: null, - min_quantity: null, - max_quantity: null, - }, - original_price: { - money_amount_id: null, - price_list_id: null, - price_list_type: null, - min_quantity: null, - max_quantity: null, - }, - }, - { - id: "price-set-PLN", - is_calculated_price_price_list: false, - calculated_amount: 300, - is_original_price_price_list: false, - original_amount: 300, - currency_code: "PLN", - calculated_price: { - money_amount_id: "money-amount-region_id-PLN", - price_list_id: null, - price_list_type: null, - min_quantity: 1, - max_quantity: 4, - }, - original_price: { - money_amount_id: "money-amount-region_id-PLN", - price_list_id: null, - price_list_type: null, - min_quantity: 1, - max_quantity: 4, - }, - }, - ]) - }) - - it("should return filled prices when 2 contexts are present and price is not setup", async () => { - const priceSetsResult = await service.calculatePrices( - { id: ["price-set-EUR", "price-set-PLN"] }, - { - context: { currency_code: "PLN", company_id: "doesnotexist" }, - } - ) - - expect(priceSetsResult).toEqual([ - { - id: "price-set-EUR", - is_calculated_price_price_list: false, - calculated_amount: null, - is_original_price_price_list: false, - original_amount: null, - currency_code: null, - calculated_price: { - money_amount_id: null, - price_list_id: null, - price_list_type: null, - min_quantity: null, - max_quantity: null, - }, - original_price: { - money_amount_id: null, - price_list_id: null, - price_list_type: null, - min_quantity: null, - max_quantity: null, - }, - }, - { - id: "price-set-PLN", - is_calculated_price_price_list: false, - calculated_amount: 1000, - is_original_price_price_list: false, - original_amount: 1000, - currency_code: "PLN", - calculated_price: { - money_amount_id: "money-amount-PLN", - price_list_id: null, - price_list_type: null, - min_quantity: 1, - max_quantity: 10, - }, - original_price: { - money_amount_id: "money-amount-PLN", - price_list_id: null, - price_list_type: null, - min_quantity: 1, - max_quantity: 10, - }, - }, - ]) - }) - - it("should return filled prices when 2 contexts are present and price is setup along with declaring quantity", async () => { - const priceSetsResult = await service.calculatePrices( - { id: ["price-set-PLN"] }, - { - context: { currency_code: "PLN", region_id: "PL", quantity: 5 }, - } - ) - - expect(priceSetsResult).toEqual([ - { - id: "price-set-PLN", - is_calculated_price_price_list: false, - calculated_amount: 250, - is_original_price_price_list: false, - original_amount: 250, - currency_code: "PLN", - calculated_price: { - money_amount_id: "money-amount-region_id-PLN-5-qty", - price_list_id: null, - price_list_type: null, - min_quantity: 4, - max_quantity: 10, - }, - original_price: { - money_amount_id: "money-amount-region_id-PLN-5-qty", - price_list_id: null, - price_list_type: null, - min_quantity: 4, - max_quantity: 10, - }, - }, - ]) - }) - - it("should return filled prices when 3 contexts are present and price is partially setup for 2", async () => { - const priceSetsResult = await service.calculatePrices( - { id: ["price-set-EUR", "price-set-PLN"] }, - { - context: { - currency_code: "PLN", - region_id: "PL", - customer_group_id: "test-customer-group", - company_id: "does-not-exist", - }, - } - ) - - expect(priceSetsResult).toEqual([ - { - id: "price-set-EUR", - is_calculated_price_price_list: false, - calculated_amount: null, - is_original_price_price_list: false, - original_amount: null, - currency_code: null, - calculated_price: { - money_amount_id: null, - price_list_id: null, - price_list_type: null, - min_quantity: null, - max_quantity: null, - }, - original_price: { - money_amount_id: null, - price_list_id: null, - price_list_type: null, - min_quantity: null, - max_quantity: null, - }, - }, - { - id: "price-set-PLN", - is_calculated_price_price_list: false, - calculated_amount: 300, - is_original_price_price_list: false, - original_amount: 300, - currency_code: "PLN", - calculated_price: { - money_amount_id: "money-amount-region_id-PLN", - price_list_id: null, - price_list_type: null, - min_quantity: 1, - max_quantity: 4, - }, - original_price: { - money_amount_id: "money-amount-region_id-PLN", - price_list_id: null, - price_list_type: null, - min_quantity: 1, - max_quantity: 4, - }, - }, - ]) - }) - - it("should return filled prices when 3 contexts are present and price is setup for 3", async () => { - const priceSetsResult = await service.calculatePrices( - { id: ["price-set-EUR", "price-set-PLN"] }, - { - context: { - currency_code: "EUR", - region_id: "PL", - customer_group_id: "test-customer-group", - company_id: "medusa-company-id", - }, - } - ) - - expect(priceSetsResult).toEqual([ - { - id: "price-set-EUR", - is_calculated_price_price_list: false, - calculated_amount: null, - is_original_price_price_list: false, - original_amount: null, - currency_code: null, - calculated_price: { - money_amount_id: null, - price_list_id: null, - price_list_type: null, - min_quantity: null, - max_quantity: null, - }, - original_price: { - money_amount_id: null, - price_list_id: null, - price_list_type: null, - min_quantity: null, - max_quantity: null, - }, - }, - // Currency Code + Region value + customer group id - { - id: "price-set-PLN", - is_calculated_price_price_list: false, - calculated_amount: 100, - is_original_price_price_list: false, - original_amount: 100, - currency_code: "EUR", - calculated_price: { - money_amount_id: "money-amount-region_id-PL-EUR-customer-group", - price_list_id: null, - price_list_type: null, - min_quantity: 1, - max_quantity: 3, - }, - original_price: { - money_amount_id: "money-amount-region_id-PL-EUR-customer-group", - price_list_id: null, - price_list_type: null, - min_quantity: 1, - max_quantity: 3, - }, - }, - ]) - }) - - it("should return filled prices when 3 contexts are present and price is setup for 3, but value is incorrect for 2. It falls back to 1 rule context when 1 rule is not setup", async () => { - const priceSetsResult = await service.calculatePrices( - { id: ["price-set-EUR", "price-set-PLN"] }, - { - context: { - currency_code: "PLN", - region_id: "PL", - customer_group_id: "does-not-exist", - company_id: "does-not-exist", - }, - } - ) - - expect(priceSetsResult).toEqual([ - { - id: "price-set-EUR", - is_calculated_price_price_list: false, - calculated_amount: null, - is_original_price_price_list: false, - original_amount: null, - currency_code: null, - calculated_price: { - money_amount_id: null, - price_list_id: null, - price_list_type: null, - min_quantity: null, - max_quantity: null, - }, - original_price: { - money_amount_id: null, - price_list_id: null, - price_list_type: null, - min_quantity: null, - max_quantity: null, - }, - }, - // PLN price set is not setup for EUR currency_code so it will default to a null set - { - id: "price-set-PLN", - is_calculated_price_price_list: false, - calculated_amount: 300, - is_original_price_price_list: false, - original_amount: 300, - currency_code: "PLN", - calculated_price: { - money_amount_id: "money-amount-region_id-PLN", - price_list_id: null, - price_list_type: null, - min_quantity: 1, - max_quantity: 4, - }, - original_price: { - money_amount_id: "money-amount-region_id-PLN", - price_list_id: null, - price_list_type: null, - min_quantity: 1, - max_quantity: 4, - }, - }, - ]) - }) - - it("should return filled prices when 3 contexts are present and price is setup for 3, but value is incorrect for 2. It falls back to default value", async () => { - const priceSetsResult = await service.calculatePrices( - { id: ["price-set-EUR", "price-set-PLN"] }, - { - context: { - currency_code: "PLN", - region_id: "does-not-exist", - customer_group_id: "does-not-exist", - company_id: "does-not-exist", - }, - } - ) - - expect(priceSetsResult).toEqual([ - { - id: "price-set-EUR", - is_calculated_price_price_list: false, - calculated_amount: null, - is_original_price_price_list: false, - original_amount: null, - currency_code: null, - calculated_price: { - money_amount_id: null, - price_list_id: null, - price_list_type: null, - min_quantity: null, - max_quantity: null, - }, - original_price: { - money_amount_id: null, - price_list_id: null, - price_list_type: null, - min_quantity: null, - max_quantity: null, - }, - }, - // PLN price set is not setup for EUR currency_code so it will default to a null set - { - id: "price-set-PLN", - is_calculated_price_price_list: false, - calculated_amount: 1000, - is_original_price_price_list: false, - original_amount: 1000, - currency_code: "PLN", - calculated_price: { - money_amount_id: "money-amount-PLN", - price_list_id: null, - price_list_type: null, - min_quantity: 1, - max_quantity: 10, - }, - original_price: { - money_amount_id: "money-amount-PLN", - price_list_id: null, - price_list_type: null, - min_quantity: 1, - max_quantity: 10, - }, - }, - ]) - }) - - it("should return null prices when 2 context is present and prices are NOT setup", async () => { - const priceSetsResult = await service.calculatePrices( - { id: ["price-set-EUR", "price-set-PLN"] }, - { - context: { currency_code: "EUR", does_not_exist_2: "Berlin" }, - } - ) - - expect(priceSetsResult).toEqual([ - { - id: "price-set-EUR", - is_calculated_price_price_list: false, - calculated_amount: null, - is_original_price_price_list: false, - original_amount: null, - currency_code: null, - calculated_price: { - money_amount_id: null, - price_list_id: null, - price_list_type: null, - min_quantity: null, - max_quantity: null, - }, - original_price: { - money_amount_id: null, - price_list_id: null, - price_list_type: null, - min_quantity: null, - max_quantity: null, - }, - }, - { - id: "price-set-PLN", - is_calculated_price_price_list: false, - calculated_amount: null, - is_original_price_price_list: false, - original_amount: null, - currency_code: null, - calculated_price: { - money_amount_id: null, - price_list_id: null, - price_list_type: null, - min_quantity: null, - max_quantity: null, - }, - original_price: { - money_amount_id: null, - price_list_id: null, - price_list_type: null, - min_quantity: null, - max_quantity: null, - }, - }, - ]) - }) - - it("should return filled prices when 2 context is present and prices are setup, but only for one of the contexts", async () => { - const priceSetsResult = await service.calculatePrices( - { id: ["price-set-EUR", "price-set-PLN"] }, - { - context: { currency_code: "PLN", region_id: "PL", city: "Berlin" }, - } - ) - - expect(priceSetsResult).toEqual([ - { - id: "price-set-EUR", - is_calculated_price_price_list: false, - calculated_amount: null, - is_original_price_price_list: false, - original_amount: null, - currency_code: null, - calculated_price: { - money_amount_id: null, - price_list_id: null, - price_list_type: null, - min_quantity: null, - max_quantity: null, - }, - original_price: { - money_amount_id: null, - price_list_id: null, - price_list_type: null, - min_quantity: null, - max_quantity: null, - }, - }, - { - id: "price-set-PLN", - is_calculated_price_price_list: false, - calculated_amount: 300, - is_original_price_price_list: false, - original_amount: 300, - currency_code: "PLN", - calculated_price: { - money_amount_id: "money-amount-region_id-PLN", - price_list_id: null, - price_list_type: null, - min_quantity: 1, - max_quantity: 4, - }, - original_price: { - money_amount_id: "money-amount-region_id-PLN", - price_list_id: null, - price_list_type: null, - min_quantity: 1, - max_quantity: 4, - }, - }, - ]) - }) - - describe("Price Lists", () => { - it("should return price list prices when price list conditions match", async () => { - await createPriceLists(service) - - const priceSetsResult = await service.calculatePrices( - { id: ["price-set-EUR", "price-set-PLN"] }, - { - context: { +moduleIntegrationTestRunner({ + moduleName: Modules.PRICING, + testSuite: ({ + MikroOrmWrapper, + service, + }: SuiteOptions) => { + describe("PricingModule Service - Calculate Price", () => { + describe("calculatePrices", () => { + beforeEach(async () => { + const moneyAmountsData = [ + { + id: "money-amount-PLN", currency_code: "PLN", - region_id: "DE", - customer_group_id: "vip-customer-group-id", - company_id: "medusa-company-id", - }, - } - ) - - expect(priceSetsResult).toEqual([ - { - id: "price-set-EUR", - is_calculated_price_price_list: false, - calculated_amount: null, - is_original_price_price_list: false, - original_amount: null, - currency_code: null, - calculated_price: { - money_amount_id: null, - price_list_id: null, - price_list_type: null, - min_quantity: null, - max_quantity: null, - }, - original_price: { - money_amount_id: null, - price_list_id: null, - price_list_type: null, - min_quantity: null, - max_quantity: null, - }, - }, - { - id: "price-set-PLN", - is_calculated_price_price_list: true, - calculated_amount: 232, - is_original_price_price_list: false, - original_amount: 400, - currency_code: "PLN", - calculated_price: { - money_amount_id: expect.any(String), - price_list_id: expect.any(String), - price_list_type: "sale", - min_quantity: null, - max_quantity: null, - }, - original_price: { - money_amount_id: expect.any(String), - price_list_id: null, - price_list_type: null, - min_quantity: 1, - max_quantity: 5, - }, - }, - ]) - }) - - it("should return best price list price first when price list conditions match", async () => { - await createPriceLists(service) - await createPriceLists( - service, - {}, - {}, - defaultPriceListPrices.map((price) => { - return { ...price, amount: price.amount / 2 } - }) - ) - - const priceSetsResult = await service.calculatePrices( - { id: ["price-set-EUR", "price-set-PLN"] }, - { - context: { - currency_code: "PLN", - region_id: "DE", - customer_group_id: "vip-customer-group-id", - company_id: "medusa-company-id", - }, - } - ) - - expect(priceSetsResult).toEqual([ - { - id: "price-set-EUR", - is_calculated_price_price_list: false, - calculated_amount: null, - is_original_price_price_list: false, - original_amount: null, - currency_code: null, - calculated_price: { - money_amount_id: null, - price_list_id: null, - price_list_type: null, - min_quantity: null, - max_quantity: null, - }, - original_price: { - money_amount_id: null, - price_list_id: null, - price_list_type: null, - min_quantity: null, - max_quantity: null, - }, - }, - { - id: "price-set-PLN", - is_calculated_price_price_list: true, - calculated_amount: 116, - is_original_price_price_list: false, - original_amount: 400, - currency_code: "PLN", - calculated_price: { - money_amount_id: expect.any(String), - price_list_id: expect.any(String), - price_list_type: "sale", - min_quantity: null, - max_quantity: null, - }, - original_price: { - money_amount_id: expect.any(String), - price_list_id: null, - price_list_type: null, - min_quantity: 1, - max_quantity: 5, - }, - }, - ]) - }) - - it("should return price list prices when price list dont have rules, but context is loaded", async () => { - await createPriceLists(service, {}, {}) - - const priceSetsResult = await service.calculatePrices( - { id: ["price-set-EUR", "price-set-PLN"] }, - { - context: { - currency_code: "PLN", - region_id: "DE", - customer_group_id: "vip-customer-group-id", - company_id: "medusa-company-id", - }, - } - ) - - expect(priceSetsResult).toEqual([ - { - id: "price-set-EUR", - is_calculated_price_price_list: false, - calculated_amount: null, - is_original_price_price_list: false, - original_amount: null, - currency_code: null, - calculated_price: { - money_amount_id: null, - price_list_id: null, - price_list_type: null, - min_quantity: null, - max_quantity: null, - }, - original_price: { - money_amount_id: null, - price_list_id: null, - price_list_type: null, - min_quantity: null, - max_quantity: null, - }, - }, - { - id: "price-set-PLN", - is_calculated_price_price_list: true, - calculated_amount: 232, - is_original_price_price_list: false, - original_amount: 400, - currency_code: "PLN", - calculated_price: { - money_amount_id: expect.any(String), - price_list_id: expect.any(String), - price_list_type: "sale", - min_quantity: null, - max_quantity: null, - }, - original_price: { - money_amount_id: expect.any(String), - price_list_id: null, - price_list_type: null, - min_quantity: 1, - max_quantity: 5, - }, - }, - ]) - }) - - it("should return price list prices when price list dont have any rules", async () => { - await createPriceLists(service, {}, {}) - - const priceSetsResult = await service.calculatePrices( - { id: ["price-set-EUR", "price-set-PLN"] }, - { - context: { - currency_code: "PLN", - }, - } - ) - - expect(priceSetsResult).toEqual([ - { - id: "price-set-EUR", - is_calculated_price_price_list: false, - calculated_amount: null, - is_original_price_price_list: false, - original_amount: null, - currency_code: null, - calculated_price: { - money_amount_id: null, - price_list_id: null, - price_list_type: null, - min_quantity: null, - max_quantity: null, - }, - original_price: { - money_amount_id: null, - price_list_id: null, - price_list_type: null, - min_quantity: null, - max_quantity: null, - }, - }, - { - id: "price-set-PLN", - is_calculated_price_price_list: true, - calculated_amount: 232, - is_original_price_price_list: false, - original_amount: 1000, - currency_code: "PLN", - calculated_price: { - money_amount_id: expect.any(String), - price_list_id: expect.any(String), - price_list_type: "sale", - min_quantity: null, - max_quantity: null, - }, - original_price: { - money_amount_id: expect.any(String), - price_list_id: null, - price_list_type: null, + amount: 1000, min_quantity: 1, max_quantity: 10, }, - }, - ]) - }) - - it("should return price list prices when price list conditions match for override", async () => { - await createPriceLists(service, { type: PriceListType.OVERRIDE }) - - const priceSetsResult = await service.calculatePrices( - { id: ["price-set-EUR", "price-set-PLN"] }, - { - context: { - currency_code: "PLN", - region_id: "DE", - customer_group_id: "vip-customer-group-id", - company_id: "medusa-company-id", - }, - } - ) - - expect(priceSetsResult).toEqual([ - { - id: "price-set-EUR", - is_calculated_price_price_list: false, - calculated_amount: null, - is_original_price_price_list: false, - original_amount: null, - currency_code: null, - calculated_price: { - money_amount_id: null, - price_list_id: null, - price_list_type: null, - min_quantity: null, - max_quantity: null, - }, - original_price: { - money_amount_id: null, - price_list_id: null, - price_list_type: null, - min_quantity: null, - max_quantity: null, - }, - }, - { - id: "price-set-PLN", - is_calculated_price_price_list: true, - calculated_amount: 232, - is_original_price_price_list: true, - original_amount: 232, - currency_code: "PLN", - calculated_price: { - money_amount_id: expect.any(String), - price_list_id: expect.any(String), - price_list_type: "override", - min_quantity: null, - max_quantity: null, - }, - original_price: { - money_amount_id: expect.any(String), - price_list_id: expect.any(String), - price_list_type: "override", - min_quantity: null, - max_quantity: null, - }, - }, - ]) - }) - - it("should not return price list prices when price list conditions only partially match", async () => { - await createPriceLists(service) - const priceSetsResult = await service.calculatePrices( - { id: ["price-set-EUR", "price-set-PLN"] }, - { - context: { - currency_code: "PLN", - region_id: "PL", - customer_group_id: "vip-customer-group-id", - company_id: "does-not-exist", - }, - } - ) - - expect(priceSetsResult).toEqual([ - { - id: "price-set-EUR", - is_calculated_price_price_list: false, - calculated_amount: null, - is_original_price_price_list: false, - original_amount: null, - currency_code: null, - calculated_price: { - money_amount_id: null, - price_list_id: null, - price_list_type: null, - min_quantity: null, - max_quantity: null, - }, - original_price: { - money_amount_id: null, - price_list_id: null, - price_list_type: null, - min_quantity: null, - max_quantity: null, - }, - }, - { - id: "price-set-PLN", - is_calculated_price_price_list: false, - calculated_amount: 300, - is_original_price_price_list: false, - original_amount: 300, - currency_code: "PLN", - calculated_price: { - money_amount_id: "money-amount-region_id-PLN", - price_list_id: null, - price_list_type: null, - min_quantity: 1, - max_quantity: 4, - }, - original_price: { - money_amount_id: "money-amount-region_id-PLN", - price_list_id: null, - price_list_type: null, - min_quantity: 1, - max_quantity: 4, - }, - }, - ]) - }) - - it("should not return price list prices when price list conditions dont match", async () => { - await createPriceLists(service) - const priceSetsResult = await service.calculatePrices( - { id: ["price-set-EUR", "price-set-PLN"] }, - { - context: { - currency_code: "PLN", - region_id: "PL", - customer_group_id: "does-not-exist", - company_id: "does-not-exist", - }, - } - ) - - expect(priceSetsResult).toEqual([ - { - id: "price-set-EUR", - is_calculated_price_price_list: false, - calculated_amount: null, - is_original_price_price_list: false, - original_amount: null, - currency_code: null, - calculated_price: { - money_amount_id: null, - price_list_id: null, - price_list_type: null, - min_quantity: null, - max_quantity: null, - }, - original_price: { - money_amount_id: null, - price_list_id: null, - price_list_type: null, - min_quantity: null, - max_quantity: null, - }, - }, - { - id: "price-set-PLN", - is_calculated_price_price_list: false, - calculated_amount: 300, - is_original_price_price_list: false, - original_amount: 300, - currency_code: "PLN", - calculated_price: { - money_amount_id: expect.any(String), - price_list_id: null, - price_list_type: null, - min_quantity: 1, - max_quantity: 4, - }, - original_price: { - money_amount_id: expect.any(String), - price_list_id: null, - price_list_type: null, - min_quantity: 1, - max_quantity: 4, - }, - }, - ]) - }) - - it("should return price list prices for pricelist with valid pricing interval", async () => { - const yesterday = ((today) => - new Date(today.setDate(today.getDate() - 1)))(new Date()) - const tomorrow = ((today) => - new Date(today.setDate(today.getDate() + 1)))(new Date()) - - await createPriceLists( - service, - { - starts_at: yesterday, - ends_at: tomorrow, - }, - {} - ) - - const priceSetsResult = await service.calculatePrices( - { id: ["price-set-EUR", "price-set-PLN"] }, - { - context: { - currency_code: "PLN", - region_id: "DE", - customer_group_id: "vip-customer-group-id", - company_id: "medusa-company-id", - }, - } - ) - - expect(priceSetsResult).toEqual([ - { - id: "price-set-EUR", - is_calculated_price_price_list: false, - calculated_amount: null, - is_original_price_price_list: false, - original_amount: null, - currency_code: null, - calculated_price: { - money_amount_id: null, - price_list_id: null, - price_list_type: null, - min_quantity: null, - max_quantity: null, - }, - original_price: { - money_amount_id: null, - price_list_id: null, - price_list_type: null, - min_quantity: null, - max_quantity: null, - }, - }, - { - id: "price-set-PLN", - is_calculated_price_price_list: true, - calculated_amount: 232, - is_original_price_price_list: false, - original_amount: 400, - currency_code: "PLN", - calculated_price: { - money_amount_id: expect.any(String), - price_list_id: expect.any(String), - price_list_type: "sale", - min_quantity: null, - max_quantity: null, - }, - original_price: { - money_amount_id: expect.any(String), - price_list_id: null, - price_list_type: null, - min_quantity: 1, - max_quantity: 5, - }, - }, - ]) - }) - - it("should not return price list prices for upcoming pricelist", async () => { - const tomorrow = ((today) => - new Date(today.setDate(today.getDate() + 1)))(new Date()) - - const tenDaysFromToday = ((today) => - new Date(today.setDate(today.getDate() + 10)))(new Date()) - - await createPriceLists( - service, - { - starts_at: tomorrow, - ends_at: tenDaysFromToday, - }, - {} - ) - - const priceSetsResult = await service.calculatePrices( - { id: ["price-set-EUR", "price-set-PLN"] }, - { - context: { - currency_code: "PLN", - region_id: "DE", - customer_group_id: "vip-customer-group-id", - company_id: "medusa-company-id", - }, - } - ) - - expect(priceSetsResult).toEqual([ - { - id: "price-set-EUR", - is_calculated_price_price_list: false, - calculated_amount: null, - is_original_price_price_list: false, - original_amount: null, - currency_code: null, - calculated_price: { - money_amount_id: null, - price_list_id: null, - price_list_type: null, - min_quantity: null, - max_quantity: null, - }, - original_price: { - money_amount_id: null, - price_list_id: null, - price_list_type: null, - min_quantity: null, - max_quantity: null, - }, - }, - { - id: "price-set-PLN", - is_calculated_price_price_list: false, - calculated_amount: 400, - is_original_price_price_list: false, - original_amount: 400, - currency_code: "PLN", - calculated_price: { - money_amount_id: "money-amount-company_id-PLN", - price_list_id: null, - price_list_type: null, - min_quantity: 1, - max_quantity: 5, - }, - original_price: { - money_amount_id: "money-amount-company_id-PLN", - price_list_id: null, - price_list_type: null, - min_quantity: 1, - max_quantity: 5, - }, - }, - ]) - }) - - it("should not return price list prices for expired pricelist", async () => { - const yesterday = ((today) => - new Date(today.setDate(today.getDate() - 1)))(new Date()) - const tenDaysAgo = ((today) => - new Date(today.setDate(today.getDate() - 10)))(new Date()) - - await createPriceLists( - service, - { - starts_at: tenDaysAgo, - ends_at: yesterday, - }, - {} - ) - - const priceSetsResult = await service.calculatePrices( - { id: ["price-set-EUR", "price-set-PLN"] }, - { - context: { - currency_code: "PLN", - region_id: "DE", - customer_group_id: "vip-customer-group-id", - company_id: "medusa-company-id", - }, - } - ) - - expect(priceSetsResult).toEqual([ - { - id: "price-set-EUR", - is_calculated_price_price_list: false, - calculated_amount: null, - is_original_price_price_list: false, - original_amount: null, - currency_code: null, - calculated_price: { - money_amount_id: null, - price_list_id: null, - price_list_type: null, - min_quantity: null, - max_quantity: null, - }, - original_price: { - money_amount_id: null, - price_list_id: null, - price_list_type: null, - min_quantity: null, - max_quantity: null, - }, - }, - { - id: "price-set-PLN", - is_calculated_price_price_list: false, - calculated_amount: 400, - is_original_price_price_list: false, - original_amount: 400, - currency_code: "PLN", - calculated_price: { - money_amount_id: "money-amount-company_id-PLN", - price_list_id: null, - price_list_type: null, - min_quantity: 1, - max_quantity: 5, - }, - original_price: { - money_amount_id: "money-amount-company_id-PLN", - price_list_id: null, - price_list_type: null, - min_quantity: 1, - max_quantity: 5, - }, - }, - ]) - }) - - it("should return price list prices for price list with customer groupst", async () => { - const [{ id }] = await createPriceLists( - service, - {}, - { - customer_group_id: [ - "vip-customer-group-id", - "vip-customer-group-id-1", - ], - }, - [ { - amount: 200, + id: "money-amount-company_id-EUR", currency_code: "EUR", - price_set_id: "price-set-EUR", + amount: 500, + min_quantity: 1, + max_quantity: 10, + }, + { + id: "money-amount-company_id-PLN", + currency_code: "PLN", + amount: 400, + min_quantity: 1, + max_quantity: 5, + }, + { + id: "money-amount-region_id-PLN", + currency_code: "PLN", + amount: 300, + min_quantity: 1, + max_quantity: 4, + }, + { + id: "money-amount-region_id+company_id-PLN", + currency_code: "PLN", + amount: 999, + min_quantity: 1, + max_quantity: 10, + }, + { + id: "money-amount-region_id-PLN-5-qty", + currency_code: "PLN", + amount: 250, + min_quantity: 4, + max_quantity: 10, + }, + { + id: "money-amount-region_id-PL-EUR", + currency_code: "EUR", + amount: 200, + min_quantity: 1, + max_quantity: 3, + }, + { + id: "money-amount-region_id-PL-EUR-4-qty", + currency_code: "EUR", + amount: 50, + min_quantity: 4, + max_quantity: 10, + }, + { + id: "money-amount-region_id-PL-EUR-customer-group", + currency_code: "EUR", + amount: 100, + min_quantity: 1, + max_quantity: 3, }, ] - ) - const priceSetsResult = await service.calculatePrices( - { id: ["price-set-EUR"] }, - { - context: { + const priceSetsData = [ + { + id: "price-set-EUR", + }, + { + id: "price-set-PLN", + }, + ] as unknown as CreatePriceSetDTO[] + + const priceSetMoneyAmountsData = [ + { + id: "psma-PLN", + title: "psma PLN", + price_set: "price-set-PLN", + money_amount: "money-amount-PLN", + rules_count: 0, + }, + { + id: "psma-company_id-EUR", + title: "psma EUR - company_id", + price_set: "price-set-EUR", + money_amount: "money-amount-company_id-EUR", + rules_count: 1, + }, + { + id: "psma-company_id-PLN", + title: "psma PLN - company_id", + price_set: "price-set-PLN", + money_amount: "money-amount-company_id-PLN", + rules_count: 1, + }, + { + id: "psma-region_id-PLN", + title: "psma PLN - region_id", + price_set: "price-set-PLN", + money_amount: "money-amount-region_id-PLN", + rules_count: 1, + }, + { + id: "psma-region_id+company_id-PLN", + title: "psma region_id + company_id", + price_set: "price-set-PLN", + money_amount: "money-amount-region_id+company_id-PLN", + rules_count: 2, + }, + { + id: "psma-region_id-PLN-5-qty", + title: "psma PLN - region_id 5 qty", + price_set: "price-set-PLN", + money_amount: "money-amount-region_id-PLN-5-qty", + rules_count: 1, + }, + { + id: "psma-region_id_company_id-PL-EUR", + title: "psma PLN - region_id PL with EUR currency", + price_set: "price-set-PLN", + money_amount: "money-amount-region_id-PL-EUR", + rules_count: 2, + }, + { + id: "psma-region_id_company_id-PL-EUR-4-qty", + title: "psma PLN - region_id PL with EUR currency for quantity 4", + price_set: "price-set-PLN", + money_amount: "money-amount-region_id-PL-EUR-4-qty", + rules_count: 2, + }, + { + id: "psma-region_id_company_id-PL-EUR-customer-group", + title: + "psma PLN - region_id PL with EUR currency for customer group", + price_set: "price-set-PLN", + money_amount: "money-amount-region_id-PL-EUR-customer-group", + rules_count: 3, + }, + ] + + const ruleTypesData = [ + { + id: "rule-type-company_id", + name: "rule type company id", + rule_attribute: "company_id", + default_priority: 2, + }, + { + id: "rule-type-region_id", + name: "rule type region id", + rule_attribute: "region_id", + default_priority: 1, + }, + { + id: "rule-type-customer_group_id", + name: "rule type customer group id", + rule_attribute: "customer_group_id", + default_priority: 3, + }, + ] + + const priceRuleData = [ + { + id: "price-rule-company_id-EUR", + price_set_id: "price-set-EUR", + rule_type_id: "rule-type-company_id", + value: "EUR", + price_list_id: "test", + price_set_money_amount_id: "psma-company_id-EUR", + }, + { + id: "price-rule-company_id-PLN", + price_set_id: "price-set-PLN", + rule_type_id: "rule-type-company_id", + value: "medusa-company-id", + price_list_id: "test", + price_set_money_amount_id: "psma-company_id-PLN", + }, + { + id: "price-rule-region_id-PLN", + price_set_id: "price-set-PLN", + rule_type_id: "rule-type-region_id", + value: "PL", + price_list_id: "test", + price_set_money_amount_id: "psma-region_id-PLN", + }, + { + id: "price-rule-region_id+company_id-PL", + price_set_id: "price-set-PLN", + rule_type_id: "rule-type-region_id", + value: "PL", + price_list_id: "test", + price_set_money_amount_id: "psma-region_id+company_id-PLN", + }, + { + id: "price-rule-region_id+company_id-medusa-company-id", + price_set_id: "price-set-PLN", + rule_type_id: "rule-type-company_id", + value: "medusa-company-id", + price_list_id: "test", + price_set_money_amount_id: "psma-region_id+company_id-PLN", + }, + { + id: "price-rule-region_id-PLN-5-qty", + price_set_id: "price-set-PLN", + rule_type_id: "rule-type-region_id", + value: "PL", + price_list_id: "test", + price_set_money_amount_id: "psma-region_id-PLN-5-qty", + }, + { + id: "price-rule-region_id-company_id-PL", + price_set_id: "price-set-PLN", + rule_type_id: "rule-type-region_id", + value: "PL", + price_list_id: "test", + price_set_money_amount_id: "psma-region_id_company_id-PL-EUR", + }, + { + id: "price-rule-region_id-company_id-PLN", + price_set_id: "price-set-PLN", + rule_type_id: "rule-type-company_id", + value: "medusa-company-id", + price_list_id: "test", + price_set_money_amount_id: "psma-region_id_company_id-PL-EUR", + }, + { + id: "price-rule-region_id-company_id-PL-4-qty", + price_set_id: "price-set-PLN", + rule_type_id: "rule-type-region_id", + value: "PL", + price_list_id: "test", + price_set_money_amount_id: + "psma-region_id_company_id-PL-EUR-4-qty", + }, + { + id: "price-rule-region_id-company_id-PLN-4-qty", + price_set_id: "price-set-PLN", + rule_type_id: "rule-type-company_id", + value: "medusa-company-id", + price_list_id: "test", + price_set_money_amount_id: + "psma-region_id_company_id-PL-EUR-4-qty", + }, + { + id: "price-rule-region_id-currency_customer_group_code-PL", + price_set_id: "price-set-PLN", + rule_type_id: "rule-type-region_id", + value: "PL", + price_list_id: "test", + price_set_money_amount_id: + "psma-region_id_company_id-PL-EUR-customer-group", + }, + { + id: "price-rule-region_id-currency_customer_group_code-PLN", + price_set_id: "price-set-PLN", + rule_type_id: "rule-type-company_id", + value: "medusa-company-id", + price_list_id: "test", + price_set_money_amount_id: + "psma-region_id_company_id-PL-EUR-customer-group", + }, + { + id: "price-rule-region_id-currency_customer_group_code-test_customer_group", + price_set_id: "price-set-PLN", + rule_type_id: "rule-type-customer_group_id", + value: "test-customer-group", + price_list_id: "test", + price_set_money_amount_id: + "psma-region_id_company_id-PL-EUR-customer-group", + }, + ] as unknown as CreatePriceRuleDTO[] + + await seedPriceData(MikroOrmWrapper.forkManager(), { + moneyAmountsData, + priceSetsData, + priceSetMoneyAmountsData, + priceSetMoneyAmountRulesData: [], + priceRuleData, + ruleTypesData, + }) + }) + + it("should throw an error when currency code is not set", async () => { + let result = service.calculatePrices( + { id: ["price-set-EUR", "price-set-PLN"] }, + {} + ) + + expect(result).rejects.toThrow( + "Method calculatePrices requires currency_code in the pricing context" + ) + + result = service.calculatePrices( + { id: ["price-set-EUR", "price-set-PLN"] }, + { context: { region_id: "DE" } } + ) + + expect(result).rejects.toThrow( + "Method calculatePrices requires currency_code in the pricing context" + ) + }) + + it("should return filled prices when 1 context is present and price is setup for PLN", async () => { + const priceSetsResult = await service.calculatePrices( + { id: ["price-set-EUR", "price-set-PLN"] }, + { + context: { currency_code: "PLN" }, + } + ) + + expect(priceSetsResult).toEqual([ + { + id: "price-set-EUR", + is_calculated_price_price_list: false, + calculated_amount: null, + is_original_price_price_list: false, + original_amount: null, + currency_code: null, + calculated_price: { + money_amount_id: null, + price_list_id: null, + price_list_type: null, + min_quantity: null, + max_quantity: null, + }, + original_price: { + money_amount_id: null, + price_list_id: null, + price_list_type: null, + min_quantity: null, + max_quantity: null, + }, + }, + { + id: "price-set-PLN", + is_calculated_price_price_list: false, + calculated_amount: 1000, + is_original_price_price_list: false, + original_amount: 1000, + currency_code: "PLN", + calculated_price: { + money_amount_id: "money-amount-PLN", + price_list_id: null, + price_list_type: null, + min_quantity: 1, + max_quantity: 10, + }, + original_price: { + money_amount_id: "money-amount-PLN", + price_list_id: null, + price_list_type: null, + min_quantity: 1, + max_quantity: 10, + }, + }, + ]) + }) + + it("should return filled prices when 1 context is present and price is setup for PLN region_id", async () => { + const priceSetsResult = await service.calculatePrices( + { id: ["price-set-EUR", "price-set-PLN"] }, + { + context: { currency_code: "PLN", region_id: "PL" }, + } + ) + + expect(priceSetsResult).toEqual([ + { + id: "price-set-EUR", + is_calculated_price_price_list: false, + calculated_amount: null, + is_original_price_price_list: false, + original_amount: null, + currency_code: null, + calculated_price: { + money_amount_id: null, + price_list_id: null, + price_list_type: null, + min_quantity: null, + max_quantity: null, + }, + original_price: { + money_amount_id: null, + price_list_id: null, + price_list_type: null, + min_quantity: null, + max_quantity: null, + }, + }, + { + id: "price-set-PLN", + is_calculated_price_price_list: false, + calculated_amount: 300, + is_original_price_price_list: false, + original_amount: 300, + currency_code: "PLN", + calculated_price: { + money_amount_id: "money-amount-region_id-PLN", + price_list_id: null, + price_list_type: null, + min_quantity: 1, + max_quantity: 4, + }, + original_price: { + money_amount_id: "money-amount-region_id-PLN", + price_list_id: null, + price_list_type: null, + min_quantity: 1, + max_quantity: 4, + }, + }, + ]) + }) + + it("should return filled prices when 1 context is present and price is setup for PLN currency_code", async () => { + const priceSetsResult = await service.calculatePrices( + { id: ["price-set-EUR", "price-set-PLN"] }, + { + context: { currency_code: "PLN" }, + } + ) + + expect(priceSetsResult).toEqual([ + { + id: "price-set-EUR", + is_calculated_price_price_list: false, + calculated_amount: null, + is_original_price_price_list: false, + original_amount: null, + currency_code: null, + calculated_price: { + money_amount_id: null, + price_list_id: null, + price_list_type: null, + min_quantity: null, + max_quantity: null, + }, + original_price: { + money_amount_id: null, + price_list_id: null, + price_list_type: null, + min_quantity: null, + max_quantity: null, + }, + }, + { + id: "price-set-PLN", + is_calculated_price_price_list: false, + calculated_amount: 1000, + is_original_price_price_list: false, + original_amount: 1000, + currency_code: "PLN", + calculated_price: { + money_amount_id: "money-amount-PLN", + price_list_id: null, + price_list_type: null, + min_quantity: 1, + max_quantity: 10, + }, + original_price: { + money_amount_id: "money-amount-PLN", + price_list_id: null, + price_list_type: null, + min_quantity: 1, + max_quantity: 10, + }, + }, + ]) + }) + + it("should return null prices when 1 context is present and price is NOT setup", async () => { + const priceSetsResult = await service.calculatePrices( + { id: ["price-set-EUR", "price-set-PLN"] }, + { + context: { currency_code: "EUR", does_not_exist: "EUR" }, + } + ) + + expect(priceSetsResult).toEqual([ + { + id: "price-set-EUR", + is_calculated_price_price_list: false, + calculated_amount: null, + is_original_price_price_list: false, + original_amount: null, + currency_code: null, + calculated_price: { + money_amount_id: null, + price_list_id: null, + price_list_type: null, + min_quantity: null, + max_quantity: null, + }, + original_price: { + money_amount_id: null, + price_list_id: null, + price_list_type: null, + min_quantity: null, + max_quantity: null, + }, + }, + { + id: "price-set-PLN", + is_calculated_price_price_list: false, + calculated_amount: null, + is_original_price_price_list: false, + original_amount: null, + currency_code: null, + calculated_price: { + money_amount_id: null, + price_list_id: null, + price_list_type: null, + min_quantity: null, + max_quantity: null, + }, + original_price: { + money_amount_id: null, + price_list_id: null, + price_list_type: null, + min_quantity: null, + max_quantity: null, + }, + }, + ]) + }) + + it("should return filled prices when 2 contexts are present and price is setup", async () => { + const priceSetsResult = await service.calculatePrices( + { id: ["price-set-EUR", "price-set-PLN"] }, + { + context: { currency_code: "PLN", region_id: "PL" }, + } + ) + + expect(priceSetsResult).toEqual([ + { + id: "price-set-EUR", + is_calculated_price_price_list: false, + calculated_amount: null, + is_original_price_price_list: false, + original_amount: null, + currency_code: null, + calculated_price: { + money_amount_id: null, + price_list_id: null, + price_list_type: null, + min_quantity: null, + max_quantity: null, + }, + original_price: { + money_amount_id: null, + price_list_id: null, + price_list_type: null, + min_quantity: null, + max_quantity: null, + }, + }, + { + id: "price-set-PLN", + is_calculated_price_price_list: false, + calculated_amount: 300, + is_original_price_price_list: false, + original_amount: 300, + currency_code: "PLN", + calculated_price: { + money_amount_id: "money-amount-region_id-PLN", + price_list_id: null, + price_list_type: null, + min_quantity: 1, + max_quantity: 4, + }, + original_price: { + money_amount_id: "money-amount-region_id-PLN", + price_list_id: null, + price_list_type: null, + min_quantity: 1, + max_quantity: 4, + }, + }, + ]) + }) + + it("should return filled prices when 2 contexts are present and price is not setup", async () => { + const priceSetsResult = await service.calculatePrices( + { id: ["price-set-EUR", "price-set-PLN"] }, + { + context: { currency_code: "PLN", company_id: "doesnotexist" }, + } + ) + + expect(priceSetsResult).toEqual([ + { + id: "price-set-EUR", + is_calculated_price_price_list: false, + calculated_amount: null, + is_original_price_price_list: false, + original_amount: null, + currency_code: null, + calculated_price: { + money_amount_id: null, + price_list_id: null, + price_list_type: null, + min_quantity: null, + max_quantity: null, + }, + original_price: { + money_amount_id: null, + price_list_id: null, + price_list_type: null, + min_quantity: null, + max_quantity: null, + }, + }, + { + id: "price-set-PLN", + is_calculated_price_price_list: false, + calculated_amount: 1000, + is_original_price_price_list: false, + original_amount: 1000, + currency_code: "PLN", + calculated_price: { + money_amount_id: "money-amount-PLN", + price_list_id: null, + price_list_type: null, + min_quantity: 1, + max_quantity: 10, + }, + original_price: { + money_amount_id: "money-amount-PLN", + price_list_id: null, + price_list_type: null, + min_quantity: 1, + max_quantity: 10, + }, + }, + ]) + }) + + it("should return filled prices when 2 contexts are present and price is setup along with declaring quantity", async () => { + const priceSetsResult = await service.calculatePrices( + { id: ["price-set-PLN"] }, + { + context: { currency_code: "PLN", region_id: "PL", quantity: 5 }, + } + ) + + expect(priceSetsResult).toEqual([ + { + id: "price-set-PLN", + is_calculated_price_price_list: false, + calculated_amount: 250, + is_original_price_price_list: false, + original_amount: 250, + currency_code: "PLN", + calculated_price: { + money_amount_id: "money-amount-region_id-PLN-5-qty", + price_list_id: null, + price_list_type: null, + min_quantity: 4, + max_quantity: 10, + }, + original_price: { + money_amount_id: "money-amount-region_id-PLN-5-qty", + price_list_id: null, + price_list_type: null, + min_quantity: 4, + max_quantity: 10, + }, + }, + ]) + }) + + it("should return filled prices when 3 contexts are present and price is partially setup for 2", async () => { + const priceSetsResult = await service.calculatePrices( + { id: ["price-set-EUR", "price-set-PLN"] }, + { + context: { + currency_code: "PLN", + region_id: "PL", + customer_group_id: "test-customer-group", + company_id: "does-not-exist", + }, + } + ) + + expect(priceSetsResult).toEqual([ + { + id: "price-set-EUR", + is_calculated_price_price_list: false, + calculated_amount: null, + is_original_price_price_list: false, + original_amount: null, + currency_code: null, + calculated_price: { + money_amount_id: null, + price_list_id: null, + price_list_type: null, + min_quantity: null, + max_quantity: null, + }, + original_price: { + money_amount_id: null, + price_list_id: null, + price_list_type: null, + min_quantity: null, + max_quantity: null, + }, + }, + { + id: "price-set-PLN", + is_calculated_price_price_list: false, + calculated_amount: 300, + is_original_price_price_list: false, + original_amount: 300, + currency_code: "PLN", + calculated_price: { + money_amount_id: "money-amount-region_id-PLN", + price_list_id: null, + price_list_type: null, + min_quantity: 1, + max_quantity: 4, + }, + original_price: { + money_amount_id: "money-amount-region_id-PLN", + price_list_id: null, + price_list_type: null, + min_quantity: 1, + max_quantity: 4, + }, + }, + ]) + }) + + it("should return filled prices when 3 contexts are present and price is setup for 3", async () => { + const priceSetsResult = await service.calculatePrices( + { id: ["price-set-EUR", "price-set-PLN"] }, + { + context: { + currency_code: "EUR", + region_id: "PL", + customer_group_id: "test-customer-group", + company_id: "medusa-company-id", + }, + } + ) + + expect(priceSetsResult).toEqual([ + { + id: "price-set-EUR", + is_calculated_price_price_list: false, + calculated_amount: null, + is_original_price_price_list: false, + original_amount: null, + currency_code: null, + calculated_price: { + money_amount_id: null, + price_list_id: null, + price_list_type: null, + min_quantity: null, + max_quantity: null, + }, + original_price: { + money_amount_id: null, + price_list_id: null, + price_list_type: null, + min_quantity: null, + max_quantity: null, + }, + }, + // Currency Code + Region value + customer group id + { + id: "price-set-PLN", + is_calculated_price_price_list: false, + calculated_amount: 100, + is_original_price_price_list: false, + original_amount: 100, currency_code: "EUR", - customer_group_id: "vip-customer-group-id", + calculated_price: { + money_amount_id: "money-amount-region_id-PL-EUR-customer-group", + price_list_id: null, + price_list_type: null, + min_quantity: 1, + max_quantity: 3, + }, + original_price: { + money_amount_id: "money-amount-region_id-PL-EUR-customer-group", + price_list_id: null, + price_list_type: null, + min_quantity: 1, + max_quantity: 3, + }, }, - } - ) + ]) + }) - expect(priceSetsResult).toEqual([ - { - id: "price-set-EUR", - is_calculated_price_price_list: true, - calculated_amount: 200, - is_original_price_price_list: false, - original_amount: null, - currency_code: "EUR", - calculated_price: { - money_amount_id: expect.any(String), - price_list_id: id, - price_list_type: "sale", - min_quantity: null, - max_quantity: null, - }, - original_price: { - money_amount_id: null, - price_list_id: null, - price_list_type: null, - min_quantity: null, - max_quantity: null, - }, - }, - ]) - }) + it("should return filled prices when 3 contexts are present and price is setup for 3, but value is incorrect for 2. It falls back to 1 rule context when 1 rule is not setup", async () => { + const priceSetsResult = await service.calculatePrices( + { id: ["price-set-EUR", "price-set-PLN"] }, + { + context: { + currency_code: "PLN", + region_id: "PL", + customer_group_id: "does-not-exist", + company_id: "does-not-exist", + }, + } + ) - it("should return price list prices when price list conditions match within prices", async () => { - await createPriceLists(service, {}, { region_id: ["DE", "PL"] }, [ - ...defaultPriceListPrices, - { - amount: 111, - currency_code: "PLN", - price_set_id: "price-set-PLN", - rules: { - region_id: "DE", + expect(priceSetsResult).toEqual([ + { + id: "price-set-EUR", + is_calculated_price_price_list: false, + calculated_amount: null, + is_original_price_price_list: false, + original_amount: null, + currency_code: null, + calculated_price: { + money_amount_id: null, + price_list_id: null, + price_list_type: null, + min_quantity: null, + max_quantity: null, + }, + original_price: { + money_amount_id: null, + price_list_id: null, + price_list_type: null, + min_quantity: null, + max_quantity: null, + }, }, - }, - ]) - - const priceSetsResult = await service.calculatePrices( - { id: ["price-set-EUR", "price-set-PLN"] }, - { - context: { + // PLN price set is not setup for EUR currency_code so it will default to a null set + { + id: "price-set-PLN", + is_calculated_price_price_list: false, + calculated_amount: 300, + is_original_price_price_list: false, + original_amount: 300, currency_code: "PLN", - region_id: "DE", - customer_group_id: "vip-customer-group-id", - company_id: "medusa-company-id", + calculated_price: { + money_amount_id: "money-amount-region_id-PLN", + price_list_id: null, + price_list_type: null, + min_quantity: 1, + max_quantity: 4, + }, + original_price: { + money_amount_id: "money-amount-region_id-PLN", + price_list_id: null, + price_list_type: null, + min_quantity: 1, + max_quantity: 4, + }, }, - } - ) + ]) + }) - expect(priceSetsResult).toEqual([ - { - id: "price-set-EUR", - is_calculated_price_price_list: false, - calculated_amount: null, - is_original_price_price_list: false, - original_amount: null, - currency_code: null, - calculated_price: { - money_amount_id: null, - price_list_id: null, - price_list_type: null, - min_quantity: null, - max_quantity: null, - }, - original_price: { - money_amount_id: null, - price_list_id: null, - price_list_type: null, - min_quantity: null, - max_quantity: null, - }, - }, - { - id: "price-set-PLN", - is_calculated_price_price_list: true, - calculated_amount: 111, - is_original_price_price_list: false, - original_amount: 400, - currency_code: "PLN", - calculated_price: { - money_amount_id: expect.any(String), - price_list_id: expect.any(String), - price_list_type: "sale", - min_quantity: null, - max_quantity: null, - }, - original_price: { - money_amount_id: expect.any(String), - price_list_id: null, - price_list_type: null, - min_quantity: 1, - max_quantity: 5, - }, - }, - ]) - }) + it("should return filled prices when 3 contexts are present and price is setup for 3, but value is incorrect for 2. It falls back to default value", async () => { + const priceSetsResult = await service.calculatePrices( + { id: ["price-set-EUR", "price-set-PLN"] }, + { + context: { + currency_code: "PLN", + region_id: "does-not-exist", + customer_group_id: "does-not-exist", + company_id: "does-not-exist", + }, + } + ) - it("should not return price list prices when price list conditions are met but price rules are not", async () => { - await createPriceLists(service, {}, { region_id: ["DE", "PL"] }, [ - ...defaultPriceListPrices, - { - amount: 111, - currency_code: "PLN", - price_set_id: "price-set-PLN", - rules: { - region_id: "PL", + expect(priceSetsResult).toEqual([ + { + id: "price-set-EUR", + is_calculated_price_price_list: false, + calculated_amount: null, + is_original_price_price_list: false, + original_amount: null, + currency_code: null, + calculated_price: { + money_amount_id: null, + price_list_id: null, + price_list_type: null, + min_quantity: null, + max_quantity: null, + }, + original_price: { + money_amount_id: null, + price_list_id: null, + price_list_type: null, + min_quantity: null, + max_quantity: null, + }, }, - }, - ]) - - const priceSetsResult = await service.calculatePrices( - { id: ["price-set-EUR", "price-set-PLN"] }, - { - context: { + // PLN price set is not setup for EUR currency_code so it will default to a null set + { + id: "price-set-PLN", + is_calculated_price_price_list: false, + calculated_amount: 1000, + is_original_price_price_list: false, + original_amount: 1000, currency_code: "PLN", - region_id: "DE", - customer_group_id: "vip-customer-group-id", - company_id: "medusa-company-id", + calculated_price: { + money_amount_id: "money-amount-PLN", + price_list_id: null, + price_list_type: null, + min_quantity: 1, + max_quantity: 10, + }, + original_price: { + money_amount_id: "money-amount-PLN", + price_list_id: null, + price_list_type: null, + min_quantity: 1, + max_quantity: 10, + }, }, - } - ) + ]) + }) - expect(priceSetsResult).toEqual([ - { - id: "price-set-EUR", - is_calculated_price_price_list: false, - calculated_amount: null, - is_original_price_price_list: false, - original_amount: null, - currency_code: null, - calculated_price: { - money_amount_id: null, - price_list_id: null, - price_list_type: null, - min_quantity: null, - max_quantity: null, + it("should return null prices when 2 context is present and prices are NOT setup", async () => { + const priceSetsResult = await service.calculatePrices( + { id: ["price-set-EUR", "price-set-PLN"] }, + { + context: { currency_code: "EUR", does_not_exist_2: "Berlin" }, + } + ) + + expect(priceSetsResult).toEqual([ + { + id: "price-set-EUR", + is_calculated_price_price_list: false, + calculated_amount: null, + is_original_price_price_list: false, + original_amount: null, + currency_code: null, + calculated_price: { + money_amount_id: null, + price_list_id: null, + price_list_type: null, + min_quantity: null, + max_quantity: null, + }, + original_price: { + money_amount_id: null, + price_list_id: null, + price_list_type: null, + min_quantity: null, + max_quantity: null, + }, }, - original_price: { - money_amount_id: null, - price_list_id: null, - price_list_type: null, - min_quantity: null, - max_quantity: null, + { + id: "price-set-PLN", + is_calculated_price_price_list: false, + calculated_amount: null, + is_original_price_price_list: false, + original_amount: null, + currency_code: null, + calculated_price: { + money_amount_id: null, + price_list_id: null, + price_list_type: null, + min_quantity: null, + max_quantity: null, + }, + original_price: { + money_amount_id: null, + price_list_id: null, + price_list_type: null, + min_quantity: null, + max_quantity: null, + }, }, - }, - { - id: "price-set-PLN", - is_calculated_price_price_list: true, - calculated_amount: 232, - is_original_price_price_list: false, - original_amount: 400, - currency_code: "PLN", - calculated_price: { - money_amount_id: expect.any(String), - price_list_id: expect.any(String), - price_list_type: "sale", - min_quantity: null, - max_quantity: null, + ]) + }) + + it("should return filled prices when 2 context is present and prices are setup, but only for one of the contexts", async () => { + const priceSetsResult = await service.calculatePrices( + { id: ["price-set-EUR", "price-set-PLN"] }, + { + context: { + currency_code: "PLN", + region_id: "PL", + city: "Berlin", + }, + } + ) + + expect(priceSetsResult).toEqual([ + { + id: "price-set-EUR", + is_calculated_price_price_list: false, + calculated_amount: null, + is_original_price_price_list: false, + original_amount: null, + currency_code: null, + calculated_price: { + money_amount_id: null, + price_list_id: null, + price_list_type: null, + min_quantity: null, + max_quantity: null, + }, + original_price: { + money_amount_id: null, + price_list_id: null, + price_list_type: null, + min_quantity: null, + max_quantity: null, + }, }, - original_price: { - money_amount_id: expect.any(String), - price_list_id: null, - price_list_type: null, - min_quantity: 1, - max_quantity: 5, + { + id: "price-set-PLN", + is_calculated_price_price_list: false, + calculated_amount: 300, + is_original_price_price_list: false, + original_amount: 300, + currency_code: "PLN", + calculated_price: { + money_amount_id: "money-amount-region_id-PLN", + price_list_id: null, + price_list_type: null, + min_quantity: 1, + max_quantity: 4, + }, + original_price: { + money_amount_id: "money-amount-region_id-PLN", + price_list_id: null, + price_list_type: null, + min_quantity: 1, + max_quantity: 4, + }, }, - }, - ]) + ]) + }) + + describe("Price Lists", () => { + it("should return price list prices when price list conditions match", async () => { + await createPriceLists(service) + + const priceSetsResult = await service.calculatePrices( + { id: ["price-set-EUR", "price-set-PLN"] }, + { + context: { + currency_code: "PLN", + region_id: "DE", + customer_group_id: "vip-customer-group-id", + company_id: "medusa-company-id", + }, + } + ) + + expect(priceSetsResult).toEqual([ + { + id: "price-set-EUR", + is_calculated_price_price_list: false, + calculated_amount: null, + is_original_price_price_list: false, + original_amount: null, + currency_code: null, + calculated_price: { + money_amount_id: null, + price_list_id: null, + price_list_type: null, + min_quantity: null, + max_quantity: null, + }, + original_price: { + money_amount_id: null, + price_list_id: null, + price_list_type: null, + min_quantity: null, + max_quantity: null, + }, + }, + { + id: "price-set-PLN", + is_calculated_price_price_list: true, + calculated_amount: 232, + is_original_price_price_list: false, + original_amount: 400, + currency_code: "PLN", + calculated_price: { + money_amount_id: expect.any(String), + price_list_id: expect.any(String), + price_list_type: "sale", + min_quantity: null, + max_quantity: null, + }, + original_price: { + money_amount_id: expect.any(String), + price_list_id: null, + price_list_type: null, + min_quantity: 1, + max_quantity: 5, + }, + }, + ]) + }) + + it("should return best price list price first when price list conditions match", async () => { + await createPriceLists(service) + await createPriceLists( + service, + {}, + {}, + defaultPriceListPrices.map((price) => { + return { ...price, amount: price.amount / 2 } + }) + ) + + const priceSetsResult = await service.calculatePrices( + { id: ["price-set-EUR", "price-set-PLN"] }, + { + context: { + currency_code: "PLN", + region_id: "DE", + customer_group_id: "vip-customer-group-id", + company_id: "medusa-company-id", + }, + } + ) + + expect(priceSetsResult).toEqual([ + { + id: "price-set-EUR", + is_calculated_price_price_list: false, + calculated_amount: null, + is_original_price_price_list: false, + original_amount: null, + currency_code: null, + calculated_price: { + money_amount_id: null, + price_list_id: null, + price_list_type: null, + min_quantity: null, + max_quantity: null, + }, + original_price: { + money_amount_id: null, + price_list_id: null, + price_list_type: null, + min_quantity: null, + max_quantity: null, + }, + }, + { + id: "price-set-PLN", + is_calculated_price_price_list: true, + calculated_amount: 116, + is_original_price_price_list: false, + original_amount: 400, + currency_code: "PLN", + calculated_price: { + money_amount_id: expect.any(String), + price_list_id: expect.any(String), + price_list_type: "sale", + min_quantity: null, + max_quantity: null, + }, + original_price: { + money_amount_id: expect.any(String), + price_list_id: null, + price_list_type: null, + min_quantity: 1, + max_quantity: 5, + }, + }, + ]) + }) + + it("should return price list prices when price list dont have rules, but context is loaded", async () => { + await createPriceLists(service, {}, {}) + + const priceSetsResult = await service.calculatePrices( + { id: ["price-set-EUR", "price-set-PLN"] }, + { + context: { + currency_code: "PLN", + region_id: "DE", + customer_group_id: "vip-customer-group-id", + company_id: "medusa-company-id", + }, + } + ) + + expect(priceSetsResult).toEqual([ + { + id: "price-set-EUR", + is_calculated_price_price_list: false, + calculated_amount: null, + is_original_price_price_list: false, + original_amount: null, + currency_code: null, + calculated_price: { + money_amount_id: null, + price_list_id: null, + price_list_type: null, + min_quantity: null, + max_quantity: null, + }, + original_price: { + money_amount_id: null, + price_list_id: null, + price_list_type: null, + min_quantity: null, + max_quantity: null, + }, + }, + { + id: "price-set-PLN", + is_calculated_price_price_list: true, + calculated_amount: 232, + is_original_price_price_list: false, + original_amount: 400, + currency_code: "PLN", + calculated_price: { + money_amount_id: expect.any(String), + price_list_id: expect.any(String), + price_list_type: "sale", + min_quantity: null, + max_quantity: null, + }, + original_price: { + money_amount_id: expect.any(String), + price_list_id: null, + price_list_type: null, + min_quantity: 1, + max_quantity: 5, + }, + }, + ]) + }) + + it("should return price list prices when price list dont have any rules", async () => { + await createPriceLists(service, {}, {}) + + const priceSetsResult = await service.calculatePrices( + { id: ["price-set-EUR", "price-set-PLN"] }, + { + context: { + currency_code: "PLN", + }, + } + ) + + expect(priceSetsResult).toEqual([ + { + id: "price-set-EUR", + is_calculated_price_price_list: false, + calculated_amount: null, + is_original_price_price_list: false, + original_amount: null, + currency_code: null, + calculated_price: { + money_amount_id: null, + price_list_id: null, + price_list_type: null, + min_quantity: null, + max_quantity: null, + }, + original_price: { + money_amount_id: null, + price_list_id: null, + price_list_type: null, + min_quantity: null, + max_quantity: null, + }, + }, + { + id: "price-set-PLN", + is_calculated_price_price_list: true, + calculated_amount: 232, + is_original_price_price_list: false, + original_amount: 1000, + currency_code: "PLN", + calculated_price: { + money_amount_id: expect.any(String), + price_list_id: expect.any(String), + price_list_type: "sale", + min_quantity: null, + max_quantity: null, + }, + original_price: { + money_amount_id: expect.any(String), + price_list_id: null, + price_list_type: null, + min_quantity: 1, + max_quantity: 10, + }, + }, + ]) + }) + + it("should return price list prices when price list conditions match for override", async () => { + await createPriceLists(service, { type: PriceListType.OVERRIDE }) + + const priceSetsResult = await service.calculatePrices( + { id: ["price-set-EUR", "price-set-PLN"] }, + { + context: { + currency_code: "PLN", + region_id: "DE", + customer_group_id: "vip-customer-group-id", + company_id: "medusa-company-id", + }, + } + ) + + expect(priceSetsResult).toEqual([ + { + id: "price-set-EUR", + is_calculated_price_price_list: false, + calculated_amount: null, + is_original_price_price_list: false, + original_amount: null, + currency_code: null, + calculated_price: { + money_amount_id: null, + price_list_id: null, + price_list_type: null, + min_quantity: null, + max_quantity: null, + }, + original_price: { + money_amount_id: null, + price_list_id: null, + price_list_type: null, + min_quantity: null, + max_quantity: null, + }, + }, + { + id: "price-set-PLN", + is_calculated_price_price_list: true, + calculated_amount: 232, + is_original_price_price_list: true, + original_amount: 232, + currency_code: "PLN", + calculated_price: { + money_amount_id: expect.any(String), + price_list_id: expect.any(String), + price_list_type: "override", + min_quantity: null, + max_quantity: null, + }, + original_price: { + money_amount_id: expect.any(String), + price_list_id: expect.any(String), + price_list_type: "override", + min_quantity: null, + max_quantity: null, + }, + }, + ]) + }) + + it("should not return price list prices when price list conditions only partially match", async () => { + await createPriceLists(service) + const priceSetsResult = await service.calculatePrices( + { id: ["price-set-EUR", "price-set-PLN"] }, + { + context: { + currency_code: "PLN", + region_id: "PL", + customer_group_id: "vip-customer-group-id", + company_id: "does-not-exist", + }, + } + ) + + expect(priceSetsResult).toEqual([ + { + id: "price-set-EUR", + is_calculated_price_price_list: false, + calculated_amount: null, + is_original_price_price_list: false, + original_amount: null, + currency_code: null, + calculated_price: { + money_amount_id: null, + price_list_id: null, + price_list_type: null, + min_quantity: null, + max_quantity: null, + }, + original_price: { + money_amount_id: null, + price_list_id: null, + price_list_type: null, + min_quantity: null, + max_quantity: null, + }, + }, + { + id: "price-set-PLN", + is_calculated_price_price_list: false, + calculated_amount: 300, + is_original_price_price_list: false, + original_amount: 300, + currency_code: "PLN", + calculated_price: { + money_amount_id: "money-amount-region_id-PLN", + price_list_id: null, + price_list_type: null, + min_quantity: 1, + max_quantity: 4, + }, + original_price: { + money_amount_id: "money-amount-region_id-PLN", + price_list_id: null, + price_list_type: null, + min_quantity: 1, + max_quantity: 4, + }, + }, + ]) + }) + + it("should not return price list prices when price list conditions dont match", async () => { + await createPriceLists(service) + const priceSetsResult = await service.calculatePrices( + { id: ["price-set-EUR", "price-set-PLN"] }, + { + context: { + currency_code: "PLN", + region_id: "PL", + customer_group_id: "does-not-exist", + company_id: "does-not-exist", + }, + } + ) + + expect(priceSetsResult).toEqual([ + { + id: "price-set-EUR", + is_calculated_price_price_list: false, + calculated_amount: null, + is_original_price_price_list: false, + original_amount: null, + currency_code: null, + calculated_price: { + money_amount_id: null, + price_list_id: null, + price_list_type: null, + min_quantity: null, + max_quantity: null, + }, + original_price: { + money_amount_id: null, + price_list_id: null, + price_list_type: null, + min_quantity: null, + max_quantity: null, + }, + }, + { + id: "price-set-PLN", + is_calculated_price_price_list: false, + calculated_amount: 300, + is_original_price_price_list: false, + original_amount: 300, + currency_code: "PLN", + calculated_price: { + money_amount_id: expect.any(String), + price_list_id: null, + price_list_type: null, + min_quantity: 1, + max_quantity: 4, + }, + original_price: { + money_amount_id: expect.any(String), + price_list_id: null, + price_list_type: null, + min_quantity: 1, + max_quantity: 4, + }, + }, + ]) + }) + + it("should return price list prices for pricelist with valid pricing interval", async () => { + const yesterday = ((today) => + new Date(today.setDate(today.getDate() - 1)))(new Date()) + const tomorrow = ((today) => + new Date(today.setDate(today.getDate() + 1)))(new Date()) + + await createPriceLists( + service, + { + starts_at: yesterday, + ends_at: tomorrow, + }, + {} + ) + + const priceSetsResult = await service.calculatePrices( + { id: ["price-set-EUR", "price-set-PLN"] }, + { + context: { + currency_code: "PLN", + region_id: "DE", + customer_group_id: "vip-customer-group-id", + company_id: "medusa-company-id", + }, + } + ) + + expect(priceSetsResult).toEqual([ + { + id: "price-set-EUR", + is_calculated_price_price_list: false, + calculated_amount: null, + is_original_price_price_list: false, + original_amount: null, + currency_code: null, + calculated_price: { + money_amount_id: null, + price_list_id: null, + price_list_type: null, + min_quantity: null, + max_quantity: null, + }, + original_price: { + money_amount_id: null, + price_list_id: null, + price_list_type: null, + min_quantity: null, + max_quantity: null, + }, + }, + { + id: "price-set-PLN", + is_calculated_price_price_list: true, + calculated_amount: 232, + is_original_price_price_list: false, + original_amount: 400, + currency_code: "PLN", + calculated_price: { + money_amount_id: expect.any(String), + price_list_id: expect.any(String), + price_list_type: "sale", + min_quantity: null, + max_quantity: null, + }, + original_price: { + money_amount_id: expect.any(String), + price_list_id: null, + price_list_type: null, + min_quantity: 1, + max_quantity: 5, + }, + }, + ]) + }) + + it("should not return price list prices for upcoming pricelist", async () => { + const tomorrow = ((today) => + new Date(today.setDate(today.getDate() + 1)))(new Date()) + + const tenDaysFromToday = ((today) => + new Date(today.setDate(today.getDate() + 10)))(new Date()) + + await createPriceLists( + service, + { + starts_at: tomorrow, + ends_at: tenDaysFromToday, + }, + {} + ) + + const priceSetsResult = await service.calculatePrices( + { id: ["price-set-EUR", "price-set-PLN"] }, + { + context: { + currency_code: "PLN", + region_id: "DE", + customer_group_id: "vip-customer-group-id", + company_id: "medusa-company-id", + }, + } + ) + + expect(priceSetsResult).toEqual([ + { + id: "price-set-EUR", + is_calculated_price_price_list: false, + calculated_amount: null, + is_original_price_price_list: false, + original_amount: null, + currency_code: null, + calculated_price: { + money_amount_id: null, + price_list_id: null, + price_list_type: null, + min_quantity: null, + max_quantity: null, + }, + original_price: { + money_amount_id: null, + price_list_id: null, + price_list_type: null, + min_quantity: null, + max_quantity: null, + }, + }, + { + id: "price-set-PLN", + is_calculated_price_price_list: false, + calculated_amount: 400, + is_original_price_price_list: false, + original_amount: 400, + currency_code: "PLN", + calculated_price: { + money_amount_id: "money-amount-company_id-PLN", + price_list_id: null, + price_list_type: null, + min_quantity: 1, + max_quantity: 5, + }, + original_price: { + money_amount_id: "money-amount-company_id-PLN", + price_list_id: null, + price_list_type: null, + min_quantity: 1, + max_quantity: 5, + }, + }, + ]) + }) + + it("should not return price list prices for expired pricelist", async () => { + const yesterday = ((today) => + new Date(today.setDate(today.getDate() - 1)))(new Date()) + const tenDaysAgo = ((today) => + new Date(today.setDate(today.getDate() - 10)))(new Date()) + + await createPriceLists( + service, + { + starts_at: tenDaysAgo, + ends_at: yesterday, + }, + {} + ) + + const priceSetsResult = await service.calculatePrices( + { id: ["price-set-EUR", "price-set-PLN"] }, + { + context: { + currency_code: "PLN", + region_id: "DE", + customer_group_id: "vip-customer-group-id", + company_id: "medusa-company-id", + }, + } + ) + + expect(priceSetsResult).toEqual([ + { + id: "price-set-EUR", + is_calculated_price_price_list: false, + calculated_amount: null, + is_original_price_price_list: false, + original_amount: null, + currency_code: null, + calculated_price: { + money_amount_id: null, + price_list_id: null, + price_list_type: null, + min_quantity: null, + max_quantity: null, + }, + original_price: { + money_amount_id: null, + price_list_id: null, + price_list_type: null, + min_quantity: null, + max_quantity: null, + }, + }, + { + id: "price-set-PLN", + is_calculated_price_price_list: false, + calculated_amount: 400, + is_original_price_price_list: false, + original_amount: 400, + currency_code: "PLN", + calculated_price: { + money_amount_id: "money-amount-company_id-PLN", + price_list_id: null, + price_list_type: null, + min_quantity: 1, + max_quantity: 5, + }, + original_price: { + money_amount_id: "money-amount-company_id-PLN", + price_list_id: null, + price_list_type: null, + min_quantity: 1, + max_quantity: 5, + }, + }, + ]) + }) + + it("should return price list prices for price list with customer groupst", async () => { + const [{ id }] = await createPriceLists( + service, + {}, + { + customer_group_id: [ + "vip-customer-group-id", + "vip-customer-group-id-1", + ], + }, + [ + { + amount: 200, + currency_code: "EUR", + price_set_id: "price-set-EUR", + }, + ] + ) + + const priceSetsResult = await service.calculatePrices( + { id: ["price-set-EUR"] }, + { + context: { + currency_code: "EUR", + customer_group_id: "vip-customer-group-id", + }, + } + ) + + expect(priceSetsResult).toEqual([ + { + id: "price-set-EUR", + is_calculated_price_price_list: true, + calculated_amount: 200, + is_original_price_price_list: false, + original_amount: null, + currency_code: "EUR", + calculated_price: { + money_amount_id: expect.any(String), + price_list_id: id, + price_list_type: "sale", + min_quantity: null, + max_quantity: null, + }, + original_price: { + money_amount_id: null, + price_list_id: null, + price_list_type: null, + min_quantity: null, + max_quantity: null, + }, + }, + ]) + }) + + it("should return price list prices when price list conditions match within prices", async () => { + await createPriceLists(service, {}, { region_id: ["DE", "PL"] }, [ + ...defaultPriceListPrices, + { + amount: 111, + currency_code: "PLN", + price_set_id: "price-set-PLN", + rules: { + region_id: "DE", + }, + }, + ]) + + const priceSetsResult = await service.calculatePrices( + { id: ["price-set-EUR", "price-set-PLN"] }, + { + context: { + currency_code: "PLN", + region_id: "DE", + customer_group_id: "vip-customer-group-id", + company_id: "medusa-company-id", + }, + } + ) + + expect(priceSetsResult).toEqual([ + { + id: "price-set-EUR", + is_calculated_price_price_list: false, + calculated_amount: null, + is_original_price_price_list: false, + original_amount: null, + currency_code: null, + calculated_price: { + money_amount_id: null, + price_list_id: null, + price_list_type: null, + min_quantity: null, + max_quantity: null, + }, + original_price: { + money_amount_id: null, + price_list_id: null, + price_list_type: null, + min_quantity: null, + max_quantity: null, + }, + }, + { + id: "price-set-PLN", + is_calculated_price_price_list: true, + calculated_amount: 111, + is_original_price_price_list: false, + original_amount: 400, + currency_code: "PLN", + calculated_price: { + money_amount_id: expect.any(String), + price_list_id: expect.any(String), + price_list_type: "sale", + min_quantity: null, + max_quantity: null, + }, + original_price: { + money_amount_id: expect.any(String), + price_list_id: null, + price_list_type: null, + min_quantity: 1, + max_quantity: 5, + }, + }, + ]) + }) + + it("should not return price list prices when price list conditions are met but price rules are not", async () => { + await createPriceLists(service, {}, { region_id: ["DE", "PL"] }, [ + ...defaultPriceListPrices, + { + amount: 111, + currency_code: "PLN", + price_set_id: "price-set-PLN", + rules: { + region_id: "PL", + }, + }, + ]) + + const priceSetsResult = await service.calculatePrices( + { id: ["price-set-EUR", "price-set-PLN"] }, + { + context: { + currency_code: "PLN", + region_id: "DE", + customer_group_id: "vip-customer-group-id", + company_id: "medusa-company-id", + }, + } + ) + + expect(priceSetsResult).toEqual([ + { + id: "price-set-EUR", + is_calculated_price_price_list: false, + calculated_amount: null, + is_original_price_price_list: false, + original_amount: null, + currency_code: null, + calculated_price: { + money_amount_id: null, + price_list_id: null, + price_list_type: null, + min_quantity: null, + max_quantity: null, + }, + original_price: { + money_amount_id: null, + price_list_id: null, + price_list_type: null, + min_quantity: null, + max_quantity: null, + }, + }, + { + id: "price-set-PLN", + is_calculated_price_price_list: true, + calculated_amount: 232, + is_original_price_price_list: false, + original_amount: 400, + currency_code: "PLN", + calculated_price: { + money_amount_id: expect.any(String), + price_list_id: expect.any(String), + price_list_type: "sale", + min_quantity: null, + max_quantity: null, + }, + original_price: { + money_amount_id: expect.any(String), + price_list_id: null, + price_list_type: null, + min_quantity: 1, + max_quantity: 5, + }, + }, + ]) + }) + }) }) }) - }) + }, }) diff --git a/packages/pricing/integration-tests/__tests__/services/pricing-module/money-amount.spec.ts b/packages/pricing/integration-tests/__tests__/services/pricing-module/money-amount.spec.ts index aeeb5f11df..096854f25c 100644 --- a/packages/pricing/integration-tests/__tests__/services/pricing-module/money-amount.spec.ts +++ b/packages/pricing/integration-tests/__tests__/services/pricing-module/money-amount.spec.ts @@ -1,436 +1,415 @@ import { Modules } from "@medusajs/modules-sdk" import { IPricingModuleService } from "@medusajs/types" import { SqlEntityManager } from "@mikro-orm/postgresql" -import { MoneyAmount } from "@models" -import { initModules } from "medusa-test-utils" import { createMoneyAmounts } from "../../../__fixtures__/money-amount" import { createPriceRules } from "../../../__fixtures__/price-rule" import { createPriceSets } from "../../../__fixtures__/price-set" import { createPriceSetMoneyAmounts } from "../../../__fixtures__/price-set-money-amount" import { createPriceSetMoneyAmountRules } from "../../../__fixtures__/price-set-money-amount-rules" import { createRuleTypes } from "../../../__fixtures__/rule-type" -import { MikroOrmWrapper } from "../../../utils" -import { getInitModuleConfig } from "../../../utils/get-init-module-config" +import { moduleIntegrationTestRunner, SuiteOptions } from "medusa-test-utils" jest.setTimeout(30000) -describe("PricingModule Service - MoneyAmount", () => { - let service: IPricingModuleService - let testManager: SqlEntityManager - let repositoryManager: SqlEntityManager - let data!: MoneyAmount[] - let shutdownFunc: () => Promise - - beforeAll(async () => { - const initModulesConfig = getInitModuleConfig() - - const { medusaApp, shutdown } = await initModules(initModulesConfig) - - service = medusaApp.modules[Modules.PRICING] - - shutdownFunc = shutdown - }) - - afterAll(async () => { - await shutdownFunc() - }) - - beforeEach(async () => { - await MikroOrmWrapper.setupDatabase() - repositoryManager = MikroOrmWrapper.forkManager() - testManager = MikroOrmWrapper.forkManager() - - data = await createMoneyAmounts(testManager) - }) - - afterEach(async () => { - await MikroOrmWrapper.clearDatabase() - }) - - describe("listMoneyAmounts", () => { - it("list moneyAmounts", async () => { - const moneyAmountsResult = await service.listMoneyAmounts() - - expect(moneyAmountsResult).toEqual( - expect.arrayContaining([ - expect.objectContaining({ - id: "money-amount-USD", - amount: 500, - }), - expect.objectContaining({ - id: "money-amount-EUR", - amount: 400, - }), - expect.objectContaining({ - id: "money-amount-CAD", - amount: 600, - }), - ]) - ) - }) - - it("should list moneyAmounts by id", async () => { - const moneyAmountsResult = await service.listMoneyAmounts({ - id: ["money-amount-USD"], +moduleIntegrationTestRunner({ + moduleName: Modules.PRICING, + testSuite: ({ + MikroOrmWrapper, + service, + }: SuiteOptions) => { + describe("PricingModule Service - MoneyAmount", () => { + let testManager: SqlEntityManager + beforeEach(async () => { + testManager = await MikroOrmWrapper.forkManager() + await createMoneyAmounts(testManager) }) - expect(moneyAmountsResult).toEqual([ - expect.objectContaining({ - id: "money-amount-USD", - }), - ]) - }) + describe("listMoneyAmounts", () => { + it("list moneyAmounts", async () => { + const moneyAmountsResult = await service.listMoneyAmounts() - it("should list moneyAmounts with relations and selects", async () => { - const moneyAmountsResult = await service.listMoneyAmounts( - { - id: ["money-amount-USD"], - }, - { - select: ["id", "min_quantity", "currency_code"], - } - ) - - const serialized = JSON.parse(JSON.stringify(moneyAmountsResult)) - - expect(serialized).toEqual([ - { - id: "money-amount-USD", - amount: null, - min_quantity: "1", - currency_code: "USD", - }, - ]) - }) - }) - - describe("listAndCountMoneyAmounts", () => { - it("should return moneyAmounts and count", async () => { - const [moneyAmountsResult, count] = - await service.listAndCountMoneyAmounts() - - expect(count).toEqual(3) - expect(moneyAmountsResult).toEqual( - expect.arrayContaining([ - expect.objectContaining({ - id: "money-amount-USD", - }), - expect.objectContaining({ - id: "money-amount-EUR", - }), - expect.objectContaining({ - id: "money-amount-CAD", - }), - ]) - ) - }) - - it("should return moneyAmounts and count when filtered", async () => { - const [moneyAmountsResult, count] = - await service.listAndCountMoneyAmounts({ - id: ["money-amount-USD"], + expect(moneyAmountsResult).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + id: "money-amount-USD", + amount: 500, + }), + expect.objectContaining({ + id: "money-amount-EUR", + amount: 400, + }), + expect.objectContaining({ + id: "money-amount-CAD", + amount: 600, + }), + ]) + ) }) - expect(count).toEqual(1) - expect(moneyAmountsResult).toEqual([ - expect.objectContaining({ - id: "money-amount-USD", - }), - ]) - }) - - it("list moneyAmounts with relations and selects", async () => { - const [moneyAmountsResult, count] = - await service.listAndCountMoneyAmounts( - { + it("should list moneyAmounts by id", async () => { + const moneyAmountsResult = await service.listMoneyAmounts({ id: ["money-amount-USD"], - }, - { - select: ["id", "min_quantity", "currency_code", "amount"], - } - ) + }) - const serialized = JSON.parse(JSON.stringify(moneyAmountsResult)) - - expect(count).toEqual(1) - expect(serialized).toEqual([ - { - id: "money-amount-USD", - amount: 500, - min_quantity: "1", - currency_code: "USD", - }, - ]) - }) - - it("should return moneyAmounts and count when using skip and take", async () => { - const [moneyAmountsResult, count] = - await service.listAndCountMoneyAmounts({}, { skip: 1, take: 1 }) - - expect(count).toEqual(3) - expect(moneyAmountsResult).toEqual([ - expect.objectContaining({ - id: "money-amount-EUR", - }), - ]) - }) - - it("should return requested fields", async () => { - const [moneyAmountsResult, count] = - await service.listAndCountMoneyAmounts( - {}, - { - take: 1, - select: ["id"], - } - ) - - const serialized = JSON.parse(JSON.stringify(moneyAmountsResult)) - - expect(count).toEqual(3) - expect(serialized).toEqual([ - { - id: "money-amount-CAD", - amount: null, - }, - ]) - }) - }) - - describe("retrieveMoneyAmount", () => { - const id = "money-amount-USD" - const amount = 500 - - it("should return moneyAmount for the given id", async () => { - const moneyAmount = await service.retrieveMoneyAmount(id) - - expect(moneyAmount).toEqual( - expect.objectContaining({ - id, + expect(moneyAmountsResult).toEqual([ + expect.objectContaining({ + id: "money-amount-USD", + }), + ]) }) - ) - }) - it("should throw an error when moneyAmount with id does not exist", async () => { - let error + it("should list moneyAmounts with relations and selects", async () => { + const moneyAmountsResult = await service.listMoneyAmounts( + { + id: ["money-amount-USD"], + }, + { + select: ["id", "min_quantity", "currency_code"], + } + ) - try { - await service.retrieveMoneyAmount("does-not-exist") - } catch (e) { - error = e - } + const serialized = JSON.parse(JSON.stringify(moneyAmountsResult)) - expect(error.message).toEqual( - "MoneyAmount with id: does-not-exist was not found" - ) - }) - - it("should throw an error when a id is not provided", async () => { - let error - - try { - await service.retrieveMoneyAmount(undefined as unknown as string) - } catch (e) { - error = e - } - - expect(error.message).toEqual("moneyAmount - id must be defined") - }) - - it("should return moneyAmount based on config select param", async () => { - const moneyAmount = await service.retrieveMoneyAmount(id, { - select: ["id", "amount"], + expect(serialized).toEqual([ + { + id: "money-amount-USD", + amount: null, + min_quantity: "1", + currency_code: "USD", + }, + ]) + }) }) - const serialized = JSON.parse(JSON.stringify(moneyAmount)) + describe("listAndCountMoneyAmounts", () => { + it("should return moneyAmounts and count", async () => { + const [moneyAmountsResult, count] = + await service.listAndCountMoneyAmounts() - expect(serialized).toEqual({ - id, - amount, - }) - }) - }) - - describe("deleteMoneyAmounts", () => { - const id = "money-amount-USD" - - it("should delete the moneyAmounts given an id successfully", async () => { - await service.deleteMoneyAmounts([id]) - - const moneyAmounts = await service.listMoneyAmounts({ - id: [id], - }) - - expect(moneyAmounts).toHaveLength(0) - }) - }) - - describe("softDeleteMoneyAmounts", () => { - const id = "money-amount-USD" - - it("should softDelete priceSetMoneyAmount and PriceRule when soft-deleting money amount", async () => { - await createPriceSets(testManager) - await createRuleTypes(testManager) - await createPriceSetMoneyAmounts(testManager) - await createPriceRules(testManager) - await createPriceSetMoneyAmountRules(testManager) - await service.softDeleteMoneyAmounts([id]) - - const [moneyAmount] = await service.listMoneyAmounts( - { - id: [id], - }, - { - relations: [ - "price_set_money_amount", - "price_set_money_amount.price_rules", - ], - withDeleted: true, - } - ) - - expect(moneyAmount).toBeTruthy() - - const deletedAt = moneyAmount.deleted_at - - expect(moneyAmount).toEqual( - expect.objectContaining({ - deleted_at: deletedAt, - price_set_money_amount: expect.objectContaining({ - deleted_at: deletedAt, - price_rules: [ + expect(count).toEqual(3) + expect(moneyAmountsResult).toEqual( + expect.arrayContaining([ expect.objectContaining({ - deleted_at: deletedAt, + id: "money-amount-USD", }), - ], - }), - }) - ) - }) - }) - - describe("restoreMoneyAmounts", () => { - const id = "money-amount-USD" - - it("should restore softDeleted priceSetMoneyAmount and PriceRule when restoring soft-deleting money amount", async () => { - await createPriceSets(testManager) - await createRuleTypes(testManager) - await createPriceSetMoneyAmounts(testManager) - await createPriceRules(testManager) - await createPriceSetMoneyAmountRules(testManager) - await service.softDeleteMoneyAmounts([id]) - await service.restoreMoneyAmounts([id]) - - const [moneyAmount] = await service.listMoneyAmounts( - { - id: [id], - }, - { - relations: [ - "price_set_money_amount", - "price_set_money_amount.price_rules", - ], - } - ) - - expect(moneyAmount).toBeTruthy() - - const deletedAt = null - - expect(moneyAmount).toEqual( - expect.objectContaining({ - deleted_at: deletedAt, - price_set_money_amount: expect.objectContaining({ - deleted_at: deletedAt, - price_rules: [ expect.objectContaining({ - deleted_at: deletedAt, + id: "money-amount-EUR", }), - ], - }), + expect.objectContaining({ + id: "money-amount-CAD", + }), + ]) + ) }) - ) - }) - }) - describe("updateMoneyAmounts", () => { - const id = "money-amount-USD" + it("should return moneyAmounts and count when filtered", async () => { + const [moneyAmountsResult, count] = + await service.listAndCountMoneyAmounts({ + id: ["money-amount-USD"], + }) - it("should update the amount of the moneyAmount successfully", async () => { - await service.updateMoneyAmounts([ - { - id, - amount: 700, - }, - ]) + expect(count).toEqual(1) + expect(moneyAmountsResult).toEqual([ + expect.objectContaining({ + id: "money-amount-USD", + }), + ]) + }) - const moneyAmount = JSON.parse( - JSON.stringify( - await service.retrieveMoneyAmount(id, { select: ["amount"] }) - ) - ) + it("list moneyAmounts with relations and selects", async () => { + const [moneyAmountsResult, count] = + await service.listAndCountMoneyAmounts( + { + id: ["money-amount-USD"], + }, + { + select: ["id", "min_quantity", "currency_code", "amount"], + } + ) - expect(moneyAmount.amount).toEqual(700) - }) + const serialized = JSON.parse(JSON.stringify(moneyAmountsResult)) - it("should update the currency of the moneyAmount successfully", async () => { - await service.updateMoneyAmounts([ - { - id, - currency_code: "EUR", - }, - ]) + expect(count).toEqual(1) + expect(serialized).toEqual([ + { + id: "money-amount-USD", + amount: 500, + min_quantity: "1", + currency_code: "USD", + }, + ]) + }) - const moneyAmount = await service.retrieveMoneyAmount(id, {}) + it("should return moneyAmounts and count when using skip and take", async () => { + const [moneyAmountsResult, count] = + await service.listAndCountMoneyAmounts({}, { skip: 1, take: 1 }) - expect(moneyAmount.currency_code).toEqual("EUR") - }) + expect(count).toEqual(3) + expect(moneyAmountsResult).toEqual([ + expect.objectContaining({ + id: "money-amount-EUR", + }), + ]) + }) - it("should throw an error when a id does not exist", async () => { - let error + it("should return requested fields", async () => { + const [moneyAmountsResult, count] = + await service.listAndCountMoneyAmounts( + {}, + { + take: 1, + select: ["id"], + } + ) - try { - await service.updateMoneyAmounts([ - { - id: "does-not-exist", - amount: 666, - }, - ]) - } catch (e) { - error = e - } + const serialized = JSON.parse(JSON.stringify(moneyAmountsResult)) - expect(error.message).toEqual( - 'MoneyAmount with id "does-not-exist" not found' - ) - }) - }) - - describe("createMoneyAmounts", () => { - it("should create a moneyAmount successfully", async () => { - await service.createMoneyAmounts([ - { - id: "money-amount-TESM", - currency_code: "USD", - amount: 333, - min_quantity: 1, - max_quantity: 4, - }, - ]) - - const [moneyAmount] = await service.listMoneyAmounts({ - id: ["money-amount-TESM"], + expect(count).toEqual(3) + expect(serialized).toEqual([ + { + id: "money-amount-CAD", + amount: null, + }, + ]) + }) }) - expect(moneyAmount).toEqual( - expect.objectContaining({ - id: "money-amount-TESM", - currency_code: "USD", - amount: 333, - min_quantity: "1", - max_quantity: "4", + describe("retrieveMoneyAmount", () => { + const id = "money-amount-USD" + const amount = 500 + + it("should return moneyAmount for the given id", async () => { + const moneyAmount = await service.retrieveMoneyAmount(id) + + expect(moneyAmount).toEqual( + expect.objectContaining({ + id, + }) + ) }) - ) + + it("should throw an error when moneyAmount with id does not exist", async () => { + let error + + try { + await service.retrieveMoneyAmount("does-not-exist") + } catch (e) { + error = e + } + + expect(error.message).toEqual( + "MoneyAmount with id: does-not-exist was not found" + ) + }) + + it("should throw an error when a id is not provided", async () => { + let error + + try { + await service.retrieveMoneyAmount(undefined as unknown as string) + } catch (e) { + error = e + } + + expect(error.message).toEqual("moneyAmount - id must be defined") + }) + + it("should return moneyAmount based on config select param", async () => { + const moneyAmount = await service.retrieveMoneyAmount(id, { + select: ["id", "amount"], + }) + + const serialized = JSON.parse(JSON.stringify(moneyAmount)) + + expect(serialized).toEqual({ + id, + amount, + }) + }) + }) + + describe("deleteMoneyAmounts", () => { + const id = "money-amount-USD" + + it("should delete the moneyAmounts given an id successfully", async () => { + await service.deleteMoneyAmounts([id]) + + const moneyAmounts = await service.listMoneyAmounts({ + id: [id], + }) + + expect(moneyAmounts).toHaveLength(0) + }) + }) + + describe("softDeleteMoneyAmounts", () => { + const id = "money-amount-USD" + + it("should softDelete priceSetMoneyAmount and PriceRule when soft-deleting money amount", async () => { + await createPriceSets(testManager) + await createRuleTypes(testManager) + await createPriceSetMoneyAmounts(testManager) + await createPriceRules(testManager) + await createPriceSetMoneyAmountRules(testManager) + await service.softDeleteMoneyAmounts([id]) + + const [moneyAmount] = await service.listMoneyAmounts( + { + id: [id], + }, + { + relations: [ + "price_set_money_amount", + "price_set_money_amount.price_rules", + ], + withDeleted: true, + } + ) + + expect(moneyAmount).toBeTruthy() + + const deletedAt = moneyAmount.deleted_at + + expect(moneyAmount).toEqual( + expect.objectContaining({ + deleted_at: deletedAt, + price_set_money_amount: expect.objectContaining({ + deleted_at: deletedAt, + price_rules: [ + expect.objectContaining({ + deleted_at: deletedAt, + }), + ], + }), + }) + ) + }) + }) + + describe("restoreMoneyAmounts", () => { + const id = "money-amount-USD" + + it("should restore softDeleted priceSetMoneyAmount and PriceRule when restoring soft-deleting money amount", async () => { + await createPriceSets(testManager) + await createRuleTypes(testManager) + await createPriceSetMoneyAmounts(testManager) + await createPriceRules(testManager) + await createPriceSetMoneyAmountRules(testManager) + await service.softDeleteMoneyAmounts([id]) + await service.restoreMoneyAmounts([id]) + + const [moneyAmount] = await service.listMoneyAmounts( + { + id: [id], + }, + { + relations: [ + "price_set_money_amount", + "price_set_money_amount.price_rules", + ], + } + ) + + expect(moneyAmount).toBeTruthy() + + const deletedAt = null + + expect(moneyAmount).toEqual( + expect.objectContaining({ + deleted_at: deletedAt, + price_set_money_amount: expect.objectContaining({ + deleted_at: deletedAt, + price_rules: [ + expect.objectContaining({ + deleted_at: deletedAt, + }), + ], + }), + }) + ) + }) + }) + + describe("updateMoneyAmounts", () => { + const id = "money-amount-USD" + + it("should update the amount of the moneyAmount successfully", async () => { + await service.updateMoneyAmounts([ + { + id, + amount: 700, + }, + ]) + + const moneyAmount = JSON.parse( + JSON.stringify( + await service.retrieveMoneyAmount(id, { select: ["amount"] }) + ) + ) + + expect(moneyAmount.amount).toEqual(700) + }) + + it("should update the currency of the moneyAmount successfully", async () => { + await service.updateMoneyAmounts([ + { + id, + currency_code: "EUR", + }, + ]) + + const moneyAmount = await service.retrieveMoneyAmount(id, {}) + + expect(moneyAmount.currency_code).toEqual("EUR") + }) + + it("should throw an error when a id does not exist", async () => { + let error + + try { + await service.updateMoneyAmounts([ + { + id: "does-not-exist", + amount: 666, + }, + ]) + } catch (e) { + error = e + } + + expect(error.message).toEqual( + 'MoneyAmount with id "does-not-exist" not found' + ) + }) + }) + + describe("createMoneyAmounts", () => { + it("should create a moneyAmount successfully", async () => { + await service.createMoneyAmounts([ + { + id: "money-amount-TESM", + currency_code: "USD", + amount: 333, + min_quantity: 1, + max_quantity: 4, + }, + ]) + + const [moneyAmount] = await service.listMoneyAmounts({ + id: ["money-amount-TESM"], + }) + + expect(moneyAmount).toEqual( + expect.objectContaining({ + id: "money-amount-TESM", + currency_code: "USD", + amount: 333, + min_quantity: "1", + max_quantity: "4", + }) + ) + }) + }) }) - }) + }, }) diff --git a/packages/pricing/integration-tests/__tests__/services/pricing-module/price-list-rule.spec.ts b/packages/pricing/integration-tests/__tests__/services/pricing-module/price-list-rule.spec.ts index 713f8fb696..610cb5fd21 100644 --- a/packages/pricing/integration-tests/__tests__/services/pricing-module/price-list-rule.spec.ts +++ b/packages/pricing/integration-tests/__tests__/services/pricing-module/price-list-rule.spec.ts @@ -1,358 +1,338 @@ -import { MikroOrmWrapper } from "../../../utils" - import { IPricingModuleService } from "@medusajs/types" import { SqlEntityManager } from "@mikro-orm/postgresql" import { createPriceLists } from "../../../__fixtures__/price-list" import { createPriceListRules } from "../../../__fixtures__/price-list-rules" import { createRuleTypes } from "../../../__fixtures__/rule-type" -import { getInitModuleConfig } from "../../../utils/get-init-module-config" import { Modules } from "@medusajs/modules-sdk" -import { initModules } from "medusa-test-utils" +import { moduleIntegrationTestRunner, SuiteOptions } from "medusa-test-utils" jest.setTimeout(30000) -describe("PriceListRule Service", () => { - let service: IPricingModuleService - let testManager: SqlEntityManager - let repositoryManager: SqlEntityManager - let shutdownFunc: () => Promise - - beforeAll(async () => { - const initModulesConfig = getInitModuleConfig() - - const { medusaApp, shutdown } = await initModules(initModulesConfig) - - service = medusaApp.modules[Modules.PRICING] - - shutdownFunc = shutdown - }) - - afterAll(async () => { - await shutdownFunc() - }) - - beforeEach(async () => { - await MikroOrmWrapper.setupDatabase() - repositoryManager = await MikroOrmWrapper.forkManager() - - testManager = await MikroOrmWrapper.forkManager() - await createRuleTypes(testManager) - await createPriceLists(testManager) - await createPriceListRules(testManager) - }) - - afterEach(async () => { - await MikroOrmWrapper.clearDatabase() - }) - - describe("list", () => { - it("should list priceListRules", async () => { - const priceListRuleResult = await service.listPriceListRules() - - expect(priceListRuleResult).toEqual([ - expect.objectContaining({ - id: "price-list-rule-1", - }), - expect.objectContaining({ - id: "price-list-rule-2", - }), - ]) - }) - - it("should list priceListRules by pricelist id", async () => { - const priceListRuleResult = await service.listPriceListRules({ - id: ["price-list-rule-1"], +moduleIntegrationTestRunner({ + moduleName: Modules.PRICING, + testSuite: ({ + MikroOrmWrapper, + service, + }: SuiteOptions) => { + describe("PriceListRule Service", () => { + let testManager: SqlEntityManager + beforeEach(async () => { + testManager = await MikroOrmWrapper.forkManager() + await createRuleTypes(testManager) + await createPriceLists(testManager) + await createPriceListRules(testManager) }) - expect(priceListRuleResult).toEqual([ - expect.objectContaining({ - id: "price-list-rule-1", - }), - ]) - }) - }) + describe("list", () => { + it("should list priceListRules", async () => { + const priceListRuleResult = await service.listPriceListRules() - describe("listAndCount", () => { - it("should return pricelistrules and count", async () => { - const [priceListRuleResult, count] = - await service.listAndCountPriceListRules() - - expect(count).toEqual(2) - expect(priceListRuleResult).toEqual([ - expect.objectContaining({ - id: "price-list-rule-1", - }), - expect.objectContaining({ - id: "price-list-rule-2", - }), - ]) - }) - - it("should return pricelistrules and count when filtered", async () => { - const [priceListRuleResult, count] = - await service.listAndCountPriceListRules({ - id: ["price-list-rule-1"], + expect(priceListRuleResult).toEqual([ + expect.objectContaining({ + id: "price-list-rule-1", + }), + expect.objectContaining({ + id: "price-list-rule-2", + }), + ]) }) - expect(count).toEqual(1) - expect(priceListRuleResult).toEqual([ - expect.objectContaining({ - id: "price-list-rule-1", - }), - ]) - }) + it("should list priceListRules by pricelist id", async () => { + const priceListRuleResult = await service.listPriceListRules({ + id: ["price-list-rule-1"], + }) - it("should return pricelistrules and count when using skip and take", async () => { - const [priceListRuleResult, count] = - await service.listAndCountPriceListRules({}, { skip: 1, take: 1 }) + expect(priceListRuleResult).toEqual([ + expect.objectContaining({ + id: "price-list-rule-1", + }), + ]) + }) + }) - expect(count).toEqual(2) - expect(priceListRuleResult).toEqual([ - expect.objectContaining({ - id: "price-list-rule-2", - }), - ]) - }) + describe("listAndCount", () => { + it("should return pricelistrules and count", async () => { + const [priceListRuleResult, count] = + await service.listAndCountPriceListRules() - it("should return requested fields", async () => { - const [priceListRuleResult, count] = - await service.listAndCountPriceListRules( - {}, - { - take: 1, - select: ["id"], + expect(count).toEqual(2) + expect(priceListRuleResult).toEqual([ + expect.objectContaining({ + id: "price-list-rule-1", + }), + expect.objectContaining({ + id: "price-list-rule-2", + }), + ]) + }) + + it("should return pricelistrules and count when filtered", async () => { + const [priceListRuleResult, count] = + await service.listAndCountPriceListRules({ + id: ["price-list-rule-1"], + }) + + expect(count).toEqual(1) + expect(priceListRuleResult).toEqual([ + expect.objectContaining({ + id: "price-list-rule-1", + }), + ]) + }) + + it("should return pricelistrules and count when using skip and take", async () => { + const [priceListRuleResult, count] = + await service.listAndCountPriceListRules({}, { skip: 1, take: 1 }) + + expect(count).toEqual(2) + expect(priceListRuleResult).toEqual([ + expect.objectContaining({ + id: "price-list-rule-2", + }), + ]) + }) + + it("should return requested fields", async () => { + const [priceListRuleResult, count] = + await service.listAndCountPriceListRules( + {}, + { + take: 1, + select: ["id"], + } + ) + + const serialized = JSON.parse(JSON.stringify(priceListRuleResult)) + + expect(count).toEqual(2) + expect(serialized).toEqual([ + { + id: "price-list-rule-1", + }, + ]) + }) + }) + + describe("retrieve", () => { + const id = "price-list-rule-1" + + it("should return priceList for the given id", async () => { + const priceListRuleResult = await service.retrievePriceListRule(id) + + expect(priceListRuleResult).toEqual( + expect.objectContaining({ + id, + }) + ) + }) + + it("should throw an error when priceListRule with id does not exist", async () => { + let error + + try { + await service.retrievePriceListRule("does-not-exist") + } catch (e) { + error = e } - ) - const serialized = JSON.parse(JSON.stringify(priceListRuleResult)) - - expect(count).toEqual(2) - expect(serialized).toEqual([ - { - id: "price-list-rule-1", - }, - ]) - }) - }) - - describe("retrieve", () => { - const id = "price-list-rule-1" - - it("should return priceList for the given id", async () => { - const priceListRuleResult = await service.retrievePriceListRule(id) - - expect(priceListRuleResult).toEqual( - expect.objectContaining({ - id, + expect(error.message).toEqual( + "PriceListRule with id: does-not-exist was not found" + ) }) - ) - }) - it("should throw an error when priceListRule with id does not exist", async () => { - let error + it("should throw an error when a id is not provided", async () => { + let error - try { - await service.retrievePriceListRule("does-not-exist") - } catch (e) { - error = e - } + try { + await service.retrievePriceListRule(undefined as unknown as string) + } catch (e) { + error = e + } - expect(error.message).toEqual( - "PriceListRule with id: does-not-exist was not found" - ) - }) - - it("should throw an error when a id is not provided", async () => { - let error - - try { - await service.retrievePriceListRule(undefined as unknown as string) - } catch (e) { - error = e - } - - expect(error.message).toEqual("priceListRule - id must be defined") - }) - }) - - describe("delete", () => { - const id = "price-list-rule-1" - - it("should delete the pricelists given an id successfully", async () => { - await service.deletePriceListRules([id]) - - const priceListResult = await service.listPriceListRules({ - id: [id], + expect(error.message).toEqual("priceListRule - id must be defined") + }) }) - expect(priceListResult).toHaveLength(0) - }) - }) + describe("delete", () => { + const id = "price-list-rule-1" - describe("update", () => { - const id = "price-list-rule-2" + it("should delete the pricelists given an id successfully", async () => { + await service.deletePriceListRules([id]) - it("should update the value of the priceListRule successfully", async () => { - await service.updatePriceListRules([ - { - id, - price_list_id: "price-list-2", - rule_type_id: "rule-type-2", - }, - ]) + const priceListResult = await service.listPriceListRules({ + id: [id], + }) - const priceList = await service.retrievePriceListRule(id, { - relations: ["price_list", "rule_type"], + expect(priceListResult).toHaveLength(0) + }) }) - expect(priceList.price_list.id).toEqual("price-list-2") - expect(priceList.rule_type.id).toEqual("rule-type-2") - }) + describe("update", () => { + const id = "price-list-rule-2" - it("should throw an error when a id does not exist", async () => { - let error + it("should update the value of the priceListRule successfully", async () => { + await service.updatePriceListRules([ + { + id, + price_list_id: "price-list-2", + rule_type_id: "rule-type-2", + }, + ]) - try { - await service.updatePriceListRules([ - { - id: "does-not-exist", - }, - ]) - } catch (e) { - error = e - } + const priceList = await service.retrievePriceListRule(id, { + relations: ["price_list", "rule_type"], + }) - expect(error.message).toEqual( - 'PriceListRule with id "does-not-exist" not found' - ) - }) - }) + expect(priceList.price_list.id).toEqual("price-list-2") + expect(priceList.rule_type.id).toEqual("rule-type-2") + }) - describe("create", () => { - it("should create a priceListRule successfully", async () => { - const [created] = await service.createPriceListRules([ - { - price_list_id: "price-list-2", - rule_type_id: "rule-type-2", - }, - ]) + it("should throw an error when a id does not exist", async () => { + let error - const [priceListRule] = await service.listPriceListRules( - { - id: [created.id], - }, - { - relations: ["price_list", "rule_type"], - } - ) + try { + await service.updatePriceListRules([ + { + id: "does-not-exist", + }, + ]) + } catch (e) { + error = e + } - expect(priceListRule.price_list.id).toEqual("price-list-2") - expect(priceListRule.rule_type.id).toEqual("rule-type-2") - }) - }) - - describe("setPriceListRules", () => { - it("should add a priceListRule to a priceList", async () => { - await createRuleTypes(testManager, [ - { - id: "rule-type-3", - name: "test", - rule_attribute: "sales_channel", - }, - ]) - - await service.setPriceListRules({ - priceListId: "price-list-1", - rules: { - sales_channel: "sc-1", - }, + expect(error.message).toEqual( + 'PriceListRule with id "does-not-exist" not found' + ) + }) }) - const [priceList] = await service.listPriceLists( - { - id: ["price-list-1"], - }, - { - relations: [ - "price_list_rules", - "price_list_rules.price_list_rule_values", - ], - } - ) + describe("create", () => { + it("should create a priceListRule successfully", async () => { + const [created] = await service.createPriceListRules([ + { + price_list_id: "price-list-2", + rule_type_id: "rule-type-2", + }, + ]) - expect(priceList.price_list_rules).toEqual( - expect.arrayContaining([ - expect.objectContaining({ - rule_type: { id: "rule-type-3" }, - price_list_rule_values: [ - expect.objectContaining({ value: "sc-1" }), - ], - }), - ]) - ) - }) + const [priceListRule] = await service.listPriceListRules( + { + id: [created.id], + }, + { + relations: ["price_list", "rule_type"], + } + ) - it("should multiple priceListRules to a priceList", async () => { - await createRuleTypes(testManager, [ - { - id: "rule-type-3", - name: "test", - rule_attribute: "sales_channel", - }, - ]) - - await service.setPriceListRules({ - priceListId: "price-list-1", - rules: { - sales_channel: ["sc-1", "sc-2"], - }, + expect(priceListRule.price_list.id).toEqual("price-list-2") + expect(priceListRule.rule_type.id).toEqual("rule-type-2") + }) }) - const [priceList] = await service.listPriceLists( - { - id: ["price-list-1"], - }, - { - relations: [ - "price_list_rules", - "price_list_rules.price_list_rule_values", - ], - } - ) + describe("setPriceListRules", () => { + it("should add a priceListRule to a priceList", async () => { + await createRuleTypes(testManager, [ + { + id: "rule-type-3", + name: "test", + rule_attribute: "sales_channel", + }, + ]) - expect(priceList.price_list_rules).toEqual( - expect.arrayContaining([ - expect.objectContaining({ - rule_type: { id: "rule-type-3" }, - price_list_rule_values: expect.arrayContaining([ - expect.objectContaining({ value: "sc-1" }), - expect.objectContaining({ value: "sc-2" }), - ]), - }), - ]) - ) - }) - }) + await service.setPriceListRules({ + priceListId: "price-list-1", + rules: { + sales_channel: "sc-1", + }, + }) - describe("removePriceListRules", () => { - it("should remove a priceListRule from a priceList", async () => { - await service.removePriceListRules({ - priceListId: "price-list-1", - rules: ["currency_code"], + const [priceList] = await service.listPriceLists( + { + id: ["price-list-1"], + }, + { + relations: [ + "price_list_rules", + "price_list_rules.price_list_rule_values", + ], + } + ) + + expect(priceList.price_list_rules).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + rule_type: { id: "rule-type-3" }, + price_list_rule_values: [ + expect.objectContaining({ value: "sc-1" }), + ], + }), + ]) + ) + }) + + it("should multiple priceListRules to a priceList", async () => { + await createRuleTypes(testManager, [ + { + id: "rule-type-3", + name: "test", + rule_attribute: "sales_channel", + }, + ]) + + await service.setPriceListRules({ + priceListId: "price-list-1", + rules: { + sales_channel: ["sc-1", "sc-2"], + }, + }) + + const [priceList] = await service.listPriceLists( + { + id: ["price-list-1"], + }, + { + relations: [ + "price_list_rules", + "price_list_rules.price_list_rule_values", + ], + } + ) + + expect(priceList.price_list_rules).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + rule_type: { id: "rule-type-3" }, + price_list_rule_values: expect.arrayContaining([ + expect.objectContaining({ value: "sc-1" }), + expect.objectContaining({ value: "sc-2" }), + ]), + }), + ]) + ) + }) }) - const [priceList] = await service.listPriceLists( - { - id: ["price-list-1"], - }, - { - relations: ["price_list_rules"], - } - ) + describe("removePriceListRules", () => { + it("should remove a priceListRule from a priceList", async () => { + await service.removePriceListRules({ + priceListId: "price-list-1", + rules: ["currency_code"], + }) - expect(priceList.price_list_rules).toEqual([ - expect.objectContaining({ rule_type: { id: "rule-type-2" } }), - ]) + const [priceList] = await service.listPriceLists( + { + id: ["price-list-1"], + }, + { + relations: ["price_list_rules"], + } + ) + + expect(priceList.price_list_rules).toEqual([ + expect.objectContaining({ rule_type: { id: "rule-type-2" } }), + ]) + }) + }) }) - }) + }, }) diff --git a/packages/pricing/integration-tests/__tests__/services/pricing-module/price-list.spec.ts b/packages/pricing/integration-tests/__tests__/services/pricing-module/price-list.spec.ts index 6cc44bec8c..b569c3e5f3 100644 --- a/packages/pricing/integration-tests/__tests__/services/pricing-module/price-list.spec.ts +++ b/packages/pricing/integration-tests/__tests__/services/pricing-module/price-list.spec.ts @@ -1,866 +1,848 @@ -import { MikroOrmWrapper } from "../../../utils" - import { Modules } from "@medusajs/modules-sdk" import { IPricingModuleService } from "@medusajs/types" -import { SqlEntityManager } from "@mikro-orm/postgresql" -import { initModules } from "medusa-test-utils" import { createPriceLists } from "../../../__fixtures__/price-list" import { createPriceSets } from "../../../__fixtures__/price-set" -import { getInitModuleConfig } from "../../../utils/get-init-module-config" +import { moduleIntegrationTestRunner, SuiteOptions } from "medusa-test-utils" jest.setTimeout(30000) -describe("PriceList Service", () => { - let service: IPricingModuleService - let testManager: SqlEntityManager - let shutdownFunc: () => Promise - - beforeAll(async () => { - const initModulesConfig = getInitModuleConfig() - - const { medusaApp, shutdown } = await initModules(initModulesConfig) - - service = medusaApp.modules[Modules.PRICING] - - shutdownFunc = shutdown - }) - - afterAll(async () => { - await shutdownFunc() - }) - - beforeEach(async () => { - await MikroOrmWrapper.setupDatabase() - await MikroOrmWrapper.forkManager() - - testManager = await MikroOrmWrapper.forkManager() - await createPriceSets(testManager) - await createPriceLists(testManager) - await service.createRuleTypes([ - { - name: "Region ID", - rule_attribute: "region_id", - }, - { - name: "Customer Group ID", - rule_attribute: "customer_group_id", - }, - ]) - }) - - afterEach(async () => { - await MikroOrmWrapper.clearDatabase() - }) - - describe("list", () => { - it("should list priceLists", async () => { - const priceListResult = await service.listPriceLists() - - expect(priceListResult).toEqual([ - expect.objectContaining({ - id: "price-list-1", - }), - expect.objectContaining({ - id: "price-list-2", - }), - ]) - }) - - it("should list pricelists by id", async () => { - const priceListResult = await service.listPriceLists({ - id: ["price-list-1"], - }) - - expect(priceListResult).toEqual([ - expect.objectContaining({ - id: "price-list-1", - }), - ]) - }) - }) - - describe("listAndCount", () => { - it("should return pricelists and count", async () => { - const [priceListResult, count] = await service.listAndCountPriceLists() - - expect(count).toEqual(2) - expect(priceListResult).toEqual([ - expect.objectContaining({ - id: "price-list-1", - }), - expect.objectContaining({ - id: "price-list-2", - }), - ]) - }) - - it("should return pricelists and count when filtered", async () => { - const [priceListResult, count] = await service.listAndCountPriceLists({ - id: ["price-list-1"], - }) - - expect(count).toEqual(1) - expect(priceListResult).toEqual([ - expect.objectContaining({ - id: "price-list-1", - }), - ]) - }) - - it("should return pricelists and count when using skip and take", async () => { - const [priceListResult, count] = await service.listAndCountPriceLists( - {}, - { skip: 1, take: 1 } - ) - - expect(count).toEqual(2) - expect(priceListResult).toEqual([ - expect.objectContaining({ - id: "price-list-2", - }), - ]) - }) - - it("should return requested fields", async () => { - const [priceListResult, count] = await service.listAndCountPriceLists( - {}, - { - take: 1, - select: ["id"], - } - ) - - const serialized = JSON.parse(JSON.stringify(priceListResult)) - - expect(count).toEqual(2) - expect(serialized).toEqual([ - { - id: "price-list-1", - }, - ]) - }) - }) - - describe("retrieve", () => { - const id = "price-list-1" - - it("should return priceList for the given id", async () => { - const priceListResult = await service.retrievePriceList(id) - - expect(priceListResult).toEqual( - expect.objectContaining({ - id, - }) - ) - }) - - it("should throw an error when priceList with id does not exist", async () => { - let error - - try { - await service.retrievePriceList("does-not-exist") - } catch (e) { - error = e - } - - expect(error.message).toEqual( - "PriceList with id: does-not-exist was not found" - ) - }) - - it("should throw an error when a id is not provided", async () => { - let error - - try { - await service.retrievePriceList(undefined as unknown as string) - } catch (e) { - error = e - } - - expect(error.message).toEqual("priceList - id must be defined") - }) - }) - - describe("delete", () => { - const id = "price-list-1" - - it("should delete the pricelists given an id successfully", async () => { - await service.deletePriceLists([id]) - - const priceListResult = await service.listPriceLists({ - id: [id], - }) - - expect(priceListResult).toHaveLength(0) - }) - }) - - describe("update", () => { - let createdId - const id = "price-list-2" - - beforeEach(async () => { - const [created] = await service.createPriceLists([ - { - title: "test", - description: "test", - starts_at: new Date("10/01/2023"), - ends_at: new Date("10/30/2023"), - rules: { - customer_group_id: [ - "vip-customer-group-id", - "another-vip-customer-group-id", - ], - region_id: ["DE", "DK"], - }, - prices: [ - { - amount: 400, - currency_code: "EUR", - price_set_id: "price-set-1", - }, - ], - }, - ]) - createdId = created.id - }) - - it("should fail to update a priceList with invalid starts_at date", async () => { - let error - try { - await service.updatePriceLists([ +moduleIntegrationTestRunner({ + moduleName: Modules.PRICING, + testSuite: ({ + MikroOrmWrapper, + service, + }: SuiteOptions) => { + describe("PriceList Service", () => { + beforeEach(async () => { + const testManager = await MikroOrmWrapper.forkManager() + await createPriceSets(testManager) + await createPriceLists(testManager) + await service.createRuleTypes([ { - id: createdId, - starts_at: "invalid-date", + name: "Region ID", + rule_attribute: "region_id", + }, + { + name: "Customer Group ID", + rule_attribute: "customer_group_id", }, ]) - } catch (err) { - error = err - } + }) - expect(error.message).toEqual( - "Cannot set price list starts at with with invalid date string: invalid-date" - ) - }) + describe("list", () => { + it("should list priceLists", async () => { + const priceListResult = await service.listPriceLists() - it("should fail to update a priceList with invalid ends_at date", async () => { - let error - try { - await service.updatePriceLists([ - { - id: createdId, - ends_at: "invalid-date", - }, - ]) - } catch (err) { - error = err - } - - expect(error.message).toEqual( - "Cannot set price list ends at with with invalid date string: invalid-date" - ) - }) - - it("should update a priceList with starts_at and ends_at dates given as string", async () => { - let [priceList] = await service.updatePriceLists([ - { - id: createdId, - starts_at: "10/10/2010", - ends_at: "10/20/2030", - }, - ]) - expect(priceList).toEqual( - expect.objectContaining({ - starts_at: new Date("10/10/2010").toISOString(), - ends_at: new Date("10/20/2030").toISOString(), - }) - ) - }) - - it("should update the starts_at date of the priceList successfully", async () => { - const updateDate = new Date() - await service.updatePriceLists([ - { - id: createdId, - starts_at: updateDate, - rules: { - new_rule: ["new-rule-value"], - }, - }, - ]) - - const [priceList] = await service.listPriceLists( - { - id: [createdId], - }, - { - relations: [ - "price_set_money_amounts.money_amount", - "price_set_money_amounts.price_set", - "price_list_rules.price_list_rule_values", - "price_list_rules.rule_type", - ], - select: [ - "id", - "starts_at", - "price_set_money_amounts.money_amount.amount", - "price_set_money_amounts.money_amount.currency_code", - "price_set_money_amounts.money_amount.price_list_id", - "price_list_rules.price_list_rule_values.value", - "price_list_rules.rule_type.rule_attribute", - ], - } - ) - - expect(priceList).toEqual( - expect.objectContaining({ - id: expect.any(String), - starts_at: updateDate, - price_set_money_amounts: expect.arrayContaining([ + expect(priceListResult).toEqual([ expect.objectContaining({ - price_list: expect.objectContaining({ - id: expect.any(String), - }), - money_amount: expect.objectContaining({ - amount: 400, - currency_code: "EUR", - }), + id: "price-list-1", }), - ]), - price_list_rules: expect.arrayContaining([ expect.objectContaining({ - id: expect.any(String), - rule_type: expect.objectContaining({ - id: expect.any(String), - rule_attribute: "new_rule", - }), - price_list_rule_values: [ - expect.objectContaining({ - id: expect.any(String), - value: "new-rule-value", - }), + id: "price-list-2", + }), + ]) + }) + + it("should list pricelists by id", async () => { + const priceListResult = await service.listPriceLists({ + id: ["price-list-1"], + }) + + expect(priceListResult).toEqual([ + expect.objectContaining({ + id: "price-list-1", + }), + ]) + }) + }) + + describe("listAndCount", () => { + it("should return pricelists and count", async () => { + const [priceListResult, count] = + await service.listAndCountPriceLists() + + expect(count).toEqual(2) + expect(priceListResult).toEqual([ + expect.objectContaining({ + id: "price-list-1", + }), + expect.objectContaining({ + id: "price-list-2", + }), + ]) + }) + + it("should return pricelists and count when filtered", async () => { + const [priceListResult, count] = await service.listAndCountPriceLists( + { + id: ["price-list-1"], + } + ) + + expect(count).toEqual(1) + expect(priceListResult).toEqual([ + expect.objectContaining({ + id: "price-list-1", + }), + ]) + }) + + it("should return pricelists and count when using skip and take", async () => { + const [priceListResult, count] = await service.listAndCountPriceLists( + {}, + { skip: 1, take: 1 } + ) + + expect(count).toEqual(2) + expect(priceListResult).toEqual([ + expect.objectContaining({ + id: "price-list-2", + }), + ]) + }) + + it("should return requested fields", async () => { + const [priceListResult, count] = await service.listAndCountPriceLists( + {}, + { + take: 1, + select: ["id"], + } + ) + + const serialized = JSON.parse(JSON.stringify(priceListResult)) + + expect(count).toEqual(2) + expect(serialized).toEqual([ + { + id: "price-list-1", + }, + ]) + }) + }) + + describe("retrieve", () => { + const id = "price-list-1" + + it("should return priceList for the given id", async () => { + const priceListResult = await service.retrievePriceList(id) + + expect(priceListResult).toEqual( + expect.objectContaining({ + id, + }) + ) + }) + + it("should throw an error when priceList with id does not exist", async () => { + let error + + try { + await service.retrievePriceList("does-not-exist") + } catch (e) { + error = e + } + + expect(error.message).toEqual( + "PriceList with id: does-not-exist was not found" + ) + }) + + it("should throw an error when a id is not provided", async () => { + let error + + try { + await service.retrievePriceList(undefined as unknown as string) + } catch (e) { + error = e + } + + expect(error.message).toEqual("priceList - id must be defined") + }) + }) + + describe("delete", () => { + const id = "price-list-1" + + it("should delete the pricelists given an id successfully", async () => { + await service.deletePriceLists([id]) + + const priceListResult = await service.listPriceLists({ + id: [id], + }) + + expect(priceListResult).toHaveLength(0) + }) + }) + + describe("update", () => { + let createdId + const id = "price-list-2" + + beforeEach(async () => { + const [created] = await service.createPriceLists([ + { + title: "test", + description: "test", + starts_at: new Date("10/01/2023"), + ends_at: new Date("10/30/2023"), + rules: { + customer_group_id: [ + "vip-customer-group-id", + "another-vip-customer-group-id", + ], + region_id: ["DE", "DK"], + }, + prices: [ + { + amount: 400, + currency_code: "EUR", + price_set_id: "price-set-1", + }, ], - }), - ]), - }) - ) - }) - - it("should throw an error when a id does not exist", async () => { - let error - - try { - await service.updatePriceLists([ - { - id: "does-not-exist", - rules_count: 2, - }, - ]) - } catch (e) { - error = e - } - - expect(error.message).toEqual( - 'PriceList with id "does-not-exist" not found' - ) - }) - }) - - describe("createPriceLists", () => { - it("should fail to create a priceList with invalid starts_at date", async () => { - let error - try { - await service.createPriceLists([ - { - title: "test", - description: "test", - starts_at: "invalid-date", - }, - ]) - } catch (err) { - error = err - } - - expect(error.message).toEqual( - "Cannot set price list starts at with with invalid date string: invalid-date" - ) - }) - - it("should fail to create a priceList with invalid ends_at date", async () => { - let error - try { - await service.createPriceLists([ - { - title: "test", - description: "test", - ends_at: "invalid-date", - }, - ]) - } catch (err) { - error = err - } - - expect(error.message).toEqual( - "Cannot set price list ends at with with invalid date string: invalid-date" - ) - }) - - it("should create a priceList with starts_at and ends_at dates given as string", async () => { - let [priceList] = await service.createPriceLists([ - { - title: "test", - description: "test", - starts_at: "10/10/2010", - ends_at: "10/20/2030", - }, - ]) - expect(priceList).toEqual( - expect.objectContaining({ - starts_at: new Date("10/10/2010").toISOString(), - ends_at: new Date("10/20/2030").toISOString(), - }) - ) - }) - - it("should create a priceList successfully", async () => { - const [created] = await service.createPriceLists([ - { - title: "test", - description: "test", - starts_at: new Date("10/01/2023"), - ends_at: new Date("10/30/2023"), - rules: { - customer_group_id: [ - "vip-customer-group-id", - "another-vip-customer-group-id", - ], - region_id: ["DE", "DK"], - }, - prices: [ - { - amount: 400, - currency_code: "EUR", - price_set_id: "price-set-1", }, - ], - }, - ]) - - const [priceList] = await service.listPriceLists( - { - id: [created.id], - }, - { - relations: [ - "price_set_money_amounts.money_amount", - "price_set_money_amounts.price_set", - "price_list_rules.price_list_rule_values", - "price_list_rules.rule_type", - ], - select: [ - "id", - "price_set_money_amounts.money_amount.amount", - "price_set_money_amounts.money_amount.currency_code", - "price_set_money_amounts.money_amount.price_list_id", - "price_list_rules.price_list_rule_values.value", - "price_list_rules.rule_type.rule_attribute", - ], - } - ) - - expect(priceList).toEqual( - expect.objectContaining({ - id: expect.any(String), - price_set_money_amounts: expect.arrayContaining([ - expect.objectContaining({ - price_list: expect.objectContaining({ - id: expect.any(String), - }), - money_amount: expect.objectContaining({ - amount: 400, - currency_code: "EUR", - }), - }), - ]), - price_list_rules: expect.arrayContaining([ - expect.objectContaining({ - id: expect.any(String), - rule_type: expect.objectContaining({ - id: expect.any(String), - rule_attribute: "customer_group_id", - }), - price_list_rule_values: expect.arrayContaining([ - expect.objectContaining({ - id: expect.any(String), - value: "vip-customer-group-id", - }), - expect.objectContaining({ - id: expect.any(String), - value: "another-vip-customer-group-id", - }), - ]), - }), - expect.objectContaining({ - id: expect.any(String), - rule_type: expect.objectContaining({ - id: expect.any(String), - rule_attribute: "region_id", - }), - price_list_rule_values: expect.arrayContaining([ - expect.objectContaining({ - id: expect.any(String), - value: "DE", - }), - expect.objectContaining({ - id: expect.any(String), - value: "DK", - }), - ]), - }), - ]), + ]) + createdId = created.id }) - ) - }) - it("should create a price list with granular rules within prices", async () => { - const [created] = await service.createPriceLists([ - { - title: "test", - description: "test", - starts_at: "10/01/2023", - ends_at: "10/30/2023", - rules: { - customer_group_id: [ - "vip-customer-group-id", - "another-vip-customer-group-id", - ], - region_id: ["DE", "DK"], - }, - prices: [ - { - amount: 400, - currency_code: "EUR", - price_set_id: "price-set-1", - rules: { - region_id: "DE", - }, - }, - { - amount: 600, - currency_code: "EUR", - price_set_id: "price-set-1", - }, - ], - }, - ]) - - const [priceList] = await service.listPriceLists( - { - id: [created.id], - }, - { - relations: [ - "price_set_money_amounts.money_amount", - "price_set_money_amounts.price_set", - "price_set_money_amounts.price_rules", - "price_list_rules.price_list_rule_values", - "price_list_rules.rule_type", - ], - select: [ - "id", - "price_set_money_amounts.price_rules.value", - "price_set_money_amounts.rules_count", - "price_set_money_amounts.money_amount.amount", - "price_set_money_amounts.money_amount.currency_code", - "price_set_money_amounts.money_amount.price_list_id", - "price_list_rules.price_list_rule_values.value", - "price_list_rules.rule_type.rule_attribute", - ], - } - ) - - expect(priceList).toEqual( - expect.objectContaining({ - id: expect.any(String), - price_set_money_amounts: expect.arrayContaining([ - expect.objectContaining({ - rules_count: 1, - price_rules: expect.arrayContaining([ - expect.objectContaining({ - id: expect.any(String), - value: "DE", - }), - ]), - price_list: expect.objectContaining({ - id: expect.any(String), - }), - money_amount: expect.objectContaining({ - amount: 400, - currency_code: "EUR", - }), - }), - expect.objectContaining({ - rules_count: 0, - price_rules: [], - price_list: expect.objectContaining({ - id: expect.any(String), - }), - money_amount: expect.objectContaining({ - amount: 600, - currency_code: "EUR", - }), - }), - ]), - price_list_rules: expect.arrayContaining([ - expect.objectContaining({ - id: expect.any(String), - rule_type: expect.objectContaining({ - id: expect.any(String), - rule_attribute: "customer_group_id", - }), - price_list_rule_values: expect.arrayContaining([ - expect.objectContaining({ - id: expect.any(String), - value: "vip-customer-group-id", - }), - expect.objectContaining({ - id: expect.any(String), - value: "another-vip-customer-group-id", - }), - ]), - }), - expect.objectContaining({ - id: expect.any(String), - rule_type: expect.objectContaining({ - id: expect.any(String), - rule_attribute: "region_id", - }), - price_list_rule_values: expect.arrayContaining([ - expect.objectContaining({ - id: expect.any(String), - value: "DE", - }), - expect.objectContaining({ - id: expect.any(String), - value: "DK", - }), - ]), - }), - ]), - }) - ) - }) - - it("should throw error when rule type does not exist", async () => { - const error = await service - .createPriceLists([ - { - title: "test", - description: "test", - rules: { - region_id: ["DE", "DK"], - missing_1: ["test-missing-1"], - }, - prices: [ + it("should fail to update a priceList with invalid starts_at date", async () => { + let error + try { + await service.updatePriceLists([ { - amount: 400, - currency_code: "EUR", - price_set_id: "price-set-1", - rules: { - region_id: "DE", - missing_2: "test-missing-2", - }, + id: createdId, + starts_at: "invalid-date", }, - ], - }, - ]) - .catch((e) => e) + ]) + } catch (err) { + error = err + } - expect(error.message).toEqual( - "Cannot find RuleTypes with rule_attribute - missing_1, missing_2" - ) - }) - }) - - describe("addPriceListPrices", () => { - it("should add a price to a priceList successfully", async () => { - await service.addPriceListPrices([ - { - priceListId: "price-list-1", - prices: [ - { - amount: 123, - currency_code: "EUR", - price_set_id: "price-set-1", - }, - ], - }, - ]) - - const [priceList] = await service.listPriceLists( - { - id: ["price-list-1"], - }, - { - relations: [ - "price_set_money_amounts.money_amount", - "price_set_money_amounts.price_set", - "price_set_money_amounts.price_rules", - "price_list_rules.price_list_rule_values", - "price_list_rules.rule_type", - ], - select: [ - "id", - "price_set_money_amounts.price_rules.value", - "price_set_money_amounts.rules_count", - "price_set_money_amounts.money_amount.amount", - "price_set_money_amounts.money_amount.currency_code", - "price_set_money_amounts.money_amount.price_list_id", - "price_list_rules.price_list_rule_values.value", - "price_list_rules.rule_type.rule_attribute", - ], - } - ) - - expect(priceList).toEqual( - expect.objectContaining({ - id: expect.any(String), - price_set_money_amounts: expect.arrayContaining([ - expect.objectContaining({ - rules_count: 0, - price_list: expect.objectContaining({ - id: expect.any(String), - }), - money_amount: expect.objectContaining({ - amount: 123, - currency_code: "EUR", - }), - }), - ]), - price_list_rules: [], + expect(error.message).toEqual( + "Cannot set price list starts at with with invalid date string: invalid-date" + ) }) - ) - }) - it("should fail to add a price with non-existing rule-types in the price-set to a priceList", async () => { - await service.createRuleTypes([ - { - name: "twitter_handle", - rule_attribute: "twitter_handle", - }, - ]) - - let error - try { - await service.addPriceListPrices([ - { - priceListId: "price-list-1", - prices: [ + it("should fail to update a priceList with invalid ends_at date", async () => { + let error + try { + await service.updatePriceLists([ { - amount: 123, - currency_code: "EUR", - price_set_id: "price-set-1", - rules: { - twitter_handle: "owjuhl", - }, + id: createdId, + ends_at: "invalid-date", }, - ], - }, - ]) - } catch (err) { - error = err - } + ]) + } catch (err) { + error = err + } - expect(error.message).toEqual( - "" + - `Invalid rule type configuration: Price set rules doesn't exist for rule_attribute "twitter_handle" in price set price-set-1` - ) - }) + expect(error.message).toEqual( + "Cannot set price list ends at with with invalid date string: invalid-date" + ) + }) - it("should add a price with rules to a priceList successfully", async () => { - await service.createRuleTypes([ - { - name: "region_id", - rule_attribute: "region_id", - }, - ]) - - const r = await service.addRules([ - { - priceSetId: "price-set-1", - rules: [{ attribute: "region_id" }], - }, - ]) - - await service.addPriceListPrices([ - { - priceListId: "price-list-1", - prices: [ + it("should update a priceList with starts_at and ends_at dates given as string", async () => { + let [priceList] = await service.updatePriceLists([ { - amount: 123, - currency_code: "EUR", - price_set_id: "price-set-1", + id: createdId, + starts_at: "10/10/2010", + ends_at: "10/20/2030", + }, + ]) + expect(priceList).toEqual( + expect.objectContaining({ + starts_at: new Date("10/10/2010").toISOString(), + ends_at: new Date("10/20/2030").toISOString(), + }) + ) + }) + + it("should update the starts_at date of the priceList successfully", async () => { + const updateDate = new Date() + await service.updatePriceLists([ + { + id: createdId, + starts_at: updateDate, rules: { - region_id: "EU", + new_rule: ["new-rule-value"], }, }, - ], - }, - ]) + ]) - const [priceList] = await service.listPriceLists( - { - id: ["price-list-1"], - }, - { - relations: [ - "price_set_money_amounts.money_amount", - "price_set_money_amounts.price_set", - "price_set_money_amounts.price_rules", - "price_set_money_amounts.price_rules.rule_type", - "price_list_rules.price_list_rule_values", - "price_list_rules.rule_type", - ], - select: [ - "id", - "price_set_money_amounts.price_rules.value", - "price_set_money_amounts.price_rules.rule_type.rule_attribute", - "price_set_money_amounts.rules_count", - "price_set_money_amounts.money_amount.amount", - "price_set_money_amounts.money_amount.currency_code", - "price_set_money_amounts.money_amount.price_list_id", - "price_list_rules.price_list_rule_values.value", - "price_list_rules.rule_type.rule_attribute", - ], - } - ) + const [priceList] = await service.listPriceLists( + { + id: [createdId], + }, + { + relations: [ + "price_set_money_amounts.money_amount", + "price_set_money_amounts.price_set", + "price_list_rules.price_list_rule_values", + "price_list_rules.rule_type", + ], + select: [ + "id", + "starts_at", + "price_set_money_amounts.money_amount.amount", + "price_set_money_amounts.money_amount.currency_code", + "price_set_money_amounts.money_amount.price_list_id", + "price_list_rules.price_list_rule_values.value", + "price_list_rules.rule_type.rule_attribute", + ], + } + ) - expect(priceList).toEqual( - expect.objectContaining({ - id: expect.any(String), - price_set_money_amounts: expect.arrayContaining([ + expect(priceList).toEqual( expect.objectContaining({ - rules_count: 1, - price_list: expect.objectContaining({ - id: expect.any(String), - }), - price_rules: [ + id: expect.any(String), + starts_at: updateDate, + price_set_money_amounts: expect.arrayContaining([ expect.objectContaining({ - value: "EU", - rule_type: expect.objectContaining({ - rule_attribute: "region_id", + price_list: expect.objectContaining({ + id: expect.any(String), + }), + money_amount: expect.objectContaining({ + amount: 400, + currency_code: "EUR", }), }), - ], - money_amount: expect.objectContaining({ - amount: 123, - currency_code: "EUR", - }), - }), - ]), - price_list_rules: [], + ]), + price_list_rules: expect.arrayContaining([ + expect.objectContaining({ + id: expect.any(String), + rule_type: expect.objectContaining({ + id: expect.any(String), + rule_attribute: "new_rule", + }), + price_list_rule_values: [ + expect.objectContaining({ + id: expect.any(String), + value: "new-rule-value", + }), + ], + }), + ]), + }) + ) }) - ) + + it("should throw an error when a id does not exist", async () => { + let error + + try { + await service.updatePriceLists([ + { + id: "does-not-exist", + rules_count: 2, + }, + ]) + } catch (e) { + error = e + } + + expect(error.message).toEqual( + 'PriceList with id "does-not-exist" not found' + ) + }) + }) + + describe("createPriceLists", () => { + it("should fail to create a priceList with invalid starts_at date", async () => { + let error + try { + await service.createPriceLists([ + { + title: "test", + description: "test", + starts_at: "invalid-date", + }, + ]) + } catch (err) { + error = err + } + + expect(error.message).toEqual( + "Cannot set price list starts at with with invalid date string: invalid-date" + ) + }) + + it("should fail to create a priceList with invalid ends_at date", async () => { + let error + try { + await service.createPriceLists([ + { + title: "test", + description: "test", + ends_at: "invalid-date", + }, + ]) + } catch (err) { + error = err + } + + expect(error.message).toEqual( + "Cannot set price list ends at with with invalid date string: invalid-date" + ) + }) + + it("should create a priceList with starts_at and ends_at dates given as string", async () => { + let [priceList] = await service.createPriceLists([ + { + title: "test", + description: "test", + starts_at: "10/10/2010", + ends_at: "10/20/2030", + }, + ]) + expect(priceList).toEqual( + expect.objectContaining({ + starts_at: new Date("10/10/2010").toISOString(), + ends_at: new Date("10/20/2030").toISOString(), + }) + ) + }) + + it("should create a priceList successfully", async () => { + const [created] = await service.createPriceLists([ + { + title: "test", + description: "test", + starts_at: new Date("10/01/2023"), + ends_at: new Date("10/30/2023"), + rules: { + customer_group_id: [ + "vip-customer-group-id", + "another-vip-customer-group-id", + ], + region_id: ["DE", "DK"], + }, + prices: [ + { + amount: 400, + currency_code: "EUR", + price_set_id: "price-set-1", + }, + ], + }, + ]) + + const [priceList] = await service.listPriceLists( + { + id: [created.id], + }, + { + relations: [ + "price_set_money_amounts.money_amount", + "price_set_money_amounts.price_set", + "price_list_rules.price_list_rule_values", + "price_list_rules.rule_type", + ], + select: [ + "id", + "price_set_money_amounts.money_amount.amount", + "price_set_money_amounts.money_amount.currency_code", + "price_set_money_amounts.money_amount.price_list_id", + "price_list_rules.price_list_rule_values.value", + "price_list_rules.rule_type.rule_attribute", + ], + } + ) + + expect(priceList).toEqual( + expect.objectContaining({ + id: expect.any(String), + price_set_money_amounts: expect.arrayContaining([ + expect.objectContaining({ + price_list: expect.objectContaining({ + id: expect.any(String), + }), + money_amount: expect.objectContaining({ + amount: 400, + currency_code: "EUR", + }), + }), + ]), + price_list_rules: expect.arrayContaining([ + expect.objectContaining({ + id: expect.any(String), + rule_type: expect.objectContaining({ + id: expect.any(String), + rule_attribute: "customer_group_id", + }), + price_list_rule_values: expect.arrayContaining([ + expect.objectContaining({ + id: expect.any(String), + value: "vip-customer-group-id", + }), + expect.objectContaining({ + id: expect.any(String), + value: "another-vip-customer-group-id", + }), + ]), + }), + expect.objectContaining({ + id: expect.any(String), + rule_type: expect.objectContaining({ + id: expect.any(String), + rule_attribute: "region_id", + }), + price_list_rule_values: expect.arrayContaining([ + expect.objectContaining({ + id: expect.any(String), + value: "DE", + }), + expect.objectContaining({ + id: expect.any(String), + value: "DK", + }), + ]), + }), + ]), + }) + ) + }) + + it("should create a price list with granular rules within prices", async () => { + const [created] = await service.createPriceLists([ + { + title: "test", + description: "test", + starts_at: "10/01/2023", + ends_at: "10/30/2023", + rules: { + customer_group_id: [ + "vip-customer-group-id", + "another-vip-customer-group-id", + ], + region_id: ["DE", "DK"], + }, + prices: [ + { + amount: 400, + currency_code: "EUR", + price_set_id: "price-set-1", + rules: { + region_id: "DE", + }, + }, + { + amount: 600, + currency_code: "EUR", + price_set_id: "price-set-1", + }, + ], + }, + ]) + + const [priceList] = await service.listPriceLists( + { + id: [created.id], + }, + { + relations: [ + "price_set_money_amounts.money_amount", + "price_set_money_amounts.price_set", + "price_set_money_amounts.price_rules", + "price_list_rules.price_list_rule_values", + "price_list_rules.rule_type", + ], + select: [ + "id", + "price_set_money_amounts.price_rules.value", + "price_set_money_amounts.rules_count", + "price_set_money_amounts.money_amount.amount", + "price_set_money_amounts.money_amount.currency_code", + "price_set_money_amounts.money_amount.price_list_id", + "price_list_rules.price_list_rule_values.value", + "price_list_rules.rule_type.rule_attribute", + ], + } + ) + + expect(priceList).toEqual( + expect.objectContaining({ + id: expect.any(String), + price_set_money_amounts: expect.arrayContaining([ + expect.objectContaining({ + rules_count: 1, + price_rules: expect.arrayContaining([ + expect.objectContaining({ + id: expect.any(String), + value: "DE", + }), + ]), + price_list: expect.objectContaining({ + id: expect.any(String), + }), + money_amount: expect.objectContaining({ + amount: 400, + currency_code: "EUR", + }), + }), + expect.objectContaining({ + rules_count: 0, + price_rules: [], + price_list: expect.objectContaining({ + id: expect.any(String), + }), + money_amount: expect.objectContaining({ + amount: 600, + currency_code: "EUR", + }), + }), + ]), + price_list_rules: expect.arrayContaining([ + expect.objectContaining({ + id: expect.any(String), + rule_type: expect.objectContaining({ + id: expect.any(String), + rule_attribute: "customer_group_id", + }), + price_list_rule_values: expect.arrayContaining([ + expect.objectContaining({ + id: expect.any(String), + value: "vip-customer-group-id", + }), + expect.objectContaining({ + id: expect.any(String), + value: "another-vip-customer-group-id", + }), + ]), + }), + expect.objectContaining({ + id: expect.any(String), + rule_type: expect.objectContaining({ + id: expect.any(String), + rule_attribute: "region_id", + }), + price_list_rule_values: expect.arrayContaining([ + expect.objectContaining({ + id: expect.any(String), + value: "DE", + }), + expect.objectContaining({ + id: expect.any(String), + value: "DK", + }), + ]), + }), + ]), + }) + ) + }) + + it("should throw error when rule type does not exist", async () => { + const error = await service + .createPriceLists([ + { + title: "test", + description: "test", + rules: { + region_id: ["DE", "DK"], + missing_1: ["test-missing-1"], + }, + prices: [ + { + amount: 400, + currency_code: "EUR", + price_set_id: "price-set-1", + rules: { + region_id: "DE", + missing_2: "test-missing-2", + }, + }, + ], + }, + ]) + .catch((e) => e) + + expect(error.message).toEqual( + "Cannot find RuleTypes with rule_attribute - missing_1, missing_2" + ) + }) + }) + + describe("addPriceListPrices", () => { + it("should add a price to a priceList successfully", async () => { + await service.addPriceListPrices([ + { + priceListId: "price-list-1", + prices: [ + { + amount: 123, + currency_code: "EUR", + price_set_id: "price-set-1", + }, + ], + }, + ]) + + const [priceList] = await service.listPriceLists( + { + id: ["price-list-1"], + }, + { + relations: [ + "price_set_money_amounts.money_amount", + "price_set_money_amounts.price_set", + "price_set_money_amounts.price_rules", + "price_list_rules.price_list_rule_values", + "price_list_rules.rule_type", + ], + select: [ + "id", + "price_set_money_amounts.price_rules.value", + "price_set_money_amounts.rules_count", + "price_set_money_amounts.money_amount.amount", + "price_set_money_amounts.money_amount.currency_code", + "price_set_money_amounts.money_amount.price_list_id", + "price_list_rules.price_list_rule_values.value", + "price_list_rules.rule_type.rule_attribute", + ], + } + ) + + expect(priceList).toEqual( + expect.objectContaining({ + id: expect.any(String), + price_set_money_amounts: expect.arrayContaining([ + expect.objectContaining({ + rules_count: 0, + price_list: expect.objectContaining({ + id: expect.any(String), + }), + money_amount: expect.objectContaining({ + amount: 123, + currency_code: "EUR", + }), + }), + ]), + price_list_rules: [], + }) + ) + }) + + it("should fail to add a price with non-existing rule-types in the price-set to a priceList", async () => { + await service.createRuleTypes([ + { + name: "twitter_handle", + rule_attribute: "twitter_handle", + }, + ]) + + let error + try { + await service.addPriceListPrices([ + { + priceListId: "price-list-1", + prices: [ + { + amount: 123, + currency_code: "EUR", + price_set_id: "price-set-1", + rules: { + twitter_handle: "owjuhl", + }, + }, + ], + }, + ]) + } catch (err) { + error = err + } + + expect(error.message).toEqual( + "" + + `Invalid rule type configuration: Price set rules doesn't exist for rule_attribute "twitter_handle" in price set price-set-1` + ) + }) + + it("should add a price with rules to a priceList successfully", async () => { + await service.createRuleTypes([ + { + name: "region_id", + rule_attribute: "region_id", + }, + ]) + + const r = await service.addRules([ + { + priceSetId: "price-set-1", + rules: [{ attribute: "region_id" }], + }, + ]) + + await service.addPriceListPrices([ + { + priceListId: "price-list-1", + prices: [ + { + amount: 123, + currency_code: "EUR", + price_set_id: "price-set-1", + rules: { + region_id: "EU", + }, + }, + ], + }, + ]) + + const [priceList] = await service.listPriceLists( + { + id: ["price-list-1"], + }, + { + relations: [ + "price_set_money_amounts.money_amount", + "price_set_money_amounts.price_set", + "price_set_money_amounts.price_rules", + "price_set_money_amounts.price_rules.rule_type", + "price_list_rules.price_list_rule_values", + "price_list_rules.rule_type", + ], + select: [ + "id", + "price_set_money_amounts.price_rules.value", + "price_set_money_amounts.price_rules.rule_type.rule_attribute", + "price_set_money_amounts.rules_count", + "price_set_money_amounts.money_amount.amount", + "price_set_money_amounts.money_amount.currency_code", + "price_set_money_amounts.money_amount.price_list_id", + "price_list_rules.price_list_rule_values.value", + "price_list_rules.rule_type.rule_attribute", + ], + } + ) + + expect(priceList).toEqual( + expect.objectContaining({ + id: expect.any(String), + price_set_money_amounts: expect.arrayContaining([ + expect.objectContaining({ + rules_count: 1, + price_list: expect.objectContaining({ + id: expect.any(String), + }), + price_rules: [ + expect.objectContaining({ + value: "EU", + rule_type: expect.objectContaining({ + rule_attribute: "region_id", + }), + }), + ], + money_amount: expect.objectContaining({ + amount: 123, + currency_code: "EUR", + }), + }), + ]), + price_list_rules: [], + }) + ) + }) + }) }) - }) + }, }) diff --git a/packages/pricing/integration-tests/__tests__/services/pricing-module/price-rule.spec.ts b/packages/pricing/integration-tests/__tests__/services/pricing-module/price-rule.spec.ts index 3895c3abf7..b2750149ab 100644 --- a/packages/pricing/integration-tests/__tests__/services/pricing-module/price-rule.spec.ts +++ b/packages/pricing/integration-tests/__tests__/services/pricing-module/price-rule.spec.ts @@ -8,330 +8,316 @@ import { createPriceSets } from "../../../__fixtures__/price-set" import { createPriceSetMoneyAmounts } from "../../../__fixtures__/price-set-money-amount" import { createPriceSetMoneyAmountRules } from "../../../__fixtures__/price-set-money-amount-rules" import { createRuleTypes } from "../../../__fixtures__/rule-type" -import { MikroOrmWrapper } from "../../../utils" -import { getInitModuleConfig } from "../../../utils/get-init-module-config" -import { initModules } from "medusa-test-utils" import { Modules } from "@medusajs/modules-sdk" +import { moduleIntegrationTestRunner, SuiteOptions } from "medusa-test-utils" jest.setTimeout(30000) -describe("PricingModule Service - PriceRule", () => { - let service: IPricingModuleService - let testManager: SqlEntityManager - let shutdownFunc: () => Promise +moduleIntegrationTestRunner({ + moduleName: Modules.PRICING, + testSuite: ({ + MikroOrmWrapper, + service, + }: SuiteOptions) => { + describe("PricingModule Service - PriceRule", () => { + let testManager: SqlEntityManager + beforeEach(async () => { + testManager = await MikroOrmWrapper.forkManager() - beforeAll(async () => { - const initModulesConfig = getInitModuleConfig() - - const { medusaApp, shutdown } = await initModules(initModulesConfig) - - service = medusaApp.modules[Modules.PRICING] - - shutdownFunc = shutdown - }) - - afterAll(async () => { - await shutdownFunc() - }) - - beforeEach(async () => { - await MikroOrmWrapper.setupDatabase() - testManager = MikroOrmWrapper.forkManager() - - await createMoneyAmounts(testManager) - await createPriceSets(testManager) - await createRuleTypes(testManager) - await createPriceSetMoneyAmounts(testManager) - await createPriceSetMoneyAmountRules(testManager) - await createPriceRules(testManager) - }) - - afterEach(async () => { - await MikroOrmWrapper.clearDatabase() - }) - - describe("list", () => { - it("should list priceRules", async () => { - const PriceRulesResult = await service.listPriceRules() - const serialized = JSON.parse(JSON.stringify(PriceRulesResult)) - - expect(serialized).toEqual([ - expect.objectContaining({ - id: "price-rule-1", - }), - expect.objectContaining({ - id: "price-rule-2", - }), - ]) - }) - - it("should list priceRules by id", async () => { - const priceRuleResult = await service.listPriceRules({ - id: ["price-rule-1"], + await createMoneyAmounts(testManager) + await createPriceSets(testManager) + await createRuleTypes(testManager) + await createPriceSetMoneyAmounts(testManager) + await createPriceSetMoneyAmountRules(testManager) + await createPriceRules(testManager) }) - expect(priceRuleResult).toEqual([ - expect.objectContaining({ - id: "price-rule-1", - }), - ]) - }) + describe("list", () => { + it("should list priceRules", async () => { + const PriceRulesResult = await service.listPriceRules() + const serialized = JSON.parse(JSON.stringify(PriceRulesResult)) - it("should list priceRules with relations and selects", async () => { - const priceRulesResult = await service.listPriceRules( - { - id: ["price-rule-1"], - }, - { - select: ["id", "price_set.id"], - relations: ["price_set"], - } - ) - - const serialized = JSON.parse(JSON.stringify(priceRulesResult)) - - expect(serialized).toEqual([ - { - id: "price-rule-1", - price_set: { - id: "price-set-1", - }, - }, - ]) - }) - - describe("listAndCount", () => { - it("should return priceRules and count", async () => { - const [priceRulesResult, count] = await service.listAndCountPriceRules() - - expect(count).toEqual(2) - expect(priceRulesResult).toEqual([ - expect.objectContaining({ - id: "price-rule-1", - }), - expect.objectContaining({ - id: "price-rule-2", - }), - ]) - }) - - it("should return priceRules and count when filtered", async () => { - const [priceRulesResult, count] = await service.listAndCountPriceRules({ - id: ["price-rule-1"], + expect(serialized).toEqual([ + expect.objectContaining({ + id: "price-rule-1", + }), + expect.objectContaining({ + id: "price-rule-2", + }), + ]) }) - expect(count).toEqual(1) - expect(priceRulesResult).toEqual([ - expect.objectContaining({ - id: "price-rule-1", - }), - ]) - }) - - it("should list PriceRules with relations and selects", async () => { - const [PriceRulesResult, count] = await service.listAndCountPriceRules( - { + it("should list priceRules by id", async () => { + const priceRuleResult = await service.listPriceRules({ id: ["price-rule-1"], - }, - { - select: ["id", "price_set.id"], - relations: ["price_set"], - } - ) - - const serialized = JSON.parse(JSON.stringify(PriceRulesResult)) - - expect(count).toEqual(1) - expect(serialized).toEqual([ - { - id: "price-rule-1", - price_set: { - id: "price-set-1", - }, - }, - ]) - }) - - it("should return PriceRules and count when using skip and take", async () => { - const [PriceRulesResult, count] = await service.listAndCountPriceRules( - {}, - { skip: 1, take: 1 } - ) - - expect(count).toEqual(2) - expect(PriceRulesResult).toEqual([ - expect.objectContaining({ - id: "price-rule-2", - }), - ]) - }) - - it("should return requested fields", async () => { - const [PriceRulesResult, count] = await service.listAndCountPriceRules( - {}, - { - take: 1, - select: ["id"], - } - ) - - const serialized = JSON.parse(JSON.stringify(PriceRulesResult)) - - expect(count).toEqual(2) - expect(serialized).toEqual([ - { - id: "price-rule-1", - }, - ]) - }) - }) - - describe("retrieve", () => { - const id = "price-rule-1" - - it("should return PriceRule for the given id", async () => { - const PriceRule = await service.retrievePriceRule(id) - - expect(PriceRule).toEqual( - expect.objectContaining({ - id, }) - ) - }) - it("should throw an error when PriceRule with id does not exist", async () => { - let error - - try { - await service.retrievePriceRule("does-not-exist") - } catch (e) { - error = e - } - - expect(error.message).toEqual( - "PriceRule with id: does-not-exist was not found" - ) - }) - - it("should throw an error when a id is not provided", async () => { - let error - - try { - await service.retrievePriceRule(undefined as unknown as string) - } catch (e) { - error = e - } - - expect(error.message).toEqual("priceRule - id must be defined") - }) - - it("should return PriceRule based on config select param", async () => { - const PriceRule = await service.retrievePriceRule(id, { - select: ["id"], + expect(priceRuleResult).toEqual([ + expect.objectContaining({ + id: "price-rule-1", + }), + ]) }) - const serialized = JSON.parse(JSON.stringify(PriceRule)) - - expect(serialized).toEqual({ - id, - }) - }) - }) - - describe("delete", () => { - const id = "price-set-1" - - it("should delete the PriceRules given an id successfully", async () => { - await service.deletePriceRules([id]) - - const PriceRules = await service.listPriceRules({ - id: [id], - }) - - expect(PriceRules).toHaveLength(0) - }) - }) - - describe("update", () => { - const id = "price-set-1" - - it("should throw an error when a id does not exist", async () => { - let error - - try { - await service.updatePriceRules([ + it("should list priceRules with relations and selects", async () => { + const priceRulesResult = await service.listPriceRules( { - id: "does-not-exist", + id: ["price-rule-1"], + }, + { + select: ["id", "price_set.id"], + relations: ["price_set"], + } + ) + + const serialized = JSON.parse(JSON.stringify(priceRulesResult)) + + expect(serialized).toEqual([ + { + id: "price-rule-1", + price_set: { + id: "price-set-1", + }, }, ]) - } catch (e) { - error = e - } - - expect(error.message).toEqual( - 'PriceRule with id "does-not-exist" not found' - ) - }) - }) - - describe("create", () => { - it("should throw an error when a id does not exist", async () => { - let error - - try { - await service.updatePriceRules([ - { - random: "does-not-exist", - } as any, - ]) - } catch (e) { - error = e - } - - expect(error.message).toEqual('PriceRule with id "" not found') - }) - - it("should create a PriceRule successfully", async () => { - const [ma] = await createMoneyAmounts(testManager, [ - { - amount: 100, - currency_code: "EUR", - }, - ]) - - const psma: PriceSetMoneyAmount = testManager.create( - PriceSetMoneyAmount, - { - price_set: "price-set-1", - money_amount: ma.id, - title: "test", - rules_count: 0, - } - ) - - await testManager.persist(psma).flush() - - await service.createPriceRules([ - { - id: "price-rule-new", - price_set_id: "price-set-1", - rule_type_id: "rule-type-1", - value: "region_1", - price_list_id: "test", - price_set_money_amount_id: psma.id, - } as unknown as CreatePriceRuleDTO, - ]) - - const [pricerule] = await service.listPriceRules({ - id: ["price-rule-new"], }) - expect(pricerule).toEqual( - expect.objectContaining({ - id: "price-rule-new", - } as unknown as CreatePriceRuleDTO) - ) + describe("listAndCount", () => { + it("should return priceRules and count", async () => { + const [priceRulesResult, count] = + await service.listAndCountPriceRules() + + expect(count).toEqual(2) + expect(priceRulesResult).toEqual([ + expect.objectContaining({ + id: "price-rule-1", + }), + expect.objectContaining({ + id: "price-rule-2", + }), + ]) + }) + + it("should return priceRules and count when filtered", async () => { + const [priceRulesResult, count] = + await service.listAndCountPriceRules({ + id: ["price-rule-1"], + }) + + expect(count).toEqual(1) + expect(priceRulesResult).toEqual([ + expect.objectContaining({ + id: "price-rule-1", + }), + ]) + }) + + it("should list PriceRules with relations and selects", async () => { + const [PriceRulesResult, count] = + await service.listAndCountPriceRules( + { + id: ["price-rule-1"], + }, + { + select: ["id", "price_set.id"], + relations: ["price_set"], + } + ) + + const serialized = JSON.parse(JSON.stringify(PriceRulesResult)) + + expect(count).toEqual(1) + expect(serialized).toEqual([ + { + id: "price-rule-1", + price_set: { + id: "price-set-1", + }, + }, + ]) + }) + + it("should return PriceRules and count when using skip and take", async () => { + const [PriceRulesResult, count] = + await service.listAndCountPriceRules({}, { skip: 1, take: 1 }) + + expect(count).toEqual(2) + expect(PriceRulesResult).toEqual([ + expect.objectContaining({ + id: "price-rule-2", + }), + ]) + }) + + it("should return requested fields", async () => { + const [PriceRulesResult, count] = + await service.listAndCountPriceRules( + {}, + { + take: 1, + select: ["id"], + } + ) + + const serialized = JSON.parse(JSON.stringify(PriceRulesResult)) + + expect(count).toEqual(2) + expect(serialized).toEqual([ + { + id: "price-rule-1", + }, + ]) + }) + }) + + describe("retrieve", () => { + const id = "price-rule-1" + + it("should return PriceRule for the given id", async () => { + const PriceRule = await service.retrievePriceRule(id) + + expect(PriceRule).toEqual( + expect.objectContaining({ + id, + }) + ) + }) + + it("should throw an error when PriceRule with id does not exist", async () => { + let error + + try { + await service.retrievePriceRule("does-not-exist") + } catch (e) { + error = e + } + + expect(error.message).toEqual( + "PriceRule with id: does-not-exist was not found" + ) + }) + + it("should throw an error when a id is not provided", async () => { + let error + + try { + await service.retrievePriceRule(undefined as unknown as string) + } catch (e) { + error = e + } + + expect(error.message).toEqual("priceRule - id must be defined") + }) + + it("should return PriceRule based on config select param", async () => { + const PriceRule = await service.retrievePriceRule(id, { + select: ["id"], + }) + + const serialized = JSON.parse(JSON.stringify(PriceRule)) + + expect(serialized).toEqual({ + id, + }) + }) + }) + + describe("delete", () => { + const id = "price-set-1" + + it("should delete the PriceRules given an id successfully", async () => { + await service.deletePriceRules([id]) + + const PriceRules = await service.listPriceRules({ + id: [id], + }) + + expect(PriceRules).toHaveLength(0) + }) + }) + + describe("update", () => { + const id = "price-set-1" + + it("should throw an error when a id does not exist", async () => { + let error + + try { + await service.updatePriceRules([ + { + id: "does-not-exist", + }, + ]) + } catch (e) { + error = e + } + + expect(error.message).toEqual( + 'PriceRule with id "does-not-exist" not found' + ) + }) + }) + + describe("create", () => { + it("should throw an error when a id does not exist", async () => { + let error + + try { + await service.updatePriceRules([ + { + random: "does-not-exist", + } as any, + ]) + } catch (e) { + error = e + } + + expect(error.message).toEqual('PriceRule with id "" not found') + }) + + it("should create a PriceRule successfully", async () => { + const [ma] = await createMoneyAmounts(testManager, [ + { + amount: 100, + currency_code: "EUR", + }, + ]) + + const psma: PriceSetMoneyAmount = testManager.create( + PriceSetMoneyAmount, + { + price_set: "price-set-1", + money_amount: ma.id, + title: "test", + rules_count: 0, + } + ) + + await testManager.persist(psma).flush() + + await service.createPriceRules([ + { + id: "price-rule-new", + price_set_id: "price-set-1", + rule_type_id: "rule-type-1", + value: "region_1", + price_list_id: "test", + price_set_money_amount_id: psma.id, + } as unknown as CreatePriceRuleDTO, + ]) + + const [pricerule] = await service.listPriceRules({ + id: ["price-rule-new"], + }) + + expect(pricerule).toEqual( + expect.objectContaining({ + id: "price-rule-new", + } as unknown as CreatePriceRuleDTO) + ) + }) + }) }) }) - }) + }, }) diff --git a/packages/pricing/integration-tests/__tests__/services/pricing-module/price-set-money-amount-rules.spec.ts b/packages/pricing/integration-tests/__tests__/services/pricing-module/price-set-money-amount-rules.spec.ts index 75f8535634..63029afcda 100644 --- a/packages/pricing/integration-tests/__tests__/services/pricing-module/price-set-money-amount-rules.spec.ts +++ b/packages/pricing/integration-tests/__tests__/services/pricing-module/price-set-money-amount-rules.spec.ts @@ -5,293 +5,274 @@ import { createPriceSets } from "../../../__fixtures__/price-set" import { createPriceSetMoneyAmounts } from "../../../__fixtures__/price-set-money-amount" import { createPriceSetMoneyAmountRules } from "../../../__fixtures__/price-set-money-amount-rules" import { createRuleTypes } from "../../../__fixtures__/rule-type" -import { MikroOrmWrapper } from "../../../utils" -import { getInitModuleConfig } from "../../../utils/get-init-module-config" -import { initModules } from "medusa-test-utils" import { Modules } from "@medusajs/modules-sdk" +import { moduleIntegrationTestRunner, SuiteOptions } from "medusa-test-utils" jest.setTimeout(30000) -describe("PricingModule Service - PriceSetMoneyAmountRules", () => { - let service: IPricingModuleService - let testManager: SqlEntityManager - let repositoryManager: SqlEntityManager - let shutdownFunc: () => Promise +moduleIntegrationTestRunner({ + moduleName: Modules.PRICING, + testSuite: ({ + MikroOrmWrapper, + service, + }: SuiteOptions) => { + describe("PricingModule Service - PriceSetMoneyAmountRules", () => { + beforeEach(async () => { + const testManager = await MikroOrmWrapper.forkManager() - beforeAll(async () => { - const initModulesConfig = getInitModuleConfig() + await createMoneyAmounts(testManager) + await createPriceSets(testManager) + await createRuleTypes(testManager) + await createPriceSetMoneyAmounts(testManager) + await createPriceSetMoneyAmountRules(testManager) + }) - const { medusaApp, shutdown } = await initModules(initModulesConfig) + describe("listPriceSetMoneyAmountRules", () => { + it("should list psmar records", async () => { + const priceSetMoneyAmountRulesResult = + await service.listPriceSetMoneyAmountRules() - service = medusaApp.modules[Modules.PRICING] - - shutdownFunc = shutdown - }) - - afterAll(async () => { - await shutdownFunc() - }) - - beforeEach(async () => { - await MikroOrmWrapper.setupDatabase() - repositoryManager = await MikroOrmWrapper.forkManager() - testManager = await MikroOrmWrapper.forkManager() - - await createMoneyAmounts(testManager) - await createPriceSets(testManager) - await createRuleTypes(testManager) - await createPriceSetMoneyAmounts(testManager) - await createPriceSetMoneyAmountRules(testManager) - }) - - afterEach(async () => { - await MikroOrmWrapper.clearDatabase() - }) - - describe("listPriceSetMoneyAmountRules", () => { - it("should list psmar records", async () => { - const priceSetMoneyAmountRulesResult = - await service.listPriceSetMoneyAmountRules() - - expect(priceSetMoneyAmountRulesResult).toEqual([ - expect.objectContaining({ - id: "psmar-1", - }), - expect.objectContaining({ - id: "psmar-2", - }), - expect.objectContaining({ - id: "psmar-3", - }), - ]) - }) - - it("should list psmar record by id", async () => { - const priceSetMoneyAmountRulesResult = - await service.listPriceSetMoneyAmountRules({ - id: ["psmar-1"], + expect(priceSetMoneyAmountRulesResult).toEqual([ + expect.objectContaining({ + id: "psmar-1", + }), + expect.objectContaining({ + id: "psmar-2", + }), + expect.objectContaining({ + id: "psmar-3", + }), + ]) }) - expect(priceSetMoneyAmountRulesResult).toEqual([ - expect.objectContaining({ - id: "psmar-1", - }), - ]) - }) - }) + it("should list psmar record by id", async () => { + const priceSetMoneyAmountRulesResult = + await service.listPriceSetMoneyAmountRules({ + id: ["psmar-1"], + }) - describe("listAndCount", () => { - it("should return psmar records and count", async () => { - const [priceSetMoneyAmountRulesResult, count] = - await service.listAndCountPriceSetMoneyAmountRules() + expect(priceSetMoneyAmountRulesResult).toEqual([ + expect.objectContaining({ + id: "psmar-1", + }), + ]) + }) + }) - expect(count).toEqual(3) - expect(priceSetMoneyAmountRulesResult).toEqual([ - expect.objectContaining({ - id: "psmar-1", - }), - expect.objectContaining({ - id: "psmar-2", - }), - expect.objectContaining({ - id: "psmar-3", - }), - ]) - }) + describe("listAndCount", () => { + it("should return psmar records and count", async () => { + const [priceSetMoneyAmountRulesResult, count] = + await service.listAndCountPriceSetMoneyAmountRules() - it("should return psmar records and count when filtered", async () => { - const [priceSetMoneyAmountRulesResult, count] = - await service.listAndCountPriceSetMoneyAmountRules({ - id: ["psmar-1"], + expect(count).toEqual(3) + expect(priceSetMoneyAmountRulesResult).toEqual([ + expect.objectContaining({ + id: "psmar-1", + }), + expect.objectContaining({ + id: "psmar-2", + }), + expect.objectContaining({ + id: "psmar-3", + }), + ]) }) - expect(count).toEqual(1) - expect(priceSetMoneyAmountRulesResult).toEqual([ - expect.objectContaining({ - id: "psmar-1", - }), - ]) - }) + it("should return psmar records and count when filtered", async () => { + const [priceSetMoneyAmountRulesResult, count] = + await service.listAndCountPriceSetMoneyAmountRules({ + id: ["psmar-1"], + }) - it("should return psmar and count when using skip and take", async () => { - const [priceSetMoneyAmountRulesResult, count] = - await service.listAndCountPriceSetMoneyAmountRules( - {}, - { skip: 1, take: 1 } - ) + expect(count).toEqual(1) + expect(priceSetMoneyAmountRulesResult).toEqual([ + expect.objectContaining({ + id: "psmar-1", + }), + ]) + }) - expect(count).toEqual(3) - expect(priceSetMoneyAmountRulesResult).toEqual([ - expect.objectContaining({ - id: "psmar-2", - }), - ]) - }) + it("should return psmar and count when using skip and take", async () => { + const [priceSetMoneyAmountRulesResult, count] = + await service.listAndCountPriceSetMoneyAmountRules( + {}, + { skip: 1, take: 1 } + ) - it("should return requested fields", async () => { - const [priceSetMoneyAmountRulesResult, count] = - await service.listAndCountPriceSetMoneyAmountRules( - {}, - { - take: 1, - select: ["value"], + expect(count).toEqual(3) + expect(priceSetMoneyAmountRulesResult).toEqual([ + expect.objectContaining({ + id: "psmar-2", + }), + ]) + }) + + it("should return requested fields", async () => { + const [priceSetMoneyAmountRulesResult, count] = + await service.listAndCountPriceSetMoneyAmountRules( + {}, + { + take: 1, + select: ["value"], + } + ) + + const serialized = JSON.parse( + JSON.stringify(priceSetMoneyAmountRulesResult) + ) + + expect(count).toEqual(3) + expect(serialized).toEqual([ + { + id: "psmar-1", + value: "EUR", + }, + ]) + }) + }) + + describe("retrievePriceSetMoneyAmountRules", () => { + it("should return priceSetMoneyAmountRules for the given id", async () => { + const priceSetMoneyAmountRules = + await service.retrievePriceSetMoneyAmountRules("psmar-1") + + expect(priceSetMoneyAmountRules).toEqual( + expect.objectContaining({ + id: "psmar-1", + }) + ) + }) + + it("should throw an error when priceSetMoneyAmountRules with id does not exist", async () => { + let error + + try { + await service.retrievePriceSetMoneyAmountRules("does-not-exist") + } catch (e) { + error = e } - ) - const serialized = JSON.parse( - JSON.stringify(priceSetMoneyAmountRulesResult) - ) - - expect(count).toEqual(3) - expect(serialized).toEqual([ - { - id: "psmar-1", - value: "EUR", - }, - ]) - }) - }) - - describe("retrievePriceSetMoneyAmountRules", () => { - it("should return priceSetMoneyAmountRules for the given id", async () => { - const priceSetMoneyAmountRules = - await service.retrievePriceSetMoneyAmountRules("psmar-1") - - expect(priceSetMoneyAmountRules).toEqual( - expect.objectContaining({ - id: "psmar-1", - }) - ) - }) - - it("should throw an error when priceSetMoneyAmountRules with id does not exist", async () => { - let error - - try { - await service.retrievePriceSetMoneyAmountRules("does-not-exist") - } catch (e) { - error = e - } - - expect(error.message).toEqual( - "PriceSetMoneyAmountRules with id: does-not-exist was not found" - ) - }) - - it("should throw an error when an id is not provided", async () => { - let error - - try { - await service.retrievePriceSetMoneyAmountRules( - undefined as unknown as string - ) - } catch (e) { - error = e - } - - expect(error.message).toEqual( - "priceSetMoneyAmountRules - id must be defined" - ) - }) - - it("should return priceSetMoneyAmountRules based on config select param", async () => { - const priceSetMoneyAmountRulesResult = - await service.retrievePriceSetMoneyAmountRules("psmar-1", { - select: ["value"], + expect(error.message).toEqual( + "PriceSetMoneyAmountRules with id: does-not-exist was not found" + ) }) - const serialized = JSON.parse( - JSON.stringify(priceSetMoneyAmountRulesResult) - ) + it("should throw an error when an id is not provided", async () => { + let error - expect(serialized).toEqual({ - value: "EUR", - id: "psmar-1", - }) - }) - }) + try { + await service.retrievePriceSetMoneyAmountRules( + undefined as unknown as string + ) + } catch (e) { + error = e + } - describe("deletePriceSetMoneyAmountRules", () => { - const id = "psmar-1" + expect(error.message).toEqual( + "priceSetMoneyAmountRules - id must be defined" + ) + }) - it("should delete the priceSetMoneyAmountRuless given an id successfully", async () => { - await service.deletePriceSetMoneyAmountRules([id]) + it("should return priceSetMoneyAmountRules based on config select param", async () => { + const priceSetMoneyAmountRulesResult = + await service.retrievePriceSetMoneyAmountRules("psmar-1", { + select: ["value"], + }) - const currencies = await service.listPriceSetMoneyAmountRules({ - id: [id], + const serialized = JSON.parse( + JSON.stringify(priceSetMoneyAmountRulesResult) + ) + + expect(serialized).toEqual({ + value: "EUR", + id: "psmar-1", + }) + }) }) - expect(currencies).toHaveLength(0) - }) - }) + describe("deletePriceSetMoneyAmountRules", () => { + const id = "psmar-1" - describe("updatePriceSetMoneyAmountRules", () => { - const id = "psmar-1" + it("should delete the priceSetMoneyAmountRuless given an id successfully", async () => { + await service.deletePriceSetMoneyAmountRules([id]) - it("should update the value of the priceSetMoneyAmountRules successfully", async () => { - await service.updatePriceSetMoneyAmountRules([ - { - id, - value: "New value", - }, - ]) + const currencies = await service.listPriceSetMoneyAmountRules({ + id: [id], + }) - const psmar = await service.retrievePriceSetMoneyAmountRules(id) - - expect(psmar.value).toEqual("New value") - }) - - it("should throw an error when a id does not exist", async () => { - let error - - try { - await service.updatePriceSetMoneyAmountRules([ - { - id: "does-not-exist", - value: "random value", - }, - ]) - } catch (e) { - error = e - } - - expect(error.message).toEqual( - 'PriceSetMoneyAmountRules with id "does-not-exist" not found' - ) - }) - }) - - describe("createPriceSetMoneyAmountRules", () => { - it("should create a priceSetMoneyAmountRules successfully", async () => { - await service.createPriceSetMoneyAmountRules([ - { - price_set_money_amount: "price-set-money-amount-EUR", - rule_type: "rule-type-2", - value: "New priceSetMoneyAmountRule", - }, - ]) - - const [created] = await service.listPriceSetMoneyAmountRules( - { - value: ["New priceSetMoneyAmountRule"], - }, - { - relations: ["price_set_money_amount", "rule_type"], - } - ) - - expect(created).toEqual( - expect.objectContaining({ - id: expect.any(String), - value: "New priceSetMoneyAmountRule", - price_set_money_amount: expect.objectContaining({ - id: "price-set-money-amount-EUR", - }), - rule_type: expect.objectContaining({ - id: "rule-type-2", - }), + expect(currencies).toHaveLength(0) }) - ) + }) + + describe("updatePriceSetMoneyAmountRules", () => { + const id = "psmar-1" + + it("should update the value of the priceSetMoneyAmountRules successfully", async () => { + await service.updatePriceSetMoneyAmountRules([ + { + id, + value: "New value", + }, + ]) + + const psmar = await service.retrievePriceSetMoneyAmountRules(id) + + expect(psmar.value).toEqual("New value") + }) + + it("should throw an error when a id does not exist", async () => { + let error + + try { + await service.updatePriceSetMoneyAmountRules([ + { + id: "does-not-exist", + value: "random value", + }, + ]) + } catch (e) { + error = e + } + + expect(error.message).toEqual( + 'PriceSetMoneyAmountRules with id "does-not-exist" not found' + ) + }) + }) + + describe("createPriceSetMoneyAmountRules", () => { + it("should create a priceSetMoneyAmountRules successfully", async () => { + await service.createPriceSetMoneyAmountRules([ + { + price_set_money_amount: "price-set-money-amount-EUR", + rule_type: "rule-type-2", + value: "New priceSetMoneyAmountRule", + }, + ]) + + const [created] = await service.listPriceSetMoneyAmountRules( + { + value: ["New priceSetMoneyAmountRule"], + }, + { + relations: ["price_set_money_amount", "rule_type"], + } + ) + + expect(created).toEqual( + expect.objectContaining({ + id: expect.any(String), + value: "New priceSetMoneyAmountRule", + price_set_money_amount: expect.objectContaining({ + id: "price-set-money-amount-EUR", + }), + rule_type: expect.objectContaining({ + id: "rule-type-2", + }), + }) + ) + }) + }) }) - }) + }, }) diff --git a/packages/pricing/integration-tests/__tests__/services/pricing-module/price-set.spec.ts b/packages/pricing/integration-tests/__tests__/services/pricing-module/price-set.spec.ts index ac6ee27716..0902499da0 100644 --- a/packages/pricing/integration-tests/__tests__/services/pricing-module/price-set.spec.ts +++ b/packages/pricing/integration-tests/__tests__/services/pricing-module/price-set.spec.ts @@ -4,14 +4,11 @@ import { IPricingModuleService, } from "@medusajs/types" import { SqlEntityManager } from "@mikro-orm/postgresql" -import { PriceSet } from "@models" import { PriceSetRuleType } from "../../../../src" import { seedPriceData } from "../../../__fixtures__/seed-price-data" -import { MikroOrmWrapper } from "../../../utils" -import { getInitModuleConfig } from "../../../utils/get-init-module-config" -import { initModules } from "medusa-test-utils" import { Modules } from "@medusajs/modules-sdk" +import { moduleIntegrationTestRunner, SuiteOptions } from "medusa-test-utils" jest.setTimeout(30000) @@ -22,7 +19,7 @@ async function createPriceSetPriceRules( const priceSetRules: PriceSetRuleType[] = [] for (let priceSetRuleData of priceSetRulesData) { - const priceRule = manager.create(PriceSetRuleType, priceSetRuleData) + const priceRule = manager.create(PriceSetRuleType, priceSetRuleData as any) priceSetRules.push(priceRule) } @@ -30,753 +27,740 @@ async function createPriceSetPriceRules( await manager.persistAndFlush(priceSetRules) } -describe("PricingModule Service - PriceSet", () => { - let service: IPricingModuleService - let repositoryManager: SqlEntityManager - let data!: PriceSet[] - let shutdownFunc: () => Promise - - beforeAll(async () => { - const initModulesConfig = getInitModuleConfig() - - const { medusaApp, shutdown } = await initModules(initModulesConfig) - - service = medusaApp.modules[Modules.PRICING] - - shutdownFunc = shutdown - }) - - afterAll(async () => { - await shutdownFunc() - }) - - beforeEach(async () => { - await MikroOrmWrapper.setupDatabase() - repositoryManager = MikroOrmWrapper.forkManager() - const testManager = MikroOrmWrapper.forkManager() - - await seedPriceData(testManager) - await createPriceSetPriceRules(testManager, [ - { - price_set: "price-set-1", - rule_type: "rule-type-1", - }, - { - price_set: "price-set-2", - rule_type: "rule-type-2", - }, - ] as unknown as CreatePriceSetRuleTypeDTO[]) - }) - - afterEach(async () => { - await MikroOrmWrapper.clearDatabase() - }) - - describe("list", () => { - it("list priceSets", async () => { - const priceSetsResult = await service.list() - - expect(priceSetsResult).toEqual([ - expect.objectContaining({ - id: "price-set-1", - }), - expect.objectContaining({ - id: "price-set-2", - }), - expect.objectContaining({ - id: "price-set-3", - }), - ]) - }) - - it("list priceSets by id", async () => { - const priceSetsResult = await service.list({ - id: ["price-set-1"], +moduleIntegrationTestRunner({ + moduleName: Modules.PRICING, + testSuite: ({ + MikroOrmWrapper, + service, + }: SuiteOptions) => { + describe("PricingModule Service - PriceSet", () => { + beforeEach(async () => { + const testManager = await MikroOrmWrapper.forkManager() + await seedPriceData(testManager) + await createPriceSetPriceRules(testManager, [ + { + price_set: "price-set-1", + rule_type: "rule-type-1", + }, + { + price_set: "price-set-2", + rule_type: "rule-type-2", + }, + ] as unknown as CreatePriceSetRuleTypeDTO[]) }) - expect(priceSetsResult).toEqual([ - expect.objectContaining({ - id: "price-set-1", - }), - ]) - }) + describe("list", () => { + it("list priceSets", async () => { + const priceSetsResult = await service.list() - it("list priceSets with relations and selects", async () => { - const priceSetsResult = await service.list( - { - id: ["price-set-1"], - }, - { - select: ["id", "money_amounts.id", "money_amounts.amount"], - relations: ["money_amounts"], - } - ) - - const serialized = JSON.parse(JSON.stringify(priceSetsResult)) - - expect(serialized).toEqual([ - { - id: "price-set-1", - money_amounts: [ - expect.objectContaining({ id: "money-amount-USD", amount: 500 }), - ], - }, - ]) - }) - }) - - describe("listAndCount", () => { - it("should return priceSets and count", async () => { - const [priceSetsResult, count] = await service.listAndCount() - - expect(count).toEqual(3) - expect(priceSetsResult).toEqual([ - expect.objectContaining({ - id: "price-set-1", - }), - expect.objectContaining({ - id: "price-set-2", - }), - expect.objectContaining({ - id: "price-set-3", - }), - ]) - }) - - it("should return priceSets and count when filtered", async () => { - const [priceSetsResult, count] = await service.listAndCount({ - id: ["price-set-1"], - }) - - expect(count).toEqual(1) - expect(priceSetsResult).toEqual([ - expect.objectContaining({ - id: "price-set-1", - }), - ]) - }) - - it("list priceSets with relations and selects", async () => { - const [priceSetsResult, count] = await service.listAndCount( - { - id: ["price-set-1"], - }, - { - select: ["id", "min_quantity", "money_amounts.id"], - relations: ["money_amounts"], - } - ) - - const serialized = JSON.parse(JSON.stringify(priceSetsResult)) - - expect(count).toEqual(1) - expect(serialized).toEqual([ - { - id: "price-set-1", - money_amounts: [expect.objectContaining({ id: "money-amount-USD" })], - }, - ]) - }) - - it("should return priceSets and count when using skip and take", async () => { - const [priceSetsResult, count] = await service.listAndCount( - {}, - { skip: 1, take: 1 } - ) - - expect(count).toEqual(3) - expect(priceSetsResult).toEqual([ - expect.objectContaining({ - id: "price-set-2", - }), - ]) - }) - - it("should return requested fields", async () => { - const [priceSetsResult, count] = await service.listAndCount( - {}, - { - take: 1, - select: ["id"], - } - ) - - const serialized = JSON.parse(JSON.stringify(priceSetsResult)) - - expect(count).toEqual(3) - expect(serialized).toEqual([ - { - id: "price-set-1", - }, - ]) - }) - }) - - describe("retrieve", () => { - const id = "price-set-1" - - it("should return priceSet for the given id", async () => { - const priceSet = await service.retrieve(id) - - expect(priceSet).toEqual( - expect.objectContaining({ - id, + expect(priceSetsResult).toEqual([ + expect.objectContaining({ + id: "price-set-1", + }), + expect.objectContaining({ + id: "price-set-2", + }), + expect.objectContaining({ + id: "price-set-3", + }), + ]) }) - ) - }) - it("should throw an error when priceSet with id does not exist", async () => { - let error + it("list priceSets by id", async () => { + const priceSetsResult = await service.list({ + id: ["price-set-1"], + }) - try { - await service.retrieve("does-not-exist") - } catch (e) { - error = e - } + expect(priceSetsResult).toEqual([ + expect.objectContaining({ + id: "price-set-1", + }), + ]) + }) - expect(error.message).toEqual( - "PriceSet with id: does-not-exist was not found" - ) - }) + it("list priceSets with relations and selects", async () => { + const priceSetsResult = await service.list( + { + id: ["price-set-1"], + }, + { + select: ["id", "money_amounts.id", "money_amounts.amount"], + relations: ["money_amounts"], + } + ) - it("should throw an error when a id is not provided", async () => { - let error + const serialized = JSON.parse(JSON.stringify(priceSetsResult)) - try { - await service.retrieve(undefined as unknown as string) - } catch (e) { - error = e - } - - expect(error.message).toEqual("priceSet - id must be defined") - }) - - it("should return priceSet based on config select param", async () => { - const priceSet = await service.retrieve(id, { - select: ["id"], + expect(serialized).toEqual([ + { + id: "price-set-1", + money_amounts: [ + expect.objectContaining({ + id: "money-amount-USD", + amount: 500, + }), + ], + }, + ]) + }) }) - const serialized = JSON.parse(JSON.stringify(priceSet)) + describe("listAndCount", () => { + it("should return priceSets and count", async () => { + const [priceSetsResult, count] = await service.listAndCount() - expect(serialized).toEqual({ - id, - }) - }) - }) + expect(count).toEqual(3) + expect(priceSetsResult).toEqual([ + expect.objectContaining({ + id: "price-set-1", + }), + expect.objectContaining({ + id: "price-set-2", + }), + expect.objectContaining({ + id: "price-set-3", + }), + ]) + }) - describe("delete", () => { - const id = "price-set-1" + it("should return priceSets and count when filtered", async () => { + const [priceSetsResult, count] = await service.listAndCount({ + id: ["price-set-1"], + }) - it("should delete the priceSets given an id successfully", async () => { - await service.delete([id]) + expect(count).toEqual(1) + expect(priceSetsResult).toEqual([ + expect.objectContaining({ + id: "price-set-1", + }), + ]) + }) - const priceSets = await service.list({ - id: [id], + it("list priceSets with relations and selects", async () => { + const [priceSetsResult, count] = await service.listAndCount( + { + id: ["price-set-1"], + }, + { + select: ["id", "min_quantity", "money_amounts.id"], + relations: ["money_amounts"], + } + ) + + const serialized = JSON.parse(JSON.stringify(priceSetsResult)) + + expect(count).toEqual(1) + expect(serialized).toEqual([ + { + id: "price-set-1", + money_amounts: [ + expect.objectContaining({ id: "money-amount-USD" }), + ], + }, + ]) + }) + + it("should return priceSets and count when using skip and take", async () => { + const [priceSetsResult, count] = await service.listAndCount( + {}, + { skip: 1, take: 1 } + ) + + expect(count).toEqual(3) + expect(priceSetsResult).toEqual([ + expect.objectContaining({ + id: "price-set-2", + }), + ]) + }) + + it("should return requested fields", async () => { + const [priceSetsResult, count] = await service.listAndCount( + {}, + { + take: 1, + select: ["id"], + } + ) + + const serialized = JSON.parse(JSON.stringify(priceSetsResult)) + + expect(count).toEqual(3) + expect(serialized).toEqual([ + { + id: "price-set-1", + }, + ]) + }) }) - expect(priceSets).toHaveLength(0) - }) - }) + describe("retrieve", () => { + const id = "price-set-1" - describe("update", () => { - const id = "price-set-1" + it("should return priceSet for the given id", async () => { + const priceSet = await service.retrieve(id) - it("should throw an error when a id does not exist", async () => { - let error + expect(priceSet).toEqual( + expect.objectContaining({ + id, + }) + ) + }) - try { - await service.update([ - { - id: "does-not-exist", - }, - ]) - } catch (e) { - error = e - } + it("should throw an error when priceSet with id does not exist", async () => { + let error - expect(error.message).toEqual( - 'PriceSet with id "does-not-exist" not found' - ) - }) - }) + try { + await service.retrieve("does-not-exist") + } catch (e) { + error = e + } - describe("create", () => { - it("should throw an error when creating a price set with rule attributes that don't exist", async () => { - let error + expect(error.message).toEqual( + "PriceSet with id: does-not-exist was not found" + ) + }) - try { - await service.create([ - { - rules: [{ rule_attribute: "does-not-exist" }], - }, - ]) - } catch (e) { - error = e - } + it("should throw an error when a id is not provided", async () => { + let error - expect(error.message).toEqual( - "Rule types don't exist for: does-not-exist" - ) - }) + try { + await service.retrieve(undefined as unknown as string) + } catch (e) { + error = e + } - it("should fail to create a price set with rule types and money amounts with rule types that don't exits", async () => { - let error + expect(error.message).toEqual("priceSet - id must be defined") + }) - try { - await service.create([ - { - rules: [{ rule_attribute: "region_id" }], - prices: [ + it("should return priceSet based on config select param", async () => { + const priceSet = await service.retrieve(id, { + select: ["id"], + }) + + const serialized = JSON.parse(JSON.stringify(priceSet)) + + expect(serialized).toEqual({ + id, + }) + }) + }) + + describe("delete", () => { + const id = "price-set-1" + + it("should delete the priceSets given an id successfully", async () => { + await service.delete([id]) + + const priceSets = await service.list({ + id: [id], + }) + + expect(priceSets).toHaveLength(0) + }) + }) + + describe("update", () => { + const id = "price-set-1" + + it("should throw an error when a id does not exist", async () => { + let error + + try { + await service.update([ { - amount: 100, - currency_code: "USD", - rules: { - city: "Berlin", + id: "does-not-exist", + }, + ]) + } catch (e) { + error = e + } + + expect(error.message).toEqual( + 'PriceSet with id "does-not-exist" not found' + ) + }) + }) + + describe("create", () => { + it("should throw an error when creating a price set with rule attributes that don't exist", async () => { + let error + + try { + await service.create([ + { + rules: [{ rule_attribute: "does-not-exist" }], + }, + ]) + } catch (e) { + error = e + } + + expect(error.message).toEqual( + "Rule types don't exist for: does-not-exist" + ) + }) + + it("should fail to create a price set with rule types and money amounts with rule types that don't exits", async () => { + let error + + try { + await service.create([ + { + rules: [{ rule_attribute: "region_id" }], + prices: [ + { + amount: 100, + currency_code: "USD", + rules: { + city: "Berlin", + }, + }, + ], + }, + ]) + } catch (e) { + error = e + } + expect(error.message).toEqual( + "Rule types don't exist for money amounts with rule attribute: city" + ) + }) + + it("should create a price set with rule types", async () => { + const [priceSet] = await service.create([ + { + rules: [{ rule_attribute: "region_id" }], + }, + ]) + + expect(priceSet).toEqual( + expect.objectContaining({ + rule_types: [ + expect.objectContaining({ + rule_attribute: "region_id", + }), + ], + }) + ) + }) + + it("should create a price set with rule types and money amounts", async () => { + const [priceSet] = await service.create([ + { + rules: [{ rule_attribute: "region_id" }], + prices: [ + { + amount: 100, + currency_code: "USD", + rules: { + region_id: "1", + }, }, - }, - ], - }, - ]) - } catch (e) { - error = e - } - expect(error.message).toEqual( - "Rule types don't exist for money amounts with rule attribute: city" - ) - }) - - it("should create a price set with rule types", async () => { - const [priceSet] = await service.create([ - { - rules: [{ rule_attribute: "region_id" }], - }, - ]) - - expect(priceSet).toEqual( - expect.objectContaining({ - rule_types: [ - expect.objectContaining({ - rule_attribute: "region_id", - }), - ], - }) - ) - }) - - it("should create a price set with rule types and money amounts", async () => { - const [priceSet] = await service.create([ - { - rules: [{ rule_attribute: "region_id" }], - prices: [ - { - amount: 100, - currency_code: "USD", - rules: { - region_id: "1", - }, + ], }, - ], - }, - ]) + ]) - expect(priceSet).toEqual( - expect.objectContaining({ - rule_types: [ + expect(priceSet).toEqual( expect.objectContaining({ - rule_attribute: "region_id", - }), - ], - money_amounts: [ - expect.objectContaining({ - amount: 100, - currency_code: "USD", - }), - ], + rule_types: [ + expect.objectContaining({ + rule_attribute: "region_id", + }), + ], + money_amounts: [ + expect.objectContaining({ + amount: 100, + currency_code: "USD", + }), + ], + }) + ) }) - ) - }) - it("should create a price set with money amounts with and without rules", async () => { - const [priceSet] = await service.create([ - { - rules: [{ rule_attribute: "region_id" }], - prices: [ + it("should create a price set with money amounts with and without rules", async () => { + const [priceSet] = await service.create([ { - amount: 100, - currency_code: "USD", - rules: { - region_id: "1", - }, + rules: [{ rule_attribute: "region_id" }], + prices: [ + { + amount: 100, + currency_code: "USD", + rules: { + region_id: "1", + }, + }, + { + amount: 150, + currency_code: "USD", + }, + ], }, - { - amount: 150, - currency_code: "USD", - }, - ], - }, - ]) + ]) - expect(priceSet).toEqual( - expect.objectContaining({ - rule_types: [ + expect(priceSet).toEqual( expect.objectContaining({ - rule_attribute: "region_id", - }), - ], - money_amounts: expect.arrayContaining([ - expect.objectContaining({ - amount: 100, - currency_code: "USD", - }), - expect.objectContaining({ - amount: 150, - currency_code: "USD", - }), - ]), + rule_types: [ + expect.objectContaining({ + rule_attribute: "region_id", + }), + ], + money_amounts: expect.arrayContaining([ + expect.objectContaining({ + amount: 100, + currency_code: "USD", + }), + expect.objectContaining({ + amount: 150, + currency_code: "USD", + }), + ]), + }) + ) }) - ) - }) - it("should create a price set with rule types and money amountss", async () => { - const [priceSet] = await service.create([ - { - rules: [{ rule_attribute: "region_id" }], - prices: [ + it("should create a price set with rule types and money amountss", async () => { + const [priceSet] = await service.create([ { - amount: 100, - currency_code: "USD", - rules: { - region_id: "10", - }, + rules: [{ rule_attribute: "region_id" }], + prices: [ + { + amount: 100, + currency_code: "USD", + rules: { + region_id: "10", + }, + }, + ], }, - ], - }, - ]) + ]) - expect(priceSet).toEqual( - expect.objectContaining({ - rule_types: [ + expect(priceSet).toEqual( expect.objectContaining({ - rule_attribute: "region_id", - }), - ], - money_amounts: [ - expect.objectContaining({ - amount: 100, - currency_code: "USD", - }), - ], - price_rules: [ - expect.objectContaining({ - value: "10", - }), - ], + rule_types: [ + expect.objectContaining({ + rule_attribute: "region_id", + }), + ], + money_amounts: [ + expect.objectContaining({ + amount: 100, + currency_code: "USD", + }), + ], + price_rules: [ + expect.objectContaining({ + value: "10", + }), + ], + }) + ) }) - ) - }) - it("should create a priceSet successfully", async () => { - await service.create([ - { - id: "price-set-new", - } as unknown as CreatePriceSetDTO, - ]) + it("should create a priceSet successfully", async () => { + await service.create([ + { + id: "price-set-new", + } as unknown as CreatePriceSetDTO, + ]) - const [priceSet] = await service.list({ - id: ["price-set-new"], + const [priceSet] = await service.list({ + id: ["price-set-new"], + }) + + expect(priceSet).toEqual( + expect.objectContaining({ + id: "price-set-new", + }) + ) + }) }) - expect(priceSet).toEqual( - expect.objectContaining({ - id: "price-set-new", + describe("removeRules", () => { + it("should delete prices for a price set associated to the rules that are deleted", async () => { + const createdPriceSet = await service.create([ + { + rules: [ + { + rule_attribute: "region_id", + }, + { + rule_attribute: "currency_code", + }, + ], + prices: [ + { + currency_code: "EUR", + amount: 100, + rules: { + region_id: "test-region", + currency_code: "test-currency", + }, + }, + { + currency_code: "EUR", + amount: 500, + rules: { + currency_code: "test-currency", + }, + }, + ], + }, + ]) + + await service.removeRules([ + { + id: createdPriceSet[0].id, + rules: ["region_id"], + }, + ]) + + let priceSet = await service.list( + { + id: [createdPriceSet[0].id], + }, + { + relations: ["rule_types", "money_amounts", "price_rules"], + } + ) + + expect( + expect.arrayContaining( + expect.objectContaining({ + id: priceSet[0].id, + price_rules: [ + { + id: expect.any(String), + rule_type: expect.objectContaining({ + rule_attribute: "currency_code", + }), + }, + ], + money_amounts: [ + expect.objectContaining({ + amount: 500, + currency_code: "EUR", + }), + ], + rule_types: [ + expect.objectContaining({ + rule_attribute: "currency_code", + }), + ], + }) + ) + ) + + await service.removeRules([ + { + id: createdPriceSet[0].id, + rules: ["currency_code"], + }, + ]) + + priceSet = await service.list( + { + id: [createdPriceSet[0].id], + }, + { + relations: ["rule_types", "money_amounts", "price_rules"], + } + ) + expect(priceSet).toEqual([ + { + id: expect.any(String), + price_rules: [], + money_amounts: [], + rule_types: [], + }, + ]) }) - ) - }) - }) + }) - describe("removeRules", () => { - it("should delete prices for a price set associated to the rules that are deleted", async () => { - const createdPriceSet = await service.create([ - { - rules: [ + describe("addPrices", () => { + it("should add prices to existing price set", async () => { + await service.addPrices([ { - rule_attribute: "region_id", + priceSetId: "price-set-1", + prices: [ + { + amount: 100, + currency_code: "USD", + rules: { currency_code: "USD" }, + }, + ], + }, + ]) + + const [priceSet] = await service.list( + { id: ["price-set-1"] }, + { relations: ["money_amounts"] } + ) + + expect(priceSet).toEqual( + expect.objectContaining({ + id: "price-set-1", + money_amounts: expect.arrayContaining([ + expect.objectContaining({ + amount: 100, + currency_code: "USD", + }), + ]), + }) + ) + }) + + it("should add prices to multiple existing price set", async () => { + await service.addPrices([ + { + priceSetId: "price-set-1", + prices: [ + { + amount: 100, + currency_code: "USD", + rules: { currency_code: "USD" }, + }, + ], }, { - rule_attribute: "currency_code", + priceSetId: "price-set-2", + prices: [ + { + amount: 150, + currency_code: "EUR", + rules: { region_id: "region-2" }, + }, + ], }, - ], - prices: [ + ]) + + const priceSets = await service.list( + { id: ["price-set-1", "price-set-2"] }, + { relations: ["money_amounts"] } + ) + + expect(priceSets).toEqual([ + expect.objectContaining({ + id: "price-set-1", + money_amounts: expect.arrayContaining([ + expect.objectContaining({ + amount: 100, + currency_code: "USD", + }), + ]), + }), + expect.objectContaining({ + id: "price-set-2", + money_amounts: expect.arrayContaining([ + expect.objectContaining({ + amount: 150, + currency_code: "EUR", + }), + ]), + }), + ]) + }) + + it("should fail with an appropriate error when trying to add a price with rule that doesn't exist", async () => { + let error + try { + await service.addPrices({ + priceSetId: "price-set-1", + prices: [ + { + amount: 100, + currency_code: "USD", + rules: { city: "Paris" }, + }, + ], + }) + } catch (e) { + error = e + } + + expect(error.message).toEqual("Rule types don't exist for: city") + }) + }) + + describe("addRules", () => { + it("should add rules to existing price set", async () => { + await service.addRules([ { - currency_code: "EUR", - amount: 100, - rules: { - region_id: "test-region", - currency_code: "test-currency", - }, + priceSetId: "price-set-1", + rules: [ + { + attribute: "region_id", + }, + ], }, - { - currency_code: "EUR", - amount: 500, - rules: { - currency_code: "test-currency", - }, - }, - ], - }, - ]) + ]) - await service.removeRules([ - { - id: createdPriceSet[0].id, - rules: ["region_id"], - }, - ]) + const [priceSet] = await service.list( + { id: ["price-set-1"] }, + { relations: ["rule_types"] } + ) - let priceSet = await service.list( - { - id: [createdPriceSet[0].id], - }, - { - relations: ["rule_types", "money_amounts", "price_rules"], - } - ) - - expect( - expect.arrayContaining( - expect.objectContaining({ - id: priceSet[0].id, - price_rules: [ - { - id: expect.any(String), - rule_type: expect.objectContaining({ + expect(priceSet).toEqual( + expect.objectContaining({ + id: "price-set-1", + rule_types: expect.arrayContaining([ + expect.objectContaining({ rule_attribute: "currency_code", }), - }, - ], - money_amounts: [ - expect.objectContaining({ - amount: 500, - currency_code: "EUR", - }), - ], - rule_types: [ - expect.objectContaining({ - rule_attribute: "currency_code", - }), - ], - }) - ) - ) - - await service.removeRules([ - { - id: createdPriceSet[0].id, - rules: ["currency_code"], - }, - ]) - - priceSet = await service.list( - { - id: [createdPriceSet[0].id], - }, - { - relations: ["rule_types", "money_amounts", "price_rules"], - } - ) - expect(priceSet).toEqual([ - { - id: expect.any(String), - price_rules: [], - money_amounts: [], - rule_types: [], - }, - ]) - }) - }) - - describe("addPrices", () => { - it("should add prices to existing price set", async () => { - await service.addPrices([ - { - priceSetId: "price-set-1", - prices: [ - { - amount: 100, - currency_code: "USD", - rules: { currency_code: "USD" }, - }, - ], - }, - ]) - - const [priceSet] = await service.list( - { id: ["price-set-1"] }, - { relations: ["money_amounts"] } - ) - - expect(priceSet).toEqual( - expect.objectContaining({ - id: "price-set-1", - money_amounts: expect.arrayContaining([ - expect.objectContaining({ - amount: 100, - currency_code: "USD", - }), - ]), + expect.objectContaining({ + rule_attribute: "region_id", + }), + ]), + }) + ) }) - ) - }) - it("should add prices to multiple existing price set", async () => { - await service.addPrices([ - { - priceSetId: "price-set-1", - prices: [ - { - amount: 100, - currency_code: "USD", - rules: { currency_code: "USD" }, - }, - ], - }, - { - priceSetId: "price-set-2", - prices: [ - { - amount: 150, - currency_code: "EUR", - rules: { region_id: "region-2" }, - }, - ], - }, - ]) + it("should fail to add rules to non-existent price sets", async () => { + let error - const priceSets = await service.list( - { id: ["price-set-1", "price-set-2"] }, - { relations: ["money_amounts"] } - ) - - expect(priceSets).toEqual([ - expect.objectContaining({ - id: "price-set-1", - money_amounts: expect.arrayContaining([ - expect.objectContaining({ - amount: 100, - currency_code: "USD", - }), - ]), - }), - expect.objectContaining({ - id: "price-set-2", - money_amounts: expect.arrayContaining([ - expect.objectContaining({ - amount: 150, - currency_code: "EUR", - }), - ]), - }), - ]) - }) - - it("should fail with an appropriate error when trying to add a price with rule that doesn't exist", async () => { - let error - try { - await service.addPrices({ - priceSetId: "price-set-1", - prices: [ - { - amount: 100, - currency_code: "USD", - rules: { city: "Paris" }, - }, - ], - }) - } catch (e) { - error = e - } - - expect(error.message).toEqual("Rule types don't exist for: city") - }) - }) - - describe("addRules", () => { - it("should add rules to existing price set", async () => { - await service.addRules([ - { - priceSetId: "price-set-1", - rules: [ - { - attribute: "region_id", - }, - ], - }, - ]) - - const [priceSet] = await service.list( - { id: ["price-set-1"] }, - { relations: ["rule_types"] } - ) - - expect(priceSet).toEqual( - expect.objectContaining({ - id: "price-set-1", - rule_types: expect.arrayContaining([ - expect.objectContaining({ - rule_attribute: "currency_code", - }), - expect.objectContaining({ - rule_attribute: "region_id", - }), - ]), - }) - ) - }) - - it("should fail to add rules to non-existent price sets", async () => { - let error - - try { - await service.addRules([ - { - priceSetId: "price-set-doesn't-exist", - rules: [ + try { + await service.addRules([ { - attribute: "region_id", + priceSetId: "price-set-doesn't-exist", + rules: [ + { + attribute: "region_id", + }, + ], }, - ], - }, - ]) - } catch (e) { - error = e - } + ]) + } catch (e) { + error = e + } - expect(error.message).toEqual( - "PriceSets with ids: price-set-doesn't-exist was not found" - ) - }) + expect(error.message).toEqual( + "PriceSets with ids: price-set-doesn't-exist was not found" + ) + }) - it("should fail to add rules with non-existent attributes", async () => { - let error + it("should fail to add rules with non-existent attributes", async () => { + let error - try { - await service.addRules([ - { - priceSetId: "price-set-1", - rules: [ + try { + await service.addRules([ { - attribute: "city", + priceSetId: "price-set-1", + rules: [ + { + attribute: "city", + }, + ], }, - ], - }, - ]) - } catch (e) { - error = e - } + ]) + } catch (e) { + error = e + } - expect(error.message).toEqual( - "Rule types don't exist for attributes: city" - ) + expect(error.message).toEqual( + "Rule types don't exist for attributes: city" + ) + }) + }) }) - }) + }, }) diff --git a/packages/pricing/integration-tests/__tests__/services/pricing-module/rule-type.spec.ts b/packages/pricing/integration-tests/__tests__/services/pricing-module/rule-type.spec.ts index 2c04056122..56c097c799 100644 --- a/packages/pricing/integration-tests/__tests__/services/pricing-module/rule-type.spec.ts +++ b/packages/pricing/integration-tests/__tests__/services/pricing-module/rule-type.spec.ts @@ -1,261 +1,241 @@ -import { IPricingModuleService } from "@medusajs/types" import { SqlEntityManager } from "@mikro-orm/postgresql" import { createRuleTypes } from "../../../__fixtures__/rule-type" -import { MikroOrmWrapper } from "../../../utils" -import { getInitModuleConfig } from "../../../utils/get-init-module-config" -import { initModules } from "medusa-test-utils" +import { moduleIntegrationTestRunner, SuiteOptions } from "medusa-test-utils" import { Modules } from "@medusajs/modules-sdk" +import { IPricingModuleService } from "@medusajs/types" -describe("PricingModuleService ruleType", () => { - let service: IPricingModuleService - let testManager: SqlEntityManager - let shutdownFunc: () => Promise - - beforeAll(async () => { - const initModulesConfig = getInitModuleConfig() - - const { medusaApp, shutdown } = await initModules(initModulesConfig) - - service = medusaApp.modules[Modules.PRICING] - - shutdownFunc = shutdown - }) - - afterAll(async () => { - await shutdownFunc() - }) - - beforeEach(async () => { - await MikroOrmWrapper.setupDatabase() - MikroOrmWrapper.forkManager() - - testManager = MikroOrmWrapper.forkManager() - - await createRuleTypes(testManager) - }) - - afterEach(async () => { - await MikroOrmWrapper.clearDatabase() - }) - - describe("listRuleTypes", () => { - it("should list rule types", async () => { - const ruleTypeResult = await service.listRuleTypes() - - expect(ruleTypeResult).toEqual([ - expect.objectContaining({ - id: "rule-type-1", - name: "rule 1", - }), - expect.objectContaining({ - id: "rule-type-2", - name: "rule 2", - }), - ]) - }) - - it("should list rule types by id", async () => { - const ruleTypeResult = await service.listRuleTypes({ - id: ["rule-type-1"], +moduleIntegrationTestRunner({ + moduleName: Modules.PRICING, + testSuite: ({ + MikroOrmWrapper, + service, + }: SuiteOptions) => { + describe("PricingModuleService ruleType", () => { + beforeEach(async () => { + const testManager = MikroOrmWrapper.forkManager() + await createRuleTypes(testManager) }) - expect(ruleTypeResult).toEqual([ - expect.objectContaining({ - id: "rule-type-1", - name: "rule 1", - }), - ]) - }) - }) + describe("listRuleTypes", () => { + it("should list rule types", async () => { + const ruleTypeResult = await service.listRuleTypes() - describe("listAndCountRuleTypes", () => { - it("should return rule types and count", async () => { - const [ruleTypeResult, count] = await service.listAndCountRuleTypes() - - expect(count).toEqual(2) - expect(ruleTypeResult).toEqual([ - expect.objectContaining({ - id: "rule-type-1", - name: "rule 1", - }), - expect.objectContaining({ - id: "rule-type-2", - name: "rule 2", - }), - ]) - }) - - it("should return rule types and count when filtered", async () => { - const [ruleTypeResult, count] = await service.listAndCountRuleTypes({ - id: ["rule-type-1"], - }) - - expect(count).toEqual(1) - expect(ruleTypeResult).toEqual([ - expect.objectContaining({ - id: "rule-type-1", - name: "rule 1", - }), - ]) - }) - - it("should return rule types and count when using skip and take", async () => { - const [ruleTypeResult, count] = await service.listAndCountRuleTypes( - {}, - { skip: 1, take: 1 } - ) - - expect(count).toEqual(2) - expect(ruleTypeResult).toEqual([ - expect.objectContaining({ - id: "rule-type-2", - name: "rule 2", - }), - ]) - }) - - it("should return requested fields", async () => { - const [ruleTypeResult, count] = await service.listAndCountRuleTypes( - {}, - { - take: 1, - select: ["name"], - } - ) - - const serialized = JSON.parse(JSON.stringify(ruleTypeResult)) - - expect(count).toEqual(2) - expect(serialized).toEqual([ - { - id: "rule-type-1", - name: "rule 1", - }, - ]) - }) - }) - - describe("retrieveRuleType", () => { - it("should return ruleType for the given id", async () => { - const ruleType = await service.retrieveRuleType("rule-type-1") - - expect(ruleType).toEqual( - expect.objectContaining({ - id: "rule-type-1", - name: "rule 1", + expect(ruleTypeResult).toEqual([ + expect.objectContaining({ + id: "rule-type-1", + name: "rule 1", + }), + expect.objectContaining({ + id: "rule-type-2", + name: "rule 2", + }), + ]) }) - ) - }) - it("should throw an error when ruleType with id does not exist", async () => { - let error + it("should list rule types by id", async () => { + const ruleTypeResult = await service.listRuleTypes({ + id: ["rule-type-1"], + }) - try { - await service.retrieveRuleType("does-not-exist") - } catch (e) { - error = e - } - - expect(error.message).toEqual( - "RuleType with id: does-not-exist was not found" - ) - }) - - it("should throw an error when an id is not provided", async () => { - let error - - try { - await service.retrieveRuleType(undefined as unknown as string) - } catch (e) { - error = e - } - - expect(error.message).toEqual("ruleType - id must be defined") - }) - - it("should return ruleType based on config select param", async () => { - const ruleTypeResult = await service.retrieveRuleType("rule-type-1", { - select: ["name"], - }) - - const serialized = JSON.parse(JSON.stringify(ruleTypeResult)) - - expect(serialized).toEqual({ - name: "rule 1", - id: "rule-type-1", - }) - }) - }) - - describe("deleteRuleTypes", () => { - const id = "rule-type-1" - - it("should delete the ruleTypes given an id successfully", async () => { - await service.deleteRuleTypes([id]) - - const currencies = await service.listRuleTypes({ - id: [id], - }) - - expect(currencies).toHaveLength(0) - }) - }) - - describe("updateRuleTypes", () => { - const id = "rule-type-1" - - it("should update the name of the ruleType successfully", async () => { - await service.updateRuleTypes([ - { - id, - name: "rule 3", - }, - ]) - - const ruletype = await service.retrieveRuleType(id) - - expect(ruletype.name).toEqual("rule 3") - }) - - it("should throw an error when a id does not exist", async () => { - let error - - try { - await service.updateRuleTypes([ - { - id: "does-not-exist", - name: "rule 3", - }, - ]) - } catch (e) { - error = e - } - - expect(error.message).toEqual( - 'RuleType with id "does-not-exist" not found' - ) - }) - }) - - describe("createRuleTypes", () => { - it("should create a ruleType successfully", async () => { - await service.createRuleTypes([ - { - name: "Test Rule", - rule_attribute: "region_id", - }, - ]) - - const [ruleType] = await service.listRuleTypes({ - name: ["Test Rule"], - }) - - expect(ruleType).toEqual( - expect.objectContaining({ - name: "Test Rule", - rule_attribute: "region_id", + expect(ruleTypeResult).toEqual([ + expect.objectContaining({ + id: "rule-type-1", + name: "rule 1", + }), + ]) }) - ) + }) + + describe("listAndCountRuleTypes", () => { + it("should return rule types and count", async () => { + const [ruleTypeResult, count] = await service.listAndCountRuleTypes() + + expect(count).toEqual(2) + expect(ruleTypeResult).toEqual([ + expect.objectContaining({ + id: "rule-type-1", + name: "rule 1", + }), + expect.objectContaining({ + id: "rule-type-2", + name: "rule 2", + }), + ]) + }) + + it("should return rule types and count when filtered", async () => { + const [ruleTypeResult, count] = await service.listAndCountRuleTypes({ + id: ["rule-type-1"], + }) + + expect(count).toEqual(1) + expect(ruleTypeResult).toEqual([ + expect.objectContaining({ + id: "rule-type-1", + name: "rule 1", + }), + ]) + }) + + it("should return rule types and count when using skip and take", async () => { + const [ruleTypeResult, count] = await service.listAndCountRuleTypes( + {}, + { skip: 1, take: 1 } + ) + + expect(count).toEqual(2) + expect(ruleTypeResult).toEqual([ + expect.objectContaining({ + id: "rule-type-2", + name: "rule 2", + }), + ]) + }) + + it("should return requested fields", async () => { + const [ruleTypeResult, count] = await service.listAndCountRuleTypes( + {}, + { + take: 1, + select: ["name"], + } + ) + + const serialized = JSON.parse(JSON.stringify(ruleTypeResult)) + + expect(count).toEqual(2) + expect(serialized).toEqual([ + { + id: "rule-type-1", + name: "rule 1", + }, + ]) + }) + }) + + describe("retrieveRuleType", () => { + it("should return ruleType for the given id", async () => { + const ruleType = await service.retrieveRuleType("rule-type-1") + + expect(ruleType).toEqual( + expect.objectContaining({ + id: "rule-type-1", + name: "rule 1", + }) + ) + }) + + it("should throw an error when ruleType with id does not exist", async () => { + let error + + try { + await service.retrieveRuleType("does-not-exist") + } catch (e) { + error = e + } + + expect(error.message).toEqual( + "RuleType with id: does-not-exist was not found" + ) + }) + + it("should throw an error when an id is not provided", async () => { + let error + + try { + await service.retrieveRuleType(undefined as unknown as string) + } catch (e) { + error = e + } + + expect(error.message).toEqual("ruleType - id must be defined") + }) + + it("should return ruleType based on config select param", async () => { + const ruleTypeResult = await service.retrieveRuleType("rule-type-1", { + select: ["name"], + }) + + const serialized = JSON.parse(JSON.stringify(ruleTypeResult)) + + expect(serialized).toEqual({ + name: "rule 1", + id: "rule-type-1", + }) + }) + }) + + describe("deleteRuleTypes", () => { + const id = "rule-type-1" + + it("should delete the ruleTypes given an id successfully", async () => { + await service.deleteRuleTypes([id]) + + const currencies = await service.listRuleTypes({ + id: [id], + }) + + expect(currencies).toHaveLength(0) + }) + }) + + describe("updateRuleTypes", () => { + const id = "rule-type-1" + + it("should update the name of the ruleType successfully", async () => { + await service.updateRuleTypes([ + { + id, + name: "rule 3", + }, + ]) + + const ruletype = await service.retrieveRuleType(id) + + expect(ruletype.name).toEqual("rule 3") + }) + + it("should throw an error when a id does not exist", async () => { + let error + + try { + await service.updateRuleTypes([ + { + id: "does-not-exist", + name: "rule 3", + }, + ]) + } catch (e) { + error = e + } + + expect(error.message).toEqual( + 'RuleType with id "does-not-exist" not found' + ) + }) + }) + + describe("createRuleTypes", () => { + it("should create a ruleType successfully", async () => { + await service.createRuleTypes([ + { + name: "Test Rule", + rule_attribute: "region_id", + }, + ]) + + const [ruleType] = await service.listRuleTypes({ + name: ["Test Rule"], + }) + + expect(ruleType).toEqual( + expect.objectContaining({ + name: "Test Rule", + rule_attribute: "region_id", + }) + ) + }) + }) }) - }) + }, }) diff --git a/packages/pricing/integration-tests/__tests__/services/rule-type/index.spec.ts b/packages/pricing/integration-tests/__tests__/services/rule-type/index.spec.ts deleted file mode 100644 index df7431aa38..0000000000 --- a/packages/pricing/integration-tests/__tests__/services/rule-type/index.spec.ts +++ /dev/null @@ -1,274 +0,0 @@ -import { SqlEntityManager } from "@mikro-orm/postgresql" -import { RuleTypeService } from "@services" - -import { createRuleTypes } from "../../../__fixtures__/rule-type" -import { MikroOrmWrapper } from "../../../utils" -import { createMedusaContainer } from "@medusajs/utils" -import { asValue } from "awilix" -import ContainerLoader from "../../../../src/loaders/container" - -jest.setTimeout(30000) - -describe("RuleType Service", () => { - let service: RuleTypeService - let testManager: SqlEntityManager - let repositoryManager: SqlEntityManager - - beforeEach(async () => { - await MikroOrmWrapper.setupDatabase() - repositoryManager = await MikroOrmWrapper.forkManager() - - const container = createMedusaContainer() - container.register("manager", asValue(repositoryManager)) - - await ContainerLoader({ container }) - - service = container.resolve("ruleTypeService") - - testManager = await MikroOrmWrapper.forkManager() - - await createRuleTypes(testManager) - }) - - afterEach(async () => { - await MikroOrmWrapper.clearDatabase() - }) - - describe("list", () => { - it("list rule types", async () => { - const ruleTypeResult = await service.list() - - expect(ruleTypeResult).toEqual([ - expect.objectContaining({ - id: "rule-type-1", - name: "rule 1", - }), - expect.objectContaining({ - id: "rule-type-2", - name: "rule 2", - }), - ]) - }) - - it("list rule types by id", async () => { - const ruleTypeResult = await service.list({ id: ["rule-type-1"] }) - - expect(ruleTypeResult).toEqual([ - expect.objectContaining({ - id: "rule-type-1", - name: "rule 1", - }), - ]) - }) - }) - - describe("listAndCount", () => { - it("should return rule types and count", async () => { - const [ruleTypeResult, count] = await service.listAndCount() - - expect(count).toEqual(2) - expect(ruleTypeResult).toEqual([ - expect.objectContaining({ - id: "rule-type-1", - name: "rule 1", - }), - expect.objectContaining({ - id: "rule-type-2", - name: "rule 2", - }), - ]) - }) - - it("should return rule types and count when filtered", async () => { - const [ruleTypeResult, count] = await service.listAndCount({ - id: ["rule-type-1"], - }) - - expect(count).toEqual(1) - expect(ruleTypeResult).toEqual([ - expect.objectContaining({ - id: "rule-type-1", - name: "rule 1", - }), - ]) - }) - - it("should return rule types and count when using skip and take", async () => { - const [ruleTypeResult, count] = await service.listAndCount( - {}, - { skip: 1, take: 1 } - ) - - expect(count).toEqual(2) - expect(ruleTypeResult).toEqual([ - expect.objectContaining({ - id: "rule-type-2", - name: "rule 2", - }), - ]) - }) - - it("should return requested fields", async () => { - const [ruleTypeResult, count] = await service.listAndCount( - {}, - { - take: 1, - select: ["name"], - } - ) - - const serialized = JSON.parse(JSON.stringify(ruleTypeResult)) - - expect(count).toEqual(2) - expect(serialized).toEqual([ - { - id: "rule-type-1", - name: "rule 1", - }, - ]) - }) - }) - - describe("retrieve", () => { - it("should return ruleType for the given id", async () => { - const ruleType = await service.retrieve("rule-type-1") - - expect(ruleType).toEqual( - expect.objectContaining({ - id: "rule-type-1", - name: "rule 1", - }) - ) - }) - - it("should throw an error when ruleType with id does not exist", async () => { - let error - - try { - await service.retrieve("does-not-exist") - } catch (e) { - error = e - } - - expect(error.message).toEqual( - "RuleType with id: does-not-exist was not found" - ) - }) - - it("should throw an error when an id is not provided", async () => { - let error - - try { - await service.retrieve(undefined as unknown as string) - } catch (e) { - error = e - } - - expect(error.message).toEqual("ruleType - id must be defined") - }) - - it("should return ruleType based on config select param", async () => { - const ruleTypeResult = await service.retrieve("rule-type-1", { - select: ["name"], - }) - - const serialized = JSON.parse(JSON.stringify(ruleTypeResult)) - - expect(serialized).toEqual({ - name: "rule 1", - id: "rule-type-1", - }) - }) - }) - - describe("delete", () => { - const id = "rule-type-1" - - it("should delete the ruleTypes given an id successfully", async () => { - await service.delete([id]) - - const ruleTypes = await service.list({ - id: [id], - }) - - expect(ruleTypes).toHaveLength(0) - }) - }) - - describe("update", () => { - const id = "rule-type-1" - - it("should update the name of the ruleType successfully", async () => { - await service.update([ - { - id, - name: "rule 3", - }, - ]) - - const ruletype = await service.retrieve(id) - - expect(ruletype.name).toEqual("rule 3") - }) - - it("should throw an error when a id does not exist", async () => { - let error - - try { - await service.update([ - { - id: "does-not-exist", - name: "rule 3", - }, - ]) - } catch (e) { - error = e - } - - expect(error.message).toEqual( - 'RuleType with id "does-not-exist" not found' - ) - }) - }) - - describe("create", () => { - it("should create a ruleType successfully", async () => { - await service.create([ - { - name: "Test Rule", - rule_attribute: "region_id", - }, - ]) - - const [ruleType] = await service.list({ - name: ["Test Rule"], - }) - - expect(ruleType).toEqual( - expect.objectContaining({ - name: "Test Rule", - rule_attribute: "region_id", - }) - ) - }) - - it("should throw an error when using one of the reserved keywords", async () => { - let error - - try { - await service.create([ - { - name: "Test Rule", - rule_attribute: "currency_code", - }, - ]) - } catch (e) { - error = e - } - - expect(error.message).toEqual( - "Can't create rule_attribute with reserved keywords [quantity, currency_code, price_list_id] - currency_code" - ) - }) - }) -}) diff --git a/packages/pricing/integration-tests/setup-env.js b/packages/pricing/integration-tests/setup-env.js deleted file mode 100644 index d7aa972c88..0000000000 --- a/packages/pricing/integration-tests/setup-env.js +++ /dev/null @@ -1,6 +0,0 @@ -if (typeof process.env.DB_TEMP_NAME === "undefined") { - const tempName = parseInt(process.env.JEST_WORKER_ID || "1") - process.env.DB_TEMP_NAME = `medusa-pricing-integration-${tempName}` -} - -process.env.MEDUSA_PRICING_DB_SCHEMA = "public" diff --git a/packages/pricing/integration-tests/setup.js b/packages/pricing/integration-tests/setup.js deleted file mode 100644 index 43f99aab4a..0000000000 --- a/packages/pricing/integration-tests/setup.js +++ /dev/null @@ -1,3 +0,0 @@ -import { JestUtils } from "medusa-test-utils" - -JestUtils.afterAllHookDropDatabase() diff --git a/packages/pricing/integration-tests/utils/config.ts b/packages/pricing/integration-tests/utils/config.ts deleted file mode 100644 index 6d3b168c42..0000000000 --- a/packages/pricing/integration-tests/utils/config.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { ModuleServiceInitializeOptions } from "@medusajs/types" - -export const databaseOptions: ModuleServiceInitializeOptions["database"] = { - schema: "public", - clientUrl: "medusa-pricing-test", -} diff --git a/packages/pricing/integration-tests/utils/database.ts b/packages/pricing/integration-tests/utils/database.ts deleted file mode 100644 index 885476b676..0000000000 --- a/packages/pricing/integration-tests/utils/database.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { TestDatabaseUtils } from "medusa-test-utils" - -import * as PricingModels from "@models" - -const pathToMigrations = "../../src/migrations" -const mikroOrmEntities = PricingModels as unknown as any[] - -export const MikroOrmWrapper = TestDatabaseUtils.getMikroOrmWrapper({ - mikroOrmEntities, - pathToMigrations, -}) - -export const MikroOrmConfig = TestDatabaseUtils.getMikroOrmConfig({ - mikroOrmEntities, - pathToMigrations, -}) - -export const DB_URL = TestDatabaseUtils.getDatabaseURL() diff --git a/packages/pricing/integration-tests/utils/get-init-module-config.ts b/packages/pricing/integration-tests/utils/get-init-module-config.ts deleted file mode 100644 index 88054f9042..0000000000 --- a/packages/pricing/integration-tests/utils/get-init-module-config.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { Modules, ModulesDefinition } from "@medusajs/modules-sdk" - -import { DB_URL } from "./database" - -export function getInitModuleConfig() { - const moduleOptions = { - defaultAdapterOptions: { - database: { - clientUrl: DB_URL, - schema: process.env.MEDUSA_PRICING_DB_SCHEMA, - }, - }, - } - - const modulesConfig_ = { - [Modules.PRODUCT]: true, - [Modules.PRICING]: { - definition: ModulesDefinition[Modules.PRICING], - options: moduleOptions, - }, - } - - return { - modulesConfig: modulesConfig_, - databaseConfig: { - clientUrl: DB_URL, - schema: process.env.MEDUSA_PRICING_DB_SCHEMA, - }, - joinerConfig: [], - } -} diff --git a/packages/pricing/integration-tests/utils/index.ts b/packages/pricing/integration-tests/utils/index.ts deleted file mode 100644 index 6b917ed30e..0000000000 --- a/packages/pricing/integration-tests/utils/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./database" diff --git a/packages/pricing/jest.config.js b/packages/pricing/jest.config.js index 7117eaa088..07c381bf59 100644 --- a/packages/pricing/jest.config.js +++ b/packages/pricing/jest.config.js @@ -18,6 +18,4 @@ module.exports = { testEnvironment: `node`, moduleFileExtensions: [`js`, `ts`], modulePathIgnorePatterns: ["dist/"], - setupFiles: ["/integration-tests/setup-env.js"], - setupFilesAfterEnv: ["/integration-tests/setup.js"], } diff --git a/packages/product/integration-tests/__fixtures__/event-bus/index.ts b/packages/product/integration-tests/__fixtures__/event-bus/index.ts deleted file mode 100644 index 5d0e552eef..0000000000 --- a/packages/product/integration-tests/__fixtures__/event-bus/index.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { - EmitData, - EventBusTypes, - IEventBusModuleService, - Message, - Subscriber, -} from "@medusajs/types" - -export class EventBusService implements IEventBusModuleService { - emit( - eventName: string, - data: T, - options?: Record - ): Promise - emit(data: EmitData[]): Promise - emit(data: Message[]): Promise - - async emit< - T, - TInput extends - | string - | EventBusTypes.EmitData[] - | EventBusTypes.Message[] = string - >( - eventOrData: TInput, - data?: T, - options: Record = {} - ): Promise {} - - subscribe(event: string | symbol, subscriber: Subscriber): this { - return this - } - - unsubscribe( - event: string | symbol, - subscriber: Subscriber, - context?: EventBusTypes.SubscriberContext - ): this { - return this - } - - withTransaction() {} -} diff --git a/packages/product/integration-tests/__tests__/module.ts b/packages/product/integration-tests/__tests__/module.ts deleted file mode 100644 index 35e83bd6aa..0000000000 --- a/packages/product/integration-tests/__tests__/module.ts +++ /dev/null @@ -1,482 +0,0 @@ -import { MedusaModule, Modules } from "@medusajs/modules-sdk" -import { IProductModuleService } from "@medusajs/types" -import { kebabCase } from "@medusajs/utils" -import { knex } from "@mikro-orm/knex" -import { initModules } from "medusa-test-utils" -import * as CustomRepositories from "../__fixtures__/module" -import { - buildProductAndRelationsData, - createProductAndTags, -} from "../__fixtures__/product" -import { productsData } from "../__fixtures__/product/data" -import { DB_URL, TestDatabase } from "../utils" -import { getInitModuleConfig } from "../utils/get-init-module-config" - -const sharedPgConnection = knex({ - client: "pg", - searchPath: process.env.MEDUSA_PRODUCT_DB_SCHEMA, - connection: { - connectionString: DB_URL, - }, -}) - -const beforeEach_ = async () => { - await TestDatabase.setupDatabase() - return await TestDatabase.forkManager() -} - -const afterEach_ = async () => { - await TestDatabase.clearDatabase() -} - -describe("Product module", function () { - describe("Using built-in data access layer", function () { - let module: IProductModuleService - let shutdownFunc: () => Promise - - beforeAll(async () => { - MedusaModule.clearInstances() - const initModulesConfig = getInitModuleConfig() - - const { medusaApp, shutdown } = await initModules(initModulesConfig) - - module = medusaApp.modules[Modules.PRODUCT] - - shutdownFunc = shutdown - }) - - afterAll(async () => { - await shutdownFunc() - }) - - beforeEach(async () => { - const testManager = await beforeEach_() - await createProductAndTags(testManager, productsData) - }) - - afterEach(afterEach_) - - it("should initialize", async () => { - expect(module).toBeDefined() - }) - - it("should have a connection that is not the shared connection", async () => { - expect( - (module as any).baseRepository_.manager_.getConnection().client - ).not.toEqual(sharedPgConnection) - }) - - it("should return a list of product", async () => { - const products = await module.list() - expect(products).toHaveLength(3) - }) - }) - - describe("Using custom data access layer", function () { - let module - let shutdownFunc: () => Promise - - beforeAll(async () => { - MedusaModule.clearInstances() - const initModulesConfig = getInitModuleConfig({ - repositories: CustomRepositories, - }) - - const { medusaApp, shutdown } = await initModules(initModulesConfig) - - module = medusaApp.modules[Modules.PRODUCT] - - shutdownFunc = shutdown - }) - - afterAll(async () => { - await shutdownFunc() - }) - - beforeEach(async () => { - const testManager = await beforeEach_() - await createProductAndTags(testManager, productsData) - }) - - afterEach(afterEach_) - - it("should initialize", async () => { - expect(module).toBeDefined() - }) - - it("should have a connection that is not the shared connection", async () => { - expect( - (module as any).baseRepository_.manager_.getConnection().client - ).not.toEqual(sharedPgConnection) - }) - - it("should return a list of product", async () => { - const products = await module.list() - - expect(module.productService_.productRepository_.find).toHaveBeenCalled() - expect(products).toHaveLength(0) - }) - }) - - describe("Using custom data access layer and manager", function () { - let module - let shutdownFunc: () => Promise - - afterEach(async () => { - await shutdownFunc() - }) - - beforeEach(async () => { - const testManager = await beforeEach_() - await createProductAndTags(testManager, productsData) - - MedusaModule.clearInstances() - const initModulesConfig = getInitModuleConfig({ - manager: testManager, - repositories: CustomRepositories, - }) - - const { medusaApp, shutdown } = await initModules(initModulesConfig) - - module = medusaApp.modules[Modules.PRODUCT] - - shutdownFunc = shutdown - }) - - afterEach(afterEach_) - - it("should initialize and return a list of product", async () => { - expect(module).toBeDefined() - }) - - it("should have a connection that is not the shared connection", async () => { - expect( - (module as any).baseRepository_.manager_.getConnection().client - ).not.toEqual(sharedPgConnection) - }) - - it("should return a list of product", async () => { - const products = await module.list() - - expect(module.productService_.productRepository_.find).toHaveBeenCalled() - expect(products).toHaveLength(0) - }) - }) - - describe("Using an existing connection", function () { - let module: IProductModuleService - let shutdownFunc: () => Promise - - afterEach(async () => { - await shutdownFunc() - }) - - beforeEach(async () => { - const testManager = await beforeEach_() - await createProductAndTags(testManager, productsData) - - MedusaModule.clearInstances() - const initModulesConfig = getInitModuleConfig({ - database: { - connection: sharedPgConnection, - }, - }) - - const { medusaApp, shutdown } = await initModules(initModulesConfig) - - module = medusaApp.modules[Modules.PRODUCT] - - shutdownFunc = shutdown - }) - - afterEach(afterEach_) - - it("should initialize and return a list of product", async () => { - expect(module).toBeDefined() - }) - - it("should have a connection that is the shared connection", async () => { - expect( - JSON.stringify( - (module as any).baseRepository_.manager_.getConnection().client - ) - ).toEqual(JSON.stringify(sharedPgConnection)) - }) - }) - - describe("create", function () { - let module: IProductModuleService - let images = ["image-1"] - let shutdownFunc: () => Promise - - afterEach(async () => { - await shutdownFunc() - }) - - beforeEach(async () => { - await beforeEach_() - - MedusaModule.clearInstances() - const initModulesConfig = getInitModuleConfig({ - database: { - connection: sharedPgConnection, - }, - }) - - const { medusaApp, shutdown } = await initModules(initModulesConfig) - - module = medusaApp.modules[Modules.PRODUCT] - - shutdownFunc = shutdown - }) - - afterEach(afterEach_) - - it("should create a product", async () => { - const data = buildProductAndRelationsData({ - images, - thumbnail: images[0], - }) - - const productsCreated = await module.create([data]) - - const products = await module.list( - { id: productsCreated[0].id }, - { - relations: [ - "images", - "categories", - "variants", - "variants.options", - "options", - "options.values", - "tags", - "type", - ], - } - ) - - expect(products).toHaveLength(1) - - expect(products[0].images).toHaveLength(1) - expect(products[0].options).toHaveLength(1) - expect(products[0].tags).toHaveLength(1) - expect(products[0].categories).toHaveLength(0) - expect(products[0].variants).toHaveLength(1) - - expect(products[0]).toEqual( - expect.objectContaining({ - id: expect.any(String), - title: data.title, - handle: kebabCase(data.title), - description: data.description, - subtitle: data.subtitle, - is_giftcard: data.is_giftcard, - discountable: data.discountable, - thumbnail: images[0], - status: data.status, - images: expect.arrayContaining([ - expect.objectContaining({ - id: expect.any(String), - url: images[0], - }), - ]), - options: expect.arrayContaining([ - expect.objectContaining({ - id: expect.any(String), - title: data.options[0].title, - values: expect.arrayContaining([ - expect.objectContaining({ - id: expect.any(String), - value: data.variants[0].options?.[0].value, - }), - ]), - }), - ]), - tags: expect.arrayContaining([ - expect.objectContaining({ - id: expect.any(String), - value: data.tags[0].value, - }), - ]), - type: expect.objectContaining({ - id: expect.any(String), - value: data.type.value, - }), - variants: expect.arrayContaining([ - expect.objectContaining({ - id: expect.any(String), - title: data.variants[0].title, - sku: data.variants[0].sku, - allow_backorder: false, - manage_inventory: true, - inventory_quantity: 100, - variant_rank: 0, - options: expect.arrayContaining([ - expect.objectContaining({ - id: expect.any(String), - value: data.variants[0].options?.[0].value, - }), - ]), - }), - ]), - }) - ) - }) - }) - - describe("softDelete", function () { - let module: IProductModuleService - let images = ["image-1"] - let shutdownFunc: () => Promise - - afterEach(async () => { - await shutdownFunc() - }) - - beforeEach(async () => { - await beforeEach_() - - MedusaModule.clearInstances() - - const initModulesConfig = getInitModuleConfig() - - const { medusaApp, shutdown } = await initModules(initModulesConfig) - - module = medusaApp.modules[Modules.PRODUCT] - - shutdownFunc = shutdown - }) - - afterEach(afterEach_) - - it("should soft delete a product and its cascaded relations", async () => { - const data = buildProductAndRelationsData({ - images, - thumbnail: images[0], - }) - - const products = await module.create([data]) - - await module.softDelete([products[0].id]) - const deletedProducts = await module.list( - { id: products[0].id }, - { - relations: [ - "variants", - "variants.options", - "options", - "options.values", - ], - withDeleted: true, - } - ) - - expect(deletedProducts).toHaveLength(1) - expect(deletedProducts[0].deleted_at).not.toBeNull() - - for (const option of deletedProducts[0].options) { - expect(option.deleted_at).not.toBeNull() - } - - const productOptionsValues = deletedProducts[0].options - .map((o) => o.values) - .flat() - - for (const optionValue of productOptionsValues) { - expect(optionValue.deleted_at).not.toBeNull() - } - - for (const variant of deletedProducts[0].variants) { - expect(variant.deleted_at).not.toBeNull() - } - - const variantsOptions = deletedProducts[0].options - .map((o) => o.values) - .flat() - - for (const option of variantsOptions) { - expect(option.deleted_at).not.toBeNull() - } - }) - }) - - describe("restore", function () { - let module: IProductModuleService - let images = ["image-1"] - let shutdownFunc: () => Promise - - afterEach(async () => { - await shutdownFunc() - }) - - beforeEach(async () => { - await beforeEach_() - - MedusaModule.clearInstances() - - const initModulesConfig = getInitModuleConfig() - - const { medusaApp, shutdown } = await initModules(initModulesConfig) - - module = medusaApp.modules[Modules.PRODUCT] - - shutdownFunc = shutdown - }) - - afterEach(afterEach_) - - it("should restore a soft deleted product and its cascaded relations", async () => { - const data = buildProductAndRelationsData({ - images, - thumbnail: images[0], - }) - - const products = await module.create([data]) - - await module.softDelete([products[0].id]) - await module.restore([products[0].id]) - - const deletedProducts = await module.list( - { id: products[0].id }, - { - relations: [ - "variants", - "variants.options", - "variants.options", - "options", - "options.values", - ], - withDeleted: true, - } - ) - - expect(deletedProducts).toHaveLength(1) - expect(deletedProducts[0].deleted_at).toBeNull() - - for (const option of deletedProducts[0].options) { - expect(option.deleted_at).toBeNull() - } - - const productOptionsValues = deletedProducts[0].options - .map((o) => o.values) - .flat() - - for (const optionValue of productOptionsValues) { - expect(optionValue.deleted_at).toBeNull() - } - - for (const variant of deletedProducts[0].variants) { - expect(variant.deleted_at).toBeNull() - } - - const variantsOptions = deletedProducts[0].options - .map((o) => o.values) - .flat() - - for (const option of variantsOptions) { - expect(option.deleted_at).toBeNull() - } - }) - }) -}) diff --git a/packages/product/integration-tests/__tests__/services/product-category/index.ts b/packages/product/integration-tests/__tests__/services/product-category/index.ts index a9d0ae6922..3822f56ee6 100644 --- a/packages/product/integration-tests/__tests__/services/product-category/index.ts +++ b/packages/product/integration-tests/__tests__/services/product-category/index.ts @@ -1,6 +1,3 @@ -import { SqlEntityManager } from "@mikro-orm/postgresql" - -import { ProductCategory } from "@models" import { ProductCategoryService } from "@services" import { createProductCategories } from "../../../__fixtures__/product-category" @@ -8,864 +5,849 @@ import { productCategoriesData, productCategoriesRankData, } from "../../../__fixtures__/product-category/data" -import { TestDatabase } from "../../../utils" -import { createMedusaContainer } from "@medusajs/utils" -import { asValue } from "awilix" -import ContainerLoader from "../../../../src/loaders/container" +import { Modules } from "@medusajs/modules-sdk" +import { moduleIntegrationTestRunner, SuiteOptions } from "medusa-test-utils" +import { IProductModuleService } from "@medusajs/types" jest.setTimeout(30000) -describe("Product category Service", () => { - let service: ProductCategoryService - let testManager: SqlEntityManager - let repositoryManager: SqlEntityManager - let productCategories!: ProductCategory[] +moduleIntegrationTestRunner({ + moduleName: Modules.PRODUCT, + testSuite: ({ + MikroOrmWrapper, + medusaApp, + }: SuiteOptions) => { + describe("Product category Service", () => { + let service: ProductCategoryService - beforeEach(async () => { - await TestDatabase.setupDatabase() - repositoryManager = await TestDatabase.forkManager() - - const container = createMedusaContainer() - container.register("manager", asValue(repositoryManager)) - - await ContainerLoader({ container }) - - service = container.resolve("productCategoryService") - }) - - afterEach(async () => { - await TestDatabase.clearDatabase() - }) - - describe("list", () => { - beforeEach(async () => { - testManager = await TestDatabase.forkManager() - - productCategories = await createProductCategories( - testManager, - productCategoriesData - ) - }) - - it("lists all product categories", async () => { - const productCategoryResults = await service.list( - {}, - { - select: ["id", "parent_category_id"], - } - ) - - expect(productCategoryResults).toEqual([ - expect.objectContaining({ - id: "category-0", - parent_category_id: null, - }), - expect.objectContaining({ - id: "category-1", - parent_category: expect.objectContaining({ - id: "category-0", - }), - }), - expect.objectContaining({ - id: "category-1-a", - parent_category: expect.objectContaining({ - id: "category-1", - }), - }), - expect.objectContaining({ - id: "category-1-b", - parent_category: expect.objectContaining({ - id: "category-1", - }), - }), - expect.objectContaining({ - id: "category-1-b-1", - parent_category: expect.objectContaining({ - id: "category-1-b", - }), - }), - ]) - }) - - it("scopes product categories by parent_category_id", async () => { - let productCategoryResults = await service.list( - { parent_category_id: null }, - { - select: ["id"], - } - ) - - expect(productCategoryResults).toEqual([ - expect.objectContaining({ - id: "category-0", - }), - ]) - - productCategoryResults = await service.list({ - parent_category_id: "category-0", + beforeEach(() => { + service = medusaApp.modules["productService"].productCategoryService_ }) - expect(productCategoryResults).toEqual([ - expect.objectContaining({ - id: "category-1", - }), - ]) + describe("list", () => { + beforeEach(async () => { + await createProductCategories( + MikroOrmWrapper.forkManager(), + productCategoriesData + ) + }) - productCategoryResults = await service.list({ - parent_category_id: ["category-1-b", "category-0"], - }) + it("lists all product categories", async () => { + const productCategoryResults = await service.list( + {}, + { + select: ["id", "parent_category_id"], + } + ) - expect(productCategoryResults).toEqual([ - expect.objectContaining({ - id: "category-1", - }), - expect.objectContaining({ - id: "category-1-b-1", - }), - ]) - }) - - it("includes the entire list of descendants when include_descendants_tree is true", async () => { - const productCategoryResults = await service.list( - { - parent_category_id: null, - include_descendants_tree: true, - }, - { - select: ["id", "handle"], - } - ) - - const serializedObject = JSON.parse( - JSON.stringify(productCategoryResults) - ) - - expect(serializedObject).toEqual([ - expect.objectContaining({ - id: "category-0", - handle: "category-0", - mpath: "category-0.", - parent_category_id: null, - parent_category: null, - category_children: [ + expect(productCategoryResults).toEqual([ + expect.objectContaining({ + id: "category-0", + parent_category_id: null, + }), expect.objectContaining({ id: "category-1", - handle: "category-1", - mpath: "category-0.category-1.", - parent_category_id: "category-0", - parent_category: "category-0", - category_children: [ - expect.objectContaining({ - id: "category-1-a", - handle: "category-1-a", - mpath: "category-0.category-1.category-1-a.", - parent_category_id: "category-1", - parent_category: "category-1", - category_children: [], - }), - expect.objectContaining({ - id: "category-1-b", - handle: "category-1-b", - mpath: "category-0.category-1.category-1-b.", - parent_category_id: "category-1", - parent_category: "category-1", - category_children: [ - expect.objectContaining({ - id: "category-1-b-1", - handle: "category-1-b-1", - mpath: - "category-0.category-1.category-1-b.category-1-b-1.", - parent_category_id: "category-1-b", - parent_category: "category-1-b", - category_children: [], - }), - ], - }), - ], + parent_category: expect.objectContaining({ + id: "category-0", + }), }), - ], - }), - ]) - }) - - it("scopes children when include_descendants_tree is true", async () => { - const productCategoryResults = await service.list( - { - parent_category_id: null, - include_descendants_tree: true, - is_internal: false, - }, - { - select: ["id", "handle"], - } - ) - - const serializedObject = JSON.parse( - JSON.stringify(productCategoryResults) - ) - - expect(serializedObject).toEqual([ - expect.objectContaining({ - id: "category-0", - handle: "category-0", - mpath: "category-0.", - parent_category_id: null, - parent_category: null, - category_children: [ - expect.objectContaining({ - id: "category-1", - handle: "category-1", - mpath: "category-0.category-1.", - parent_category_id: "category-0", - parent_category: "category-0", - category_children: [ - expect.objectContaining({ - id: "category-1-a", - handle: "category-1-a", - mpath: "category-0.category-1.category-1-a.", - parent_category_id: "category-1", - parent_category: "category-1", - category_children: [], - }), - ], - }), - ], - }), - ]) - }) - }) - - describe("retrieve", () => { - const categoryOneId = "category-1" - - beforeEach(async () => { - testManager = await TestDatabase.forkManager() - - productCategories = await createProductCategories( - testManager, - productCategoriesData - ) - }) - - it("should return category for the given id", async () => { - const productCategoryResults = await service.retrieve(categoryOneId) - - expect(productCategoryResults).toEqual( - expect.objectContaining({ - id: categoryOneId, - }) - ) - }) - - it("should throw an error when category with id does not exist", async () => { - let error - - try { - await service.retrieve("does-not-exist") - } catch (e) { - error = e - } - - expect(error.message).toEqual( - "ProductCategory with id: does-not-exist was not found" - ) - }) - - it("should throw an error when an id is not provided", async () => { - let error - - try { - await service.retrieve(undefined as unknown as string) - } catch (e) { - error = e - } - - expect(error.message).toEqual('"productCategoryId" must be defined') - }) - - it("should return category based on config select param", async () => { - const productCategoryResults = await service.retrieve(categoryOneId, { - select: ["id", "parent_category_id"], - }) - - expect(productCategoryResults).toEqual( - expect.objectContaining({ - id: categoryOneId, - parent_category_id: "category-0", - }) - ) - }) - - it("should return category based on config relation param", async () => { - const productCategoryResults = await service.retrieve(categoryOneId, { - select: ["id", "parent_category_id"], - relations: ["parent_category"], - }) - - expect(productCategoryResults).toEqual( - expect.objectContaining({ - id: categoryOneId, - category_children: [ expect.objectContaining({ id: "category-1-a", + parent_category: expect.objectContaining({ + id: "category-1", + }), }), expect.objectContaining({ id: "category-1-b", + parent_category: expect.objectContaining({ + id: "category-1", + }), }), - ], - parent_category: expect.objectContaining({ - id: "category-0", - }), + expect.objectContaining({ + id: "category-1-b-1", + parent_category: expect.objectContaining({ + id: "category-1-b", + }), + }), + ]) }) - ) - }) - }) - describe("listAndCount", () => { - beforeEach(async () => { - testManager = await TestDatabase.forkManager() + it("scopes product categories by parent_category_id", async () => { + let productCategoryResults = await service.list( + { parent_category_id: null }, + { + select: ["id"], + } + ) - productCategories = await createProductCategories( - testManager, - productCategoriesData - ) - }) + expect(productCategoryResults).toEqual([ + expect.objectContaining({ + id: "category-0", + }), + ]) - it("should return categories and count based on take and skip", async () => { - let results = await service.listAndCount( - {}, - { - take: 1, - } - ) + productCategoryResults = await service.list({ + parent_category_id: "category-0", + }) - expect(results[1]).toEqual(5) - expect(results[0]).toEqual([ - expect.objectContaining({ - id: "category-0", - }), - ]) - - results = await service.listAndCount( - {}, - { - take: 1, - skip: 1, - } - ) - - expect(results[1]).toEqual(5) - expect(results[0]).toEqual([ - expect.objectContaining({ - id: "category-1", - }), - ]) - }) - - it("should return all product categories and count", async () => { - const productCategoryResults = await service.listAndCount( - {}, - { - select: ["id", "parent_category_id"], - relations: ["parent_category"], - } - ) - - expect(productCategoryResults[1]).toEqual(5) - expect(productCategoryResults[0]).toEqual([ - expect.objectContaining({ - id: "category-0", - parent_category: null, - }), - expect.objectContaining({ - id: "category-1", - parent_category: expect.objectContaining({ - id: "category-0", - }), - }), - expect.objectContaining({ - id: "category-1-a", - parent_category: expect.objectContaining({ - id: "category-1", - }), - }), - expect.objectContaining({ - id: "category-1-b", - parent_category: expect.objectContaining({ - id: "category-1", - }), - }), - expect.objectContaining({ - id: "category-1-b-1", - parent_category: expect.objectContaining({ - id: "category-1-b", - }), - }), - ]) - }) - - it("should only return categories that are scoped by parent_category_id", async () => { - let productCategoryResults = await service.listAndCount( - { parent_category_id: null }, - { - select: ["id"], - } - ) - - expect(productCategoryResults[1]).toEqual(1) - expect(productCategoryResults[0]).toEqual([ - expect.objectContaining({ - id: "category-0", - }), - ]) - - productCategoryResults = await service.listAndCount({ - parent_category_id: "category-0", - }) - - expect(productCategoryResults[1]).toEqual(1) - expect(productCategoryResults[0]).toEqual([ - expect.objectContaining({ - id: "category-1", - }), - ]) - - productCategoryResults = await service.listAndCount({ - parent_category_id: ["category-1-b", "category-0"], - }) - - expect(productCategoryResults[1]).toEqual(2) - expect(productCategoryResults[0]).toEqual([ - expect.objectContaining({ - id: "category-1", - }), - expect.objectContaining({ - id: "category-1-b-1", - }), - ]) - }) - - it("should includes descendants when include_descendants_tree is true", async () => { - const productCategoryResults = await service.listAndCount( - { - parent_category_id: null, - include_descendants_tree: true, - }, - { - select: ["id", "handle"], - } - ) - - expect(productCategoryResults[1]).toEqual(1) - - const serializedObject = JSON.parse( - JSON.stringify(productCategoryResults[0]) - ) - - expect(serializedObject).toEqual([ - expect.objectContaining({ - id: "category-0", - handle: "category-0", - mpath: "category-0.", - parent_category_id: null, - parent_category: null, - category_children: [ + expect(productCategoryResults).toEqual([ expect.objectContaining({ id: "category-1", - handle: "category-1", - mpath: "category-0.category-1.", - parent_category_id: "category-0", - parent_category: "category-0", + }), + ]) + + productCategoryResults = await service.list({ + parent_category_id: ["category-1-b", "category-0"], + }) + + expect(productCategoryResults).toEqual([ + expect.objectContaining({ + id: "category-1", + }), + expect.objectContaining({ + id: "category-1-b-1", + }), + ]) + }) + + it("includes the entire list of descendants when include_descendants_tree is true", async () => { + const productCategoryResults = await service.list( + { + parent_category_id: null, + include_descendants_tree: true, + }, + { + select: ["id", "handle"], + } + ) + + const serializedObject = JSON.parse( + JSON.stringify(productCategoryResults) + ) + + expect(serializedObject).toEqual([ + expect.objectContaining({ + id: "category-0", + handle: "category-0", + mpath: "category-0.", + parent_category_id: null, + parent_category: null, category_children: [ expect.objectContaining({ - id: "category-1-a", - handle: "category-1-a", - mpath: "category-0.category-1.category-1-a.", - parent_category_id: "category-1", - parent_category: "category-1", - category_children: [], - }), - expect.objectContaining({ - id: "category-1-b", - handle: "category-1-b", - mpath: "category-0.category-1.category-1-b.", - parent_category_id: "category-1", - parent_category: "category-1", + id: "category-1", + handle: "category-1", + mpath: "category-0.category-1.", + parent_category_id: "category-0", + parent_category: "category-0", category_children: [ expect.objectContaining({ - id: "category-1-b-1", - handle: "category-1-b-1", - mpath: - "category-0.category-1.category-1-b.category-1-b-1.", - parent_category_id: "category-1-b", - parent_category: "category-1-b", + id: "category-1-a", + handle: "category-1-a", + mpath: "category-0.category-1.category-1-a.", + parent_category_id: "category-1", + parent_category: "category-1", + category_children: [], + }), + expect.objectContaining({ + id: "category-1-b", + handle: "category-1-b", + mpath: "category-0.category-1.category-1-b.", + parent_category_id: "category-1", + parent_category: "category-1", + category_children: [ + expect.objectContaining({ + id: "category-1-b-1", + handle: "category-1-b-1", + mpath: + "category-0.category-1.category-1-b.category-1-b-1.", + parent_category_id: "category-1-b", + parent_category: "category-1-b", + category_children: [], + }), + ], + }), + ], + }), + ], + }), + ]) + }) + + it("scopes children when include_descendants_tree is true", async () => { + const productCategoryResults = await service.list( + { + parent_category_id: null, + include_descendants_tree: true, + is_internal: false, + }, + { + select: ["id", "handle"], + } + ) + + const serializedObject = JSON.parse( + JSON.stringify(productCategoryResults) + ) + + expect(serializedObject).toEqual([ + expect.objectContaining({ + id: "category-0", + handle: "category-0", + mpath: "category-0.", + parent_category_id: null, + parent_category: null, + category_children: [ + expect.objectContaining({ + id: "category-1", + handle: "category-1", + mpath: "category-0.category-1.", + parent_category_id: "category-0", + parent_category: "category-0", + category_children: [ + expect.objectContaining({ + id: "category-1-a", + handle: "category-1-a", + mpath: "category-0.category-1.category-1-a.", + parent_category_id: "category-1", + parent_category: "category-1", category_children: [], }), ], }), ], }), - ], - }), - ]) - }) + ]) + }) + }) - it("should filter out children when include_descendants_tree is true", async () => { - const productCategoryResults = await service.listAndCount( - { - parent_category_id: null, - include_descendants_tree: true, - is_internal: false, - }, - { - select: ["id", "handle"], - } - ) + describe("retrieve", () => { + const categoryOneId = "category-1" - expect(productCategoryResults[1]).toEqual(1) + beforeEach(async () => { + await createProductCategories( + MikroOrmWrapper.forkManager(), + productCategoriesData + ) + }) - const serializedObject = JSON.parse( - JSON.stringify(productCategoryResults[0]) - ) + it("should return category for the given id", async () => { + const productCategoryResults = await service.retrieve(categoryOneId) - expect(serializedObject).toEqual([ - expect.objectContaining({ - id: "category-0", - handle: "category-0", - mpath: "category-0.", - parent_category_id: null, - parent_category: null, - category_children: [ + expect(productCategoryResults).toEqual( expect.objectContaining({ - id: "category-1", - handle: "category-1", - mpath: "category-0.category-1.", + id: categoryOneId, + }) + ) + }) + + it("should throw an error when category with id does not exist", async () => { + let error + + try { + await service.retrieve("does-not-exist") + } catch (e) { + error = e + } + + expect(error.message).toEqual( + "ProductCategory with id: does-not-exist was not found" + ) + }) + + it("should throw an error when an id is not provided", async () => { + let error + + try { + await service.retrieve(undefined as unknown as string) + } catch (e) { + error = e + } + + expect(error.message).toEqual('"productCategoryId" must be defined') + }) + + it("should return category based on config select param", async () => { + const productCategoryResults = await service.retrieve(categoryOneId, { + select: ["id", "parent_category_id"], + }) + + expect(productCategoryResults).toEqual( + expect.objectContaining({ + id: categoryOneId, parent_category_id: "category-0", - parent_category: "category-0", + }) + ) + }) + + it("should return category based on config relation param", async () => { + const productCategoryResults = await service.retrieve(categoryOneId, { + select: ["id", "parent_category_id"], + relations: ["parent_category"], + }) + + expect(productCategoryResults).toEqual( + expect.objectContaining({ + id: categoryOneId, category_children: [ expect.objectContaining({ id: "category-1-a", - handle: "category-1-a", - mpath: "category-0.category-1.category-1-a.", - parent_category_id: "category-1", - parent_category: "category-1", - category_children: [], + }), + expect.objectContaining({ + id: "category-1-b", + }), + ], + parent_category: expect.objectContaining({ + id: "category-0", + }), + }) + ) + }) + }) + + describe("listAndCount", () => { + beforeEach(async () => { + await createProductCategories( + MikroOrmWrapper.forkManager(), + productCategoriesData + ) + }) + + it("should return categories and count based on take and skip", async () => { + let results = await service.listAndCount( + {}, + { + take: 1, + } + ) + + expect(results[1]).toEqual(5) + expect(results[0]).toEqual([ + expect.objectContaining({ + id: "category-0", + }), + ]) + + results = await service.listAndCount( + {}, + { + take: 1, + skip: 1, + } + ) + + expect(results[1]).toEqual(5) + expect(results[0]).toEqual([ + expect.objectContaining({ + id: "category-1", + }), + ]) + }) + + it("should return all product categories and count", async () => { + const productCategoryResults = await service.listAndCount( + {}, + { + select: ["id", "parent_category_id"], + relations: ["parent_category"], + } + ) + + expect(productCategoryResults[1]).toEqual(5) + expect(productCategoryResults[0]).toEqual([ + expect.objectContaining({ + id: "category-0", + parent_category: null, + }), + expect.objectContaining({ + id: "category-1", + parent_category: expect.objectContaining({ + id: "category-0", + }), + }), + expect.objectContaining({ + id: "category-1-a", + parent_category: expect.objectContaining({ + id: "category-1", + }), + }), + expect.objectContaining({ + id: "category-1-b", + parent_category: expect.objectContaining({ + id: "category-1", + }), + }), + expect.objectContaining({ + id: "category-1-b-1", + parent_category: expect.objectContaining({ + id: "category-1-b", + }), + }), + ]) + }) + + it("should only return categories that are scoped by parent_category_id", async () => { + let productCategoryResults = await service.listAndCount( + { parent_category_id: null }, + { + select: ["id"], + } + ) + + expect(productCategoryResults[1]).toEqual(1) + expect(productCategoryResults[0]).toEqual([ + expect.objectContaining({ + id: "category-0", + }), + ]) + + productCategoryResults = await service.listAndCount({ + parent_category_id: "category-0", + }) + + expect(productCategoryResults[1]).toEqual(1) + expect(productCategoryResults[0]).toEqual([ + expect.objectContaining({ + id: "category-1", + }), + ]) + + productCategoryResults = await service.listAndCount({ + parent_category_id: ["category-1-b", "category-0"], + }) + + expect(productCategoryResults[1]).toEqual(2) + expect(productCategoryResults[0]).toEqual([ + expect.objectContaining({ + id: "category-1", + }), + expect.objectContaining({ + id: "category-1-b-1", + }), + ]) + }) + + it("should includes descendants when include_descendants_tree is true", async () => { + const productCategoryResults = await service.listAndCount( + { + parent_category_id: null, + include_descendants_tree: true, + }, + { + select: ["id", "handle"], + } + ) + + expect(productCategoryResults[1]).toEqual(1) + + const serializedObject = JSON.parse( + JSON.stringify(productCategoryResults[0]) + ) + + expect(serializedObject).toEqual([ + expect.objectContaining({ + id: "category-0", + handle: "category-0", + mpath: "category-0.", + parent_category_id: null, + parent_category: null, + category_children: [ + expect.objectContaining({ + id: "category-1", + handle: "category-1", + mpath: "category-0.category-1.", + parent_category_id: "category-0", + parent_category: "category-0", + category_children: [ + expect.objectContaining({ + id: "category-1-a", + handle: "category-1-a", + mpath: "category-0.category-1.category-1-a.", + parent_category_id: "category-1", + parent_category: "category-1", + category_children: [], + }), + expect.objectContaining({ + id: "category-1-b", + handle: "category-1-b", + mpath: "category-0.category-1.category-1-b.", + parent_category_id: "category-1", + parent_category: "category-1", + category_children: [ + expect.objectContaining({ + id: "category-1-b-1", + handle: "category-1-b-1", + mpath: + "category-0.category-1.category-1-b.category-1-b-1.", + parent_category_id: "category-1-b", + parent_category: "category-1-b", + category_children: [], + }), + ], + }), + ], }), ], }), - ], - }), - ]) - }) - }) - - describe("create", () => { - it("should create a category successfully", async () => { - await service.create({ - name: "New Category", - parent_category_id: null, - }) - - const [productCategory] = await service.list( - { - name: "New Category", - }, - { - select: ["name", "rank"], - } - ) - - expect(productCategory).toEqual( - expect.objectContaining({ - name: "New Category", - rank: "0", + ]) }) - ) - }) - it("should append rank from an existing category depending on parent", async () => { - await service.create({ - name: "New Category", - parent_category_id: null, - rank: 0, - }) + it("should filter out children when include_descendants_tree is true", async () => { + const productCategoryResults = await service.listAndCount( + { + parent_category_id: null, + include_descendants_tree: true, + is_internal: false, + }, + { + select: ["id", "handle"], + } + ) - await service.create({ - name: "New Category 2", - parent_category_id: null, - }) + expect(productCategoryResults[1]).toEqual(1) - const [productCategoryNew] = await service.list( - { - name: "New Category 2", - }, - { - select: ["name", "rank"], - } - ) + const serializedObject = JSON.parse( + JSON.stringify(productCategoryResults[0]) + ) - expect(productCategoryNew).toEqual( - expect.objectContaining({ - name: "New Category 2", - rank: "1", + expect(serializedObject).toEqual([ + expect.objectContaining({ + id: "category-0", + handle: "category-0", + mpath: "category-0.", + parent_category_id: null, + parent_category: null, + category_children: [ + expect.objectContaining({ + id: "category-1", + handle: "category-1", + mpath: "category-0.category-1.", + parent_category_id: "category-0", + parent_category: "category-0", + category_children: [ + expect.objectContaining({ + id: "category-1-a", + handle: "category-1-a", + mpath: "category-0.category-1.category-1-a.", + parent_category_id: "category-1", + parent_category: "category-1", + category_children: [], + }), + ], + }), + ], + }), + ]) }) - ) - - await service.create({ - name: "New Category 2.1", - parent_category_id: productCategoryNew.id, }) - const [productCategoryWithParent] = await service.list( - { - name: "New Category 2.1", - }, - { - select: ["name", "rank", "parent_category_id"], - } - ) + describe("create", () => { + it("should create a category successfully", async () => { + await service.create({ + name: "New Category", + parent_category_id: null, + }) - expect(productCategoryWithParent).toEqual( - expect.objectContaining({ - name: "New Category 2.1", - parent_category_id: productCategoryNew.id, - rank: "0", + const [productCategory] = await service.list( + { + name: "New Category", + }, + { + select: ["name", "rank"], + } + ) + + expect(productCategory).toEqual( + expect.objectContaining({ + name: "New Category", + rank: "0", + }) + ) }) - ) - }) - }) - describe("update", () => { - let productCategoryZero - let productCategoryOne - let productCategoryTwo - let productCategoryZeroZero - let productCategoryZeroOne - let productCategoryZeroTwo - let categories + it("should append rank from an existing category depending on parent", async () => { + await service.create({ + name: "New Category", + parent_category_id: null, + rank: 0, + }) - beforeEach(async () => { - testManager = await TestDatabase.forkManager() + await service.create({ + name: "New Category 2", + parent_category_id: null, + }) - categories = await createProductCategories( - testManager, - productCategoriesRankData - ) + const [productCategoryNew] = await service.list( + { + name: "New Category 2", + }, + { + select: ["name", "rank"], + } + ) - productCategoryZero = categories[0] - productCategoryOne = categories[1] - productCategoryTwo = categories[2] - productCategoryZeroZero = categories[3] - productCategoryZeroOne = categories[4] - productCategoryZeroTwo = categories[5] - }) + expect(productCategoryNew).toEqual( + expect.objectContaining({ + name: "New Category 2", + rank: "1", + }) + ) - it("should update the name of the category successfully", async () => { - await service.update(productCategoryZero.id, { - name: "New Category", - }) + await service.create({ + name: "New Category 2.1", + parent_category_id: productCategoryNew.id, + }) - const productCategory = await service.retrieve(productCategoryZero.id, { - select: ["name"], - }) + const [productCategoryWithParent] = await service.list( + { + name: "New Category 2.1", + }, + { + select: ["name", "rank", "parent_category_id"], + } + ) - expect(productCategory.name).toEqual("New Category") - }) - - it("should throw an error when an id does not exist", async () => { - let error - - try { - await service.update("does-not-exist", { - name: "New Category", + expect(productCategoryWithParent).toEqual( + expect.objectContaining({ + name: "New Category 2.1", + parent_category_id: productCategoryNew.id, + rank: "0", + }) + ) }) - } catch (e) { - error = e - } - - expect(error.message).toEqual( - `ProductCategory not found ({ id: 'does-not-exist' })` - ) - }) - - it("should reorder rank successfully in the same parent", async () => { - await service.update(productCategoryTwo.id, { - rank: 0, }) - const productCategories = await service.list( - { - parent_category_id: null, - }, - { - select: ["name", "rank"], - } - ) + describe("update", () => { + let productCategoryZero + let productCategoryOne + let productCategoryTwo + let productCategoryZeroZero + let productCategoryZeroOne + let productCategoryZeroTwo + let categories - expect(productCategories).toEqual( - expect.arrayContaining([ - expect.objectContaining({ - id: productCategoryTwo.id, - rank: "0", - }), - expect.objectContaining({ - id: productCategoryZero.id, - rank: "1", - }), - expect.objectContaining({ - id: productCategoryOne.id, - rank: "2", - }), - ]) - ) - }) + beforeEach(async () => { + categories = await createProductCategories( + MikroOrmWrapper.forkManager(), + productCategoriesRankData + ) - it("should reorder rank successfully when changing parent", async () => { - await service.update(productCategoryTwo.id, { - rank: 0, - parent_category_id: productCategoryZero.id, + productCategoryZero = categories[0] + productCategoryOne = categories[1] + productCategoryTwo = categories[2] + productCategoryZeroZero = categories[3] + productCategoryZeroOne = categories[4] + productCategoryZeroTwo = categories[5] + }) + + it("should update the name of the category successfully", async () => { + await service.update(productCategoryZero.id, { + name: "New Category", + }) + + const productCategory = await service.retrieve( + productCategoryZero.id, + { + select: ["name"], + } + ) + + expect(productCategory.name).toEqual("New Category") + }) + + it("should throw an error when an id does not exist", async () => { + let error + + try { + await service.update("does-not-exist", { + name: "New Category", + }) + } catch (e) { + error = e + } + + expect(error.message).toEqual( + `ProductCategory not found ({ id: 'does-not-exist' })` + ) + }) + + it("should reorder rank successfully in the same parent", async () => { + await service.update(productCategoryTwo.id, { + rank: 0, + }) + + const productCategories = await service.list( + { + parent_category_id: null, + }, + { + select: ["name", "rank"], + } + ) + + expect(productCategories).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + id: productCategoryTwo.id, + rank: "0", + }), + expect.objectContaining({ + id: productCategoryZero.id, + rank: "1", + }), + expect.objectContaining({ + id: productCategoryOne.id, + rank: "2", + }), + ]) + ) + }) + + it("should reorder rank successfully when changing parent", async () => { + await service.update(productCategoryTwo.id, { + rank: 0, + parent_category_id: productCategoryZero.id, + }) + + const productCategories = await service.list( + { + parent_category_id: productCategoryZero.id, + }, + { + select: ["name", "rank"], + } + ) + + expect(productCategories).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + id: productCategoryTwo.id, + rank: "0", + }), + expect.objectContaining({ + id: productCategoryZeroZero.id, + rank: "1", + }), + expect.objectContaining({ + id: productCategoryZeroOne.id, + rank: "2", + }), + expect.objectContaining({ + id: productCategoryZeroTwo.id, + rank: "3", + }), + ]) + ) + }) + + it("should reorder rank successfully when changing parent and in first position", async () => { + await service.update(productCategoryTwo.id, { + rank: 0, + parent_category_id: productCategoryZero.id, + }) + + const productCategories = await service.list( + { + parent_category_id: productCategoryZero.id, + }, + { + select: ["name", "rank"], + } + ) + + expect(productCategories).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + id: productCategoryTwo.id, + rank: "0", + }), + expect.objectContaining({ + id: productCategoryZeroZero.id, + rank: "1", + }), + expect.objectContaining({ + id: productCategoryZeroOne.id, + rank: "2", + }), + expect.objectContaining({ + id: productCategoryZeroTwo.id, + rank: "3", + }), + ]) + ) + }) }) - const productCategories = await service.list( - { - parent_category_id: productCategoryZero.id, - }, - { - select: ["name", "rank"], - } - ) + describe("delete", () => { + let productCategoryZero + let productCategoryOne + let productCategoryTwo + let categories - expect(productCategories).toEqual( - expect.arrayContaining([ - expect.objectContaining({ - id: productCategoryTwo.id, - rank: "0", - }), - expect.objectContaining({ - id: productCategoryZeroZero.id, - rank: "1", - }), - expect.objectContaining({ - id: productCategoryZeroOne.id, - rank: "2", - }), - expect.objectContaining({ - id: productCategoryZeroTwo.id, - rank: "3", - }), - ]) - ) - }) + beforeEach(async () => { + categories = await createProductCategories( + MikroOrmWrapper.forkManager(), + productCategoriesRankData + ) - it("should reorder rank successfully when changing parent and in first position", async () => { - await service.update(productCategoryTwo.id, { - rank: 0, - parent_category_id: productCategoryZero.id, + productCategoryZero = categories[0] + productCategoryOne = categories[1] + productCategoryTwo = categories[2] + }) + + it("should throw an error when an id does not exist", async () => { + let error + + try { + await service.delete("does-not-exist") + } catch (e) { + error = e + } + + expect(error.message).toEqual( + `ProductCategory not found ({ id: 'does-not-exist' })` + ) + }) + + it("should throw an error when it has children", async () => { + let error + + try { + await service.delete(productCategoryZero.id) + } catch (e) { + error = e + } + + expect(error.message).toEqual( + `Deleting ProductCategory (category-0-0) with category children is not allowed` + ) + }) + + it("should reorder siblings rank successfully on deleting", async () => { + await service.delete(productCategoryOne.id) + + const productCategories = await service.list( + { + parent_category_id: null, + }, + { + select: ["id", "rank"], + } + ) + + expect(productCategories).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + id: productCategoryZero.id, + rank: "0", + }), + expect.objectContaining({ + id: productCategoryTwo.id, + rank: "1", + }), + ]) + ) + }) }) - - const productCategories = await service.list( - { - parent_category_id: productCategoryZero.id, - }, - { - select: ["name", "rank"], - } - ) - - expect(productCategories).toEqual( - expect.arrayContaining([ - expect.objectContaining({ - id: productCategoryTwo.id, - rank: "0", - }), - expect.objectContaining({ - id: productCategoryZeroZero.id, - rank: "1", - }), - expect.objectContaining({ - id: productCategoryZeroOne.id, - rank: "2", - }), - expect.objectContaining({ - id: productCategoryZeroTwo.id, - rank: "3", - }), - ]) - ) }) - }) - - describe("delete", () => { - let productCategoryZero - let productCategoryOne - let productCategoryTwo - let categories - - beforeEach(async () => { - testManager = await TestDatabase.forkManager() - - categories = await createProductCategories( - testManager, - productCategoriesRankData - ) - - productCategoryZero = categories[0] - productCategoryOne = categories[1] - productCategoryTwo = categories[2] - }) - - it("should throw an error when an id does not exist", async () => { - let error - - try { - await service.delete("does-not-exist") - } catch (e) { - error = e - } - - expect(error.message).toEqual( - `ProductCategory not found ({ id: 'does-not-exist' })` - ) - }) - - it("should throw an error when it has children", async () => { - let error - - try { - await service.delete(productCategoryZero.id) - } catch (e) { - error = e - } - - expect(error.message).toEqual( - `Deleting ProductCategory (category-0-0) with category children is not allowed` - ) - }) - - it("should reorder siblings rank successfully on deleting", async () => { - await service.delete(productCategoryOne.id) - - const productCategories = await service.list( - { - parent_category_id: null, - }, - { - select: ["id", "rank"], - } - ) - - expect(productCategories).toEqual( - expect.arrayContaining([ - expect.objectContaining({ - id: productCategoryZero.id, - rank: "0", - }), - expect.objectContaining({ - id: productCategoryTwo.id, - rank: "1", - }), - ]) - ) - }) - }) + }, }) diff --git a/packages/product/integration-tests/__tests__/services/product-collection/index.ts b/packages/product/integration-tests/__tests__/services/product-collection/index.ts index 0f415c00c2..b316feeee7 100644 --- a/packages/product/integration-tests/__tests__/services/product-collection/index.ts +++ b/packages/product/integration-tests/__tests__/services/product-collection/index.ts @@ -1,366 +1,367 @@ -import { SqlEntityManager } from "@mikro-orm/postgresql" - -import { ProductCollection } from "@models" import { ProductCollectionService } from "@services" import { createCollections } from "../../../__fixtures__/product" -import { TestDatabase } from "../../../utils" -import { createMedusaContainer } from "@medusajs/utils" -import { asValue } from "awilix" -import ContainerLoader from "../../../../src/loaders/container" +import { Modules } from "@medusajs/modules-sdk" +import { moduleIntegrationTestRunner, SuiteOptions } from "medusa-test-utils" +import { IProductModuleService } from "@medusajs/types" jest.setTimeout(30000) -describe("Product collection Service", () => { - let service: ProductCollectionService - let testManager: SqlEntityManager - let repositoryManager: SqlEntityManager - let collectionsData: ProductCollection[] = [] +moduleIntegrationTestRunner({ + moduleName: Modules.PRODUCT, + testSuite: ({ + MikroOrmWrapper, + medusaApp, + }: SuiteOptions) => { + describe("Product collection Service", () => { + let service: ProductCollectionService - beforeEach(async () => { - await TestDatabase.setupDatabase() - repositoryManager = await TestDatabase.forkManager() - - const container = createMedusaContainer() - container.register("manager", asValue(repositoryManager)) - - await ContainerLoader({ container }) - - service = container.resolve("productCollectionService") - }) - - afterEach(async () => { - await TestDatabase.clearDatabase() - }) - - describe("list", () => { - const data = [ - { - id: "test-1", - title: "col 1", - }, - { - id: "test-2", - title: "col 2", - }, - { - id: "test-3", - title: "col 3 extra", - }, - { - id: "test-4", - title: "col 4 extra", - }, - ] - - beforeEach(async () => { - testManager = await TestDatabase.forkManager() - - collectionsData = await createCollections(testManager, data) - }) - - it("list product collections", async () => { - const productCollectionResults = await service.list() - - expect(productCollectionResults).toEqual([ - expect.objectContaining({ - id: "test-1", - title: "col 1", - }), - expect.objectContaining({ - id: "test-2", - title: "col 2", - }), - expect.objectContaining({ - id: "test-3", - title: "col 3 extra", - }), - expect.objectContaining({ - id: "test-4", - title: "col 4 extra", - }), - ]) - }) - - it("list product collections by id", async () => { - const productCollectionResults = await service.list({ id: data![0].id }) - - expect(productCollectionResults).toEqual([ - expect.objectContaining({ - id: "test-1", - title: "col 1", - }), - ]) - }) - - it("list product collections by title matching string", async () => { - const productCollectionResults = await service.list({ - title: "col 3 extra", + beforeEach(() => { + service = medusaApp.modules["productService"].productCollectionService_ }) - expect(productCollectionResults).toEqual([ - expect.objectContaining({ - id: "test-3", - title: "col 3 extra", - }), - ]) - }) - }) - - describe("listAndCount", () => { - const data = [ - { - id: "test-1", - title: "col 1", - }, - { - id: "test-2", - title: "col 2", - }, - { - id: "test-3", - title: "col 3 extra", - }, - { - id: "test-4", - title: "col 4 extra", - }, - ] - - beforeEach(async () => { - testManager = await TestDatabase.forkManager() - - collectionsData = await createCollections(testManager, data) - }) - - it("should return all collections and count", async () => { - const [productCollectionResults, count] = await service.listAndCount() - const serialized = JSON.parse(JSON.stringify(productCollectionResults)) - - expect(serialized).toEqual([ - expect.objectContaining({ - id: "test-1", - title: "col 1", - }), - expect.objectContaining({ - id: "test-2", - title: "col 2", - }), - expect.objectContaining({ - id: "test-3", - title: "col 3 extra", - }), - expect.objectContaining({ - id: "test-4", - title: "col 4 extra", - }), - ]) - }) - - it("should return count and collections based on filter data", async () => { - const [productCollectionResults, count] = await service.listAndCount({ - id: data![0].id, - }) - const serialized = JSON.parse(JSON.stringify(productCollectionResults)) - - expect(count).toEqual(1) - expect(serialized).toEqual([ - expect.objectContaining({ - id: "test-1", - title: "col 1", - }), - ]) - }) - - it("should return count and collections based on config data", async () => { - const [productCollectionResults, count] = await service.listAndCount( - {}, - { - relations: ["products"], - select: ["title"], - take: 1, - skip: 1, - } - ) - const serialized = JSON.parse(JSON.stringify(productCollectionResults)) - - expect(count).toEqual(4) - expect(serialized).toEqual([ - { - id: "test-2", - title: "col 2", - products: [], - }, - ]) - }) - }) - - describe("retrieve", () => { - const collectionData = { - id: "collection-1", - title: "collection 1", - } - - beforeEach(async () => { - testManager = await TestDatabase.forkManager() - - await createCollections(testManager, [collectionData]) - }) - - it("should return collection for the given id", async () => { - const productCollectionResults = await service.retrieve(collectionData.id) - - expect(productCollectionResults).toEqual( - expect.objectContaining({ - id: collectionData.id, - }) - ) - }) - - it("should throw an error when collection with id does not exist", async () => { - let error - - try { - await service.retrieve("does-not-exist") - } catch (e) { - error = e - } - - expect(error.message).toEqual( - "ProductCollection with id: does-not-exist was not found" - ) - }) - - it("should throw an error when an id is not provided", async () => { - let error - - try { - await service.retrieve(undefined as unknown as string) - } catch (e) { - error = e - } - - expect(error.message).toEqual("productCollection - id must be defined") - }) - - it("should return collection based on config select param", async () => { - const productCollectionResults = await service.retrieve( - collectionData.id, - { - select: ["id", "title"], - } - ) - - const serialized = JSON.parse(JSON.stringify(productCollectionResults)) - - expect(serialized).toEqual({ - id: collectionData.id, - title: collectionData.title, - }) - }) - - it("should return collection based on config relation param", async () => { - const productCollectionResults = await service.retrieve( - collectionData.id, - { - select: ["id", "title"], - relations: ["products"], - } - ) - - const serialized = JSON.parse(JSON.stringify(productCollectionResults)) - - expect(serialized).toEqual({ - id: collectionData.id, - title: collectionData.title, - products: [], - }) - }) - }) - - describe("delete", () => { - const collectionId = "collection-1" - const collectionData = { - id: collectionId, - title: "collection 1", - } - - beforeEach(async () => { - testManager = await TestDatabase.forkManager() - - await createCollections(testManager, [collectionData]) - }) - - it("should delete the product collection given an ID successfully", async () => { - await service.delete([collectionId]) - - const collections = await service.list({ - id: collectionId, - }) - - expect(collections).toHaveLength(0) - }) - }) - - describe("update", () => { - const collectionId = "collection-1" - const collectionData = { - id: collectionId, - title: "collection 1", - } - - beforeEach(async () => { - testManager = await TestDatabase.forkManager() - - await createCollections(testManager, [collectionData]) - }) - - it("should update the value of the collection successfully", async () => { - await service.update([ - { - id: collectionId, - title: "New Collection", - }, - ]) - - const productCollection = await service.retrieve(collectionId) - - expect(productCollection.title).toEqual("New Collection") - }) - - it("should throw an error when an id does not exist", async () => { - let error - - try { - await service.update([ + describe("list", () => { + const data = [ { - id: "does-not-exist", - title: "New Collection", + id: "test-1", + title: "col 1", }, - ]) - } catch (e) { - error = e - } + { + id: "test-2", + title: "col 2", + }, + { + id: "test-3", + title: "col 3 extra", + }, + { + id: "test-4", + title: "col 4 extra", + }, + ] - expect(error.message).toEqual( - 'ProductCollection with id "does-not-exist" not found' - ) - }) - }) + beforeEach(async () => { + await createCollections(MikroOrmWrapper.forkManager(), data) + }) - describe("create", () => { - it("should create a collection successfully", async () => { - await service.create([ - { - title: "New Collection", - }, - ]) + it("list product collections", async () => { + const productCollectionResults = await service.list() - const [productCollection] = await service.list({ - title: "New Collection", + expect(productCollectionResults).toEqual([ + expect.objectContaining({ + id: "test-1", + title: "col 1", + }), + expect.objectContaining({ + id: "test-2", + title: "col 2", + }), + expect.objectContaining({ + id: "test-3", + title: "col 3 extra", + }), + expect.objectContaining({ + id: "test-4", + title: "col 4 extra", + }), + ]) + }) + + it("list product collections by id", async () => { + const productCollectionResults = await service.list({ + id: data![0].id, + }) + + expect(productCollectionResults).toEqual([ + expect.objectContaining({ + id: "test-1", + title: "col 1", + }), + ]) + }) + + it("list product collections by title matching string", async () => { + const productCollectionResults = await service.list({ + title: "col 3 extra", + }) + + expect(productCollectionResults).toEqual([ + expect.objectContaining({ + id: "test-3", + title: "col 3 extra", + }), + ]) + }) }) - expect(productCollection.title).toEqual("New Collection") + describe("listAndCount", () => { + const data = [ + { + id: "test-1", + title: "col 1", + }, + { + id: "test-2", + title: "col 2", + }, + { + id: "test-3", + title: "col 3 extra", + }, + { + id: "test-4", + title: "col 4 extra", + }, + ] + + beforeEach(async () => { + await createCollections(MikroOrmWrapper.forkManager(), data) + }) + + it("should return all collections and count", async () => { + const [productCollectionResults, count] = await service.listAndCount() + const serialized = JSON.parse( + JSON.stringify(productCollectionResults) + ) + + expect(serialized).toEqual([ + expect.objectContaining({ + id: "test-1", + title: "col 1", + }), + expect.objectContaining({ + id: "test-2", + title: "col 2", + }), + expect.objectContaining({ + id: "test-3", + title: "col 3 extra", + }), + expect.objectContaining({ + id: "test-4", + title: "col 4 extra", + }), + ]) + }) + + it("should return count and collections based on filter data", async () => { + const [productCollectionResults, count] = await service.listAndCount({ + id: data![0].id, + }) + const serialized = JSON.parse( + JSON.stringify(productCollectionResults) + ) + + expect(count).toEqual(1) + expect(serialized).toEqual([ + expect.objectContaining({ + id: "test-1", + title: "col 1", + }), + ]) + }) + + it("should return count and collections based on config data", async () => { + const [productCollectionResults, count] = await service.listAndCount( + {}, + { + relations: ["products"], + select: ["title"], + take: 1, + skip: 1, + } + ) + const serialized = JSON.parse( + JSON.stringify(productCollectionResults) + ) + + expect(count).toEqual(4) + expect(serialized).toEqual([ + { + id: "test-2", + title: "col 2", + products: [], + }, + ]) + }) + }) + + describe("retrieve", () => { + const collectionData = { + id: "collection-1", + title: "collection 1", + } + + beforeEach(async () => { + await createCollections(MikroOrmWrapper.forkManager(), [ + collectionData, + ]) + }) + + it("should return collection for the given id", async () => { + const productCollectionResults = await service.retrieve( + collectionData.id + ) + + expect(productCollectionResults).toEqual( + expect.objectContaining({ + id: collectionData.id, + }) + ) + }) + + it("should throw an error when collection with id does not exist", async () => { + let error + + try { + await service.retrieve("does-not-exist") + } catch (e) { + error = e + } + + expect(error.message).toEqual( + "ProductCollection with id: does-not-exist was not found" + ) + }) + + it("should throw an error when an id is not provided", async () => { + let error + + try { + await service.retrieve(undefined as unknown as string) + } catch (e) { + error = e + } + + expect(error.message).toEqual( + "productCollection - id must be defined" + ) + }) + + it("should return collection based on config select param", async () => { + const productCollectionResults = await service.retrieve( + collectionData.id, + { + select: ["id", "title"], + } + ) + + const serialized = JSON.parse( + JSON.stringify(productCollectionResults) + ) + + expect(serialized).toEqual({ + id: collectionData.id, + title: collectionData.title, + }) + }) + + it("should return collection based on config relation param", async () => { + const productCollectionResults = await service.retrieve( + collectionData.id, + { + select: ["id", "title"], + relations: ["products"], + } + ) + + const serialized = JSON.parse( + JSON.stringify(productCollectionResults) + ) + + expect(serialized).toEqual({ + id: collectionData.id, + title: collectionData.title, + products: [], + }) + }) + }) + + describe("delete", () => { + const collectionId = "collection-1" + const collectionData = { + id: collectionId, + title: "collection 1", + } + + beforeEach(async () => { + await createCollections(MikroOrmWrapper.forkManager(), [ + collectionData, + ]) + }) + + it("should delete the product collection given an ID successfully", async () => { + await service.delete([collectionId]) + + const collections = await service.list({ + id: collectionId, + }) + + expect(collections).toHaveLength(0) + }) + }) + + describe("update", () => { + const collectionId = "collection-1" + const collectionData = { + id: collectionId, + title: "collection 1", + } + + beforeEach(async () => { + await createCollections(MikroOrmWrapper.forkManager(), [ + collectionData, + ]) + }) + + it("should update the value of the collection successfully", async () => { + await service.update([ + { + id: collectionId, + title: "New Collection", + }, + ]) + + const productCollection = await service.retrieve(collectionId) + + expect(productCollection.title).toEqual("New Collection") + }) + + it("should throw an error when an id does not exist", async () => { + let error + + try { + await service.update([ + { + id: "does-not-exist", + title: "New Collection", + }, + ]) + } catch (e) { + error = e + } + + expect(error.message).toEqual( + 'ProductCollection with id "does-not-exist" not found' + ) + }) + }) + + describe("create", () => { + it("should create a collection successfully", async () => { + await service.create([ + { + title: "New Collection", + }, + ]) + + const [productCollection] = await service.list({ + title: "New Collection", + }) + + expect(productCollection.title).toEqual("New Collection") + }) + }) }) - }) + }, }) diff --git a/packages/product/integration-tests/__tests__/services/product-module-service/product-categories.spec.ts b/packages/product/integration-tests/__tests__/services/product-module-service/product-categories.spec.ts index 179b53cee8..967223f329 100644 --- a/packages/product/integration-tests/__tests__/services/product-module-service/product-categories.spec.ts +++ b/packages/product/integration-tests/__tests__/services/product-module-service/product-categories.spec.ts @@ -1,617 +1,611 @@ -import { MedusaModule, Modules } from "@medusajs/modules-sdk" +import { Modules } from "@medusajs/modules-sdk" import { IProductModuleService, ProductTypes } from "@medusajs/types" -import { SqlEntityManager } from "@mikro-orm/postgresql" import { Product, ProductCategory } from "@models" -import { initModules } from "medusa-test-utils" -import { EventBusService } from "../../../__fixtures__/event-bus" +import { MockEventBusService } from "medusa-test-utils" import { createProductCategories } from "../../../__fixtures__/product-category" import { productCategoriesRankData } from "../../../__fixtures__/product-category/data" -import { TestDatabase, getInitModuleConfig } from "../../../utils" +import { moduleIntegrationTestRunner, SuiteOptions } from "medusa-test-utils" -describe("ProductModuleService product categories", () => { - let service: IProductModuleService - let testManager: SqlEntityManager - let productOne: Product - let productTwo: Product - let productCategoryOne: ProductCategory - let productCategoryTwo: ProductCategory - let productCategories: ProductCategory[] +jest.setTimeout(30000) - let shutdownFunc: () => Promise +moduleIntegrationTestRunner({ + moduleName: Modules.PRODUCT, + injectedDependencies: { + eventBusModuleService: new MockEventBusService(), + }, + testSuite: ({ + MikroOrmWrapper, + service, + }: SuiteOptions) => { + describe("ProductModuleService product categories", () => { + let productOne: Product + let productTwo: Product + let productCategoryOne: ProductCategory + let productCategoryTwo: ProductCategory + let productCategories: ProductCategory[] - beforeAll(async () => { - MedusaModule.clearInstances() + beforeEach(async () => { + const testManager = await MikroOrmWrapper.forkManager() - const initModulesConfig = getInitModuleConfig() - - const { medusaApp, shutdown } = await initModules(initModulesConfig) - - service = medusaApp.modules[Modules.PRODUCT] - - shutdownFunc = shutdown - }) - - afterAll(async () => { - await shutdownFunc() - }) - - beforeEach(async () => { - await TestDatabase.setupDatabase() - - testManager = await TestDatabase.forkManager() - - productOne = testManager.create(Product, { - id: "product-1", - title: "product 1", - status: ProductTypes.ProductStatus.PUBLISHED, - }) - - productTwo = testManager.create(Product, { - id: "product-2", - title: "product 2", - status: ProductTypes.ProductStatus.PUBLISHED, - }) - - const productCategoriesData = [ - { - id: "test-1", - name: "category 1", - products: [productOne], - }, - { - id: "test-2", - name: "category", - products: [productTwo], - }, - ] - - productCategories = await createProductCategories( - testManager, - productCategoriesData - ) - - productCategoryOne = productCategories[0] - productCategoryTwo = productCategories[1] - - await testManager.persistAndFlush([productCategoryOne, productCategoryTwo]) - }) - - afterEach(async () => { - await TestDatabase.clearDatabase() - jest.clearAllMocks() - }) - - describe("listCategories", () => { - it("should return categories queried by ID", async () => { - const results = await service.listCategories({ - id: productCategoryOne.id, - }) - - expect(results).toEqual([ - expect.objectContaining({ - id: productCategoryOne.id, - }), - ]) - }) - - it("should return categories based on the options and filter parameter", async () => { - let results = await service.listCategories( - { - id: productCategoryOne.id, - }, - { - take: 1, - } - ) - - expect(results).toEqual([ - expect.objectContaining({ - id: productCategoryOne.id, - }), - ]) - - results = await service.listCategories({}, { take: 1, skip: 1 }) - - expect(results).toEqual([ - expect.objectContaining({ - id: productCategoryTwo.id, - }), - ]) - }) - - it("should return only requested fields and relations for categories", async () => { - const results = await service.listCategories( - { - id: productCategoryOne.id, - }, - { - select: ["id", "name", "products.title"], - relations: ["products"], - } - ) - - expect(results).toEqual([ - expect.objectContaining({ - id: "test-1", - name: "category 1", - products: [ - expect.objectContaining({ - id: "product-1", - title: "product 1", - }), - ], - }), - ]) - }) - }) - - describe("listAndCountCategories", () => { - it("should return categories and count queried by ID", async () => { - const results = await service.listAndCountCategories({ - id: productCategoryOne.id, - }) - - expect(results[1]).toEqual(1) - expect(results[0]).toEqual([ - expect.objectContaining({ - id: productCategoryOne.id, - }), - ]) - }) - - it("should return categories and count based on the options and filter parameter", async () => { - let results = await service.listAndCountCategories( - { - id: productCategoryOne.id, - }, - { - take: 1, - } - ) - - expect(results[1]).toEqual(1) - expect(results[0]).toEqual([ - expect.objectContaining({ - id: productCategoryOne.id, - }), - ]) - - results = await service.listAndCountCategories({}, { take: 1 }) - - expect(results[1]).toEqual(2) - - results = await service.listAndCountCategories({}, { take: 1, skip: 1 }) - - expect(results[1]).toEqual(2) - expect(results[0]).toEqual([ - expect.objectContaining({ - id: productCategoryTwo.id, - }), - ]) - }) - - it("should return only requested fields and relations for categories", async () => { - const results = await service.listAndCountCategories( - { - id: productCategoryOne.id, - }, - { - select: ["id", "name", "products.title"], - relations: ["products"], - } - ) - - expect(results[1]).toEqual(1) - expect(results[0]).toEqual([ - expect.objectContaining({ - id: "test-1", - name: "category 1", - products: [ - expect.objectContaining({ - id: "product-1", - title: "product 1", - }), - ], - }), - ]) - }) - }) - - describe("retrieveCategory", () => { - it("should return the requested category", async () => { - const result = await service.retrieveCategory(productCategoryOne.id, { - select: ["id", "name"], - }) - - expect(result).toEqual( - expect.objectContaining({ - id: "test-1", - name: "category 1", + productOne = testManager.create(Product, { + id: "product-1", + title: "product 1", + status: ProductTypes.ProductStatus.PUBLISHED, }) - ) - }) - it("should return requested attributes when requested through config", async () => { - const result = await service.retrieveCategory(productCategoryOne.id, { - select: ["id", "name", "products.title"], - relations: ["products"], - }) - - expect(result).toEqual( - expect.objectContaining({ - id: "test-1", - name: "category 1", - products: [ - expect.objectContaining({ - id: "product-1", - title: "product 1", - }), - ], + productTwo = testManager.create(Product, { + id: "product-2", + title: "product 2", + status: ProductTypes.ProductStatus.PUBLISHED, }) - ) - }) - it("should throw an error when a category with ID does not exist", async () => { - let error + const productCategoriesData = [ + { + id: "test-1", + name: "category 1", + products: [productOne], + }, + { + id: "test-2", + name: "category", + products: [productTwo], + }, + ] - try { - await service.retrieveCategory("does-not-exist") - } catch (e) { - error = e - } + productCategories = await createProductCategories( + testManager, + productCategoriesData + ) - expect(error.message).toEqual( - "ProductCategory with id: does-not-exist was not found" - ) - }) - }) + productCategoryOne = productCategories[0] + productCategoryTwo = productCategories[1] - describe("createCategory", () => { - it("should create a category successfully", async () => { - await service.createCategory({ - name: "New Category", - parent_category_id: productCategoryOne.id, + await testManager.persistAndFlush([ + productCategoryOne, + productCategoryTwo, + ]) }) - const [productCategory] = await service.listCategories( - { - name: "New Category", - }, - { - select: ["name", "rank"], - } - ) - - expect(productCategory).toEqual( - expect.objectContaining({ - name: "New Category", - rank: "0", - }) - ) - }) - - it("should emit events through event bus", async () => { - const eventBusSpy = jest.spyOn(EventBusService.prototype, "emit") - - const category = await service.createCategory({ - name: "New Category", - parent_category_id: productCategoryOne.id, + afterEach(async () => { + jest.clearAllMocks() }) - expect(eventBusSpy).toHaveBeenCalledTimes(1) - expect(eventBusSpy).toHaveBeenCalledWith("product-category.created", { - id: category.id, - }) - }) - - it("should append rank from an existing category depending on parent", async () => { - await service.createCategory({ - name: "New Category", - parent_category_id: productCategoryOne.id, - rank: 0, - }) - - await service.createCategory({ - name: "New Category 2", - parent_category_id: productCategoryOne.id, - }) - - const [productCategoryNew] = await service.listCategories( - { - name: "New Category 2", - }, - { - select: ["name", "rank"], - } - ) - - expect(productCategoryNew).toEqual( - expect.objectContaining({ - name: "New Category 2", - rank: "1", - }) - ) - - await service.createCategory({ - name: "New Category 2.1", - parent_category_id: productCategoryNew.id, - }) - - const [productCategoryWithParent] = await service.listCategories( - { - name: "New Category 2.1", - }, - { - select: ["name", "rank", "parent_category_id"], - } - ) - - expect(productCategoryWithParent).toEqual( - expect.objectContaining({ - name: "New Category 2.1", - parent_category_id: productCategoryNew.id, - rank: "0", - }) - ) - }) - }) - - describe("updateCategory", () => { - let productCategoryZero - let productCategoryOne - let productCategoryTwo - let productCategoryZeroZero - let productCategoryZeroOne - let productCategoryZeroTwo - let categories - - beforeEach(async () => { - testManager = await TestDatabase.forkManager() - - categories = await createProductCategories( - testManager, - productCategoriesRankData - ) - - productCategoryZero = categories[0] - productCategoryOne = categories[1] - productCategoryTwo = categories[2] - productCategoryZeroZero = categories[3] - productCategoryZeroOne = categories[4] - productCategoryZeroTwo = categories[5] - }) - - it("should emit events through event bus", async () => { - const eventBusSpy = jest.spyOn(EventBusService.prototype, "emit") - await service.updateCategory(productCategoryZero.id, { - name: "New Category", - }) - - expect(eventBusSpy).toHaveBeenCalledTimes(1) - expect(eventBusSpy).toHaveBeenCalledWith("product-category.updated", { - id: productCategoryZero.id, - }) - }) - - it("should update the name of the category successfully", async () => { - await service.updateCategory(productCategoryZero.id, { - name: "New Category", - }) - - const productCategory = await service.retrieveCategory( - productCategoryZero.id, - { - select: ["name"], - } - ) - - expect(productCategory.name).toEqual("New Category") - }) - - it("should throw an error when an id does not exist", async () => { - let error - - try { - await service.updateCategory("does-not-exist", { - name: "New Category", - }) - } catch (e) { - error = e - } - - expect(error.message).toEqual( - `ProductCategory not found ({ id: 'does-not-exist' })` - ) - }) - - it("should reorder rank successfully in the same parent", async () => { - await service.updateCategory(productCategoryTwo.id, { - rank: 0, - }) - - const productCategories = await service.listCategories( - { - parent_category_id: null, - }, - { - select: ["name", "rank"], - } - ) - - expect(productCategories).toEqual( - expect.arrayContaining([ - expect.objectContaining({ - id: productCategoryTwo.id, - rank: "0", - }), - expect.objectContaining({ - id: productCategoryZero.id, - rank: "1", - }), - expect.objectContaining({ + describe("listCategories", () => { + it("should return categories queried by ID", async () => { + const results = await service.listCategories({ id: productCategoryOne.id, - rank: "2", - }), - ]) - ) - }) + }) - it("should reorder rank successfully when changing parent", async () => { - await service.updateCategory(productCategoryTwo.id, { - rank: 0, - parent_category_id: productCategoryZero.id, + expect(results).toEqual([ + expect.objectContaining({ + id: productCategoryOne.id, + }), + ]) + }) + + it("should return categories based on the options and filter parameter", async () => { + let results = await service.listCategories( + { + id: productCategoryOne.id, + }, + { + take: 1, + } + ) + + expect(results).toEqual([ + expect.objectContaining({ + id: productCategoryOne.id, + }), + ]) + + results = await service.listCategories({}, { take: 1, skip: 1 }) + + expect(results).toEqual([ + expect.objectContaining({ + id: productCategoryTwo.id, + }), + ]) + }) + + it("should return only requested fields and relations for categories", async () => { + const results = await service.listCategories( + { + id: productCategoryOne.id, + }, + { + select: ["id", "name", "products.title"], + relations: ["products"], + } + ) + + expect(results).toEqual([ + expect.objectContaining({ + id: "test-1", + name: "category 1", + products: [ + expect.objectContaining({ + id: "product-1", + title: "product 1", + }), + ], + }), + ]) + }) }) - const productCategories = await service.listCategories( - { - parent_category_id: productCategoryZero.id, - }, - { - select: ["name", "rank"], - } - ) + describe("listAndCountCategories", () => { + it("should return categories and count queried by ID", async () => { + const results = await service.listAndCountCategories({ + id: productCategoryOne.id, + }) - expect(productCategories).toEqual( - expect.arrayContaining([ - expect.objectContaining({ - id: productCategoryTwo.id, - rank: "0", - }), - expect.objectContaining({ - id: productCategoryZeroZero.id, - rank: "1", - }), - expect.objectContaining({ - id: productCategoryZeroOne.id, - rank: "2", - }), - expect.objectContaining({ - id: productCategoryZeroTwo.id, - rank: "3", - }), - ]) - ) - }) + expect(results[1]).toEqual(1) + expect(results[0]).toEqual([ + expect.objectContaining({ + id: productCategoryOne.id, + }), + ]) + }) - it("should reorder rank successfully when changing parent and in first position", async () => { - await service.updateCategory(productCategoryTwo.id, { - rank: 0, - parent_category_id: productCategoryZero.id, + it("should return categories and count based on the options and filter parameter", async () => { + let results = await service.listAndCountCategories( + { + id: productCategoryOne.id, + }, + { + take: 1, + } + ) + + expect(results[1]).toEqual(1) + expect(results[0]).toEqual([ + expect.objectContaining({ + id: productCategoryOne.id, + }), + ]) + + results = await service.listAndCountCategories({}, { take: 1 }) + + expect(results[1]).toEqual(2) + + results = await service.listAndCountCategories( + {}, + { take: 1, skip: 1 } + ) + + expect(results[1]).toEqual(2) + expect(results[0]).toEqual([ + expect.objectContaining({ + id: productCategoryTwo.id, + }), + ]) + }) + + it("should return only requested fields and relations for categories", async () => { + const results = await service.listAndCountCategories( + { + id: productCategoryOne.id, + }, + { + select: ["id", "name", "products.title"], + relations: ["products"], + } + ) + + expect(results[1]).toEqual(1) + expect(results[0]).toEqual([ + expect.objectContaining({ + id: "test-1", + name: "category 1", + products: [ + expect.objectContaining({ + id: "product-1", + title: "product 1", + }), + ], + }), + ]) + }) }) - const productCategories = await service.listCategories( - { - parent_category_id: productCategoryZero.id, - }, - { - select: ["name", "rank"], - } - ) + describe("retrieveCategory", () => { + it("should return the requested category", async () => { + const result = await service.retrieveCategory(productCategoryOne.id, { + select: ["id", "name"], + }) - expect(productCategories).toEqual( - expect.arrayContaining([ - expect.objectContaining({ - id: productCategoryTwo.id, - rank: "0", - }), - expect.objectContaining({ - id: productCategoryZeroZero.id, - rank: "1", - }), - expect.objectContaining({ - id: productCategoryZeroOne.id, - rank: "2", - }), - expect.objectContaining({ - id: productCategoryZeroTwo.id, - rank: "3", - }), - ]) - ) - }) - }) + expect(result).toEqual( + expect.objectContaining({ + id: "test-1", + name: "category 1", + }) + ) + }) - describe("deleteCategory", () => { - let productCategoryZero - let productCategoryOne - let productCategoryTwo - let categories + it("should return requested attributes when requested through config", async () => { + const result = await service.retrieveCategory(productCategoryOne.id, { + select: ["id", "name", "products.title"], + relations: ["products"], + }) - beforeEach(async () => { - testManager = await TestDatabase.forkManager() + expect(result).toEqual( + expect.objectContaining({ + id: "test-1", + name: "category 1", + products: [ + expect.objectContaining({ + id: "product-1", + title: "product 1", + }), + ], + }) + ) + }) - categories = await createProductCategories( - testManager, - productCategoriesRankData - ) + it("should throw an error when a category with ID does not exist", async () => { + let error - productCategoryZero = categories[0] - productCategoryOne = categories[1] - productCategoryTwo = categories[2] - }) + try { + await service.retrieveCategory("does-not-exist") + } catch (e) { + error = e + } - it("should emit events through event bus", async () => { - const eventBusSpy = jest.spyOn(EventBusService.prototype, "emit") - await service.deleteCategory(productCategoryOne.id) - - expect(eventBusSpy).toHaveBeenCalledTimes(1) - expect(eventBusSpy).toHaveBeenCalledWith("product-category.deleted", { - id: productCategoryOne.id, + expect(error.message).toEqual( + "ProductCategory with id: does-not-exist was not found" + ) + }) }) - }) - it("should throw an error when an id does not exist", async () => { - let error + describe("createCategory", () => { + it("should create a category successfully", async () => { + await service.createCategory({ + name: "New Category", + parent_category_id: productCategoryOne.id, + }) - try { - await service.deleteCategory("does-not-exist") - } catch (e) { - error = e - } + const [productCategory] = await service.listCategories( + { + name: "New Category", + }, + { + select: ["name", "rank"], + } + ) - expect(error.message).toEqual( - `ProductCategory not found ({ id: 'does-not-exist' })` - ) - }) + expect(productCategory).toEqual( + expect.objectContaining({ + name: "New Category", + rank: "0", + }) + ) + }) - it("should throw an error when it has children", async () => { - let error + it("should emit events through event bus", async () => { + const eventBusSpy = jest.spyOn(MockEventBusService.prototype, "emit") - try { - await service.deleteCategory(productCategoryZero.id) - } catch (e) { - error = e - } + const category = await service.createCategory({ + name: "New Category", + parent_category_id: productCategoryOne.id, + }) - expect(error.message).toEqual( - `Deleting ProductCategory (category-0-0) with category children is not allowed` - ) - }) + expect(eventBusSpy).toHaveBeenCalledTimes(1) + expect(eventBusSpy).toHaveBeenCalledWith("product-category.created", { + id: category.id, + }) + }) - it("should reorder siblings rank successfully on deleting", async () => { - await service.deleteCategory(productCategoryOne.id) + it("should append rank from an existing category depending on parent", async () => { + await service.createCategory({ + name: "New Category", + parent_category_id: productCategoryOne.id, + rank: 0, + }) - const productCategories = await service.listCategories( - { - parent_category_id: null, - }, - { - select: ["id", "rank"], - } - ) + await service.createCategory({ + name: "New Category 2", + parent_category_id: productCategoryOne.id, + }) - expect(productCategories).toEqual( - expect.arrayContaining([ - expect.objectContaining({ + const [productCategoryNew] = await service.listCategories( + { + name: "New Category 2", + }, + { + select: ["name", "rank"], + } + ) + + expect(productCategoryNew).toEqual( + expect.objectContaining({ + name: "New Category 2", + rank: "1", + }) + ) + + await service.createCategory({ + name: "New Category 2.1", + parent_category_id: productCategoryNew.id, + }) + + const [productCategoryWithParent] = await service.listCategories( + { + name: "New Category 2.1", + }, + { + select: ["name", "rank", "parent_category_id"], + } + ) + + expect(productCategoryWithParent).toEqual( + expect.objectContaining({ + name: "New Category 2.1", + parent_category_id: productCategoryNew.id, + rank: "0", + }) + ) + }) + }) + + describe("updateCategory", () => { + let productCategoryZero + let productCategoryOne + let productCategoryTwo + let productCategoryZeroZero + let productCategoryZeroOne + let productCategoryZeroTwo + let categories + + beforeEach(async () => { + const testManager = await MikroOrmWrapper.forkManager() + + categories = await createProductCategories( + testManager, + productCategoriesRankData + ) + + productCategoryZero = categories[0] + productCategoryOne = categories[1] + productCategoryTwo = categories[2] + productCategoryZeroZero = categories[3] + productCategoryZeroOne = categories[4] + productCategoryZeroTwo = categories[5] + }) + + it("should emit events through event bus", async () => { + const eventBusSpy = jest.spyOn(MockEventBusService.prototype, "emit") + await service.updateCategory(productCategoryZero.id, { + name: "New Category", + }) + + expect(eventBusSpy).toHaveBeenCalledTimes(1) + expect(eventBusSpy).toHaveBeenCalledWith("product-category.updated", { id: productCategoryZero.id, - rank: "0", - }), - expect.objectContaining({ - id: productCategoryTwo.id, - rank: "1", - }), - ]) - ) + }) + }) + + it("should update the name of the category successfully", async () => { + await service.updateCategory(productCategoryZero.id, { + name: "New Category", + }) + + const productCategory = await service.retrieveCategory( + productCategoryZero.id, + { + select: ["name"], + } + ) + + expect(productCategory.name).toEqual("New Category") + }) + + it("should throw an error when an id does not exist", async () => { + let error + + try { + await service.updateCategory("does-not-exist", { + name: "New Category", + }) + } catch (e) { + error = e + } + + expect(error.message).toEqual( + `ProductCategory not found ({ id: 'does-not-exist' })` + ) + }) + + it("should reorder rank successfully in the same parent", async () => { + await service.updateCategory(productCategoryTwo.id, { + rank: 0, + }) + + const productCategories = await service.listCategories( + { + parent_category_id: null, + }, + { + select: ["name", "rank"], + } + ) + + expect(productCategories).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + id: productCategoryTwo.id, + rank: "0", + }), + expect.objectContaining({ + id: productCategoryZero.id, + rank: "1", + }), + expect.objectContaining({ + id: productCategoryOne.id, + rank: "2", + }), + ]) + ) + }) + + it("should reorder rank successfully when changing parent", async () => { + await service.updateCategory(productCategoryTwo.id, { + rank: 0, + parent_category_id: productCategoryZero.id, + }) + + const productCategories = await service.listCategories( + { + parent_category_id: productCategoryZero.id, + }, + { + select: ["name", "rank"], + } + ) + + expect(productCategories).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + id: productCategoryTwo.id, + rank: "0", + }), + expect.objectContaining({ + id: productCategoryZeroZero.id, + rank: "1", + }), + expect.objectContaining({ + id: productCategoryZeroOne.id, + rank: "2", + }), + expect.objectContaining({ + id: productCategoryZeroTwo.id, + rank: "3", + }), + ]) + ) + }) + + it("should reorder rank successfully when changing parent and in first position", async () => { + await service.updateCategory(productCategoryTwo.id, { + rank: 0, + parent_category_id: productCategoryZero.id, + }) + + const productCategories = await service.listCategories( + { + parent_category_id: productCategoryZero.id, + }, + { + select: ["name", "rank"], + } + ) + + expect(productCategories).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + id: productCategoryTwo.id, + rank: "0", + }), + expect.objectContaining({ + id: productCategoryZeroZero.id, + rank: "1", + }), + expect.objectContaining({ + id: productCategoryZeroOne.id, + rank: "2", + }), + expect.objectContaining({ + id: productCategoryZeroTwo.id, + rank: "3", + }), + ]) + ) + }) + }) + + describe("deleteCategory", () => { + let productCategoryZero + let productCategoryOne + let productCategoryTwo + let categories + + beforeEach(async () => { + const testManager = await MikroOrmWrapper.forkManager() + + categories = await createProductCategories( + testManager, + productCategoriesRankData + ) + + productCategoryZero = categories[0] + productCategoryOne = categories[1] + productCategoryTwo = categories[2] + }) + + it("should emit events through event bus", async () => { + const eventBusSpy = jest.spyOn(MockEventBusService.prototype, "emit") + await service.deleteCategory(productCategoryOne.id) + + expect(eventBusSpy).toHaveBeenCalledTimes(1) + expect(eventBusSpy).toHaveBeenCalledWith("product-category.deleted", { + id: productCategoryOne.id, + }) + }) + + it("should throw an error when an id does not exist", async () => { + let error + + try { + await service.deleteCategory("does-not-exist") + } catch (e) { + error = e + } + + expect(error.message).toEqual( + `ProductCategory not found ({ id: 'does-not-exist' })` + ) + }) + + it("should throw an error when it has children", async () => { + let error + + try { + await service.deleteCategory(productCategoryZero.id) + } catch (e) { + error = e + } + + expect(error.message).toEqual( + `Deleting ProductCategory (category-0-0) with category children is not allowed` + ) + }) + + it("should reorder siblings rank successfully on deleting", async () => { + await service.deleteCategory(productCategoryOne.id) + + const productCategories = await service.listCategories( + { + parent_category_id: null, + }, + { + select: ["id", "rank"], + } + ) + + expect(productCategories).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + id: productCategoryZero.id, + rank: "0", + }), + expect.objectContaining({ + id: productCategoryTwo.id, + rank: "1", + }), + ]) + ) + }) + }) }) - }) + }, }) diff --git a/packages/product/integration-tests/__tests__/services/product-module-service/product-collections.spec.ts b/packages/product/integration-tests/__tests__/services/product-module-service/product-collections.spec.ts index a3ba93aa71..feeffb6796 100644 --- a/packages/product/integration-tests/__tests__/services/product-module-service/product-collections.spec.ts +++ b/packages/product/integration-tests/__tests__/services/product-module-service/product-collections.spec.ts @@ -1,442 +1,443 @@ import { IProductModuleService, ProductTypes } from "@medusajs/types" -import { SqlEntityManager } from "@mikro-orm/postgresql" import { Product, ProductCollection } from "@models" -import { EventBusService } from "../../../__fixtures__/event-bus" +import { MockEventBusService } from "medusa-test-utils" import { createCollections } from "../../../__fixtures__/product" -import { getInitModuleConfig, TestDatabase } from "../../../utils" -import { MedusaModule, Modules } from "@medusajs/modules-sdk" -import { initModules } from "medusa-test-utils" +import { Modules } from "@medusajs/modules-sdk" +import { moduleIntegrationTestRunner, SuiteOptions } from "medusa-test-utils" -describe("ProductModuleService product collections", () => { - let service: IProductModuleService - let testManager: SqlEntityManager - let productOne: Product - let productTwo: Product - let productCollectionOne: ProductCollection - let productCollectionTwo: ProductCollection - let productCollections: ProductCollection[] +jest.setTimeout(30000) - let shutdownFunc: () => Promise +moduleIntegrationTestRunner({ + moduleName: Modules.PRODUCT, + injectedDependencies: { + eventBusModuleService: new MockEventBusService(), + }, + testSuite: ({ + MikroOrmWrapper, + service, + }: SuiteOptions) => { + describe("ProductModuleService product collections", () => { + let productOne: Product + let productTwo: Product + let productCollectionOne: ProductCollection + let productCollectionTwo: ProductCollection + let productCollections: ProductCollection[] - beforeAll(async () => { - MedusaModule.clearInstances() + beforeEach(async () => { + const testManager = await MikroOrmWrapper.forkManager() - const initModulesConfig = getInitModuleConfig() - - const { medusaApp, shutdown } = await initModules(initModulesConfig) - - service = medusaApp.modules[Modules.PRODUCT] - - shutdownFunc = shutdown - }) - - afterAll(async () => { - await shutdownFunc() - }) - - beforeEach(async () => { - await TestDatabase.setupDatabase() - - testManager = await TestDatabase.forkManager() - - productOne = testManager.create(Product, { - id: "product-1", - title: "product 1", - status: ProductTypes.ProductStatus.PUBLISHED, - }) - - productTwo = testManager.create(Product, { - id: "product-2", - title: "product 2", - status: ProductTypes.ProductStatus.PUBLISHED, - }) - - const productCollectionsData = [ - { - id: "test-1", - title: "collection 1", - products: [productOne], - }, - { - id: "test-2", - title: "collection", - products: [productTwo], - }, - ] - - productCollections = await createCollections( - testManager, - productCollectionsData - ) - - productCollectionOne = productCollections[0] - productCollectionTwo = productCollections[1] - }) - - afterEach(async () => { - await TestDatabase.clearDatabase() - jest.clearAllMocks() - }) - - describe("listCollections", () => { - it("should return collections queried by ID", async () => { - const results = await service.listCollections({ - id: productCollectionOne.id, - }) - - expect(results).toEqual([ - expect.objectContaining({ - id: productCollectionOne.id, - }), - ]) - }) - - it("should return collections based on the options and filter parameter", async () => { - let results = await service.listCollections( - { - id: productCollectionOne.id, - }, - { - take: 1, - } - ) - - expect(results).toEqual([ - expect.objectContaining({ - id: productCollectionOne.id, - }), - ]) - - results = await service.listCollections({}, { take: 1, skip: 1 }) - - expect(results).toEqual([ - expect.objectContaining({ - id: productCollectionTwo.id, - }), - ]) - }) - - it("should return only requested fields and relations for collections", async () => { - const results = await service.listCollections( - { - id: productCollectionOne.id, - }, - { - select: ["id", "title", "products.title"], - relations: ["products"], - } - ) - - expect(results).toEqual([ - expect.objectContaining({ - id: "test-1", - title: "collection 1", - products: [ - expect.objectContaining({ - id: "product-1", - title: "product 1", - }), - ], - }), - ]) - }) - }) - - describe("listAndCountCollections", () => { - it("should return collections and count queried by ID", async () => { - const results = await service.listAndCountCollections({ - id: productCollectionOne.id, - }) - - expect(results[1]).toEqual(1) - expect(results[0]).toEqual([ - expect.objectContaining({ - id: productCollectionOne.id, - }), - ]) - }) - - it("should return collections and count based on the options and filter parameter", async () => { - let results = await service.listAndCountCollections( - { - id: productCollectionOne.id, - }, - { - take: 1, - } - ) - - expect(results[1]).toEqual(1) - expect(results[0]).toEqual([ - expect.objectContaining({ - id: productCollectionOne.id, - }), - ]) - - results = await service.listAndCountCollections({}, { take: 1 }) - - expect(results[1]).toEqual(2) - - results = await service.listAndCountCollections({}, { take: 1, skip: 1 }) - - expect(results[1]).toEqual(2) - expect(results[0]).toEqual([ - expect.objectContaining({ - id: productCollectionTwo.id, - }), - ]) - }) - - it("should return only requested fields and relations for collections", async () => { - const results = await service.listAndCountCollections( - { - id: productCollectionOne.id, - }, - { - select: ["id", "title", "products.title"], - relations: ["products"], - } - ) - - expect(results[1]).toEqual(1) - expect(results[0]).toEqual([ - expect.objectContaining({ - id: "test-1", - title: "collection 1", - products: [ - expect.objectContaining({ - id: "product-1", - title: "product 1", - }), - ], - }), - ]) - }) - }) - - describe("retrieveCollection", () => { - it("should return the requested collection", async () => { - const result = await service.retrieveCollection(productCollectionOne.id) - - expect(result).toEqual( - expect.objectContaining({ - id: "test-1", - title: "collection 1", + productOne = testManager.create(Product, { + id: "product-1", + title: "product 1", + status: ProductTypes.ProductStatus.PUBLISHED, }) - ) - }) - it("should return requested attributes when requested through config", async () => { - const result = await service.retrieveCollection(productCollectionOne.id, { - select: ["id", "title", "products.title"], - relations: ["products"], - }) - - expect(result).toEqual( - expect.objectContaining({ - id: "test-1", - title: "collection 1", - products: [ - expect.objectContaining({ - id: "product-1", - title: "product 1", - }), - ], + productTwo = testManager.create(Product, { + id: "product-2", + title: "product 2", + status: ProductTypes.ProductStatus.PUBLISHED, }) - ) - }) - it("should throw an error when a collection with ID does not exist", async () => { - let error - - try { - await service.retrieveCollection("does-not-exist") - } catch (e) { - error = e - } - - expect(error.message).toEqual( - "ProductCollection with id: does-not-exist was not found" - ) - }) - }) - - describe("deleteCollections", () => { - const collectionId = "test-1" - - it("should delete the product collection given an ID successfully", async () => { - await service.deleteCollections([collectionId]) - - const collections = await service.listCollections({ - id: collectionId, - }) - - expect(collections).toHaveLength(0) - }) - - it("should emit events through event bus", async () => { - const eventBusSpy = jest.spyOn(EventBusService.prototype, "emit") - await service.deleteCollections([collectionId]) - - expect(eventBusSpy).toHaveBeenCalledTimes(1) - expect(eventBusSpy).toHaveBeenCalledWith([ - { - eventName: "product-collection.deleted", - data: { id: collectionId }, - }, - ]) - }) - }) - - describe("updateCollections", () => { - const collectionId = "test-1" - - it("should emit events through event bus", async () => { - const eventBusSpy = jest.spyOn(EventBusService.prototype, "emit") - - await service.upsertCollections([ - { - id: collectionId, - title: "New Collection", - }, - ]) - - expect(eventBusSpy).toHaveBeenCalledTimes(1) - expect(eventBusSpy).toHaveBeenCalledWith([ - { - eventName: "product-collection.updated", - data: { id: collectionId }, - }, - ]) - }) - - it("should update the value of the collection successfully", async () => { - await service.upsertCollections([ - { - id: collectionId, - title: "New Collection", - }, - ]) - - const productCollection = await service.retrieveCollection(collectionId) - - expect(productCollection.title).toEqual("New Collection") - }) - - it("should add products to a collection successfully", async () => { - await service.upsertCollections([ - { - id: collectionId, - product_ids: [productOne.id, productTwo.id], - }, - ]) - - const productCollection = await service.retrieveCollection(collectionId, { - select: ["products.id"], - relations: ["products"], - }) - - expect(productCollection.products).toHaveLength(2) - expect(productCollection).toEqual( - expect.objectContaining({ - products: expect.arrayContaining([ - expect.objectContaining({ - id: productOne.id, - }), - expect.objectContaining({ - id: productTwo.id, - }), - ]), - }) - ) - }) - - it("should throw an error when an id does not exist", async () => { - let error - - try { - await service.upsertCollections([ + const productCollectionsData = [ { - id: "does-not-exist", - title: "New Collection", + id: "test-1", + title: "collection 1", + products: [productOne], }, - ]) - } catch (e) { - error = e - } + { + id: "test-2", + title: "collection", + products: [productTwo], + }, + ] - expect(error.message).toEqual( - 'ProductCollection with id "does-not-exist" not found' - ) - }) - }) + productCollections = await createCollections( + testManager, + productCollectionsData + ) - describe("createCollections", () => { - it("should create a collection successfully", async () => { - const res = await service.createCollections([ - { - title: "New Collection", - }, - ]) - - const [productCollection] = await service.listCollections({ - title: "New Collection", + productCollectionOne = productCollections[0] + productCollectionTwo = productCollections[1] }) - expect(productCollection.title).toEqual("New Collection") - }) + afterEach(async () => { + jest.clearAllMocks() + }) - it("should create collection with products successfully", async () => { - await service.createCollections([ - { - title: "New Collection with products", - handle: "new-collection-with-products", - product_ids: [productOne.id, productTwo.id], - }, - ]) + describe("listCollections", () => { + it("should return collections queried by ID", async () => { + const results = await service.listCollections({ + id: productCollectionOne.id, + }) - const [productCollection] = await service.listCollections( - { - handle: "new-collection-with-products", - }, - { - select: ["title", "handle", "products.id"], - relations: ["products"], - } - ) - - expect(productCollection).toEqual( - expect.objectContaining({ - title: "New Collection with products", - handle: "new-collection-with-products", - products: [ + expect(results).toEqual([ expect.objectContaining({ - id: productOne.id, + id: productCollectionOne.id, }), - expect.objectContaining({ - id: productTwo.id, - }), - ], + ]) }) - ) + + it("should return collections based on the options and filter parameter", async () => { + let results = await service.listCollections( + { + id: productCollectionOne.id, + }, + { + take: 1, + } + ) + + expect(results).toEqual([ + expect.objectContaining({ + id: productCollectionOne.id, + }), + ]) + + results = await service.listCollections({}, { take: 1, skip: 1 }) + + expect(results).toEqual([ + expect.objectContaining({ + id: productCollectionTwo.id, + }), + ]) + }) + + it("should return only requested fields and relations for collections", async () => { + const results = await service.listCollections( + { + id: productCollectionOne.id, + }, + { + select: ["id", "title", "products.title"], + relations: ["products"], + } + ) + + expect(results).toEqual([ + expect.objectContaining({ + id: "test-1", + title: "collection 1", + products: [ + expect.objectContaining({ + id: "product-1", + title: "product 1", + }), + ], + }), + ]) + }) + }) + + describe("listAndCountCollections", () => { + it("should return collections and count queried by ID", async () => { + const results = await service.listAndCountCollections({ + id: productCollectionOne.id, + }) + + expect(results[1]).toEqual(1) + expect(results[0]).toEqual([ + expect.objectContaining({ + id: productCollectionOne.id, + }), + ]) + }) + + it("should return collections and count based on the options and filter parameter", async () => { + let results = await service.listAndCountCollections( + { + id: productCollectionOne.id, + }, + { + take: 1, + } + ) + + expect(results[1]).toEqual(1) + expect(results[0]).toEqual([ + expect.objectContaining({ + id: productCollectionOne.id, + }), + ]) + + results = await service.listAndCountCollections({}, { take: 1 }) + + expect(results[1]).toEqual(2) + + results = await service.listAndCountCollections( + {}, + { take: 1, skip: 1 } + ) + + expect(results[1]).toEqual(2) + expect(results[0]).toEqual([ + expect.objectContaining({ + id: productCollectionTwo.id, + }), + ]) + }) + + it("should return only requested fields and relations for collections", async () => { + const results = await service.listAndCountCollections( + { + id: productCollectionOne.id, + }, + { + select: ["id", "title", "products.title"], + relations: ["products"], + } + ) + + expect(results[1]).toEqual(1) + expect(results[0]).toEqual([ + expect.objectContaining({ + id: "test-1", + title: "collection 1", + products: [ + expect.objectContaining({ + id: "product-1", + title: "product 1", + }), + ], + }), + ]) + }) + }) + + describe("retrieveCollection", () => { + it("should return the requested collection", async () => { + const result = await service.retrieveCollection( + productCollectionOne.id + ) + + expect(result).toEqual( + expect.objectContaining({ + id: "test-1", + title: "collection 1", + }) + ) + }) + + it("should return requested attributes when requested through config", async () => { + const result = await service.retrieveCollection( + productCollectionOne.id, + { + select: ["id", "title", "products.title"], + relations: ["products"], + } + ) + + expect(result).toEqual( + expect.objectContaining({ + id: "test-1", + title: "collection 1", + products: [ + expect.objectContaining({ + id: "product-1", + title: "product 1", + }), + ], + }) + ) + }) + + it("should throw an error when a collection with ID does not exist", async () => { + let error + + try { + await service.retrieveCollection("does-not-exist") + } catch (e) { + error = e + } + + expect(error.message).toEqual( + "ProductCollection with id: does-not-exist was not found" + ) + }) + }) + + describe("deleteCollections", () => { + const collectionId = "test-1" + + it("should delete the product collection given an ID successfully", async () => { + await service.deleteCollections([collectionId]) + + const collections = await service.listCollections({ + id: collectionId, + }) + + expect(collections).toHaveLength(0) + }) + + it("should emit events through event bus", async () => { + const eventBusSpy = jest.spyOn(MockEventBusService.prototype, "emit") + await service.deleteCollections([collectionId]) + + expect(eventBusSpy).toHaveBeenCalledTimes(1) + expect(eventBusSpy).toHaveBeenCalledWith([ + { + eventName: "product-collection.deleted", + data: { id: collectionId }, + }, + ]) + }) + }) + + describe("updateCollections", () => { + const collectionId = "test-1" + + it("should emit events through event bus", async () => { + const eventBusSpy = jest.spyOn(MockEventBusService.prototype, "emit") + + await service.upsertCollections([ + { + id: collectionId, + title: "New Collection", + }, + ]) + + expect(eventBusSpy).toHaveBeenCalledTimes(1) + expect(eventBusSpy).toHaveBeenCalledWith([ + { + eventName: "product-collection.updated", + data: { id: collectionId }, + }, + ]) + }) + + it("should update the value of the collection successfully", async () => { + await service.upsertCollections([ + { + id: collectionId, + title: "New Collection", + }, + ]) + + const productCollection = await service.retrieveCollection( + collectionId + ) + + expect(productCollection.title).toEqual("New Collection") + }) + + it("should add products to a collection successfully", async () => { + await service.upsertCollections([ + { + id: collectionId, + product_ids: [productOne.id, productTwo.id], + }, + ]) + + const productCollection = await service.retrieveCollection( + collectionId, + { + select: ["products.id"], + relations: ["products"], + } + ) + + expect(productCollection.products).toHaveLength(2) + expect(productCollection).toEqual( + expect.objectContaining({ + products: expect.arrayContaining([ + expect.objectContaining({ + id: productOne.id, + }), + expect.objectContaining({ + id: productTwo.id, + }), + ]), + }) + ) + }) + + it("should throw an error when an id does not exist", async () => { + let error + + try { + await service.upsertCollections([ + { + id: "does-not-exist", + title: "New Collection", + }, + ]) + } catch (e) { + error = e + } + + expect(error.message).toEqual( + 'ProductCollection with id "does-not-exist" not found' + ) + }) + }) + + describe("createCollections", () => { + it("should create a collection successfully", async () => { + const res = await service.createCollections([ + { + title: "New Collection", + }, + ]) + + const [productCollection] = await service.listCollections({ + title: "New Collection", + }) + + expect(productCollection.title).toEqual("New Collection") + }) + + it("should create collection with products successfully", async () => { + await service.createCollections([ + { + title: "New Collection with products", + handle: "new-collection-with-products", + product_ids: [productOne.id, productTwo.id], + }, + ]) + + const [productCollection] = await service.listCollections( + { + handle: "new-collection-with-products", + }, + { + select: ["title", "handle", "products.id"], + relations: ["products"], + } + ) + + expect(productCollection).toEqual( + expect.objectContaining({ + title: "New Collection with products", + handle: "new-collection-with-products", + products: [ + expect.objectContaining({ + id: productOne.id, + }), + expect.objectContaining({ + id: productTwo.id, + }), + ], + }) + ) + }) + + it("should emit events through event bus", async () => { + const eventBusSpy = jest.spyOn(MockEventBusService.prototype, "emit") + + const collections = await service.createCollections([ + { + title: "New Collection", + }, + ]) + + expect(eventBusSpy).toHaveBeenCalledTimes(1) + expect(eventBusSpy).toHaveBeenCalledWith([ + { + eventName: "product-collection.created", + data: { id: collections[0].id }, + }, + ]) + }) + }) }) - - it("should emit events through event bus", async () => { - const eventBusSpy = jest.spyOn(EventBusService.prototype, "emit") - - const collections = await service.createCollections([ - { - title: "New Collection", - }, - ]) - - expect(eventBusSpy).toHaveBeenCalledTimes(1) - expect(eventBusSpy).toHaveBeenCalledWith([ - { - eventName: "product-collection.created", - data: { id: collections[0].id }, - }, - ]) - }) - }) + }, }) diff --git a/packages/product/integration-tests/__tests__/services/product-module-service/product-options.spec.ts b/packages/product/integration-tests/__tests__/services/product-module-service/product-options.spec.ts index 1803bf5cf3..f2d4f4b7ba 100644 --- a/packages/product/integration-tests/__tests__/services/product-module-service/product-options.spec.ts +++ b/packages/product/integration-tests/__tests__/services/product-module-service/product-options.spec.ts @@ -1,325 +1,307 @@ import { IProductModuleService, ProductTypes } from "@medusajs/types" -import { SqlEntityManager } from "@mikro-orm/postgresql" import { Product, ProductOption } from "@models" -import { getInitModuleConfig, TestDatabase } from "../../../utils" -import { MedusaModule, Modules } from "@medusajs/modules-sdk" -import { initModules } from "medusa-test-utils" +import { Modules } from "@medusajs/modules-sdk" +import { moduleIntegrationTestRunner, SuiteOptions } from "medusa-test-utils" -describe("ProductModuleService product options", () => { - let service: IProductModuleService - let testManager: SqlEntityManager - let optionOne: ProductOption - let optionTwo: ProductOption - let productOne: Product - let productTwo: Product +jest.setTimeout(30000) - let shutdownFunc: () => Promise +moduleIntegrationTestRunner({ + moduleName: Modules.PRODUCT, + testSuite: ({ + MikroOrmWrapper, + service, + }: SuiteOptions) => { + describe("ProductModuleService product options", () => { + let optionOne: ProductOption + let optionTwo: ProductOption + let productOne: Product + let productTwo: Product - beforeAll(async () => { - MedusaModule.clearInstances() - - const initModulesConfig = getInitModuleConfig() - - const { medusaApp, shutdown } = await initModules(initModulesConfig) - - service = medusaApp.modules[Modules.PRODUCT] - - shutdownFunc = shutdown - }) - - afterAll(async () => { - await shutdownFunc() - }) - - beforeEach(async () => { - await TestDatabase.setupDatabase() - - testManager = await TestDatabase.forkManager() - productOne = testManager.create(Product, { - id: "product-1", - title: "product 1", - status: ProductTypes.ProductStatus.PUBLISHED, - }) - - productTwo = testManager.create(Product, { - id: "product-2", - title: "product 2", - status: ProductTypes.ProductStatus.PUBLISHED, - }) - - optionOne = testManager.create(ProductOption, { - id: "option-1", - title: "option 1", - product: productOne, - }) - - optionTwo = testManager.create(ProductOption, { - id: "option-2", - title: "option 1", - product: productTwo, - }) - - await testManager.persistAndFlush([optionOne, optionTwo]) - }) - - afterEach(async () => { - await TestDatabase.clearDatabase() - }) - - describe("listOptions", () => { - it("should return options and count queried by ID", async () => { - const options = await service.listOptions({ - id: optionOne.id, - }) - - expect(options).toEqual([ - expect.objectContaining({ - id: optionOne.id, - }), - ]) - }) - - it("should return options and count based on the options and filter parameter", async () => { - let options = await service.listOptions( - { - id: optionOne.id, - }, - { - take: 1, - } - ) - - expect(options).toEqual([ - expect.objectContaining({ - id: optionOne.id, - }), - ]) - - options = await service.listOptions({}, { take: 1, skip: 1 }) - - expect(options).toEqual([ - expect.objectContaining({ - id: optionTwo.id, - }), - ]) - }) - - it("should return only requested fields and relations for options", async () => { - const options = await service.listOptions( - { - id: optionOne.id, - }, - { - select: ["title", "product.id"], - relations: ["product"], - take: 1, - } - ) - - expect(options).toEqual([ - { - id: optionOne.id, - title: optionOne.title, - product_id: productOne.id, - product: { - id: productOne.id, - }, - }, - ]) - }) - }) - - describe("listAndCountOptions", () => { - it("should return options and count queried by ID", async () => { - const [options, count] = await service.listAndCountOptions({ - id: optionOne.id, - }) - - expect(count).toEqual(1) - expect(options).toEqual([ - expect.objectContaining({ - id: optionOne.id, - }), - ]) - }) - - it("should return options and count based on the options and filter parameter", async () => { - let [options, count] = await service.listAndCountOptions( - { - id: optionOne.id, - }, - { - take: 1, - } - ) - - expect(count).toEqual(1) - expect(options).toEqual([ - expect.objectContaining({ - id: optionOne.id, - }), - ]) - ;[options, count] = await service.listAndCountOptions({}, { take: 1 }) - - expect(count).toEqual(2) - ;[options, count] = await service.listAndCountOptions( - {}, - { take: 1, skip: 1 } - ) - - expect(count).toEqual(2) - expect(options).toEqual([ - expect.objectContaining({ - id: optionTwo.id, - }), - ]) - }) - - it("should return only requested fields and relations for options", async () => { - const [options, count] = await service.listAndCountOptions( - { - id: optionOne.id, - }, - { - select: ["title", "product.id"], - relations: ["product"], - take: 1, - } - ) - - expect(count).toEqual(1) - expect(options).toEqual([ - { - id: optionOne.id, - title: optionOne.title, - product_id: productOne.id, - product: { - id: productOne.id, - }, - }, - ]) - }) - }) - - describe("retrieveOption", () => { - it("should return the requested option", async () => { - const option = await service.retrieveOption(optionOne.id) - - expect(option).toEqual( - expect.objectContaining({ - id: optionOne.id, + beforeEach(async () => { + const testManager = await MikroOrmWrapper.forkManager() + productOne = testManager.create(Product, { + id: "product-1", + title: "product 1", + status: ProductTypes.ProductStatus.PUBLISHED, }) - ) - }) - it("should return requested attributes when requested through config", async () => { - const option = await service.retrieveOption(optionOne.id, { - select: ["id", "product.title"], - relations: ["product"], + productTwo = testManager.create(Product, { + id: "product-2", + title: "product 2", + status: ProductTypes.ProductStatus.PUBLISHED, + }) + + optionOne = testManager.create(ProductOption, { + id: "option-1", + title: "option 1", + product: productOne, + }) + + optionTwo = testManager.create(ProductOption, { + id: "option-2", + title: "option 1", + product: productTwo, + }) + + await testManager.persistAndFlush([optionOne, optionTwo]) }) - expect(option).toEqual( - expect.objectContaining({ - id: optionOne.id, - product: { - id: "product-1", - title: "product 1", - }, + describe("listOptions", () => { + it("should return options and count queried by ID", async () => { + const options = await service.listOptions({ + id: optionOne.id, + }) + + expect(options).toEqual([ + expect.objectContaining({ + id: optionOne.id, + }), + ]) }) - ) - }) - it("should throw an error when a option with ID does not exist", async () => { - let error + it("should return options and count based on the options and filter parameter", async () => { + let options = await service.listOptions( + { + id: optionOne.id, + }, + { + take: 1, + } + ) - try { - await service.retrieveOption("does-not-exist") - } catch (e) { - error = e - } + expect(options).toEqual([ + expect.objectContaining({ + id: optionOne.id, + }), + ]) - expect(error.message).toEqual( - "ProductOption with id: does-not-exist was not found" - ) - }) - }) + options = await service.listOptions({}, { take: 1, skip: 1 }) - describe("deleteOptions", () => { - const optionId = "option-1" + expect(options).toEqual([ + expect.objectContaining({ + id: optionTwo.id, + }), + ]) + }) - it("should delete the product option given an ID successfully", async () => { - await service.deleteOptions([optionId]) + it("should return only requested fields and relations for options", async () => { + const options = await service.listOptions( + { + id: optionOne.id, + }, + { + select: ["title", "product.id"], + relations: ["product"], + take: 1, + } + ) - const options = await service.listOptions({ - id: optionId, + expect(options).toEqual([ + { + id: optionOne.id, + title: optionOne.title, + product_id: productOne.id, + product: { + id: productOne.id, + }, + }, + ]) + }) }) - expect(options).toHaveLength(0) - }) - }) + describe("listAndCountOptions", () => { + it("should return options and count queried by ID", async () => { + const [options, count] = await service.listAndCountOptions({ + id: optionOne.id, + }) - describe("updateOptions", () => { - const optionId = "option-1" - - it("should update the title of the option successfully", async () => { - await service.updateOptions([ - { - id: optionId, - title: "new test", - }, - ]) - - const productOption = await service.retrieveOption(optionId) - - expect(productOption.title).toEqual("new test") - }) - - it("should throw an error when an id does not exist", async () => { - let error - - try { - await service.updateOptions([ - { - id: "does-not-exist", - }, - ]) - } catch (e) { - error = e - } - - expect(error.message).toEqual( - 'ProductOption with id "does-not-exist" not found' - ) - }) - }) - - describe("createOptions", () => { - it("should create a option successfully", async () => { - const res = await service.createOptions([ - { - title: "test", - product_id: productOne.id, - }, - ]) - - const [productOption] = await service.listOptions( - { - title: "test", - }, - { - select: ["id", "title", "product.id"], - relations: ["product"], - } - ) - - expect(productOption).toEqual( - expect.objectContaining({ - title: "test", - product: expect.objectContaining({ - id: productOne.id, - }), + expect(count).toEqual(1) + expect(options).toEqual([ + expect.objectContaining({ + id: optionOne.id, + }), + ]) }) - ) + + it("should return options and count based on the options and filter parameter", async () => { + let [options, count] = await service.listAndCountOptions( + { + id: optionOne.id, + }, + { + take: 1, + } + ) + + expect(count).toEqual(1) + expect(options).toEqual([ + expect.objectContaining({ + id: optionOne.id, + }), + ]) + ;[options, count] = await service.listAndCountOptions({}, { take: 1 }) + + expect(count).toEqual(2) + ;[options, count] = await service.listAndCountOptions( + {}, + { take: 1, skip: 1 } + ) + + expect(count).toEqual(2) + expect(options).toEqual([ + expect.objectContaining({ + id: optionTwo.id, + }), + ]) + }) + + it("should return only requested fields and relations for options", async () => { + const [options, count] = await service.listAndCountOptions( + { + id: optionOne.id, + }, + { + select: ["title", "product.id"], + relations: ["product"], + take: 1, + } + ) + + expect(count).toEqual(1) + expect(options).toEqual([ + { + id: optionOne.id, + title: optionOne.title, + product_id: productOne.id, + product: { + id: productOne.id, + }, + }, + ]) + }) + }) + + describe("retrieveOption", () => { + it("should return the requested option", async () => { + const option = await service.retrieveOption(optionOne.id) + + expect(option).toEqual( + expect.objectContaining({ + id: optionOne.id, + }) + ) + }) + + it("should return requested attributes when requested through config", async () => { + const option = await service.retrieveOption(optionOne.id, { + select: ["id", "product.title"], + relations: ["product"], + }) + + expect(option).toEqual( + expect.objectContaining({ + id: optionOne.id, + product: { + id: "product-1", + title: "product 1", + }, + }) + ) + }) + + it("should throw an error when a option with ID does not exist", async () => { + let error + + try { + await service.retrieveOption("does-not-exist") + } catch (e) { + error = e + } + + expect(error.message).toEqual( + "ProductOption with id: does-not-exist was not found" + ) + }) + }) + + describe("deleteOptions", () => { + const optionId = "option-1" + + it("should delete the product option given an ID successfully", async () => { + await service.deleteOptions([optionId]) + + const options = await service.listOptions({ + id: optionId, + }) + + expect(options).toHaveLength(0) + }) + }) + + describe("updateOptions", () => { + const optionId = "option-1" + + it("should update the title of the option successfully", async () => { + await service.updateOptions([ + { + id: optionId, + title: "new test", + }, + ]) + + const productOption = await service.retrieveOption(optionId) + + expect(productOption.title).toEqual("new test") + }) + + it("should throw an error when an id does not exist", async () => { + let error + + try { + await service.updateOptions([ + { + id: "does-not-exist", + }, + ]) + } catch (e) { + error = e + } + + expect(error.message).toEqual( + 'ProductOption with id "does-not-exist" not found' + ) + }) + }) + + describe("createOptions", () => { + it("should create a option successfully", async () => { + const res = await service.createOptions([ + { + title: "test", + product_id: productOne.id, + }, + ]) + + const [productOption] = await service.listOptions( + { + title: "test", + }, + { + select: ["id", "title", "product.id"], + relations: ["product"], + } + ) + + expect(productOption).toEqual( + expect.objectContaining({ + title: "test", + product: expect.objectContaining({ + id: productOne.id, + }), + }) + ) + }) + }) }) - }) + }, }) diff --git a/packages/product/integration-tests/__tests__/services/product-module-service/product-tags.spec.ts b/packages/product/integration-tests/__tests__/services/product-module-service/product-tags.spec.ts index cdb9bb4865..fc17d052c8 100644 --- a/packages/product/integration-tests/__tests__/services/product-module-service/product-tags.spec.ts +++ b/packages/product/integration-tests/__tests__/services/product-module-service/product-tags.spec.ts @@ -1,313 +1,298 @@ -import { MedusaModule, Modules } from "@medusajs/modules-sdk" +import { Modules } from "@medusajs/modules-sdk" import { IProductModuleService, ProductTypes } from "@medusajs/types" -import { SqlEntityManager } from "@mikro-orm/postgresql" import { Product, ProductTag } from "@models" -import { initModules } from "medusa-test-utils" -import { TestDatabase, getInitModuleConfig } from "../../../utils" +import { moduleIntegrationTestRunner, SuiteOptions } from "medusa-test-utils" -describe("ProductModuleService product tags", () => { - let service: IProductModuleService - let testManager: SqlEntityManager - let tagOne: ProductTag - let tagTwo: ProductTag - let productOne: Product - let productTwo: Product +jest.setTimeout(30000) - let shutdownFunc: () => Promise +moduleIntegrationTestRunner({ + moduleName: Modules.PRODUCT, + testSuite: ({ + MikroOrmWrapper, + service, + }: SuiteOptions) => { + describe("ProductModuleService product tags", () => { + let tagOne: ProductTag + let tagTwo: ProductTag + let productOne: Product + let productTwo: Product - beforeAll(async () => { - MedusaModule.clearInstances() - - const initModulesConfig = getInitModuleConfig() - - const { medusaApp, shutdown } = await initModules(initModulesConfig) - - service = medusaApp.modules[Modules.PRODUCT] - - shutdownFunc = shutdown - }) - - afterAll(async () => { - await shutdownFunc() - }) - - beforeEach(async () => { - await TestDatabase.setupDatabase() - - testManager = await TestDatabase.forkManager() - productOne = testManager.create(Product, { - id: "product-1", - title: "product 1", - status: ProductTypes.ProductStatus.PUBLISHED, - }) - - productTwo = testManager.create(Product, { - id: "product-2", - title: "product 2", - status: ProductTypes.ProductStatus.PUBLISHED, - }) - - tagOne = testManager.create(ProductTag, { - id: "tag-1", - value: "tag 1", - products: [productOne], - }) - - tagTwo = testManager.create(ProductTag, { - id: "tag-2", - value: "tag", - products: [productTwo], - }) - - await testManager.persistAndFlush([tagOne, tagTwo]) - }) - - afterEach(async () => { - await TestDatabase.clearDatabase() - }) - - describe("listTags", () => { - it("should return tags and count queried by ID", async () => { - const tags = await service.listTags({ - id: tagOne.id, - }) - - expect(tags).toEqual([ - expect.objectContaining({ - id: tagOne.id, - }), - ]) - }) - - it("should return tags and count based on the options and filter parameter", async () => { - let tags = await service.listTags( - { - id: tagOne.id, - }, - { - take: 1, - } - ) - - expect(tags).toEqual([ - expect.objectContaining({ - id: tagOne.id, - }), - ]) - - tags = await service.listTags({}, { take: 1, skip: 1 }) - - expect(tags).toEqual([ - expect.objectContaining({ - id: tagTwo.id, - }), - ]) - }) - - it("should return only requested fields and relations for tags", async () => { - const tags = await service.listTags( - { - id: tagOne.id, - }, - { - select: ["value", "products.id"], - relations: ["products"], - take: 1, - } - ) - - expect(tags).toEqual([ - { - id: tagOne.id, - value: tagOne.value, - products: [ - { - id: productOne.id, - }, - ], - }, - ]) - }) - }) - - describe("listAndCountTags", () => { - it("should return tags and count queried by ID", async () => { - const [tags, count] = await service.listAndCountTags({ - id: tagOne.id, - }) - - expect(count).toEqual(1) - expect(tags).toEqual([ - expect.objectContaining({ - id: tagOne.id, - }), - ]) - }) - - it("should return tags and count based on the options and filter parameter", async () => { - let [tags, count] = await service.listAndCountTags( - { - id: tagOne.id, - }, - { - take: 1, - } - ) - - expect(count).toEqual(1) - expect(tags).toEqual([ - expect.objectContaining({ - id: tagOne.id, - }), - ]) - ;[tags, count] = await service.listAndCountTags({}, { take: 1 }) - - expect(count).toEqual(2) - ;[tags, count] = await service.listAndCountTags({}, { take: 1, skip: 1 }) - - expect(count).toEqual(2) - expect(tags).toEqual([ - expect.objectContaining({ - id: tagTwo.id, - }), - ]) - }) - - it("should return only requested fields and relations for tags", async () => { - const [tags, count] = await service.listAndCountTags( - { - id: tagOne.id, - }, - { - select: ["value", "products.id"], - relations: ["products"], - take: 1, - } - ) - - expect(count).toEqual(1) - expect(tags).toEqual([ - { - id: tagOne.id, - value: tagOne.value, - products: [ - { - id: productOne.id, - }, - ], - }, - ]) - }) - }) - - describe("retrieveTag", () => { - it("should return the requested tag", async () => { - const tag = await service.retrieveTag(tagOne.id) - - expect(tag).toEqual( - expect.objectContaining({ - id: tagOne.id, + beforeEach(async () => { + const testManager = await MikroOrmWrapper.forkManager() + productOne = testManager.create(Product, { + id: "product-1", + title: "product 1", + status: ProductTypes.ProductStatus.PUBLISHED, }) - ) - }) - it("should return requested attributes when requested through config", async () => { - const tag = await service.retrieveTag(tagOne.id, { - select: ["id", "value", "products.title"], - relations: ["products"], + productTwo = testManager.create(Product, { + id: "product-2", + title: "product 2", + status: ProductTypes.ProductStatus.PUBLISHED, + }) + + tagOne = testManager.create(ProductTag, { + id: "tag-1", + value: "tag 1", + products: [productOne], + }) + + tagTwo = testManager.create(ProductTag, { + id: "tag-2", + value: "tag", + products: [productTwo], + }) + + await testManager.persistAndFlush([tagOne, tagTwo]) }) - expect(tag).toEqual( - expect.objectContaining({ - id: tagOne.id, - value: tagOne.value, - products: [ + describe("listTags", () => { + it("should return tags and count queried by ID", async () => { + const tags = await service.listTags({ + id: tagOne.id, + }) + + expect(tags).toEqual([ expect.objectContaining({ - title: "product 1", + id: tagOne.id, }), - ], + ]) }) - ) - }) - it("should throw an error when a tag with ID does not exist", async () => { - let error + it("should return tags and count based on the options and filter parameter", async () => { + let tags = await service.listTags( + { + id: tagOne.id, + }, + { + take: 1, + } + ) - try { - await service.retrieveTag("does-not-exist") - } catch (e) { - error = e - } + expect(tags).toEqual([ + expect.objectContaining({ + id: tagOne.id, + }), + ]) - expect(error.message).toEqual( - "ProductTag with id: does-not-exist was not found" - ) - }) - }) + tags = await service.listTags({}, { take: 1, skip: 1 }) - describe("deleteTags", () => { - const tagId = "tag-1" + expect(tags).toEqual([ + expect.objectContaining({ + id: tagTwo.id, + }), + ]) + }) - it("should delete the product tag given an ID successfully", async () => { - await service.deleteTags([tagId]) + it("should return only requested fields and relations for tags", async () => { + const tags = await service.listTags( + { + id: tagOne.id, + }, + { + select: ["value", "products.id"], + relations: ["products"], + take: 1, + } + ) - const tags = await service.listTags({ - id: tagId, + expect(tags).toEqual([ + { + id: tagOne.id, + value: tagOne.value, + products: [ + { + id: productOne.id, + }, + ], + }, + ]) + }) }) - expect(tags).toHaveLength(0) - }) - }) + describe("listAndCountTags", () => { + it("should return tags and count queried by ID", async () => { + const [tags, count] = await service.listAndCountTags({ + id: tagOne.id, + }) - describe("updateTags", () => { - const tagId = "tag-1" + expect(count).toEqual(1) + expect(tags).toEqual([ + expect.objectContaining({ + id: tagOne.id, + }), + ]) + }) - it("should update the value of the tag successfully", async () => { - await service.updateTags([ - { - id: tagId, - value: "UK", - }, - ]) + it("should return tags and count based on the options and filter parameter", async () => { + let [tags, count] = await service.listAndCountTags( + { + id: tagOne.id, + }, + { + take: 1, + } + ) - const productTag = await service.retrieveTag(tagId) + expect(count).toEqual(1) + expect(tags).toEqual([ + expect.objectContaining({ + id: tagOne.id, + }), + ]) + ;[tags, count] = await service.listAndCountTags({}, { take: 1 }) - expect(productTag.value).toEqual("UK") - }) + expect(count).toEqual(2) + ;[tags, count] = await service.listAndCountTags( + {}, + { take: 1, skip: 1 } + ) - it("should throw an error when an id does not exist", async () => { - let error + expect(count).toEqual(2) + expect(tags).toEqual([ + expect.objectContaining({ + id: tagTwo.id, + }), + ]) + }) - try { - await service.updateTags([ - { - id: "does-not-exist", + it("should return only requested fields and relations for tags", async () => { + const [tags, count] = await service.listAndCountTags( + { + id: tagOne.id, + }, + { + select: ["value", "products.id"], + relations: ["products"], + take: 1, + } + ) + + expect(count).toEqual(1) + expect(tags).toEqual([ + { + id: tagOne.id, + value: tagOne.value, + products: [ + { + id: productOne.id, + }, + ], + }, + ]) + }) + }) + + describe("retrieveTag", () => { + it("should return the requested tag", async () => { + const tag = await service.retrieveTag(tagOne.id) + + expect(tag).toEqual( + expect.objectContaining({ + id: tagOne.id, + }) + ) + }) + + it("should return requested attributes when requested through config", async () => { + const tag = await service.retrieveTag(tagOne.id, { + select: ["id", "value", "products.title"], + relations: ["products"], + }) + + expect(tag).toEqual( + expect.objectContaining({ + id: tagOne.id, + value: tagOne.value, + products: [ + expect.objectContaining({ + title: "product 1", + }), + ], + }) + ) + }) + + it("should throw an error when a tag with ID does not exist", async () => { + let error + + try { + await service.retrieveTag("does-not-exist") + } catch (e) { + error = e + } + + expect(error.message).toEqual( + "ProductTag with id: does-not-exist was not found" + ) + }) + }) + + describe("deleteTags", () => { + const tagId = "tag-1" + + it("should delete the product tag given an ID successfully", async () => { + await service.deleteTags([tagId]) + + const tags = await service.listTags({ + id: tagId, + }) + + expect(tags).toHaveLength(0) + }) + }) + + describe("updateTags", () => { + const tagId = "tag-1" + + it("should update the value of the tag successfully", async () => { + await service.updateTags([ + { + id: tagId, + value: "UK", + }, + ]) + + const productTag = await service.retrieveTag(tagId) + + expect(productTag.value).toEqual("UK") + }) + + it("should throw an error when an id does not exist", async () => { + let error + + try { + await service.updateTags([ + { + id: "does-not-exist", + value: "UK", + }, + ]) + } catch (e) { + error = e + } + + expect(error.message).toEqual( + 'ProductTag with id "does-not-exist" not found' + ) + }) + }) + + describe("createTags", () => { + it("should create a tag successfully", async () => { + const res = await service.createTags([ + { + value: "UK", + }, + ]) + + const productTag = await service.listTags({ value: "UK", - }, - ]) - } catch (e) { - error = e - } + }) - expect(error.message).toEqual( - 'ProductTag with id "does-not-exist" not found' - ) - }) - }) - - describe("createTags", () => { - it("should create a tag successfully", async () => { - const res = await service.createTags([ - { - value: "UK", - }, - ]) - - const productTag = await service.listTags({ - value: "UK", + expect(productTag[0]?.value).toEqual("UK") + }) }) - - expect(productTag[0]?.value).toEqual("UK") }) - }) + }, }) diff --git a/packages/product/integration-tests/__tests__/services/product-module-service/product-types.spec.ts b/packages/product/integration-tests/__tests__/services/product-module-service/product-types.spec.ts index 529a355b58..0dabd13bfe 100644 --- a/packages/product/integration-tests/__tests__/services/product-module-service/product-types.spec.ts +++ b/packages/product/integration-tests/__tests__/services/product-module-service/product-types.spec.ts @@ -1,281 +1,263 @@ -import { MedusaModule, Modules } from "@medusajs/modules-sdk" +import { Modules } from "@medusajs/modules-sdk" import { IProductModuleService } from "@medusajs/types" -import { SqlEntityManager } from "@mikro-orm/postgresql" import { ProductType } from "@models" -import { initModules } from "medusa-test-utils" -import { getInitModuleConfig, TestDatabase } from "../../../utils" +import { moduleIntegrationTestRunner, SuiteOptions } from "medusa-test-utils" -describe("ProductModuleService product types", () => { - let service: IProductModuleService - let testManager: SqlEntityManager - let typeOne: ProductType - let typeTwo: ProductType +jest.setTimeout(30000) - let shutdownFunc: () => Promise +moduleIntegrationTestRunner({ + moduleName: Modules.PRODUCT, + testSuite: ({ + MikroOrmWrapper, + service, + }: SuiteOptions) => { + describe("ProductModuleService product types", () => { + let typeOne: ProductType + let typeTwo: ProductType - beforeAll(async () => { - MedusaModule.clearInstances() + beforeEach(async () => { + const testManager = await MikroOrmWrapper.forkManager() - const initModulesConfig = getInitModuleConfig() - - const { medusaApp, shutdown } = await initModules(initModulesConfig) - - service = medusaApp.modules[Modules.PRODUCT] - - shutdownFunc = shutdown - }) - - afterAll(async () => { - await shutdownFunc() - }) - - beforeEach(async () => { - await TestDatabase.setupDatabase() - - testManager = await TestDatabase.forkManager() - - typeOne = testManager.create(ProductType, { - id: "type-1", - value: "type 1", - }) - - typeTwo = testManager.create(ProductType, { - id: "type-2", - value: "type", - }) - - await testManager.persistAndFlush([typeOne, typeTwo]) - }) - - afterEach(async () => { - await TestDatabase.clearDatabase() - }) - - describe("listTypes", () => { - it("should return types and count queried by ID", async () => { - const types = await service.listTypes({ - id: typeOne.id, - }) - - expect(types).toEqual([ - expect.objectContaining({ - id: typeOne.id, - }), - ]) - }) - - it("should return types and count based on the options and filter parameter", async () => { - let types = await service.listTypes( - { - id: typeOne.id, - }, - { - take: 1, - } - ) - - expect(types).toEqual([ - expect.objectContaining({ - id: typeOne.id, - }), - ]) - - types = await service.listTypes({}, { take: 1, skip: 1 }) - - expect(types).toEqual([ - expect.objectContaining({ - id: typeTwo.id, - }), - ]) - }) - - it("should return only requested fields for types", async () => { - const types = await service.listTypes( - { - id: typeOne.id, - }, - { - select: ["value"], - take: 1, - } - ) - - expect(types).toEqual([ - { - id: typeOne.id, - value: typeOne.value, - }, - ]) - }) - }) - - describe("listAndCountTypes", () => { - it("should return types and count queried by ID", async () => { - const [types, count] = await service.listAndCountTypes({ - id: typeOne.id, - }) - - expect(count).toEqual(1) - expect(types).toEqual([ - expect.objectContaining({ - id: typeOne.id, - }), - ]) - }) - - it("should return types and count based on the options and filter parameter", async () => { - let [types, count] = await service.listAndCountTypes( - { - id: typeOne.id, - }, - { - take: 1, - } - ) - - expect(count).toEqual(1) - expect(types).toEqual([ - expect.objectContaining({ - id: typeOne.id, - }), - ]) - ;[types, count] = await service.listAndCountTypes({}, { take: 1 }) - - expect(count).toEqual(2) - ;[types, count] = await service.listAndCountTypes( - {}, - { take: 1, skip: 1 } - ) - - expect(count).toEqual(2) - expect(types).toEqual([ - expect.objectContaining({ - id: typeTwo.id, - }), - ]) - }) - - it("should return only requested fields for types", async () => { - const [types, count] = await service.listAndCountTypes( - { - id: typeOne.id, - }, - { - select: ["value"], - take: 1, - } - ) - - expect(count).toEqual(1) - expect(types).toEqual([ - { - id: typeOne.id, - value: typeOne.value, - }, - ]) - }) - }) - - describe("retrieveType", () => { - it("should return the requested type", async () => { - const type = await service.retrieveType(typeOne.id) - - expect(type).toEqual( - expect.objectContaining({ - id: typeOne.id, + typeOne = testManager.create(ProductType, { + id: "type-1", + value: "type 1", }) - ) - }) - it("should return requested attributes when requested through config", async () => { - const type = await service.retrieveType(typeOne.id, { - select: ["id", "value"], + typeTwo = testManager.create(ProductType, { + id: "type-2", + value: "type", + }) + + await testManager.persistAndFlush([typeOne, typeTwo]) }) - expect(type).toEqual({ - id: typeOne.id, - value: typeOne.value, - }) - }) + describe("listTypes", () => { + it("should return types and count queried by ID", async () => { + const types = await service.listTypes({ + id: typeOne.id, + }) - it("should throw an error when a type with ID does not exist", async () => { - let error + expect(types).toEqual([ + expect.objectContaining({ + id: typeOne.id, + }), + ]) + }) - try { - await service.retrieveType("does-not-exist") - } catch (e) { - error = e - } + it("should return types and count based on the options and filter parameter", async () => { + let types = await service.listTypes( + { + id: typeOne.id, + }, + { + take: 1, + } + ) - expect(error.message).toEqual( - "ProductType with id: does-not-exist was not found" - ) - }) - }) + expect(types).toEqual([ + expect.objectContaining({ + id: typeOne.id, + }), + ]) - describe("deleteTypes", () => { - const typeId = "type-1" + types = await service.listTypes({}, { take: 1, skip: 1 }) - it("should delete the product type given an ID successfully", async () => { - await service.deleteTypes([typeId]) + expect(types).toEqual([ + expect.objectContaining({ + id: typeTwo.id, + }), + ]) + }) - const types = await service.listTypes({ - id: typeId, + it("should return only requested fields for types", async () => { + const types = await service.listTypes( + { + id: typeOne.id, + }, + { + select: ["value"], + take: 1, + } + ) + + expect(types).toEqual([ + { + id: typeOne.id, + value: typeOne.value, + }, + ]) + }) }) - expect(types).toHaveLength(0) - }) - }) + describe("listAndCountTypes", () => { + it("should return types and count queried by ID", async () => { + const [types, count] = await service.listAndCountTypes({ + id: typeOne.id, + }) - describe("updateTypes", () => { - const typeId = "type-1" + expect(count).toEqual(1) + expect(types).toEqual([ + expect.objectContaining({ + id: typeOne.id, + }), + ]) + }) - it("should update the value of the type successfully", async () => { - await service.updateTypes([ - { - id: typeId, - value: "UK", - }, - ]) + it("should return types and count based on the options and filter parameter", async () => { + let [types, count] = await service.listAndCountTypes( + { + id: typeOne.id, + }, + { + take: 1, + } + ) - const productType = await service.retrieveType(typeId) + expect(count).toEqual(1) + expect(types).toEqual([ + expect.objectContaining({ + id: typeOne.id, + }), + ]) + ;[types, count] = await service.listAndCountTypes({}, { take: 1 }) - expect(productType.value).toEqual("UK") - }) + expect(count).toEqual(2) + ;[types, count] = await service.listAndCountTypes( + {}, + { take: 1, skip: 1 } + ) - it("should throw an error when an id does not exist", async () => { - let error + expect(count).toEqual(2) + expect(types).toEqual([ + expect.objectContaining({ + id: typeTwo.id, + }), + ]) + }) - try { - await service.updateTypes([ - { - id: "does-not-exist", + it("should return only requested fields for types", async () => { + const [types, count] = await service.listAndCountTypes( + { + id: typeOne.id, + }, + { + select: ["value"], + take: 1, + } + ) + + expect(count).toEqual(1) + expect(types).toEqual([ + { + id: typeOne.id, + value: typeOne.value, + }, + ]) + }) + }) + + describe("retrieveType", () => { + it("should return the requested type", async () => { + const type = await service.retrieveType(typeOne.id) + + expect(type).toEqual( + expect.objectContaining({ + id: typeOne.id, + }) + ) + }) + + it("should return requested attributes when requested through config", async () => { + const type = await service.retrieveType(typeOne.id, { + select: ["id", "value"], + }) + + expect(type).toEqual({ + id: typeOne.id, + value: typeOne.value, + }) + }) + + it("should throw an error when a type with ID does not exist", async () => { + let error + + try { + await service.retrieveType("does-not-exist") + } catch (e) { + error = e + } + + expect(error.message).toEqual( + "ProductType with id: does-not-exist was not found" + ) + }) + }) + + describe("deleteTypes", () => { + const typeId = "type-1" + + it("should delete the product type given an ID successfully", async () => { + await service.deleteTypes([typeId]) + + const types = await service.listTypes({ + id: typeId, + }) + + expect(types).toHaveLength(0) + }) + }) + + describe("updateTypes", () => { + const typeId = "type-1" + + it("should update the value of the type successfully", async () => { + await service.updateTypes([ + { + id: typeId, + value: "UK", + }, + ]) + + const productType = await service.retrieveType(typeId) + + expect(productType.value).toEqual("UK") + }) + + it("should throw an error when an id does not exist", async () => { + let error + + try { + await service.updateTypes([ + { + id: "does-not-exist", + value: "UK", + }, + ]) + } catch (e) { + error = e + } + + expect(error.message).toEqual( + 'ProductType with id "does-not-exist" not found' + ) + }) + }) + + describe("createTypes", () => { + it("should create a type successfully", async () => { + const res = await service.createTypes([ + { + value: "UK", + }, + ]) + + const productType = await service.listTypes({ value: "UK", - }, - ]) - } catch (e) { - error = e - } + }) - expect(error.message).toEqual( - 'ProductType with id "does-not-exist" not found' - ) - }) - }) - - describe("createTypes", () => { - it("should create a type successfully", async () => { - const res = await service.createTypes([ - { - value: "UK", - }, - ]) - - const productType = await service.listTypes({ - value: "UK", + expect(productType[0]?.value).toEqual("UK") + }) }) - - expect(productType[0]?.value).toEqual("UK") }) - }) + }, }) diff --git a/packages/product/integration-tests/__tests__/services/product-module-service/product-variants.spec.ts b/packages/product/integration-tests/__tests__/services/product-module-service/product-variants.spec.ts index 8fe3edddc0..b61a5e92be 100644 --- a/packages/product/integration-tests/__tests__/services/product-module-service/product-variants.spec.ts +++ b/packages/product/integration-tests/__tests__/services/product-module-service/product-variants.spec.ts @@ -1,186 +1,170 @@ -import { MedusaModule, Modules } from "@medusajs/modules-sdk" +import { Modules } from "@medusajs/modules-sdk" import { IProductModuleService, ProductTypes } from "@medusajs/types" -import { SqlEntityManager } from "@mikro-orm/postgresql" import { Product, ProductVariant } from "@models" -import { initModules } from "medusa-test-utils" -import { TestDatabase, getInitModuleConfig } from "../../../utils" -describe("ProductModuleService product variants", () => { - let service: IProductModuleService - let testManager: SqlEntityManager - let variantOne: ProductVariant - let variantTwo: ProductVariant - let productOne: Product - let productTwo: Product +import { moduleIntegrationTestRunner, SuiteOptions } from "medusa-test-utils" - let shutdownFunc: () => Promise +jest.setTimeout(30000) - beforeAll(async () => { - MedusaModule.clearInstances() +moduleIntegrationTestRunner({ + moduleName: Modules.PRODUCT, + testSuite: ({ + MikroOrmWrapper, + service, + }: SuiteOptions) => { + describe("ProductModuleService product variants", () => { + let variantOne: ProductVariant + let variantTwo: ProductVariant + let productOne: Product + let productTwo: Product - const initModulesConfig = getInitModuleConfig() + beforeEach(async () => { + const testManager = await MikroOrmWrapper.forkManager() - const { medusaApp, shutdown } = await initModules(initModulesConfig) + productOne = testManager.create(Product, { + id: "product-1", + title: "product 1", + status: ProductTypes.ProductStatus.PUBLISHED, + }) - service = medusaApp.modules[Modules.PRODUCT] + productTwo = testManager.create(Product, { + id: "product-2", + title: "product 2", + status: ProductTypes.ProductStatus.PUBLISHED, + }) - shutdownFunc = shutdown - }) + variantOne = testManager.create(ProductVariant, { + id: "test-1", + title: "variant 1", + inventory_quantity: 10, + product: productOne, + }) - afterAll(async () => { - await shutdownFunc() - }) + variantTwo = testManager.create(ProductVariant, { + id: "test-2", + title: "variant", + inventory_quantity: 10, + product: productTwo, + }) - beforeEach(async () => { - await TestDatabase.setupDatabase() - testManager = await TestDatabase.forkManager() - - productOne = testManager.create(Product, { - id: "product-1", - title: "product 1", - status: ProductTypes.ProductStatus.PUBLISHED, - }) - - productTwo = testManager.create(Product, { - id: "product-2", - title: "product 2", - status: ProductTypes.ProductStatus.PUBLISHED, - }) - - variantOne = testManager.create(ProductVariant, { - id: "test-1", - title: "variant 1", - inventory_quantity: 10, - product: productOne, - }) - - variantTwo = testManager.create(ProductVariant, { - id: "test-2", - title: "variant", - inventory_quantity: 10, - product: productTwo, - }) - - await testManager.persistAndFlush([variantOne, variantTwo]) - }) - - afterEach(async () => { - await TestDatabase.clearDatabase() - }) - - describe("listAndCountVariants", () => { - it("should return variants and count queried by ID", async () => { - const results = await service.listAndCountVariants({ - id: variantOne.id, + await testManager.persistAndFlush([variantOne, variantTwo]) }) - expect(results[1]).toEqual(1) - expect(results[0]).toEqual([ - expect.objectContaining({ - id: variantOne.id, - }), - ]) - }) + describe("listAndCountVariants", () => { + it("should return variants and count queried by ID", async () => { + const results = await service.listAndCountVariants({ + id: variantOne.id, + }) - it("should return variants and count based on the options and filter parameter", async () => { - let results = await service.listAndCountVariants( - { - id: variantOne.id, - }, - { - take: 1, - } - ) - - expect(results[1]).toEqual(1) - expect(results[0]).toEqual([ - expect.objectContaining({ - id: variantOne.id, - }), - ]) - - results = await service.listAndCountVariants({}, { take: 1 }) - - expect(results[1]).toEqual(2) - - results = await service.listAndCountVariants({}, { take: 1, skip: 1 }) - - expect(results[1]).toEqual(2) - expect(results[0]).toEqual([ - expect.objectContaining({ - id: variantTwo.id, - }), - ]) - }) - - it("should return only requested fields and relations for variants", async () => { - const results = await service.listAndCountVariants( - { - id: variantOne.id, - }, - { - select: ["id", "title", "product.title"] as any, - relations: ["product"], - } - ) - - expect(results[1]).toEqual(1) - expect(results[0]).toEqual([ - expect.objectContaining({ - id: "test-1", - title: "variant 1", - // TODO: investigate why this is returning more than the expected results - product: expect.objectContaining({ - id: "product-1", - title: "product 1", - }), - }), - ]) - }) - }) - - describe("retrieveVariant", () => { - it("should return the requested variant", async () => { - const result = await service.retrieveVariant(variantOne.id) - - expect(result).toEqual( - expect.objectContaining({ - id: "test-1", - title: "variant 1", + expect(results[1]).toEqual(1) + expect(results[0]).toEqual([ + expect.objectContaining({ + id: variantOne.id, + }), + ]) }) - ) - }) - it("should return requested attributes when requested through config", async () => { - const result = await service.retrieveVariant(variantOne.id, { - select: ["id", "title", "product.title"] as any, - relations: ["product"], + it("should return variants and count based on the options and filter parameter", async () => { + let results = await service.listAndCountVariants( + { + id: variantOne.id, + }, + { + take: 1, + } + ) + + expect(results[1]).toEqual(1) + expect(results[0]).toEqual([ + expect.objectContaining({ + id: variantOne.id, + }), + ]) + + results = await service.listAndCountVariants({}, { take: 1 }) + + expect(results[1]).toEqual(2) + + results = await service.listAndCountVariants({}, { take: 1, skip: 1 }) + + expect(results[1]).toEqual(2) + expect(results[0]).toEqual([ + expect.objectContaining({ + id: variantTwo.id, + }), + ]) + }) + + it("should return only requested fields and relations for variants", async () => { + const results = await service.listAndCountVariants( + { + id: variantOne.id, + }, + { + select: ["id", "title", "product.title"] as any, + relations: ["product"], + } + ) + + expect(results[1]).toEqual(1) + expect(results[0]).toEqual([ + expect.objectContaining({ + id: "test-1", + title: "variant 1", + // TODO: investigate why this is returning more than the expected results + product: expect.objectContaining({ + id: "product-1", + title: "product 1", + }), + }), + ]) + }) }) - expect(result).toEqual( - expect.objectContaining({ - id: "test-1", - title: "variant 1", - product: expect.objectContaining({ - id: "product-1", - title: "product 1", - }), + describe("retrieveVariant", () => { + it("should return the requested variant", async () => { + const result = await service.retrieveVariant(variantOne.id) + + expect(result).toEqual( + expect.objectContaining({ + id: "test-1", + title: "variant 1", + }) + ) }) - ) + + it("should return requested attributes when requested through config", async () => { + const result = await service.retrieveVariant(variantOne.id, { + select: ["id", "title", "product.title"] as any, + relations: ["product"], + }) + + expect(result).toEqual( + expect.objectContaining({ + id: "test-1", + title: "variant 1", + product: expect.objectContaining({ + id: "product-1", + title: "product 1", + }), + }) + ) + }) + + it("should throw an error when a variant with ID does not exist", async () => { + let error + + try { + await service.retrieveVariant("does-not-exist") + } catch (e) { + error = e + } + + expect(error.message).toEqual( + "ProductVariant with id: does-not-exist was not found" + ) + }) + }) }) - - it("should throw an error when a variant with ID does not exist", async () => { - let error - - try { - await service.retrieveVariant("does-not-exist") - } catch (e) { - error = e - } - - expect(error.message).toEqual( - "ProductVariant with id: does-not-exist was not found" - ) - }) - }) + }, }) diff --git a/packages/product/integration-tests/__tests__/services/product-module-service/products.spec.ts b/packages/product/integration-tests/__tests__/services/product-module-service/products.spec.ts index a8fa89c8f2..5ada08793c 100644 --- a/packages/product/integration-tests/__tests__/services/product-module-service/products.spec.ts +++ b/packages/product/integration-tests/__tests__/services/product-module-service/products.spec.ts @@ -1,4 +1,4 @@ -import { MedusaModule, Modules } from "@medusajs/modules-sdk" +import { Modules } from "@medusajs/modules-sdk" import { IProductModuleService, ProductTypes } from "@medusajs/types" import { kebabCase } from "@medusajs/utils" import { @@ -9,955 +9,865 @@ import { ProductVariant, } from "@models" -import { initModules } from "medusa-test-utils" -import { initialize } from "../../../../src" -import { EventBusService } from "../../../__fixtures__/event-bus" +import { MockEventBusService } from "medusa-test-utils" import { createCollections, createTypes } from "../../../__fixtures__/product" import { createProductCategories } from "../../../__fixtures__/product-category" import { buildProductAndRelationsData } from "../../../__fixtures__/product/data/create-product" -import { DB_URL, TestDatabase, getInitModuleConfig } from "../../../utils" import { UpdateProductInput } from "../../../../src/types/services/product" +import { moduleIntegrationTestRunner, SuiteOptions } from "medusa-test-utils" jest.setTimeout(30000) -const beforeEach_ = async () => { - await TestDatabase.setupDatabase() - return await TestDatabase.forkManager() -} +moduleIntegrationTestRunner({ + moduleName: Modules.PRODUCT, + injectedDependencies: { + eventBusModuleService: new MockEventBusService(), + }, + testSuite: ({ + MikroOrmWrapper, + service, + }: SuiteOptions) => { + describe("ProductModuleService products", function () { + let productCollectionOne: ProductCollection + let productCollectionTwo: ProductCollection -const afterEach_ = async () => { - await TestDatabase.clearDatabase() - jest.clearAllMocks() -} + const productCollectionsData = [ + { + id: "test-1", + title: "col 1", + }, + { + id: "test-2", + title: "col 2", + }, + ] -describe("ProductModuleService products", function () { - let productCollectionOne: ProductCollection - let productCollectionTwo: ProductCollection - - const productCollectionsData = [ - { - id: "test-1", - title: "col 1", - }, - { - id: "test-2", - title: "col 2", - }, - ] - - describe("update", function () { - let module: IProductModuleService - let productOne: Product - let productTwo: Product - let productCategoryOne: ProductCategory - let productCategoryTwo: ProductCategory - let variantOne: ProductVariant - let variantTwo: ProductVariant - let variantThree: ProductVariant - let productTypeOne: ProductType - let productTypeTwo: ProductType - let images = ["image-1"] - - const productCategoriesData = [ - { - id: "test-1", - name: "category 1", - }, - { - id: "test-2", - name: "category 2", - }, - ] - - const productTypesData = [ - { - id: "type-1", - value: "type 1", - }, - { - id: "type-2", - value: "type 2", - }, - ] - - const tagsData = [ - { - id: "tag-1", - value: "tag 1", - }, - ] - - let shutdownFunc: () => Promise - - beforeAll(async () => { - MedusaModule.clearInstances() - - const initModulesConfig = getInitModuleConfig() - - const { medusaApp, shutdown } = await initModules(initModulesConfig) - - module = medusaApp.modules[Modules.PRODUCT] - - shutdownFunc = shutdown - }) - - afterAll(async () => { - await shutdownFunc() - }) - - beforeEach(async () => { - const testManager = await beforeEach_() - - const collections = await createCollections( - testManager, - productCollectionsData - ) - - productCollectionOne = collections[0] - productCollectionTwo = collections[1] - - const types = await createTypes(testManager, productTypesData) - - productTypeOne = types[0] - productTypeTwo = types[1] - - const categories = await createProductCategories( - testManager, - productCategoriesData - ) - - productCategoryOne = categories[0] - productCategoryTwo = categories[1] - - productOne = testManager.create(Product, { - id: "product-1", - title: "product 1", - status: ProductTypes.ProductStatus.PUBLISHED, + afterEach(() => { + jest.clearAllMocks() }) - productTwo = testManager.create(Product, { - id: "product-2", - title: "product 2", - status: ProductTypes.ProductStatus.PUBLISHED, - categories: [productCategoryOne], - collection_id: productCollectionOne.id, - tags: tagsData, - }) + describe("update", function () { + let productOne: Product + let productTwo: Product + let productCategoryOne: ProductCategory + let productCategoryTwo: ProductCategory + let variantTwo: ProductVariant + let productTypeOne: ProductType + let productTypeTwo: ProductType + let images = ["image-1"] - variantOne = testManager.create(ProductVariant, { - id: "variant-1", - title: "variant 1", - inventory_quantity: 10, - product: productOne, - }) + const productCategoriesData = [ + { + id: "test-1", + name: "category 1", + }, + { + id: "test-2", + name: "category 2", + }, + ] - variantTwo = testManager.create(ProductVariant, { - id: "variant-2", - title: "variant 2", - inventory_quantity: 10, - product: productTwo, - }) + const productTypesData = [ + { + id: "type-1", + value: "type 1", + }, + { + id: "type-2", + value: "type 2", + }, + ] - variantThree = testManager.create(ProductVariant, { - id: "variant-3", - title: "variant 3", - inventory_quantity: 10, - product: productTwo, - }) + const tagsData = [ + { + id: "tag-1", + value: "tag 1", + }, + ] - await testManager.persistAndFlush([productOne, productTwo]) - }) + beforeEach(async () => { + const testManager = MikroOrmWrapper.forkManager() - afterEach(afterEach_) + const collections = await createCollections( + testManager, + productCollectionsData + ) - it("should update a product and upsert relations that are not created yet", async () => { - const data = buildProductAndRelationsData({ - images, - thumbnail: images[0], - }) + productCollectionOne = collections[0] + productCollectionTwo = collections[1] - const variantTitle = data.variants[0].title + const types = await createTypes(testManager, productTypesData) - const productBefore = (await module.retrieve(productOne.id, { - relations: [ - "images", - "variants", - "options", - "options.values", - "variants.options", - "tags", - "type", - ], - })) as unknown as UpdateProductInput + productTypeOne = types[0] + productTypeTwo = types[1] - productBefore.title = "updated title" - productBefore.variants = [...productBefore.variants!, ...data.variants] - productBefore.type = { value: "new-type" } - productBefore.options = data.options - productBefore.images = data.images - productBefore.thumbnail = data.thumbnail - productBefore.tags = data.tags + const categories = await createProductCategories( + testManager, + productCategoriesData + ) - const updatedProducts = await module.upsert([productBefore]) - expect(updatedProducts).toHaveLength(1) + productCategoryOne = categories[0] + productCategoryTwo = categories[1] - const product = await module.retrieve(productBefore.id, { - relations: [ - "images", - "variants", - "options", - "options.values", - "variants.options", - "tags", - "type", - ], - }) + productOne = testManager.create(Product, { + id: "product-1", + title: "product 1", + status: ProductTypes.ProductStatus.PUBLISHED, + }) - const createdVariant = product.variants.find( - (v) => v.title === variantTitle - )! + productTwo = testManager.create(Product, { + id: "product-2", + title: "product 2", + status: ProductTypes.ProductStatus.PUBLISHED, + categories: [productCategoryOne], + collection_id: productCollectionOne.id, + tags: tagsData, + }) - expect(product.images).toHaveLength(1) - expect(createdVariant?.options).toHaveLength(1) - expect(product.tags).toHaveLength(1) - expect(product.variants).toHaveLength(2) + testManager.create(ProductVariant, { + id: "variant-1", + title: "variant 1", + inventory_quantity: 10, + product: productOne, + }) - expect(product).toEqual( - expect.objectContaining({ - id: expect.any(String), - title: "updated title", - description: productBefore.description, - subtitle: productBefore.subtitle, - is_giftcard: productBefore.is_giftcard, - discountable: productBefore.discountable, - thumbnail: images[0], - status: productBefore.status, - images: expect.arrayContaining([ + variantTwo = testManager.create(ProductVariant, { + id: "variant-2", + title: "variant 2", + inventory_quantity: 10, + product: productTwo, + }) + + testManager.create(ProductVariant, { + id: "variant-3", + title: "variant 3", + inventory_quantity: 10, + product: productTwo, + }) + + await testManager.persistAndFlush([productOne, productTwo]) + }) + + it("should update a product and upsert relations that are not created yet", async () => { + const data = buildProductAndRelationsData({ + images, + thumbnail: images[0], + }) + + const variantTitle = data.variants[0].title + + const productBefore = (await service.retrieve(productOne.id, { + relations: [ + "images", + "variants", + "options", + "options.values", + "variants.options", + "tags", + "type", + ], + })) as unknown as UpdateProductInput + + productBefore.title = "updated title" + productBefore.variants = [ + ...productBefore.variants!, + ...data.variants, + ] + productBefore.type = { value: "new-type" } + // TODO: Change test (this one and below) to not require setting product ID here. + productBefore.options = data.options.map((o) => ({ + ...o, + product_id: productBefore.id, + })) + productBefore.images = data.images + productBefore.thumbnail = data.thumbnail + productBefore.tags = data.tags + + const updatedProducts = await service.upsert([productBefore]) + expect(updatedProducts).toHaveLength(1) + + const product = await service.retrieve(productBefore.id, { + relations: [ + "images", + "variants", + "options", + "options.values", + "variants.options", + "tags", + "type", + ], + }) + + const createdVariant = product.variants.find( + (v) => v.title === variantTitle + )! + + expect(product.images).toHaveLength(1) + expect(createdVariant?.options).toHaveLength(1) + expect(product.tags).toHaveLength(1) + expect(product.variants).toHaveLength(2) + + expect(product).toEqual( expect.objectContaining({ id: expect.any(String), - url: images[0], - }), - ]), - options: expect.arrayContaining([ - expect.objectContaining({ - id: expect.any(String), - title: productBefore.options?.[0].title, - values: expect.arrayContaining([ + title: "updated title", + description: productBefore.description, + subtitle: productBefore.subtitle, + is_giftcard: productBefore.is_giftcard, + discountable: productBefore.discountable, + thumbnail: images[0], + status: productBefore.status, + images: expect.arrayContaining([ expect.objectContaining({ id: expect.any(String), - value: createdVariant.options?.[0].value, + url: images[0], }), ]), - }), - ]), - tags: expect.arrayContaining([ - expect.objectContaining({ - id: expect.any(String), - value: productBefore.tags?.[0].value, - }), - ]), - type: expect.objectContaining({ - id: expect.any(String), - value: productBefore.type!.value, - }), - variants: expect.arrayContaining([ - expect.objectContaining({ - id: expect.any(String), - title: createdVariant.title, - sku: createdVariant.sku, - allow_backorder: false, - manage_inventory: true, - inventory_quantity: 100, - variant_rank: 0, options: expect.arrayContaining([ expect.objectContaining({ id: expect.any(String), - value: createdVariant.options?.[0].value, + title: productBefore.options?.[0].title, + values: expect.arrayContaining([ + expect.objectContaining({ + id: expect.any(String), + value: createdVariant.options?.[0].value, + }), + ]), }), ]), - }), - ]), + tags: expect.arrayContaining([ + expect.objectContaining({ + id: expect.any(String), + value: productBefore.tags?.[0].value, + }), + ]), + type: expect.objectContaining({ + id: expect.any(String), + value: productBefore.type!.value, + }), + variants: expect.arrayContaining([ + expect.objectContaining({ + id: expect.any(String), + title: createdVariant.title, + sku: createdVariant.sku, + allow_backorder: false, + manage_inventory: true, + inventory_quantity: 100, + variant_rank: 0, + options: expect.arrayContaining([ + expect.objectContaining({ + id: expect.any(String), + value: createdVariant.options?.[0].value, + }), + ]), + }), + ]), + }) + ) }) - ) - }) - it("should emit events through event bus", async () => { - const eventBusSpy = jest.spyOn(EventBusService.prototype, "emit") - const data = buildProductAndRelationsData({ - images, - thumbnail: images[0], - }) + it("should emit events through event bus", async () => { + const eventBusSpy = jest.spyOn(MockEventBusService.prototype, "emit") + const data = buildProductAndRelationsData({ + images, + thumbnail: images[0], + }) - const updateData = { - ...data, - id: productOne.id, - title: "updated title", - } + const updateData = { + ...data, + options: data.options.map((o) => ({ + ...o, + product_id: productOne.id, + })), + id: productOne.id, + title: "updated title", + } - await module.upsert([updateData]) + await service.upsert([updateData]) - expect(eventBusSpy).toHaveBeenCalledTimes(1) - expect(eventBusSpy).toHaveBeenCalledWith([ - { - eventName: "product.updated", - data: { id: productOne.id }, - }, - ]) - }) + expect(eventBusSpy).toHaveBeenCalledTimes(1) + expect(eventBusSpy).toHaveBeenCalledWith([ + { + eventName: "product.updated", + data: { id: productOne.id }, + }, + ]) + }) - it("should add relationships to a product", async () => { - const updateData = { - id: productOne.id, - categories: [ - { - id: productCategoryOne.id, - }, - ], - collection_id: productCollectionOne.id, - type_id: productTypeOne.id, - } + it("should add relationships to a product", async () => { + const updateData = { + id: productOne.id, + categories: [ + { + id: productCategoryOne.id, + }, + ], + collection_id: productCollectionOne.id, + type_id: productTypeOne.id, + } - await module.upsert([updateData]) + await service.upsert([updateData]) - const product = await module.retrieve(updateData.id, { - relations: ["categories", "collection", "type"], - }) + const product = await service.retrieve(updateData.id, { + relations: ["categories", "collection", "type"], + }) - expect(product).toEqual( - expect.objectContaining({ - id: productOne.id, - categories: [ + expect(product).toEqual( expect.objectContaining({ - id: productCategoryOne.id, - }), - ], - collection: expect.objectContaining({ - id: productCollectionOne.id, - }), - type: expect.objectContaining({ - id: productTypeOne.id, - }), + id: productOne.id, + categories: [ + expect.objectContaining({ + id: productCategoryOne.id, + }), + ], + collection: expect.objectContaining({ + id: productCollectionOne.id, + }), + type: expect.objectContaining({ + id: productTypeOne.id, + }), + }) + ) }) - ) - }) - it("should upsert a product type when type object is passed", async () => { - let updateData = { - id: productTwo.id, - type: { - id: productTypeOne.id, - value: productTypeOne.value, - }, - } + it("should upsert a product type when type object is passed", async () => { + let updateData = { + id: productTwo.id, + type: { + id: productTypeOne.id, + value: productTypeOne.value, + }, + } - await module.upsert([updateData]) + await service.upsert([updateData]) - let product = await module.retrieve(updateData.id, { - relations: ["type"], - }) + let product = await service.retrieve(updateData.id, { + relations: ["type"], + }) - expect(product).toEqual( - expect.objectContaining({ - id: productTwo.id, - type: expect.objectContaining({ - id: productTypeOne.id, - }), - }) - ) - - updateData = { - id: productTwo.id, - type: { - id: "new-type-id", - value: "new-type-value", - }, - } - - await module.upsert([updateData]) - - product = await module.retrieve(updateData.id, { - relations: ["type"], - }) - - expect(product).toEqual( - expect.objectContaining({ - id: productTwo.id, - type: expect.objectContaining({ - ...updateData.type, - }), - }) - ) - }) - - it("should replace relationships of a product", async () => { - const newTagData = { - id: "tag-2", - value: "tag 2", - } - - const updateData = { - id: productTwo.id, - categories: [ - { - id: productCategoryTwo.id, - }, - ], - collection_id: productCollectionTwo.id, - type_id: productTypeTwo.id, - tags: [newTagData], - } - - await module.upsert([updateData]) - - const product = await module.retrieve(updateData.id, { - relations: ["categories", "collection", "tags", "type"], - }) - - expect(product).toEqual( - expect.objectContaining({ - id: productTwo.id, - categories: [ + expect(product).toEqual( expect.objectContaining({ - id: productCategoryTwo.id, - }), - ], - collection: expect.objectContaining({ - id: productCollectionTwo.id, - }), - tags: [ + id: productTwo.id, + type: expect.objectContaining({ + id: productTypeOne.id, + }), + }) + ) + + updateData = { + id: productTwo.id, + type: { + id: "new-type-id", + value: "new-type-value", + }, + } + + await service.upsert([updateData]) + + product = await service.retrieve(updateData.id, { + relations: ["type"], + }) + + expect(product).toEqual( expect.objectContaining({ - id: newTagData.id, - value: newTagData.value, - }), - ], - type: expect.objectContaining({ - id: productTypeTwo.id, - }), + id: productTwo.id, + type: expect.objectContaining({ + ...updateData.type, + }), + }) + ) }) - ) - }) - it("should remove relationships of a product", async () => { - const updateData = { - id: productTwo.id, - categories: [], - collection_id: null, - type_id: null, - tags: [], - } + it("should replace relationships of a product", async () => { + const newTagData = { + id: "tag-2", + value: "tag 2", + } - await module.upsert([updateData]) + const updateData = { + id: productTwo.id, + categories: [ + { + id: productCategoryTwo.id, + }, + ], + collection_id: productCollectionTwo.id, + type_id: productTypeTwo.id, + tags: [newTagData], + } - const product = await module.retrieve(updateData.id, { - relations: ["categories", "collection", "tags"], - }) + await service.upsert([updateData]) - expect(product).toEqual( - expect.objectContaining({ - id: productTwo.id, - categories: [], - tags: [], - collection: null, - type: null, - }) - ) - }) + const product = await service.retrieve(updateData.id, { + relations: ["categories", "collection", "tags", "type"], + }) - it("should throw an error when product ID does not exist", async () => { - let error - const updateData = { - id: "does-not-exist", - title: "test", - } - - try { - await module.upsert([updateData]) - } catch (e) { - error = e.message - } - - expect(error).toEqual(`Product with id "does-not-exist" not found`) - }) - - it("should update, create and delete variants", async () => { - const updateData = { - id: productTwo.id, - // Note: VariantThree is already assigned to productTwo, that should be deleted - variants: [ - { - id: variantTwo.id, - title: "updated-variant", - }, - { - title: "created-variant", - }, - ], - } - - await module.upsert([updateData]) - - const product = await module.retrieve(updateData.id, { - relations: ["variants"], - }) - - expect(product.variants).toHaveLength(2) - expect(product).toEqual( - expect.objectContaining({ - id: expect.any(String), - variants: expect.arrayContaining([ + expect(product).toEqual( expect.objectContaining({ - id: variantTwo.id, - title: "updated-variant", - }), - expect.objectContaining({ - id: expect.any(String), - title: "created-variant", - }), - ]), + id: productTwo.id, + categories: [ + expect.objectContaining({ + id: productCategoryTwo.id, + }), + ], + collection: expect.objectContaining({ + id: productCollectionTwo.id, + }), + tags: [ + expect.objectContaining({ + id: newTagData.id, + value: newTagData.value, + }), + ], + type: expect.objectContaining({ + id: productTypeTwo.id, + }), + }) + ) }) - ) - }) - it("should throw an error when variant with id does not exist", async () => { - let error + it("should remove relationships of a product", async () => { + const updateData = { + id: productTwo.id, + categories: [], + collection_id: null, + type_id: null, + tags: [], + } - const updateData = { - id: productTwo.id, - // Note: VariantThree is already assigned to productTwo, that should be deleted - variants: [ - { + await service.upsert([updateData]) + + const product = await service.retrieve(updateData.id, { + relations: ["categories", "collection", "tags"], + }) + + expect(product).toEqual( + expect.objectContaining({ + id: productTwo.id, + categories: [], + tags: [], + collection: null, + type: null, + }) + ) + }) + + it("should throw an error when product ID does not exist", async () => { + let error + const updateData = { id: "does-not-exist", - title: "updated-variant", - }, - { - title: "created-variant", - }, - ], - } + title: "test", + } - try { - await module.upsert([updateData]) - } catch (e) { - error = e - } + try { + await service.upsert([updateData]) + } catch (e) { + error = e.message + } - await module.retrieve(updateData.id, { - relations: ["variants"], - }) + expect(error).toEqual(`Product with id "does-not-exist" not found`) + }) - expect(error.message).toEqual( - `ProductVariant with id "does-not-exist" not found` - ) - }) - }) + it("should update, create and delete variants", async () => { + const updateData = { + id: productTwo.id, + // Note: VariantThree is already assigned to productTwo, that should be deleted + variants: [ + { + id: variantTwo.id, + title: "updated-variant", + }, + { + title: "created-variant", + }, + ], + } - describe("create", function () { - let module: IProductModuleService - let images = ["image-1"] - let eventBus + await service.upsert([updateData]) - beforeEach(async () => { - await beforeEach_() + const product = await service.retrieve(updateData.id, { + relations: ["variants"], + }) - MedusaModule.clearInstances() - - eventBus = new EventBusService() - module = await initialize( - { - database: { - clientUrl: DB_URL, - schema: process.env.MEDUSA_PRODUCT_DB_SCHEMA, - }, - }, - { - eventBusModuleService: eventBus, - } - ) - }) - - afterEach(afterEach_) - - it("should create a product", async () => { - const data = buildProductAndRelationsData({ - images, - thumbnail: images[0], - }) - - const productsCreated = await module.create([data]) - - const products = await module.list( - { id: productsCreated[0].id }, - { - relations: [ - "images", - "categories", - "variants", - "variants.options", - "options", - "options.values", - "tags", - "type", - ], - } - ) - - expect(products).toHaveLength(1) - expect(products[0].images).toHaveLength(1) - expect(products[0].options).toHaveLength(1) - expect(products[0].tags).toHaveLength(1) - expect(products[0].categories).toHaveLength(0) - expect(products[0].variants).toHaveLength(1) - - expect(products[0]).toEqual( - expect.objectContaining({ - id: expect.any(String), - title: data.title, - handle: kebabCase(data.title), - description: data.description, - subtitle: data.subtitle, - is_giftcard: data.is_giftcard, - discountable: data.discountable, - thumbnail: images[0], - status: data.status, - images: expect.arrayContaining([ + expect(product.variants).toHaveLength(2) + expect(product).toEqual( expect.objectContaining({ id: expect.any(String), - url: images[0], - }), - ]), - options: expect.arrayContaining([ - expect.objectContaining({ - id: expect.any(String), - title: data.options[0].title, - values: expect.arrayContaining([ + variants: expect.arrayContaining([ + expect.objectContaining({ + id: variantTwo.id, + title: "updated-variant", + }), expect.objectContaining({ id: expect.any(String), - value: data.variants[0].options?.[0].value, + title: "created-variant", }), ]), - }), - ]), - tags: expect.arrayContaining([ + }) + ) + }) + + it("should throw an error when variant with id does not exist", async () => { + let error + + const updateData = { + id: productTwo.id, + // Note: VariantThree is already assigned to productTwo, that should be deleted + variants: [ + { + id: "does-not-exist", + title: "updated-variant", + }, + { + title: "created-variant", + }, + ], + } + + try { + await service.upsert([updateData]) + } catch (e) { + error = e + } + + await service.retrieve(updateData.id, { + relations: ["variants"], + }) + + expect(error.message).toEqual( + `ProductVariant with id "does-not-exist" not found` + ) + }) + }) + + describe("create", function () { + let images = ["image-1"] + it("should create a product", async () => { + const data = buildProductAndRelationsData({ + images, + thumbnail: images[0], + }) + + const productsCreated = await service.create([data]) + + const products = await service.list( + { id: productsCreated[0].id }, + { + relations: [ + "images", + "categories", + "variants", + "variants.options", + "options", + "options.values", + "tags", + "type", + ], + } + ) + + expect(products).toHaveLength(1) + expect(products[0].images).toHaveLength(1) + expect(products[0].options).toHaveLength(1) + expect(products[0].tags).toHaveLength(1) + expect(products[0].categories).toHaveLength(0) + expect(products[0].variants).toHaveLength(1) + + expect(products[0]).toEqual( expect.objectContaining({ id: expect.any(String), - value: data.tags[0].value, - }), - ]), - type: expect.objectContaining({ - id: expect.any(String), - value: data.type.value, - }), - variants: expect.arrayContaining([ - expect.objectContaining({ - id: expect.any(String), - title: data.variants[0].title, - sku: data.variants[0].sku, - allow_backorder: false, - manage_inventory: true, - inventory_quantity: 100, - variant_rank: 0, + title: data.title, + handle: kebabCase(data.title), + description: data.description, + subtitle: data.subtitle, + is_giftcard: data.is_giftcard, + discountable: data.discountable, + thumbnail: images[0], + status: data.status, + images: expect.arrayContaining([ + expect.objectContaining({ + id: expect.any(String), + url: images[0], + }), + ]), options: expect.arrayContaining([ expect.objectContaining({ id: expect.any(String), - value: data.variants[0].options?.[0].value, + title: data.options[0].title, + values: expect.arrayContaining([ + expect.objectContaining({ + id: expect.any(String), + value: data.variants[0].options?.[0].value, + }), + ]), }), ]), - }), - ]), + tags: expect.arrayContaining([ + expect.objectContaining({ + id: expect.any(String), + value: data.tags[0].value, + }), + ]), + type: expect.objectContaining({ + id: expect.any(String), + value: data.type.value, + }), + variants: expect.arrayContaining([ + expect.objectContaining({ + id: expect.any(String), + title: data.variants[0].title, + sku: data.variants[0].sku, + allow_backorder: false, + manage_inventory: true, + inventory_quantity: 100, + variant_rank: 0, + options: expect.arrayContaining([ + expect.objectContaining({ + id: expect.any(String), + value: data.variants[0].options?.[0].value, + }), + ]), + }), + ]), + }) + ) }) - ) - }) - it("should emit events through eventBus", async () => { - const eventBusSpy = jest.spyOn(EventBusService.prototype, "emit") - const data = buildProductAndRelationsData({ - images, - thumbnail: images[0], + it("should emit events through eventBus", async () => { + const eventBusSpy = jest.spyOn(MockEventBusService.prototype, "emit") + const data = buildProductAndRelationsData({ + images, + thumbnail: images[0], + }) + + const products = await service.create([data]) + expect(eventBusSpy).toHaveBeenCalledTimes(1) + expect(eventBusSpy).toHaveBeenCalledWith([ + { + eventName: "product.created", + data: { id: products[0].id }, + }, + ]) + }) }) - const products = await module.create([data]) + describe("softDelete", function () { + let images = ["image-1"] + it("should soft delete a product and its cascaded relations", async () => { + const data = buildProductAndRelationsData({ + images, + thumbnail: images[0], + }) - expect(eventBusSpy).toHaveBeenCalledTimes(1) - expect(eventBusSpy).toHaveBeenCalledWith([ - { - eventName: "product.created", - data: { id: products[0].id }, - }, - ]) - }) - }) + const products = await service.create([data]) - describe("softDelete", function () { - let module: IProductModuleService - let images = ["image-1"] - let eventBus + await service.softDelete([products[0].id]) - beforeEach(async () => { - await beforeEach_() + const deletedProducts = await service.list( + { id: products[0].id }, + { + relations: [ + "variants", + "variants.options", + "options", + "options.values", + ], + withDeleted: true, + } + ) - MedusaModule.clearInstances() + expect(deletedProducts).toHaveLength(1) + expect(deletedProducts[0].deleted_at).not.toBeNull() - eventBus = new EventBusService() - module = await initialize( - { - database: { - clientUrl: DB_URL, - schema: process.env.MEDUSA_PRODUCT_DB_SCHEMA, - }, - }, - { - eventBusModuleService: eventBus, - } - ) - }) + for (const option of deletedProducts[0].options) { + expect(option.deleted_at).not.toBeNull() + } - afterEach(afterEach_) + const productOptionsValues = deletedProducts[0].options + .map((o) => o.values) + .flat() - it("should soft delete a product and its cascaded relations", async () => { - const data = buildProductAndRelationsData({ - images, - thumbnail: images[0], + for (const optionValue of productOptionsValues) { + expect(optionValue.deleted_at).not.toBeNull() + } + + for (const variant of deletedProducts[0].variants) { + expect(variant.deleted_at).not.toBeNull() + } + + const variantsOptions = deletedProducts[0].options + .map((o) => o.values) + .flat() + + for (const option of variantsOptions) { + expect(option.deleted_at).not.toBeNull() + } + }) + + it("should retrieve soft-deleted products if filtered on deleted_at", async () => { + const data = buildProductAndRelationsData({ + images, + thumbnail: images[0], + }) + + const products = await service.create([data]) + + await service.softDelete([products[0].id]) + + const softDeleted = await service.list({ + deleted_at: { $gt: "01-01-2022" }, + }) + + expect(softDeleted).toHaveLength(1) + }) + + it("should emit events through eventBus", async () => { + const eventBusSpy = jest.spyOn(MockEventBusService.prototype, "emit") + const data = buildProductAndRelationsData({ + images, + thumbnail: images[0], + }) + + const products = await service.create([data]) + + await service.softDelete([products[0].id]) + + expect(eventBusSpy).toHaveBeenCalledWith([ + { + eventName: "product.created", + data: { id: products[0].id }, + }, + ]) + }) }) - const products = await module.create([data]) + describe("restore", function () { + let images = ["image-1"] - await module.softDelete([products[0].id]) + it("should restore a soft deleted product and its cascaded relations", async () => { + const data = buildProductAndRelationsData({ + images, + thumbnail: images[0], + }) - const deletedProducts = await module.list( - { id: products[0].id }, - { - relations: [ - "variants", - "variants.options", - "options", - "options.values", - ], - withDeleted: true, - } - ) + const products = await service.create([data]) - expect(deletedProducts).toHaveLength(1) - expect(deletedProducts[0].deleted_at).not.toBeNull() + let retrievedProducts = await service.list({ id: products[0].id }) - for (const option of deletedProducts[0].options) { - expect(option.deleted_at).not.toBeNull() - } + expect(retrievedProducts).toHaveLength(1) + expect(retrievedProducts[0].deleted_at).toBeNull() - const productOptionsValues = deletedProducts[0].options - .map((o) => o.values) - .flat() + await service.softDelete([products[0].id]) - for (const optionValue of productOptionsValues) { - expect(optionValue.deleted_at).not.toBeNull() - } + retrievedProducts = await service.list( + { id: products[0].id }, + { + withDeleted: true, + } + ) - for (const variant of deletedProducts[0].variants) { - expect(variant.deleted_at).not.toBeNull() - } + expect(retrievedProducts).toHaveLength(1) + expect(retrievedProducts[0].deleted_at).not.toBeNull() - const variantsOptions = deletedProducts[0].options - .map((o) => o.values) - .flat() + await service.restore([products[0].id]) - for (const option of variantsOptions) { - expect(option.deleted_at).not.toBeNull() - } - }) + const deletedProducts = await service.list( + { id: products[0].id }, + { + relations: [ + "variants", + "variants.options", + "variants.options", + "options", + "options.values", + ], + withDeleted: true, + } + ) - it("should retrieve soft-deleted products if filtered on deleted_at", async () => { - const data = buildProductAndRelationsData({ - images, - thumbnail: images[0], + expect(deletedProducts).toHaveLength(1) + expect(deletedProducts[0].deleted_at).toBeNull() + + for (const option of deletedProducts[0].options) { + expect(option.deleted_at).toBeNull() + } + + const productOptionsValues = deletedProducts[0].options + .map((o) => o.values) + .flat() + + for (const optionValue of productOptionsValues) { + expect(optionValue.deleted_at).toBeNull() + } + + for (const variant of deletedProducts[0].variants) { + expect(variant.deleted_at).toBeNull() + } + + const variantsOptions = deletedProducts[0].options + .map((o) => o.values) + .flat() + + for (const option of variantsOptions) { + expect(option.deleted_at).toBeNull() + } + }) }) - const products = await module.create([data]) + describe("list", function () { + beforeEach(async () => { + const collections = await createCollections( + MikroOrmWrapper.forkManager(), + productCollectionsData + ) - await module.softDelete([products[0].id]) + productCollectionOne = collections[0] + productCollectionTwo = collections[1] - const softDeleted = await module.list({ - deleted_at: { $gt: "01-01-2022" }, - }) + const productOneData = buildProductAndRelationsData({ + collection_id: productCollectionOne.id, + }) - expect(softDeleted).toHaveLength(1) - }) + const productTwoData = buildProductAndRelationsData({ + collection_id: productCollectionTwo.id, + }) - it("should emit events through eventBus", async () => { - const eventBusSpy = jest.spyOn(EventBusService.prototype, "emit") - const data = buildProductAndRelationsData({ - images, - thumbnail: images[0], - }) + await service.create([productOneData, productTwoData]) + }) - const products = await module.create([data]) + it("should return a list of products scoped by collection id", async () => { + const productsWithCollectionOne = await service.list( + { collection_id: productCollectionOne.id }, + { + relations: ["collection"], + } + ) - await module.softDelete([products[0].id]) + expect(productsWithCollectionOne).toHaveLength(1) - expect(eventBusSpy).toHaveBeenCalledWith([ - { - eventName: "product.created", - data: { id: products[0].id }, - }, - ]) - }) - }) + expect([ + expect.objectContaining({ + collection: expect.objectContaining({ + id: productCollectionOne.id, + }), + }), + ]) + }) - describe("restore", function () { - let module: IProductModuleService - let images = ["image-1"] + it("should returns empty array when querying for a collection that doesnt exist", async () => { + const products = await service.list( + { + categories: { id: ["collection-doesnt-exist-id"] }, + }, + { + select: ["title", "collection.title"], + relations: ["collection"], + } + ) - beforeEach(async () => { - await beforeEach_() - - MedusaModule.clearInstances() - - module = await initialize({ - database: { - clientUrl: DB_URL, - schema: process.env.MEDUSA_PRODUCT_DB_SCHEMA, - }, + expect(products).toEqual([]) + }) }) }) - - afterEach(afterEach_) - - it("should restore a soft deleted product and its cascaded relations", async () => { - const data = buildProductAndRelationsData({ - images, - thumbnail: images[0], - }) - - const products = await module.create([data]) - - let retrievedProducts = await module.list({ id: products[0].id }) - - expect(retrievedProducts).toHaveLength(1) - expect(retrievedProducts[0].deleted_at).toBeNull() - - await module.softDelete([products[0].id]) - - retrievedProducts = await module.list( - { id: products[0].id }, - { - withDeleted: true, - } - ) - - expect(retrievedProducts).toHaveLength(1) - expect(retrievedProducts[0].deleted_at).not.toBeNull() - - await module.restore([products[0].id]) - - const deletedProducts = await module.list( - { id: products[0].id }, - { - relations: [ - "variants", - "variants.options", - "variants.options", - "options", - "options.values", - ], - withDeleted: true, - } - ) - - expect(deletedProducts).toHaveLength(1) - expect(deletedProducts[0].deleted_at).toBeNull() - - for (const option of deletedProducts[0].options) { - expect(option.deleted_at).toBeNull() - } - - const productOptionsValues = deletedProducts[0].options - .map((o) => o.values) - .flat() - - for (const optionValue of productOptionsValues) { - expect(optionValue.deleted_at).toBeNull() - } - - for (const variant of deletedProducts[0].variants) { - expect(variant.deleted_at).toBeNull() - } - - const variantsOptions = deletedProducts[0].options - .map((o) => o.values) - .flat() - - for (const option of variantsOptions) { - expect(option.deleted_at).toBeNull() - } - }) - }) - - describe("list", function () { - let module: IProductModuleService - let collections: ProductCollection - - beforeEach(async () => { - const testManager = await beforeEach_() - - const collections = await createCollections( - testManager, - productCollectionsData - ) - - productCollectionOne = collections[0] - productCollectionTwo = collections[1] - - MedusaModule.clearInstances() - - module = await initialize({ - database: { - clientUrl: DB_URL, - schema: process.env.MEDUSA_PRODUCT_DB_SCHEMA, - }, - }) - - const productOneData = buildProductAndRelationsData({ - collection_id: productCollectionOne.id, - }) - - const productTwoData = buildProductAndRelationsData({ - collection_id: productCollectionTwo.id, - }) - - await module.create([productOneData, productTwoData]) - }) - - afterEach(afterEach_) - - it("should return a list of products scoped by collection id", async () => { - const productsWithCollectionOne = await module.list( - { collection_id: productCollectionOne.id }, - { - relations: ["collection"], - } - ) - - expect(productsWithCollectionOne).toHaveLength(1) - - expect([ - expect.objectContaining({ - collection: expect.objectContaining({ - id: productCollectionOne.id, - }), - }), - ]) - }) - - it("should returns empty array when querying for a collection that doesnt exist", async () => { - const products = await module.list( - { - categories: { id: ["collection-doesnt-exist-id"] }, - }, - { - select: ["title", "collection.title"], - relations: ["collection"], - } - ) - - expect(products).toEqual([]) - }) - }) + }, }) diff --git a/packages/product/integration-tests/__tests__/services/product-option/index.ts b/packages/product/integration-tests/__tests__/services/product-option/index.ts index 6f7e05b14e..9601836f76 100644 --- a/packages/product/integration-tests/__tests__/services/product-option/index.ts +++ b/packages/product/integration-tests/__tests__/services/product-option/index.ts @@ -1,376 +1,370 @@ -import { SqlEntityManager } from "@mikro-orm/postgresql" - import { ProductOptionService } from "@services" import { Product } from "@models" -import { TestDatabase } from "../../../utils" import { createOptions } from "../../../__fixtures__/product" import { ProductTypes } from "@medusajs/types" -import { asValue } from "awilix" -import { createMedusaContainer } from "@medusajs/utils" -import ContainerLoader from "../../../../src/loaders/container" + +import { Modules } from "@medusajs/modules-sdk" +import { moduleIntegrationTestRunner, SuiteOptions } from "medusa-test-utils" +import { IProductModuleService } from "@medusajs/types" jest.setTimeout(30000) -describe("ProductOption Service", () => { - let service: ProductOptionService - let testManager: SqlEntityManager - let repositoryManager: SqlEntityManager - let productOne: Product - let productTwo: Product - let data!: Product[] +moduleIntegrationTestRunner({ + moduleName: Modules.PRODUCT, + testSuite: ({ + MikroOrmWrapper, + medusaApp, + }: SuiteOptions) => { + describe("ProductOption Service", () => { + let service: ProductOptionService - const productOneData = { - id: "product-1", - title: "product 1", - status: ProductTypes.ProductStatus.PUBLISHED, - } - - const productTwoData = { - id: "product-2", - title: "product 2", - status: ProductTypes.ProductStatus.PUBLISHED, - } - - beforeEach(async () => { - await TestDatabase.setupDatabase() - repositoryManager = await TestDatabase.forkManager() - - const container = createMedusaContainer() - container.register("manager", asValue(repositoryManager)) - - await ContainerLoader({ container }) - - service = container.resolve("productOptionService") - - testManager = await TestDatabase.forkManager() - productOne = testManager.create(Product, productOneData) - productTwo = testManager.create(Product, productTwoData) - - data = await createOptions(testManager, [ - { - id: "option-1", - title: "Option 1", - product: productOne, - }, - { - id: "option-2", - title: "Option 2", - product: productOne, - }, - ]) - }) - - afterEach(async () => { - await TestDatabase.clearDatabase() - }) - - describe("list", () => { - it("list product option", async () => { - const optionResults = await service.list() - - expect(optionResults).toEqual([ - expect.objectContaining({ - id: "option-1", - title: "Option 1", - }), - expect.objectContaining({ - id: "option-2", - title: "Option 2", - }), - ]) - }) - - it("list product option by id", async () => { - const optionResults = await service.list({ id: "option-2" }) - - expect(optionResults).toEqual([ - expect.objectContaining({ - id: "option-2", - title: "Option 2", - }), - ]) - }) - - it("list product option by title matching string", async () => { - const optionResults = await service.list({ title: "Option 1" }) - - expect(optionResults).toEqual([ - expect.objectContaining({ - id: "option-1", - title: "Option 1", - }), - ]) - }) - }) - - describe("listAndCount", () => { - it("should return product option and count", async () => { - const [optionResults, count] = await service.listAndCount() - - expect(count).toEqual(2) - expect(optionResults).toEqual([ - expect.objectContaining({ - id: "option-1", - title: "Option 1", - }), - expect.objectContaining({ - id: "option-2", - title: "Option 2", - }), - ]) - }) - - it("should return product option and count when filtered", async () => { - const [optionResults, count] = await service.listAndCount({ - id: "option-2", + beforeEach(() => { + service = medusaApp.modules["productService"].productOptionService_ }) - expect(count).toEqual(1) - expect(optionResults).toEqual([ - expect.objectContaining({ - id: "option-2", - }), - ]) - }) + let productOne: Product + let productTwo: Product - it("should return product option and count when using skip and take", async () => { - const [optionResults, count] = await service.listAndCount( - {}, - { skip: 1, take: 1 } - ) - - expect(count).toEqual(2) - expect(optionResults).toEqual([ - expect.objectContaining({ - id: "option-2", - }), - ]) - }) - - it("should return requested fields", async () => { - const [optionResults, count] = await service.listAndCount( - {}, - { - take: 1, - select: ["title"], - } - ) - - const serialized = JSON.parse(JSON.stringify(optionResults)) - - expect(count).toEqual(2) - expect(serialized).toEqual([ - expect.objectContaining({ - id: "option-1", - }), - ]) - }) - }) - - describe("retrieve", () => { - const optionId = "option-1" - const optionValue = "Option 1" - - it("should return option for the given id", async () => { - const option = await service.retrieve(optionId) - - expect(option).toEqual( - expect.objectContaining({ - id: optionId, - }) - ) - }) - - it("should throw an error when option with id does not exist", async () => { - let error - - try { - await service.retrieve("does-not-exist") - } catch (e) { - error = e + const productOneData = { + id: "product-1", + title: "product 1", + status: ProductTypes.ProductStatus.PUBLISHED, } - expect(error.message).toEqual( - "ProductOption with id: does-not-exist was not found" - ) - }) - - it("should throw an error when an id is not provided", async () => { - let error - - try { - await service.retrieve(undefined as unknown as string) - } catch (e) { - error = e + const productTwoData = { + id: "product-2", + title: "product 2", + status: ProductTypes.ProductStatus.PUBLISHED, } - expect(error.message).toEqual("productOption - id must be defined") - }) + beforeEach(async () => { + const testManager = MikroOrmWrapper.forkManager() + productOne = testManager.create(Product, productOneData) + productTwo = testManager.create(Product, productTwoData) - it("should return option based on config select param", async () => { - const option = await service.retrieve(optionId, { - select: ["id", "title"], - }) - - const serialized = JSON.parse(JSON.stringify(option)) - - expect(serialized).toEqual({ - id: optionId, - title: optionValue, - }) - }) - }) - - describe("delete", () => { - const optionId = "option-1" - - it("should delete the product option given an ID successfully", async () => { - await service.delete([optionId]) - - const options = await service.list({ - id: optionId, - }) - - expect(options).toHaveLength(0) - }) - }) - - describe("update", () => { - const optionId = "option-1" - - it("should update the title of the option successfully", async () => { - await service.update([ - { - id: optionId, - title: "UK", - }, - ]) - - const productOption = await service.retrieve(optionId) - - expect(productOption.title).toEqual("UK") - }) - - it("should update the relationship of the option successfully", async () => { - await service.update([ - { - id: optionId, - product_id: productTwo.id, - }, - ]) - - const productOption = await service.retrieve(optionId, { - relations: ["product"], - }) - - expect(productOption).toEqual( - expect.objectContaining({ - id: optionId, - product: expect.objectContaining({ - id: productTwo.id, - }), - }) - ) - }) - - it("should throw an error when an id does not exist", async () => { - let error - - try { - await service.update([ + await createOptions(testManager, [ { - id: "does-not-exist", - title: "UK", + id: "option-1", + title: "Option 1", + product: productOne, }, - ]) - } catch (e) { - error = e - } - - expect(error.message).toEqual( - 'ProductOption with id "does-not-exist" not found' - ) - }) - }) - - describe("create", () => { - it("should create a option successfully", async () => { - await service.create([ - { - title: "UK", - product: productOne, - }, - ]) - - const [productOption] = await service.list( - { - title: "UK", - }, - { - relations: ["product"], - } - ) - - expect(productOption).toEqual( - expect.objectContaining({ - title: "UK", - product: expect.objectContaining({ - id: productOne.id, - }), - }) - ) - }) - }) - - describe("upsert", function () { - it("should create an option and update another option successfully", async () => { - const productOption = ( - await service.create([ { - title: "UK", + id: "option-2", + title: "Option 2", product: productOne, }, ]) - )[0] + }) - const optionToUpdate = { - id: productOption.id, - title: "US", - } + describe("list", () => { + it("list product option", async () => { + const optionResults = await service.list() - const newOption = { - title: "US2", - product_id: productOne.id, - } + expect(optionResults).toEqual([ + expect.objectContaining({ + id: "option-1", + title: "Option 1", + }), + expect.objectContaining({ + id: "option-2", + title: "Option 2", + }), + ]) + }) - await service.upsert([optionToUpdate, newOption]) + it("list product option by id", async () => { + const optionResults = await service.list({ id: "option-2" }) - const productOptions = await service.list( - { - title: "US%", - }, - { - relations: ["product"], - } - ) + expect(optionResults).toEqual([ + expect.objectContaining({ + id: "option-2", + title: "Option 2", + }), + ]) + }) - expect(JSON.parse(JSON.stringify(productOptions))).toEqual( - expect.arrayContaining([ - expect.objectContaining({ + it("list product option by title matching string", async () => { + const optionResults = await service.list({ title: "Option 1" }) + + expect(optionResults).toEqual([ + expect.objectContaining({ + id: "option-1", + title: "Option 1", + }), + ]) + }) + }) + + describe("listAndCount", () => { + it("should return product option and count", async () => { + const [optionResults, count] = await service.listAndCount() + + expect(count).toEqual(2) + expect(optionResults).toEqual([ + expect.objectContaining({ + id: "option-1", + title: "Option 1", + }), + expect.objectContaining({ + id: "option-2", + title: "Option 2", + }), + ]) + }) + + it("should return product option and count when filtered", async () => { + const [optionResults, count] = await service.listAndCount({ + id: "option-2", + }) + + expect(count).toEqual(1) + expect(optionResults).toEqual([ + expect.objectContaining({ + id: "option-2", + }), + ]) + }) + + it("should return product option and count when using skip and take", async () => { + const [optionResults, count] = await service.listAndCount( + {}, + { skip: 1, take: 1 } + ) + + expect(count).toEqual(2) + expect(optionResults).toEqual([ + expect.objectContaining({ + id: "option-2", + }), + ]) + }) + + it("should return requested fields", async () => { + const [optionResults, count] = await service.listAndCount( + {}, + { + take: 1, + select: ["title"], + } + ) + + const serialized = JSON.parse(JSON.stringify(optionResults)) + + expect(count).toEqual(2) + expect(serialized).toEqual([ + expect.objectContaining({ + id: "option-1", + }), + ]) + }) + }) + + describe("retrieve", () => { + const optionId = "option-1" + const optionValue = "Option 1" + + it("should return option for the given id", async () => { + const option = await service.retrieve(optionId) + + expect(option).toEqual( + expect.objectContaining({ + id: optionId, + }) + ) + }) + + it("should throw an error when option with id does not exist", async () => { + let error + + try { + await service.retrieve("does-not-exist") + } catch (e) { + error = e + } + + expect(error.message).toEqual( + "ProductOption with id: does-not-exist was not found" + ) + }) + + it("should throw an error when an id is not provided", async () => { + let error + + try { + await service.retrieve(undefined as unknown as string) + } catch (e) { + error = e + } + + expect(error.message).toEqual("productOption - id must be defined") + }) + + it("should return option based on config select param", async () => { + const option = await service.retrieve(optionId, { + select: ["id", "title"], + }) + + const serialized = JSON.parse(JSON.stringify(option)) + + expect(serialized).toEqual({ + id: optionId, + title: optionValue, + }) + }) + }) + + describe("delete", () => { + const optionId = "option-1" + + it("should delete the product option given an ID successfully", async () => { + await service.delete([optionId]) + + const options = await service.list({ + id: optionId, + }) + + expect(options).toHaveLength(0) + }) + }) + + describe("update", () => { + const optionId = "option-1" + + it("should update the title of the option successfully", async () => { + await service.update([ + { + id: optionId, + title: "UK", + }, + ]) + + const productOption = await service.retrieve(optionId) + + expect(productOption.title).toEqual("UK") + }) + + it("should update the relationship of the option successfully", async () => { + await service.update([ + { + id: optionId, + product_id: productTwo.id, + }, + ]) + + const productOption = await service.retrieve(optionId, { + relations: ["product"], + }) + + expect(productOption).toEqual( + expect.objectContaining({ + id: optionId, + product: expect.objectContaining({ + id: productTwo.id, + }), + }) + ) + }) + + it("should throw an error when an id does not exist", async () => { + let error + + try { + await service.update([ + { + id: "does-not-exist", + title: "UK", + }, + ]) + } catch (e) { + error = e + } + + expect(error.message).toEqual( + 'ProductOption with id "does-not-exist" not found' + ) + }) + }) + + describe("create", () => { + it("should create a option successfully", async () => { + await service.create([ + { + title: "UK", + product: productOne, + }, + ]) + + const [productOption] = await service.list( + { + title: "UK", + }, + { + relations: ["product"], + } + ) + + expect(productOption).toEqual( + expect.objectContaining({ + title: "UK", + product: expect.objectContaining({ + id: productOne.id, + }), + }) + ) + }) + }) + + describe("upsert", function () { + it("should create an option and update another option successfully", async () => { + const productOption = ( + await service.create([ + { + title: "UK", + product: productOne, + }, + ]) + )[0] + + const optionToUpdate = { + id: productOption.id, title: "US", - product: expect.objectContaining({ - id: productOne.id, - }), - }), - expect.objectContaining({ - title: newOption.title, - product: expect.objectContaining({ - id: productOne.id, - }), - }), - ]) - ) + } + + const newOption = { + title: "US2", + product_id: productOne.id, + } + + await service.upsert([optionToUpdate, newOption]) + + const productOptions = await service.list( + { + title: "US%", + }, + { + relations: ["product"], + } + ) + + expect(JSON.parse(JSON.stringify(productOptions))).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + title: "US", + product: expect.objectContaining({ + id: productOne.id, + }), + }), + expect.objectContaining({ + title: newOption.title, + product: expect.objectContaining({ + id: productOne.id, + }), + }), + ]) + ) + }) + }) }) - }) + }, }) diff --git a/packages/product/integration-tests/__tests__/services/product-tag/index.ts b/packages/product/integration-tests/__tests__/services/product-tag/index.ts index 5d31d25ae1..8d2193d202 100644 --- a/packages/product/integration-tests/__tests__/services/product-tag/index.ts +++ b/packages/product/integration-tests/__tests__/services/product-tag/index.ts @@ -1,341 +1,335 @@ -import { SqlEntityManager } from "@mikro-orm/postgresql" - import { Product } from "@models" import { ProductTagService } from "@services" import { ProductTypes } from "@medusajs/types" -import { createMedusaContainer } from "@medusajs/utils" -import { asValue } from "awilix" -import ContainerLoader from "../../../../src/loaders/container" import { createProductAndTags } from "../../../__fixtures__/product" -import { TestDatabase } from "../../../utils" +import { Modules } from "@medusajs/modules-sdk" +import { moduleIntegrationTestRunner, SuiteOptions } from "medusa-test-utils" +import { IProductModuleService } from "@medusajs/types" jest.setTimeout(30000) -describe("ProductTag Service", () => { - let service: ProductTagService - let testManager: SqlEntityManager - let repositoryManager: SqlEntityManager - let data!: Product[] +moduleIntegrationTestRunner({ + moduleName: Modules.PRODUCT, + testSuite: ({ + MikroOrmWrapper, + medusaApp, + }: SuiteOptions) => { + describe("ProductTag Service", () => { + let data!: Product[] + let service: ProductTagService - const productsData = [ - { - id: "test-1", - title: "product 1", - status: ProductTypes.ProductStatus.PUBLISHED, - tags: [ - { - id: "tag-1", - value: "France", - }, - ], - }, - { - id: "test-2", - title: "product", - status: ProductTypes.ProductStatus.PUBLISHED, - tags: [ - { - id: "tag-2", - value: "Germany", - }, - { - id: "tag-3", - value: "United States", - }, - { - id: "tag-4", - value: "United Kingdom", - }, - ], - }, - ] - - beforeEach(async () => { - await TestDatabase.setupDatabase() - repositoryManager = await TestDatabase.forkManager() - - const container = createMedusaContainer() - container.register("manager", asValue(repositoryManager)) - - await ContainerLoader({ container }) - - service = container.resolve("productTagService") - - testManager = await TestDatabase.forkManager() - - data = await createProductAndTags(testManager, productsData) - }) - - afterEach(async () => { - await TestDatabase.clearDatabase() - }) - - describe("list", () => { - it("list product tags", async () => { - const tagsResults = await service.list() - - expect(tagsResults).toEqual([ - expect.objectContaining({ - id: "tag-1", - value: "France", - }), - expect.objectContaining({ - id: "tag-2", - value: "Germany", - }), - expect.objectContaining({ - id: "tag-3", - value: "United States", - }), - expect.objectContaining({ - id: "tag-4", - value: "United Kingdom", - }), - ]) - }) - - it("list product tags by id", async () => { - const tagsResults = await service.list({ id: data[0].tags![0].id }) - - expect(tagsResults).toEqual([ - expect.objectContaining({ - id: "tag-1", - value: "France", - }), - ]) - }) - - it("list product tags by value matching string", async () => { - const tagsResults = await service.list({ value: "united kingdom" }) - - expect(tagsResults).toEqual([ - expect.objectContaining({ - id: "tag-4", - value: "United Kingdom", - }), - ]) - }) - }) - - describe("listAndCount", () => { - it("should return product tags and count", async () => { - const [tagsResults, count] = await service.listAndCount() - - expect(count).toEqual(4) - expect(tagsResults).toEqual([ - expect.objectContaining({ - id: "tag-1", - value: "France", - }), - expect.objectContaining({ - id: "tag-2", - value: "Germany", - }), - expect.objectContaining({ - id: "tag-3", - value: "United States", - }), - expect.objectContaining({ - id: "tag-4", - value: "United Kingdom", - }), - ]) - }) - - it("should return product tags and count when filtered", async () => { - const [tagsResults, count] = await service.listAndCount({ - id: data[0].tags![0].id, + beforeEach(() => { + service = medusaApp.modules["productService"].productTagService_ }) - expect(count).toEqual(1) - expect(tagsResults).toEqual([ - expect.objectContaining({ - id: "tag-1", - }), - ]) - }) - - it("should return product tags and count when using skip and take", async () => { - const [tagsResults, count] = await service.listAndCount( - {}, - { skip: 1, take: 2 } - ) - - expect(count).toEqual(4) - expect(tagsResults).toEqual([ - expect.objectContaining({ - id: "tag-2", - }), - expect.objectContaining({ - id: "tag-3", - }), - ]) - }) - - it("should return requested fields and relations", async () => { - const [tagsResults, count] = await service.listAndCount( - {}, + const productsData = [ { - take: 1, - select: ["value", "products.id"], - relations: ["products"], - } - ) - - const serialized = JSON.parse(JSON.stringify(tagsResults)) - - expect(count).toEqual(4) - expect(serialized).toEqual([ - expect.objectContaining({ - id: "tag-1", - products: [ + id: "test-1", + title: "product 1", + status: ProductTypes.ProductStatus.PUBLISHED, + tags: [ { - id: "test-1", + id: "tag-1", + value: "France", }, ], - }), - ]) - }) - }) + }, + { + id: "test-2", + title: "product", + status: ProductTypes.ProductStatus.PUBLISHED, + tags: [ + { + id: "tag-2", + value: "Germany", + }, + { + id: "tag-3", + value: "United States", + }, + { + id: "tag-4", + value: "United Kingdom", + }, + ], + }, + ] - describe("retrieve", () => { - const tagId = "tag-1" - const tagValue = "France" - const productId = "test-1" + beforeEach(async () => { + data = await createProductAndTags( + MikroOrmWrapper.forkManager(), + productsData + ) + }) - it("should return tag for the given id", async () => { - const tag = await service.retrieve(tagId) + describe("list", () => { + it("list product tags", async () => { + const tagsResults = await service.list() - expect(tag).toEqual( - expect.objectContaining({ - id: tagId, + expect(tagsResults).toEqual([ + expect.objectContaining({ + id: "tag-1", + value: "France", + }), + expect.objectContaining({ + id: "tag-2", + value: "Germany", + }), + expect.objectContaining({ + id: "tag-3", + value: "United States", + }), + expect.objectContaining({ + id: "tag-4", + value: "United Kingdom", + }), + ]) }) - ) - }) - it("should throw an error when tag with id does not exist", async () => { - let error + it("list product tags by id", async () => { + const tagsResults = await service.list({ id: data[0].tags![0].id }) - try { - await service.retrieve("does-not-exist") - } catch (e) { - error = e - } + expect(tagsResults).toEqual([ + expect.objectContaining({ + id: "tag-1", + value: "France", + }), + ]) + }) - expect(error.message).toEqual( - "ProductTag with id: does-not-exist was not found" - ) - }) + it("list product tags by value matching string", async () => { + const tagsResults = await service.list({ value: "united kingdom" }) - it("should throw an error when an id is not provided", async () => { - let error - - try { - await service.retrieve(undefined as unknown as string) - } catch (e) { - error = e - } - - expect(error.message).toEqual("productTag - id must be defined") - }) - - it("should return tag based on config select param", async () => { - const tag = await service.retrieve(tagId, { - select: ["id", "value"], + expect(tagsResults).toEqual([ + expect.objectContaining({ + id: "tag-4", + value: "United Kingdom", + }), + ]) + }) }) - const serialized = JSON.parse(JSON.stringify(tag)) + describe("listAndCount", () => { + it("should return product tags and count", async () => { + const [tagsResults, count] = await service.listAndCount() - expect(serialized).toEqual({ - id: tagId, - value: tagValue, - }) - }) + expect(count).toEqual(4) + expect(tagsResults).toEqual([ + expect.objectContaining({ + id: "tag-1", + value: "France", + }), + expect.objectContaining({ + id: "tag-2", + value: "Germany", + }), + expect.objectContaining({ + id: "tag-3", + value: "United States", + }), + expect.objectContaining({ + id: "tag-4", + value: "United Kingdom", + }), + ]) + }) - it("should return tag based on config relation param", async () => { - const tag = await service.retrieve(tagId, { - select: ["id", "value", "products.id"], - relations: ["products"], + it("should return product tags and count when filtered", async () => { + const [tagsResults, count] = await service.listAndCount({ + id: data[0].tags![0].id, + }) + + expect(count).toEqual(1) + expect(tagsResults).toEqual([ + expect.objectContaining({ + id: "tag-1", + }), + ]) + }) + + it("should return product tags and count when using skip and take", async () => { + const [tagsResults, count] = await service.listAndCount( + {}, + { skip: 1, take: 2 } + ) + + expect(count).toEqual(4) + expect(tagsResults).toEqual([ + expect.objectContaining({ + id: "tag-2", + }), + expect.objectContaining({ + id: "tag-3", + }), + ]) + }) + + it("should return requested fields and relations", async () => { + const [tagsResults, count] = await service.listAndCount( + {}, + { + take: 1, + select: ["value", "products.id"], + relations: ["products"], + } + ) + + const serialized = JSON.parse(JSON.stringify(tagsResults)) + + expect(count).toEqual(4) + expect(serialized).toEqual([ + expect.objectContaining({ + id: "tag-1", + products: [ + { + id: "test-1", + }, + ], + }), + ]) + }) }) - const serialized = JSON.parse(JSON.stringify(tag)) + describe("retrieve", () => { + const tagId = "tag-1" + const tagValue = "France" + const productId = "test-1" - expect(serialized).toEqual({ - id: tagId, - value: tagValue, - products: [ - expect.objectContaining({ - id: productId, - }), - ], - }) - }) - }) + it("should return tag for the given id", async () => { + const tag = await service.retrieve(tagId) - describe("delete", () => { - const tagId = "tag-1" + expect(tag).toEqual( + expect.objectContaining({ + id: tagId, + }) + ) + }) - it("should delete the product tag given an ID successfully", async () => { - await service.delete([tagId]) + it("should throw an error when tag with id does not exist", async () => { + let error - const tags = await service.list({ - id: tagId, + try { + await service.retrieve("does-not-exist") + } catch (e) { + error = e + } + + expect(error.message).toEqual( + "ProductTag with id: does-not-exist was not found" + ) + }) + + it("should throw an error when an id is not provided", async () => { + let error + + try { + await service.retrieve(undefined as unknown as string) + } catch (e) { + error = e + } + + expect(error.message).toEqual("productTag - id must be defined") + }) + + it("should return tag based on config select param", async () => { + const tag = await service.retrieve(tagId, { + select: ["id", "value"], + }) + + const serialized = JSON.parse(JSON.stringify(tag)) + + expect(serialized).toEqual({ + id: tagId, + value: tagValue, + }) + }) + + it("should return tag based on config relation param", async () => { + const tag = await service.retrieve(tagId, { + select: ["id", "value", "products.id"], + relations: ["products"], + }) + + const serialized = JSON.parse(JSON.stringify(tag)) + + expect(serialized).toEqual({ + id: tagId, + value: tagValue, + products: [ + expect.objectContaining({ + id: productId, + }), + ], + }) + }) }) - expect(tags).toHaveLength(0) - }) - }) + describe("delete", () => { + const tagId = "tag-1" - describe("update", () => { - const tagId = "tag-1" + it("should delete the product tag given an ID successfully", async () => { + await service.delete([tagId]) - it("should update the value of the tag successfully", async () => { - await service.update([ - { - id: tagId, - value: "UK", - }, - ]) + const tags = await service.list({ + id: tagId, + }) - const productTag = await service.retrieve(tagId) + expect(tags).toHaveLength(0) + }) + }) - expect(productTag.value).toEqual("UK") - }) + describe("update", () => { + const tagId = "tag-1" - it("should throw an error when an id does not exist", async () => { - let error + it("should update the value of the tag successfully", async () => { + await service.update([ + { + id: tagId, + value: "UK", + }, + ]) - try { - await service.update([ - { - id: "does-not-exist", + const productTag = await service.retrieve(tagId) + + expect(productTag.value).toEqual("UK") + }) + + it("should throw an error when an id does not exist", async () => { + let error + + try { + await service.update([ + { + id: "does-not-exist", + value: "UK", + }, + ]) + } catch (e) { + error = e + } + + expect(error.message).toEqual( + 'ProductTag with id "does-not-exist" not found' + ) + }) + }) + + describe("create", () => { + it("should create a tag successfully", async () => { + await service.create([ + { + value: "UK", + }, + ]) + + const [productTag] = await service.list({ value: "UK", - }, - ]) - } catch (e) { - error = e - } + }) - expect(error.message).toEqual( - 'ProductTag with id "does-not-exist" not found' - ) - }) - }) - - describe("create", () => { - it("should create a tag successfully", async () => { - await service.create([ - { - value: "UK", - }, - ]) - - const [productTag] = await service.list({ - value: "UK", + expect(productTag.value).toEqual("UK") + }) }) - - expect(productTag.value).toEqual("UK") }) - }) + }, }) diff --git a/packages/product/integration-tests/__tests__/services/product-type/index.ts b/packages/product/integration-tests/__tests__/services/product-type/index.ts index da07bc92fb..190311f50b 100644 --- a/packages/product/integration-tests/__tests__/services/product-type/index.ts +++ b/packages/product/integration-tests/__tests__/services/product-type/index.ts @@ -1,284 +1,277 @@ -import { SqlEntityManager } from "@mikro-orm/postgresql" - -import ContainerLoader from "../../../../src/loaders/container" import { ProductTypeService } from "@services" import { Product } from "@models" -import { TestDatabase } from "../../../utils" import { createProductAndTypes } from "../../../__fixtures__/product" import { ProductTypes } from "@medusajs/types" -import { createMedusaContainer } from "@medusajs/utils" -import { asValue } from "awilix" +import { Modules } from "@medusajs/modules-sdk" +import { moduleIntegrationTestRunner, SuiteOptions } from "medusa-test-utils" +import { IProductModuleService } from "@medusajs/types" jest.setTimeout(30000) -describe("ProductType Service", () => { - let service: ProductTypeService - let testManager: SqlEntityManager - let repositoryManager: SqlEntityManager - let data!: Product[] +moduleIntegrationTestRunner({ + moduleName: Modules.PRODUCT, + testSuite: ({ + MikroOrmWrapper, + medusaApp, + }: SuiteOptions) => { + describe("ProductType Service", () => { + let data!: Product[] + let service: ProductTypeService - const productsData = [ - { - id: "product-1", - title: "product 1", - status: ProductTypes.ProductStatus.PUBLISHED, - type: { - id: "type-1", - value: "Type 1", - }, - }, - { - id: "product-2", - title: "product", - status: ProductTypes.ProductStatus.PUBLISHED, - type: { - id: "type-2", - value: "Type 2", - }, - }, - ] - - beforeEach(async () => { - await TestDatabase.setupDatabase() - repositoryManager = await TestDatabase.forkManager() - - const container = createMedusaContainer() - container.register("manager", asValue(repositoryManager)) - - await ContainerLoader({ container }) - - service = container.resolve("productTypeService") - - testManager = await TestDatabase.forkManager() - - data = await createProductAndTypes(testManager, productsData) - }) - - afterEach(async () => { - await TestDatabase.clearDatabase() - }) - - describe("list", () => { - it("list product type", async () => { - const typeResults = await service.list() - - expect(typeResults).toEqual([ - expect.objectContaining({ - id: "type-1", - value: "Type 1", - }), - expect.objectContaining({ - id: "type-2", - value: "Type 2", - }), - ]) - }) - - it("list product type by id", async () => { - const typeResults = await service.list({ id: data[0].type.id }) - - expect(typeResults).toEqual([ - expect.objectContaining({ - id: "type-1", - value: "Type 1", - }), - ]) - }) - - it("list product type by value matching string", async () => { - const typeResults = await service.list({ value: "Type 1" }) - - expect(typeResults).toEqual([ - expect.objectContaining({ - id: "type-1", - value: "Type 1", - }), - ]) - }) - }) - - describe("listAndCount", () => { - it("should return product type and count", async () => { - const [typeResults, count] = await service.listAndCount() - - expect(count).toEqual(2) - expect(typeResults).toEqual([ - expect.objectContaining({ - id: "type-1", - value: "Type 1", - }), - expect.objectContaining({ - id: "type-2", - value: "Type 2", - }), - ]) - }) - - it("should return product type and count when filtered", async () => { - const [typeResults, count] = await service.listAndCount({ - id: data[0].type.id, + beforeEach(() => { + service = medusaApp.modules["productService"].productTypeService_ }) - expect(count).toEqual(1) - expect(typeResults).toEqual([ - expect.objectContaining({ - id: "type-1", - }), - ]) - }) - - it("should return product type and count when using skip and take", async () => { - const [typeResults, count] = await service.listAndCount( - {}, - { skip: 1, take: 1 } - ) - - expect(count).toEqual(2) - expect(typeResults).toEqual([ - expect.objectContaining({ - id: "type-2", - }), - ]) - }) - - it("should return requested fields", async () => { - const [typeResults, count] = await service.listAndCount( - {}, + const productsData = [ { - take: 1, - select: ["value"], - } - ) - - const serialized = JSON.parse(JSON.stringify(typeResults)) - - expect(count).toEqual(2) - expect(serialized).toEqual([ - expect.objectContaining({ - id: "type-1", - }), - ]) - }) - }) - - describe("retrieve", () => { - const typeId = "type-1" - const typeValue = "Type 1" - - it("should return type for the given id", async () => { - const type = await service.retrieve(typeId) - - expect(type).toEqual( - expect.objectContaining({ - id: typeId, - }) - ) - }) - - it("should throw an error when type with id does not exist", async () => { - let error - - try { - await service.retrieve("does-not-exist") - } catch (e) { - error = e - } - - expect(error.message).toEqual( - "ProductType with id: does-not-exist was not found" - ) - }) - - it("should throw an error when an id is not provided", async () => { - let error - - try { - await service.retrieve(undefined as unknown as string) - } catch (e) { - error = e - } - - expect(error.message).toEqual("productType - id must be defined") - }) - - it("should return type based on config select param", async () => { - const type = await service.retrieve(typeId, { - select: ["id", "value"], - }) - - const serialized = JSON.parse(JSON.stringify(type)) - - expect(serialized).toEqual({ - id: typeId, - value: typeValue, - }) - }) - }) - - describe("delete", () => { - const typeId = "type-1" - - it("should delete the product type given an ID successfully", async () => { - await service.delete([typeId]) - - const types = await service.list({ - id: typeId, - }) - - expect(types).toHaveLength(0) - }) - }) - - describe("update", () => { - const typeId = "type-1" - - it("should update the value of the type successfully", async () => { - await service.update([ - { - id: typeId, - value: "UK", - }, - ]) - - const productType = await service.retrieve(typeId) - - expect(productType.value).toEqual("UK") - }) - - it("should throw an error when an id does not exist", async () => { - let error - - try { - await service.update([ - { - id: "does-not-exist", - value: "UK", + id: "product-1", + title: "product 1", + status: ProductTypes.ProductStatus.PUBLISHED, + type: { + id: "type-1", + value: "Type 1", }, - ]) - } catch (e) { - error = e - } - - expect(error.message).toEqual( - 'ProductType with id "does-not-exist" not found' - ) - }) - }) - - describe("create", () => { - it("should create a type successfully", async () => { - await service.create([ - { - value: "UK", }, - ]) + { + id: "product-2", + title: "product", + status: ProductTypes.ProductStatus.PUBLISHED, + type: { + id: "type-2", + value: "Type 2", + }, + }, + ] - const [productType] = await service.list({ - value: "UK", + beforeEach(async () => { + data = await createProductAndTypes( + MikroOrmWrapper.forkManager(), + productsData + ) + }) + describe("list", () => { + it("list product type", async () => { + const typeResults = await service.list() + + expect(typeResults).toEqual([ + expect.objectContaining({ + id: "type-1", + value: "Type 1", + }), + expect.objectContaining({ + id: "type-2", + value: "Type 2", + }), + ]) + }) + + it("list product type by id", async () => { + const typeResults = await service.list({ id: data[0].type.id }) + + expect(typeResults).toEqual([ + expect.objectContaining({ + id: "type-1", + value: "Type 1", + }), + ]) + }) + + it("list product type by value matching string", async () => { + const typeResults = await service.list({ value: "Type 1" }) + + expect(typeResults).toEqual([ + expect.objectContaining({ + id: "type-1", + value: "Type 1", + }), + ]) + }) }) - expect(productType.value).toEqual("UK") + describe("listAndCount", () => { + it("should return product type and count", async () => { + const [typeResults, count] = await service.listAndCount() + + expect(count).toEqual(2) + expect(typeResults).toEqual([ + expect.objectContaining({ + id: "type-1", + value: "Type 1", + }), + expect.objectContaining({ + id: "type-2", + value: "Type 2", + }), + ]) + }) + + it("should return product type and count when filtered", async () => { + const [typeResults, count] = await service.listAndCount({ + id: data[0].type.id, + }) + + expect(count).toEqual(1) + expect(typeResults).toEqual([ + expect.objectContaining({ + id: "type-1", + }), + ]) + }) + + it("should return product type and count when using skip and take", async () => { + const [typeResults, count] = await service.listAndCount( + {}, + { skip: 1, take: 1 } + ) + + expect(count).toEqual(2) + expect(typeResults).toEqual([ + expect.objectContaining({ + id: "type-2", + }), + ]) + }) + + it("should return requested fields", async () => { + const [typeResults, count] = await service.listAndCount( + {}, + { + take: 1, + select: ["value"], + } + ) + + const serialized = JSON.parse(JSON.stringify(typeResults)) + + expect(count).toEqual(2) + expect(serialized).toEqual([ + expect.objectContaining({ + id: "type-1", + }), + ]) + }) + }) + + describe("retrieve", () => { + const typeId = "type-1" + const typeValue = "Type 1" + + it("should return type for the given id", async () => { + const type = await service.retrieve(typeId) + + expect(type).toEqual( + expect.objectContaining({ + id: typeId, + }) + ) + }) + + it("should throw an error when type with id does not exist", async () => { + let error + + try { + await service.retrieve("does-not-exist") + } catch (e) { + error = e + } + + expect(error.message).toEqual( + "ProductType with id: does-not-exist was not found" + ) + }) + + it("should throw an error when an id is not provided", async () => { + let error + + try { + await service.retrieve(undefined as unknown as string) + } catch (e) { + error = e + } + + expect(error.message).toEqual("productType - id must be defined") + }) + + it("should return type based on config select param", async () => { + const type = await service.retrieve(typeId, { + select: ["id", "value"], + }) + + const serialized = JSON.parse(JSON.stringify(type)) + + expect(serialized).toEqual({ + id: typeId, + value: typeValue, + }) + }) + }) + + describe("delete", () => { + const typeId = "type-1" + + it("should delete the product type given an ID successfully", async () => { + await service.delete([typeId]) + + const types = await service.list({ + id: typeId, + }) + + expect(types).toHaveLength(0) + }) + }) + + describe("update", () => { + const typeId = "type-1" + + it("should update the value of the type successfully", async () => { + await service.update([ + { + id: typeId, + value: "UK", + }, + ]) + + const productType = await service.retrieve(typeId) + + expect(productType.value).toEqual("UK") + }) + + it("should throw an error when an id does not exist", async () => { + let error + + try { + await service.update([ + { + id: "does-not-exist", + value: "UK", + }, + ]) + } catch (e) { + error = e + } + + expect(error.message).toEqual( + 'ProductType with id "does-not-exist" not found' + ) + }) + }) + + describe("create", () => { + it("should create a type successfully", async () => { + await service.create([ + { + value: "UK", + }, + ]) + + const [productType] = await service.list({ + value: "UK", + }) + + expect(productType.value).toEqual("UK") + }) + }) }) - }) + }, }) diff --git a/packages/product/integration-tests/__tests__/services/product-variant/index.ts b/packages/product/integration-tests/__tests__/services/product-variant/index.ts index 992133f98f..5a5f0609b5 100644 --- a/packages/product/integration-tests/__tests__/services/product-variant/index.ts +++ b/packages/product/integration-tests/__tests__/services/product-variant/index.ts @@ -1,12 +1,8 @@ import { ProductOption } from "@medusajs/client-types" import { ProductTypes } from "@medusajs/types" -import { createMedusaContainer } from "@medusajs/utils" import { Collection } from "@mikro-orm/core" -import { SqlEntityManager } from "@mikro-orm/postgresql" import { Product, ProductTag, ProductVariant } from "@models" import { ProductVariantService } from "@services" -import { asValue } from "awilix" -import ContainerLoader from "../../../../src/loaders/container" import { createOptions, createProductAndTags, @@ -14,313 +10,319 @@ import { } from "../../../__fixtures__/product" import { productsData, variantsData } from "../../../__fixtures__/product/data" import { buildProductVariantOnlyData } from "../../../__fixtures__/variant/data/create-variant" -import { TestDatabase } from "../../../utils" -// TODO: fix tests -describe.skip("ProductVariant Service", () => { - let service: ProductVariantService - let testManager: SqlEntityManager - let repositoryManager: SqlEntityManager - let variantOne: ProductVariant - let variantTwo: ProductVariant - let productOne: Product - const productVariantTestOne = "test-1" +import { Modules } from "@medusajs/modules-sdk" +import { moduleIntegrationTestRunner, SuiteOptions } from "medusa-test-utils" +import { IProductModuleService } from "@medusajs/types" - beforeEach(async () => { - await TestDatabase.setupDatabase() - repositoryManager = await TestDatabase.forkManager() +jest.setTimeout(30000) - const container = createMedusaContainer() - container.register("manager", asValue(repositoryManager)) +moduleIntegrationTestRunner({ + moduleName: Modules.PRODUCT, + testSuite: ({ + MikroOrmWrapper, + medusaApp, + }: SuiteOptions) => { + describe.skip("ProductVariant Service", () => { + let variantOne: ProductVariant + let variantTwo: ProductVariant + let productOne: Product + const productVariantTestOne = "test-1" + let service: ProductVariantService - await ContainerLoader({ container }) - - service = container.resolve("productVariantService") - }) - - afterEach(async () => { - await TestDatabase.clearDatabase() - }) - - describe("list", () => { - beforeEach(async () => { - testManager = await TestDatabase.forkManager() - - productOne = testManager.create(Product, { - id: "product-1", - title: "product 1", - status: ProductTypes.ProductStatus.PUBLISHED, + beforeEach(() => { + service = medusaApp.modules["productService"].productVariantService_ }) - variantOne = testManager.create(ProductVariant, { - id: productVariantTestOne, - title: "variant 1", - inventory_quantity: 10, - product: productOne, - }) + describe("list", () => { + beforeEach(async () => { + const testManager = await MikroOrmWrapper.forkManager() - variantTwo = testManager.create(ProductVariant, { - id: "test-2", - title: "variant", - inventory_quantity: 10, - product: productOne, - }) - - await testManager.persistAndFlush([variantOne, variantTwo]) - }) - - it("selecting by properties, scopes out the results", async () => { - const results = await service.list({ - id: variantOne.id, - }) - - expect(results).toEqual([ - expect.objectContaining({ - id: variantOne.id, - title: "variant 1", - inventory_quantity: "10", - }), - ]) - }) - - it("passing a limit, scopes the result to the limit", async () => { - const results = await service.list( - {}, - { - take: 1, - } - ) - - expect(results).toEqual([ - expect.objectContaining({ - id: variantOne.id, - }), - ]) - }) - - it("passing populate, scopes the results of the response", async () => { - const results = await service.list( - { - id: productVariantTestOne, - }, - { - select: ["id", "title", "product.title"] as any, - relations: ["product"], - } - ) - - expect(results).toEqual([ - expect.objectContaining({ - id: productVariantTestOne, - title: "variant 1", - product: expect.objectContaining({ + productOne = testManager.create(Product, { id: "product-1", title: "product 1", - tags: expect.any(Collection), - variants: expect.any(Collection), - }), - }), - ]) + status: ProductTypes.ProductStatus.PUBLISHED, + }) - expect(JSON.parse(JSON.stringify(results))).toEqual([ - { - id: productVariantTestOne, - title: "variant 1", - product: { - id: "product-1", - title: "product 1", - }, - }, - ]) - }) - }) + variantOne = testManager.create(ProductVariant, { + id: productVariantTestOne, + title: "variant 1", + inventory_quantity: 10, + product: productOne, + }) - describe("relation: options", () => { - let products: Product[] - let variants: ProductVariant[] - let options: ProductOption[] + variantTwo = testManager.create(ProductVariant, { + id: "test-2", + title: "variant", + inventory_quantity: 10, + product: productOne, + }) - beforeEach(async () => { - testManager = await TestDatabase.forkManager() + await testManager.persistAndFlush([variantOne, variantTwo]) + }) - products = (await createProductAndTags( - testManager, - productsData - )) as Product[] - variants = (await createProductVariants( - testManager, - variantsData - )) as ProductVariant[] + it("selecting by properties, scopes out the results", async () => { + const results = await service.list({ + id: variantOne.id, + }) - options = await createOptions(testManager, [ - { - id: "test-option-1", - title: "size", - product: products[0], - values: [ - { id: "value-1", value: "XS", variant: products[0].variants[0] }, - { id: "value-1", value: "XL", variant: products[0].variants[0] }, - ], - }, - { - id: "test-option-2", - title: "color", - product: products[0], - value: "blue", - variant: products[0].variants[0], - }, - ]) - }) + expect(results).toEqual([ + expect.objectContaining({ + id: variantOne.id, + title: "variant 1", + inventory_quantity: "10", + }), + ]) + }) - it("filter by options relation", async () => { - const variants = await service.list( - { options: { id: ["value-1"] } }, - { relations: ["options"] } - ) + it("passing a limit, scopes the result to the limit", async () => { + const results = await service.list( + {}, + { + take: 1, + } + ) - expect(JSON.parse(JSON.stringify(variants))).toEqual([ - expect.objectContaining({ - id: productVariantTestOne, - title: "variant title", - sku: "sku 1", - }), - ]) - }) - }) + expect(results).toEqual([ + expect.objectContaining({ + id: variantOne.id, + }), + ]) + }) - describe("create", function () { - let products: Product[] - let productOptions!: ProductOption[] + it("passing populate, scopes the results of the response", async () => { + const results = await service.list( + { + id: productVariantTestOne, + }, + { + select: ["id", "title", "product.title"] as any, + relations: ["product"], + } + ) - beforeEach(async () => { - testManager = await TestDatabase.forkManager() + expect(results).toEqual([ + expect.objectContaining({ + id: productVariantTestOne, + title: "variant 1", + product: expect.objectContaining({ + id: "product-1", + title: "product 1", + tags: expect.any(Collection), + variants: expect.any(Collection), + }), + }), + ]) - products = (await createProductAndTags( - testManager, - productsData - )) as Product[] - - productOptions = await createOptions(testManager, [ - { - id: "test-option-1", - title: "size", - product: products[0], - }, - ]) - }) - - it("should create a variant", async () => { - const data = buildProductVariantOnlyData({ - options: [ - { - option: productOptions[0], - value: "XS", - }, - ], + expect(JSON.parse(JSON.stringify(results))).toEqual([ + { + id: productVariantTestOne, + title: "variant 1", + product: { + id: "product-1", + title: "product 1", + }, + }, + ]) + }) }) - const variants = await service.create(products[0].id, [data]) + describe("relation: options", () => { + let products: Product[] + let variants: ProductVariant[] + let options: ProductOption[] - expect(variants).toHaveLength(1) - expect(variants[0].options).toHaveLength(1) + beforeEach(async () => { + const testManager = await MikroOrmWrapper.forkManager() - expect(JSON.parse(JSON.stringify(variants[0]))).toEqual( - expect.objectContaining({ - id: expect.any(String), - title: data.title, - sku: data.sku, - inventory_quantity: 100, - allow_backorder: false, - manage_inventory: true, - variant_rank: 0, - product: expect.objectContaining({ - id: products[0].id, - }), - options: expect.arrayContaining([ + products = (await createProductAndTags( + testManager, + productsData + )) as Product[] + variants = (await createProductVariants( + testManager, + variantsData + )) as ProductVariant[] + + options = await createOptions(testManager, [ + { + id: "test-option-1", + title: "size", + product: products[0], + values: [ + { + id: "value-1", + value: "XS", + variant: products[0].variants[0], + }, + { + id: "value-1", + value: "XL", + variant: products[0].variants[0], + }, + ], + }, + { + id: "test-option-2", + title: "color", + product: products[0], + value: "blue", + variant: products[0].variants[0], + }, + ]) + }) + + it("filter by options relation", async () => { + const variants = await service.list( + { options: { id: ["value-1"] } }, + { relations: ["options"] } + ) + + expect(JSON.parse(JSON.stringify(variants))).toEqual([ + expect.objectContaining({ + id: productVariantTestOne, + title: "variant title", + sku: "sku 1", + }), + ]) + }) + }) + + describe("create", function () { + let products: Product[] + let productOptions!: ProductOption[] + + beforeEach(async () => { + const testManager = await MikroOrmWrapper.forkManager() + + products = (await createProductAndTags( + testManager, + productsData + )) as Product[] + + productOptions = await createOptions(testManager, [ + { + id: "test-option-1", + title: "size", + product: products[0], + }, + ]) + }) + + it("should create a variant", async () => { + const data = buildProductVariantOnlyData({ + options: [ + { + option: productOptions[0], + value: "XS", + }, + ], + }) + + const variants = await service.create(products[0].id, [data]) + + expect(variants).toHaveLength(1) + expect(variants[0].options).toHaveLength(1) + + expect(JSON.parse(JSON.stringify(variants[0]))).toEqual( expect.objectContaining({ id: expect.any(String), - value: data.options![0].value, - }), - ]), + title: data.title, + sku: data.sku, + inventory_quantity: 100, + allow_backorder: false, + manage_inventory: true, + variant_rank: 0, + product: expect.objectContaining({ + id: products[0].id, + }), + options: expect.arrayContaining([ + expect.objectContaining({ + id: expect.any(String), + value: data.options![0].value, + }), + ]), + }) + ) }) - ) - }) - }) - - describe("retrieve", () => { - beforeEach(async () => { - testManager = await TestDatabase.forkManager() - - productOne = testManager.create(Product, { - id: "product-1", - title: "product 1", - status: ProductTypes.ProductStatus.PUBLISHED, }) - variantOne = testManager.create(ProductVariant, { - id: productVariantTestOne, - title: "variant 1", - inventory_quantity: 10, - product: productOne, - }) + describe("retrieve", () => { + beforeEach(async () => { + const testManager = await MikroOrmWrapper.forkManager() - await testManager.persistAndFlush([variantOne]) - }) - - it("should return the requested variant", async () => { - const result = await service.retrieve(variantOne.id) - - expect(result).toEqual( - expect.objectContaining({ - id: productVariantTestOne, - title: "variant 1", - }) - ) - }) - - it("should return requested attributes when requested through config", async () => { - const result = await service.retrieve(variantOne.id, { - select: ["id", "title", "product.title"] as any, - relations: ["product"], - }) - - expect(result).toEqual( - expect.objectContaining({ - id: productVariantTestOne, - title: "variant 1", - product_id: "product-1", - product: expect.objectContaining({ + productOne = testManager.create(Product, { id: "product-1", title: "product 1", - }), + status: ProductTypes.ProductStatus.PUBLISHED, + }) + + variantOne = testManager.create(ProductVariant, { + id: productVariantTestOne, + title: "variant 1", + inventory_quantity: 10, + product: productOne, + }) + + await testManager.persistAndFlush([variantOne]) }) - ) + + it("should return the requested variant", async () => { + const result = await service.retrieve(variantOne.id) + + expect(result).toEqual( + expect.objectContaining({ + id: productVariantTestOne, + title: "variant 1", + }) + ) + }) + + it("should return requested attributes when requested through config", async () => { + const result = await service.retrieve(variantOne.id, { + select: ["id", "title", "product.title"] as any, + relations: ["product"], + }) + + expect(result).toEqual( + expect.objectContaining({ + id: productVariantTestOne, + title: "variant 1", + product_id: "product-1", + product: expect.objectContaining({ + id: "product-1", + title: "product 1", + }), + }) + ) + }) + + it("should throw an error when a variant with ID does not exist", async () => { + let error + + try { + await service.retrieve("does-not-exist") + } catch (e) { + error = e + } + + expect(error.message).toEqual( + "ProductVariant with id: does-not-exist was not found" + ) + }) + + it("should throw an error when an id is not provided", async () => { + let error + + try { + await service.retrieve(undefined as unknown as string) + } catch (e) { + error = e + } + + expect(error.message).toEqual("productVariant - id must be defined") + }) + }) }) - - it("should throw an error when a variant with ID does not exist", async () => { - let error - - try { - await service.retrieve("does-not-exist") - } catch (e) { - error = e - } - - expect(error.message).toEqual( - "ProductVariant with id: does-not-exist was not found" - ) - }) - - it("should throw an error when an id is not provided", async () => { - let error - - try { - await service.retrieve(undefined as unknown as string) - } catch (e) { - error = e - } - - expect(error.message).toEqual("productVariant - id must be defined") - }) - }) + }, }) diff --git a/packages/product/integration-tests/__tests__/services/product/index.ts b/packages/product/integration-tests/__tests__/services/product/index.ts index e2511d4667..ecbd08f2fa 100644 --- a/packages/product/integration-tests/__tests__/services/product/index.ts +++ b/packages/product/integration-tests/__tests__/services/product/index.ts @@ -1,10 +1,4 @@ -import { - Image, - Product, - ProductCategory, - ProductCollection, - ProductVariant, -} from "@models" +import { Image, Product, ProductCategory, ProductCollection } from "@models" import { assignCategoriesToProduct, buildProductOnlyData, @@ -19,638 +13,642 @@ import { productsData, variantsData, } from "../../../__fixtures__/product/data" - -import { ProductDTO, ProductTypes } from "@medusajs/types" -import { createMedusaContainer, kebabCase } from "@medusajs/utils" -import { SqlEntityManager } from "@mikro-orm/postgresql" import { ProductService } from "@services" -import { asValue } from "awilix" -import ContainerLoader from "../../../../src/loaders/container" +import { + IProductModuleService, + ProductDTO, + ProductTypes, +} from "@medusajs/types" +import { kebabCase } from "@medusajs/utils" +import { SqlEntityManager } from "@mikro-orm/postgresql" import { createProductCategories } from "../../../__fixtures__/product-category" -import { TestDatabase } from "../../../utils" +import { Modules } from "@medusajs/modules-sdk" +import { moduleIntegrationTestRunner, SuiteOptions } from "medusa-test-utils" jest.setTimeout(30000) -describe("Product Service", () => { - let service: ProductService - let testManager: SqlEntityManager - let repositoryManager: SqlEntityManager - let products!: Product[] - let productOne: Product - let variants!: ProductVariant[] - let categories!: ProductCategory[] - let collections!: ProductCollection[] +moduleIntegrationTestRunner({ + moduleName: Modules.PRODUCT, + testSuite: ({ + MikroOrmWrapper, + medusaApp, + }: SuiteOptions) => { + let service: ProductService - beforeEach(async () => { - await TestDatabase.setupDatabase() - repositoryManager = await TestDatabase.forkManager() - - const container = createMedusaContainer() - container.register("manager", asValue(repositoryManager)) - - await ContainerLoader({ container }) - - service = container.resolve("productService") - }) - - afterEach(async () => { - await TestDatabase.clearDatabase() - }) - - describe("retrieve", () => { - beforeEach(async () => { - testManager = await TestDatabase.forkManager() - productOne = testManager.create(Product, { - id: "product-1", - title: "product 1", - status: ProductTypes.ProductStatus.PUBLISHED, - }) - - await testManager.persistAndFlush([productOne]) + beforeEach(() => { + service = medusaApp.modules["productService"].productService_ }) - it("should throw an error when an id is not provided", async () => { - let error + describe("Product Service", () => { + let testManager: SqlEntityManager + let products!: Product[] + let productOne: Product + let categories!: ProductCategory[] - try { - await service.retrieve(undefined as unknown as string) - } catch (e) { - error = e - } + describe("retrieve", () => { + beforeEach(async () => { + testManager = await MikroOrmWrapper.forkManager() + productOne = testManager.create(Product, { + id: "product-1", + title: "product 1", + status: ProductTypes.ProductStatus.PUBLISHED, + }) - expect(error.message).toEqual("product - id must be defined") - }) - - it("should throw an error when product with id does not exist", async () => { - let error - - try { - await service.retrieve("does-not-exist") - } catch (e) { - error = e - } - - expect(error.message).toEqual( - "Product with id: does-not-exist was not found" - ) - }) - - it("should return a product when product with an id exists", async () => { - const result = await service.retrieve(productOne.id) - - expect(result).toEqual( - expect.objectContaining({ - id: productOne.id, + await testManager.persistAndFlush([productOne]) }) - ) - }) - }) - describe("create", function () { - let images: Image[] = [] + it("should throw an error when an id is not provided", async () => { + let error - beforeEach(async () => { - testManager = await TestDatabase.forkManager() - - images = await createImages(testManager, ["image-1"]) - }) - - it("should create a product", async () => { - const data = buildProductOnlyData({ - images, - thumbnail: images[0].url, - }) - - const products = await service.create([data]) - - expect(products).toHaveLength(1) - expect(JSON.parse(JSON.stringify(products[0]))).toEqual( - expect.objectContaining({ - id: expect.any(String), - title: data.title, - handle: kebabCase(data.title), - description: data.description, - subtitle: data.subtitle, - is_giftcard: data.is_giftcard, - discountable: data.discountable, - thumbnail: images[0].url, - status: data.status, - images: expect.arrayContaining([ - expect.objectContaining({ - id: images[0].id, - url: images[0].url, - }), - ]), - }) - ) - }) - }) - - describe("update", function () { - let images: Image[] = [] - - beforeEach(async () => { - testManager = await TestDatabase.forkManager() - images = await createImages(testManager, ["image-1", "image-2"]) - - productOne = testManager.create(Product, { - id: "product-1", - title: "product 1", - status: ProductTypes.ProductStatus.PUBLISHED, - }) - - await testManager.persistAndFlush([productOne]) - }) - - it("should update a product and its allowed relations", async () => { - const updateData = [ - { - id: productOne.id, - title: "update test 1", - images: images, - thumbnail: images[0].url, - }, - ] - - const products = await service.update(updateData) - - expect(products.length).toEqual(1) - - let result = await service.retrieve(productOne.id, { - relations: ["images", "thumbnail"], - }) - let serialized = JSON.parse(JSON.stringify(result)) - - expect(serialized).toEqual( - expect.objectContaining({ - id: productOne.id, - title: "update test 1", - thumbnail: images[0].url, - images: [ - expect.objectContaining({ - url: images[0].url, - }), - expect.objectContaining({ - url: images[1].url, - }), - ], - }) - ) - }) - - it("should throw an error when id is not present", async () => { - let error - const updateData = [ - { - id: productOne.id, - title: "update test 1", - }, - { - id: undefined as unknown as string, - title: "update test 2", - }, - ] - - try { - await service.update(updateData) - } catch (e) { - error = e - } - - expect(error.message).toEqual(`Product with id "" not found`) - - let result = await service.retrieve(productOne.id) - - expect(result.title).not.toBe("update test 1") - }) - - it("should throw an error when product with id does not exist", async () => { - let error - const updateData = [ - { - id: "does-not-exist", - title: "update test 1", - }, - ] - - try { - await service.update(updateData) - } catch (e) { - error = e - } - - expect(error.message).toEqual( - `Product with id "does-not-exist" not found` - ) - }) - }) - - describe("list", () => { - describe("soft deleted", function () { - let product - - beforeEach(async () => { - testManager = await TestDatabase.forkManager() - - const products = await createProductAndTags(testManager, productsData) - - product = products[1] - await service.softDelete([products[0].id]) - }) - - it("should list all products that are not deleted", async () => { - const products = await service.list() - - expect(products).toHaveLength(2) - expect(products[0].id).toEqual(product.id) - }) - - it("should list all products including the deleted", async () => { - const products = await service.list({}, { withDeleted: true }) - - expect(products).toHaveLength(3) - }) - }) - - describe("relation: tags", () => { - beforeEach(async () => { - testManager = await TestDatabase.forkManager() - - products = await createProductAndTags(testManager, productsData) - }) - - it("should filter by id and including relations", async () => { - const productsResult = await service.list( - { - id: products[0].id, - }, - { - relations: ["tags"], + try { + await service.retrieve(undefined as unknown as string) + } catch (e) { + error = e } - ) - productsResult.forEach((product, index) => { - const tags = product.tags.toArray() + expect(error.message).toEqual("product - id must be defined") + }) - expect(product).toEqual( + it("should throw an error when product with id does not exist", async () => { + let error + + try { + await service.retrieve("does-not-exist") + } catch (e) { + error = e + } + + expect(error.message).toEqual( + "Product with id: does-not-exist was not found" + ) + }) + + it("should return a product when product with an id exists", async () => { + const result = await service.retrieve(productOne.id) + + expect(result).toEqual( expect.objectContaining({ - id: productsData[index].id, - title: productsData[index].title, + id: productOne.id, }) ) + }) + }) - tags.forEach((tag, tagIndex) => { - expect(tag).toEqual( - expect.objectContaining({ - ...productsData[index].tags[tagIndex], + describe("create", function () { + let images: Image[] = [] + + beforeEach(async () => { + testManager = await MikroOrmWrapper.forkManager() + + images = await createImages(testManager, ["image-1"]) + }) + + it("should create a product", async () => { + const data = buildProductOnlyData({ + images, + thumbnail: images[0].url, + }) + + const products = await service.create([data]) + + expect(products).toHaveLength(1) + expect(JSON.parse(JSON.stringify(products[0]))).toEqual( + expect.objectContaining({ + id: expect.any(String), + title: data.title, + handle: kebabCase(data.title), + description: data.description, + subtitle: data.subtitle, + is_giftcard: data.is_giftcard, + discountable: data.discountable, + thumbnail: images[0].url, + status: data.status, + images: expect.arrayContaining([ + expect.objectContaining({ + id: images[0].id, + url: images[0].url, + }), + ]), + }) + ) + }) + }) + + describe("update", function () { + let images: Image[] = [] + + beforeEach(async () => { + testManager = await MikroOrmWrapper.forkManager() + images = await createImages(testManager, ["image-1", "image-2"]) + + productOne = testManager.create(Product, { + id: "product-1", + title: "product 1", + status: ProductTypes.ProductStatus.PUBLISHED, + }) + + await testManager.persistAndFlush([productOne]) + }) + + it("should update a product and its allowed relations", async () => { + const updateData = [ + { + id: productOne.id, + title: "update test 1", + images: images, + thumbnail: images[0].url, + }, + ] + + const products = await service.update(updateData) + + expect(products.length).toEqual(1) + + let result = await service.retrieve(productOne.id, { + relations: ["images", "thumbnail"], + }) + let serialized = JSON.parse(JSON.stringify(result)) + + expect(serialized).toEqual( + expect.objectContaining({ + id: productOne.id, + title: "update test 1", + thumbnail: images[0].url, + images: [ + expect.objectContaining({ + url: images[0].url, + }), + expect.objectContaining({ + url: images[1].url, + }), + ], + }) + ) + }) + + it("should throw an error when id is not present", async () => { + let error + const updateData = [ + { + id: productOne.id, + title: "update test 1", + }, + { + id: undefined as unknown as string, + title: "update test 2", + }, + ] + + try { + await service.update(updateData) + } catch (e) { + error = e + } + + expect(error.message).toEqual(`Product with id "" not found`) + + let result = await service.retrieve(productOne.id) + + expect(result.title).not.toBe("update test 1") + }) + + it("should throw an error when product with id does not exist", async () => { + let error + const updateData = [ + { + id: "does-not-exist", + title: "update test 1", + }, + ] + + try { + await service.update(updateData) + } catch (e) { + error = e + } + + expect(error.message).toEqual( + `Product with id "does-not-exist" not found` + ) + }) + }) + + describe("list", () => { + describe("soft deleted", function () { + let product + + beforeEach(async () => { + testManager = await MikroOrmWrapper.forkManager() + + const products = await createProductAndTags( + testManager, + productsData + ) + + product = products[1] + await service.softDelete([products[0].id]) + }) + + it("should list all products that are not deleted", async () => { + const products = await service.list() + + expect(products).toHaveLength(2) + expect(products[0].id).toEqual(product.id) + }) + + it("should list all products including the deleted", async () => { + const products = await service.list({}, { withDeleted: true }) + + expect(products).toHaveLength(3) + }) + }) + + describe("relation: tags", () => { + beforeEach(async () => { + testManager = await MikroOrmWrapper.forkManager() + + products = await createProductAndTags(testManager, productsData) + }) + + it("should filter by id and including relations", async () => { + const productsResult = await service.list( + { + id: products[0].id, + }, + { + relations: ["tags"], + } + ) + + productsResult.forEach((product, index) => { + const tags = product.tags.toArray() + + expect(product).toEqual( + expect.objectContaining({ + id: productsData[index].id, + title: productsData[index].title, + }) + ) + + tags.forEach((tag, tagIndex) => { + expect(tag).toEqual( + expect.objectContaining({ + ...productsData[index].tags[tagIndex], + }) + ) }) + }) + }) + + it("should filter by id and without relations", async () => { + const productsResult = await service.list({ + id: products[0].id, + }) + + productsResult.forEach((product, index) => { + const tags = product.tags.getItems(false) + + expect(product).toEqual( + expect.objectContaining({ + id: productsData[index].id, + title: productsData[index].title, + }) + ) + + expect(tags.length).toBe(0) + }) + }) + }) + + describe("relation: categories", () => { + let workingProduct: Product + let workingCategory: ProductCategory + + beforeEach(async () => { + testManager = await MikroOrmWrapper.forkManager() + + products = await createProductAndTags(testManager, productsData) + workingProduct = products.find((p) => p.id === "test-1") as Product + categories = await createProductCategories( + testManager, + categoriesData + ) + workingCategory = (await testManager.findOne( + ProductCategory, + "category-1" + )) as ProductCategory + + workingProduct = await assignCategoriesToProduct( + testManager, + workingProduct, + categories ) }) - }) - }) - it("should filter by id and without relations", async () => { - const productsResult = await service.list({ - id: products[0].id, - }) + it("should filter by categories relation and scope fields", async () => { + const products = await service.list( + { + id: workingProduct.id, + categories: { id: [workingCategory.id] }, + }, + { + select: [ + "title", + "categories.name", + "categories.handle", + "categories.mpath", + ] as (keyof ProductDTO)[], + relations: ["categories"], + } + ) - productsResult.forEach((product, index) => { - const tags = product.tags.getItems(false) + const product = products.find( + (p) => p.id === workingProduct.id + ) as unknown as Product - expect(product).toEqual( - expect.objectContaining({ - id: productsData[index].id, - title: productsData[index].title, - }) - ) - - expect(tags.length).toBe(0) - }) - }) - }) - - describe("relation: categories", () => { - let workingProduct: Product - let workingCategory: ProductCategory - - beforeEach(async () => { - testManager = await TestDatabase.forkManager() - - products = await createProductAndTags(testManager, productsData) - workingProduct = products.find((p) => p.id === "test-1") as Product - categories = await createProductCategories(testManager, categoriesData) - workingCategory = (await testManager.findOne( - ProductCategory, - "category-1" - )) as ProductCategory - - workingProduct = await assignCategoriesToProduct( - testManager, - workingProduct, - categories - ) - }) - - it("should filter by categories relation and scope fields", async () => { - const products = await service.list( - { - id: workingProduct.id, - categories: { id: [workingCategory.id] }, - }, - { - select: [ - "title", - "categories.name", - "categories.handle", - "categories.mpath", - ] as (keyof ProductDTO)[], - relations: ["categories"], - } - ) - - const product = products.find( - (p) => p.id === workingProduct.id - ) as unknown as Product - - expect(product).toEqual( - expect.objectContaining({ - id: workingProduct.id, - title: workingProduct.title, - }) - ) - - expect(product.categories.toArray()).toEqual([ - { - id: "category-0", - name: "category 0", - handle: "category-0", - mpath: "category-0.", - }, - { - id: "category-1", - name: "category 1", - handle: "category-1", - mpath: "category-0.category-1.", - }, - { - id: "category-1-a", - name: "category 1 a", - handle: "category-1-a", - mpath: "category-0.category-1.category-1-a.", - }, - ]) - }) - - it("should returns empty array when querying for a category that doesnt exist", async () => { - const products = await service.list( - { - id: workingProduct.id, - categories: { id: ["category-doesnt-exist-id"] }, - }, - { - select: [ - "title", - "categories.name", - "categories.handle", - ] as (keyof ProductDTO)[], - relations: ["categories"], - } - ) - - expect(products).toEqual([]) - }) - }) - - describe("relation: collections", () => { - let workingProduct: Product - let workingProductTwo: Product - let workingCollection: ProductCollection - let workingCollectionTwo: ProductCollection - const collectionData = [ - { - id: "test-1", - title: "col 1", - }, - { - id: "test-2", - title: "col 2", - }, - ] - - beforeEach(async () => { - testManager = await TestDatabase.forkManager() - collections = await createCollections(testManager, collectionData) - workingCollection = (await testManager.findOne( - ProductCollection, - "test-1" - )) as ProductCollection - workingCollectionTwo = (await testManager.findOne( - ProductCollection, - "test-2" - )) as ProductCollection - - products = await createProductAndTags(testManager, [ - { - ...productsData[0], - collection_id: workingCollection.id, - }, - { - ...productsData[1], - collection_id: workingCollectionTwo.id, - }, - { - ...productsData[2], - }, - ]) - - workingProduct = products.find((p) => p.id === "test-1") as Product - workingProductTwo = products.find((p) => p.id === "test-2") as Product - }) - - it("should filter by collection relation and scope fields", async () => { - const products = await service.list( - { - id: workingProduct.id, - collection_id: workingCollection.id, - }, - { - select: ["title", "collection.title"], - relations: ["collection"], - } - ) - - const serialized = JSON.parse(JSON.stringify(products)) - - expect(serialized.length).toEqual(1) - expect(serialized).toEqual([ - { - id: workingProduct.id, - title: workingProduct.title, - collection: { - id: workingCollection.id, - title: workingCollection.title, - }, - }, - ]) - }) - - it("should filter by collection when multiple collection ids are passed", async () => { - const products = await service.list( - { - collection_id: [workingCollection.id, workingCollectionTwo.id], - }, - { - select: ["title", "collection.title"], - relations: ["collection"], - } - ) - - const serialized = JSON.parse(JSON.stringify(products)) - - expect(serialized.length).toEqual(2) - expect(serialized).toEqual([ - { - id: workingProduct.id, - title: workingProduct.title, - collection_id: workingCollection.id, - collection: { - id: workingCollection.id, - title: workingCollection.title, - }, - }, - { - id: workingProductTwo.id, - title: workingProductTwo.title, - collection_id: workingCollectionTwo.id, - collection: { - id: workingCollectionTwo.id, - title: workingCollectionTwo.title, - }, - }, - ]) - }) - - it("should returns empty array when querying for a collection that doesnt exist", async () => { - const products = await service.list( - { - id: workingProduct.id, - collection_id: "collection-doesnt-exist-id", - }, - { - select: ["title", "collection.title"] as (keyof ProductDTO)[], - relations: ["collection"], - } - ) - - expect(products).toEqual([]) - }) - }) - - describe("relation: variants", () => { - beforeEach(async () => { - testManager = await TestDatabase.forkManager() - - products = await createProductAndTags(testManager, productsData) - variants = await createProductVariants(testManager, variantsData) - }) - - it("should filter by id and including relations", async () => { - const productsResult = await service.list( - { - id: products[0].id, - }, - { - relations: ["variants"], - } - ) - - productsResult.forEach((product, index) => { - const variants = product.variants.toArray() - - expect(product).toEqual( - expect.objectContaining({ - id: productsData[index].id, - title: productsData[index].title, - }) - ) - - variants.forEach((variant, variantIndex) => { - const expectedVariant = variantsData.filter( - (d) => d.product.id === product.id - )[variantIndex] - - const variantProduct = variant.product - - expect(variant).toEqual( + expect(product).toEqual( expect.objectContaining({ - id: expectedVariant.id, - sku: expectedVariant.sku, - title: expectedVariant.title, + id: workingProduct.id, + title: workingProduct.title, }) ) + + expect(product.categories.toArray()).toEqual([ + { + id: "category-0", + name: "category 0", + handle: "category-0", + mpath: "category-0.", + }, + { + id: "category-1", + name: "category 1", + handle: "category-1", + mpath: "category-0.category-1.", + }, + { + id: "category-1-a", + name: "category 1 a", + handle: "category-1-a", + mpath: "category-0.category-1.category-1-a.", + }, + ]) + }) + + it("should returns empty array when querying for a category that doesnt exist", async () => { + const products = await service.list( + { + id: workingProduct.id, + categories: { id: ["category-doesnt-exist-id"] }, + }, + { + select: [ + "title", + "categories.name", + "categories.handle", + ] as (keyof ProductDTO)[], + relations: ["categories"], + } + ) + + expect(products).toEqual([]) + }) + }) + + describe("relation: collections", () => { + let workingProduct: Product + let workingProductTwo: Product + let workingCollection: ProductCollection + let workingCollectionTwo: ProductCollection + const collectionData = [ + { + id: "test-1", + title: "col 1", + }, + { + id: "test-2", + title: "col 2", + }, + ] + + beforeEach(async () => { + testManager = await MikroOrmWrapper.forkManager() + await createCollections(testManager, collectionData) + workingCollection = (await testManager.findOne( + ProductCollection, + "test-1" + )) as ProductCollection + workingCollectionTwo = (await testManager.findOne( + ProductCollection, + "test-2" + )) as ProductCollection + + products = await createProductAndTags(testManager, [ + { + ...productsData[0], + collection_id: workingCollection.id, + }, + { + ...productsData[1], + collection_id: workingCollectionTwo.id, + }, + { + ...productsData[2], + }, + ]) + + workingProduct = products.find((p) => p.id === "test-1") as Product + workingProductTwo = products.find( + (p) => p.id === "test-2" + ) as Product + }) + + it("should filter by collection relation and scope fields", async () => { + const products = await service.list( + { + id: workingProduct.id, + collection_id: workingCollection.id, + }, + { + select: ["title", "collection.title"], + relations: ["collection"], + } + ) + + const serialized = JSON.parse(JSON.stringify(products)) + + expect(serialized.length).toEqual(1) + expect(serialized).toEqual([ + { + id: workingProduct.id, + title: workingProduct.title, + collection: { + id: workingCollection.id, + title: workingCollection.title, + }, + }, + ]) + }) + + it("should filter by collection when multiple collection ids are passed", async () => { + const products = await service.list( + { + collection_id: [workingCollection.id, workingCollectionTwo.id], + }, + { + select: ["title", "collection.title"], + relations: ["collection"], + } + ) + + const serialized = JSON.parse(JSON.stringify(products)) + + expect(serialized.length).toEqual(2) + expect(serialized).toEqual([ + { + id: workingProduct.id, + title: workingProduct.title, + collection_id: workingCollection.id, + collection: { + id: workingCollection.id, + title: workingCollection.title, + }, + }, + { + id: workingProductTwo.id, + title: workingProductTwo.title, + collection_id: workingCollectionTwo.id, + collection: { + id: workingCollectionTwo.id, + title: workingCollectionTwo.title, + }, + }, + ]) + }) + + it("should returns empty array when querying for a collection that doesnt exist", async () => { + const products = await service.list( + { + id: workingProduct.id, + collection_id: "collection-doesnt-exist-id", + }, + { + select: ["title", "collection.title"] as (keyof ProductDTO)[], + relations: ["collection"], + } + ) + + expect(products).toEqual([]) + }) + }) + + describe("relation: variants", () => { + beforeEach(async () => { + testManager = await MikroOrmWrapper.forkManager() + + products = await createProductAndTags(testManager, productsData) + await createProductVariants(testManager, variantsData) + }) + + it("should filter by id and including relations", async () => { + const productsResult = await service.list( + { + id: products[0].id, + }, + { + relations: ["variants"], + } + ) + + productsResult.forEach((product, index) => { + const variants = product.variants.toArray() + + expect(product).toEqual( + expect.objectContaining({ + id: productsData[index].id, + title: productsData[index].title, + }) + ) + + variants.forEach((variant, variantIndex) => { + const expectedVariant = variantsData.filter( + (d) => d.product.id === product.id + )[variantIndex] + + const variantProduct = variant.product + + expect(variant).toEqual( + expect.objectContaining({ + id: expectedVariant.id, + sku: expectedVariant.sku, + title: expectedVariant.title, + }) + ) + }) + }) }) }) }) - }) - }) - describe("softDelete", function () { - let images: Image[] = [] + describe("softDelete", function () { + let images: Image[] = [] - beforeEach(async () => { - testManager = await TestDatabase.forkManager() + beforeEach(async () => { + testManager = await MikroOrmWrapper.forkManager() - images = await createImages(testManager, ["image-1"]) - }) + images = await createImages(testManager, ["image-1"]) + }) - it("should soft delete a product", async () => { - const data = buildProductOnlyData({ - images, - thumbnail: images[0].url, + it("should soft delete a product", async () => { + const data = buildProductOnlyData({ + images, + thumbnail: images[0].url, + }) + + const products = await service.create([data]) + await service.softDelete(products.map((p) => p.id)) + const deleteProducts = await service.list( + { id: products.map((p) => p.id) }, + { + relations: [ + "variants", + "variants.options", + "options", + "options.values", + ], + withDeleted: true, + } + ) + + expect(deleteProducts).toHaveLength(1) + expect(deleteProducts[0].deleted_at).not.toBeNull() + }) }) - const products = await service.create([data]) - await service.softDelete(products.map((p) => p.id)) - const deleteProducts = await service.list( - { id: products.map((p) => p.id) }, - { - relations: [ - "variants", - "variants.options", - "options", - "options.values", - ], - withDeleted: true, - } - ) + describe("restore", function () { + let images: Image[] = [] - expect(deleteProducts).toHaveLength(1) - expect(deleteProducts[0].deleted_at).not.toBeNull() - }) - }) + beforeEach(async () => { + testManager = await MikroOrmWrapper.forkManager() - describe("restore", function () { - let images: Image[] = [] + images = await createImages(testManager, ["image-1"]) + }) - beforeEach(async () => { - testManager = await TestDatabase.forkManager() + it("should restore a soft deleted product", async () => { + const data = buildProductOnlyData({ + images, + thumbnail: images[0].url, + }) - images = await createImages(testManager, ["image-1"]) - }) + const products = await service.create([data]) + const product = products[0] + await service.softDelete([product.id]) + const [restoreProducts] = await service.restore([product.id]) - it("should restore a soft deleted product", async () => { - const data = buildProductOnlyData({ - images, - thumbnail: images[0].url, + expect(restoreProducts).toHaveLength(1) + expect(restoreProducts[0].deleted_at).toBeNull() + }) }) - - const products = await service.create([data]) - const product = products[0] - await service.softDelete([product.id]) - const [restoreProducts] = await service.restore([product.id]) - - expect(restoreProducts).toHaveLength(1) - expect(restoreProducts[0].deleted_at).toBeNull() }) - }) + }, }) diff --git a/packages/product/integration-tests/setup-env.js b/packages/product/integration-tests/setup-env.js deleted file mode 100644 index f2c14d8dbb..0000000000 --- a/packages/product/integration-tests/setup-env.js +++ /dev/null @@ -1,6 +0,0 @@ -if (typeof process.env.DB_TEMP_NAME === "undefined") { - const tempName = parseInt(process.env.JEST_WORKER_ID || "1") - process.env.DB_TEMP_NAME = `medusa-integration-${tempName}` -} - -process.env.MEDUSA_PRODUCT_DB_SCHEMA = "medusa-product" diff --git a/packages/product/integration-tests/setup.js b/packages/product/integration-tests/setup.js deleted file mode 100644 index 4c07c20a9c..0000000000 --- a/packages/product/integration-tests/setup.js +++ /dev/null @@ -1,22 +0,0 @@ -const { dropDatabase } = require("pg-god") - -const DB_HOST = process.env.DB_HOST ?? "localhost" -const DB_USERNAME = process.env.DB_USERNAME ?? "postgres" -const DB_PASSWORD = process.env.DB_PASSWORD ?? "" -const DB_NAME = process.env.DB_TEMP_NAME - -const pgGodCredentials = { - user: DB_USERNAME, - password: DB_PASSWORD, - host: DB_HOST, -} - -afterAll(async () => { - try { - await dropDatabase({ databaseName: DB_NAME }, pgGodCredentials) - } catch (e) { - console.error( - `This might fail if it is run during the unit tests since there is no database to drop. Otherwise, please check what is the issue. ${e.message}` - ) - } -}) diff --git a/packages/product/integration-tests/utils/config.ts b/packages/product/integration-tests/utils/config.ts deleted file mode 100644 index 3e15868f6e..0000000000 --- a/packages/product/integration-tests/utils/config.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { ModuleServiceInitializeOptions } from "@medusajs/types" - -export const databaseOptions: ModuleServiceInitializeOptions["database"] = { - schema: "public", - clientUrl: "medusa-products-test", -} diff --git a/packages/product/integration-tests/utils/database.ts b/packages/product/integration-tests/utils/database.ts deleted file mode 100644 index be8e26ecfe..0000000000 --- a/packages/product/integration-tests/utils/database.ts +++ /dev/null @@ -1,95 +0,0 @@ -import { TSMigrationGenerator } from "@mikro-orm/migrations" -import { MikroORM, Options, SqlEntityManager } from "@mikro-orm/postgresql" -import * as ProductModels from "@models" -import * as process from "process" - -const DB_HOST = process.env.DB_HOST ?? "localhost" -const DB_USERNAME = process.env.DB_USERNAME ?? "" -const DB_PASSWORD = process.env.DB_PASSWORD -const DB_NAME = process.env.DB_TEMP_NAME -export const DB_URL = `postgres://${DB_USERNAME}${ - DB_PASSWORD ? `:${DB_PASSWORD}` : "" -}@${DB_HOST}/${DB_NAME}` - -const ORMConfig: Options = { - type: "postgresql", - clientUrl: DB_URL, - entities: Object.values(ProductModels), - schema: process.env.MEDUSA_PRODUCT_DB_SCHEMA, - debug: false, - migrations: { - path: "../../src/migrations", - pathTs: "../../src/migrations", - glob: "!(*.d).{js,ts}", - silent: true, - dropTables: true, - transactional: true, - allOrNothing: true, - safe: false, - generator: TSMigrationGenerator, - }, -} - -interface TestDatabase { - orm: MikroORM | null - manager: SqlEntityManager | null - - setupDatabase(): Promise - clearDatabase(): Promise - getManager(): SqlEntityManager - forkManager(): Promise - getORM(): MikroORM -} - -export const TestDatabase: TestDatabase = { - orm: null, - manager: null, - - getManager() { - if (this.manager === null) { - throw new Error("manager entity not available") - } - - return this.manager - }, - - async forkManager() { - if (this.manager === null) { - throw new Error("manager entity not available") - } - - return await this.manager.fork() - }, - - getORM() { - if (this.orm === null) { - throw new Error("orm entity not available") - } - - return this.orm - }, - - async setupDatabase() { - // Initializing the ORM - this.orm = await MikroORM.init(ORMConfig) - - if (this.orm === null) { - throw new Error("ORM not configured") - } - - this.manager = await this.orm.em - - await this.orm.schema.refreshDatabase() // ensure db exists and is fresh - }, - - async clearDatabase() { - if (this.orm === null) { - throw new Error("ORM not configured") - } - - await this.orm.close() - - this.orm = null - this.manager = null - }, -} diff --git a/packages/product/integration-tests/utils/get-init-module-config.ts b/packages/product/integration-tests/utils/get-init-module-config.ts deleted file mode 100644 index a57806bb17..0000000000 --- a/packages/product/integration-tests/utils/get-init-module-config.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { Modules, ModulesDefinition } from "@medusajs/modules-sdk" - -import { DB_URL } from "./database" -import { EventBusService } from "../__fixtures__/event-bus" - -export function getInitModuleConfig( - additionalOptions: any = {}, - injectedDependencies: null | Record = null -) { - const moduleOptions = { - defaultAdapterOptions: { - database: { - clientUrl: DB_URL, - schema: process.env.MEDUSA_PRODUCT_DB_SCHEMA, - }, - }, - ...additionalOptions, - } - - const modulesConfig_ = { - [Modules.PRODUCT]: { - definition: ModulesDefinition[Modules.PRODUCT], - options: moduleOptions, - }, - } - - const defaultInjectedDependencies = { - eventBusModuleService: new EventBusService(), - } - - return { - injectedDependencies: injectedDependencies ?? defaultInjectedDependencies, - modulesConfig: modulesConfig_, - databaseConfig: { - clientUrl: DB_URL, - schema: process.env.MEDUSA_PRODUCT_DB_SCHEMA, - }, - joinerConfig: [], - } -} diff --git a/packages/product/integration-tests/utils/index.ts b/packages/product/integration-tests/utils/index.ts deleted file mode 100644 index ba28fb5523..0000000000 --- a/packages/product/integration-tests/utils/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from "./database" -export * from "./get-init-module-config" diff --git a/packages/product/jest.config.js b/packages/product/jest.config.js index dce2002dae..0c652264ea 100644 --- a/packages/product/jest.config.js +++ b/packages/product/jest.config.js @@ -17,6 +17,4 @@ module.exports = { testEnvironment: `node`, moduleFileExtensions: [`js`, `ts`], modulePathIgnorePatterns: ["dist/"], - setupFiles: ["/integration-tests/setup-env.js"], - setupFilesAfterEnv: ["/integration-tests/setup.js"], } diff --git a/packages/product/src/services/product-module-service.ts b/packages/product/src/services/product-module-service.ts index 180d08ae22..a514849179 100644 --- a/packages/product/src/services/product-module-service.ts +++ b/packages/product/src/services/product-module-service.ts @@ -693,7 +693,6 @@ export default class ProductModuleService< @MedusaContext() sharedContext: Context = {} ): Promise { const input = Array.isArray(data) ? data : [data] - const products = await this.create_(input, sharedContext) const createdProducts = await this.baseRepository_.serialize< diff --git a/packages/promotion/integration-tests/__tests__/services/promotion-module/campaign.spec.ts b/packages/promotion/integration-tests/__tests__/services/promotion-module/campaign.spec.ts index be18d5fc5a..cd6c8bc586 100644 --- a/packages/promotion/integration-tests/__tests__/services/promotion-module/campaign.spec.ts +++ b/packages/promotion/integration-tests/__tests__/services/promotion-module/campaign.spec.ts @@ -1,464 +1,444 @@ import { Modules } from "@medusajs/modules-sdk" import { IPromotionModuleService } from "@medusajs/types" -import { SqlEntityManager } from "@mikro-orm/postgresql" -import { initModules } from "medusa-test-utils" +import { moduleIntegrationTestRunner, SuiteOptions } from "medusa-test-utils" import { createCampaigns } from "../../../__fixtures__/campaigns" import { createPromotions } from "../../../__fixtures__/promotion" -import { MikroOrmWrapper } from "../../../utils" -import { getInitModuleConfig } from "../../../utils/get-init-module-config" jest.setTimeout(30000) -describe("Promotion Module Service: Campaigns", () => { - let service: IPromotionModuleService - let repositoryManager: SqlEntityManager - let shutdownFunc: () => void - - beforeAll(async () => { - const initModulesConfig = getInitModuleConfig() - - const { medusaApp, shutdown } = await initModules(initModulesConfig) - - service = medusaApp.modules[Modules.PROMOTION] - - shutdownFunc = shutdown - }) - - afterAll(async () => { - shutdownFunc() - }) - - beforeEach(async () => { - await MikroOrmWrapper.setupDatabase() - repositoryManager = MikroOrmWrapper.forkManager() - }) - - afterEach(async () => { - await MikroOrmWrapper.clearDatabase() - }) - - describe("listAndCountCampaigns", () => { - beforeEach(async () => { - await createCampaigns(repositoryManager) - }) - - it("should return all campaigns and its count", async () => { - const [campaigns, count] = await service.listAndCountCampaigns() - - expect(count).toEqual(2) - expect(campaigns).toEqual([ - { - id: "campaign-id-1", - name: "campaign 1", - description: "test description", - currency: "USD", - campaign_identifier: "test-1", - starts_at: expect.any(Date), - ends_at: expect.any(Date), - budget: expect.any(Object), - created_at: expect.any(Date), - updated_at: expect.any(Date), - deleted_at: null, - }, - { - id: "campaign-id-2", - name: "campaign 1", - description: "test description", - currency: "USD", - campaign_identifier: "test-2", - starts_at: expect.any(Date), - ends_at: expect.any(Date), - budget: expect.any(Object), - created_at: expect.any(Date), - updated_at: expect.any(Date), - deleted_at: null, - }, - ]) - }) - - it("should return all campaigns based on config select and relations param", async () => { - const [campaigns, count] = await service.listAndCountCampaigns( - { - id: ["campaign-id-1"], - }, - { - relations: ["budget"], - select: ["name", "budget.limit"], - } - ) - - expect(count).toEqual(1) - expect(campaigns).toEqual([ - { - id: "campaign-id-1", - name: "campaign 1", - budget: expect.objectContaining({ - id: expect.any(String), - campaign: expect.any(Object), - limit: 1000, - }), - }, - ]) - }) - }) - - describe("createCampaigns", () => { - it("should throw an error when required params are not passed", async () => { - const error = await service - .createCampaigns([ - { - name: "test", - } as any, - ]) - .catch((e) => e) - - expect(error.message).toContain( - "Value for Campaign.campaign_identifier is required, 'undefined' found" - ) - }) - - it("should create a basic campaign successfully", async () => { - const startsAt = new Date("01/01/2024") - const endsAt = new Date("01/01/2025") - const [createdCampaign] = await service.createCampaigns([ - { - name: "test", - campaign_identifier: "test", - starts_at: startsAt, - ends_at: endsAt, - }, - ]) - - const campaign = await service.retrieveCampaign(createdCampaign.id) - - expect(campaign).toEqual( - expect.objectContaining({ - name: "test", - campaign_identifier: "test", - starts_at: startsAt, - ends_at: endsAt, +moduleIntegrationTestRunner({ + moduleName: Modules.PROMOTION, + testSuite: ({ + MikroOrmWrapper, + service, + }: SuiteOptions) => { + describe("Promotion Module Service: Campaigns", () => { + describe("listAndCountCampaigns", () => { + beforeEach(async () => { + await createCampaigns(MikroOrmWrapper.forkManager()) }) - ) - }) - it("should create a campaign with campaign budget successfully", async () => { - const startsAt = new Date("01/01/2024") - const endsAt = new Date("01/01/2025") + it("should return all campaigns and its count", async () => { + const [campaigns, count] = await service.listAndCountCampaigns() - const [createdPromotion] = await service.createCampaigns([ - { - name: "test", - campaign_identifier: "test", - starts_at: startsAt, - ends_at: endsAt, - budget: { - limit: 1000, - type: "usage", - used: 10, - }, - }, - ]) + expect(count).toEqual(2) + expect(campaigns).toEqual([ + { + id: "campaign-id-1", + name: "campaign 1", + description: "test description", + currency: "USD", + campaign_identifier: "test-1", + starts_at: expect.any(Date), + ends_at: expect.any(Date), + budget: expect.any(Object), + created_at: expect.any(Date), + updated_at: expect.any(Date), + deleted_at: null, + }, + { + id: "campaign-id-2", + name: "campaign 1", + description: "test description", + currency: "USD", + campaign_identifier: "test-2", + starts_at: expect.any(Date), + ends_at: expect.any(Date), + budget: expect.any(Object), + created_at: expect.any(Date), + updated_at: expect.any(Date), + deleted_at: null, + }, + ]) + }) - const campaign = await service.retrieveCampaign(createdPromotion.id, { - relations: ["budget"], + it("should return all campaigns based on config select and relations param", async () => { + const [campaigns, count] = await service.listAndCountCampaigns( + { + id: ["campaign-id-1"], + }, + { + relations: ["budget"], + select: ["name", "budget.limit"], + } + ) + + expect(count).toEqual(1) + expect(campaigns).toEqual([ + { + id: "campaign-id-1", + name: "campaign 1", + budget: expect.objectContaining({ + id: expect.any(String), + campaign: expect.any(Object), + limit: 1000, + }), + }, + ]) + }) }) - expect(campaign).toEqual( - expect.objectContaining({ - name: "test", - campaign_identifier: "test", - starts_at: startsAt, - ends_at: endsAt, - budget: expect.objectContaining({ - limit: 1000, - type: "usage", - used: 10, - }), + describe("createCampaigns", () => { + it("should throw an error when required params are not passed", async () => { + const error = await service + .createCampaigns([ + { + name: "test", + } as any, + ]) + .catch((e) => e) + + expect(error.message).toContain( + "Value for Campaign.campaign_identifier is required, 'undefined' found" + ) }) - ) - }) - it("should create a basic campaign with promotions successfully", async () => { - await createPromotions(repositoryManager) + it("should create a basic campaign successfully", async () => { + const startsAt = new Date("01/01/2024") + const endsAt = new Date("01/01/2025") + const [createdCampaign] = await service.createCampaigns([ + { + name: "test", + campaign_identifier: "test", + starts_at: startsAt, + ends_at: endsAt, + }, + ]) - const startsAt = new Date("01/01/2024") - const endsAt = new Date("01/01/2025") - const [createdCampaign] = await service.createCampaigns([ - { - name: "test", - campaign_identifier: "test", - starts_at: startsAt, - ends_at: endsAt, - promotions: [{ id: "promotion-id-1" }, { id: "promotion-id-2" }], - }, - ]) + const campaign = await service.retrieveCampaign(createdCampaign.id) - const campaign = await service.retrieveCampaign(createdCampaign.id, { - relations: ["promotions"], - }) - - expect(campaign).toEqual( - expect.objectContaining({ - name: "test", - campaign_identifier: "test", - starts_at: startsAt, - ends_at: endsAt, - promotions: [ + expect(campaign).toEqual( expect.objectContaining({ - id: "promotion-id-1", - }), + name: "test", + campaign_identifier: "test", + starts_at: startsAt, + ends_at: endsAt, + }) + ) + }) + + it("should create a campaign with campaign budget successfully", async () => { + const startsAt = new Date("01/01/2024") + const endsAt = new Date("01/01/2025") + + const [createdPromotion] = await service.createCampaigns([ + { + name: "test", + campaign_identifier: "test", + starts_at: startsAt, + ends_at: endsAt, + budget: { + limit: 1000, + type: "usage", + used: 10, + }, + }, + ]) + + const campaign = await service.retrieveCampaign(createdPromotion.id, { + relations: ["budget"], + }) + + expect(campaign).toEqual( expect.objectContaining({ - id: "promotion-id-2", - }), - ], + name: "test", + campaign_identifier: "test", + starts_at: startsAt, + ends_at: endsAt, + budget: expect.objectContaining({ + limit: 1000, + type: "usage", + used: 10, + }), + }) + ) }) - ) - }) - }) - describe("updateCampaigns", () => { - it("should throw an error when required params are not passed", async () => { - const error = await service - .updateCampaigns([ - { - name: "test", - } as any, - ]) - .catch((e) => e) + it("should create a basic campaign with promotions successfully", async () => { + await createPromotions(MikroOrmWrapper.forkManager()) - expect(error.message).toContain('Campaign with id "" not found') - }) + const startsAt = new Date("01/01/2024") + const endsAt = new Date("01/01/2025") + const [createdCampaign] = await service.createCampaigns([ + { + name: "test", + campaign_identifier: "test", + starts_at: startsAt, + ends_at: endsAt, + promotions: [{ id: "promotion-id-1" }, { id: "promotion-id-2" }], + }, + ]) - it("should update the attributes of a campaign successfully", async () => { - await createCampaigns(repositoryManager) + const campaign = await service.retrieveCampaign(createdCampaign.id, { + relations: ["promotions"], + }) - const [updatedCampaign] = await service.updateCampaigns([ - { - id: "campaign-id-1", - description: "test description 1", - currency: "EUR", - campaign_identifier: "new", - starts_at: new Date("01/01/2024"), - ends_at: new Date("01/01/2025"), - }, - ]) - - expect(updatedCampaign).toEqual( - expect.objectContaining({ - description: "test description 1", - currency: "EUR", - campaign_identifier: "new", - starts_at: new Date("01/01/2024"), - ends_at: new Date("01/01/2025"), - }) - ) - }) - - it("should update the attributes of a campaign budget successfully", async () => { - await createCampaigns(repositoryManager) - - const [updatedCampaign] = await service.updateCampaigns([ - { - id: "campaign-id-1", - budget: { - limit: 100, - used: 100, - }, - }, - ]) - - expect(updatedCampaign).toEqual( - expect.objectContaining({ - budget: expect.objectContaining({ - limit: 100, - used: 100, - }), - }) - ) - }) - - it("should update promotions of a campaign successfully", async () => { - await createCampaigns(repositoryManager) - await createPromotions(repositoryManager) - - const [updatedCampaign] = await service.updateCampaigns([ - { - id: "campaign-id-1", - description: "test description 1", - currency: "EUR", - campaign_identifier: "new", - starts_at: new Date("01/01/2024"), - ends_at: new Date("01/01/2025"), - promotions: [{ id: "promotion-id-1" }, { id: "promotion-id-2" }], - }, - ]) - - expect(updatedCampaign).toEqual( - expect.objectContaining({ - description: "test description 1", - currency: "EUR", - campaign_identifier: "new", - starts_at: new Date("01/01/2024"), - ends_at: new Date("01/01/2025"), - promotions: [ + expect(campaign).toEqual( expect.objectContaining({ - id: "promotion-id-1", - }), + name: "test", + campaign_identifier: "test", + starts_at: startsAt, + ends_at: endsAt, + promotions: [ + expect.objectContaining({ + id: "promotion-id-1", + }), + expect.objectContaining({ + id: "promotion-id-2", + }), + ], + }) + ) + }) + }) + + describe("updateCampaigns", () => { + it("should throw an error when required params are not passed", async () => { + const error = await service + .updateCampaigns([ + { + name: "test", + } as any, + ]) + .catch((e) => e) + + expect(error.message).toContain('Campaign with id "" not found') + }) + + it("should update the attributes of a campaign successfully", async () => { + await createCampaigns(MikroOrmWrapper.forkManager()) + + const [updatedCampaign] = await service.updateCampaigns([ + { + id: "campaign-id-1", + description: "test description 1", + currency: "EUR", + campaign_identifier: "new", + starts_at: new Date("01/01/2024"), + ends_at: new Date("01/01/2025"), + }, + ]) + + expect(updatedCampaign).toEqual( expect.objectContaining({ - id: "promotion-id-2", - }), - ], + description: "test description 1", + currency: "EUR", + campaign_identifier: "new", + starts_at: new Date("01/01/2024"), + ends_at: new Date("01/01/2025"), + }) + ) }) - ) - }) - it("should remove promotions of the campaign successfully", async () => { - await createCampaigns(repositoryManager) - await createPromotions(repositoryManager) + it("should update the attributes of a campaign budget successfully", async () => { + await createCampaigns(MikroOrmWrapper.forkManager()) - await service.updateCampaigns({ - id: "campaign-id-1", - promotions: [{ id: "promotion-id-1" }, { id: "promotion-id-2" }], - }) + const [updatedCampaign] = await service.updateCampaigns([ + { + id: "campaign-id-1", + budget: { + limit: 100, + used: 100, + }, + }, + ]) - const updatedCampaign = await service.updateCampaigns({ - id: "campaign-id-1", - promotions: [{ id: "promotion-id-1" }], - }) - - expect(updatedCampaign).toEqual( - expect.objectContaining({ - promotions: [ + expect(updatedCampaign).toEqual( expect.objectContaining({ - id: "promotion-id-1", - }), - ], + budget: expect.objectContaining({ + limit: 100, + used: 100, + }), + }) + ) }) - ) - }) - }) - describe("retrieveCampaign", () => { - beforeEach(async () => { - await createCampaigns(repositoryManager) - }) + it("should update promotions of a campaign successfully", async () => { + await createCampaigns(MikroOrmWrapper.forkManager()) + await createPromotions(MikroOrmWrapper.forkManager()) - const id = "campaign-id-1" + const [updatedCampaign] = await service.updateCampaigns([ + { + id: "campaign-id-1", + description: "test description 1", + currency: "EUR", + campaign_identifier: "new", + starts_at: new Date("01/01/2024"), + ends_at: new Date("01/01/2025"), + promotions: [{ id: "promotion-id-1" }, { id: "promotion-id-2" }], + }, + ]) - it("should return campaign for the given id", async () => { - const campaign = await service.retrieveCampaign(id) - - expect(campaign).toEqual( - expect.objectContaining({ - id, + expect(updatedCampaign).toEqual( + expect.objectContaining({ + description: "test description 1", + currency: "EUR", + campaign_identifier: "new", + starts_at: new Date("01/01/2024"), + ends_at: new Date("01/01/2025"), + promotions: [ + expect.objectContaining({ + id: "promotion-id-1", + }), + expect.objectContaining({ + id: "promotion-id-2", + }), + ], + }) + ) }) - ) - }) - it("should throw an error when campaign with id does not exist", async () => { - let error + it("should remove promotions of the campaign successfully", async () => { + await createCampaigns(MikroOrmWrapper.forkManager()) + await createPromotions(MikroOrmWrapper.forkManager()) - try { - await service.retrieveCampaign("does-not-exist") - } catch (e) { - error = e - } + await service.updateCampaigns({ + id: "campaign-id-1", + promotions: [{ id: "promotion-id-1" }, { id: "promotion-id-2" }], + }) - expect(error.message).toEqual( - "Campaign with id: does-not-exist was not found" - ) - }) + const updatedCampaign = await service.updateCampaigns({ + id: "campaign-id-1", + promotions: [{ id: "promotion-id-1" }], + }) - it("should throw an error when a id is not provided", async () => { - let error - - try { - await service.retrieveCampaign(undefined as unknown as string) - } catch (e) { - error = e - } - - expect(error.message).toEqual("campaign - id must be defined") - }) - - it("should return campaign based on config select param", async () => { - const campaign = await service.retrieveCampaign(id, { - select: ["id"], + expect(updatedCampaign).toEqual( + expect.objectContaining({ + promotions: [ + expect.objectContaining({ + id: "promotion-id-1", + }), + ], + }) + ) + }) }) - const serialized = JSON.parse(JSON.stringify(campaign)) + describe("retrieveCampaign", () => { + beforeEach(async () => { + await createCampaigns(MikroOrmWrapper.forkManager()) + }) - expect(serialized).toEqual({ - id, - }) - }) - }) + const id = "campaign-id-1" - describe("deleteCampaigns", () => { - it("should delete the campaigns given an id successfully", async () => { - const [createdCampaign] = await service.createCampaigns([ - { - name: "test", - campaign_identifier: "test", - starts_at: new Date("01/01/2024"), - ends_at: new Date("01/01/2025"), - }, - ]) + it("should return campaign for the given id", async () => { + const campaign = await service.retrieveCampaign(id) - await service.deleteCampaigns([createdCampaign.id]) + expect(campaign).toEqual( + expect.objectContaining({ + id, + }) + ) + }) - const campaigns = await service.listCampaigns( - { - id: [createdCampaign.id], - }, - { withDeleted: true } - ) + it("should throw an error when campaign with id does not exist", async () => { + let error - expect(campaigns).toHaveLength(0) - }) - }) + try { + await service.retrieveCampaign("does-not-exist") + } catch (e) { + error = e + } - describe("softDeleteCampaigns", () => { - it("should soft delete the campaigns given an id successfully", async () => { - const [createdCampaign] = await service.createCampaigns([ - { - name: "test", - campaign_identifier: "test", - starts_at: new Date("01/01/2024"), - ends_at: new Date("01/01/2025"), - }, - ]) + expect(error.message).toEqual( + "Campaign with id: does-not-exist was not found" + ) + }) - await service.softDeleteCampaigns([createdCampaign.id]) + it("should throw an error when a id is not provided", async () => { + let error - const campaigns = await service.listCampaigns({ - id: [createdCampaign.id], + try { + await service.retrieveCampaign(undefined as unknown as string) + } catch (e) { + error = e + } + + expect(error.message).toEqual("campaign - id must be defined") + }) + + it("should return campaign based on config select param", async () => { + const campaign = await service.retrieveCampaign(id, { + select: ["id"], + }) + + const serialized = JSON.parse(JSON.stringify(campaign)) + + expect(serialized).toEqual({ + id, + }) + }) }) - expect(campaigns).toHaveLength(0) + describe("deleteCampaigns", () => { + it("should delete the campaigns given an id successfully", async () => { + const [createdCampaign] = await service.createCampaigns([ + { + name: "test", + campaign_identifier: "test", + starts_at: new Date("01/01/2024"), + ends_at: new Date("01/01/2025"), + }, + ]) + + await service.deleteCampaigns([createdCampaign.id]) + + const campaigns = await service.listCampaigns( + { + id: [createdCampaign.id], + }, + { withDeleted: true } + ) + + expect(campaigns).toHaveLength(0) + }) + }) + + describe("softDeleteCampaigns", () => { + it("should soft delete the campaigns given an id successfully", async () => { + const [createdCampaign] = await service.createCampaigns([ + { + name: "test", + campaign_identifier: "test", + starts_at: new Date("01/01/2024"), + ends_at: new Date("01/01/2025"), + }, + ]) + + await service.softDeleteCampaigns([createdCampaign.id]) + + const campaigns = await service.listCampaigns({ + id: [createdCampaign.id], + }) + + expect(campaigns).toHaveLength(0) + }) + }) + + describe("restoreCampaigns", () => { + it("should restore the campaigns given an id successfully", async () => { + const [createdCampaign] = await service.createCampaigns([ + { + name: "test", + campaign_identifier: "test", + starts_at: new Date("01/01/2024"), + ends_at: new Date("01/01/2025"), + }, + ]) + + await service.softDeleteCampaigns([createdCampaign.id]) + + let campaigns = await service.listCampaigns({ + id: [createdCampaign.id], + }) + + expect(campaigns).toHaveLength(0) + await service.restoreCampaigns([createdCampaign.id]) + + campaigns = await service.listCampaigns({ id: [createdCampaign.id] }) + expect(campaigns).toHaveLength(1) + }) + }) }) - }) - - describe("restoreCampaigns", () => { - it("should restore the campaigns given an id successfully", async () => { - const [createdCampaign] = await service.createCampaigns([ - { - name: "test", - campaign_identifier: "test", - starts_at: new Date("01/01/2024"), - ends_at: new Date("01/01/2025"), - }, - ]) - - await service.softDeleteCampaigns([createdCampaign.id]) - - let campaigns = await service.listCampaigns({ id: [createdCampaign.id] }) - - expect(campaigns).toHaveLength(0) - await service.restoreCampaigns([createdCampaign.id]) - - campaigns = await service.listCampaigns({ id: [createdCampaign.id] }) - expect(campaigns).toHaveLength(1) - }) - }) + }, }) diff --git a/packages/promotion/integration-tests/__tests__/services/promotion-module/compute-actions.spec.ts b/packages/promotion/integration-tests/__tests__/services/promotion-module/compute-actions.spec.ts index 30cea95c96..0bd32d73b0 100644 --- a/packages/promotion/integration-tests/__tests__/services/promotion-module/compute-actions.spec.ts +++ b/packages/promotion/integration-tests/__tests__/services/promotion-module/compute-actions.spec.ts @@ -1,305 +1,21 @@ import { Modules } from "@medusajs/modules-sdk" import { IPromotionModuleService } from "@medusajs/types" import { ApplicationMethodType, PromotionType } from "@medusajs/utils" -import { SqlEntityManager } from "@mikro-orm/postgresql" -import { initModules } from "medusa-test-utils" +import { moduleIntegrationTestRunner, SuiteOptions } from "medusa-test-utils" import { createCampaigns } from "../../../__fixtures__/campaigns" -import { MikroOrmWrapper } from "../../../utils" -import { getInitModuleConfig } from "../../../utils/get-init-module-config" jest.setTimeout(30000) -describe("Promotion Service: computeActions", () => { - let service: IPromotionModuleService - let repositoryManager: SqlEntityManager - let shutdownFunc: () => void - - beforeAll(async () => { - const initModulesConfig = getInitModuleConfig() - - const { medusaApp, shutdown } = await initModules(initModulesConfig) - - service = medusaApp.modules[Modules.PROMOTION] - - shutdownFunc = shutdown - }) - - afterAll(async () => { - shutdownFunc() - }) - - beforeEach(async () => { - await MikroOrmWrapper.setupDatabase() - repositoryManager = MikroOrmWrapper.forkManager() - }) - - afterEach(async () => { - await MikroOrmWrapper.clearDatabase() - }) - - describe("when code is not present in database", () => { - it("should return empty array when promotion does not exist", async () => { - const response = await service.computeActions(["DOES_NOT_EXIST"], { - customer: { - customer_group: { - id: "VIP", - }, - }, - items: [ - { - id: "item_cotton_tshirt", - quantity: 1, - subtotal: 100, - product_category: { - id: "catg_cotton", - }, - product: { - id: "prod_tshirt", - }, - }, - { - id: "item_cotton_sweater", - quantity: 5, - subtotal: 750, - product_category: { - id: "catg_cotton", - }, - product: { - id: "prod_sweater", - }, - }, - ], - }) - - expect(response).toEqual([]) - }) - - it("should throw error when code in items adjustment does not exist", async () => { - const [createdPromotion] = await service.create([ - { - code: "PROMOTION_TEST", - type: PromotionType.STANDARD, - application_method: { - type: "fixed", - target_type: "items", - allocation: "each", - value: 200, - max_quantity: 1, - }, - }, - ]) - - const error = await service - .computeActions(["PROMOTION_TEST"], { - items: [ - { - id: "item_cotton_tshirt", - quantity: 1, - subtotal: 100, - adjustments: [ - { - id: "test-adjustment", - code: "DOES_NOT_EXIST", - }, - ], - }, - { - id: "item_cotton_sweater", - quantity: 5, - subtotal: 750, - }, - ], - }) - .catch((e) => e) - - expect(error.message).toContain( - "Applied Promotion for code (DOES_NOT_EXIST) not found" - ) - }) - - it("should throw error when code in shipping adjustment does not exist", async () => { - const [createdPromotion] = await service.create([ - { - code: "PROMOTION_TEST", - type: PromotionType.STANDARD, - application_method: { - type: "fixed", - target_type: "items", - allocation: "each", - value: 200, - max_quantity: 1, - }, - }, - ]) - - const error = await service - .computeActions(["PROMOTION_TEST"], { - items: [ - { - id: "item_cotton_tshirt", - quantity: 1, - subtotal: 100, - }, - { - id: "item_cotton_sweater", - quantity: 5, - subtotal: 750, - adjustments: [ - { - id: "test-adjustment", - code: "DOES_NOT_EXIST", - }, - ], - }, - ], - }) - .catch((e) => e) - - expect(error.message).toContain( - "Applied Promotion for code (DOES_NOT_EXIST) not found" - ) - }) - }) - - describe("when promotion is for items and allocation is each", () => { - describe("when application type is fixed", () => { - it("should compute the correct item amendments", async () => { - const [createdPromotion] = await service.create([ - { - code: "PROMOTION_TEST", - type: PromotionType.STANDARD, - rules: [ - { - attribute: "customer.customer_group.id", - operator: "in", - values: ["VIP", "top100"], - }, - ], - application_method: { - type: "fixed", - target_type: "items", - allocation: "each", - value: 200, - max_quantity: 1, - target_rules: [ - { - attribute: "product_category.id", - operator: "eq", - values: ["catg_cotton"], - }, - ], - }, - }, - ]) - - const result = await service.computeActions(["PROMOTION_TEST"], { - customer: { - customer_group: { - id: "VIP", - }, - }, - items: [ - { - id: "item_cotton_tshirt", - quantity: 1, - subtotal: 100, - product_category: { - id: "catg_cotton", - }, - product: { - id: "prod_tshirt", - }, - }, - { - id: "item_cotton_sweater", - quantity: 5, - subtotal: 750, - product_category: { - id: "catg_cotton", - }, - product: { - id: "prod_sweater", - }, - }, - ], - }) - - expect(result).toEqual([ - { - action: "addItemAdjustment", - item_id: "item_cotton_tshirt", - amount: 100, - code: "PROMOTION_TEST", - }, - { - action: "addItemAdjustment", - item_id: "item_cotton_sweater", - amount: 150, - code: "PROMOTION_TEST", - }, - ]) - }) - - it("should compute the correct item amendments when there are multiple promotions to apply", async () => { - const [createdPromotion] = await service.create([ - { - code: "PROMOTION_TEST", - type: PromotionType.STANDARD, - rules: [ - { - attribute: "customer.customer_group.id", - operator: "in", - values: ["VIP", "top100"], - }, - ], - application_method: { - type: "fixed", - target_type: "items", - allocation: "each", - value: 30, - max_quantity: 2, - target_rules: [ - { - attribute: "product_category.id", - operator: "eq", - values: ["catg_cotton"], - }, - ], - }, - }, - ]) - - const [createdPromotionTwo] = await service.create([ - { - code: "PROMOTION_TEST_2", - type: PromotionType.STANDARD, - rules: [ - { - attribute: "customer.customer_group.id", - operator: "in", - values: ["VIP", "top100"], - }, - ], - application_method: { - type: "fixed", - target_type: "items", - allocation: "each", - value: 50, - max_quantity: 1, - target_rules: [ - { - attribute: "product_category.id", - operator: "eq", - values: ["catg_cotton"], - }, - ], - }, - }, - ]) - - const result = await service.computeActions( - ["PROMOTION_TEST", "PROMOTION_TEST_2"], - { +moduleIntegrationTestRunner({ + moduleName: Modules.PROMOTION, + testSuite: ({ + MikroOrmWrapper, + service, + }: SuiteOptions) => { + describe("Promotion Service: computeActions", () => { + describe("when code is not present in database", () => { + it("should return empty array when promotion does not exist", async () => { + const response = await service.computeActions(["DOES_NOT_EXIST"], { customer: { customer_group: { id: "VIP", @@ -309,7 +25,7 @@ describe("Promotion Service: computeActions", () => { { id: "item_cotton_tshirt", quantity: 1, - subtotal: 50, + subtotal: 100, product_category: { id: "catg_cotton", }, @@ -319,8 +35,8 @@ describe("Promotion Service: computeActions", () => { }, { id: "item_cotton_sweater", - quantity: 1, - subtotal: 150, + quantity: 5, + subtotal: 750, product_category: { id: "catg_cotton", }, @@ -329,97 +45,4100 @@ describe("Promotion Service: computeActions", () => { }, }, ], - } - ) + }) - expect(result).toEqual([ - { - action: "addItemAdjustment", - item_id: "item_cotton_tshirt", - amount: 30, - code: "PROMOTION_TEST", - }, - { - action: "addItemAdjustment", - item_id: "item_cotton_sweater", - amount: 30, - code: "PROMOTION_TEST", - }, - { - action: "addItemAdjustment", - item_id: "item_cotton_tshirt", - amount: 20, - code: "PROMOTION_TEST_2", - }, - { - action: "addItemAdjustment", - item_id: "item_cotton_sweater", - amount: 50, - code: "PROMOTION_TEST_2", - }, - ]) + expect(response).toEqual([]) + }) + + it("should throw error when code in items adjustment does not exist", async () => { + const [createdPromotion] = await service.create([ + { + code: "PROMOTION_TEST", + type: PromotionType.STANDARD, + application_method: { + type: "fixed", + target_type: "items", + allocation: "each", + value: 200, + max_quantity: 1, + }, + }, + ]) + + const error = await service + .computeActions(["PROMOTION_TEST"], { + items: [ + { + id: "item_cotton_tshirt", + quantity: 1, + subtotal: 100, + adjustments: [ + { + id: "test-adjustment", + code: "DOES_NOT_EXIST", + }, + ], + }, + { + id: "item_cotton_sweater", + quantity: 5, + subtotal: 750, + }, + ], + }) + .catch((e) => e) + + expect(error.message).toContain( + "Applied Promotion for code (DOES_NOT_EXIST) not found" + ) + }) + + it("should throw error when code in shipping adjustment does not exist", async () => { + const [createdPromotion] = await service.create([ + { + code: "PROMOTION_TEST", + type: PromotionType.STANDARD, + application_method: { + type: "fixed", + target_type: "items", + allocation: "each", + value: 200, + max_quantity: 1, + }, + }, + ]) + + const error = await service + .computeActions(["PROMOTION_TEST"], { + items: [ + { + id: "item_cotton_tshirt", + quantity: 1, + subtotal: 100, + }, + { + id: "item_cotton_sweater", + quantity: 5, + subtotal: 750, + adjustments: [ + { + id: "test-adjustment", + code: "DOES_NOT_EXIST", + }, + ], + }, + ], + }) + .catch((e) => e) + + expect(error.message).toContain( + "Applied Promotion for code (DOES_NOT_EXIST) not found" + ) + }) }) - it("should not compute actions when applicable total is 0", async () => { - const [createdPromotion] = await service.create([ - { - code: "PROMOTION_TEST", - type: PromotionType.STANDARD, - rules: [ + describe("when promotion is for items and allocation is each", () => { + describe("when application type is fixed", () => { + it("should compute the correct item amendments", async () => { + const [createdPromotion] = await service.create([ { - attribute: "customer.customer_group.id", - operator: "in", - values: ["VIP", "top100"], + code: "PROMOTION_TEST", + type: PromotionType.STANDARD, + rules: [ + { + attribute: "customer.customer_group.id", + operator: "in", + values: ["VIP", "top100"], + }, + ], + application_method: { + type: "fixed", + target_type: "items", + allocation: "each", + value: 200, + max_quantity: 1, + target_rules: [ + { + attribute: "product_category.id", + operator: "eq", + values: ["catg_cotton"], + }, + ], + }, }, - ], - application_method: { - type: "fixed", - target_type: "items", - allocation: "each", - value: 500, - max_quantity: 2, - target_rules: [ + ]) + + const result = await service.computeActions(["PROMOTION_TEST"], { + customer: { + customer_group: { + id: "VIP", + }, + }, + items: [ { - attribute: "product_category.id", - operator: "eq", - values: ["catg_cotton"], + id: "item_cotton_tshirt", + quantity: 1, + subtotal: 100, + product_category: { + id: "catg_cotton", + }, + product: { + id: "prod_tshirt", + }, + }, + { + id: "item_cotton_sweater", + quantity: 5, + subtotal: 750, + product_category: { + id: "catg_cotton", + }, + product: { + id: "prod_sweater", + }, }, ], - }, - }, - ]) + }) - const [createdPromotionTwo] = await service.create([ - { - code: "PROMOTION_TEST_2", - type: PromotionType.STANDARD, - rules: [ + expect(result).toEqual([ { - attribute: "customer.customer_group.id", - operator: "in", - values: ["VIP", "top100"], + action: "addItemAdjustment", + item_id: "item_cotton_tshirt", + amount: 100, + code: "PROMOTION_TEST", }, - ], - application_method: { - type: "fixed", - target_type: "items", - allocation: "each", - value: 50, - max_quantity: 1, - target_rules: [ + { + action: "addItemAdjustment", + item_id: "item_cotton_sweater", + amount: 150, + code: "PROMOTION_TEST", + }, + ]) + }) + + it("should compute the correct item amendments when there are multiple promotions to apply", async () => { + const [createdPromotion] = await service.create([ + { + code: "PROMOTION_TEST", + type: PromotionType.STANDARD, + rules: [ + { + attribute: "customer.customer_group.id", + operator: "in", + values: ["VIP", "top100"], + }, + ], + application_method: { + type: "fixed", + target_type: "items", + allocation: "each", + value: 30, + max_quantity: 2, + target_rules: [ + { + attribute: "product_category.id", + operator: "eq", + values: ["catg_cotton"], + }, + ], + }, + }, + ]) + + const [createdPromotionTwo] = await service.create([ + { + code: "PROMOTION_TEST_2", + type: PromotionType.STANDARD, + rules: [ + { + attribute: "customer.customer_group.id", + operator: "in", + values: ["VIP", "top100"], + }, + ], + application_method: { + type: "fixed", + target_type: "items", + allocation: "each", + value: 50, + max_quantity: 1, + target_rules: [ + { + attribute: "product_category.id", + operator: "eq", + values: ["catg_cotton"], + }, + ], + }, + }, + ]) + + const result = await service.computeActions( + ["PROMOTION_TEST", "PROMOTION_TEST_2"], + { + customer: { + customer_group: { + id: "VIP", + }, + }, + items: [ + { + id: "item_cotton_tshirt", + quantity: 1, + subtotal: 50, + product_category: { + id: "catg_cotton", + }, + product: { + id: "prod_tshirt", + }, + }, + { + id: "item_cotton_sweater", + quantity: 1, + subtotal: 150, + product_category: { + id: "catg_cotton", + }, + product: { + id: "prod_sweater", + }, + }, + ], + } + ) + + expect(result).toEqual([ + { + action: "addItemAdjustment", + item_id: "item_cotton_tshirt", + amount: 30, + code: "PROMOTION_TEST", + }, + { + action: "addItemAdjustment", + item_id: "item_cotton_sweater", + amount: 30, + code: "PROMOTION_TEST", + }, + { + action: "addItemAdjustment", + item_id: "item_cotton_tshirt", + amount: 20, + code: "PROMOTION_TEST_2", + }, + { + action: "addItemAdjustment", + item_id: "item_cotton_sweater", + amount: 50, + code: "PROMOTION_TEST_2", + }, + ]) + }) + + it("should not compute actions when applicable total is 0", async () => { + const [createdPromotion] = await service.create([ + { + code: "PROMOTION_TEST", + type: PromotionType.STANDARD, + rules: [ + { + attribute: "customer.customer_group.id", + operator: "in", + values: ["VIP", "top100"], + }, + ], + application_method: { + type: "fixed", + target_type: "items", + allocation: "each", + value: 500, + max_quantity: 2, + target_rules: [ + { + attribute: "product_category.id", + operator: "eq", + values: ["catg_cotton"], + }, + ], + }, + }, + ]) + + const [createdPromotionTwo] = await service.create([ + { + code: "PROMOTION_TEST_2", + type: PromotionType.STANDARD, + rules: [ + { + attribute: "customer.customer_group.id", + operator: "in", + values: ["VIP", "top100"], + }, + ], + application_method: { + type: "fixed", + target_type: "items", + allocation: "each", + value: 50, + max_quantity: 1, + target_rules: [ + { + attribute: "product_category.id", + operator: "eq", + values: ["catg_cotton"], + }, + ], + }, + }, + ]) + + const result = await service.computeActions( + ["PROMOTION_TEST", "PROMOTION_TEST_2"], + { + customer: { + customer_group: { + id: "VIP", + }, + }, + items: [ + { + id: "item_cotton_tshirt", + quantity: 1, + subtotal: 50, + product_category: { + id: "catg_cotton", + }, + product: { + id: "prod_tshirt", + }, + }, + { + id: "item_cotton_sweater", + quantity: 1, + subtotal: 150, + product_category: { + id: "catg_cotton", + }, + product: { + id: "prod_sweater", + }, + }, + ], + } + ) + + expect(result).toEqual([ + { + action: "addItemAdjustment", + item_id: "item_cotton_tshirt", + amount: 50, + code: "PROMOTION_TEST", + }, + { + action: "addItemAdjustment", + item_id: "item_cotton_sweater", + amount: 150, + code: "PROMOTION_TEST", + }, + ]) + }) + + it("should compute budget exceeded action when applicable total exceeds campaign budget for type spend", async () => { + await createCampaigns(MikroOrmWrapper.forkManager()) + + const [createdPromotion] = await service.create([ + { + code: "PROMOTION_TEST", + type: PromotionType.STANDARD, + rules: [ + { + attribute: "customer.customer_group.id", + operator: "in", + values: ["VIP", "top100"], + }, + ], + campaign_id: "campaign-id-1", + application_method: { + type: "fixed", + target_type: "items", + allocation: "each", + value: 500, + max_quantity: 5, + target_rules: [ + { + attribute: "product_category.id", + operator: "eq", + values: ["catg_cotton"], + }, + ], + }, + }, + ]) + + const result = await service.computeActions(["PROMOTION_TEST"], { + customer: { + customer_group: { + id: "VIP", + }, + }, + items: [ { - attribute: "product_category.id", - operator: "eq", - values: ["catg_cotton"], + id: "item_cotton_tshirt", + quantity: 5, + subtotal: 5000, + product_category: { + id: "catg_cotton", + }, + product: { + id: "prod_tshirt", + }, }, ], - }, - }, - ]) + }) - const result = await service.computeActions( - ["PROMOTION_TEST", "PROMOTION_TEST_2"], - { + expect(result).toEqual([ + { + action: "addItemAdjustment", + item_id: "item_cotton_tshirt", + amount: 500, + code: "PROMOTION_TEST", + }, + ]) + }) + + it("should compute budget exceeded action when applicable total exceeds campaign budget for type usage", async () => { + await createCampaigns(MikroOrmWrapper.forkManager()) + + const [createdPromotion] = await service.create([ + { + code: "PROMOTION_TEST", + type: PromotionType.STANDARD, + rules: [ + { + attribute: "customer.customer_group.id", + operator: "in", + values: ["VIP", "top100"], + }, + ], + campaign_id: "campaign-id-2", + application_method: { + type: "fixed", + target_type: "items", + allocation: "each", + value: 500, + max_quantity: 5, + target_rules: [ + { + attribute: "product_category.id", + operator: "eq", + values: ["catg_cotton"], + }, + ], + }, + }, + ]) + + await service.updateCampaigns({ + id: "campaign-id-2", + budget: { used: 1000 }, + }) + + const result = await service.computeActions(["PROMOTION_TEST"], { + customer: { + customer_group: { + id: "VIP", + }, + }, + items: [ + { + id: "item_cotton_tshirt", + quantity: 5, + subtotal: 5000, + product_category: { + id: "catg_cotton", + }, + product: { + id: "prod_tshirt", + }, + }, + ], + }) + + expect(result).toEqual([ + { action: "campaignBudgetExceeded", code: "PROMOTION_TEST" }, + ]) + }) + }) + + describe("when application type is percentage", () => { + it("should compute the correct item amendments", async () => { + const [createdPromotion] = await service.create([ + { + code: "PROMOTION_TEST", + type: PromotionType.STANDARD, + rules: [ + { + attribute: "customer.customer_group.id", + operator: "in", + values: ["VIP", "top100"], + }, + ], + application_method: { + type: ApplicationMethodType.PERCENTAGE, + target_type: "items", + allocation: "each", + value: 10, + max_quantity: 1, + target_rules: [ + { + attribute: "product_category.id", + operator: "eq", + values: ["catg_cotton"], + }, + ], + }, + }, + ]) + + const result = await service.computeActions(["PROMOTION_TEST"], { + customer: { + customer_group: { + id: "VIP", + }, + }, + items: [ + { + id: "item_cotton_tshirt", + quantity: 1, + subtotal: 100, + product_category: { + id: "catg_cotton", + }, + product: { + id: "prod_tshirt", + }, + }, + { + id: "item_cotton_sweater", + quantity: 5, + subtotal: 750, + product_category: { + id: "catg_cotton", + }, + product: { + id: "prod_sweater", + }, + }, + ], + }) + + expect(result).toEqual([ + { + action: "addItemAdjustment", + item_id: "item_cotton_tshirt", + amount: 10, + code: "PROMOTION_TEST", + }, + { + action: "addItemAdjustment", + item_id: "item_cotton_sweater", + amount: 15, + code: "PROMOTION_TEST", + }, + ]) + }) + + it("should compute the correct item amendments when there are multiple promotions to apply", async () => { + const [createdPromotion] = await service.create([ + { + code: "PROMOTION_TEST", + type: PromotionType.STANDARD, + rules: [ + { + attribute: "customer.customer_group.id", + operator: "in", + values: ["VIP", "top100"], + }, + ], + application_method: { + type: ApplicationMethodType.PERCENTAGE, + target_type: "items", + allocation: "each", + value: 30, + max_quantity: 2, + target_rules: [ + { + attribute: "product_category.id", + operator: "eq", + values: ["catg_cotton"], + }, + ], + }, + }, + ]) + + const [createdPromotionTwo] = await service.create([ + { + code: "PROMOTION_TEST_2", + type: PromotionType.STANDARD, + rules: [ + { + attribute: "customer.customer_group.id", + operator: "in", + values: ["VIP", "top100"], + }, + ], + application_method: { + type: ApplicationMethodType.PERCENTAGE, + target_type: "items", + allocation: "each", + value: 10, + max_quantity: 1, + target_rules: [ + { + attribute: "product_category.id", + operator: "eq", + values: ["catg_cotton"], + }, + ], + }, + }, + ]) + + const result = await service.computeActions( + ["PROMOTION_TEST", "PROMOTION_TEST_2"], + { + customer: { + customer_group: { + id: "VIP", + }, + }, + items: [ + { + id: "item_cotton_tshirt", + quantity: 3, + subtotal: 150, + product_category: { + id: "catg_cotton", + }, + product: { + id: "prod_tshirt", + }, + }, + { + id: "item_cotton_sweater", + quantity: 1, + subtotal: 150, + product_category: { + id: "catg_cotton", + }, + product: { + id: "prod_sweater", + }, + }, + ], + } + ) + + expect(result).toEqual([ + { + action: "addItemAdjustment", + item_id: "item_cotton_tshirt", + amount: 30, + code: "PROMOTION_TEST", + }, + { + action: "addItemAdjustment", + item_id: "item_cotton_sweater", + amount: 45, + code: "PROMOTION_TEST", + }, + { + action: "addItemAdjustment", + item_id: "item_cotton_tshirt", + amount: 2, + code: "PROMOTION_TEST_2", + }, + { + action: "addItemAdjustment", + item_id: "item_cotton_sweater", + amount: 10.5, + code: "PROMOTION_TEST_2", + }, + ]) + }) + + it("should not compute actions when applicable total is 0", async () => { + const [createdPromotion] = await service.create([ + { + code: "PROMOTION_TEST", + type: PromotionType.STANDARD, + rules: [ + { + attribute: "customer.customer_group.id", + operator: "in", + values: ["VIP", "top100"], + }, + ], + application_method: { + type: ApplicationMethodType.PERCENTAGE, + target_type: "items", + allocation: "each", + value: 100, + max_quantity: 10, + target_rules: [ + { + attribute: "product_category.id", + operator: "eq", + values: ["catg_cotton"], + }, + ], + }, + }, + ]) + + const [createdPromotionTwo] = await service.create([ + { + code: "PROMOTION_TEST_2", + type: PromotionType.STANDARD, + rules: [ + { + attribute: "customer.customer_group.id", + operator: "in", + values: ["VIP", "top100"], + }, + ], + application_method: { + type: ApplicationMethodType.PERCENTAGE, + target_type: "items", + allocation: "each", + value: 50, + max_quantity: 10, + target_rules: [ + { + attribute: "product_category.id", + operator: "eq", + values: ["catg_cotton"], + }, + ], + }, + }, + ]) + + const result = await service.computeActions( + ["PROMOTION_TEST", "PROMOTION_TEST_2"], + { + customer: { + customer_group: { + id: "VIP", + }, + }, + items: [ + { + id: "item_cotton_tshirt", + quantity: 1, + subtotal: 50, + product_category: { + id: "catg_cotton", + }, + product: { + id: "prod_tshirt", + }, + }, + { + id: "item_cotton_sweater", + quantity: 1, + subtotal: 150, + product_category: { + id: "catg_cotton", + }, + product: { + id: "prod_sweater", + }, + }, + ], + } + ) + + expect(result).toEqual([ + { + action: "addItemAdjustment", + item_id: "item_cotton_tshirt", + amount: 50, + code: "PROMOTION_TEST", + }, + { + action: "addItemAdjustment", + item_id: "item_cotton_sweater", + amount: 150, + code: "PROMOTION_TEST", + }, + ]) + }) + + it("should compute budget exceeded action when applicable total exceeds campaign budget for type spend", async () => { + await createCampaigns(MikroOrmWrapper.forkManager()) + + const [createdPromotion] = await service.create([ + { + code: "PROMOTION_TEST", + type: PromotionType.STANDARD, + rules: [ + { + attribute: "customer.customer_group.id", + operator: "in", + values: ["VIP", "top100"], + }, + ], + campaign_id: "campaign-id-1", + application_method: { + type: ApplicationMethodType.PERCENTAGE, + target_type: "items", + allocation: "each", + value: 100, + max_quantity: 5, + target_rules: [ + { + attribute: "product_category.id", + operator: "eq", + values: ["catg_cotton"], + }, + ], + }, + }, + ]) + + const result = await service.computeActions(["PROMOTION_TEST"], { + customer: { + customer_group: { + id: "VIP", + }, + }, + items: [ + { + id: "item_cotton_tshirt", + quantity: 5, + subtotal: 10000, + product_category: { + id: "catg_cotton", + }, + product: { + id: "prod_tshirt", + }, + }, + ], + }) + + expect(result).toEqual([ + { action: "campaignBudgetExceeded", code: "PROMOTION_TEST" }, + ]) + }) + + it("should compute budget exceeded action when applicable total exceeds campaign budget for type usage", async () => { + await createCampaigns(MikroOrmWrapper.forkManager()) + + const [createdPromotion] = await service.create([ + { + code: "PROMOTION_TEST", + type: PromotionType.STANDARD, + rules: [ + { + attribute: "customer.customer_group.id", + operator: "in", + values: ["VIP", "top100"], + }, + ], + campaign_id: "campaign-id-2", + application_method: { + type: ApplicationMethodType.PERCENTAGE, + target_type: "items", + allocation: "each", + value: 10, + max_quantity: 5, + target_rules: [ + { + attribute: "product_category.id", + operator: "eq", + values: ["catg_cotton"], + }, + ], + }, + }, + ]) + + await service.updateCampaigns({ + id: "campaign-id-2", + budget: { used: 1000 }, + }) + + const result = await service.computeActions(["PROMOTION_TEST"], { + customer: { + customer_group: { + id: "VIP", + }, + }, + items: [ + { + id: "item_cotton_tshirt", + quantity: 5, + subtotal: 5000, + product_category: { + id: "catg_cotton", + }, + product: { + id: "prod_tshirt", + }, + }, + ], + }) + + expect(result).toEqual([ + { action: "campaignBudgetExceeded", code: "PROMOTION_TEST" }, + ]) + }) + }) + }) + + describe("when promotion is for items and allocation is across", () => { + describe("when application type is fixed", () => { + it("should compute the correct item amendments", async () => { + const [createdPromotion] = await service.create([ + { + code: "PROMOTION_TEST", + type: PromotionType.STANDARD, + rules: [ + { + attribute: "customer.customer_group.id", + operator: "in", + values: ["VIP", "top100"], + }, + ], + application_method: { + type: "fixed", + target_type: "items", + allocation: "across", + value: 400, + target_rules: [ + { + attribute: "product_category.id", + operator: "eq", + values: ["catg_cotton"], + }, + ], + }, + }, + ]) + + const result = await service.computeActions(["PROMOTION_TEST"], { + customer: { + customer_group: { + id: "VIP", + }, + }, + items: [ + { + id: "item_cotton_tshirt", + quantity: 2, + subtotal: 200, + product_category: { + id: "catg_cotton", + }, + product: { + id: "prod_tshirt", + }, + }, + { + id: "item_cotton_sweater", + quantity: 2, + subtotal: 600, + product_category: { + id: "catg_cotton", + }, + product: { + id: "prod_sweater", + }, + }, + ], + }) + + expect(result).toEqual([ + { + action: "addItemAdjustment", + item_id: "item_cotton_tshirt", + amount: 100, + code: "PROMOTION_TEST", + }, + { + action: "addItemAdjustment", + item_id: "item_cotton_sweater", + amount: 300, + code: "PROMOTION_TEST", + }, + ]) + }) + + it("should compute the correct item amendments when promotion is automatic", async () => { + const [createdPromotion] = await service.create([ + { + code: "PROMOTION_TEST", + type: PromotionType.STANDARD, + is_automatic: true, + rules: [ + { + attribute: "customer.customer_group.id", + operator: "in", + values: ["VIP", "top100"], + }, + ], + application_method: { + type: "fixed", + target_type: "items", + allocation: "across", + value: 400, + target_rules: [ + { + attribute: "product_category.id", + operator: "eq", + values: ["catg_cotton"], + }, + ], + }, + }, + ]) + + const result = await service.computeActions([], { + customer: { + customer_group: { + id: "VIP", + }, + }, + items: [ + { + id: "item_cotton_tshirt", + quantity: 2, + subtotal: 200, + product_category: { + id: "catg_cotton", + }, + product: { + id: "prod_tshirt", + }, + }, + { + id: "item_cotton_sweater", + quantity: 2, + subtotal: 600, + product_category: { + id: "catg_cotton", + }, + product: { + id: "prod_sweater", + }, + }, + ], + }) + + expect(result).toEqual([ + { + action: "addItemAdjustment", + item_id: "item_cotton_tshirt", + amount: 100, + code: "PROMOTION_TEST", + }, + { + action: "addItemAdjustment", + item_id: "item_cotton_sweater", + amount: 300, + code: "PROMOTION_TEST", + }, + ]) + }) + + it("should compute the correct item amendments when there are multiple promotions to apply", async () => { + const [createdPromotion] = await service.create([ + { + code: "PROMOTION_TEST", + type: PromotionType.STANDARD, + rules: [ + { + attribute: "customer.customer_group.id", + operator: "in", + values: ["VIP", "top100"], + }, + ], + application_method: { + type: "fixed", + target_type: "items", + allocation: "across", + value: 30, + target_rules: [ + { + attribute: "product_category.id", + operator: "eq", + values: ["catg_cotton"], + }, + ], + }, + }, + ]) + + const [createdPromotionTwo] = await service.create([ + { + code: "PROMOTION_TEST_2", + type: PromotionType.STANDARD, + rules: [ + { + attribute: "customer.customer_group.id", + operator: "in", + values: ["VIP", "top100"], + }, + ], + application_method: { + type: "fixed", + target_type: "items", + allocation: "across", + value: 50, + target_rules: [ + { + attribute: "product_category.id", + operator: "eq", + values: ["catg_cotton"], + }, + ], + }, + }, + ]) + + const result = await service.computeActions( + ["PROMOTION_TEST", "PROMOTION_TEST_2"], + { + customer: { + customer_group: { + id: "VIP", + }, + }, + items: [ + { + id: "item_cotton_tshirt", + quantity: 1, + subtotal: 50, + product_category: { + id: "catg_cotton", + }, + product: { + id: "prod_tshirt", + }, + }, + { + id: "item_cotton_sweater", + quantity: 1, + subtotal: 150, + product_category: { + id: "catg_cotton", + }, + product: { + id: "prod_sweater", + }, + }, + ], + } + ) + + expect(result).toEqual([ + { + action: "addItemAdjustment", + item_id: "item_cotton_tshirt", + amount: 7.5, + code: "PROMOTION_TEST", + }, + { + action: "addItemAdjustment", + item_id: "item_cotton_sweater", + amount: 22.5, + code: "PROMOTION_TEST", + }, + { + action: "addItemAdjustment", + item_id: "item_cotton_tshirt", + amount: 12.5, + code: "PROMOTION_TEST_2", + }, + { + action: "addItemAdjustment", + item_id: "item_cotton_sweater", + amount: 37.5, + code: "PROMOTION_TEST_2", + }, + ]) + }) + + it("should not compute actions when applicable total is 0", async () => { + const [createdPromotion] = await service.create([ + { + code: "PROMOTION_TEST", + type: PromotionType.STANDARD, + rules: [ + { + attribute: "customer.customer_group.id", + operator: "in", + values: ["VIP", "top100"], + }, + ], + application_method: { + type: "fixed", + target_type: "items", + allocation: "across", + value: 1000, + target_rules: [ + { + attribute: "product_category.id", + operator: "eq", + values: ["catg_cotton"], + }, + ], + }, + }, + ]) + + const [createdPromotionTwo] = await service.create([ + { + code: "PROMOTION_TEST_2", + type: PromotionType.STANDARD, + rules: [ + { + attribute: "customer.customer_group.id", + operator: "in", + values: ["VIP", "top100"], + }, + ], + application_method: { + type: "fixed", + target_type: "items", + allocation: "across", + value: 50, + target_rules: [ + { + attribute: "product_category.id", + operator: "eq", + values: ["catg_cotton"], + }, + ], + }, + }, + ]) + + const result = await service.computeActions( + ["PROMOTION_TEST", "PROMOTION_TEST_2"], + { + customer: { + customer_group: { + id: "VIP", + }, + }, + items: [ + { + id: "item_cotton_tshirt", + quantity: 1, + subtotal: 50, + product_category: { + id: "catg_cotton", + }, + product: { + id: "prod_tshirt", + }, + }, + { + id: "item_cotton_sweater", + quantity: 1, + subtotal: 150, + product_category: { + id: "catg_cotton", + }, + product: { + id: "prod_sweater", + }, + }, + ], + } + ) + + expect(result).toEqual([ + { + action: "addItemAdjustment", + item_id: "item_cotton_tshirt", + amount: 50, + code: "PROMOTION_TEST", + }, + { + action: "addItemAdjustment", + item_id: "item_cotton_sweater", + amount: 150, + code: "PROMOTION_TEST", + }, + ]) + }) + + it("should compute budget exceeded action when applicable total exceeds campaign budget for type spend", async () => { + await createCampaigns(MikroOrmWrapper.forkManager()) + + const [createdPromotion] = await service.create([ + { + code: "PROMOTION_TEST", + type: PromotionType.STANDARD, + rules: [ + { + attribute: "customer.customer_group.id", + operator: "in", + values: ["VIP", "top100"], + }, + ], + campaign_id: "campaign-id-1", + application_method: { + type: "fixed", + target_type: "items", + allocation: "across", + value: 1500, + target_rules: [ + { + attribute: "product_category.id", + operator: "eq", + values: ["catg_cotton"], + }, + ], + }, + }, + ]) + + const result = await service.computeActions(["PROMOTION_TEST"], { + customer: { + customer_group: { + id: "VIP", + }, + }, + items: [ + { + id: "item_cotton_tshirt", + quantity: 5, + subtotal: 5000, + product_category: { + id: "catg_cotton", + }, + product: { + id: "prod_tshirt", + }, + }, + ], + }) + + expect(result).toEqual([ + { action: "campaignBudgetExceeded", code: "PROMOTION_TEST" }, + ]) + }) + + it("should compute budget exceeded action when applicable total exceeds campaign budget for type usage", async () => { + await createCampaigns(MikroOrmWrapper.forkManager()) + + const [createdPromotion] = await service.create([ + { + code: "PROMOTION_TEST", + type: PromotionType.STANDARD, + rules: [ + { + attribute: "customer.customer_group.id", + operator: "in", + values: ["VIP", "top100"], + }, + ], + campaign_id: "campaign-id-2", + application_method: { + type: "fixed", + target_type: "items", + allocation: "across", + value: 500, + target_rules: [ + { + attribute: "product_category.id", + operator: "eq", + values: ["catg_cotton"], + }, + ], + }, + }, + ]) + + await service.updateCampaigns({ + id: "campaign-id-2", + budget: { used: 1000 }, + }) + + const result = await service.computeActions(["PROMOTION_TEST"], { + customer: { + customer_group: { + id: "VIP", + }, + }, + items: [ + { + id: "item_cotton_tshirt", + quantity: 5, + subtotal: 5000, + product_category: { + id: "catg_cotton", + }, + product: { + id: "prod_tshirt", + }, + }, + ], + }) + + expect(result).toEqual([ + { action: "campaignBudgetExceeded", code: "PROMOTION_TEST" }, + ]) + }) + }) + + describe("when application type is percentage", () => { + it("should compute the correct item amendments", async () => { + const [createdPromotion] = await service.create([ + { + code: "PROMOTION_TEST", + type: PromotionType.STANDARD, + rules: [ + { + attribute: "customer.customer_group.id", + operator: "in", + values: ["VIP", "top100"], + }, + ], + application_method: { + type: ApplicationMethodType.PERCENTAGE, + target_type: "items", + allocation: "across", + value: 10, + target_rules: [ + { + attribute: "product_category.id", + operator: "eq", + values: ["catg_cotton"], + }, + ], + }, + }, + ]) + + const result = await service.computeActions(["PROMOTION_TEST"], { + customer: { + customer_group: { + id: "VIP", + }, + }, + items: [ + { + id: "item_cotton_tshirt", + quantity: 2, + subtotal: 200, + product_category: { + id: "catg_cotton", + }, + product: { + id: "prod_tshirt", + }, + }, + { + id: "item_cotton_sweater", + quantity: 2, + subtotal: 600, + product_category: { + id: "catg_cotton", + }, + product: { + id: "prod_sweater", + }, + }, + ], + }) + + expect(result).toEqual([ + { + action: "addItemAdjustment", + item_id: "item_cotton_tshirt", + amount: 20, + code: "PROMOTION_TEST", + }, + { + action: "addItemAdjustment", + item_id: "item_cotton_sweater", + amount: 60, + code: "PROMOTION_TEST", + }, + ]) + }) + + it("should compute the correct item amendments when promotion is automatic", async () => { + const [createdPromotion] = await service.create([ + { + code: "PROMOTION_TEST", + type: PromotionType.STANDARD, + is_automatic: true, + rules: [ + { + attribute: "customer.customer_group.id", + operator: "in", + values: ["VIP", "top100"], + }, + ], + application_method: { + type: ApplicationMethodType.PERCENTAGE, + target_type: "items", + allocation: "across", + value: 10, + target_rules: [ + { + attribute: "product_category.id", + operator: "eq", + values: ["catg_cotton"], + }, + ], + }, + }, + ]) + + const result = await service.computeActions([], { + customer: { + customer_group: { + id: "VIP", + }, + }, + items: [ + { + id: "item_cotton_tshirt", + quantity: 2, + subtotal: 200, + product_category: { + id: "catg_cotton", + }, + product: { + id: "prod_tshirt", + }, + }, + { + id: "item_cotton_sweater", + quantity: 2, + subtotal: 600, + product_category: { + id: "catg_cotton", + }, + product: { + id: "prod_sweater", + }, + }, + ], + }) + + expect(result).toEqual([ + { + action: "addItemAdjustment", + item_id: "item_cotton_tshirt", + amount: 20, + code: "PROMOTION_TEST", + }, + { + action: "addItemAdjustment", + item_id: "item_cotton_sweater", + amount: 60, + code: "PROMOTION_TEST", + }, + ]) + }) + + it("should compute the correct item amendments when there are multiple promotions to apply", async () => { + const [createdPromotion] = await service.create([ + { + code: "PROMOTION_TEST", + type: PromotionType.STANDARD, + rules: [ + { + attribute: "customer.customer_group.id", + operator: "in", + values: ["VIP", "top100"], + }, + ], + application_method: { + type: ApplicationMethodType.PERCENTAGE, + target_type: "items", + allocation: "across", + value: 10, + target_rules: [ + { + attribute: "product_category.id", + operator: "eq", + values: ["catg_cotton"], + }, + ], + }, + }, + ]) + + const [createdPromotionTwo] = await service.create([ + { + code: "PROMOTION_TEST_2", + type: PromotionType.STANDARD, + rules: [ + { + attribute: "customer.customer_group.id", + operator: "in", + values: ["VIP", "top100"], + }, + ], + application_method: { + type: ApplicationMethodType.PERCENTAGE, + target_type: "items", + allocation: "across", + value: 10, + target_rules: [ + { + attribute: "product_category.id", + operator: "eq", + values: ["catg_cotton"], + }, + ], + }, + }, + ]) + + const result = await service.computeActions( + ["PROMOTION_TEST", "PROMOTION_TEST_2"], + { + customer: { + customer_group: { + id: "VIP", + }, + }, + items: [ + { + id: "item_cotton_tshirt", + quantity: 1, + subtotal: 50, + product_category: { + id: "catg_cotton", + }, + product: { + id: "prod_tshirt", + }, + }, + { + id: "item_cotton_sweater", + quantity: 1, + subtotal: 150, + product_category: { + id: "catg_cotton", + }, + product: { + id: "prod_sweater", + }, + }, + ], + } + ) + + expect(result).toEqual([ + { + action: "addItemAdjustment", + item_id: "item_cotton_tshirt", + amount: 5, + code: "PROMOTION_TEST", + }, + { + action: "addItemAdjustment", + item_id: "item_cotton_sweater", + amount: 15, + code: "PROMOTION_TEST", + }, + { + action: "addItemAdjustment", + item_id: "item_cotton_tshirt", + amount: 4.5, + code: "PROMOTION_TEST_2", + }, + { + action: "addItemAdjustment", + item_id: "item_cotton_sweater", + amount: 13.5, + code: "PROMOTION_TEST_2", + }, + ]) + }) + + it("should not compute actions when applicable total is 0", async () => { + const [createdPromotion] = await service.create([ + { + code: "PROMOTION_TEST", + type: PromotionType.STANDARD, + rules: [ + { + attribute: "customer.customer_group.id", + operator: "in", + values: ["VIP", "top100"], + }, + ], + application_method: { + type: ApplicationMethodType.PERCENTAGE, + target_type: "items", + allocation: "across", + value: 10, + target_rules: [ + { + attribute: "product_category.id", + operator: "eq", + values: ["catg_cotton"], + }, + ], + }, + }, + ]) + + const [createdPromotionTwo] = await service.create([ + { + code: "PROMOTION_TEST_2", + type: PromotionType.STANDARD, + rules: [ + { + attribute: "customer.customer_group.id", + operator: "in", + values: ["VIP", "top100"], + }, + ], + application_method: { + type: ApplicationMethodType.PERCENTAGE, + target_type: "items", + allocation: "across", + value: 10, + target_rules: [ + { + attribute: "product_category.id", + operator: "eq", + values: ["catg_cotton"], + }, + ], + }, + }, + ]) + + const result = await service.computeActions( + ["PROMOTION_TEST", "PROMOTION_TEST_2"], + { + customer: { + customer_group: { + id: "VIP", + }, + }, + items: [ + { + id: "item_cotton_tshirt", + quantity: 1, + subtotal: 50, + product_category: { + id: "catg_cotton", + }, + product: { + id: "prod_tshirt", + }, + }, + { + id: "item_cotton_sweater", + quantity: 1, + subtotal: 150, + product_category: { + id: "catg_cotton", + }, + product: { + id: "prod_sweater", + }, + }, + ], + } + ) + + expect(result).toEqual([ + { + action: "addItemAdjustment", + item_id: "item_cotton_tshirt", + amount: 5, + code: "PROMOTION_TEST", + }, + { + action: "addItemAdjustment", + item_id: "item_cotton_sweater", + amount: 15, + code: "PROMOTION_TEST", + }, + { + action: "addItemAdjustment", + item_id: "item_cotton_tshirt", + amount: 4.5, + code: "PROMOTION_TEST_2", + }, + { + action: "addItemAdjustment", + item_id: "item_cotton_sweater", + amount: 13.5, + code: "PROMOTION_TEST_2", + }, + ]) + }) + + it("should compute budget exceeded action when applicable total exceeds campaign budget for type spend", async () => { + await createCampaigns(MikroOrmWrapper.forkManager()) + + const [createdPromotion] = await service.create([ + { + code: "PROMOTION_TEST", + type: PromotionType.STANDARD, + rules: [ + { + attribute: "customer.customer_group.id", + operator: "in", + values: ["VIP", "top100"], + }, + ], + campaign_id: "campaign-id-1", + application_method: { + type: ApplicationMethodType.PERCENTAGE, + target_type: "items", + allocation: "across", + value: 100, + target_rules: [ + { + attribute: "product_category.id", + operator: "eq", + values: ["catg_cotton"], + }, + ], + }, + }, + ]) + + const result = await service.computeActions(["PROMOTION_TEST"], { + customer: { + customer_group: { + id: "VIP", + }, + }, + items: [ + { + id: "item_cotton_tshirt", + quantity: 5, + subtotal: 5000, + product_category: { + id: "catg_cotton", + }, + product: { + id: "prod_tshirt", + }, + }, + ], + }) + + expect(result).toEqual([ + { action: "campaignBudgetExceeded", code: "PROMOTION_TEST" }, + ]) + }) + + it("should compute budget exceeded action when applicable total exceeds campaign budget for type usage", async () => { + await createCampaigns(MikroOrmWrapper.forkManager()) + + const [createdPromotion] = await service.create([ + { + code: "PROMOTION_TEST", + type: PromotionType.STANDARD, + rules: [ + { + attribute: "customer.customer_group.id", + operator: "in", + values: ["VIP", "top100"], + }, + ], + campaign_id: "campaign-id-2", + application_method: { + type: ApplicationMethodType.PERCENTAGE, + target_type: "items", + allocation: "across", + value: 10, + target_rules: [ + { + attribute: "product_category.id", + operator: "eq", + values: ["catg_cotton"], + }, + ], + }, + }, + ]) + + await service.updateCampaigns({ + id: "campaign-id-2", + budget: { used: 1000 }, + }) + + const result = await service.computeActions(["PROMOTION_TEST"], { + customer: { + customer_group: { + id: "VIP", + }, + }, + items: [ + { + id: "item_cotton_tshirt", + quantity: 5, + subtotal: 5000, + product_category: { + id: "catg_cotton", + }, + product: { + id: "prod_tshirt", + }, + }, + ], + }) + + expect(result).toEqual([ + { action: "campaignBudgetExceeded", code: "PROMOTION_TEST" }, + ]) + }) + }) + }) + + describe("when promotion is for shipping_method and allocation is each", () => { + describe("when application type is fixed", () => { + it("should compute the correct shipping_method amendments", async () => { + const [createdPromotion] = await service.create([ + { + code: "PROMOTION_TEST", + type: PromotionType.STANDARD, + rules: [ + { + attribute: "customer.customer_group.id", + operator: "in", + values: ["VIP", "top100"], + }, + ], + application_method: { + type: "fixed", + target_type: "shipping_methods", + allocation: "each", + value: 200, + max_quantity: 2, + target_rules: [ + { + attribute: "shipping_option.id", + operator: "in", + values: ["express", "standard"], + }, + ], + }, + }, + ]) + + const result = await service.computeActions(["PROMOTION_TEST"], { + customer: { + customer_group: { + id: "VIP", + }, + }, + shipping_methods: [ + { + id: "shipping_method_express", + subtotal: 250, + shipping_option: { + id: "express", + }, + }, + { + id: "shipping_method_standard", + subtotal: 150, + shipping_option: { + id: "standard", + }, + }, + { + id: "shipping_method_snail", + subtotal: 200, + shipping_option: { + id: "snail", + }, + }, + ], + }) + + expect(result).toEqual([ + { + action: "addShippingMethodAdjustment", + shipping_method_id: "shipping_method_express", + amount: 200, + code: "PROMOTION_TEST", + }, + { + action: "addShippingMethodAdjustment", + shipping_method_id: "shipping_method_standard", + amount: 150, + code: "PROMOTION_TEST", + }, + ]) + }) + + it("should compute the correct shipping_method amendments when promotion is automatic", async () => { + const [createdPromotion] = await service.create([ + { + code: "PROMOTION_TEST", + type: PromotionType.STANDARD, + is_automatic: true, + rules: [ + { + attribute: "customer.customer_group.id", + operator: "in", + values: ["VIP", "top100"], + }, + ], + application_method: { + type: "fixed", + target_type: "shipping_methods", + allocation: "each", + value: 200, + max_quantity: 2, + target_rules: [ + { + attribute: "shipping_option.id", + operator: "in", + values: ["express", "standard"], + }, + ], + }, + }, + ]) + + const result = await service.computeActions([], { + customer: { + customer_group: { + id: "VIP", + }, + }, + shipping_methods: [ + { + id: "shipping_method_express", + subtotal: 250, + shipping_option: { + id: "express", + }, + }, + { + id: "shipping_method_standard", + subtotal: 150, + shipping_option: { + id: "standard", + }, + }, + { + id: "shipping_method_snail", + subtotal: 200, + shipping_option: { + id: "snail", + }, + }, + ], + }) + + expect(result).toEqual([ + { + action: "addShippingMethodAdjustment", + shipping_method_id: "shipping_method_express", + amount: 200, + code: "PROMOTION_TEST", + }, + { + action: "addShippingMethodAdjustment", + shipping_method_id: "shipping_method_standard", + amount: 150, + code: "PROMOTION_TEST", + }, + ]) + }) + + it("should compute the correct shipping_method amendments when promotion is automatic and prevent_auto_promotions is false", async () => { + const [createdPromotion] = await service.create([ + { + code: "PROMOTION_TEST", + type: PromotionType.STANDARD, + is_automatic: true, + rules: [ + { + attribute: "customer.customer_group.id", + operator: "in", + values: ["VIP", "top100"], + }, + ], + application_method: { + type: "fixed", + target_type: "shipping_methods", + allocation: "each", + value: 200, + max_quantity: 2, + target_rules: [ + { + attribute: "shipping_option.id", + operator: "in", + values: ["express", "standard"], + }, + ], + }, + }, + ]) + + const result = await service.computeActions( + [], + { + customer: { + customer_group: { + id: "VIP", + }, + }, + shipping_methods: [ + { + id: "shipping_method_express", + subtotal: 250, + shipping_option: { + id: "express", + }, + }, + { + id: "shipping_method_standard", + subtotal: 150, + shipping_option: { + id: "standard", + }, + }, + { + id: "shipping_method_snail", + subtotal: 200, + shipping_option: { + id: "snail", + }, + }, + ], + }, + { prevent_auto_promotions: true } + ) + + expect(result).toEqual([]) + }) + + it("should compute the correct item amendments when there are multiple promotions to apply", async () => { + const [createdPromotion] = await service.create([ + { + code: "PROMOTION_TEST", + type: PromotionType.STANDARD, + rules: [ + { + attribute: "customer.customer_group.id", + operator: "in", + values: ["VIP", "top100"], + }, + ], + application_method: { + type: "fixed", + target_type: "shipping_methods", + allocation: "each", + value: 200, + max_quantity: 2, + target_rules: [ + { + attribute: "shipping_option.id", + operator: "in", + values: ["express", "standard"], + }, + ], + }, + }, + ]) + + const [createdPromotionTwo] = await service.create([ + { + code: "PROMOTION_TEST_2", + type: PromotionType.STANDARD, + rules: [ + { + attribute: "customer.customer_group.id", + operator: "in", + values: ["VIP", "top100"], + }, + ], + application_method: { + type: "fixed", + target_type: "shipping_methods", + allocation: "each", + value: 200, + max_quantity: 2, + target_rules: [ + { + attribute: "shipping_option.id", + operator: "in", + values: ["express", "standard"], + }, + ], + }, + }, + ]) + + const result = await service.computeActions( + ["PROMOTION_TEST", "PROMOTION_TEST_2"], + { + customer: { + customer_group: { + id: "VIP", + }, + }, + shipping_methods: [ + { + id: "shipping_method_express", + subtotal: 250, + shipping_option: { + id: "express", + }, + }, + { + id: "shipping_method_standard", + subtotal: 150, + shipping_option: { + id: "standard", + }, + }, + { + id: "shipping_method_snail", + subtotal: 200, + shipping_option: { + id: "snail", + }, + }, + ], + } + ) + + expect(result).toEqual([ + { + action: "addShippingMethodAdjustment", + shipping_method_id: "shipping_method_express", + amount: 200, + code: "PROMOTION_TEST", + }, + { + action: "addShippingMethodAdjustment", + shipping_method_id: "shipping_method_standard", + amount: 150, + code: "PROMOTION_TEST", + }, + { + action: "addShippingMethodAdjustment", + shipping_method_id: "shipping_method_express", + amount: 50, + code: "PROMOTION_TEST_2", + }, + ]) + }) + + it("should not compute actions when applicable total is 0", async () => { + const [createdPromotion] = await service.create([ + { + code: "PROMOTION_TEST", + type: PromotionType.STANDARD, + rules: [ + { + attribute: "customer.customer_group.id", + operator: "in", + values: ["VIP", "top100"], + }, + ], + application_method: { + type: "fixed", + target_type: "shipping_methods", + allocation: "each", + value: 500, + max_quantity: 2, + target_rules: [ + { + attribute: "shipping_option.id", + operator: "in", + values: ["express", "standard"], + }, + ], + }, + }, + ]) + + const [createdPromotionTwo] = await service.create([ + { + code: "PROMOTION_TEST_2", + type: PromotionType.STANDARD, + rules: [ + { + attribute: "customer.customer_group.id", + operator: "in", + values: ["VIP", "top100"], + }, + ], + application_method: { + type: "fixed", + target_type: "shipping_methods", + allocation: "each", + value: 200, + max_quantity: 2, + target_rules: [ + { + attribute: "shipping_option.id", + operator: "in", + values: ["express", "standard"], + }, + ], + }, + }, + ]) + + const result = await service.computeActions( + ["PROMOTION_TEST", "PROMOTION_TEST_2"], + { + customer: { + customer_group: { + id: "VIP", + }, + }, + shipping_methods: [ + { + id: "shipping_method_express", + subtotal: 250, + shipping_option: { + id: "express", + }, + }, + { + id: "shipping_method_standard", + subtotal: 150, + shipping_option: { + id: "standard", + }, + }, + { + id: "shipping_method_snail", + subtotal: 200, + shipping_option: { + id: "snail", + }, + }, + ], + } + ) + + expect(result).toEqual([ + { + action: "addShippingMethodAdjustment", + shipping_method_id: "shipping_method_express", + amount: 250, + code: "PROMOTION_TEST", + }, + { + action: "addShippingMethodAdjustment", + shipping_method_id: "shipping_method_standard", + amount: 150, + code: "PROMOTION_TEST", + }, + ]) + }) + + it("should compute budget exceeded action when applicable total exceeds campaign budget for type spend", async () => { + await createCampaigns(MikroOrmWrapper.forkManager()) + + const [createdPromotion] = await service.create([ + { + code: "PROMOTION_TEST", + type: PromotionType.STANDARD, + rules: [ + { + attribute: "customer.customer_group.id", + operator: "in", + values: ["VIP", "top100"], + }, + ], + campaign_id: "campaign-id-1", + application_method: { + type: "fixed", + target_type: "shipping_methods", + allocation: "each", + value: 1200, + max_quantity: 2, + target_rules: [ + { + attribute: "shipping_option.id", + operator: "in", + values: ["express", "standard"], + }, + ], + }, + }, + ]) + + const result = await service.computeActions(["PROMOTION_TEST"], { + customer: { + customer_group: { + id: "VIP", + }, + }, + shipping_methods: [ + { + id: "shipping_method_express", + subtotal: 1200, + shipping_option: { + id: "express", + }, + }, + ], + }) + + expect(result).toEqual([ + { action: "campaignBudgetExceeded", code: "PROMOTION_TEST" }, + ]) + }) + + it("should compute budget exceeded action when applicable total exceeds campaign budget for type usage", async () => { + await createCampaigns(MikroOrmWrapper.forkManager()) + + const [createdPromotion] = await service.create([ + { + code: "PROMOTION_TEST", + type: PromotionType.STANDARD, + rules: [ + { + attribute: "customer.customer_group.id", + operator: "in", + values: ["VIP", "top100"], + }, + ], + campaign_id: "campaign-id-2", + application_method: { + type: "fixed", + target_type: "shipping_methods", + allocation: "each", + value: 1200, + max_quantity: 2, + target_rules: [ + { + attribute: "shipping_option.id", + operator: "in", + values: ["express", "standard"], + }, + ], + }, + }, + ]) + + await service.updateCampaigns({ + id: "campaign-id-2", + budget: { used: 1000 }, + }) + + const result = await service.computeActions(["PROMOTION_TEST"], { + customer: { + customer_group: { + id: "VIP", + }, + }, + shipping_methods: [ + { + id: "shipping_method_express", + subtotal: 1200, + shipping_option: { + id: "express", + }, + }, + ], + }) + + expect(result).toEqual([ + { action: "campaignBudgetExceeded", code: "PROMOTION_TEST" }, + ]) + }) + }) + + describe("when application type is percentage", () => { + it("should compute the correct shipping_method amendments", async () => { + const [createdPromotion] = await service.create([ + { + code: "PROMOTION_TEST", + type: PromotionType.STANDARD, + rules: [ + { + attribute: "customer.customer_group.id", + operator: "in", + values: ["VIP", "top100"], + }, + ], + application_method: { + type: ApplicationMethodType.PERCENTAGE, + target_type: "shipping_methods", + allocation: "each", + value: 10, + max_quantity: 2, + target_rules: [ + { + attribute: "shipping_option.id", + operator: "in", + values: ["express", "standard"], + }, + ], + }, + }, + ]) + + const result = await service.computeActions(["PROMOTION_TEST"], { + customer: { + customer_group: { + id: "VIP", + }, + }, + shipping_methods: [ + { + id: "shipping_method_express", + subtotal: 250, + shipping_option: { + id: "express", + }, + }, + { + id: "shipping_method_standard", + subtotal: 150, + shipping_option: { + id: "standard", + }, + }, + { + id: "shipping_method_snail", + subtotal: 200, + shipping_option: { + id: "snail", + }, + }, + ], + }) + + expect(result).toEqual([ + { + action: "addShippingMethodAdjustment", + shipping_method_id: "shipping_method_express", + amount: 25, + code: "PROMOTION_TEST", + }, + { + action: "addShippingMethodAdjustment", + shipping_method_id: "shipping_method_standard", + amount: 15, + code: "PROMOTION_TEST", + }, + ]) + }) + + it("should compute the correct shipping_method amendments when promotion is automatic", async () => { + const [createdPromotion] = await service.create([ + { + code: "PROMOTION_TEST", + type: PromotionType.STANDARD, + is_automatic: true, + rules: [ + { + attribute: "customer.customer_group.id", + operator: "in", + values: ["VIP", "top100"], + }, + ], + application_method: { + type: ApplicationMethodType.PERCENTAGE, + target_type: "shipping_methods", + allocation: "each", + value: 10, + max_quantity: 2, + target_rules: [ + { + attribute: "shipping_option.id", + operator: "in", + values: ["express", "standard"], + }, + ], + }, + }, + ]) + + const result = await service.computeActions([], { + customer: { + customer_group: { + id: "VIP", + }, + }, + shipping_methods: [ + { + id: "shipping_method_express", + subtotal: 250, + shipping_option: { + id: "express", + }, + }, + { + id: "shipping_method_standard", + subtotal: 150, + shipping_option: { + id: "standard", + }, + }, + { + id: "shipping_method_snail", + subtotal: 200, + shipping_option: { + id: "snail", + }, + }, + ], + }) + + expect(result).toEqual([ + { + action: "addShippingMethodAdjustment", + shipping_method_id: "shipping_method_express", + amount: 25, + code: "PROMOTION_TEST", + }, + { + action: "addShippingMethodAdjustment", + shipping_method_id: "shipping_method_standard", + amount: 15, + code: "PROMOTION_TEST", + }, + ]) + }) + + it("should compute the correct shipping_method amendments when promotion is automatic and prevent_auto_promotions is false", async () => { + const [createdPromotion] = await service.create([ + { + code: "PROMOTION_TEST", + type: PromotionType.STANDARD, + is_automatic: true, + rules: [ + { + attribute: "customer.customer_group.id", + operator: "in", + values: ["VIP", "top100"], + }, + ], + application_method: { + type: ApplicationMethodType.PERCENTAGE, + target_type: "shipping_methods", + allocation: "each", + value: 10, + max_quantity: 2, + target_rules: [ + { + attribute: "shipping_option.id", + operator: "in", + values: ["express", "standard"], + }, + ], + }, + }, + ]) + + const result = await service.computeActions( + [], + { + customer: { + customer_group: { + id: "VIP", + }, + }, + shipping_methods: [ + { + id: "shipping_method_express", + subtotal: 250, + shipping_option: { + id: "express", + }, + }, + { + id: "shipping_method_standard", + subtotal: 150, + shipping_option: { + id: "standard", + }, + }, + { + id: "shipping_method_snail", + subtotal: 200, + shipping_option: { + id: "snail", + }, + }, + ], + }, + { prevent_auto_promotions: true } + ) + + expect(result).toEqual([]) + }) + + it("should compute the correct item amendments when there are multiple promotions to apply", async () => { + const [createdPromotion] = await service.create([ + { + code: "PROMOTION_TEST", + type: PromotionType.STANDARD, + rules: [ + { + attribute: "customer.customer_group.id", + operator: "in", + values: ["VIP", "top100"], + }, + ], + application_method: { + type: ApplicationMethodType.PERCENTAGE, + target_type: "shipping_methods", + allocation: "each", + value: 10, + max_quantity: 2, + target_rules: [ + { + attribute: "shipping_option.id", + operator: "in", + values: ["express", "standard"], + }, + ], + }, + }, + ]) + + const [createdPromotionTwo] = await service.create([ + { + code: "PROMOTION_TEST_2", + type: PromotionType.STANDARD, + rules: [ + { + attribute: "customer.customer_group.id", + operator: "in", + values: ["VIP", "top100"], + }, + ], + application_method: { + type: ApplicationMethodType.PERCENTAGE, + target_type: "shipping_methods", + allocation: "each", + value: 10, + max_quantity: 2, + target_rules: [ + { + attribute: "shipping_option.id", + operator: "in", + values: ["express", "standard"], + }, + ], + }, + }, + ]) + + const result = await service.computeActions( + ["PROMOTION_TEST", "PROMOTION_TEST_2"], + { + customer: { + customer_group: { + id: "VIP", + }, + }, + shipping_methods: [ + { + id: "shipping_method_express", + subtotal: 250, + shipping_option: { + id: "express", + }, + }, + { + id: "shipping_method_standard", + subtotal: 150, + shipping_option: { + id: "standard", + }, + }, + { + id: "shipping_method_snail", + subtotal: 200, + shipping_option: { + id: "snail", + }, + }, + ], + } + ) + + expect(result).toEqual([ + { + action: "addShippingMethodAdjustment", + shipping_method_id: "shipping_method_express", + amount: 25, + code: "PROMOTION_TEST", + }, + { + action: "addShippingMethodAdjustment", + shipping_method_id: "shipping_method_standard", + amount: 15, + code: "PROMOTION_TEST", + }, + { + action: "addShippingMethodAdjustment", + shipping_method_id: "shipping_method_express", + amount: 22.5, + code: "PROMOTION_TEST_2", + }, + { + action: "addShippingMethodAdjustment", + shipping_method_id: "shipping_method_standard", + amount: 13.5, + code: "PROMOTION_TEST_2", + }, + ]) + }) + + it("should not compute actions when applicable total is 0", async () => { + const [createdPromotion] = await service.create([ + { + code: "PROMOTION_TEST", + type: PromotionType.STANDARD, + rules: [ + { + attribute: "customer.customer_group.id", + operator: "in", + values: ["VIP", "top100"], + }, + ], + application_method: { + type: ApplicationMethodType.PERCENTAGE, + target_type: "shipping_methods", + allocation: "each", + value: 10, + max_quantity: 2, + target_rules: [ + { + attribute: "shipping_option.id", + operator: "in", + values: ["express", "standard"], + }, + ], + }, + }, + ]) + + const [createdPromotionTwo] = await service.create([ + { + code: "PROMOTION_TEST_2", + type: PromotionType.STANDARD, + rules: [ + { + attribute: "customer.customer_group.id", + operator: "in", + values: ["VIP", "top100"], + }, + ], + application_method: { + type: ApplicationMethodType.PERCENTAGE, + target_type: "shipping_methods", + allocation: "each", + value: 10, + max_quantity: 2, + target_rules: [ + { + attribute: "shipping_option.id", + operator: "in", + values: ["express", "standard"], + }, + ], + }, + }, + ]) + + const result = await service.computeActions( + ["PROMOTION_TEST", "PROMOTION_TEST_2"], + { + customer: { + customer_group: { + id: "VIP", + }, + }, + shipping_methods: [ + { + id: "shipping_method_express", + subtotal: 250, + shipping_option: { + id: "express", + }, + }, + { + id: "shipping_method_standard", + subtotal: 150, + shipping_option: { + id: "standard", + }, + }, + { + id: "shipping_method_snail", + subtotal: 200, + shipping_option: { + id: "snail", + }, + }, + ], + } + ) + + expect(result).toEqual([ + { + action: "addShippingMethodAdjustment", + shipping_method_id: "shipping_method_express", + amount: 25, + code: "PROMOTION_TEST", + }, + { + action: "addShippingMethodAdjustment", + shipping_method_id: "shipping_method_standard", + amount: 15, + code: "PROMOTION_TEST", + }, + { + action: "addShippingMethodAdjustment", + shipping_method_id: "shipping_method_express", + amount: 22.5, + code: "PROMOTION_TEST_2", + }, + { + action: "addShippingMethodAdjustment", + shipping_method_id: "shipping_method_standard", + amount: 13.5, + code: "PROMOTION_TEST_2", + }, + ]) + }) + + it("should compute budget exceeded action when applicable total exceeds campaign budget for type spend", async () => { + await createCampaigns(MikroOrmWrapper.forkManager()) + + const [createdPromotion] = await service.create([ + { + code: "PROMOTION_TEST", + type: PromotionType.STANDARD, + rules: [ + { + attribute: "customer.customer_group.id", + operator: "in", + values: ["VIP", "top100"], + }, + ], + campaign_id: "campaign-id-1", + application_method: { + type: ApplicationMethodType.PERCENTAGE, + target_type: "shipping_methods", + allocation: "each", + value: 100, + max_quantity: 2, + target_rules: [ + { + attribute: "shipping_option.id", + operator: "in", + values: ["express", "standard"], + }, + ], + }, + }, + ]) + + const result = await service.computeActions(["PROMOTION_TEST"], { + customer: { + customer_group: { + id: "VIP", + }, + }, + shipping_methods: [ + { + id: "shipping_method_express", + subtotal: 1200, + shipping_option: { + id: "express", + }, + }, + ], + }) + + expect(result).toEqual([ + { action: "campaignBudgetExceeded", code: "PROMOTION_TEST" }, + ]) + }) + + it("should compute budget exceeded action when applicable total exceeds campaign budget for type usage", async () => { + await createCampaigns(MikroOrmWrapper.forkManager()) + + const [createdPromotion] = await service.create([ + { + code: "PROMOTION_TEST", + type: PromotionType.STANDARD, + rules: [ + { + attribute: "customer.customer_group.id", + operator: "in", + values: ["VIP", "top100"], + }, + ], + campaign_id: "campaign-id-2", + application_method: { + type: ApplicationMethodType.PERCENTAGE, + target_type: "shipping_methods", + allocation: "each", + value: 10, + max_quantity: 2, + target_rules: [ + { + attribute: "shipping_option.id", + operator: "in", + values: ["express", "standard"], + }, + ], + }, + }, + ]) + + await service.updateCampaigns({ + id: "campaign-id-2", + budget: { used: 1000 }, + }) + + const result = await service.computeActions(["PROMOTION_TEST"], { + customer: { + customer_group: { + id: "VIP", + }, + }, + shipping_methods: [ + { + id: "shipping_method_express", + subtotal: 1200, + shipping_option: { + id: "express", + }, + }, + ], + }) + + expect(result).toEqual([ + { action: "campaignBudgetExceeded", code: "PROMOTION_TEST" }, + ]) + }) + }) + }) + + describe("when promotion is for shipping_method and allocation is across", () => { + describe("when application type is fixed", () => { + it("should compute the correct shipping_method amendments", async () => { + const [createdPromotion] = await service.create([ + { + code: "PROMOTION_TEST", + type: PromotionType.STANDARD, + rules: [ + { + attribute: "customer.customer_group.id", + operator: "in", + values: ["VIP", "top100"], + }, + ], + application_method: { + type: ApplicationMethodType.FIXED, + target_type: "shipping_methods", + allocation: "across", + value: 200, + target_rules: [ + { + attribute: "shipping_option.id", + operator: "in", + values: ["express", "standard"], + }, + ], + }, + }, + ]) + + const result = await service.computeActions(["PROMOTION_TEST"], { + customer: { + customer_group: { + id: "VIP", + }, + }, + shipping_methods: [ + { + id: "shipping_method_express", + subtotal: 500, + shipping_option: { + id: "express", + }, + }, + { + id: "shipping_method_standard", + subtotal: 100, + shipping_option: { + id: "standard", + }, + }, + { + id: "shipping_method_snail", + subtotal: 200, + shipping_option: { + id: "snail", + }, + }, + ], + }) + + expect(result).toEqual([ + { + action: "addShippingMethodAdjustment", + shipping_method_id: "shipping_method_express", + amount: 166.66666666666669, + code: "PROMOTION_TEST", + }, + { + action: "addShippingMethodAdjustment", + shipping_method_id: "shipping_method_standard", + amount: 33.33333333333333, + code: "PROMOTION_TEST", + }, + ]) + }) + + it("should compute the correct shipping_method amendments when promotion is automatic", async () => { + const [createdPromotion] = await service.create([ + { + code: "PROMOTION_TEST", + type: PromotionType.STANDARD, + is_automatic: true, + rules: [ + { + attribute: "customer.customer_group.id", + operator: "in", + values: ["VIP", "top100"], + }, + ], + application_method: { + type: ApplicationMethodType.FIXED, + target_type: "shipping_methods", + allocation: "across", + value: 200, + target_rules: [ + { + attribute: "shipping_option.id", + operator: "in", + values: ["express", "standard"], + }, + ], + }, + }, + ]) + + const result = await service.computeActions([], { + customer: { + customer_group: { + id: "VIP", + }, + }, + shipping_methods: [ + { + id: "shipping_method_express", + subtotal: 500, + shipping_option: { + id: "express", + }, + }, + { + id: "shipping_method_standard", + subtotal: 100, + shipping_option: { + id: "standard", + }, + }, + { + id: "shipping_method_snail", + subtotal: 200, + shipping_option: { + id: "snail", + }, + }, + ], + }) + + expect(result).toEqual([ + { + action: "addShippingMethodAdjustment", + shipping_method_id: "shipping_method_express", + amount: 166.66666666666669, + code: "PROMOTION_TEST", + }, + { + action: "addShippingMethodAdjustment", + shipping_method_id: "shipping_method_standard", + amount: 33.33333333333333, + code: "PROMOTION_TEST", + }, + ]) + }) + + it("should compute the correct item amendments when there are multiple promotions to apply", async () => { + const [createdPromotion] = await service.create([ + { + code: "PROMOTION_TEST", + type: PromotionType.STANDARD, + rules: [ + { + attribute: "customer.customer_group.id", + operator: "in", + values: ["VIP", "top100"], + }, + ], + application_method: { + type: ApplicationMethodType.FIXED, + target_type: "shipping_methods", + allocation: "across", + value: 200, + target_rules: [ + { + attribute: "shipping_option.id", + operator: "in", + values: ["express", "standard"], + }, + ], + }, + }, + ]) + + const [createdPromotion2] = await service.create([ + { + code: "PROMOTION_TEST_2", + type: PromotionType.STANDARD, + rules: [ + { + attribute: "customer.customer_group.id", + operator: "in", + values: ["VIP", "top100"], + }, + ], + application_method: { + type: ApplicationMethodType.FIXED, + target_type: "shipping_methods", + allocation: "across", + value: 200, + target_rules: [ + { + attribute: "shipping_option.id", + operator: "in", + values: ["express", "standard"], + }, + ], + }, + }, + ]) + + const result = await service.computeActions( + ["PROMOTION_TEST", "PROMOTION_TEST_2"], + { + customer: { + customer_group: { + id: "VIP", + }, + }, + shipping_methods: [ + { + id: "shipping_method_express", + subtotal: 500, + shipping_option: { + id: "express", + }, + }, + { + id: "shipping_method_standard", + subtotal: 100, + shipping_option: { + id: "standard", + }, + }, + { + id: "shipping_method_snail", + subtotal: 200, + shipping_option: { + id: "snail", + }, + }, + ], + } + ) + + expect(result).toEqual([ + { + action: "addShippingMethodAdjustment", + shipping_method_id: "shipping_method_express", + amount: 166.66666666666669, + code: "PROMOTION_TEST", + }, + { + action: "addShippingMethodAdjustment", + shipping_method_id: "shipping_method_standard", + amount: 33.33333333333333, + code: "PROMOTION_TEST", + }, + { + action: "addShippingMethodAdjustment", + shipping_method_id: "shipping_method_express", + amount: 166.66666666666666, + code: "PROMOTION_TEST_2", + }, + { + action: "addShippingMethodAdjustment", + shipping_method_id: "shipping_method_standard", + amount: 33.333333333333336, + code: "PROMOTION_TEST_2", + }, + ]) + }) + + it("should not compute actions when applicable total is 0", async () => { + const [createdPromotion] = await service.create([ + { + code: "PROMOTION_TEST", + type: PromotionType.STANDARD, + rules: [ + { + attribute: "customer.customer_group.id", + operator: "in", + values: ["VIP", "top100"], + }, + ], + application_method: { + type: ApplicationMethodType.FIXED, + target_type: "shipping_methods", + allocation: "across", + value: 1000, + target_rules: [ + { + attribute: "shipping_option.id", + operator: "in", + values: ["express", "standard"], + }, + ], + }, + }, + ]) + + const [createdPromotion2] = await service.create([ + { + code: "PROMOTION_TEST_2", + type: PromotionType.STANDARD, + rules: [ + { + attribute: "customer.customer_group.id", + operator: "in", + values: ["VIP", "top100"], + }, + ], + application_method: { + type: ApplicationMethodType.FIXED, + target_type: "shipping_methods", + allocation: "across", + value: 200, + target_rules: [ + { + attribute: "shipping_option.id", + operator: "in", + values: ["express", "standard"], + }, + ], + }, + }, + ]) + + const result = await service.computeActions( + ["PROMOTION_TEST", "PROMOTION_TEST_2"], + { + customer: { + customer_group: { + id: "VIP", + }, + }, + shipping_methods: [ + { + id: "shipping_method_express", + subtotal: 500, + shipping_option: { + id: "express", + }, + }, + { + id: "shipping_method_standard", + subtotal: 100, + shipping_option: { + id: "standard", + }, + }, + { + id: "shipping_method_snail", + subtotal: 200, + shipping_option: { + id: "snail", + }, + }, + ], + } + ) + + expect(result).toEqual([ + { + action: "addShippingMethodAdjustment", + shipping_method_id: "shipping_method_express", + amount: 500, + code: "PROMOTION_TEST", + }, + { + action: "addShippingMethodAdjustment", + shipping_method_id: "shipping_method_standard", + amount: 100, + code: "PROMOTION_TEST", + }, + ]) + }) + + it("should compute budget exceeded action when applicable total exceeds campaign budget for type spend", async () => { + await createCampaigns(MikroOrmWrapper.forkManager()) + + const [createdPromotion] = await service.create([ + { + code: "PROMOTION_TEST", + type: PromotionType.STANDARD, + rules: [ + { + attribute: "customer.customer_group.id", + operator: "in", + values: ["VIP", "top100"], + }, + ], + campaign_id: "campaign-id-1", + application_method: { + type: ApplicationMethodType.FIXED, + target_type: "shipping_methods", + allocation: "across", + value: 1200, + target_rules: [ + { + attribute: "shipping_option.id", + operator: "in", + values: ["express", "standard"], + }, + ], + }, + }, + ]) + + const result = await service.computeActions(["PROMOTION_TEST"], { + customer: { + customer_group: { + id: "VIP", + }, + }, + shipping_methods: [ + { + id: "shipping_method_express", + subtotal: 1200, + shipping_option: { + id: "express", + }, + }, + ], + }) + + expect(result).toEqual([ + { action: "campaignBudgetExceeded", code: "PROMOTION_TEST" }, + ]) + }) + + it("should compute budget exceeded action when applicable total exceeds campaign budget for type usage", async () => { + await createCampaigns(MikroOrmWrapper.forkManager()) + + const [createdPromotion] = await service.create([ + { + code: "PROMOTION_TEST", + type: PromotionType.STANDARD, + rules: [ + { + attribute: "customer.customer_group.id", + operator: "in", + values: ["VIP", "top100"], + }, + ], + campaign_id: "campaign-id-2", + application_method: { + type: ApplicationMethodType.FIXED, + target_type: "shipping_methods", + allocation: "across", + value: 1200, + target_rules: [ + { + attribute: "shipping_option.id", + operator: "in", + values: ["express", "standard"], + }, + ], + }, + }, + ]) + + await service.updateCampaigns({ + id: "campaign-id-2", + budget: { used: 1000 }, + }) + + const result = await service.computeActions(["PROMOTION_TEST"], { + customer: { + customer_group: { + id: "VIP", + }, + }, + shipping_methods: [ + { + id: "shipping_method_express", + subtotal: 1200, + shipping_option: { + id: "express", + }, + }, + ], + }) + + expect(result).toEqual([ + { action: "campaignBudgetExceeded", code: "PROMOTION_TEST" }, + ]) + }) + }) + + describe("when application type is percentage", () => { + it("should compute the correct shipping_method amendments", async () => { + const [createdPromotion] = await service.create([ + { + code: "PROMOTION_TEST", + type: PromotionType.STANDARD, + rules: [ + { + attribute: "customer.customer_group.id", + operator: "in", + values: ["VIP", "top100"], + }, + ], + application_method: { + type: ApplicationMethodType.PERCENTAGE, + target_type: "shipping_methods", + allocation: "across", + value: 10, + target_rules: [ + { + attribute: "shipping_option.id", + operator: "in", + values: ["express", "standard"], + }, + ], + }, + }, + ]) + + const result = await service.computeActions(["PROMOTION_TEST"], { + customer: { + customer_group: { + id: "VIP", + }, + }, + shipping_methods: [ + { + id: "shipping_method_express", + subtotal: 500, + shipping_option: { + id: "express", + }, + }, + { + id: "shipping_method_standard", + subtotal: 100, + shipping_option: { + id: "standard", + }, + }, + { + id: "shipping_method_snail", + subtotal: 200, + shipping_option: { + id: "snail", + }, + }, + ], + }) + + expect(result).toEqual([ + { + action: "addShippingMethodAdjustment", + shipping_method_id: "shipping_method_express", + amount: 50, + code: "PROMOTION_TEST", + }, + { + action: "addShippingMethodAdjustment", + shipping_method_id: "shipping_method_standard", + amount: 10, + code: "PROMOTION_TEST", + }, + ]) + }) + + it("should compute the correct shipping_method amendments when promotion is automatic", async () => { + const [createdPromotion] = await service.create([ + { + code: "PROMOTION_TEST", + type: PromotionType.STANDARD, + is_automatic: true, + rules: [ + { + attribute: "customer.customer_group.id", + operator: "in", + values: ["VIP", "top100"], + }, + ], + application_method: { + type: ApplicationMethodType.PERCENTAGE, + target_type: "shipping_methods", + allocation: "across", + value: 10, + target_rules: [ + { + attribute: "shipping_option.id", + operator: "in", + values: ["express", "standard"], + }, + ], + }, + }, + ]) + + const result = await service.computeActions([], { + customer: { + customer_group: { + id: "VIP", + }, + }, + shipping_methods: [ + { + id: "shipping_method_express", + subtotal: 500, + shipping_option: { + id: "express", + }, + }, + { + id: "shipping_method_standard", + subtotal: 100, + shipping_option: { + id: "standard", + }, + }, + { + id: "shipping_method_snail", + subtotal: 200, + shipping_option: { + id: "snail", + }, + }, + ], + }) + + expect(result).toEqual([ + { + action: "addShippingMethodAdjustment", + shipping_method_id: "shipping_method_express", + amount: 50, + code: "PROMOTION_TEST", + }, + { + action: "addShippingMethodAdjustment", + shipping_method_id: "shipping_method_standard", + amount: 10, + code: "PROMOTION_TEST", + }, + ]) + }) + + it("should compute the correct item amendments when there are multiple promotions to apply", async () => { + const [createdPromotion] = await service.create([ + { + code: "PROMOTION_TEST", + type: PromotionType.STANDARD, + rules: [ + { + attribute: "customer.customer_group.id", + operator: "in", + values: ["VIP", "top100"], + }, + ], + application_method: { + type: ApplicationMethodType.PERCENTAGE, + target_type: "shipping_methods", + allocation: "across", + value: 10, + target_rules: [ + { + attribute: "shipping_option.id", + operator: "in", + values: ["express", "standard"], + }, + ], + }, + }, + ]) + + const [createdPromotion2] = await service.create([ + { + code: "PROMOTION_TEST_2", + type: PromotionType.STANDARD, + rules: [ + { + attribute: "customer.customer_group.id", + operator: "in", + values: ["VIP", "top100"], + }, + ], + application_method: { + type: ApplicationMethodType.PERCENTAGE, + target_type: "shipping_methods", + allocation: "across", + value: 10, + target_rules: [ + { + attribute: "shipping_option.id", + operator: "in", + values: ["express", "standard"], + }, + ], + }, + }, + ]) + + const result = await service.computeActions( + ["PROMOTION_TEST", "PROMOTION_TEST_2"], + { + customer: { + customer_group: { + id: "VIP", + }, + }, + shipping_methods: [ + { + id: "shipping_method_express", + subtotal: 500, + shipping_option: { + id: "express", + }, + }, + { + id: "shipping_method_standard", + subtotal: 100, + shipping_option: { + id: "standard", + }, + }, + { + id: "shipping_method_snail", + subtotal: 200, + shipping_option: { + id: "snail", + }, + }, + ], + } + ) + + expect(result).toEqual([ + { + action: "addShippingMethodAdjustment", + shipping_method_id: "shipping_method_express", + amount: 50, + code: "PROMOTION_TEST", + }, + { + action: "addShippingMethodAdjustment", + shipping_method_id: "shipping_method_standard", + amount: 10, + code: "PROMOTION_TEST", + }, + { + action: "addShippingMethodAdjustment", + shipping_method_id: "shipping_method_express", + amount: 45, + code: "PROMOTION_TEST_2", + }, + { + action: "addShippingMethodAdjustment", + shipping_method_id: "shipping_method_standard", + amount: 9, + code: "PROMOTION_TEST_2", + }, + ]) + }) + + it("should not compute actions when applicable total is 0", async () => { + const [createdPromotion] = await service.create([ + { + code: "PROMOTION_TEST", + type: PromotionType.STANDARD, + rules: [ + { + attribute: "customer.customer_group.id", + operator: "in", + values: ["VIP", "top100"], + }, + ], + application_method: { + type: ApplicationMethodType.PERCENTAGE, + target_type: "shipping_methods", + allocation: "across", + value: 100, + target_rules: [ + { + attribute: "shipping_option.id", + operator: "in", + values: ["express", "standard"], + }, + ], + }, + }, + ]) + + const [createdPromotion2] = await service.create([ + { + code: "PROMOTION_TEST_2", + type: PromotionType.STANDARD, + rules: [ + { + attribute: "customer.customer_group.id", + operator: "in", + values: ["VIP", "top100"], + }, + ], + application_method: { + type: ApplicationMethodType.PERCENTAGE, + target_type: "shipping_methods", + allocation: "across", + value: 10, + target_rules: [ + { + attribute: "shipping_option.id", + operator: "in", + values: ["express", "standard"], + }, + ], + }, + }, + ]) + + const result = await service.computeActions( + ["PROMOTION_TEST", "PROMOTION_TEST_2"], + { + customer: { + customer_group: { + id: "VIP", + }, + }, + shipping_methods: [ + { + id: "shipping_method_express", + subtotal: 500, + shipping_option: { + id: "express", + }, + }, + { + id: "shipping_method_standard", + subtotal: 100, + shipping_option: { + id: "standard", + }, + }, + { + id: "shipping_method_snail", + subtotal: 200, + shipping_option: { + id: "snail", + }, + }, + ], + } + ) + + expect(result).toEqual([ + { + action: "addShippingMethodAdjustment", + shipping_method_id: "shipping_method_express", + amount: 500, + code: "PROMOTION_TEST", + }, + { + action: "addShippingMethodAdjustment", + shipping_method_id: "shipping_method_standard", + amount: 100, + code: "PROMOTION_TEST", + }, + ]) + }) + + it("should compute budget exceeded action when applicable total exceeds campaign budget for type spend", async () => { + await createCampaigns(MikroOrmWrapper.forkManager()) + + const [createdPromotion] = await service.create([ + { + code: "PROMOTION_TEST", + type: PromotionType.STANDARD, + rules: [ + { + attribute: "customer.customer_group.id", + operator: "in", + values: ["VIP", "top100"], + }, + ], + campaign_id: "campaign-id-1", + application_method: { + type: ApplicationMethodType.PERCENTAGE, + target_type: "shipping_methods", + allocation: "across", + value: 100, + target_rules: [ + { + attribute: "shipping_option.id", + operator: "in", + values: ["express", "standard"], + }, + ], + }, + }, + ]) + + const result = await service.computeActions(["PROMOTION_TEST"], { + customer: { + customer_group: { + id: "VIP", + }, + }, + shipping_methods: [ + { + id: "shipping_method_express", + subtotal: 1200, + shipping_option: { + id: "express", + }, + }, + ], + }) + + expect(result).toEqual([ + { action: "campaignBudgetExceeded", code: "PROMOTION_TEST" }, + ]) + }) + + it("should compute budget exceeded action when applicable total exceeds campaign budget for type usage", async () => { + await createCampaigns(MikroOrmWrapper.forkManager()) + + const [createdPromotion] = await service.create([ + { + code: "PROMOTION_TEST", + type: PromotionType.STANDARD, + rules: [ + { + attribute: "customer.customer_group.id", + operator: "in", + values: ["VIP", "top100"], + }, + ], + campaign_id: "campaign-id-2", + application_method: { + type: ApplicationMethodType.PERCENTAGE, + target_type: "shipping_methods", + allocation: "across", + value: 10, + target_rules: [ + { + attribute: "shipping_option.id", + operator: "in", + values: ["express", "standard"], + }, + ], + }, + }, + ]) + + await service.updateCampaigns({ + id: "campaign-id-2", + budget: { used: 1000 }, + }) + + const result = await service.computeActions(["PROMOTION_TEST"], { + customer: { + customer_group: { + id: "VIP", + }, + }, + shipping_methods: [ + { + id: "shipping_method_express", + subtotal: 1200, + shipping_option: { + id: "express", + }, + }, + ], + }) + + expect(result).toEqual([ + { action: "campaignBudgetExceeded", code: "PROMOTION_TEST" }, + ]) + }) + }) + }) + + describe("when promotion is for the entire order", () => { + it("should compute the correct item amendments", async () => { + const [createdPromotion] = await service.create([ + { + code: "PROMOTION_TEST", + type: PromotionType.STANDARD, + rules: [ + { + attribute: "customer.customer_group.id", + operator: "in", + values: ["VIP", "top100"], + }, + ], + application_method: { + type: "fixed", + target_type: "order", + value: 200, + max_quantity: 2, + }, + }, + ]) + + const result = await service.computeActions(["PROMOTION_TEST"], { customer: { customer_group: { id: "VIP", @@ -429,7 +4148,7 @@ describe("Promotion Service: computeActions", () => { { id: "item_cotton_tshirt", quantity: 1, - subtotal: 50, + subtotal: 100, product_category: { id: "catg_cotton", }, @@ -439,8 +4158,8 @@ describe("Promotion Service: computeActions", () => { }, { id: "item_cotton_sweater", - quantity: 1, - subtotal: 150, + quantity: 2, + subtotal: 300, product_category: { id: "catg_cotton", }, @@ -449,410 +4168,47 @@ describe("Promotion Service: computeActions", () => { }, }, ], - } - ) + }) - expect(result).toEqual([ - { - action: "addItemAdjustment", - item_id: "item_cotton_tshirt", - amount: 50, - code: "PROMOTION_TEST", - }, - { - action: "addItemAdjustment", - item_id: "item_cotton_sweater", - amount: 150, - code: "PROMOTION_TEST", - }, - ]) - }) - - it("should compute budget exceeded action when applicable total exceeds campaign budget for type spend", async () => { - await createCampaigns(repositoryManager) - - const [createdPromotion] = await service.create([ - { - code: "PROMOTION_TEST", - type: PromotionType.STANDARD, - rules: [ - { - attribute: "customer.customer_group.id", - operator: "in", - values: ["VIP", "top100"], - }, - ], - campaign_id: "campaign-id-1", - application_method: { - type: "fixed", - target_type: "items", - allocation: "each", - value: 500, - max_quantity: 5, - target_rules: [ - { - attribute: "product_category.id", - operator: "eq", - values: ["catg_cotton"], - }, - ], - }, - }, - ]) - - const result = await service.computeActions(["PROMOTION_TEST"], { - customer: { - customer_group: { - id: "VIP", - }, - }, - items: [ + expect(result).toEqual([ { - id: "item_cotton_tshirt", - quantity: 5, - subtotal: 5000, - product_category: { - id: "catg_cotton", - }, - product: { - id: "prod_tshirt", - }, - }, - ], - }) - - expect(result).toEqual([ - { - action: "addItemAdjustment", - item_id: "item_cotton_tshirt", - amount: 500, - code: "PROMOTION_TEST", - }, - ]) - }) - - it("should compute budget exceeded action when applicable total exceeds campaign budget for type usage", async () => { - await createCampaigns(repositoryManager) - - const [createdPromotion] = await service.create([ - { - code: "PROMOTION_TEST", - type: PromotionType.STANDARD, - rules: [ - { - attribute: "customer.customer_group.id", - operator: "in", - values: ["VIP", "top100"], - }, - ], - campaign_id: "campaign-id-2", - application_method: { - type: "fixed", - target_type: "items", - allocation: "each", - value: 500, - max_quantity: 5, - target_rules: [ - { - attribute: "product_category.id", - operator: "eq", - values: ["catg_cotton"], - }, - ], - }, - }, - ]) - - await service.updateCampaigns({ - id: "campaign-id-2", - budget: { used: 1000 }, - }) - - const result = await service.computeActions(["PROMOTION_TEST"], { - customer: { - customer_group: { - id: "VIP", - }, - }, - items: [ - { - id: "item_cotton_tshirt", - quantity: 5, - subtotal: 5000, - product_category: { - id: "catg_cotton", - }, - product: { - id: "prod_tshirt", - }, - }, - ], - }) - - expect(result).toEqual([ - { action: "campaignBudgetExceeded", code: "PROMOTION_TEST" }, - ]) - }) - }) - - describe("when application type is percentage", () => { - it("should compute the correct item amendments", async () => { - const [createdPromotion] = await service.create([ - { - code: "PROMOTION_TEST", - type: PromotionType.STANDARD, - rules: [ - { - attribute: "customer.customer_group.id", - operator: "in", - values: ["VIP", "top100"], - }, - ], - application_method: { - type: ApplicationMethodType.PERCENTAGE, - target_type: "items", - allocation: "each", - value: 10, - max_quantity: 1, - target_rules: [ - { - attribute: "product_category.id", - operator: "eq", - values: ["catg_cotton"], - }, - ], - }, - }, - ]) - - const result = await service.computeActions(["PROMOTION_TEST"], { - customer: { - customer_group: { - id: "VIP", - }, - }, - items: [ - { - id: "item_cotton_tshirt", - quantity: 1, - subtotal: 100, - product_category: { - id: "catg_cotton", - }, - product: { - id: "prod_tshirt", - }, + action: "addItemAdjustment", + item_id: "item_cotton_tshirt", + amount: 50, + code: "PROMOTION_TEST", }, { - id: "item_cotton_sweater", - quantity: 5, - subtotal: 750, - product_category: { - id: "catg_cotton", - }, - product: { - id: "prod_sweater", - }, + action: "addItemAdjustment", + item_id: "item_cotton_sweater", + amount: 150, + code: "PROMOTION_TEST", }, - ], + ]) }) - expect(result).toEqual([ - { - action: "addItemAdjustment", - item_id: "item_cotton_tshirt", - amount: 10, - code: "PROMOTION_TEST", - }, - { - action: "addItemAdjustment", - item_id: "item_cotton_sweater", - amount: 15, - code: "PROMOTION_TEST", - }, - ]) - }) - - it("should compute the correct item amendments when there are multiple promotions to apply", async () => { - const [createdPromotion] = await service.create([ - { - code: "PROMOTION_TEST", - type: PromotionType.STANDARD, - rules: [ - { - attribute: "customer.customer_group.id", - operator: "in", - values: ["VIP", "top100"], - }, - ], - application_method: { - type: ApplicationMethodType.PERCENTAGE, - target_type: "items", - allocation: "each", - value: 30, - max_quantity: 2, - target_rules: [ + it("should compute the correct item amendments when promotion is automatic", async () => { + const [createdPromotion] = await service.create([ + { + code: "PROMOTION_TEST", + type: PromotionType.STANDARD, + is_automatic: true, + rules: [ { - attribute: "product_category.id", - operator: "eq", - values: ["catg_cotton"], + attribute: "customer.customer_group.id", + operator: "in", + values: ["VIP", "top100"], }, ], - }, - }, - ]) - - const [createdPromotionTwo] = await service.create([ - { - code: "PROMOTION_TEST_2", - type: PromotionType.STANDARD, - rules: [ - { - attribute: "customer.customer_group.id", - operator: "in", - values: ["VIP", "top100"], - }, - ], - application_method: { - type: ApplicationMethodType.PERCENTAGE, - target_type: "items", - allocation: "each", - value: 10, - max_quantity: 1, - target_rules: [ - { - attribute: "product_category.id", - operator: "eq", - values: ["catg_cotton"], - }, - ], - }, - }, - ]) - - const result = await service.computeActions( - ["PROMOTION_TEST", "PROMOTION_TEST_2"], - { - customer: { - customer_group: { - id: "VIP", + application_method: { + type: "fixed", + target_type: "order", + value: 200, + max_quantity: 2, }, }, - items: [ - { - id: "item_cotton_tshirt", - quantity: 3, - subtotal: 150, - product_category: { - id: "catg_cotton", - }, - product: { - id: "prod_tshirt", - }, - }, - { - id: "item_cotton_sweater", - quantity: 1, - subtotal: 150, - product_category: { - id: "catg_cotton", - }, - product: { - id: "prod_sweater", - }, - }, - ], - } - ) + ]) - expect(result).toEqual([ - { - action: "addItemAdjustment", - item_id: "item_cotton_tshirt", - amount: 30, - code: "PROMOTION_TEST", - }, - { - action: "addItemAdjustment", - item_id: "item_cotton_sweater", - amount: 45, - code: "PROMOTION_TEST", - }, - { - action: "addItemAdjustment", - item_id: "item_cotton_tshirt", - amount: 2, - code: "PROMOTION_TEST_2", - }, - { - action: "addItemAdjustment", - item_id: "item_cotton_sweater", - amount: 10.5, - code: "PROMOTION_TEST_2", - }, - ]) - }) - - it("should not compute actions when applicable total is 0", async () => { - const [createdPromotion] = await service.create([ - { - code: "PROMOTION_TEST", - type: PromotionType.STANDARD, - rules: [ - { - attribute: "customer.customer_group.id", - operator: "in", - values: ["VIP", "top100"], - }, - ], - application_method: { - type: ApplicationMethodType.PERCENTAGE, - target_type: "items", - allocation: "each", - value: 100, - max_quantity: 10, - target_rules: [ - { - attribute: "product_category.id", - operator: "eq", - values: ["catg_cotton"], - }, - ], - }, - }, - ]) - - const [createdPromotionTwo] = await service.create([ - { - code: "PROMOTION_TEST_2", - type: PromotionType.STANDARD, - rules: [ - { - attribute: "customer.customer_group.id", - operator: "in", - values: ["VIP", "top100"], - }, - ], - application_method: { - type: ApplicationMethodType.PERCENTAGE, - target_type: "items", - allocation: "each", - value: 50, - max_quantity: 10, - target_rules: [ - { - attribute: "product_category.id", - operator: "eq", - values: ["catg_cotton"], - }, - ], - }, - }, - ]) - - const result = await service.computeActions( - ["PROMOTION_TEST", "PROMOTION_TEST_2"], - { + const result = await service.computeActions([], { customer: { customer_group: { id: "VIP", @@ -862,7 +4218,7 @@ describe("Promotion Service: computeActions", () => { { id: "item_cotton_tshirt", quantity: 1, - subtotal: 50, + subtotal: 100, product_category: { id: "catg_cotton", }, @@ -872,8 +4228,8 @@ describe("Promotion Service: computeActions", () => { }, { id: "item_cotton_sweater", - quantity: 1, - subtotal: 150, + quantity: 2, + subtotal: 300, product_category: { id: "catg_cotton", }, @@ -882,361 +4238,259 @@ describe("Promotion Service: computeActions", () => { }, }, ], - } - ) + }) - expect(result).toEqual([ - { - action: "addItemAdjustment", - item_id: "item_cotton_tshirt", - amount: 50, - code: "PROMOTION_TEST", - }, - { - action: "addItemAdjustment", - item_id: "item_cotton_sweater", - amount: 150, - code: "PROMOTION_TEST", - }, - ]) - }) - - it("should compute budget exceeded action when applicable total exceeds campaign budget for type spend", async () => { - await createCampaigns(repositoryManager) - - const [createdPromotion] = await service.create([ - { - code: "PROMOTION_TEST", - type: PromotionType.STANDARD, - rules: [ - { - attribute: "customer.customer_group.id", - operator: "in", - values: ["VIP", "top100"], - }, - ], - campaign_id: "campaign-id-1", - application_method: { - type: ApplicationMethodType.PERCENTAGE, - target_type: "items", - allocation: "each", - value: 100, - max_quantity: 5, - target_rules: [ - { - attribute: "product_category.id", - operator: "eq", - values: ["catg_cotton"], - }, - ], - }, - }, - ]) - - const result = await service.computeActions(["PROMOTION_TEST"], { - customer: { - customer_group: { - id: "VIP", - }, - }, - items: [ + expect(result).toEqual([ { - id: "item_cotton_tshirt", - quantity: 5, - subtotal: 10000, - product_category: { - id: "catg_cotton", - }, - product: { - id: "prod_tshirt", - }, + action: "addItemAdjustment", + item_id: "item_cotton_tshirt", + amount: 50, + code: "PROMOTION_TEST", }, - ], + { + action: "addItemAdjustment", + item_id: "item_cotton_sweater", + amount: 150, + code: "PROMOTION_TEST", + }, + ]) }) - expect(result).toEqual([ - { action: "campaignBudgetExceeded", code: "PROMOTION_TEST" }, - ]) - }) - - it("should compute budget exceeded action when applicable total exceeds campaign budget for type usage", async () => { - await createCampaigns(repositoryManager) - - const [createdPromotion] = await service.create([ - { - code: "PROMOTION_TEST", - type: PromotionType.STANDARD, - rules: [ - { - attribute: "customer.customer_group.id", - operator: "in", - values: ["VIP", "top100"], - }, - ], - campaign_id: "campaign-id-2", - application_method: { - type: ApplicationMethodType.PERCENTAGE, - target_type: "items", - allocation: "each", - value: 10, - max_quantity: 5, - target_rules: [ + it("should compute the correct item amendments when there are multiple promotions to apply", async () => { + const [createdPromotion] = await service.create([ + { + code: "PROMOTION_TEST", + type: PromotionType.STANDARD, + rules: [ { - attribute: "product_category.id", - operator: "eq", - values: ["catg_cotton"], + attribute: "customer.customer_group.id", + operator: "in", + values: ["VIP", "top100"], }, ], + application_method: { + type: "fixed", + target_type: "order", + value: 30, + max_quantity: 2, + }, }, - }, - ]) + ]) - await service.updateCampaigns({ - id: "campaign-id-2", - budget: { used: 1000 }, + const [createdPromotionTwo] = await service.create([ + { + code: "PROMOTION_TEST_2", + type: PromotionType.STANDARD, + rules: [ + { + attribute: "customer.customer_group.id", + operator: "in", + values: ["VIP", "top100"], + }, + ], + application_method: { + type: "fixed", + target_type: "order", + value: 50, + max_quantity: 1, + }, + }, + ]) + + const result = await service.computeActions( + ["PROMOTION_TEST", "PROMOTION_TEST_2"], + { + customer: { + customer_group: { + id: "VIP", + }, + }, + items: [ + { + id: "item_cotton_tshirt", + quantity: 1, + subtotal: 50, + product_category: { + id: "catg_cotton", + }, + product: { + id: "prod_tshirt", + }, + }, + { + id: "item_cotton_sweater", + quantity: 1, + subtotal: 150, + product_category: { + id: "catg_cotton", + }, + product: { + id: "prod_sweater", + }, + }, + ], + } + ) + + expect(result).toEqual([ + { + action: "addItemAdjustment", + item_id: "item_cotton_tshirt", + amount: 7.5, + code: "PROMOTION_TEST", + }, + { + action: "addItemAdjustment", + item_id: "item_cotton_sweater", + amount: 22.5, + code: "PROMOTION_TEST", + }, + { + action: "addItemAdjustment", + item_id: "item_cotton_tshirt", + amount: 12.5, + code: "PROMOTION_TEST_2", + }, + { + action: "addItemAdjustment", + item_id: "item_cotton_sweater", + amount: 37.5, + code: "PROMOTION_TEST_2", + }, + ]) }) - const result = await service.computeActions(["PROMOTION_TEST"], { - customer: { - customer_group: { - id: "VIP", - }, - }, - items: [ + it("should not compute actions when applicable total is 0", async () => { + const [createdPromotion] = await service.create([ { - id: "item_cotton_tshirt", - quantity: 5, - subtotal: 5000, - product_category: { - id: "catg_cotton", - }, - product: { - id: "prod_tshirt", - }, - }, - ], - }) - - expect(result).toEqual([ - { action: "campaignBudgetExceeded", code: "PROMOTION_TEST" }, - ]) - }) - }) - }) - - describe("when promotion is for items and allocation is across", () => { - describe("when application type is fixed", () => { - it("should compute the correct item amendments", async () => { - const [createdPromotion] = await service.create([ - { - code: "PROMOTION_TEST", - type: PromotionType.STANDARD, - rules: [ - { - attribute: "customer.customer_group.id", - operator: "in", - values: ["VIP", "top100"], - }, - ], - application_method: { - type: "fixed", - target_type: "items", - allocation: "across", - value: 400, - target_rules: [ + code: "PROMOTION_TEST", + type: PromotionType.STANDARD, + rules: [ { - attribute: "product_category.id", - operator: "eq", - values: ["catg_cotton"], + attribute: "customer.customer_group.id", + operator: "in", + values: ["VIP", "top100"], }, ], + application_method: { + type: "fixed", + target_type: "order", + value: 500, + max_quantity: 2, + }, }, - }, - ]) + ]) - const result = await service.computeActions(["PROMOTION_TEST"], { - customer: { - customer_group: { - id: "VIP", - }, - }, - items: [ + const [createdPromotionTwo] = await service.create([ { - id: "item_cotton_tshirt", - quantity: 2, - subtotal: 200, - product_category: { - id: "catg_cotton", + code: "PROMOTION_TEST_2", + type: PromotionType.STANDARD, + rules: [ + { + attribute: "customer.customer_group.id", + operator: "in", + values: ["VIP", "top100"], + }, + ], + application_method: { + type: "fixed", + target_type: "order", + value: 50, + max_quantity: 1, }, - product: { - id: "prod_tshirt", + }, + ]) + + const result = await service.computeActions( + ["PROMOTION_TEST", "PROMOTION_TEST_2"], + { + customer: { + customer_group: { + id: "VIP", + }, }, + items: [ + { + id: "item_cotton_tshirt", + quantity: 1, + subtotal: 50, + product_category: { + id: "catg_cotton", + }, + product: { + id: "prod_tshirt", + }, + }, + { + id: "item_cotton_sweater", + quantity: 1, + subtotal: 150, + product_category: { + id: "catg_cotton", + }, + product: { + id: "prod_sweater", + }, + }, + ], + } + ) + + expect(result).toEqual([ + { + action: "addItemAdjustment", + item_id: "item_cotton_tshirt", + amount: 50, + code: "PROMOTION_TEST", }, { - id: "item_cotton_sweater", - quantity: 2, - subtotal: 600, - product_category: { - id: "catg_cotton", - }, - product: { - id: "prod_sweater", - }, + action: "addItemAdjustment", + item_id: "item_cotton_sweater", + amount: 150, + code: "PROMOTION_TEST", }, - ], + ]) }) - - expect(result).toEqual([ - { - action: "addItemAdjustment", - item_id: "item_cotton_tshirt", - amount: 100, - code: "PROMOTION_TEST", - }, - { - action: "addItemAdjustment", - item_id: "item_cotton_sweater", - amount: 300, - code: "PROMOTION_TEST", - }, - ]) }) - it("should compute the correct item amendments when promotion is automatic", async () => { - const [createdPromotion] = await service.create([ - { - code: "PROMOTION_TEST", - type: PromotionType.STANDARD, - is_automatic: true, - rules: [ - { - attribute: "customer.customer_group.id", - operator: "in", - values: ["VIP", "top100"], - }, - ], - application_method: { - type: "fixed", - target_type: "items", - allocation: "across", - value: 400, - target_rules: [ - { - attribute: "product_category.id", - operator: "eq", - values: ["catg_cotton"], - }, - ], - }, - }, - ]) - - const result = await service.computeActions([], { - customer: { - customer_group: { - id: "VIP", - }, - }, - items: [ + describe("when adjustments are present in the context", () => { + it("should compute the correct item amendments along with removal of applied item adjustment", async () => { + const [adjustmentPromotion] = await service.create([ { - id: "item_cotton_tshirt", - quantity: 2, - subtotal: 200, - product_category: { - id: "catg_cotton", - }, - product: { - id: "prod_tshirt", - }, + code: "ADJUSTMENT_CODE", + type: PromotionType.STANDARD, }, + ]) + + const [createdPromotion] = await service.create([ { - id: "item_cotton_sweater", - quantity: 2, - subtotal: 600, - product_category: { - id: "catg_cotton", - }, - product: { - id: "prod_sweater", - }, - }, - ], - }) - - expect(result).toEqual([ - { - action: "addItemAdjustment", - item_id: "item_cotton_tshirt", - amount: 100, - code: "PROMOTION_TEST", - }, - { - action: "addItemAdjustment", - item_id: "item_cotton_sweater", - amount: 300, - code: "PROMOTION_TEST", - }, - ]) - }) - - it("should compute the correct item amendments when there are multiple promotions to apply", async () => { - const [createdPromotion] = await service.create([ - { - code: "PROMOTION_TEST", - type: PromotionType.STANDARD, - rules: [ - { - attribute: "customer.customer_group.id", - operator: "in", - values: ["VIP", "top100"], - }, - ], - application_method: { - type: "fixed", - target_type: "items", - allocation: "across", - value: 30, - target_rules: [ + code: "PROMOTION_TEST", + type: PromotionType.STANDARD, + rules: [ { - attribute: "product_category.id", - operator: "eq", - values: ["catg_cotton"], + attribute: "customer.customer_group.id", + operator: "in", + values: ["VIP", "top100"], }, ], - }, - }, - ]) - - const [createdPromotionTwo] = await service.create([ - { - code: "PROMOTION_TEST_2", - type: PromotionType.STANDARD, - rules: [ - { - attribute: "customer.customer_group.id", - operator: "in", - values: ["VIP", "top100"], + application_method: { + type: "fixed", + target_type: "items", + allocation: "each", + value: 200, + max_quantity: 1, + target_rules: [ + { + attribute: "product_category.id", + operator: "eq", + values: ["catg_cotton"], + }, + ], }, - ], - application_method: { - type: "fixed", - target_type: "items", - allocation: "across", - value: 50, - target_rules: [ - { - attribute: "product_category.id", - operator: "eq", - values: ["catg_cotton"], - }, - ], }, - }, - ]) + ]) - const result = await service.computeActions( - ["PROMOTION_TEST", "PROMOTION_TEST_2"], - { + const result = await service.computeActions(["PROMOTION_TEST"], { customer: { customer_group: { id: "VIP", @@ -1246,18 +4500,24 @@ describe("Promotion Service: computeActions", () => { { id: "item_cotton_tshirt", quantity: 1, - subtotal: 50, + subtotal: 100, product_category: { id: "catg_cotton", }, product: { id: "prod_tshirt", }, + adjustments: [ + { + id: "test-adjustment", + code: "ADJUSTMENT_CODE", + }, + ], }, { id: "item_cotton_sweater", - quantity: 1, - subtotal: 150, + quantity: 5, + subtotal: 750, product_category: { id: "catg_cotton", }, @@ -1266,2117 +4526,65 @@ describe("Promotion Service: computeActions", () => { }, }, ], - } - ) + }) - expect(result).toEqual([ - { - action: "addItemAdjustment", - item_id: "item_cotton_tshirt", - amount: 7.5, - code: "PROMOTION_TEST", - }, - { - action: "addItemAdjustment", - item_id: "item_cotton_sweater", - amount: 22.5, - code: "PROMOTION_TEST", - }, - { - action: "addItemAdjustment", - item_id: "item_cotton_tshirt", - amount: 12.5, - code: "PROMOTION_TEST_2", - }, - { - action: "addItemAdjustment", - item_id: "item_cotton_sweater", - amount: 37.5, - code: "PROMOTION_TEST_2", - }, - ]) - }) - - it("should not compute actions when applicable total is 0", async () => { - const [createdPromotion] = await service.create([ - { - code: "PROMOTION_TEST", - type: PromotionType.STANDARD, - rules: [ - { - attribute: "customer.customer_group.id", - operator: "in", - values: ["VIP", "top100"], - }, - ], - application_method: { - type: "fixed", - target_type: "items", - allocation: "across", - value: 1000, - target_rules: [ - { - attribute: "product_category.id", - operator: "eq", - values: ["catg_cotton"], - }, - ], - }, - }, - ]) - - const [createdPromotionTwo] = await service.create([ - { - code: "PROMOTION_TEST_2", - type: PromotionType.STANDARD, - rules: [ - { - attribute: "customer.customer_group.id", - operator: "in", - values: ["VIP", "top100"], - }, - ], - application_method: { - type: "fixed", - target_type: "items", - allocation: "across", - value: 50, - target_rules: [ - { - attribute: "product_category.id", - operator: "eq", - values: ["catg_cotton"], - }, - ], - }, - }, - ]) - - const result = await service.computeActions( - ["PROMOTION_TEST", "PROMOTION_TEST_2"], - { - customer: { - customer_group: { - id: "VIP", - }, - }, - items: [ - { - id: "item_cotton_tshirt", - quantity: 1, - subtotal: 50, - product_category: { - id: "catg_cotton", - }, - product: { - id: "prod_tshirt", - }, - }, - { - id: "item_cotton_sweater", - quantity: 1, - subtotal: 150, - product_category: { - id: "catg_cotton", - }, - product: { - id: "prod_sweater", - }, - }, - ], - } - ) - - expect(result).toEqual([ - { - action: "addItemAdjustment", - item_id: "item_cotton_tshirt", - amount: 50, - code: "PROMOTION_TEST", - }, - { - action: "addItemAdjustment", - item_id: "item_cotton_sweater", - amount: 150, - code: "PROMOTION_TEST", - }, - ]) - }) - - it("should compute budget exceeded action when applicable total exceeds campaign budget for type spend", async () => { - await createCampaigns(repositoryManager) - - const [createdPromotion] = await service.create([ - { - code: "PROMOTION_TEST", - type: PromotionType.STANDARD, - rules: [ - { - attribute: "customer.customer_group.id", - operator: "in", - values: ["VIP", "top100"], - }, - ], - campaign_id: "campaign-id-1", - application_method: { - type: "fixed", - target_type: "items", - allocation: "across", - value: 1500, - target_rules: [ - { - attribute: "product_category.id", - operator: "eq", - values: ["catg_cotton"], - }, - ], - }, - }, - ]) - - const result = await service.computeActions(["PROMOTION_TEST"], { - customer: { - customer_group: { - id: "VIP", - }, - }, - items: [ + expect(result).toEqual([ { - id: "item_cotton_tshirt", - quantity: 5, - subtotal: 5000, - product_category: { - id: "catg_cotton", - }, - product: { - id: "prod_tshirt", - }, + action: "removeItemAdjustment", + adjustment_id: "test-adjustment", + code: "ADJUSTMENT_CODE", }, - ], + { + action: "addItemAdjustment", + item_id: "item_cotton_tshirt", + amount: 100, + code: "PROMOTION_TEST", + }, + { + action: "addItemAdjustment", + item_id: "item_cotton_sweater", + amount: 150, + code: "PROMOTION_TEST", + }, + ]) }) - expect(result).toEqual([ - { action: "campaignBudgetExceeded", code: "PROMOTION_TEST" }, - ]) - }) - - it("should compute budget exceeded action when applicable total exceeds campaign budget for type usage", async () => { - await createCampaigns(repositoryManager) - - const [createdPromotion] = await service.create([ - { - code: "PROMOTION_TEST", - type: PromotionType.STANDARD, - rules: [ - { - attribute: "customer.customer_group.id", - operator: "in", - values: ["VIP", "top100"], - }, - ], - campaign_id: "campaign-id-2", - application_method: { - type: "fixed", - target_type: "items", - allocation: "across", - value: 500, - target_rules: [ - { - attribute: "product_category.id", - operator: "eq", - values: ["catg_cotton"], - }, - ], - }, - }, - ]) - - await service.updateCampaigns({ - id: "campaign-id-2", - budget: { used: 1000 }, - }) - - const result = await service.computeActions(["PROMOTION_TEST"], { - customer: { - customer_group: { - id: "VIP", - }, - }, - items: [ + it("should compute the correct item amendments along with removal of applied shipping adjustment", async () => { + const [adjustmentPromotion] = await service.create([ { - id: "item_cotton_tshirt", - quantity: 5, - subtotal: 5000, - product_category: { - id: "catg_cotton", - }, - product: { - id: "prod_tshirt", - }, + code: "ADJUSTMENT_CODE", + type: PromotionType.STANDARD, }, - ], - }) + ]) - expect(result).toEqual([ - { action: "campaignBudgetExceeded", code: "PROMOTION_TEST" }, - ]) - }) - }) - - describe("when application type is percentage", () => { - it("should compute the correct item amendments", async () => { - const [createdPromotion] = await service.create([ - { - code: "PROMOTION_TEST", - type: PromotionType.STANDARD, - rules: [ - { - attribute: "customer.customer_group.id", - operator: "in", - values: ["VIP", "top100"], - }, - ], - application_method: { - type: ApplicationMethodType.PERCENTAGE, - target_type: "items", - allocation: "across", - value: 10, - target_rules: [ - { - attribute: "product_category.id", - operator: "eq", - values: ["catg_cotton"], - }, - ], - }, - }, - ]) - - const result = await service.computeActions(["PROMOTION_TEST"], { - customer: { - customer_group: { - id: "VIP", - }, - }, - items: [ + const [createdPromotion] = await service.create([ { - id: "item_cotton_tshirt", - quantity: 2, - subtotal: 200, - product_category: { - id: "catg_cotton", - }, - product: { - id: "prod_tshirt", - }, - }, - { - id: "item_cotton_sweater", - quantity: 2, - subtotal: 600, - product_category: { - id: "catg_cotton", - }, - product: { - id: "prod_sweater", - }, - }, - ], - }) - - expect(result).toEqual([ - { - action: "addItemAdjustment", - item_id: "item_cotton_tshirt", - amount: 20, - code: "PROMOTION_TEST", - }, - { - action: "addItemAdjustment", - item_id: "item_cotton_sweater", - amount: 60, - code: "PROMOTION_TEST", - }, - ]) - }) - - it("should compute the correct item amendments when promotion is automatic", async () => { - const [createdPromotion] = await service.create([ - { - code: "PROMOTION_TEST", - type: PromotionType.STANDARD, - is_automatic: true, - rules: [ - { - attribute: "customer.customer_group.id", - operator: "in", - values: ["VIP", "top100"], - }, - ], - application_method: { - type: ApplicationMethodType.PERCENTAGE, - target_type: "items", - allocation: "across", - value: 10, - target_rules: [ + code: "PROMOTION_TEST", + type: PromotionType.STANDARD, + rules: [ { - attribute: "product_category.id", - operator: "eq", - values: ["catg_cotton"], - }, - ], - }, - }, - ]) - - const result = await service.computeActions([], { - customer: { - customer_group: { - id: "VIP", - }, - }, - items: [ - { - id: "item_cotton_tshirt", - quantity: 2, - subtotal: 200, - product_category: { - id: "catg_cotton", - }, - product: { - id: "prod_tshirt", - }, - }, - { - id: "item_cotton_sweater", - quantity: 2, - subtotal: 600, - product_category: { - id: "catg_cotton", - }, - product: { - id: "prod_sweater", - }, - }, - ], - }) - - expect(result).toEqual([ - { - action: "addItemAdjustment", - item_id: "item_cotton_tshirt", - amount: 20, - code: "PROMOTION_TEST", - }, - { - action: "addItemAdjustment", - item_id: "item_cotton_sweater", - amount: 60, - code: "PROMOTION_TEST", - }, - ]) - }) - - it("should compute the correct item amendments when there are multiple promotions to apply", async () => { - const [createdPromotion] = await service.create([ - { - code: "PROMOTION_TEST", - type: PromotionType.STANDARD, - rules: [ - { - attribute: "customer.customer_group.id", - operator: "in", - values: ["VIP", "top100"], - }, - ], - application_method: { - type: ApplicationMethodType.PERCENTAGE, - target_type: "items", - allocation: "across", - value: 10, - target_rules: [ - { - attribute: "product_category.id", - operator: "eq", - values: ["catg_cotton"], - }, - ], - }, - }, - ]) - - const [createdPromotionTwo] = await service.create([ - { - code: "PROMOTION_TEST_2", - type: PromotionType.STANDARD, - rules: [ - { - attribute: "customer.customer_group.id", - operator: "in", - values: ["VIP", "top100"], - }, - ], - application_method: { - type: ApplicationMethodType.PERCENTAGE, - target_type: "items", - allocation: "across", - value: 10, - target_rules: [ - { - attribute: "product_category.id", - operator: "eq", - values: ["catg_cotton"], - }, - ], - }, - }, - ]) - - const result = await service.computeActions( - ["PROMOTION_TEST", "PROMOTION_TEST_2"], - { - customer: { - customer_group: { - id: "VIP", - }, - }, - items: [ - { - id: "item_cotton_tshirt", - quantity: 1, - subtotal: 50, - product_category: { - id: "catg_cotton", - }, - product: { - id: "prod_tshirt", - }, - }, - { - id: "item_cotton_sweater", - quantity: 1, - subtotal: 150, - product_category: { - id: "catg_cotton", - }, - product: { - id: "prod_sweater", - }, - }, - ], - } - ) - - expect(result).toEqual([ - { - action: "addItemAdjustment", - item_id: "item_cotton_tshirt", - amount: 5, - code: "PROMOTION_TEST", - }, - { - action: "addItemAdjustment", - item_id: "item_cotton_sweater", - amount: 15, - code: "PROMOTION_TEST", - }, - { - action: "addItemAdjustment", - item_id: "item_cotton_tshirt", - amount: 4.5, - code: "PROMOTION_TEST_2", - }, - { - action: "addItemAdjustment", - item_id: "item_cotton_sweater", - amount: 13.5, - code: "PROMOTION_TEST_2", - }, - ]) - }) - - it("should not compute actions when applicable total is 0", async () => { - const [createdPromotion] = await service.create([ - { - code: "PROMOTION_TEST", - type: PromotionType.STANDARD, - rules: [ - { - attribute: "customer.customer_group.id", - operator: "in", - values: ["VIP", "top100"], - }, - ], - application_method: { - type: ApplicationMethodType.PERCENTAGE, - target_type: "items", - allocation: "across", - value: 10, - target_rules: [ - { - attribute: "product_category.id", - operator: "eq", - values: ["catg_cotton"], - }, - ], - }, - }, - ]) - - const [createdPromotionTwo] = await service.create([ - { - code: "PROMOTION_TEST_2", - type: PromotionType.STANDARD, - rules: [ - { - attribute: "customer.customer_group.id", - operator: "in", - values: ["VIP", "top100"], - }, - ], - application_method: { - type: ApplicationMethodType.PERCENTAGE, - target_type: "items", - allocation: "across", - value: 10, - target_rules: [ - { - attribute: "product_category.id", - operator: "eq", - values: ["catg_cotton"], - }, - ], - }, - }, - ]) - - const result = await service.computeActions( - ["PROMOTION_TEST", "PROMOTION_TEST_2"], - { - customer: { - customer_group: { - id: "VIP", - }, - }, - items: [ - { - id: "item_cotton_tshirt", - quantity: 1, - subtotal: 50, - product_category: { - id: "catg_cotton", - }, - product: { - id: "prod_tshirt", - }, - }, - { - id: "item_cotton_sweater", - quantity: 1, - subtotal: 150, - product_category: { - id: "catg_cotton", - }, - product: { - id: "prod_sweater", - }, - }, - ], - } - ) - - expect(result).toEqual([ - { - action: "addItemAdjustment", - item_id: "item_cotton_tshirt", - amount: 5, - code: "PROMOTION_TEST", - }, - { - action: "addItemAdjustment", - item_id: "item_cotton_sweater", - amount: 15, - code: "PROMOTION_TEST", - }, - { - action: "addItemAdjustment", - item_id: "item_cotton_tshirt", - amount: 4.5, - code: "PROMOTION_TEST_2", - }, - { - action: "addItemAdjustment", - item_id: "item_cotton_sweater", - amount: 13.5, - code: "PROMOTION_TEST_2", - }, - ]) - }) - - it("should compute budget exceeded action when applicable total exceeds campaign budget for type spend", async () => { - await createCampaigns(repositoryManager) - - const [createdPromotion] = await service.create([ - { - code: "PROMOTION_TEST", - type: PromotionType.STANDARD, - rules: [ - { - attribute: "customer.customer_group.id", - operator: "in", - values: ["VIP", "top100"], - }, - ], - campaign_id: "campaign-id-1", - application_method: { - type: ApplicationMethodType.PERCENTAGE, - target_type: "items", - allocation: "across", - value: 100, - target_rules: [ - { - attribute: "product_category.id", - operator: "eq", - values: ["catg_cotton"], - }, - ], - }, - }, - ]) - - const result = await service.computeActions(["PROMOTION_TEST"], { - customer: { - customer_group: { - id: "VIP", - }, - }, - items: [ - { - id: "item_cotton_tshirt", - quantity: 5, - subtotal: 5000, - product_category: { - id: "catg_cotton", - }, - product: { - id: "prod_tshirt", - }, - }, - ], - }) - - expect(result).toEqual([ - { action: "campaignBudgetExceeded", code: "PROMOTION_TEST" }, - ]) - }) - - it("should compute budget exceeded action when applicable total exceeds campaign budget for type usage", async () => { - await createCampaigns(repositoryManager) - - const [createdPromotion] = await service.create([ - { - code: "PROMOTION_TEST", - type: PromotionType.STANDARD, - rules: [ - { - attribute: "customer.customer_group.id", - operator: "in", - values: ["VIP", "top100"], - }, - ], - campaign_id: "campaign-id-2", - application_method: { - type: ApplicationMethodType.PERCENTAGE, - target_type: "items", - allocation: "across", - value: 10, - target_rules: [ - { - attribute: "product_category.id", - operator: "eq", - values: ["catg_cotton"], - }, - ], - }, - }, - ]) - - await service.updateCampaigns({ - id: "campaign-id-2", - budget: { used: 1000 }, - }) - - const result = await service.computeActions(["PROMOTION_TEST"], { - customer: { - customer_group: { - id: "VIP", - }, - }, - items: [ - { - id: "item_cotton_tshirt", - quantity: 5, - subtotal: 5000, - product_category: { - id: "catg_cotton", - }, - product: { - id: "prod_tshirt", - }, - }, - ], - }) - - expect(result).toEqual([ - { action: "campaignBudgetExceeded", code: "PROMOTION_TEST" }, - ]) - }) - }) - }) - - describe("when promotion is for shipping_method and allocation is each", () => { - describe("when application type is fixed", () => { - it("should compute the correct shipping_method amendments", async () => { - const [createdPromotion] = await service.create([ - { - code: "PROMOTION_TEST", - type: PromotionType.STANDARD, - rules: [ - { - attribute: "customer.customer_group.id", - operator: "in", - values: ["VIP", "top100"], - }, - ], - application_method: { - type: "fixed", - target_type: "shipping_methods", - allocation: "each", - value: 200, - max_quantity: 2, - target_rules: [ - { - attribute: "shipping_option.id", + attribute: "customer.customer_group.id", operator: "in", - values: ["express", "standard"], + values: ["VIP", "top100"], }, ], - }, - }, - ]) - - const result = await service.computeActions(["PROMOTION_TEST"], { - customer: { - customer_group: { - id: "VIP", - }, - }, - shipping_methods: [ - { - id: "shipping_method_express", - subtotal: 250, - shipping_option: { - id: "express", + application_method: { + type: "fixed", + target_type: "shipping_methods", + allocation: "across", + value: 200, + target_rules: [ + { + attribute: "shipping_option.id", + operator: "in", + values: ["express", "standard"], + }, + ], }, }, - { - id: "shipping_method_standard", - subtotal: 150, - shipping_option: { - id: "standard", - }, - }, - { - id: "shipping_method_snail", - subtotal: 200, - shipping_option: { - id: "snail", - }, - }, - ], - }) + ]) - expect(result).toEqual([ - { - action: "addShippingMethodAdjustment", - shipping_method_id: "shipping_method_express", - amount: 200, - code: "PROMOTION_TEST", - }, - { - action: "addShippingMethodAdjustment", - shipping_method_id: "shipping_method_standard", - amount: 150, - code: "PROMOTION_TEST", - }, - ]) - }) - - it("should compute the correct shipping_method amendments when promotion is automatic", async () => { - const [createdPromotion] = await service.create([ - { - code: "PROMOTION_TEST", - type: PromotionType.STANDARD, - is_automatic: true, - rules: [ - { - attribute: "customer.customer_group.id", - operator: "in", - values: ["VIP", "top100"], - }, - ], - application_method: { - type: "fixed", - target_type: "shipping_methods", - allocation: "each", - value: 200, - max_quantity: 2, - target_rules: [ - { - attribute: "shipping_option.id", - operator: "in", - values: ["express", "standard"], - }, - ], - }, - }, - ]) - - const result = await service.computeActions([], { - customer: { - customer_group: { - id: "VIP", - }, - }, - shipping_methods: [ - { - id: "shipping_method_express", - subtotal: 250, - shipping_option: { - id: "express", - }, - }, - { - id: "shipping_method_standard", - subtotal: 150, - shipping_option: { - id: "standard", - }, - }, - { - id: "shipping_method_snail", - subtotal: 200, - shipping_option: { - id: "snail", - }, - }, - ], - }) - - expect(result).toEqual([ - { - action: "addShippingMethodAdjustment", - shipping_method_id: "shipping_method_express", - amount: 200, - code: "PROMOTION_TEST", - }, - { - action: "addShippingMethodAdjustment", - shipping_method_id: "shipping_method_standard", - amount: 150, - code: "PROMOTION_TEST", - }, - ]) - }) - - it("should compute the correct shipping_method amendments when promotion is automatic and prevent_auto_promotions is false", async () => { - const [createdPromotion] = await service.create([ - { - code: "PROMOTION_TEST", - type: PromotionType.STANDARD, - is_automatic: true, - rules: [ - { - attribute: "customer.customer_group.id", - operator: "in", - values: ["VIP", "top100"], - }, - ], - application_method: { - type: "fixed", - target_type: "shipping_methods", - allocation: "each", - value: 200, - max_quantity: 2, - target_rules: [ - { - attribute: "shipping_option.id", - operator: "in", - values: ["express", "standard"], - }, - ], - }, - }, - ]) - - const result = await service.computeActions( - [], - { - customer: { - customer_group: { - id: "VIP", - }, - }, - shipping_methods: [ - { - id: "shipping_method_express", - subtotal: 250, - shipping_option: { - id: "express", - }, - }, - { - id: "shipping_method_standard", - subtotal: 150, - shipping_option: { - id: "standard", - }, - }, - { - id: "shipping_method_snail", - subtotal: 200, - shipping_option: { - id: "snail", - }, - }, - ], - }, - { prevent_auto_promotions: true } - ) - - expect(result).toEqual([]) - }) - - it("should compute the correct item amendments when there are multiple promotions to apply", async () => { - const [createdPromotion] = await service.create([ - { - code: "PROMOTION_TEST", - type: PromotionType.STANDARD, - rules: [ - { - attribute: "customer.customer_group.id", - operator: "in", - values: ["VIP", "top100"], - }, - ], - application_method: { - type: "fixed", - target_type: "shipping_methods", - allocation: "each", - value: 200, - max_quantity: 2, - target_rules: [ - { - attribute: "shipping_option.id", - operator: "in", - values: ["express", "standard"], - }, - ], - }, - }, - ]) - - const [createdPromotionTwo] = await service.create([ - { - code: "PROMOTION_TEST_2", - type: PromotionType.STANDARD, - rules: [ - { - attribute: "customer.customer_group.id", - operator: "in", - values: ["VIP", "top100"], - }, - ], - application_method: { - type: "fixed", - target_type: "shipping_methods", - allocation: "each", - value: 200, - max_quantity: 2, - target_rules: [ - { - attribute: "shipping_option.id", - operator: "in", - values: ["express", "standard"], - }, - ], - }, - }, - ]) - - const result = await service.computeActions( - ["PROMOTION_TEST", "PROMOTION_TEST_2"], - { - customer: { - customer_group: { - id: "VIP", - }, - }, - shipping_methods: [ - { - id: "shipping_method_express", - subtotal: 250, - shipping_option: { - id: "express", - }, - }, - { - id: "shipping_method_standard", - subtotal: 150, - shipping_option: { - id: "standard", - }, - }, - { - id: "shipping_method_snail", - subtotal: 200, - shipping_option: { - id: "snail", - }, - }, - ], - } - ) - - expect(result).toEqual([ - { - action: "addShippingMethodAdjustment", - shipping_method_id: "shipping_method_express", - amount: 200, - code: "PROMOTION_TEST", - }, - { - action: "addShippingMethodAdjustment", - shipping_method_id: "shipping_method_standard", - amount: 150, - code: "PROMOTION_TEST", - }, - { - action: "addShippingMethodAdjustment", - shipping_method_id: "shipping_method_express", - amount: 50, - code: "PROMOTION_TEST_2", - }, - ]) - }) - - it("should not compute actions when applicable total is 0", async () => { - const [createdPromotion] = await service.create([ - { - code: "PROMOTION_TEST", - type: PromotionType.STANDARD, - rules: [ - { - attribute: "customer.customer_group.id", - operator: "in", - values: ["VIP", "top100"], - }, - ], - application_method: { - type: "fixed", - target_type: "shipping_methods", - allocation: "each", - value: 500, - max_quantity: 2, - target_rules: [ - { - attribute: "shipping_option.id", - operator: "in", - values: ["express", "standard"], - }, - ], - }, - }, - ]) - - const [createdPromotionTwo] = await service.create([ - { - code: "PROMOTION_TEST_2", - type: PromotionType.STANDARD, - rules: [ - { - attribute: "customer.customer_group.id", - operator: "in", - values: ["VIP", "top100"], - }, - ], - application_method: { - type: "fixed", - target_type: "shipping_methods", - allocation: "each", - value: 200, - max_quantity: 2, - target_rules: [ - { - attribute: "shipping_option.id", - operator: "in", - values: ["express", "standard"], - }, - ], - }, - }, - ]) - - const result = await service.computeActions( - ["PROMOTION_TEST", "PROMOTION_TEST_2"], - { - customer: { - customer_group: { - id: "VIP", - }, - }, - shipping_methods: [ - { - id: "shipping_method_express", - subtotal: 250, - shipping_option: { - id: "express", - }, - }, - { - id: "shipping_method_standard", - subtotal: 150, - shipping_option: { - id: "standard", - }, - }, - { - id: "shipping_method_snail", - subtotal: 200, - shipping_option: { - id: "snail", - }, - }, - ], - } - ) - - expect(result).toEqual([ - { - action: "addShippingMethodAdjustment", - shipping_method_id: "shipping_method_express", - amount: 250, - code: "PROMOTION_TEST", - }, - { - action: "addShippingMethodAdjustment", - shipping_method_id: "shipping_method_standard", - amount: 150, - code: "PROMOTION_TEST", - }, - ]) - }) - - it("should compute budget exceeded action when applicable total exceeds campaign budget for type spend", async () => { - await createCampaigns(repositoryManager) - - const [createdPromotion] = await service.create([ - { - code: "PROMOTION_TEST", - type: PromotionType.STANDARD, - rules: [ - { - attribute: "customer.customer_group.id", - operator: "in", - values: ["VIP", "top100"], - }, - ], - campaign_id: "campaign-id-1", - application_method: { - type: "fixed", - target_type: "shipping_methods", - allocation: "each", - value: 1200, - max_quantity: 2, - target_rules: [ - { - attribute: "shipping_option.id", - operator: "in", - values: ["express", "standard"], - }, - ], - }, - }, - ]) - - const result = await service.computeActions(["PROMOTION_TEST"], { - customer: { - customer_group: { - id: "VIP", - }, - }, - shipping_methods: [ - { - id: "shipping_method_express", - subtotal: 1200, - shipping_option: { - id: "express", - }, - }, - ], - }) - - expect(result).toEqual([ - { action: "campaignBudgetExceeded", code: "PROMOTION_TEST" }, - ]) - }) - - it("should compute budget exceeded action when applicable total exceeds campaign budget for type usage", async () => { - await createCampaigns(repositoryManager) - - const [createdPromotion] = await service.create([ - { - code: "PROMOTION_TEST", - type: PromotionType.STANDARD, - rules: [ - { - attribute: "customer.customer_group.id", - operator: "in", - values: ["VIP", "top100"], - }, - ], - campaign_id: "campaign-id-2", - application_method: { - type: "fixed", - target_type: "shipping_methods", - allocation: "each", - value: 1200, - max_quantity: 2, - target_rules: [ - { - attribute: "shipping_option.id", - operator: "in", - values: ["express", "standard"], - }, - ], - }, - }, - ]) - - await service.updateCampaigns({ - id: "campaign-id-2", - budget: { used: 1000 }, - }) - - const result = await service.computeActions(["PROMOTION_TEST"], { - customer: { - customer_group: { - id: "VIP", - }, - }, - shipping_methods: [ - { - id: "shipping_method_express", - subtotal: 1200, - shipping_option: { - id: "express", - }, - }, - ], - }) - - expect(result).toEqual([ - { action: "campaignBudgetExceeded", code: "PROMOTION_TEST" }, - ]) - }) - }) - - describe("when application type is percentage", () => { - it("should compute the correct shipping_method amendments", async () => { - const [createdPromotion] = await service.create([ - { - code: "PROMOTION_TEST", - type: PromotionType.STANDARD, - rules: [ - { - attribute: "customer.customer_group.id", - operator: "in", - values: ["VIP", "top100"], - }, - ], - application_method: { - type: ApplicationMethodType.PERCENTAGE, - target_type: "shipping_methods", - allocation: "each", - value: 10, - max_quantity: 2, - target_rules: [ - { - attribute: "shipping_option.id", - operator: "in", - values: ["express", "standard"], - }, - ], - }, - }, - ]) - - const result = await service.computeActions(["PROMOTION_TEST"], { - customer: { - customer_group: { - id: "VIP", - }, - }, - shipping_methods: [ - { - id: "shipping_method_express", - subtotal: 250, - shipping_option: { - id: "express", - }, - }, - { - id: "shipping_method_standard", - subtotal: 150, - shipping_option: { - id: "standard", - }, - }, - { - id: "shipping_method_snail", - subtotal: 200, - shipping_option: { - id: "snail", - }, - }, - ], - }) - - expect(result).toEqual([ - { - action: "addShippingMethodAdjustment", - shipping_method_id: "shipping_method_express", - amount: 25, - code: "PROMOTION_TEST", - }, - { - action: "addShippingMethodAdjustment", - shipping_method_id: "shipping_method_standard", - amount: 15, - code: "PROMOTION_TEST", - }, - ]) - }) - - it("should compute the correct shipping_method amendments when promotion is automatic", async () => { - const [createdPromotion] = await service.create([ - { - code: "PROMOTION_TEST", - type: PromotionType.STANDARD, - is_automatic: true, - rules: [ - { - attribute: "customer.customer_group.id", - operator: "in", - values: ["VIP", "top100"], - }, - ], - application_method: { - type: ApplicationMethodType.PERCENTAGE, - target_type: "shipping_methods", - allocation: "each", - value: 10, - max_quantity: 2, - target_rules: [ - { - attribute: "shipping_option.id", - operator: "in", - values: ["express", "standard"], - }, - ], - }, - }, - ]) - - const result = await service.computeActions([], { - customer: { - customer_group: { - id: "VIP", - }, - }, - shipping_methods: [ - { - id: "shipping_method_express", - subtotal: 250, - shipping_option: { - id: "express", - }, - }, - { - id: "shipping_method_standard", - subtotal: 150, - shipping_option: { - id: "standard", - }, - }, - { - id: "shipping_method_snail", - subtotal: 200, - shipping_option: { - id: "snail", - }, - }, - ], - }) - - expect(result).toEqual([ - { - action: "addShippingMethodAdjustment", - shipping_method_id: "shipping_method_express", - amount: 25, - code: "PROMOTION_TEST", - }, - { - action: "addShippingMethodAdjustment", - shipping_method_id: "shipping_method_standard", - amount: 15, - code: "PROMOTION_TEST", - }, - ]) - }) - - it("should compute the correct shipping_method amendments when promotion is automatic and prevent_auto_promotions is false", async () => { - const [createdPromotion] = await service.create([ - { - code: "PROMOTION_TEST", - type: PromotionType.STANDARD, - is_automatic: true, - rules: [ - { - attribute: "customer.customer_group.id", - operator: "in", - values: ["VIP", "top100"], - }, - ], - application_method: { - type: ApplicationMethodType.PERCENTAGE, - target_type: "shipping_methods", - allocation: "each", - value: 10, - max_quantity: 2, - target_rules: [ - { - attribute: "shipping_option.id", - operator: "in", - values: ["express", "standard"], - }, - ], - }, - }, - ]) - - const result = await service.computeActions( - [], - { - customer: { - customer_group: { - id: "VIP", - }, - }, - shipping_methods: [ - { - id: "shipping_method_express", - subtotal: 250, - shipping_option: { - id: "express", - }, - }, - { - id: "shipping_method_standard", - subtotal: 150, - shipping_option: { - id: "standard", - }, - }, - { - id: "shipping_method_snail", - subtotal: 200, - shipping_option: { - id: "snail", - }, - }, - ], - }, - { prevent_auto_promotions: true } - ) - - expect(result).toEqual([]) - }) - - it("should compute the correct item amendments when there are multiple promotions to apply", async () => { - const [createdPromotion] = await service.create([ - { - code: "PROMOTION_TEST", - type: PromotionType.STANDARD, - rules: [ - { - attribute: "customer.customer_group.id", - operator: "in", - values: ["VIP", "top100"], - }, - ], - application_method: { - type: ApplicationMethodType.PERCENTAGE, - target_type: "shipping_methods", - allocation: "each", - value: 10, - max_quantity: 2, - target_rules: [ - { - attribute: "shipping_option.id", - operator: "in", - values: ["express", "standard"], - }, - ], - }, - }, - ]) - - const [createdPromotionTwo] = await service.create([ - { - code: "PROMOTION_TEST_2", - type: PromotionType.STANDARD, - rules: [ - { - attribute: "customer.customer_group.id", - operator: "in", - values: ["VIP", "top100"], - }, - ], - application_method: { - type: ApplicationMethodType.PERCENTAGE, - target_type: "shipping_methods", - allocation: "each", - value: 10, - max_quantity: 2, - target_rules: [ - { - attribute: "shipping_option.id", - operator: "in", - values: ["express", "standard"], - }, - ], - }, - }, - ]) - - const result = await service.computeActions( - ["PROMOTION_TEST", "PROMOTION_TEST_2"], - { - customer: { - customer_group: { - id: "VIP", - }, - }, - shipping_methods: [ - { - id: "shipping_method_express", - subtotal: 250, - shipping_option: { - id: "express", - }, - }, - { - id: "shipping_method_standard", - subtotal: 150, - shipping_option: { - id: "standard", - }, - }, - { - id: "shipping_method_snail", - subtotal: 200, - shipping_option: { - id: "snail", - }, - }, - ], - } - ) - - expect(result).toEqual([ - { - action: "addShippingMethodAdjustment", - shipping_method_id: "shipping_method_express", - amount: 25, - code: "PROMOTION_TEST", - }, - { - action: "addShippingMethodAdjustment", - shipping_method_id: "shipping_method_standard", - amount: 15, - code: "PROMOTION_TEST", - }, - { - action: "addShippingMethodAdjustment", - shipping_method_id: "shipping_method_express", - amount: 22.5, - code: "PROMOTION_TEST_2", - }, - { - action: "addShippingMethodAdjustment", - shipping_method_id: "shipping_method_standard", - amount: 13.5, - code: "PROMOTION_TEST_2", - }, - ]) - }) - - it("should not compute actions when applicable total is 0", async () => { - const [createdPromotion] = await service.create([ - { - code: "PROMOTION_TEST", - type: PromotionType.STANDARD, - rules: [ - { - attribute: "customer.customer_group.id", - operator: "in", - values: ["VIP", "top100"], - }, - ], - application_method: { - type: ApplicationMethodType.PERCENTAGE, - target_type: "shipping_methods", - allocation: "each", - value: 10, - max_quantity: 2, - target_rules: [ - { - attribute: "shipping_option.id", - operator: "in", - values: ["express", "standard"], - }, - ], - }, - }, - ]) - - const [createdPromotionTwo] = await service.create([ - { - code: "PROMOTION_TEST_2", - type: PromotionType.STANDARD, - rules: [ - { - attribute: "customer.customer_group.id", - operator: "in", - values: ["VIP", "top100"], - }, - ], - application_method: { - type: ApplicationMethodType.PERCENTAGE, - target_type: "shipping_methods", - allocation: "each", - value: 10, - max_quantity: 2, - target_rules: [ - { - attribute: "shipping_option.id", - operator: "in", - values: ["express", "standard"], - }, - ], - }, - }, - ]) - - const result = await service.computeActions( - ["PROMOTION_TEST", "PROMOTION_TEST_2"], - { - customer: { - customer_group: { - id: "VIP", - }, - }, - shipping_methods: [ - { - id: "shipping_method_express", - subtotal: 250, - shipping_option: { - id: "express", - }, - }, - { - id: "shipping_method_standard", - subtotal: 150, - shipping_option: { - id: "standard", - }, - }, - { - id: "shipping_method_snail", - subtotal: 200, - shipping_option: { - id: "snail", - }, - }, - ], - } - ) - - expect(result).toEqual([ - { - action: "addShippingMethodAdjustment", - shipping_method_id: "shipping_method_express", - amount: 25, - code: "PROMOTION_TEST", - }, - { - action: "addShippingMethodAdjustment", - shipping_method_id: "shipping_method_standard", - amount: 15, - code: "PROMOTION_TEST", - }, - { - action: "addShippingMethodAdjustment", - shipping_method_id: "shipping_method_express", - amount: 22.5, - code: "PROMOTION_TEST_2", - }, - { - action: "addShippingMethodAdjustment", - shipping_method_id: "shipping_method_standard", - amount: 13.5, - code: "PROMOTION_TEST_2", - }, - ]) - }) - - it("should compute budget exceeded action when applicable total exceeds campaign budget for type spend", async () => { - await createCampaigns(repositoryManager) - - const [createdPromotion] = await service.create([ - { - code: "PROMOTION_TEST", - type: PromotionType.STANDARD, - rules: [ - { - attribute: "customer.customer_group.id", - operator: "in", - values: ["VIP", "top100"], - }, - ], - campaign_id: "campaign-id-1", - application_method: { - type: ApplicationMethodType.PERCENTAGE, - target_type: "shipping_methods", - allocation: "each", - value: 100, - max_quantity: 2, - target_rules: [ - { - attribute: "shipping_option.id", - operator: "in", - values: ["express", "standard"], - }, - ], - }, - }, - ]) - - const result = await service.computeActions(["PROMOTION_TEST"], { - customer: { - customer_group: { - id: "VIP", - }, - }, - shipping_methods: [ - { - id: "shipping_method_express", - subtotal: 1200, - shipping_option: { - id: "express", - }, - }, - ], - }) - - expect(result).toEqual([ - { action: "campaignBudgetExceeded", code: "PROMOTION_TEST" }, - ]) - }) - - it("should compute budget exceeded action when applicable total exceeds campaign budget for type usage", async () => { - await createCampaigns(repositoryManager) - - const [createdPromotion] = await service.create([ - { - code: "PROMOTION_TEST", - type: PromotionType.STANDARD, - rules: [ - { - attribute: "customer.customer_group.id", - operator: "in", - values: ["VIP", "top100"], - }, - ], - campaign_id: "campaign-id-2", - application_method: { - type: ApplicationMethodType.PERCENTAGE, - target_type: "shipping_methods", - allocation: "each", - value: 10, - max_quantity: 2, - target_rules: [ - { - attribute: "shipping_option.id", - operator: "in", - values: ["express", "standard"], - }, - ], - }, - }, - ]) - - await service.updateCampaigns({ - id: "campaign-id-2", - budget: { used: 1000 }, - }) - - const result = await service.computeActions(["PROMOTION_TEST"], { - customer: { - customer_group: { - id: "VIP", - }, - }, - shipping_methods: [ - { - id: "shipping_method_express", - subtotal: 1200, - shipping_option: { - id: "express", - }, - }, - ], - }) - - expect(result).toEqual([ - { action: "campaignBudgetExceeded", code: "PROMOTION_TEST" }, - ]) - }) - }) - }) - - describe("when promotion is for shipping_method and allocation is across", () => { - describe("when application type is fixed", () => { - it("should compute the correct shipping_method amendments", async () => { - const [createdPromotion] = await service.create([ - { - code: "PROMOTION_TEST", - type: PromotionType.STANDARD, - rules: [ - { - attribute: "customer.customer_group.id", - operator: "in", - values: ["VIP", "top100"], - }, - ], - application_method: { - type: ApplicationMethodType.FIXED, - target_type: "shipping_methods", - allocation: "across", - value: 200, - target_rules: [ - { - attribute: "shipping_option.id", - operator: "in", - values: ["express", "standard"], - }, - ], - }, - }, - ]) - - const result = await service.computeActions(["PROMOTION_TEST"], { - customer: { - customer_group: { - id: "VIP", - }, - }, - shipping_methods: [ - { - id: "shipping_method_express", - subtotal: 500, - shipping_option: { - id: "express", - }, - }, - { - id: "shipping_method_standard", - subtotal: 100, - shipping_option: { - id: "standard", - }, - }, - { - id: "shipping_method_snail", - subtotal: 200, - shipping_option: { - id: "snail", - }, - }, - ], - }) - - expect(result).toEqual([ - { - action: "addShippingMethodAdjustment", - shipping_method_id: "shipping_method_express", - amount: 166.66666666666669, - code: "PROMOTION_TEST", - }, - { - action: "addShippingMethodAdjustment", - shipping_method_id: "shipping_method_standard", - amount: 33.33333333333333, - code: "PROMOTION_TEST", - }, - ]) - }) - - it("should compute the correct shipping_method amendments when promotion is automatic", async () => { - const [createdPromotion] = await service.create([ - { - code: "PROMOTION_TEST", - type: PromotionType.STANDARD, - is_automatic: true, - rules: [ - { - attribute: "customer.customer_group.id", - operator: "in", - values: ["VIP", "top100"], - }, - ], - application_method: { - type: ApplicationMethodType.FIXED, - target_type: "shipping_methods", - allocation: "across", - value: 200, - target_rules: [ - { - attribute: "shipping_option.id", - operator: "in", - values: ["express", "standard"], - }, - ], - }, - }, - ]) - - const result = await service.computeActions([], { - customer: { - customer_group: { - id: "VIP", - }, - }, - shipping_methods: [ - { - id: "shipping_method_express", - subtotal: 500, - shipping_option: { - id: "express", - }, - }, - { - id: "shipping_method_standard", - subtotal: 100, - shipping_option: { - id: "standard", - }, - }, - { - id: "shipping_method_snail", - subtotal: 200, - shipping_option: { - id: "snail", - }, - }, - ], - }) - - expect(result).toEqual([ - { - action: "addShippingMethodAdjustment", - shipping_method_id: "shipping_method_express", - amount: 166.66666666666669, - code: "PROMOTION_TEST", - }, - { - action: "addShippingMethodAdjustment", - shipping_method_id: "shipping_method_standard", - amount: 33.33333333333333, - code: "PROMOTION_TEST", - }, - ]) - }) - - it("should compute the correct item amendments when there are multiple promotions to apply", async () => { - const [createdPromotion] = await service.create([ - { - code: "PROMOTION_TEST", - type: PromotionType.STANDARD, - rules: [ - { - attribute: "customer.customer_group.id", - operator: "in", - values: ["VIP", "top100"], - }, - ], - application_method: { - type: ApplicationMethodType.FIXED, - target_type: "shipping_methods", - allocation: "across", - value: 200, - target_rules: [ - { - attribute: "shipping_option.id", - operator: "in", - values: ["express", "standard"], - }, - ], - }, - }, - ]) - - const [createdPromotion2] = await service.create([ - { - code: "PROMOTION_TEST_2", - type: PromotionType.STANDARD, - rules: [ - { - attribute: "customer.customer_group.id", - operator: "in", - values: ["VIP", "top100"], - }, - ], - application_method: { - type: ApplicationMethodType.FIXED, - target_type: "shipping_methods", - allocation: "across", - value: 200, - target_rules: [ - { - attribute: "shipping_option.id", - operator: "in", - values: ["express", "standard"], - }, - ], - }, - }, - ]) - - const result = await service.computeActions( - ["PROMOTION_TEST", "PROMOTION_TEST_2"], - { + const result = await service.computeActions(["PROMOTION_TEST"], { customer: { customer_group: { id: "VIP", @@ -3389,6 +4597,12 @@ describe("Promotion Service: computeActions", () => { shipping_option: { id: "express", }, + adjustments: [ + { + id: "test-adjustment", + code: "ADJUSTMENT_CODE", + }, + ], }, { id: "shipping_method_standard", @@ -3405,1627 +4619,403 @@ describe("Promotion Service: computeActions", () => { }, }, ], - } - ) + }) - expect(result).toEqual([ - { - action: "addShippingMethodAdjustment", - shipping_method_id: "shipping_method_express", - amount: 166.66666666666669, - code: "PROMOTION_TEST", - }, - { - action: "addShippingMethodAdjustment", - shipping_method_id: "shipping_method_standard", - amount: 33.33333333333333, - code: "PROMOTION_TEST", - }, - { - action: "addShippingMethodAdjustment", - shipping_method_id: "shipping_method_express", - amount: 166.66666666666666, - code: "PROMOTION_TEST_2", - }, - { - action: "addShippingMethodAdjustment", - shipping_method_id: "shipping_method_standard", - amount: 33.333333333333336, - code: "PROMOTION_TEST_2", - }, - ]) + expect(result).toEqual([ + { + action: "removeShippingMethodAdjustment", + adjustment_id: "test-adjustment", + code: "ADJUSTMENT_CODE", + }, + { + action: "addShippingMethodAdjustment", + shipping_method_id: "shipping_method_express", + amount: 166.66666666666669, + code: "PROMOTION_TEST", + }, + { + action: "addShippingMethodAdjustment", + shipping_method_id: "shipping_method_standard", + amount: 33.33333333333333, + code: "PROMOTION_TEST", + }, + ]) + }) }) - it("should not compute actions when applicable total is 0", async () => { - const [createdPromotion] = await service.create([ - { - code: "PROMOTION_TEST", - type: PromotionType.STANDARD, - rules: [ - { - attribute: "customer.customer_group.id", - operator: "in", - values: ["VIP", "top100"], - }, - ], - application_method: { - type: ApplicationMethodType.FIXED, - target_type: "shipping_methods", - allocation: "across", - value: 1000, - target_rules: [ - { - attribute: "shipping_option.id", - operator: "in", - values: ["express", "standard"], - }, - ], - }, - }, - ]) - - const [createdPromotion2] = await service.create([ - { - code: "PROMOTION_TEST_2", - type: PromotionType.STANDARD, - rules: [ - { - attribute: "customer.customer_group.id", - operator: "in", - values: ["VIP", "top100"], - }, - ], - application_method: { - type: ApplicationMethodType.FIXED, - target_type: "shipping_methods", - allocation: "across", - value: 200, - target_rules: [ - { - attribute: "shipping_option.id", - operator: "in", - values: ["express", "standard"], - }, - ], - }, - }, - ]) - - const result = await service.computeActions( - ["PROMOTION_TEST", "PROMOTION_TEST_2"], - { + describe("when promotion of type buyget", () => { + it("should compute adjustment when target and buy rules match", async () => { + const context = { customer: { customer_group: { id: "VIP", }, }, - shipping_methods: [ + items: [ { - id: "shipping_method_express", - subtotal: 500, - shipping_option: { - id: "express", + id: "item_cotton_tshirt", + quantity: 2, + subtotal: 1000, + product_category: { + id: "catg_tshirt", + }, + product: { + id: "prod_tshirt_1", }, }, { - id: "shipping_method_standard", - subtotal: 100, - shipping_option: { - id: "standard", + id: "item_cotton_tshirt2", + quantity: 2, + subtotal: 2000, + product_category: { + id: "catg_tshirt", + }, + product: { + id: "prod_tshirt_2", }, }, { - id: "shipping_method_snail", - subtotal: 200, - shipping_option: { - id: "snail", + id: "item_cotton_sweater", + quantity: 2, + subtotal: 2000, + product_category: { + id: "catg_sweater", + }, + product: { + id: "prod_sweater_1", }, }, ], } - ) - expect(result).toEqual([ - { - action: "addShippingMethodAdjustment", - shipping_method_id: "shipping_method_express", - amount: 500, - code: "PROMOTION_TEST", - }, - { - action: "addShippingMethodAdjustment", - shipping_method_id: "shipping_method_standard", - amount: 100, - code: "PROMOTION_TEST", - }, - ]) - }) - - it("should compute budget exceeded action when applicable total exceeds campaign budget for type spend", async () => { - await createCampaigns(repositoryManager) - - const [createdPromotion] = await service.create([ - { - code: "PROMOTION_TEST", - type: PromotionType.STANDARD, - rules: [ - { - attribute: "customer.customer_group.id", - operator: "in", - values: ["VIP", "top100"], - }, - ], - campaign_id: "campaign-id-1", - application_method: { - type: ApplicationMethodType.FIXED, - target_type: "shipping_methods", - allocation: "across", - value: 1200, - target_rules: [ + const [createdPromotion] = await service.create([ + { + code: "PROMOTION_TEST", + type: PromotionType.BUYGET, + rules: [ { - attribute: "shipping_option.id", + attribute: "customer.customer_group.id", operator: "in", - values: ["express", "standard"], + values: ["VIP", "top100"], }, ], - }, - }, - ]) - - const result = await service.computeActions(["PROMOTION_TEST"], { - customer: { - customer_group: { - id: "VIP", - }, - }, - shipping_methods: [ - { - id: "shipping_method_express", - subtotal: 1200, - shipping_option: { - id: "express", + application_method: { + type: "fixed", + target_type: "items", + allocation: "each", + max_quantity: 1, + apply_to_quantity: 1, + buy_rules_min_quantity: 1, + target_rules: [ + { + attribute: "product_category.id", + operator: "eq", + values: ["catg_tshirt"], + }, + ], + buy_rules: [ + { + attribute: "product_category.id", + operator: "eq", + values: ["catg_sweater"], + }, + ], }, }, - ], + ]) + + const result = await service.computeActions( + ["PROMOTION_TEST"], + context + ) + + expect(result).toEqual([ + { + action: "addItemAdjustment", + item_id: "item_cotton_tshirt2", + amount: 1000, + code: "PROMOTION_TEST", + }, + ]) }) - expect(result).toEqual([ - { action: "campaignBudgetExceeded", code: "PROMOTION_TEST" }, - ]) - }) - - it("should compute budget exceeded action when applicable total exceeds campaign budget for type usage", async () => { - await createCampaigns(repositoryManager) - - const [createdPromotion] = await service.create([ - { - code: "PROMOTION_TEST", - type: PromotionType.STANDARD, - rules: [ - { - attribute: "customer.customer_group.id", - operator: "in", - values: ["VIP", "top100"], - }, - ], - campaign_id: "campaign-id-2", - application_method: { - type: ApplicationMethodType.FIXED, - target_type: "shipping_methods", - allocation: "across", - value: 1200, - target_rules: [ - { - attribute: "shipping_option.id", - operator: "in", - values: ["express", "standard"], - }, - ], - }, - }, - ]) - - await service.updateCampaigns({ - id: "campaign-id-2", - budget: { used: 1000 }, - }) - - const result = await service.computeActions(["PROMOTION_TEST"], { - customer: { - customer_group: { - id: "VIP", - }, - }, - shipping_methods: [ - { - id: "shipping_method_express", - subtotal: 1200, - shipping_option: { - id: "express", - }, - }, - ], - }) - - expect(result).toEqual([ - { action: "campaignBudgetExceeded", code: "PROMOTION_TEST" }, - ]) - }) - }) - - describe("when application type is percentage", () => { - it("should compute the correct shipping_method amendments", async () => { - const [createdPromotion] = await service.create([ - { - code: "PROMOTION_TEST", - type: PromotionType.STANDARD, - rules: [ - { - attribute: "customer.customer_group.id", - operator: "in", - values: ["VIP", "top100"], - }, - ], - application_method: { - type: ApplicationMethodType.PERCENTAGE, - target_type: "shipping_methods", - allocation: "across", - value: 10, - target_rules: [ - { - attribute: "shipping_option.id", - operator: "in", - values: ["express", "standard"], - }, - ], - }, - }, - ]) - - const result = await service.computeActions(["PROMOTION_TEST"], { - customer: { - customer_group: { - id: "VIP", - }, - }, - shipping_methods: [ - { - id: "shipping_method_express", - subtotal: 500, - shipping_option: { - id: "express", - }, - }, - { - id: "shipping_method_standard", - subtotal: 100, - shipping_option: { - id: "standard", - }, - }, - { - id: "shipping_method_snail", - subtotal: 200, - shipping_option: { - id: "snail", - }, - }, - ], - }) - - expect(result).toEqual([ - { - action: "addShippingMethodAdjustment", - shipping_method_id: "shipping_method_express", - amount: 50, - code: "PROMOTION_TEST", - }, - { - action: "addShippingMethodAdjustment", - shipping_method_id: "shipping_method_standard", - amount: 10, - code: "PROMOTION_TEST", - }, - ]) - }) - - it("should compute the correct shipping_method amendments when promotion is automatic", async () => { - const [createdPromotion] = await service.create([ - { - code: "PROMOTION_TEST", - type: PromotionType.STANDARD, - is_automatic: true, - rules: [ - { - attribute: "customer.customer_group.id", - operator: "in", - values: ["VIP", "top100"], - }, - ], - application_method: { - type: ApplicationMethodType.PERCENTAGE, - target_type: "shipping_methods", - allocation: "across", - value: 10, - target_rules: [ - { - attribute: "shipping_option.id", - operator: "in", - values: ["express", "standard"], - }, - ], - }, - }, - ]) - - const result = await service.computeActions([], { - customer: { - customer_group: { - id: "VIP", - }, - }, - shipping_methods: [ - { - id: "shipping_method_express", - subtotal: 500, - shipping_option: { - id: "express", - }, - }, - { - id: "shipping_method_standard", - subtotal: 100, - shipping_option: { - id: "standard", - }, - }, - { - id: "shipping_method_snail", - subtotal: 200, - shipping_option: { - id: "snail", - }, - }, - ], - }) - - expect(result).toEqual([ - { - action: "addShippingMethodAdjustment", - shipping_method_id: "shipping_method_express", - amount: 50, - code: "PROMOTION_TEST", - }, - { - action: "addShippingMethodAdjustment", - shipping_method_id: "shipping_method_standard", - amount: 10, - code: "PROMOTION_TEST", - }, - ]) - }) - - it("should compute the correct item amendments when there are multiple promotions to apply", async () => { - const [createdPromotion] = await service.create([ - { - code: "PROMOTION_TEST", - type: PromotionType.STANDARD, - rules: [ - { - attribute: "customer.customer_group.id", - operator: "in", - values: ["VIP", "top100"], - }, - ], - application_method: { - type: ApplicationMethodType.PERCENTAGE, - target_type: "shipping_methods", - allocation: "across", - value: 10, - target_rules: [ - { - attribute: "shipping_option.id", - operator: "in", - values: ["express", "standard"], - }, - ], - }, - }, - ]) - - const [createdPromotion2] = await service.create([ - { - code: "PROMOTION_TEST_2", - type: PromotionType.STANDARD, - rules: [ - { - attribute: "customer.customer_group.id", - operator: "in", - values: ["VIP", "top100"], - }, - ], - application_method: { - type: ApplicationMethodType.PERCENTAGE, - target_type: "shipping_methods", - allocation: "across", - value: 10, - target_rules: [ - { - attribute: "shipping_option.id", - operator: "in", - values: ["express", "standard"], - }, - ], - }, - }, - ]) - - const result = await service.computeActions( - ["PROMOTION_TEST", "PROMOTION_TEST_2"], - { + it("should return empty array when conditions for minimum qty aren't met", async () => { + const context = { customer: { customer_group: { id: "VIP", }, }, - shipping_methods: [ + items: [ { - id: "shipping_method_express", - subtotal: 500, - shipping_option: { - id: "express", + id: "item_cotton_tshirt", + quantity: 2, + subtotal: 1000, + product_category: { + id: "catg_tshirt", + }, + product: { + id: "prod_tshirt_1", }, }, { - id: "shipping_method_standard", - subtotal: 100, - shipping_option: { - id: "standard", + id: "item_cotton_tshirt2", + quantity: 2, + subtotal: 2000, + product_category: { + id: "catg_tshirt", + }, + product: { + id: "prod_tshirt_2", }, }, { - id: "shipping_method_snail", - subtotal: 200, - shipping_option: { - id: "snail", + id: "item_cotton_sweater", + quantity: 2, + subtotal: 2000, + product_category: { + id: "catg_sweater", + }, + product: { + id: "prod_sweater_1", }, }, ], } - ) - expect(result).toEqual([ - { - action: "addShippingMethodAdjustment", - shipping_method_id: "shipping_method_express", - amount: 50, - code: "PROMOTION_TEST", - }, - { - action: "addShippingMethodAdjustment", - shipping_method_id: "shipping_method_standard", - amount: 10, - code: "PROMOTION_TEST", - }, - { - action: "addShippingMethodAdjustment", - shipping_method_id: "shipping_method_express", - amount: 45, - code: "PROMOTION_TEST_2", - }, - { - action: "addShippingMethodAdjustment", - shipping_method_id: "shipping_method_standard", - amount: 9, - code: "PROMOTION_TEST_2", - }, - ]) - }) - - it("should not compute actions when applicable total is 0", async () => { - const [createdPromotion] = await service.create([ - { - code: "PROMOTION_TEST", - type: PromotionType.STANDARD, - rules: [ - { - attribute: "customer.customer_group.id", - operator: "in", - values: ["VIP", "top100"], - }, - ], - application_method: { - type: ApplicationMethodType.PERCENTAGE, - target_type: "shipping_methods", - allocation: "across", - value: 100, - target_rules: [ + const [createdPromotion] = await service.create([ + { + code: "PROMOTION_TEST", + type: PromotionType.BUYGET, + rules: [ { - attribute: "shipping_option.id", + attribute: "customer.customer_group.id", operator: "in", - values: ["express", "standard"], + values: ["VIP", "top100"], }, ], - }, - }, - ]) - - const [createdPromotion2] = await service.create([ - { - code: "PROMOTION_TEST_2", - type: PromotionType.STANDARD, - rules: [ - { - attribute: "customer.customer_group.id", - operator: "in", - values: ["VIP", "top100"], + application_method: { + type: "fixed", + target_type: "items", + allocation: "each", + max_quantity: 1, + apply_to_quantity: 1, + buy_rules_min_quantity: 4, + target_rules: [ + { + attribute: "product_category.id", + operator: "eq", + values: ["catg_tshirt"], + }, + ], + buy_rules: [ + { + attribute: "product_category.id", + operator: "eq", + values: ["catg_sweater"], + }, + ], }, - ], - application_method: { - type: ApplicationMethodType.PERCENTAGE, - target_type: "shipping_methods", - allocation: "across", - value: 10, - target_rules: [ - { - attribute: "shipping_option.id", - operator: "in", - values: ["express", "standard"], - }, - ], }, - }, - ]) + ]) - const result = await service.computeActions( - ["PROMOTION_TEST", "PROMOTION_TEST_2"], - { + const result = await service.computeActions( + ["PROMOTION_TEST"], + context + ) + + expect(result).toEqual([]) + }) + + it("should compute actions for multiple items when conditions for target qty exceed one item", async () => { + const context = { customer: { customer_group: { id: "VIP", }, }, - shipping_methods: [ + items: [ { - id: "shipping_method_express", - subtotal: 500, - shipping_option: { - id: "express", + id: "item_cotton_tshirt", + quantity: 2, + subtotal: 1000, + product_category: { + id: "catg_tshirt", + }, + product: { + id: "prod_tshirt_1", }, }, { - id: "shipping_method_standard", - subtotal: 100, - shipping_option: { - id: "standard", + id: "item_cotton_tshirt2", + quantity: 2, + subtotal: 2000, + product_category: { + id: "catg_tshirt", + }, + product: { + id: "prod_tshirt_2", }, }, { - id: "shipping_method_snail", - subtotal: 200, - shipping_option: { - id: "snail", + id: "item_cotton_sweater", + quantity: 2, + subtotal: 2000, + product_category: { + id: "catg_sweater", + }, + product: { + id: "prod_sweater_1", }, }, ], } - ) - expect(result).toEqual([ - { - action: "addShippingMethodAdjustment", - shipping_method_id: "shipping_method_express", - amount: 500, - code: "PROMOTION_TEST", - }, - { - action: "addShippingMethodAdjustment", - shipping_method_id: "shipping_method_standard", - amount: 100, - code: "PROMOTION_TEST", - }, - ]) - }) - - it("should compute budget exceeded action when applicable total exceeds campaign budget for type spend", async () => { - await createCampaigns(repositoryManager) - - const [createdPromotion] = await service.create([ - { - code: "PROMOTION_TEST", - type: PromotionType.STANDARD, - rules: [ - { - attribute: "customer.customer_group.id", - operator: "in", - values: ["VIP", "top100"], - }, - ], - campaign_id: "campaign-id-1", - application_method: { - type: ApplicationMethodType.PERCENTAGE, - target_type: "shipping_methods", - allocation: "across", - value: 100, - target_rules: [ + const [createdPromotion] = await service.create([ + { + code: "PROMOTION_TEST", + type: PromotionType.BUYGET, + rules: [ { - attribute: "shipping_option.id", + attribute: "customer.customer_group.id", operator: "in", - values: ["express", "standard"], + values: ["VIP", "top100"], }, ], - }, - }, - ]) - - const result = await service.computeActions(["PROMOTION_TEST"], { - customer: { - customer_group: { - id: "VIP", - }, - }, - shipping_methods: [ - { - id: "shipping_method_express", - subtotal: 1200, - shipping_option: { - id: "express", + application_method: { + type: "fixed", + target_type: "items", + allocation: "each", + max_quantity: 1, + apply_to_quantity: 4, + buy_rules_min_quantity: 1, + target_rules: [ + { + attribute: "product_category.id", + operator: "eq", + values: ["catg_tshirt"], + }, + ], + buy_rules: [ + { + attribute: "product_category.id", + operator: "eq", + values: ["catg_sweater"], + }, + ], }, }, - ], + ]) + + const result = await service.computeActions( + ["PROMOTION_TEST"], + context + ) + + expect(result).toEqual([ + { + action: "addItemAdjustment", + item_id: "item_cotton_tshirt2", + amount: 2000, + code: "PROMOTION_TEST", + }, + { + action: "addItemAdjustment", + item_id: "item_cotton_tshirt", + amount: 1000, + code: "PROMOTION_TEST", + }, + ]) }) - expect(result).toEqual([ - { action: "campaignBudgetExceeded", code: "PROMOTION_TEST" }, - ]) - }) - - it("should compute budget exceeded action when applicable total exceeds campaign budget for type usage", async () => { - await createCampaigns(repositoryManager) - - const [createdPromotion] = await service.create([ - { - code: "PROMOTION_TEST", - type: PromotionType.STANDARD, - rules: [ + it("should return empty array when target rules arent met with context", async () => { + const context = { + customer: { + customer_group: { + id: "VIP", + }, + }, + items: [ { - attribute: "customer.customer_group.id", - operator: "in", - values: ["VIP", "top100"], + id: "item_cotton_tshirt", + quantity: 2, + subtotal: 1000, + product_category: { + id: "catg_tshirt", + }, + product: { + id: "prod_tshirt_1", + }, + }, + { + id: "item_cotton_tshirt2", + quantity: 2, + subtotal: 2000, + product_category: { + id: "catg_tshirt", + }, + product: { + id: "prod_tshirt_2", + }, + }, + { + id: "item_cotton_sweater", + quantity: 2, + subtotal: 2000, + product_category: { + id: "catg_sweater", + }, + product: { + id: "prod_sweater_1", + }, }, ], - campaign_id: "campaign-id-2", - application_method: { - type: ApplicationMethodType.PERCENTAGE, - target_type: "shipping_methods", - allocation: "across", - value: 10, - target_rules: [ + } + + const [createdPromotion] = await service.create([ + { + code: "PROMOTION_TEST", + type: PromotionType.BUYGET, + rules: [ { - attribute: "shipping_option.id", + attribute: "customer.customer_group.id", operator: "in", - values: ["express", "standard"], + values: ["VIP", "top100"], }, ], + application_method: { + type: "fixed", + target_type: "items", + allocation: "each", + max_quantity: 1, + apply_to_quantity: 4, + buy_rules_min_quantity: 1, + target_rules: [ + { + attribute: "product_category.id", + operator: "eq", + values: ["catg_not-found"], + }, + ], + buy_rules: [ + { + attribute: "product_category.id", + operator: "eq", + values: ["catg_sweater"], + }, + ], + }, }, - }, - ]) + ]) - await service.updateCampaigns({ - id: "campaign-id-2", - budget: { used: 1000 }, + const result = await service.computeActions( + ["PROMOTION_TEST"], + context + ) + + expect(result).toEqual([]) }) - - const result = await service.computeActions(["PROMOTION_TEST"], { - customer: { - customer_group: { - id: "VIP", - }, - }, - shipping_methods: [ - { - id: "shipping_method_express", - subtotal: 1200, - shipping_option: { - id: "express", - }, - }, - ], - }) - - expect(result).toEqual([ - { action: "campaignBudgetExceeded", code: "PROMOTION_TEST" }, - ]) }) }) - }) - - describe("when promotion is for the entire order", () => { - it("should compute the correct item amendments", async () => { - const [createdPromotion] = await service.create([ - { - code: "PROMOTION_TEST", - type: PromotionType.STANDARD, - rules: [ - { - attribute: "customer.customer_group.id", - operator: "in", - values: ["VIP", "top100"], - }, - ], - application_method: { - type: "fixed", - target_type: "order", - value: 200, - max_quantity: 2, - }, - }, - ]) - - const result = await service.computeActions(["PROMOTION_TEST"], { - customer: { - customer_group: { - id: "VIP", - }, - }, - items: [ - { - id: "item_cotton_tshirt", - quantity: 1, - subtotal: 100, - product_category: { - id: "catg_cotton", - }, - product: { - id: "prod_tshirt", - }, - }, - { - id: "item_cotton_sweater", - quantity: 2, - subtotal: 300, - product_category: { - id: "catg_cotton", - }, - product: { - id: "prod_sweater", - }, - }, - ], - }) - - expect(result).toEqual([ - { - action: "addItemAdjustment", - item_id: "item_cotton_tshirt", - amount: 50, - code: "PROMOTION_TEST", - }, - { - action: "addItemAdjustment", - item_id: "item_cotton_sweater", - amount: 150, - code: "PROMOTION_TEST", - }, - ]) - }) - - it("should compute the correct item amendments when promotion is automatic", async () => { - const [createdPromotion] = await service.create([ - { - code: "PROMOTION_TEST", - type: PromotionType.STANDARD, - is_automatic: true, - rules: [ - { - attribute: "customer.customer_group.id", - operator: "in", - values: ["VIP", "top100"], - }, - ], - application_method: { - type: "fixed", - target_type: "order", - value: 200, - max_quantity: 2, - }, - }, - ]) - - const result = await service.computeActions([], { - customer: { - customer_group: { - id: "VIP", - }, - }, - items: [ - { - id: "item_cotton_tshirt", - quantity: 1, - subtotal: 100, - product_category: { - id: "catg_cotton", - }, - product: { - id: "prod_tshirt", - }, - }, - { - id: "item_cotton_sweater", - quantity: 2, - subtotal: 300, - product_category: { - id: "catg_cotton", - }, - product: { - id: "prod_sweater", - }, - }, - ], - }) - - expect(result).toEqual([ - { - action: "addItemAdjustment", - item_id: "item_cotton_tshirt", - amount: 50, - code: "PROMOTION_TEST", - }, - { - action: "addItemAdjustment", - item_id: "item_cotton_sweater", - amount: 150, - code: "PROMOTION_TEST", - }, - ]) - }) - - it("should compute the correct item amendments when there are multiple promotions to apply", async () => { - const [createdPromotion] = await service.create([ - { - code: "PROMOTION_TEST", - type: PromotionType.STANDARD, - rules: [ - { - attribute: "customer.customer_group.id", - operator: "in", - values: ["VIP", "top100"], - }, - ], - application_method: { - type: "fixed", - target_type: "order", - value: 30, - max_quantity: 2, - }, - }, - ]) - - const [createdPromotionTwo] = await service.create([ - { - code: "PROMOTION_TEST_2", - type: PromotionType.STANDARD, - rules: [ - { - attribute: "customer.customer_group.id", - operator: "in", - values: ["VIP", "top100"], - }, - ], - application_method: { - type: "fixed", - target_type: "order", - value: 50, - max_quantity: 1, - }, - }, - ]) - - const result = await service.computeActions( - ["PROMOTION_TEST", "PROMOTION_TEST_2"], - { - customer: { - customer_group: { - id: "VIP", - }, - }, - items: [ - { - id: "item_cotton_tshirt", - quantity: 1, - subtotal: 50, - product_category: { - id: "catg_cotton", - }, - product: { - id: "prod_tshirt", - }, - }, - { - id: "item_cotton_sweater", - quantity: 1, - subtotal: 150, - product_category: { - id: "catg_cotton", - }, - product: { - id: "prod_sweater", - }, - }, - ], - } - ) - - expect(result).toEqual([ - { - action: "addItemAdjustment", - item_id: "item_cotton_tshirt", - amount: 7.5, - code: "PROMOTION_TEST", - }, - { - action: "addItemAdjustment", - item_id: "item_cotton_sweater", - amount: 22.5, - code: "PROMOTION_TEST", - }, - { - action: "addItemAdjustment", - item_id: "item_cotton_tshirt", - amount: 12.5, - code: "PROMOTION_TEST_2", - }, - { - action: "addItemAdjustment", - item_id: "item_cotton_sweater", - amount: 37.5, - code: "PROMOTION_TEST_2", - }, - ]) - }) - - it("should not compute actions when applicable total is 0", async () => { - const [createdPromotion] = await service.create([ - { - code: "PROMOTION_TEST", - type: PromotionType.STANDARD, - rules: [ - { - attribute: "customer.customer_group.id", - operator: "in", - values: ["VIP", "top100"], - }, - ], - application_method: { - type: "fixed", - target_type: "order", - value: 500, - max_quantity: 2, - }, - }, - ]) - - const [createdPromotionTwo] = await service.create([ - { - code: "PROMOTION_TEST_2", - type: PromotionType.STANDARD, - rules: [ - { - attribute: "customer.customer_group.id", - operator: "in", - values: ["VIP", "top100"], - }, - ], - application_method: { - type: "fixed", - target_type: "order", - value: 50, - max_quantity: 1, - }, - }, - ]) - - const result = await service.computeActions( - ["PROMOTION_TEST", "PROMOTION_TEST_2"], - { - customer: { - customer_group: { - id: "VIP", - }, - }, - items: [ - { - id: "item_cotton_tshirt", - quantity: 1, - subtotal: 50, - product_category: { - id: "catg_cotton", - }, - product: { - id: "prod_tshirt", - }, - }, - { - id: "item_cotton_sweater", - quantity: 1, - subtotal: 150, - product_category: { - id: "catg_cotton", - }, - product: { - id: "prod_sweater", - }, - }, - ], - } - ) - - expect(result).toEqual([ - { - action: "addItemAdjustment", - item_id: "item_cotton_tshirt", - amount: 50, - code: "PROMOTION_TEST", - }, - { - action: "addItemAdjustment", - item_id: "item_cotton_sweater", - amount: 150, - code: "PROMOTION_TEST", - }, - ]) - }) - }) - - describe("when adjustments are present in the context", () => { - it("should compute the correct item amendments along with removal of applied item adjustment", async () => { - const [adjustmentPromotion] = await service.create([ - { - code: "ADJUSTMENT_CODE", - type: PromotionType.STANDARD, - }, - ]) - - const [createdPromotion] = await service.create([ - { - code: "PROMOTION_TEST", - type: PromotionType.STANDARD, - rules: [ - { - attribute: "customer.customer_group.id", - operator: "in", - values: ["VIP", "top100"], - }, - ], - application_method: { - type: "fixed", - target_type: "items", - allocation: "each", - value: 200, - max_quantity: 1, - target_rules: [ - { - attribute: "product_category.id", - operator: "eq", - values: ["catg_cotton"], - }, - ], - }, - }, - ]) - - const result = await service.computeActions(["PROMOTION_TEST"], { - customer: { - customer_group: { - id: "VIP", - }, - }, - items: [ - { - id: "item_cotton_tshirt", - quantity: 1, - subtotal: 100, - product_category: { - id: "catg_cotton", - }, - product: { - id: "prod_tshirt", - }, - adjustments: [ - { - id: "test-adjustment", - code: "ADJUSTMENT_CODE", - }, - ], - }, - { - id: "item_cotton_sweater", - quantity: 5, - subtotal: 750, - product_category: { - id: "catg_cotton", - }, - product: { - id: "prod_sweater", - }, - }, - ], - }) - - expect(result).toEqual([ - { - action: "removeItemAdjustment", - adjustment_id: "test-adjustment", - code: "ADJUSTMENT_CODE", - }, - { - action: "addItemAdjustment", - item_id: "item_cotton_tshirt", - amount: 100, - code: "PROMOTION_TEST", - }, - { - action: "addItemAdjustment", - item_id: "item_cotton_sweater", - amount: 150, - code: "PROMOTION_TEST", - }, - ]) - }) - - it("should compute the correct item amendments along with removal of applied shipping adjustment", async () => { - const [adjustmentPromotion] = await service.create([ - { - code: "ADJUSTMENT_CODE", - type: PromotionType.STANDARD, - }, - ]) - - const [createdPromotion] = await service.create([ - { - code: "PROMOTION_TEST", - type: PromotionType.STANDARD, - rules: [ - { - attribute: "customer.customer_group.id", - operator: "in", - values: ["VIP", "top100"], - }, - ], - application_method: { - type: "fixed", - target_type: "shipping_methods", - allocation: "across", - value: 200, - target_rules: [ - { - attribute: "shipping_option.id", - operator: "in", - values: ["express", "standard"], - }, - ], - }, - }, - ]) - - const result = await service.computeActions(["PROMOTION_TEST"], { - customer: { - customer_group: { - id: "VIP", - }, - }, - shipping_methods: [ - { - id: "shipping_method_express", - subtotal: 500, - shipping_option: { - id: "express", - }, - adjustments: [ - { - id: "test-adjustment", - code: "ADJUSTMENT_CODE", - }, - ], - }, - { - id: "shipping_method_standard", - subtotal: 100, - shipping_option: { - id: "standard", - }, - }, - { - id: "shipping_method_snail", - subtotal: 200, - shipping_option: { - id: "snail", - }, - }, - ], - }) - - expect(result).toEqual([ - { - action: "removeShippingMethodAdjustment", - adjustment_id: "test-adjustment", - code: "ADJUSTMENT_CODE", - }, - { - action: "addShippingMethodAdjustment", - shipping_method_id: "shipping_method_express", - amount: 166.66666666666669, - code: "PROMOTION_TEST", - }, - { - action: "addShippingMethodAdjustment", - shipping_method_id: "shipping_method_standard", - amount: 33.33333333333333, - code: "PROMOTION_TEST", - }, - ]) - }) - }) - - describe("when promotion of type buyget", () => { - it("should compute adjustment when target and buy rules match", async () => { - const context = { - customer: { - customer_group: { - id: "VIP", - }, - }, - items: [ - { - id: "item_cotton_tshirt", - quantity: 2, - subtotal: 1000, - product_category: { - id: "catg_tshirt", - }, - product: { - id: "prod_tshirt_1", - }, - }, - { - id: "item_cotton_tshirt2", - quantity: 2, - subtotal: 2000, - product_category: { - id: "catg_tshirt", - }, - product: { - id: "prod_tshirt_2", - }, - }, - { - id: "item_cotton_sweater", - quantity: 2, - subtotal: 2000, - product_category: { - id: "catg_sweater", - }, - product: { - id: "prod_sweater_1", - }, - }, - ], - } - - const [createdPromotion] = await service.create([ - { - code: "PROMOTION_TEST", - type: PromotionType.BUYGET, - rules: [ - { - attribute: "customer.customer_group.id", - operator: "in", - values: ["VIP", "top100"], - }, - ], - application_method: { - type: "fixed", - target_type: "items", - allocation: "each", - max_quantity: 1, - apply_to_quantity: 1, - buy_rules_min_quantity: 1, - target_rules: [ - { - attribute: "product_category.id", - operator: "eq", - values: ["catg_tshirt"], - }, - ], - buy_rules: [ - { - attribute: "product_category.id", - operator: "eq", - values: ["catg_sweater"], - }, - ], - }, - }, - ]) - - const result = await service.computeActions(["PROMOTION_TEST"], context) - - expect(result).toEqual([ - { - action: "addItemAdjustment", - item_id: "item_cotton_tshirt2", - amount: 1000, - code: "PROMOTION_TEST", - }, - ]) - }) - - it("should return empty array when conditions for minimum qty aren't met", async () => { - const context = { - customer: { - customer_group: { - id: "VIP", - }, - }, - items: [ - { - id: "item_cotton_tshirt", - quantity: 2, - subtotal: 1000, - product_category: { - id: "catg_tshirt", - }, - product: { - id: "prod_tshirt_1", - }, - }, - { - id: "item_cotton_tshirt2", - quantity: 2, - subtotal: 2000, - product_category: { - id: "catg_tshirt", - }, - product: { - id: "prod_tshirt_2", - }, - }, - { - id: "item_cotton_sweater", - quantity: 2, - subtotal: 2000, - product_category: { - id: "catg_sweater", - }, - product: { - id: "prod_sweater_1", - }, - }, - ], - } - - const [createdPromotion] = await service.create([ - { - code: "PROMOTION_TEST", - type: PromotionType.BUYGET, - rules: [ - { - attribute: "customer.customer_group.id", - operator: "in", - values: ["VIP", "top100"], - }, - ], - application_method: { - type: "fixed", - target_type: "items", - allocation: "each", - max_quantity: 1, - apply_to_quantity: 1, - buy_rules_min_quantity: 4, - target_rules: [ - { - attribute: "product_category.id", - operator: "eq", - values: ["catg_tshirt"], - }, - ], - buy_rules: [ - { - attribute: "product_category.id", - operator: "eq", - values: ["catg_sweater"], - }, - ], - }, - }, - ]) - - const result = await service.computeActions(["PROMOTION_TEST"], context) - - expect(result).toEqual([]) - }) - - it("should compute actions for multiple items when conditions for target qty exceed one item", async () => { - const context = { - customer: { - customer_group: { - id: "VIP", - }, - }, - items: [ - { - id: "item_cotton_tshirt", - quantity: 2, - subtotal: 1000, - product_category: { - id: "catg_tshirt", - }, - product: { - id: "prod_tshirt_1", - }, - }, - { - id: "item_cotton_tshirt2", - quantity: 2, - subtotal: 2000, - product_category: { - id: "catg_tshirt", - }, - product: { - id: "prod_tshirt_2", - }, - }, - { - id: "item_cotton_sweater", - quantity: 2, - subtotal: 2000, - product_category: { - id: "catg_sweater", - }, - product: { - id: "prod_sweater_1", - }, - }, - ], - } - - const [createdPromotion] = await service.create([ - { - code: "PROMOTION_TEST", - type: PromotionType.BUYGET, - rules: [ - { - attribute: "customer.customer_group.id", - operator: "in", - values: ["VIP", "top100"], - }, - ], - application_method: { - type: "fixed", - target_type: "items", - allocation: "each", - max_quantity: 1, - apply_to_quantity: 4, - buy_rules_min_quantity: 1, - target_rules: [ - { - attribute: "product_category.id", - operator: "eq", - values: ["catg_tshirt"], - }, - ], - buy_rules: [ - { - attribute: "product_category.id", - operator: "eq", - values: ["catg_sweater"], - }, - ], - }, - }, - ]) - - const result = await service.computeActions(["PROMOTION_TEST"], context) - - expect(result).toEqual([ - { - action: "addItemAdjustment", - item_id: "item_cotton_tshirt2", - amount: 2000, - code: "PROMOTION_TEST", - }, - { - action: "addItemAdjustment", - item_id: "item_cotton_tshirt", - amount: 1000, - code: "PROMOTION_TEST", - }, - ]) - }) - - it("should return empty array when target rules arent met with context", async () => { - const context = { - customer: { - customer_group: { - id: "VIP", - }, - }, - items: [ - { - id: "item_cotton_tshirt", - quantity: 2, - subtotal: 1000, - product_category: { - id: "catg_tshirt", - }, - product: { - id: "prod_tshirt_1", - }, - }, - { - id: "item_cotton_tshirt2", - quantity: 2, - subtotal: 2000, - product_category: { - id: "catg_tshirt", - }, - product: { - id: "prod_tshirt_2", - }, - }, - { - id: "item_cotton_sweater", - quantity: 2, - subtotal: 2000, - product_category: { - id: "catg_sweater", - }, - product: { - id: "prod_sweater_1", - }, - }, - ], - } - - const [createdPromotion] = await service.create([ - { - code: "PROMOTION_TEST", - type: PromotionType.BUYGET, - rules: [ - { - attribute: "customer.customer_group.id", - operator: "in", - values: ["VIP", "top100"], - }, - ], - application_method: { - type: "fixed", - target_type: "items", - allocation: "each", - max_quantity: 1, - apply_to_quantity: 4, - buy_rules_min_quantity: 1, - target_rules: [ - { - attribute: "product_category.id", - operator: "eq", - values: ["catg_not-found"], - }, - ], - buy_rules: [ - { - attribute: "product_category.id", - operator: "eq", - values: ["catg_sweater"], - }, - ], - }, - }, - ]) - - const result = await service.computeActions(["PROMOTION_TEST"], context) - - expect(result).toEqual([]) - }) - }) + }, }) diff --git a/packages/promotion/integration-tests/__tests__/services/promotion-module/promotion.spec.ts b/packages/promotion/integration-tests/__tests__/services/promotion-module/promotion.spec.ts index 755afaa96a..d18b4eac94 100644 --- a/packages/promotion/integration-tests/__tests__/services/promotion-module/promotion.spec.ts +++ b/packages/promotion/integration-tests/__tests__/services/promotion-module/promotion.spec.ts @@ -6,1495 +6,1489 @@ import { CampaignBudgetType, PromotionType, } from "@medusajs/utils" -import { SqlEntityManager } from "@mikro-orm/postgresql" -import { initModules } from "medusa-test-utils" import { createCampaigns } from "../../../__fixtures__/campaigns" import { createPromotions } from "../../../__fixtures__/promotion" -import { MikroOrmWrapper } from "../../../utils" -import { getInitModuleConfig } from "../../../utils/get-init-module-config" +import { moduleIntegrationTestRunner, SuiteOptions } from "medusa-test-utils" jest.setTimeout(30000) -describe("Promotion Service", () => { - let service: IPromotionModuleService - let repositoryManager: SqlEntityManager - let shutdownFunc: () => void +moduleIntegrationTestRunner({ + moduleName: Modules.PROMOTION, + testSuite: ({ + MikroOrmWrapper, + service, + }: SuiteOptions) => { + describe("Promotion Service", () => { + describe("create", () => { + it("should throw an error when required params are not passed", async () => { + const error = await service + .create([ + { + type: PromotionType.STANDARD, + } as any, + ]) + .catch((e) => e) - beforeAll(async () => { - const initModulesConfig = getInitModuleConfig() - - const { medusaApp, shutdown } = await initModules(initModulesConfig) - - service = medusaApp.modules[Modules.PROMOTION] - - shutdownFunc = shutdown - }) - - afterAll(async () => { - shutdownFunc() - }) - - beforeEach(async () => { - await MikroOrmWrapper.setupDatabase() - repositoryManager = MikroOrmWrapper.forkManager() - }) - - afterEach(async () => { - await MikroOrmWrapper.clearDatabase() - }) - - describe("create", () => { - it("should throw an error when required params are not passed", async () => { - const error = await service - .create([ - { - type: PromotionType.STANDARD, - } as any, - ]) - .catch((e) => e) - - expect(error.message).toContain( - "Value for Promotion.code is required, 'undefined' found" - ) - }) - - it("should create a basic promotion successfully", async () => { - const [createdPromotion] = await service.create([ - { - code: "PROMOTION_TEST", - type: PromotionType.STANDARD, - }, - ]) - - const [promotion] = await service.list({ - id: [createdPromotion.id], - }) - - expect(promotion).toEqual( - expect.objectContaining({ - code: "PROMOTION_TEST", - is_automatic: false, - type: "standard", + expect(error.message).toContain( + "Value for Promotion.code is required, 'undefined' found" + ) }) - ) - }) - it("should create a promotion with order application method successfully", async () => { - const [createdPromotion] = await service.create([ - { - code: "PROMOTION_TEST", - type: PromotionType.STANDARD, - application_method: { - type: "fixed", - target_type: "order", - value: "100", - }, - }, - ]) - - const [promotion] = await service.list( - { - id: [createdPromotion.id], - }, - { - relations: ["application_method"], - } - ) - - expect(promotion).toEqual( - expect.objectContaining({ - code: "PROMOTION_TEST", - is_automatic: false, - type: "standard", - application_method: expect.objectContaining({ - type: "fixed", - target_type: "order", - value: 100, - }), - }) - ) - }) - - it("should throw error when percentage type and value is greater than 100", async () => { - const error = await service - .create({ - code: "PROMOTION_TEST", - type: PromotionType.STANDARD, - application_method: { - type: ApplicationMethodType.PERCENTAGE, - target_type: ApplicationMethodTargetType.ORDER, - value: "1000", - }, - }) - .catch((e) => e) - - expect(error.message).toContain( - "Application Method value should be a percentage number between 0 and 100" - ) - }) - - it("should throw an error when both campaign and campaign_id are provided", async () => { - const startsAt = new Date("01/01/2023") - const endsAt = new Date("01/01/2023") - - const error = await service - .create({ - code: "PROMOTION_TEST", - type: PromotionType.STANDARD, - campaign_id: "campaign-id-1", - campaign: { - name: "test", - campaign_identifier: "test-promotion-test", - starts_at: startsAt, - ends_at: endsAt, - budget: { - type: CampaignBudgetType.SPEND, - used: 100, - limit: 100, + it("should create a basic promotion successfully", async () => { + const [createdPromotion] = await service.create([ + { + code: "PROMOTION_TEST", + type: PromotionType.STANDARD, }, - }, + ]) + + const [promotion] = await service.list({ + id: [createdPromotion.id], + }) + + expect(promotion).toEqual( + expect.objectContaining({ + code: "PROMOTION_TEST", + is_automatic: false, + type: "standard", + }) + ) }) - .catch((e) => e) - expect(error.message).toContain( - "Provide either the 'campaign' or 'campaign_id' parameter; both cannot be used simultaneously." - ) - }) + it("should create a promotion with order application method successfully", async () => { + const [createdPromotion] = await service.create([ + { + code: "PROMOTION_TEST", + type: PromotionType.STANDARD, + application_method: { + type: "fixed", + target_type: "order", + value: "100", + }, + }, + ]) - it("should create a basic promotion with campaign successfully", async () => { - const startsAt = new Date("01/01/2023") - const endsAt = new Date("01/01/2023") + const [promotion] = await service.list( + { + id: [createdPromotion.id], + }, + { + relations: ["application_method"], + } + ) - await createCampaigns(repositoryManager) - - const createdPromotion = await service.create({ - code: "PROMOTION_TEST", - type: PromotionType.STANDARD, - campaign: { - name: "test", - campaign_identifier: "test-promotion-test", - starts_at: startsAt, - ends_at: endsAt, - budget: { - type: CampaignBudgetType.SPEND, - used: 100, - limit: 100, - }, - }, - }) - - const [promotion] = await service.list( - { id: [createdPromotion.id] }, - { relations: ["campaign.budget"] } - ) - - expect(promotion).toEqual( - expect.objectContaining({ - code: "PROMOTION_TEST", - is_automatic: false, - type: "standard", - campaign: expect.objectContaining({ - name: "test", - campaign_identifier: "test-promotion-test", - starts_at: startsAt, - ends_at: endsAt, - budget: expect.objectContaining({ - type: CampaignBudgetType.SPEND, - used: 100, - limit: 100, - }), - }), + expect(promotion).toEqual( + expect.objectContaining({ + code: "PROMOTION_TEST", + is_automatic: false, + type: "standard", + application_method: expect.objectContaining({ + type: "fixed", + target_type: "order", + value: 100, + }), + }) + ) }) - ) - }) - it("should create a basic promotion with an existing campaign successfully", async () => { - await createCampaigns(repositoryManager) + it("should throw error when percentage type and value is greater than 100", async () => { + const error = await service + .create({ + code: "PROMOTION_TEST", + type: PromotionType.STANDARD, + application_method: { + type: ApplicationMethodType.PERCENTAGE, + target_type: ApplicationMethodTargetType.ORDER, + value: "1000", + }, + }) + .catch((e) => e) - const createdPromotion = await service.create({ - code: "PROMOTION_TEST", - type: PromotionType.STANDARD, - campaign_id: "campaign-id-1", - }) - - const [promotion] = await service.list( - { id: [createdPromotion.id] }, - { relations: ["campaign.budget"] } - ) - - expect(promotion).toEqual( - expect.objectContaining({ - code: "PROMOTION_TEST", - is_automatic: false, - type: "standard", - campaign: expect.objectContaining({ - id: "campaign-id-1", - budget: expect.objectContaining({ - type: CampaignBudgetType.SPEND, - limit: 1000, - used: 0, - }), - }), + expect(error.message).toContain( + "Application Method value should be a percentage number between 0 and 100" + ) }) - ) - }) - it("should throw error when creating an item application method without allocation", async () => { - const error = await service - .create([ - { + it("should throw an error when both campaign and campaign_id are provided", async () => { + const startsAt = new Date("01/01/2023") + const endsAt = new Date("01/01/2023") + + const error = await service + .create({ + code: "PROMOTION_TEST", + type: PromotionType.STANDARD, + campaign_id: "campaign-id-1", + campaign: { + name: "test", + campaign_identifier: "test-promotion-test", + starts_at: startsAt, + ends_at: endsAt, + budget: { + type: CampaignBudgetType.SPEND, + used: 100, + limit: 100, + }, + }, + }) + .catch((e) => e) + + expect(error.message).toContain( + "Provide either the 'campaign' or 'campaign_id' parameter; both cannot be used simultaneously." + ) + }) + + it("should create a basic promotion with campaign successfully", async () => { + const startsAt = new Date("01/01/2023") + const endsAt = new Date("01/01/2023") + + await createCampaigns(MikroOrmWrapper.forkManager()) + + const createdPromotion = await service.create({ code: "PROMOTION_TEST", type: PromotionType.STANDARD, - application_method: { - type: "fixed", - target_type: "items", - value: "100", + campaign: { + name: "test", + campaign_identifier: "test-promotion-test", + starts_at: startsAt, + ends_at: endsAt, + budget: { + type: CampaignBudgetType.SPEND, + used: 100, + limit: 100, + }, }, - }, - ]) - .catch((e) => e) + }) - expect(error.message).toContain( - "application_method.allocation should be either 'across OR each' when application_method.target_type is either 'shipping_methods OR items'" - ) - }) + const [promotion] = await service.list( + { id: [createdPromotion.id] }, + { relations: ["campaign.budget"] } + ) - it("should throw error when creating an item application, each allocation, without max quanity", async () => { - const error = await service - .create([ - { + expect(promotion).toEqual( + expect.objectContaining({ + code: "PROMOTION_TEST", + is_automatic: false, + type: "standard", + campaign: expect.objectContaining({ + name: "test", + campaign_identifier: "test-promotion-test", + starts_at: startsAt, + ends_at: endsAt, + budget: expect.objectContaining({ + type: CampaignBudgetType.SPEND, + used: 100, + limit: 100, + }), + }), + }) + ) + }) + + it("should create a basic promotion with an existing campaign successfully", async () => { + await createCampaigns(MikroOrmWrapper.forkManager()) + + const createdPromotion = await service.create({ code: "PROMOTION_TEST", type: PromotionType.STANDARD, - application_method: { - type: "fixed", - allocation: "each", - target_type: "shipping_methods", - value: "100", - }, - }, - ]) - .catch((e) => e) + campaign_id: "campaign-id-1", + }) - expect(error.message).toContain( - "application_method.max_quantity is required when application_method.allocation is 'each'" - ) - }) + const [promotion] = await service.list( + { id: [createdPromotion.id] }, + { relations: ["campaign.budget"] } + ) - it("should throw error when creating an order application method with rules", async () => { - const error = await service - .create([ - { - code: "PROMOTION_TEST", - type: PromotionType.STANDARD, - application_method: { - type: "fixed", - target_type: "order", - value: "100", - target_rules: [ + expect(promotion).toEqual( + expect.objectContaining({ + code: "PROMOTION_TEST", + is_automatic: false, + type: "standard", + campaign: expect.objectContaining({ + id: "campaign-id-1", + budget: expect.objectContaining({ + type: CampaignBudgetType.SPEND, + limit: 1000, + used: 0, + }), + }), + }) + ) + }) + + it("should throw error when creating an item application method without allocation", async () => { + const error = await service + .create([ + { + code: "PROMOTION_TEST", + type: PromotionType.STANDARD, + application_method: { + type: "fixed", + target_type: "items", + value: "100", + }, + }, + ]) + .catch((e) => e) + + expect(error.message).toContain( + "application_method.allocation should be either 'across OR each' when application_method.target_type is either 'shipping_methods OR items'" + ) + }) + + it("should throw error when creating an item application, each allocation, without max quanity", async () => { + const error = await service + .create([ + { + code: "PROMOTION_TEST", + type: PromotionType.STANDARD, + application_method: { + type: "fixed", + allocation: "each", + target_type: "shipping_methods", + value: "100", + }, + }, + ]) + .catch((e) => e) + + expect(error.message).toContain( + "application_method.max_quantity is required when application_method.allocation is 'each'" + ) + }) + + it("should throw error when creating an order application method with rules", async () => { + const error = await service + .create([ + { + code: "PROMOTION_TEST", + type: PromotionType.STANDARD, + application_method: { + type: "fixed", + target_type: "order", + value: "100", + target_rules: [ + { + attribute: "product_id", + operator: "eq", + values: ["prod_tshirt"], + }, + ], + }, + }, + ]) + .catch((e) => e) + + expect(error.message).toContain( + "Target rules for application method with target type (order) is not allowed" + ) + }) + + it("should create a promotion with rules successfully", async () => { + const [createdPromotion] = await service.create([ + { + code: "PROMOTION_TEST", + type: PromotionType.STANDARD, + rules: [ { - attribute: "product_id", - operator: "eq", - values: ["prod_tshirt"], + attribute: "customer_group_id", + operator: "in", + values: ["VIP", "top100"], }, ], }, - }, - ]) - .catch((e) => e) + ]) - expect(error.message).toContain( - "Target rules for application method with target type (order) is not allowed" - ) - }) + const [promotion] = await service.list( + { + id: [createdPromotion.id], + }, + { + relations: ["rules", "rules.values"], + } + ) - it("should create a promotion with rules successfully", async () => { - const [createdPromotion] = await service.create([ - { - code: "PROMOTION_TEST", - type: PromotionType.STANDARD, - rules: [ + expect(promotion).toEqual( + expect.objectContaining({ + code: "PROMOTION_TEST", + is_automatic: false, + type: "standard", + rules: [ + expect.objectContaining({ + attribute: "customer_group_id", + operator: "in", + values: expect.arrayContaining([ + expect.objectContaining({ + value: "VIP", + }), + expect.objectContaining({ + value: "top100", + }), + ]), + }), + ], + }) + ) + }) + + it("should create a promotion with rules with single value successfully", async () => { + const [createdPromotion] = await service.create([ + { + code: "PROMOTION_TEST", + type: PromotionType.STANDARD, + rules: [ + { + attribute: "customer_group_id", + operator: "eq", + values: "VIP", + }, + ], + }, + ]) + + const [promotion] = await service.list( + { + id: [createdPromotion.id], + }, + { + relations: ["rules", "rules.values"], + } + ) + + expect(promotion).toEqual( + expect.objectContaining({ + code: "PROMOTION_TEST", + is_automatic: false, + type: "standard", + rules: [ + expect.objectContaining({ + attribute: "customer_group_id", + operator: "eq", + values: expect.arrayContaining([ + expect.objectContaining({ + value: "VIP", + }), + ]), + }), + ], + }) + ) + }) + + it("should throw an error when rule attribute is invalid", async () => { + const error = await service + .create([ + { + code: "PROMOTION_TEST", + type: PromotionType.STANDARD, + rules: [ + { + attribute: "", + operator: "eq", + values: "VIP", + } as any, + ], + }, + ]) + .catch((e) => e) + + expect(error.message).toContain( + "rules[].attribute is a required field" + ) + }) + + it("should throw an error when rule operator is invalid", async () => { + let error = await service + .create([ + { + code: "PROMOTION_TEST", + type: PromotionType.STANDARD, + rules: [ + { + attribute: "customer_group", + operator: "", + values: "VIP", + } as any, + ], + }, + ]) + .catch((e) => e) + + expect(error.message).toContain( + "rules[].operator is a required field" + ) + + error = await service + .create([ + { + code: "PROMOTION_TEST", + type: PromotionType.STANDARD, + rules: [ + { + attribute: "customer_group", + operator: "doesnotexist", + values: "VIP", + } as any, + ], + }, + ]) + .catch((e) => e) + + expect(error.message).toContain( + "rules[].operator (doesnotexist) is invalid. It should be one of gte, lte, gt, lt, eq, ne, in" + ) + }) + + it("should create a basic buyget promotion successfully", async () => { + const createdPromotion = await service + .create({ + code: "PROMOTION_TEST", + type: PromotionType.BUYGET, + }) + .catch((e) => e) + + const [promotion] = await service.list({ + id: [createdPromotion.id], + }) + + expect(promotion).toEqual( + expect.objectContaining({ + code: "PROMOTION_TEST", + is_automatic: false, + type: PromotionType.BUYGET, + }) + ) + }) + + it("should throw an error when target_rules are not present for buyget promotion", async () => { + const error = await service + .create({ + code: "PROMOTION_TEST", + type: PromotionType.BUYGET, + application_method: { + type: "fixed", + target_type: "items", + allocation: "across", + value: "100", + buy_rules: [ + { + attribute: "product_collection", + operator: "eq", + values: ["pcol_towel"], + }, + ], + }, + }) + .catch((e) => e) + + expect(error.message).toContain( + "Target rules are required for buyget promotion type" + ) + }) + + it("should throw an error when buy_rules are not present for buyget promotion", async () => { + const error = await service + .create({ + code: "PROMOTION_TEST", + type: PromotionType.BUYGET, + application_method: { + type: "fixed", + target_type: "items", + allocation: "across", + value: "100", + }, + }) + .catch((e) => e) + + expect(error.message).toContain( + "Buy rules are required for buyget promotion type" + ) + }) + + it("should throw an error when apply_to_quantity is not present for buyget promotion", async () => { + const error = await service + .create({ + code: "PROMOTION_TEST", + type: PromotionType.BUYGET, + application_method: { + type: "fixed", + target_type: "items", + allocation: "across", + value: "100", + buy_rules_min_quantity: 1, + buy_rules: [ + { + attribute: "product_collection.id", + operator: "eq", + values: ["pcol_towel"], + }, + ], + target_rules: [ + { + attribute: "product.id", + operator: "eq", + values: ["prod_mat"], + }, + ], + }, + }) + .catch((e) => e) + + expect(error.message).toContain( + "apply_to_quantity is a required field for Promotion type of buyget" + ) + }) + + it("should throw an error when buy_rules_min_quantity is not present for buyget promotion", async () => { + const error = await service + .create({ + code: "PROMOTION_TEST", + type: PromotionType.BUYGET, + application_method: { + type: "fixed", + target_type: "items", + allocation: "across", + value: "100", + apply_to_quantity: 1, + buy_rules: [ + { + attribute: "product_collection.id", + operator: "eq", + values: ["pcol_towel"], + }, + ], + target_rules: [ + { + attribute: "product.id", + operator: "eq", + values: ["prod_mat"], + }, + ], + }, + }) + .catch((e) => e) + + expect(error.message).toContain( + "buy_rules_min_quantity is a required field for Promotion type of buyget" + ) + }) + + it("should create a buyget promotion with rules successfully", async () => { + const createdPromotion = await service.create({ + code: "PROMOTION_TEST", + type: PromotionType.BUYGET, + application_method: { + type: "fixed", + target_type: "items", + allocation: "across", + value: "100", + apply_to_quantity: 1, + buy_rules_min_quantity: 1, + buy_rules: [ + { + attribute: "product_collection.id", + operator: "eq", + values: ["pcol_towel"], + }, + ], + target_rules: [ + { + attribute: "product.id", + operator: "eq", + values: "prod_mat", + }, + ], + }, + }) + + expect(createdPromotion).toEqual( + expect.objectContaining({ + code: "PROMOTION_TEST", + is_automatic: false, + type: PromotionType.BUYGET, + application_method: expect.objectContaining({ + type: "fixed", + target_type: "items", + allocation: "across", + value: 100, + apply_to_quantity: 1, + buy_rules_min_quantity: 1, + target_rules: [ + expect.objectContaining({ + attribute: "product.id", + operator: "eq", + values: [expect.objectContaining({ value: "prod_mat" })], + }), + ], + buy_rules: [ + expect.objectContaining({ + attribute: "product_collection.id", + operator: "eq", + values: [expect.objectContaining({ value: "pcol_towel" })], + }), + ], + }), + }) + ) + }) + }) + + describe("update", () => { + it("should throw an error when required params are not passed", async () => { + const error = await service + .update([ + { + type: PromotionType.STANDARD, + } as any, + ]) + .catch((e) => e) + + expect(error.message).toContain('Promotion with id "" not found') + }) + + it("should update the attributes of a promotion successfully", async () => { + await createPromotions(MikroOrmWrapper.forkManager()) + + const [updatedPromotion] = await service.update([ + { + id: "promotion-id-1", + is_automatic: true, + code: "TEST", + type: PromotionType.BUYGET, + } as any, + ]) + + expect(updatedPromotion).toEqual( + expect.objectContaining({ + is_automatic: true, + code: "TEST", + type: PromotionType.BUYGET, + }) + ) + }) + + it("should update the attributes of a application method successfully", async () => { + const [createdPromotion] = await service.create([ + { + code: "TEST", + type: PromotionType.STANDARD, + application_method: { + type: "fixed", + target_type: "items", + allocation: "across", + value: "100", + }, + }, + ]) + const applicationMethod = createdPromotion.application_method + + const [updatedPromotion] = await service.update([ + { + id: createdPromotion.id, + application_method: { + id: applicationMethod?.id as string, + value: "200", + }, + }, + ]) + + expect(updatedPromotion).toEqual( + expect.objectContaining({ + application_method: expect.objectContaining({ + value: 200, + }), + }) + ) + }) + + it("should change max_quantity to 0 when target_type is changed to order", async () => { + const [createdPromotion] = await service.create([ + { + code: "TEST", + type: PromotionType.STANDARD, + application_method: { + type: "fixed", + target_type: "items", + allocation: "each", + value: "100", + max_quantity: 500, + }, + }, + ]) + const applicationMethod = createdPromotion.application_method + + const [updatedPromotion] = await service.update([ + { + id: createdPromotion.id, + application_method: { + id: applicationMethod?.id as string, + target_type: "order", + allocation: "across", + }, + }, + ]) + + expect(updatedPromotion).toEqual( + expect.objectContaining({ + application_method: expect.objectContaining({ + target_type: "order", + allocation: "across", + max_quantity: 0, + }), + }) + ) + }) + + it("should validate the attributes of a application method successfully", async () => { + const [createdPromotion] = await service.create([ + { + code: "TEST", + type: PromotionType.STANDARD, + application_method: { + type: "fixed", + target_type: "order", + allocation: "across", + value: "100", + }, + }, + ]) + const applicationMethod = createdPromotion.application_method + + let error = await service + .update([ + { + id: createdPromotion.id, + application_method: { + id: applicationMethod?.id as string, + target_type: "should-error", + } as any, + }, + ]) + .catch((e) => e) + + expect(error.message).toContain( + `application_method.target_type should be one of order, shipping_methods, items` + ) + + error = await service + .update([ + { + id: createdPromotion.id, + application_method: { + id: applicationMethod?.id as string, + allocation: "should-error", + } as any, + }, + ]) + .catch((e) => e) + + expect(error.message).toContain( + `application_method.allocation should be one of each, across` + ) + + error = await service + .update([ + { + id: createdPromotion.id, + application_method: { + id: applicationMethod?.id as string, + type: "should-error", + } as any, + }, + ]) + .catch((e) => e) + + expect(error.message).toContain( + `application_method.type should be one of fixed, percentage` + ) + }) + + it("should update campaign of the promotion", async () => { + await createCampaigns(MikroOrmWrapper.forkManager()) + const [createdPromotion] = await createPromotions( + MikroOrmWrapper.forkManager(), + [ + { + is_automatic: true, + code: "TEST", + type: PromotionType.BUYGET, + campaign_id: "campaign-id-1", + }, + ] + ) + + const [updatedPromotion] = await service.update([ + { + id: createdPromotion.id, + campaign_id: "campaign-id-2", + }, + ]) + + expect(updatedPromotion).toEqual( + expect.objectContaining({ + campaign: expect.objectContaining({ + id: "campaign-id-2", + }), + }) + ) + }) + }) + + describe("retrieve", () => { + beforeEach(async () => { + await createPromotions(MikroOrmWrapper.forkManager()) + }) + + const id = "promotion-id-1" + + it("should return promotion for the given id", async () => { + const promotion = await service.retrieve(id) + + expect(promotion).toEqual( + expect.objectContaining({ + id, + }) + ) + }) + + it("should throw an error when promotion with id does not exist", async () => { + let error + + try { + await service.retrieve("does-not-exist") + } catch (e) { + error = e + } + + expect(error.message).toEqual( + "Promotion with id: does-not-exist was not found" + ) + }) + + it("should throw an error when a id is not provided", async () => { + let error + + try { + await service.retrieve(undefined as unknown as string) + } catch (e) { + error = e + } + + expect(error.message).toEqual("promotion - id must be defined") + }) + + it("should return promotion based on config select param", async () => { + const promotion = await service.retrieve(id, { + select: ["id"], + }) + + const serialized = JSON.parse(JSON.stringify(promotion)) + + expect(serialized).toEqual({ + id, + }) + }) + }) + + describe("listAndCount", () => { + beforeEach(async () => { + await createPromotions(MikroOrmWrapper.forkManager(), [ + { + id: "promotion-id-1", + code: "PROMOTION_1", + type: PromotionType.STANDARD, + application_method: { + type: ApplicationMethodType.FIXED, + value: "200", + target_type: "items", + }, + } as any, + { + id: "promotion-id-2", + code: "PROMOTION_2", + type: PromotionType.STANDARD, + } as any, + ]) + }) + + it("should return all promotions and count", async () => { + const [promotions, count] = await service.listAndCount() + + expect(count).toEqual(2) + expect(promotions).toEqual([ + { + id: "promotion-id-1", + code: "PROMOTION_1", + campaign: null, + is_automatic: false, + type: "standard", + application_method: expect.any(Object), + created_at: expect.any(Date), + updated_at: expect.any(Date), + deleted_at: null, + }, + { + id: "promotion-id-2", + code: "PROMOTION_2", + campaign: null, + is_automatic: false, + type: "standard", + application_method: null, + created_at: expect.any(Date), + updated_at: expect.any(Date), + deleted_at: null, + }, + ]) + }) + + it("should return all promotions based on config select and relations param", async () => { + const [promotions, count] = await service.listAndCount( + { + id: ["promotion-id-1"], + }, + { + relations: ["application_method"], + select: ["code", "application_method.type"], + } + ) + + expect(count).toEqual(1) + expect(promotions).toEqual([ + { + id: "promotion-id-1", + code: "PROMOTION_1", + application_method: { + id: expect.any(String), + promotion: expect.any(Object), + type: "fixed", + }, + }, + ]) + }) + }) + + describe("delete", () => { + it("should soft delete the promotions given an id successfully", async () => { + const createdPromotion = await service.create({ + code: "TEST", + type: "standard", + }) + + await service.delete([createdPromotion.id]) + + const promotions = await service.list( + { + id: [createdPromotion.id], + }, + { withDeleted: true } + ) + + expect(promotions).toHaveLength(0) + }) + }) + + describe("softDelete", () => { + it("should soft delete the promotions given an id successfully", async () => { + const createdPromotion = await service.create({ + code: "TEST", + type: "standard", + }) + + await service.softDelete([createdPromotion.id]) + + const promotions = await service.list({ + id: [createdPromotion.id], + }) + + expect(promotions).toHaveLength(0) + }) + }) + + describe("restore", () => { + it("should restore the promotions given an id successfully", async () => { + const createdPromotion = await service.create({ + code: "TEST", + type: "standard", + }) + + await service.softDelete([createdPromotion.id]) + + let promotions = await service.list({ id: [createdPromotion.id] }) + + expect(promotions).toHaveLength(0) + await service.restore([createdPromotion.id]) + + promotions = await service.list({ id: [createdPromotion.id] }) + expect(promotions).toHaveLength(1) + }) + }) + + describe("addPromotionRules", () => { + let promotion + + beforeEach(async () => { + ;[promotion] = await service.create([ + { + code: "TEST", + type: PromotionType.STANDARD, + application_method: { + type: "fixed", + target_type: "items", + allocation: "each", + value: "100", + max_quantity: 500, + }, + }, + ]) + }) + + it("should throw an error when promotion with id does not exist", async () => { + let error + + try { + await service.addPromotionRules("does-not-exist", []) + } catch (e) { + error = e + } + + expect(error.message).toEqual( + "Promotion with id: does-not-exist was not found" + ) + }) + + it("should throw an error when a id is not provided", async () => { + let error + + try { + await service.addPromotionRules(undefined as unknown as string, []) + } catch (e) { + error = e + } + + expect(error.message).toEqual("promotion - id must be defined") + }) + + it("should successfully add rules to a promotion", async () => { + const promotionRules = await service.addPromotionRules(promotion.id, [ { attribute: "customer_group_id", operator: "in", values: ["VIP", "top100"], }, - ], - }, - ]) + ]) - const [promotion] = await service.list( - { - id: [createdPromotion.id], - }, - { - relations: ["rules", "rules.values"], - } - ) - - expect(promotion).toEqual( - expect.objectContaining({ - code: "PROMOTION_TEST", - is_automatic: false, - type: "standard", - rules: [ + expect(promotionRules).toEqual([ expect.objectContaining({ + id: promotionRules[0].id, attribute: "customer_group_id", operator: "in", values: expect.arrayContaining([ - expect.objectContaining({ - value: "VIP", - }), - expect.objectContaining({ - value: "top100", - }), + expect.objectContaining({ value: "VIP" }), + expect.objectContaining({ value: "top100" }), ]), }), - ], + ]) }) - ) - }) + }) - it("should create a promotion with rules with single value successfully", async () => { - const [createdPromotion] = await service.create([ - { - code: "PROMOTION_TEST", - type: PromotionType.STANDARD, - rules: [ + describe("addPromotionTargetRules", () => { + let promotion + + beforeEach(async () => { + ;[promotion] = await service.create([ { - attribute: "customer_group_id", - operator: "eq", - values: "VIP", + code: "TEST", + type: PromotionType.STANDARD, + application_method: { + type: "fixed", + target_type: "items", + allocation: "each", + value: "100", + max_quantity: 500, + }, }, - ], - }, - ]) - - const [promotion] = await service.list( - { - id: [createdPromotion.id], - }, - { - relations: ["rules", "rules.values"], - } - ) - - expect(promotion).toEqual( - expect.objectContaining({ - code: "PROMOTION_TEST", - is_automatic: false, - type: "standard", - rules: [ - expect.objectContaining({ - attribute: "customer_group_id", - operator: "eq", - values: expect.arrayContaining([ - expect.objectContaining({ - value: "VIP", - }), - ]), - }), - ], + ]) }) - ) - }) - it("should throw an error when rule attribute is invalid", async () => { - const error = await service - .create([ - { - code: "PROMOTION_TEST", - type: PromotionType.STANDARD, - rules: [ - { - attribute: "", - operator: "eq", - values: "VIP", - } as any, - ], - }, - ]) - .catch((e) => e) + it("should throw an error when promotion with id does not exist", async () => { + let error - expect(error.message).toContain("rules[].attribute is a required field") - }) + try { + await service.addPromotionTargetRules("does-not-exist", []) + } catch (e) { + error = e + } - it("should throw an error when rule operator is invalid", async () => { - let error = await service - .create([ - { - code: "PROMOTION_TEST", - type: PromotionType.STANDARD, - rules: [ - { - attribute: "customer_group", - operator: "", - values: "VIP", - } as any, - ], - }, - ]) - .catch((e) => e) - - expect(error.message).toContain("rules[].operator is a required field") - - error = await service - .create([ - { - code: "PROMOTION_TEST", - type: PromotionType.STANDARD, - rules: [ - { - attribute: "customer_group", - operator: "doesnotexist", - values: "VIP", - } as any, - ], - }, - ]) - .catch((e) => e) - - expect(error.message).toContain( - "rules[].operator (doesnotexist) is invalid. It should be one of gte, lte, gt, lt, eq, ne, in" - ) - }) - - it("should create a basic buyget promotion successfully", async () => { - const createdPromotion = await service - .create({ - code: "PROMOTION_TEST", - type: PromotionType.BUYGET, + expect(error.message).toEqual( + "Promotion with id: does-not-exist was not found" + ) }) - .catch((e) => e) - const [promotion] = await service.list({ - id: [createdPromotion.id], - }) + it("should throw an error when a id is not provided", async () => { + let error - expect(promotion).toEqual( - expect.objectContaining({ - code: "PROMOTION_TEST", - is_automatic: false, - type: PromotionType.BUYGET, + try { + await service.addPromotionTargetRules( + undefined as unknown as string, + [] + ) + } catch (e) { + error = e + } + + expect(error.message).toEqual("promotion - id must be defined") }) - ) - }) - it("should throw an error when target_rules are not present for buyget promotion", async () => { - const error = await service - .create({ - code: "PROMOTION_TEST", - type: PromotionType.BUYGET, - application_method: { - type: "fixed", - target_type: "items", - allocation: "across", - value: "100", - buy_rules: [ - { - attribute: "product_collection", - operator: "eq", - values: ["pcol_towel"], - }, - ], - }, - }) - .catch((e) => e) - - expect(error.message).toContain( - "Target rules are required for buyget promotion type" - ) - }) - - it("should throw an error when buy_rules are not present for buyget promotion", async () => { - const error = await service - .create({ - code: "PROMOTION_TEST", - type: PromotionType.BUYGET, - application_method: { - type: "fixed", - target_type: "items", - allocation: "across", - value: "100", - }, - }) - .catch((e) => e) - - expect(error.message).toContain( - "Buy rules are required for buyget promotion type" - ) - }) - - it("should throw an error when apply_to_quantity is not present for buyget promotion", async () => { - const error = await service - .create({ - code: "PROMOTION_TEST", - type: PromotionType.BUYGET, - application_method: { - type: "fixed", - target_type: "items", - allocation: "across", - value: "100", - buy_rules_min_quantity: 1, - buy_rules: [ - { - attribute: "product_collection.id", - operator: "eq", - values: ["pcol_towel"], - }, - ], - target_rules: [ - { - attribute: "product.id", - operator: "eq", - values: ["prod_mat"], - }, - ], - }, - }) - .catch((e) => e) - - expect(error.message).toContain( - "apply_to_quantity is a required field for Promotion type of buyget" - ) - }) - - it("should throw an error when buy_rules_min_quantity is not present for buyget promotion", async () => { - const error = await service - .create({ - code: "PROMOTION_TEST", - type: PromotionType.BUYGET, - application_method: { - type: "fixed", - target_type: "items", - allocation: "across", - value: "100", - apply_to_quantity: 1, - buy_rules: [ - { - attribute: "product_collection.id", - operator: "eq", - values: ["pcol_towel"], - }, - ], - target_rules: [ - { - attribute: "product.id", - operator: "eq", - values: ["prod_mat"], - }, - ], - }, - }) - .catch((e) => e) - - expect(error.message).toContain( - "buy_rules_min_quantity is a required field for Promotion type of buyget" - ) - }) - - it("should create a buyget promotion with rules successfully", async () => { - const createdPromotion = await service.create({ - code: "PROMOTION_TEST", - type: PromotionType.BUYGET, - application_method: { - type: "fixed", - target_type: "items", - allocation: "across", - value: "100", - apply_to_quantity: 1, - buy_rules_min_quantity: 1, - buy_rules: [ - { - attribute: "product_collection.id", - operator: "eq", - values: ["pcol_towel"], - }, - ], - target_rules: [ - { - attribute: "product.id", - operator: "eq", - values: "prod_mat", - }, - ], - }, - }) - - expect(createdPromotion).toEqual( - expect.objectContaining({ - code: "PROMOTION_TEST", - is_automatic: false, - type: PromotionType.BUYGET, - application_method: expect.objectContaining({ - type: "fixed", - target_type: "items", - allocation: "across", - value: 100, - apply_to_quantity: 1, - buy_rules_min_quantity: 1, - target_rules: [ - expect.objectContaining({ - attribute: "product.id", - operator: "eq", - values: [expect.objectContaining({ value: "prod_mat" })], - }), - ], - buy_rules: [ - expect.objectContaining({ - attribute: "product_collection.id", - operator: "eq", - values: [expect.objectContaining({ value: "pcol_towel" })], - }), - ], - }), - }) - ) - }) - }) - - describe("update", () => { - it("should throw an error when required params are not passed", async () => { - const error = await service - .update([ - { - type: PromotionType.STANDARD, - } as any, - ]) - .catch((e) => e) - - expect(error.message).toContain('Promotion with id "" not found') - }) - - it("should update the attributes of a promotion successfully", async () => { - await createPromotions(repositoryManager) - - const [updatedPromotion] = await service.update([ - { - id: "promotion-id-1", - is_automatic: true, - code: "TEST", - type: PromotionType.BUYGET, - } as any, - ]) - - expect(updatedPromotion).toEqual( - expect.objectContaining({ - is_automatic: true, - code: "TEST", - type: PromotionType.BUYGET, - }) - ) - }) - - it("should update the attributes of a application method successfully", async () => { - const [createdPromotion] = await service.create([ - { - code: "TEST", - type: PromotionType.STANDARD, - application_method: { - type: "fixed", - target_type: "items", - allocation: "across", - value: "100", - }, - }, - ]) - const applicationMethod = createdPromotion.application_method - - const [updatedPromotion] = await service.update([ - { - id: createdPromotion.id, - application_method: { - id: applicationMethod?.id as string, - value: "200", - }, - }, - ]) - - expect(updatedPromotion).toEqual( - expect.objectContaining({ - application_method: expect.objectContaining({ - value: 200, - }), - }) - ) - }) - - it("should change max_quantity to 0 when target_type is changed to order", async () => { - const [createdPromotion] = await service.create([ - { - code: "TEST", - type: PromotionType.STANDARD, - application_method: { - type: "fixed", - target_type: "items", - allocation: "each", - value: "100", - max_quantity: 500, - }, - }, - ]) - const applicationMethod = createdPromotion.application_method - - const [updatedPromotion] = await service.update([ - { - id: createdPromotion.id, - application_method: { - id: applicationMethod?.id as string, - target_type: "order", - allocation: "across", - }, - }, - ]) - - expect(updatedPromotion).toEqual( - expect.objectContaining({ - application_method: expect.objectContaining({ - target_type: "order", - allocation: "across", - max_quantity: 0, - }), - }) - ) - }) - - it("should validate the attributes of a application method successfully", async () => { - const [createdPromotion] = await service.create([ - { - code: "TEST", - type: PromotionType.STANDARD, - application_method: { - type: "fixed", - target_type: "order", - allocation: "across", - value: "100", - }, - }, - ]) - const applicationMethod = createdPromotion.application_method - - let error = await service - .update([ - { - id: createdPromotion.id, - application_method: { - id: applicationMethod?.id as string, - target_type: "should-error", - } as any, - }, - ]) - .catch((e) => e) - - expect(error.message).toContain( - `application_method.target_type should be one of order, shipping_methods, items` - ) - - error = await service - .update([ - { - id: createdPromotion.id, - application_method: { - id: applicationMethod?.id as string, - allocation: "should-error", - } as any, - }, - ]) - .catch((e) => e) - - expect(error.message).toContain( - `application_method.allocation should be one of each, across` - ) - - error = await service - .update([ - { - id: createdPromotion.id, - application_method: { - id: applicationMethod?.id as string, - type: "should-error", - } as any, - }, - ]) - .catch((e) => e) - - expect(error.message).toContain( - `application_method.type should be one of fixed, percentage` - ) - }) - - it("should update campaign of the promotion", async () => { - await createCampaigns(repositoryManager) - const [createdPromotion] = await createPromotions(repositoryManager, [ - { - is_automatic: true, - code: "TEST", - type: PromotionType.BUYGET, - campaign_id: "campaign-id-1", - }, - ]) - - const [updatedPromotion] = await service.update([ - { - id: createdPromotion.id, - campaign_id: "campaign-id-2", - }, - ]) - - expect(updatedPromotion).toEqual( - expect.objectContaining({ - campaign: expect.objectContaining({ - id: "campaign-id-2", - }), - }) - ) - }) - }) - - describe("retrieve", () => { - beforeEach(async () => { - await createPromotions(repositoryManager) - }) - - const id = "promotion-id-1" - - it("should return promotion for the given id", async () => { - const promotion = await service.retrieve(id) - - expect(promotion).toEqual( - expect.objectContaining({ - id, - }) - ) - }) - - it("should throw an error when promotion with id does not exist", async () => { - let error - - try { - await service.retrieve("does-not-exist") - } catch (e) { - error = e - } - - expect(error.message).toEqual( - "Promotion with id: does-not-exist was not found" - ) - }) - - it("should throw an error when a id is not provided", async () => { - let error - - try { - await service.retrieve(undefined as unknown as string) - } catch (e) { - error = e - } - - expect(error.message).toEqual("promotion - id must be defined") - }) - - it("should return promotion based on config select param", async () => { - const promotion = await service.retrieve(id, { - select: ["id"], - }) - - const serialized = JSON.parse(JSON.stringify(promotion)) - - expect(serialized).toEqual({ - id, - }) - }) - }) - - describe("listAndCount", () => { - beforeEach(async () => { - await createPromotions(repositoryManager, [ - { - id: "promotion-id-1", - code: "PROMOTION_1", - type: PromotionType.STANDARD, - application_method: { - type: ApplicationMethodType.FIXED, - value: "200", - target_type: "items", - }, - } as any, - { - id: "promotion-id-2", - code: "PROMOTION_2", - type: PromotionType.STANDARD, - } as any, - ]) - }) - - it("should return all promotions and count", async () => { - const [promotions, count] = await service.listAndCount() - - expect(count).toEqual(2) - expect(promotions).toEqual([ - { - id: "promotion-id-1", - code: "PROMOTION_1", - campaign: null, - is_automatic: false, - type: "standard", - application_method: expect.any(Object), - created_at: expect.any(Date), - updated_at: expect.any(Date), - deleted_at: null, - }, - { - id: "promotion-id-2", - code: "PROMOTION_2", - campaign: null, - is_automatic: false, - type: "standard", - application_method: null, - created_at: expect.any(Date), - updated_at: expect.any(Date), - deleted_at: null, - }, - ]) - }) - - it("should return all promotions based on config select and relations param", async () => { - const [promotions, count] = await service.listAndCount( - { - id: ["promotion-id-1"], - }, - { - relations: ["application_method"], - select: ["code", "application_method.type"], - } - ) - - expect(count).toEqual(1) - expect(promotions).toEqual([ - { - id: "promotion-id-1", - code: "PROMOTION_1", - application_method: { - id: expect.any(String), - promotion: expect.any(Object), - type: "fixed", - }, - }, - ]) - }) - }) - - describe("delete", () => { - it("should soft delete the promotions given an id successfully", async () => { - const createdPromotion = await service.create({ - code: "TEST", - type: "standard", - }) - - await service.delete([createdPromotion.id]) - - const promotions = await service.list( - { - id: [createdPromotion.id], - }, - { withDeleted: true } - ) - - expect(promotions).toHaveLength(0) - }) - }) - - describe("softDelete", () => { - it("should soft delete the promotions given an id successfully", async () => { - const createdPromotion = await service.create({ - code: "TEST", - type: "standard", - }) - - await service.softDelete([createdPromotion.id]) - - const promotions = await service.list({ - id: [createdPromotion.id], - }) - - expect(promotions).toHaveLength(0) - }) - }) - - describe("restore", () => { - it("should restore the promotions given an id successfully", async () => { - const createdPromotion = await service.create({ - code: "TEST", - type: "standard", - }) - - await service.softDelete([createdPromotion.id]) - - let promotions = await service.list({ id: [createdPromotion.id] }) - - expect(promotions).toHaveLength(0) - await service.restore([createdPromotion.id]) - - promotions = await service.list({ id: [createdPromotion.id] }) - expect(promotions).toHaveLength(1) - }) - }) - - describe("addPromotionRules", () => { - let promotion - - beforeEach(async () => { - ;[promotion] = await service.create([ - { - code: "TEST", - type: PromotionType.STANDARD, - application_method: { - type: "fixed", - target_type: "items", - allocation: "each", - value: "100", - max_quantity: 500, - }, - }, - ]) - }) - - it("should throw an error when promotion with id does not exist", async () => { - let error - - try { - await service.addPromotionRules("does-not-exist", []) - } catch (e) { - error = e - } - - expect(error.message).toEqual( - "Promotion with id: does-not-exist was not found" - ) - }) - - it("should throw an error when a id is not provided", async () => { - let error - - try { - await service.addPromotionRules(undefined as unknown as string, []) - } catch (e) { - error = e - } - - expect(error.message).toEqual("promotion - id must be defined") - }) - - it("should successfully add rules to a promotion", async () => { - const promotionRules = await service.addPromotionRules(promotion.id, [ - { - attribute: "customer_group_id", - operator: "in", - values: ["VIP", "top100"], - }, - ]) - - expect(promotionRules).toEqual([ - expect.objectContaining({ - id: promotionRules[0].id, - attribute: "customer_group_id", - operator: "in", - values: expect.arrayContaining([ - expect.objectContaining({ value: "VIP" }), - expect.objectContaining({ value: "top100" }), - ]), - }), - ]) - }) - }) - - describe("addPromotionTargetRules", () => { - let promotion - - beforeEach(async () => { - ;[promotion] = await service.create([ - { - code: "TEST", - type: PromotionType.STANDARD, - application_method: { - type: "fixed", - target_type: "items", - allocation: "each", - value: "100", - max_quantity: 500, - }, - }, - ]) - }) - - it("should throw an error when promotion with id does not exist", async () => { - let error - - try { - await service.addPromotionTargetRules("does-not-exist", []) - } catch (e) { - error = e - } - - expect(error.message).toEqual( - "Promotion with id: does-not-exist was not found" - ) - }) - - it("should throw an error when a id is not provided", async () => { - let error - - try { - await service.addPromotionTargetRules( - undefined as unknown as string, - [] - ) - } catch (e) { - error = e - } - - expect(error.message).toEqual("promotion - id must be defined") - }) - - it("should successfully create target rules for a promotion", async () => { - const promotionRules = await service.addPromotionTargetRules( - promotion.id, - [ - { - attribute: "customer_group_id", - operator: "in", - values: ["VIP", "top100"], - }, - ] - ) - - expect(promotionRules).toEqual([ - expect.objectContaining({ - id: promotionRules[0].id, - attribute: "customer_group_id", - operator: "in", - values: expect.arrayContaining([ - expect.objectContaining({ value: "VIP" }), - expect.objectContaining({ value: "top100" }), - ]), - }), - ]) - }) - }) - - describe("addPromotionBuyRules", () => { - let promotion - - beforeEach(async () => { - ;[promotion] = await service.create([ - { - code: "TEST", - type: PromotionType.BUYGET, - application_method: { - type: "fixed", - target_type: "items", - allocation: "each", - value: "100", - max_quantity: 500, - apply_to_quantity: 1, - buy_rules_min_quantity: 1, - target_rules: [ - { - attribute: "product.id", - operator: "in", - values: ["prod_1", "prod_2"], - }, - ], - buy_rules: [ - { - attribute: "product_collection.id", - operator: "eq", - values: ["pcol_towel"], - }, - ], - }, - }, - ]) - }) - - it("should throw an error when promotion with id does not exist", async () => { - let error - - try { - await service.addPromotionBuyRules("does-not-exist", []) - } catch (e) { - error = e - } - - expect(error.message).toEqual( - "Promotion with id: does-not-exist was not found" - ) - }) - - it("should throw an error when a id is not provided", async () => { - let error - - try { - await service.addPromotionBuyRules(undefined as unknown as string, []) - } catch (e) { - error = e - } - - expect(error.message).toEqual("promotion - id must be defined") - }) - - it("should successfully create buy rules for a buyget promotion", async () => { - const promotionRules = await service.addPromotionBuyRules(promotion.id, [ - { - attribute: "product.id", - operator: "in", - values: ["prod_3", "prod_4"], - }, - ]) - - expect(promotionRules).toEqual([ - expect.objectContaining({ - id: promotionRules[0].id, - attribute: "product.id", - operator: "in", - values: expect.arrayContaining([ - expect.objectContaining({ value: "prod_3" }), - expect.objectContaining({ value: "prod_4" }), - ]), - }), - ]) - }) - }) - - describe("removePromotionRules", () => { - let promotion - - beforeEach(async () => { - ;[promotion] = await service.create([ - { - code: "TEST", - type: PromotionType.STANDARD, - rules: [ - { - attribute: "customer_group_id", - operator: "in", - values: ["VIP", "top100"], - }, - ], - application_method: { - type: "fixed", - target_type: "items", - allocation: "each", - value: "100", - max_quantity: 500, - }, - }, - ]) - }) - - it("should throw an error when promotion with id does not exist", async () => { - let error - - try { - await service.removePromotionRules("does-not-exist", []) - } catch (e) { - error = e - } - - expect(error.message).toEqual( - "Promotion with id: does-not-exist was not found" - ) - }) - - it("should throw an error when a id is not provided", async () => { - let error - - try { - await service.removePromotionRules(undefined as unknown as string, []) - } catch (e) { - error = e - } - - expect(error.message).toEqual("promotion - id must be defined") - }) - - it("should successfully remove rules for a promotion", async () => { - const ruleIds = promotion.rules.map((rule) => rule.id) - - await service.removePromotionRules(promotion.id, ruleIds) - - const updatedPromotion = await service.retrieve(promotion.id, { - relations: ["rules", "rules.values"], - }) - - expect(updatedPromotion).toEqual( - expect.objectContaining({ - id: promotion.id, - rules: [], - }) - ) - }) - }) - - describe("removePromotionTargetRules", () => { - let promotion - - beforeEach(async () => { - ;[promotion] = await service.create([ - { - code: "TEST", - type: PromotionType.STANDARD, - application_method: { - type: "fixed", - target_type: "items", - allocation: "each", - value: "100", - max_quantity: 500, - target_rules: [ + it("should successfully create target rules for a promotion", async () => { + const promotionRules = await service.addPromotionTargetRules( + promotion.id, + [ { attribute: "customer_group_id", operator: "in", values: ["VIP", "top100"], }, - ], - }, - }, - ]) - }) + ] + ) - it("should throw an error when promotion with id does not exist", async () => { - let error - - try { - await service.removePromotionTargetRules("does-not-exist", []) - } catch (e) { - error = e - } - - expect(error.message).toEqual( - "Promotion with id: does-not-exist was not found" - ) - }) - - it("should throw an error when a id is not provided", async () => { - let error - - try { - await service.removePromotionTargetRules( - undefined as unknown as string, - [] - ) - } catch (e) { - error = e - } - - expect(error.message).toEqual("promotion - id must be defined") - }) - - it("should successfully create rules for a promotion", async () => { - const ruleIds = promotion.application_method.target_rules.map( - (rule) => rule.id - ) - - await service.removePromotionTargetRules(promotion.id, ruleIds) - - const updatedPromotion = await service.retrieve(promotion.id, { - relations: ["application_method.target_rules.values"], + expect(promotionRules).toEqual([ + expect.objectContaining({ + id: promotionRules[0].id, + attribute: "customer_group_id", + operator: "in", + values: expect.arrayContaining([ + expect.objectContaining({ value: "VIP" }), + expect.objectContaining({ value: "top100" }), + ]), + }), + ]) + }) }) - expect(updatedPromotion).toEqual( - expect.objectContaining({ - id: promotion.id, - application_method: expect.objectContaining({ - target_rules: [], - }), + describe("addPromotionBuyRules", () => { + let promotion + + beforeEach(async () => { + ;[promotion] = await service.create([ + { + code: "TEST", + type: PromotionType.BUYGET, + application_method: { + type: "fixed", + target_type: "items", + allocation: "each", + value: "100", + max_quantity: 500, + apply_to_quantity: 1, + buy_rules_min_quantity: 1, + target_rules: [ + { + attribute: "product.id", + operator: "in", + values: ["prod_1", "prod_2"], + }, + ], + buy_rules: [ + { + attribute: "product_collection.id", + operator: "eq", + values: ["pcol_towel"], + }, + ], + }, + }, + ]) }) - ) - }) - }) - describe("removePromotionBuyRules", () => { - let promotion + it("should throw an error when promotion with id does not exist", async () => { + let error - beforeEach(async () => { - ;[promotion] = await service.create([ - { - code: "TEST", - type: PromotionType.BUYGET, - application_method: { - type: "fixed", - target_type: "items", - allocation: "each", - value: "100", - max_quantity: 500, - apply_to_quantity: 1, - buy_rules_min_quantity: 1, - target_rules: [ + try { + await service.addPromotionBuyRules("does-not-exist", []) + } catch (e) { + error = e + } + + expect(error.message).toEqual( + "Promotion with id: does-not-exist was not found" + ) + }) + + it("should throw an error when a id is not provided", async () => { + let error + + try { + await service.addPromotionBuyRules( + undefined as unknown as string, + [] + ) + } catch (e) { + error = e + } + + expect(error.message).toEqual("promotion - id must be defined") + }) + + it("should successfully create buy rules for a buyget promotion", async () => { + const promotionRules = await service.addPromotionBuyRules( + promotion.id, + [ { attribute: "product.id", operator: "in", - values: ["prod_1", "prod_2"], + values: ["prod_3", "prod_4"], }, - ], - buy_rules: [ - { - attribute: "product_collection", - operator: "eq", - values: ["pcol_towel"], - }, - ], - }, - }, - ]) - }) + ] + ) - it("should throw an error when promotion with id does not exist", async () => { - let error - - try { - await service.removePromotionBuyRules("does-not-exist", []) - } catch (e) { - error = e - } - - expect(error.message).toEqual( - "Promotion with id: does-not-exist was not found" - ) - }) - - it("should throw an error when a id is not provided", async () => { - let error - - try { - await service.removePromotionBuyRules( - undefined as unknown as string, - [] - ) - } catch (e) { - error = e - } - - expect(error.message).toEqual("promotion - id must be defined") - }) - - it("should successfully remove rules for a promotion", async () => { - const ruleIds = promotion.application_method.buy_rules.map( - (rule) => rule.id - ) - - await service.removePromotionBuyRules(promotion.id, ruleIds) - - const updatedPromotion = await service.retrieve(promotion.id, { - relations: ["application_method.buy_rules.values"], + expect(promotionRules).toEqual([ + expect.objectContaining({ + id: promotionRules[0].id, + attribute: "product.id", + operator: "in", + values: expect.arrayContaining([ + expect.objectContaining({ value: "prod_3" }), + expect.objectContaining({ value: "prod_4" }), + ]), + }), + ]) + }) }) - expect(updatedPromotion).toEqual( - expect.objectContaining({ - id: promotion.id, - application_method: expect.objectContaining({ - buy_rules: [], - }), + describe("removePromotionRules", () => { + let promotion + + beforeEach(async () => { + ;[promotion] = await service.create([ + { + code: "TEST", + type: PromotionType.STANDARD, + rules: [ + { + attribute: "customer_group_id", + operator: "in", + values: ["VIP", "top100"], + }, + ], + application_method: { + type: "fixed", + target_type: "items", + allocation: "each", + value: "100", + max_quantity: 500, + }, + }, + ]) }) - ) + + it("should throw an error when promotion with id does not exist", async () => { + let error + + try { + await service.removePromotionRules("does-not-exist", []) + } catch (e) { + error = e + } + + expect(error.message).toEqual( + "Promotion with id: does-not-exist was not found" + ) + }) + + it("should throw an error when a id is not provided", async () => { + let error + + try { + await service.removePromotionRules( + undefined as unknown as string, + [] + ) + } catch (e) { + error = e + } + + expect(error.message).toEqual("promotion - id must be defined") + }) + + it("should successfully remove rules for a promotion", async () => { + const ruleIds = promotion.rules.map((rule) => rule.id) + + await service.removePromotionRules(promotion.id, ruleIds) + + const updatedPromotion = await service.retrieve(promotion.id, { + relations: ["rules", "rules.values"], + }) + + expect(updatedPromotion).toEqual( + expect.objectContaining({ + id: promotion.id, + rules: [], + }) + ) + }) + }) + + describe("removePromotionTargetRules", () => { + let promotion + + beforeEach(async () => { + ;[promotion] = await service.create([ + { + code: "TEST", + type: PromotionType.STANDARD, + application_method: { + type: "fixed", + target_type: "items", + allocation: "each", + value: "100", + max_quantity: 500, + target_rules: [ + { + attribute: "customer_group_id", + operator: "in", + values: ["VIP", "top100"], + }, + ], + }, + }, + ]) + }) + + it("should throw an error when promotion with id does not exist", async () => { + let error + + try { + await service.removePromotionTargetRules("does-not-exist", []) + } catch (e) { + error = e + } + + expect(error.message).toEqual( + "Promotion with id: does-not-exist was not found" + ) + }) + + it("should throw an error when a id is not provided", async () => { + let error + + try { + await service.removePromotionTargetRules( + undefined as unknown as string, + [] + ) + } catch (e) { + error = e + } + + expect(error.message).toEqual("promotion - id must be defined") + }) + + it("should successfully create rules for a promotion", async () => { + const ruleIds = promotion.application_method.target_rules.map( + (rule) => rule.id + ) + + await service.removePromotionTargetRules(promotion.id, ruleIds) + + const updatedPromotion = await service.retrieve(promotion.id, { + relations: ["application_method.target_rules.values"], + }) + + expect(updatedPromotion).toEqual( + expect.objectContaining({ + id: promotion.id, + application_method: expect.objectContaining({ + target_rules: [], + }), + }) + ) + }) + }) + + describe("removePromotionBuyRules", () => { + let promotion + + beforeEach(async () => { + ;[promotion] = await service.create([ + { + code: "TEST", + type: PromotionType.BUYGET, + application_method: { + type: "fixed", + target_type: "items", + allocation: "each", + value: "100", + max_quantity: 500, + apply_to_quantity: 1, + buy_rules_min_quantity: 1, + target_rules: [ + { + attribute: "product.id", + operator: "in", + values: ["prod_1", "prod_2"], + }, + ], + buy_rules: [ + { + attribute: "product_collection", + operator: "eq", + values: ["pcol_towel"], + }, + ], + }, + }, + ]) + }) + + it("should throw an error when promotion with id does not exist", async () => { + let error + + try { + await service.removePromotionBuyRules("does-not-exist", []) + } catch (e) { + error = e + } + + expect(error.message).toEqual( + "Promotion with id: does-not-exist was not found" + ) + }) + + it("should throw an error when a id is not provided", async () => { + let error + + try { + await service.removePromotionBuyRules( + undefined as unknown as string, + [] + ) + } catch (e) { + error = e + } + + expect(error.message).toEqual("promotion - id must be defined") + }) + + it("should successfully remove rules for a promotion", async () => { + const ruleIds = promotion.application_method.buy_rules.map( + (rule) => rule.id + ) + + await service.removePromotionBuyRules(promotion.id, ruleIds) + + const updatedPromotion = await service.retrieve(promotion.id, { + relations: ["application_method.buy_rules.values"], + }) + + expect(updatedPromotion).toEqual( + expect.objectContaining({ + id: promotion.id, + application_method: expect.objectContaining({ + buy_rules: [], + }), + }) + ) + }) + }) }) - }) + }, }) diff --git a/packages/promotion/integration-tests/__tests__/services/promotion-module/register-usage.spec.ts b/packages/promotion/integration-tests/__tests__/services/promotion-module/register-usage.spec.ts index b3f836bca8..b02b777612 100644 --- a/packages/promotion/integration-tests/__tests__/services/promotion-module/register-usage.spec.ts +++ b/packages/promotion/integration-tests/__tests__/services/promotion-module/register-usage.spec.ts @@ -1,209 +1,189 @@ import { Modules } from "@medusajs/modules-sdk" import { IPromotionModuleService } from "@medusajs/types" -import { SqlEntityManager } from "@mikro-orm/postgresql" -import { initModules } from "medusa-test-utils" +import { moduleIntegrationTestRunner, SuiteOptions } from "medusa-test-utils" import { createCampaigns } from "../../../__fixtures__/campaigns" -import { MikroOrmWrapper } from "../../../utils" -import { getInitModuleConfig } from "../../../utils/get-init-module-config" jest.setTimeout(30000) -describe("Promotion Service: campaign usage", () => { - let service: IPromotionModuleService - let repositoryManager: SqlEntityManager - let shutdownFunc: () => void - - beforeAll(async () => { - const initModulesConfig = getInitModuleConfig() - - const { medusaApp, shutdown } = await initModules(initModulesConfig) - - service = medusaApp.modules[Modules.PROMOTION] - - shutdownFunc = shutdown - }) - - afterAll(async () => { - shutdownFunc() - }) - - beforeEach(async () => { - await MikroOrmWrapper.setupDatabase() - repositoryManager = MikroOrmWrapper.forkManager() - - await createCampaigns(repositoryManager) - }) - - afterEach(async () => { - await MikroOrmWrapper.clearDatabase() - }) - - describe("registerUsage", () => { - it("should register usage for type spend", async () => { - const createdPromotion = await service.create({ - code: "TEST_PROMO_SPEND", - type: "standard", - campaign_id: "campaign-id-1", +moduleIntegrationTestRunner({ + moduleName: Modules.PROMOTION, + testSuite: ({ + MikroOrmWrapper, + service, + }: SuiteOptions) => { + describe("Promotion Service: campaign usage", () => { + beforeEach(async () => { + await createCampaigns(MikroOrmWrapper.forkManager()) }) - await service.registerUsage([ - { - action: "addShippingMethodAdjustment", - shipping_method_id: "shipping_method_express", - amount: 200, - code: createdPromotion.code!, - }, - { - action: "addShippingMethodAdjustment", - shipping_method_id: "shipping_method_standard", - amount: 500, - code: createdPromotion.code!, - }, - ]) + describe("registerUsage", () => { + it("should register usage for type spend", async () => { + const createdPromotion = await service.create({ + code: "TEST_PROMO_SPEND", + type: "standard", + campaign_id: "campaign-id-1", + }) - const campaign = await service.retrieveCampaign("campaign-id-1", { - relations: ["budget"], - }) + await service.registerUsage([ + { + action: "addShippingMethodAdjustment", + shipping_method_id: "shipping_method_express", + amount: 200, + code: createdPromotion.code!, + }, + { + action: "addShippingMethodAdjustment", + shipping_method_id: "shipping_method_standard", + amount: 500, + code: createdPromotion.code!, + }, + ]) - expect(campaign.budget).toEqual( - expect.objectContaining({ - type: "spend", - limit: 1000, - used: 700, + const campaign = await service.retrieveCampaign("campaign-id-1", { + relations: ["budget"], + }) + + expect(campaign.budget).toEqual( + expect.objectContaining({ + type: "spend", + limit: 1000, + used: 700, + }) + ) }) - ) - }) - it("should register usage for type usage", async () => { - const createdPromotion = await service.create({ - code: "TEST_PROMO_USAGE", - type: "standard", - campaign_id: "campaign-id-2", - }) + it("should register usage for type usage", async () => { + const createdPromotion = await service.create({ + code: "TEST_PROMO_USAGE", + type: "standard", + campaign_id: "campaign-id-2", + }) - await service.registerUsage([ - { - action: "addShippingMethodAdjustment", - shipping_method_id: "shipping_method_express", - amount: 200, - code: createdPromotion.code!, - }, - { - action: "addShippingMethodAdjustment", - shipping_method_id: "shipping_method_standard", - amount: 500, - code: createdPromotion.code!, - }, - ]) + await service.registerUsage([ + { + action: "addShippingMethodAdjustment", + shipping_method_id: "shipping_method_express", + amount: 200, + code: createdPromotion.code!, + }, + { + action: "addShippingMethodAdjustment", + shipping_method_id: "shipping_method_standard", + amount: 500, + code: createdPromotion.code!, + }, + ]) - const campaign = await service.retrieveCampaign("campaign-id-2", { - relations: ["budget"], - }) + const campaign = await service.retrieveCampaign("campaign-id-2", { + relations: ["budget"], + }) - expect(campaign.budget).toEqual( - expect.objectContaining({ - type: "usage", - limit: 1000, - used: 1, + expect(campaign.budget).toEqual( + expect.objectContaining({ + type: "usage", + limit: 1000, + used: 1, + }) + ) }) - ) - }) - it("should not throw an error when compute action with code does not exist", async () => { - const response = await service - .registerUsage([ - { - action: "addShippingMethodAdjustment", - shipping_method_id: "shipping_method_express", - amount: 200, - code: "DOESNOTEXIST", - }, - ]) - .catch((e) => e) + it("should not throw an error when compute action with code does not exist", async () => { + const response = await service + .registerUsage([ + { + action: "addShippingMethodAdjustment", + shipping_method_id: "shipping_method_express", + amount: 200, + code: "DOESNOTEXIST", + }, + ]) + .catch((e) => e) - expect(response).toEqual(undefined) - }) - - it("should not register usage when limit is exceed for type usage", async () => { - const createdPromotion = await service.create({ - code: "TEST_PROMO_USAGE", - type: "standard", - campaign_id: "campaign-id-2", - }) - - await service.updateCampaigns({ - id: "campaign-id-2", - budget: { used: 1000, limit: 1000 }, - }) - - await service.registerUsage([ - { - action: "addShippingMethodAdjustment", - shipping_method_id: "shipping_method_express", - amount: 200, - code: createdPromotion.code!, - }, - { - action: "addShippingMethodAdjustment", - shipping_method_id: "shipping_method_standard", - amount: 500, - code: createdPromotion.code!, - }, - ]) - - const campaign = await service.retrieveCampaign("campaign-id-2", { - relations: ["budget"], - }) - - expect(campaign).toEqual( - expect.objectContaining({ - budget: expect.objectContaining({ - limit: 1000, - used: 1000, - }), + expect(response).toEqual(undefined) }) - ) - }) - it("should not register usage above limit when exceeded for type spend", async () => { - const createdPromotion = await service.create({ - code: "TEST_PROMO_SPEND", - type: "standard", - campaign_id: "campaign-id-1", - }) + it("should not register usage when limit is exceed for type usage", async () => { + const createdPromotion = await service.create({ + code: "TEST_PROMO_USAGE", + type: "standard", + campaign_id: "campaign-id-2", + }) - await service.updateCampaigns({ - id: "campaign-id-1", - budget: { used: 900, limit: 1000 }, - }) + await service.updateCampaigns({ + id: "campaign-id-2", + budget: { used: 1000, limit: 1000 }, + }) - await service.registerUsage([ - { - action: "addShippingMethodAdjustment", - shipping_method_id: "shipping_method_express", - amount: 100, - code: createdPromotion.code!, - }, - { - action: "addShippingMethodAdjustment", - shipping_method_id: "shipping_method_standard", - amount: 100, - code: createdPromotion.code!, - }, - ]) + await service.registerUsage([ + { + action: "addShippingMethodAdjustment", + shipping_method_id: "shipping_method_express", + amount: 200, + code: createdPromotion.code!, + }, + { + action: "addShippingMethodAdjustment", + shipping_method_id: "shipping_method_standard", + amount: 500, + code: createdPromotion.code!, + }, + ]) - const campaign = await service.retrieveCampaign("campaign-id-1", { - relations: ["budget"], - }) + const campaign = await service.retrieveCampaign("campaign-id-2", { + relations: ["budget"], + }) - expect(campaign).toEqual( - expect.objectContaining({ - budget: expect.objectContaining({ - limit: 1000, - used: 1000, - }), + expect(campaign).toEqual( + expect.objectContaining({ + budget: expect.objectContaining({ + limit: 1000, + used: 1000, + }), + }) + ) }) - ) + + it("should not register usage above limit when exceeded for type spend", async () => { + const createdPromotion = await service.create({ + code: "TEST_PROMO_SPEND", + type: "standard", + campaign_id: "campaign-id-1", + }) + + await service.updateCampaigns({ + id: "campaign-id-1", + budget: { used: 900, limit: 1000 }, + }) + + await service.registerUsage([ + { + action: "addShippingMethodAdjustment", + shipping_method_id: "shipping_method_express", + amount: 100, + code: createdPromotion.code!, + }, + { + action: "addShippingMethodAdjustment", + shipping_method_id: "shipping_method_standard", + amount: 100, + code: createdPromotion.code!, + }, + ]) + + const campaign = await service.retrieveCampaign("campaign-id-1", { + relations: ["budget"], + }) + + expect(campaign).toEqual( + expect.objectContaining({ + budget: expect.objectContaining({ + limit: 1000, + used: 1000, + }), + }) + ) + }) + }) }) - }) + }, }) diff --git a/packages/promotion/integration-tests/__tests__/services/promotion/index.spec.ts b/packages/promotion/integration-tests/__tests__/services/promotion/index.spec.ts index 452efa8585..47bda3d54e 100644 --- a/packages/promotion/integration-tests/__tests__/services/promotion/index.spec.ts +++ b/packages/promotion/integration-tests/__tests__/services/promotion/index.spec.ts @@ -1,71 +1,58 @@ -import { createMedusaContainer, PromotionType } from "@medusajs/utils" -import { SqlEntityManager } from "@mikro-orm/postgresql" -import { PromotionService } from "@services" +import { PromotionType } from "@medusajs/utils" +import { moduleIntegrationTestRunner, SuiteOptions } from "medusa-test-utils" import { createPromotions } from "../../../__fixtures__/promotion" -import { MikroOrmWrapper } from "../../../utils" -import { asValue } from "awilix" -import ContainerLoader from "../../../../src/loaders/container" +import { IPromotionModuleService } from "@medusajs/types" +import { Modules } from "@medusajs/modules-sdk" jest.setTimeout(30000) -describe("Promotion Service", () => { - let service: PromotionService - let testManager: SqlEntityManager - let repositoryManager: SqlEntityManager - - beforeEach(async () => { - await MikroOrmWrapper.setupDatabase() - repositoryManager = await MikroOrmWrapper.forkManager() - testManager = await MikroOrmWrapper.forkManager() - - const container = createMedusaContainer() - container.register("manager", asValue(repositoryManager)) - - await ContainerLoader({ container }) - - service = container.resolve("promotionService") - - await createPromotions(testManager) - }) - - afterEach(async () => { - await MikroOrmWrapper.clearDatabase() - }) - - describe("create", () => { - it("should throw an error when required params are not passed", async () => { - const error = await service - .create([ - { - type: PromotionType.STANDARD, - } as any, - ]) - .catch((e) => e) - - expect(error.message).toContain( - "Value for Promotion.code is required, 'undefined' found" - ) - }) - - it("should create a promotion successfully", async () => { - await service.create([ - { - code: "PROMOTION_TEST", - type: PromotionType.STANDARD, - }, - ]) - - const [promotion] = await service.list({ - code: ["PROMOTION_TEST"], +moduleIntegrationTestRunner({ + moduleName: Modules.PROMOTION, + testSuite: ({ + MikroOrmWrapper, + service, + }: SuiteOptions) => { + describe("Promotion Service", () => { + beforeEach(async () => { + await createPromotions(MikroOrmWrapper.forkManager()) }) - expect(promotion).toEqual( - expect.objectContaining({ - code: "PROMOTION_TEST", - is_automatic: false, - type: "standard", + describe("create", () => { + it("should throw an error when required params are not passed", async () => { + const error = await service + .create([ + { + type: PromotionType.STANDARD, + } as any, + ]) + .catch((e) => e) + + expect(error.message).toContain( + "Value for Promotion.code is required, 'undefined' found" + ) }) - ) + + it("should create a promotion successfully", async () => { + await service.create([ + { + code: "PROMOTION_TEST", + type: PromotionType.STANDARD, + }, + ]) + + const [promotion] = await service.list({ + code: ["PROMOTION_TEST"], + }) + + expect(promotion).toEqual( + expect.objectContaining({ + code: "PROMOTION_TEST", + is_automatic: false, + type: "standard", + }) + ) + }) + }) }) - }) + }, }) diff --git a/packages/promotion/integration-tests/setup-env.js b/packages/promotion/integration-tests/setup-env.js deleted file mode 100644 index dc7c561c75..0000000000 --- a/packages/promotion/integration-tests/setup-env.js +++ /dev/null @@ -1,6 +0,0 @@ -if (typeof process.env.DB_TEMP_NAME === "undefined") { - const tempName = parseInt(process.env.JEST_WORKER_ID || "1") - process.env.DB_TEMP_NAME = `medusa-promotion-integration-${tempName}` -} - -process.env.MEDUSA_PROMOTION_DB_SCHEMA = "public" diff --git a/packages/promotion/integration-tests/setup.js b/packages/promotion/integration-tests/setup.js deleted file mode 100644 index 43f99aab4a..0000000000 --- a/packages/promotion/integration-tests/setup.js +++ /dev/null @@ -1,3 +0,0 @@ -import { JestUtils } from "medusa-test-utils" - -JestUtils.afterAllHookDropDatabase() diff --git a/packages/promotion/integration-tests/utils/config.ts b/packages/promotion/integration-tests/utils/config.ts deleted file mode 100644 index 83730cc610..0000000000 --- a/packages/promotion/integration-tests/utils/config.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { ModuleServiceInitializeOptions } from "@medusajs/types" - -export const databaseOptions: ModuleServiceInitializeOptions["database"] = { - schema: "public", - clientUrl: "medusa-promotion-test", -} diff --git a/packages/promotion/integration-tests/utils/database.ts b/packages/promotion/integration-tests/utils/database.ts deleted file mode 100644 index 825aa4b93c..0000000000 --- a/packages/promotion/integration-tests/utils/database.ts +++ /dev/null @@ -1,18 +0,0 @@ -import {TestDatabaseUtils} from "medusa-test-utils" - -import * as PromotionModels from "@models" - -const pathToMigrations = "../../src/migrations" -const mikroOrmEntities = PromotionModels as unknown as any[] - -export const MikroOrmWrapper = TestDatabaseUtils.getMikroOrmWrapper({ - mikroOrmEntities, - pathToMigrations, -}) - -export const MikroOrmConfig = TestDatabaseUtils.getMikroOrmConfig({ - mikroOrmEntities, - pathToMigrations, -}) - -export const DB_URL = TestDatabaseUtils.getDatabaseURL() diff --git a/packages/promotion/integration-tests/utils/get-init-module-config.ts b/packages/promotion/integration-tests/utils/get-init-module-config.ts deleted file mode 100644 index b512096f97..0000000000 --- a/packages/promotion/integration-tests/utils/get-init-module-config.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { Modules, ModulesDefinition } from "@medusajs/modules-sdk" - -import { DB_URL } from "./database" - -export function getInitModuleConfig() { - const moduleOptions = { - defaultAdapterOptions: { - database: { - clientUrl: DB_URL, - schema: process.env.MEDUSA_PROMOTION_DB_SCHEMA, - }, - }, - } - - const injectedDependencies = {} - - const modulesConfig_ = { - [Modules.PROMOTION]: { - definition: ModulesDefinition[Modules.PROMOTION], - options: moduleOptions, - }, - } - - return { - injectedDependencies, - modulesConfig: modulesConfig_, - databaseConfig: { - clientUrl: DB_URL, - schema: process.env.MEDUSA_PROMOTION_DB_SCHEMA, - }, - joinerConfig: [], - } -} diff --git a/packages/promotion/integration-tests/utils/index.ts b/packages/promotion/integration-tests/utils/index.ts deleted file mode 100644 index 5ca5d1bdc0..0000000000 --- a/packages/promotion/integration-tests/utils/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from "./config" -export * from "./database" diff --git a/packages/promotion/jest.config.js b/packages/promotion/jest.config.js index a3d93259ca..bc1eac4b4f 100644 --- a/packages/promotion/jest.config.js +++ b/packages/promotion/jest.config.js @@ -18,6 +18,4 @@ module.exports = { testEnvironment: `node`, moduleFileExtensions: [`js`, `ts`], modulePathIgnorePatterns: ["dist/"], - setupFiles: ["/integration-tests/setup-env.js"], - setupFilesAfterEnv: ["/integration-tests/setup.js"], } diff --git a/packages/region/integration-tests/__tests__/region-module.spec.ts b/packages/region/integration-tests/__tests__/region-module.spec.ts index ca3f654f3e..261e6f3147 100644 --- a/packages/region/integration-tests/__tests__/region-module.spec.ts +++ b/packages/region/integration-tests/__tests__/region-module.spec.ts @@ -1,362 +1,353 @@ import { Modules } from "@medusajs/modules-sdk" import { IRegionModuleService } from "@medusajs/types" -import { initModules } from "medusa-test-utils" -import { MikroOrmWrapper } from "../utils" -import { getInitModuleConfig } from "../utils/get-init-module-config" +import { moduleIntegrationTestRunner, SuiteOptions } from "medusa-test-utils" jest.setTimeout(30000) -describe("Region Module Service", () => { - let service: IRegionModuleService - let shutdownFunc: () => Promise - - beforeEach(async () => { - await MikroOrmWrapper.setupDatabase() - - const initModulesConfig = getInitModuleConfig() - const { medusaApp, shutdown } = await initModules(initModulesConfig) - service = medusaApp.modules[Modules.REGION] - - shutdownFunc = shutdown - }) - - afterEach(async () => { - await MikroOrmWrapper.clearDatabase() - await shutdownFunc() - }) - - it("should create countries on application start", async () => { - const countries = await service.listCountries({}, { take: null }) - expect(countries.length).toEqual(250) - }) - - it("should create and list a region", async () => { - const createdRegion = await service.create({ - name: "Europe", - currency_code: "EUR", - automatic_taxes: false, - }) - - expect(createdRegion).toEqual( - expect.objectContaining({ - id: createdRegion.id, - name: "Europe", - currency_code: "eur", - countries: [], - automatic_taxes: false, +moduleIntegrationTestRunner({ + moduleName: Modules.REGION, + testSuite: ({ + MikroOrmWrapper, + service, + }: SuiteOptions) => { + describe("Region Module Service", () => { + it("should create countries on application start", async () => { + const countries = await service.listCountries({}, { take: null }) + expect(countries.length).toEqual(250) }) - ) - const region = await service.retrieve(createdRegion.id, { - relations: ["countries"], - }) + it("should create and list a region", async () => { + const createdRegion = await service.create({ + name: "Europe", + currency_code: "EUR", + automatic_taxes: false, + }) - expect(region).toEqual( - expect.objectContaining({ - id: region.id, - name: "Europe", - currency_code: "eur", - countries: [], - }) - ) - }) - - it("should create a region with countries", async () => { - const createdRegion = await service.create({ - name: "North America", - currency_code: "USD", - countries: ["us", "ca"], - }) - - const region = await service.retrieve(createdRegion.id, { - relations: ["countries"], - }) - - expect(region).toEqual( - expect.objectContaining({ - id: region.id, - name: "North America", - currency_code: "usd", - automatic_taxes: true, - countries: [ + expect(createdRegion).toEqual( expect.objectContaining({ - display_name: "Canada", - iso_2: "ca", - }), + id: createdRegion.id, + name: "Europe", + currency_code: "eur", + countries: [], + automatic_taxes: false, + }) + ) + + const region = await service.retrieve(createdRegion.id, { + relations: ["countries"], + }) + + expect(region).toEqual( expect.objectContaining({ - display_name: "United States", - iso_2: "us", - }), - ], + id: region.id, + name: "Europe", + currency_code: "eur", + countries: [], + }) + ) }) - ) - }) - it("should throw when country doesn't exist", async () => { - await expect( - service.create({ - name: "North America", - currency_code: "USD", - countries: ["neverland"], - }) - ).rejects.toThrowError('Countries with codes: "neverland" do not exist') - }) - - it("should throw when country is already assigned to a region", async () => { - await service.create({ - name: "North America", - currency_code: "USD", - countries: ["us"], - }) - - await expect( - service.create({ - name: "United States", - currency_code: "USD", - countries: ["us"], - }) - ).rejects.toThrowError( - 'Countries with codes: "us" are already assigned to a region' - ) - }) - - it("should throw when country is being assigned to multiple regions", async () => { - await expect( - service.create([ - { - name: "United States", + it("should create a region with countries", async () => { + const createdRegion = await service.create({ + name: "North America", currency_code: "USD", - countries: ["us"], - }, - { + countries: ["us", "ca"], + }) + + const region = await service.retrieve(createdRegion.id, { + relations: ["countries"], + }) + + expect(region).toEqual( + expect.objectContaining({ + id: region.id, + name: "North America", + currency_code: "usd", + automatic_taxes: true, + countries: [ + expect.objectContaining({ + display_name: "Canada", + iso_2: "ca", + }), + expect.objectContaining({ + display_name: "United States", + iso_2: "us", + }), + ], + }) + ) + }) + + it("should throw when country doesn't exist", async () => { + await expect( + service.create({ + name: "North America", + currency_code: "USD", + countries: ["neverland"], + }) + ).rejects.toThrowError('Countries with codes: "neverland" do not exist') + }) + + it("should throw when country is already assigned to a region", async () => { + await service.create({ name: "North America", currency_code: "USD", countries: ["us"], - }, - ]) - ).rejects.toThrowError( - 'Countries with codes: "us" are already assigned to a region' - ) - }) + }) - it("should upsert the region successfully", async () => { - const createdRegion = await service.upsert({ - name: "North America", - currency_code: "USD", - countries: ["us", "ca"], - }) + await expect( + service.create({ + name: "United States", + currency_code: "USD", + countries: ["us"], + }) + ).rejects.toThrowError( + 'Countries with codes: "us" are already assigned to a region' + ) + }) - await service.upsert({ - id: createdRegion.id, - name: "Americas", - currency_code: "MXN", - countries: ["us", "mx"], - automatic_taxes: false, - }) + it("should throw when country is being assigned to multiple regions", async () => { + await expect( + service.create([ + { + name: "United States", + currency_code: "USD", + countries: ["us"], + }, + { + name: "North America", + currency_code: "USD", + countries: ["us"], + }, + ]) + ).rejects.toThrowError( + 'Countries with codes: "us" are already assigned to a region' + ) + }) - const latestRegion = await service.retrieve(createdRegion.id, { - relations: ["countries"], - }) + it("should upsert the region successfully", async () => { + const createdRegion = await service.upsert({ + name: "North America", + currency_code: "USD", + countries: ["us", "ca"], + }) - expect(latestRegion).toMatchObject({ - id: createdRegion.id, - name: "Americas", - currency_code: "mxn", - automatic_taxes: false, - }) - expect(latestRegion.countries.map((c) => c.iso_2)).toEqual(["mx", "us"]) - }) - - it("should allow mixing create and update operations in upsert", async () => { - const createdRegion = await service.upsert({ - name: "North America", - currency_code: "USD", - countries: ["us", "ca"], - }) - - const upserted = await service.upsert([ - { - id: createdRegion.id, - name: "Americas", - currency_code: "USD", - countries: ["us", "ca"], - }, - { - name: "Central America", - currency_code: "MXN", - countries: ["mx"], - }, - ]) - - expect(upserted).toEqual( - expect.arrayContaining([ - expect.objectContaining({ + await service.upsert({ id: createdRegion.id, name: "Americas", - currency_code: "usd", - }), - expect.objectContaining({ - name: "Central America", - currency_code: "mxn", - }), - ]) - ) - }) - - it("should update the region successfully", async () => { - const createdRegion = await service.create({ - name: "North America", - currency_code: "USD", - countries: ["us", "ca"], - }) - - await service.update(createdRegion.id, { - name: "Americas", - currency_code: "MXN", - countries: ["us", "mx"], - }) - - const latestRegion = await service.retrieve(createdRegion.id, { - relations: ["countries"], - }) - - expect(latestRegion).toMatchObject({ - id: createdRegion.id, - name: "Americas", - currency_code: "mxn", - }) - - expect(latestRegion.countries.map((c) => c.iso_2)).toEqual(["mx", "us"]) - }) - - it("should update the region without affecting countries if countries are undefined", async () => { - const createdRegion = await service.create({ - name: "North America", - currency_code: "USD", - countries: ["us", "ca"], - }) - - await service.update(createdRegion.id, { - name: "Americas", - currency_code: "MXN", - }) - - const updatedRegion = await service.retrieve(createdRegion.id, { - relations: ["countries"], - }) - - expect(updatedRegion).toMatchObject({ - id: createdRegion.id, - name: "Americas", - currency_code: "mxn", - }) - - expect(updatedRegion.countries.map((c) => c.iso_2)).toEqual(["ca", "us"]) - }) - - it("should remove the countries in a region successfully", async () => { - const createdRegion = await service.create({ - name: "North America", - currency_code: "USD", - countries: ["us", "ca"], - }) - - await service.update(createdRegion.id, { - name: "Americas", - currency_code: "MXN", - countries: [], - }) - - const updatedRegion = await service.retrieve(createdRegion.id, { - relations: ["countries"], - }) - - expect(updatedRegion).toMatchObject({ - id: createdRegion.id, - name: "Americas", - currency_code: "mxn", - }) - - expect(updatedRegion.countries).toHaveLength(0) - }) - - it("should fail updating the region countries to non-existent ones", async () => { - const createdRegion = await service.create({ - name: "North America", - currency_code: "USD", - countries: ["us", "ca"], - }) - - await expect( - service.update( - { id: createdRegion.id }, - { - countries: ["us", "neverland"], - } - ) - ).rejects.toThrowError('Countries with codes: "neverland" do not exist') - }) - - it("should fail updating the region if there are duplicate countries", async () => { - const createdRegion = await service.create({ - name: "North America", - currency_code: "USD", - countries: ["us", "ca"], - }) - - await expect( - service.update( - { id: createdRegion.id }, - { - countries: ["us", "us"], - } - ) - ).rejects.toThrowError( - 'Countries with codes: "us" are already assigned to a region' - ) - }) - - it("should fail updating the region if country is already used", async () => { - const [createdRegion] = await service.create([ - { - name: "North America", - currency_code: "USD", - countries: ["us", "ca"], - }, - { - name: "Americas", - currency_code: "USD", - countries: ["mx"], - }, - ]) - - await expect( - service.update( - { id: createdRegion.id }, - { + currency_code: "MXN", countries: ["us", "mx"], - } - ) - ).rejects.toThrowError( - 'Countries with codes: "mx" are already assigned to a region' - ) - }) + automatic_taxes: false, + }) - it("should unset the region ID on the country when deleting a region", async () => { - const createdRegion = await service.create({ - name: "North America", - currency_code: "USD", - countries: ["us", "ca"], + const latestRegion = await service.retrieve(createdRegion.id, { + relations: ["countries"], + }) + + expect(latestRegion).toMatchObject({ + id: createdRegion.id, + name: "Americas", + currency_code: "mxn", + automatic_taxes: false, + }) + expect(latestRegion.countries.map((c) => c.iso_2)).toEqual(["mx", "us"]) + }) + + it("should allow mixing create and update operations in upsert", async () => { + const createdRegion = await service.upsert({ + name: "North America", + currency_code: "USD", + countries: ["us", "ca"], + }) + + const upserted = await service.upsert([ + { + id: createdRegion.id, + name: "Americas", + currency_code: "USD", + countries: ["us", "ca"], + }, + { + name: "Central America", + currency_code: "MXN", + countries: ["mx"], + }, + ]) + + expect(upserted).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + id: createdRegion.id, + name: "Americas", + currency_code: "usd", + }), + expect.objectContaining({ + name: "Central America", + currency_code: "mxn", + }), + ]) + ) + }) + + it("should update the region successfully", async () => { + const createdRegion = await service.create({ + name: "North America", + currency_code: "USD", + countries: ["us", "ca"], + }) + + await service.update(createdRegion.id, { + name: "Americas", + currency_code: "MXN", + countries: ["us", "mx"], + }) + + const latestRegion = await service.retrieve(createdRegion.id, { + relations: ["countries"], + }) + + expect(latestRegion).toMatchObject({ + id: createdRegion.id, + name: "Americas", + currency_code: "mxn", + }) + + expect(latestRegion.countries.map((c) => c.iso_2)).toEqual(["mx", "us"]) + }) + + it("should update the region without affecting countries if countries are undefined", async () => { + const createdRegion = await service.create({ + name: "North America", + currency_code: "USD", + countries: ["us", "ca"], + }) + + await service.update(createdRegion.id, { + name: "Americas", + currency_code: "MXN", + }) + + const updatedRegion = await service.retrieve(createdRegion.id, { + relations: ["countries"], + }) + + expect(updatedRegion).toMatchObject({ + id: createdRegion.id, + name: "Americas", + currency_code: "mxn", + }) + + expect(updatedRegion.countries.map((c) => c.iso_2)).toEqual([ + "ca", + "us", + ]) + }) + + it("should remove the countries in a region successfully", async () => { + const createdRegion = await service.create({ + name: "North America", + currency_code: "USD", + countries: ["us", "ca"], + }) + + await service.update(createdRegion.id, { + name: "Americas", + currency_code: "MXN", + countries: [], + }) + + const updatedRegion = await service.retrieve(createdRegion.id, { + relations: ["countries"], + }) + + expect(updatedRegion).toMatchObject({ + id: createdRegion.id, + name: "Americas", + currency_code: "mxn", + }) + + expect(updatedRegion.countries).toHaveLength(0) + }) + + it("should fail updating the region countries to non-existent ones", async () => { + const createdRegion = await service.create({ + name: "North America", + currency_code: "USD", + countries: ["us", "ca"], + }) + + await expect( + service.update( + { id: createdRegion.id }, + { + countries: ["us", "neverland"], + } + ) + ).rejects.toThrowError('Countries with codes: "neverland" do not exist') + }) + + it("should fail updating the region if there are duplicate countries", async () => { + const createdRegion = await service.create({ + name: "North America", + currency_code: "USD", + countries: ["us", "ca"], + }) + + await expect( + service.update( + { id: createdRegion.id }, + { + countries: ["us", "us"], + } + ) + ).rejects.toThrowError( + 'Countries with codes: "us" are already assigned to a region' + ) + }) + + it("should fail updating the region if country is already used", async () => { + const [createdRegion] = await service.create([ + { + name: "North America", + currency_code: "USD", + countries: ["us", "ca"], + }, + { + name: "Americas", + currency_code: "USD", + countries: ["mx"], + }, + ]) + + await expect( + service.update( + { id: createdRegion.id }, + { + countries: ["us", "mx"], + } + ) + ).rejects.toThrowError( + 'Countries with codes: "mx" are already assigned to a region' + ) + }) + + it("should unset the region ID on the country when deleting a region", async () => { + const createdRegion = await service.create({ + name: "North America", + currency_code: "USD", + countries: ["us", "ca"], + }) + + await service.delete(createdRegion.id) + + const resp = await service.create({ + name: "North America", + currency_code: "USD", + countries: ["us", "ca"], + }) + + expect(resp.countries).toHaveLength(2) + }) }) - - await service.delete(createdRegion.id) - - const resp = await service.create({ - name: "North America", - currency_code: "USD", - countries: ["us", "ca"], - }) - - expect(resp.countries).toHaveLength(2) - }) + }, }) diff --git a/packages/region/integration-tests/setup-env.js b/packages/region/integration-tests/setup-env.js deleted file mode 100644 index 7eff9a200b..0000000000 --- a/packages/region/integration-tests/setup-env.js +++ /dev/null @@ -1,8 +0,0 @@ -if (typeof process.env.DB_TEMP_NAME === "undefined") { - const tempName = parseInt(process.env.JEST_WORKER_ID || "1") - process.env.DB_TEMP_NAME = `medusa-region-integration-${tempName}` -} - -process.env.MEDUSA_REGION_DB_SCHEMA = "public" -// TODO: Remove this when all modules are migrated to v2 -process.env.MEDUSA_FF_MEDUSA_V2 = true diff --git a/packages/region/integration-tests/setup.js b/packages/region/integration-tests/setup.js deleted file mode 100644 index 43f99aab4a..0000000000 --- a/packages/region/integration-tests/setup.js +++ /dev/null @@ -1,3 +0,0 @@ -import { JestUtils } from "medusa-test-utils" - -JestUtils.afterAllHookDropDatabase() diff --git a/packages/region/integration-tests/utils/config.ts b/packages/region/integration-tests/utils/config.ts deleted file mode 100644 index 7c5cb65747..0000000000 --- a/packages/region/integration-tests/utils/config.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { ModuleServiceInitializeOptions } from "@medusajs/types" - -export const databaseOptions: ModuleServiceInitializeOptions["database"] = { - schema: "public", - clientUrl: "medusa-region-test", -} diff --git a/packages/region/integration-tests/utils/database.ts b/packages/region/integration-tests/utils/database.ts deleted file mode 100644 index 66272d1565..0000000000 --- a/packages/region/integration-tests/utils/database.ts +++ /dev/null @@ -1,18 +0,0 @@ -import {TestDatabaseUtils} from "medusa-test-utils" - -import * as RegionModels from "@models" - -const pathToMigrations = "../../src/migrations" -const mikroOrmEntities = RegionModels as unknown as any[] - -export const MikroOrmWrapper = TestDatabaseUtils.getMikroOrmWrapper({ - mikroOrmEntities, - pathToMigrations, -}) - -export const MikroOrmConfig = TestDatabaseUtils.getMikroOrmConfig({ - mikroOrmEntities, - pathToMigrations, -}) - -export const DB_URL = TestDatabaseUtils.getDatabaseURL() diff --git a/packages/region/integration-tests/utils/get-init-module-config.ts b/packages/region/integration-tests/utils/get-init-module-config.ts deleted file mode 100644 index 1c891ce6f4..0000000000 --- a/packages/region/integration-tests/utils/get-init-module-config.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { Modules, ModulesDefinition } from "@medusajs/modules-sdk" - -import { DB_URL } from "./database" - -export function getInitModuleConfig() { - const moduleOptions = { - defaultAdapterOptions: { - database: { - clientUrl: DB_URL, - schema: process.env.MEDUSA_REGION_DB_SCHEMA, - }, - }, - } - - const injectedDependencies = {} - - const modulesConfig_ = { - [Modules.REGION]: { - definition: ModulesDefinition[Modules.REGION], - options: moduleOptions, - }, - } - - return { - injectedDependencies, - modulesConfig: modulesConfig_, - databaseConfig: { - clientUrl: DB_URL, - schema: process.env.MEDUSA_REGION_DB_SCHEMA, - }, - joinerConfig: [], - } -} diff --git a/packages/region/integration-tests/utils/index.ts b/packages/region/integration-tests/utils/index.ts deleted file mode 100644 index e930063cf1..0000000000 --- a/packages/region/integration-tests/utils/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from "./config" -export * from "./database" - diff --git a/packages/region/jest.config.js b/packages/region/jest.config.js index 456054fe8a..58c887c1c3 100644 --- a/packages/region/jest.config.js +++ b/packages/region/jest.config.js @@ -17,6 +17,4 @@ module.exports = { testEnvironment: `node`, moduleFileExtensions: [`js`, `ts`], modulePathIgnorePatterns: ["dist/"], - setupFiles: ["/integration-tests/setup-env.js"], - setupFilesAfterEnv: ["/integration-tests/setup.js"], } diff --git a/packages/user/integration-tests/__tests__/services/module/invite.spec.ts b/packages/user/integration-tests/__tests__/services/module/invite.spec.ts index 346bd61eeb..b58d62d481 100644 --- a/packages/user/integration-tests/__tests__/services/module/invite.spec.ts +++ b/packages/user/integration-tests/__tests__/services/module/invite.spec.ts @@ -1,12 +1,9 @@ import { IUserModuleService } from "@medusajs/types/dist/user" -import { MikroOrmWrapper } from "../../../utils" import { MockEventBusService } from "medusa-test-utils" import { Modules } from "@medusajs/modules-sdk" -import { SqlEntityManager } from "@mikro-orm/postgresql" import { UserEvents } from "@medusajs/utils" import { createInvites } from "../../../__fixtures__/invite" -import { getInitModuleConfig } from "../../../utils/get-init-module-config" -import { initModules } from "medusa-test-utils" +import { moduleIntegrationTestRunner, SuiteOptions } from "medusa-test-utils" jest.setTimeout(30000) @@ -28,243 +25,235 @@ const defaultInviteData = [ }, ] -describe("UserModuleService - Invite", () => { - let service: IUserModuleService - let testManager: SqlEntityManager - let shutdownFunc: () => Promise - - beforeAll(async () => { - const initModulesConfig = getInitModuleConfig() - - const { medusaApp, shutdown } = await initModules(initModulesConfig) - - service = medusaApp.modules[Modules.USER] - - shutdownFunc = shutdown - }) - - beforeEach(async () => { - await MikroOrmWrapper.setupDatabase() - testManager = MikroOrmWrapper.forkManager() - jest.clearAllMocks() - }) - - afterEach(async () => { - await MikroOrmWrapper.clearDatabase() - }) - - afterAll(async () => { - await shutdownFunc() - }) - - describe("listInvites", () => { - it("should list invites", async () => { - await createInvites(testManager, defaultInviteData) - - const invites = await service.listInvites() - - expect(invites).toEqual([ - expect.objectContaining({ - id: "1", - }), - expect.objectContaining({ - id: "2", - }), - ]) - }) - - it("should list invites by id", async () => { - await createInvites(testManager, defaultInviteData) - const invites = await service.listInvites({ - id: ["1"], +moduleIntegrationTestRunner({ + moduleName: Modules.USER, + moduleOptions: { + jwt_secret: "test", + }, + injectedDependencies: { + eventBusModuleService: new MockEventBusService(), + }, + testSuite: ({ + MikroOrmWrapper, + service, + }: SuiteOptions) => { + describe("UserModuleService - Invite", () => { + beforeEach(async () => { + jest.clearAllMocks() }) - expect(invites).toEqual([ - expect.objectContaining({ - id: "1", - }), - ]) - }) - }) + describe("listInvites", () => { + it("should list invites", async () => { + await createInvites(MikroOrmWrapper.forkManager(), defaultInviteData) - describe("listAndCountInvites", () => { - it("should list and count invites", async () => { - await createInvites(testManager, defaultInviteData) - const [invites, count] = await service.listAndCountInvites() + const invites = await service.listInvites() - expect(count).toEqual(2) - expect(invites).toEqual([ - expect.objectContaining({ - id: "1", - }), - expect.objectContaining({ - id: "2", - }), - ]) - }) - - it("should listAndCount invites by id", async () => { - await createInvites(testManager, defaultInviteData) - const [invites, count] = await service.listAndCountInvites({ - id: "1", - }) - - expect(count).toEqual(1) - expect(invites).toEqual([ - expect.objectContaining({ - id: "1", - }), - ]) - }) - }) - - describe("retrieveInvite", () => { - const id = "1" - - it("should return an invite for the given id", async () => { - await createInvites(testManager, defaultInviteData) - const invite = await service.retrieveInvite(id) - - expect(invite).toEqual( - expect.objectContaining({ - id, + expect(invites).toEqual([ + expect.objectContaining({ + id: "1", + }), + expect.objectContaining({ + id: "2", + }), + ]) }) - ) - }) - it("should throw an error when an invite with the given id does not exist", async () => { - const error = await service - .retrieveInvite("does-not-exist") - .catch((e) => e) + it("should list invites by id", async () => { + await createInvites(MikroOrmWrapper.forkManager(), defaultInviteData) + const invites = await service.listInvites({ + id: ["1"], + }) - expect(error.message).toEqual( - "Invite with id: does-not-exist was not found" - ) - }) - - it("should throw an error when inviteId is not provided", async () => { - const error = await service - .retrieveInvite(undefined as unknown as string) - .catch((e) => e) - - expect(error.message).toEqual("invite - id must be defined") - }) - - it("should return invite based on config select param", async () => { - await createInvites(testManager, defaultInviteData) - const invite = await service.retrieveInvite(id, { - select: ["id"], - }) - - expect(invite).toEqual({ - id, - }) - }) - }) - - describe("updateInvite", () => { - it("should throw an error when an id does not exist", async () => { - const error = await service - .updateInvites([ - { - id: "does-not-exist", - }, - ]) - .catch((e) => e) - - expect(error.message).toEqual('Invite with id "does-not-exist" not found') - }) - - it("should emit invite updated events", async () => { - await createInvites(testManager, defaultInviteData) - - jest.clearAllMocks() - - const eventBusSpy = jest.spyOn(MockEventBusService.prototype, "emit") - await service.updateInvites([ - { - id: "1", - accepted: true, - }, - ]) - - expect(eventBusSpy).toHaveBeenCalledTimes(1) - expect(eventBusSpy).toHaveBeenCalledWith([ - expect.objectContaining({ - body: expect.objectContaining({ - data: { id: "1" }, - }), - eventName: UserEvents.invite_updated, - }), - ]) - }) - }) - - describe("resendInvite", () => { - it("should emit token generated event for invites", async () => { - await createInvites(testManager, defaultInviteData) - const eventBusSpy = jest.spyOn(MockEventBusService.prototype, "emit") - - await service.refreshInviteTokens(["1"]) - - expect(eventBusSpy).toHaveBeenCalledTimes(1) - expect(eventBusSpy).toHaveBeenCalledWith([ - expect.objectContaining({ - body: expect.objectContaining({ - data: { id: "1" }, - }), - eventName: UserEvents.invite_token_generated, - }), - ]) - }) - }) - describe("createInvitie", () => { - it("should create an invite successfully", async () => { - await service.createInvites(defaultInviteData) - - const [invites, count] = await service.listAndCountInvites({ - id: ["1"], - }) - - expect(count).toEqual(1) - expect(invites[0]).toEqual( - expect.objectContaining({ - id: "1", + expect(invites).toEqual([ + expect.objectContaining({ + id: "1", + }), + ]) }) - ) - }) + }) - it("should emit invite created events", async () => { - const eventBusSpy = jest.spyOn(MockEventBusService.prototype, "emit") - await service.createInvites(defaultInviteData) + describe("listAndCountInvites", () => { + it("should list and count invites", async () => { + await createInvites(MikroOrmWrapper.forkManager(), defaultInviteData) + const [invites, count] = await service.listAndCountInvites() - expect(eventBusSpy).toHaveBeenCalledTimes(1) - expect(eventBusSpy).toHaveBeenCalledWith([ - expect.objectContaining({ - body: expect.objectContaining({ - data: { id: "1" }, - }), - eventName: UserEvents.invite_created, - }), - expect.objectContaining({ - body: expect.objectContaining({ - data: { id: "2" }, - }), - eventName: UserEvents.invite_created, - }), - expect.objectContaining({ - body: expect.objectContaining({ - data: { id: "1" }, - }), - eventName: UserEvents.invite_token_generated, - }), - expect.objectContaining({ - body: expect.objectContaining({ - data: { id: "2" }, - }), - eventName: UserEvents.invite_token_generated, - }), - ]) + expect(count).toEqual(2) + expect(invites).toEqual([ + expect.objectContaining({ + id: "1", + }), + expect.objectContaining({ + id: "2", + }), + ]) + }) + + it("should listAndCount invites by id", async () => { + await createInvites(MikroOrmWrapper.forkManager(), defaultInviteData) + const [invites, count] = await service.listAndCountInvites({ + id: "1", + }) + + expect(count).toEqual(1) + expect(invites).toEqual([ + expect.objectContaining({ + id: "1", + }), + ]) + }) + }) + + describe("retrieveInvite", () => { + const id = "1" + + it("should return an invite for the given id", async () => { + await createInvites(MikroOrmWrapper.forkManager(), defaultInviteData) + const invite = await service.retrieveInvite(id) + + expect(invite).toEqual( + expect.objectContaining({ + id, + }) + ) + }) + + it("should throw an error when an invite with the given id does not exist", async () => { + const error = await service + .retrieveInvite("does-not-exist") + .catch((e) => e) + + expect(error.message).toEqual( + "Invite with id: does-not-exist was not found" + ) + }) + + it("should throw an error when inviteId is not provided", async () => { + const error = await service + .retrieveInvite(undefined as unknown as string) + .catch((e) => e) + + expect(error.message).toEqual("invite - id must be defined") + }) + + it("should return invite based on config select param", async () => { + await createInvites(MikroOrmWrapper.forkManager(), defaultInviteData) + const invite = await service.retrieveInvite(id, { + select: ["id"], + }) + + expect(invite).toEqual({ + id, + }) + }) + }) + + describe("updateInvite", () => { + it("should throw an error when an id does not exist", async () => { + const error = await service + .updateInvites([ + { + id: "does-not-exist", + }, + ]) + .catch((e) => e) + + expect(error.message).toEqual( + 'Invite with id "does-not-exist" not found' + ) + }) + + it("should emit invite updated events", async () => { + await createInvites(MikroOrmWrapper.forkManager(), defaultInviteData) + + jest.clearAllMocks() + + const eventBusSpy = jest.spyOn(MockEventBusService.prototype, "emit") + await service.updateInvites([ + { + id: "1", + accepted: true, + }, + ]) + + expect(eventBusSpy).toHaveBeenCalledTimes(1) + expect(eventBusSpy).toHaveBeenCalledWith([ + expect.objectContaining({ + body: expect.objectContaining({ + data: { id: "1" }, + }), + eventName: UserEvents.invite_updated, + }), + ]) + }) + }) + + describe("resendInvite", () => { + it("should emit token generated event for invites", async () => { + await createInvites(MikroOrmWrapper.forkManager(), defaultInviteData) + const eventBusSpy = jest.spyOn(MockEventBusService.prototype, "emit") + + await service.refreshInviteTokens(["1"]) + + expect(eventBusSpy).toHaveBeenCalledTimes(1) + expect(eventBusSpy).toHaveBeenCalledWith([ + expect.objectContaining({ + body: expect.objectContaining({ + data: { id: "1" }, + }), + eventName: UserEvents.invite_token_generated, + }), + ]) + }) + }) + describe("createInvitie", () => { + it("should create an invite successfully", async () => { + await service.createInvites(defaultInviteData) + + const [invites, count] = await service.listAndCountInvites({ + id: ["1"], + }) + + expect(count).toEqual(1) + expect(invites[0]).toEqual( + expect.objectContaining({ + id: "1", + }) + ) + }) + + it("should emit invite created events", async () => { + const eventBusSpy = jest.spyOn(MockEventBusService.prototype, "emit") + await service.createInvites(defaultInviteData) + + expect(eventBusSpy).toHaveBeenCalledTimes(1) + expect(eventBusSpy).toHaveBeenCalledWith([ + expect.objectContaining({ + body: expect.objectContaining({ + data: { id: "1" }, + }), + eventName: UserEvents.invite_created, + }), + expect.objectContaining({ + body: expect.objectContaining({ + data: { id: "2" }, + }), + eventName: UserEvents.invite_created, + }), + expect.objectContaining({ + body: expect.objectContaining({ + data: { id: "1" }, + }), + eventName: UserEvents.invite_token_generated, + }), + expect.objectContaining({ + body: expect.objectContaining({ + data: { id: "2" }, + }), + eventName: UserEvents.invite_token_generated, + }), + ]) + }) + }) }) - }) + }, }) diff --git a/packages/user/integration-tests/__tests__/services/module/user.spec.ts b/packages/user/integration-tests/__tests__/services/module/user.spec.ts index 3f5150745e..77f3bcab8f 100644 --- a/packages/user/integration-tests/__tests__/services/module/user.spec.ts +++ b/packages/user/integration-tests/__tests__/services/module/user.spec.ts @@ -1,12 +1,9 @@ import { IUserModuleService } from "@medusajs/types/dist/user" -import { MikroOrmWrapper } from "../../../utils" import { MockEventBusService } from "medusa-test-utils" import { Modules } from "@medusajs/modules-sdk" -import { SqlEntityManager } from "@mikro-orm/postgresql" import { UserEvents } from "@medusajs/utils" import { createUsers } from "../../../__fixtures__/user" -import { getInitModuleConfig } from "../../../utils/get-init-module-config" -import { initModules } from "medusa-test-utils" +import { moduleIntegrationTestRunner, SuiteOptions } from "medusa-test-utils" jest.setTimeout(30000) @@ -21,231 +18,224 @@ const defaultUserData = [ }, ] -describe("UserModuleService - User", () => { - let service: IUserModuleService - let testManager: SqlEntityManager - let shutdownFunc: () => Promise - - beforeAll(async () => { - const initModulesConfig = getInitModuleConfig() - - const { medusaApp, shutdown } = await initModules(initModulesConfig) - - service = medusaApp.modules[Modules.USER] - - shutdownFunc = shutdown - }) - - beforeEach(async () => { - await MikroOrmWrapper.setupDatabase() - testManager = MikroOrmWrapper.forkManager() - }) - - afterEach(async () => { - await MikroOrmWrapper.clearDatabase() - jest.clearAllMocks() - }) - - afterAll(async () => { - await shutdownFunc() - }) - - describe("list", () => { - it("should list users", async () => { - await createUsers(testManager, defaultUserData) - - const users = await service.list() - - expect(users).toEqual([ - expect.objectContaining({ - id: "1", - }), - expect.objectContaining({ - id: "2", - }), - ]) - }) - - it("should list users by id", async () => { - await createUsers(testManager, defaultUserData) - const users = await service.list({ - id: ["1"], +moduleIntegrationTestRunner({ + moduleName: Modules.USER, + moduleOptions: { + jwt_secret: "test", + }, + injectedDependencies: { + eventBusModuleService: new MockEventBusService(), + }, + testSuite: ({ + MikroOrmWrapper, + service, + medusaApp, + }: SuiteOptions) => { + describe("UserModuleService - User", () => { + afterEach(async () => { + jest.clearAllMocks() }) - expect(users).toEqual([ - expect.objectContaining({ - id: "1", - }), - ]) - }) - }) + describe("list", () => { + it("should list users", async () => { + await createUsers(MikroOrmWrapper.forkManager(), defaultUserData) - describe("listAndCount", () => { - it("should list and count users", async () => { - await createUsers(testManager, defaultUserData) - const [users, count] = await service.listAndCount() + const users = await service.list() - expect(count).toEqual(2) - expect(users).toEqual([ - expect.objectContaining({ - id: "1", - }), - expect.objectContaining({ - id: "2", - }), - ]) - }) - - it("should list and count users by id", async () => { - await createUsers(testManager, defaultUserData) - const [Users, count] = await service.listAndCount({ - id: "1", - }) - - expect(count).toEqual(1) - expect(Users).toEqual([ - expect.objectContaining({ - id: "1", - }), - ]) - }) - }) - - describe("retrieve", () => { - const id = "1" - - it("should return an user for the given id", async () => { - await createUsers(testManager, defaultUserData) - - const user = await service.retrieve(id) - - expect(user).toEqual( - expect.objectContaining({ - id, + expect(users).toEqual([ + expect.objectContaining({ + id: "1", + }), + expect.objectContaining({ + id: "2", + }), + ]) }) - ) - }) - it("should throw an error when an user with the given id does not exist", async () => { - const error = await service.retrieve("does-not-exist").catch((e) => e) + it("should list users by id", async () => { + await createUsers(MikroOrmWrapper.forkManager(), defaultUserData) + const users = await service.list({ + id: ["1"], + }) - expect(error.message).toEqual( - "User with id: does-not-exist was not found" - ) - }) - - it("should throw an error when a userId is not provided", async () => { - const error = await service - .retrieve(undefined as unknown as string) - .catch((e) => e) - - expect(error.message).toEqual("user - id must be defined") - }) - - it("should return user based on config select param", async () => { - await createUsers(testManager, defaultUserData) - - const User = await service.retrieve(id, { - select: ["id"], - }) - - const serialized = JSON.parse(JSON.stringify(User)) - - expect(serialized).toEqual({ - id, - }) - }) - }) - - describe("delete", () => { - const id = "1" - - it("should delete the users given an id successfully", async () => { - await createUsers(testManager, defaultUserData) - - await service.delete([id]) - - const users = await service.list({ - id: [id], - }) - - expect(users).toHaveLength(0) - }) - }) - - describe("update", () => { - it("should throw an error when a id does not exist", async () => { - const error = await service - .update([ - { - id: "does-not-exist", - }, - ]) - .catch((e) => e) - - expect(error.message).toEqual('User with id "does-not-exist" not found') - }) - - it("should emit user created events", async () => { - const eventBusSpy = jest.spyOn(MockEventBusService.prototype, "emit") - await service.create(defaultUserData) - - jest.clearAllMocks() - - await service.update([ - { - id: "1", - first_name: "John", - }, - ]) - - expect(eventBusSpy).toHaveBeenCalledTimes(1) - expect(eventBusSpy).toHaveBeenCalledWith([ - expect.objectContaining({ - body: expect.objectContaining({ - data: { id: "1" }, - }), - eventName: UserEvents.updated, - }), - ]) - }) - }) - - describe("create", () => { - it("should create a user successfully", async () => { - await service.create(defaultUserData) - - const [User, count] = await service.listAndCount({ - id: ["1"], - }) - - expect(count).toEqual(1) - expect(User[0]).toEqual( - expect.objectContaining({ - id: "1", + expect(users).toEqual([ + expect.objectContaining({ + id: "1", + }), + ]) }) - ) - }) + }) - it("should emit user created events", async () => { - const eventBusSpy = jest.spyOn(MockEventBusService.prototype, "emit") - await service.create(defaultUserData) + describe("listAndCount", () => { + it("should list and count users", async () => { + await createUsers(MikroOrmWrapper.forkManager(), defaultUserData) + const [users, count] = await service.listAndCount() - expect(eventBusSpy).toHaveBeenCalledTimes(1) - expect(eventBusSpy).toHaveBeenCalledWith([ - expect.objectContaining({ - body: expect.objectContaining({ - data: { id: "1" }, - }), - eventName: UserEvents.created, - }), - expect.objectContaining({ - body: expect.objectContaining({ - data: { id: "2" }, - }), - eventName: UserEvents.created, - }), - ]) + expect(count).toEqual(2) + expect(users).toEqual([ + expect.objectContaining({ + id: "1", + }), + expect.objectContaining({ + id: "2", + }), + ]) + }) + + it("should list and count users by id", async () => { + await createUsers(MikroOrmWrapper.forkManager(), defaultUserData) + const [Users, count] = await service.listAndCount({ + id: "1", + }) + + expect(count).toEqual(1) + expect(Users).toEqual([ + expect.objectContaining({ + id: "1", + }), + ]) + }) + }) + + describe("retrieve", () => { + const id = "1" + + it("should return an user for the given id", async () => { + await createUsers(MikroOrmWrapper.forkManager(), defaultUserData) + + const user = await service.retrieve(id) + + expect(user).toEqual( + expect.objectContaining({ + id, + }) + ) + }) + + it("should throw an error when an user with the given id does not exist", async () => { + const error = await service.retrieve("does-not-exist").catch((e) => e) + + expect(error.message).toEqual( + "User with id: does-not-exist was not found" + ) + }) + + it("should throw an error when a userId is not provided", async () => { + const error = await service + .retrieve(undefined as unknown as string) + .catch((e) => e) + + expect(error.message).toEqual("user - id must be defined") + }) + + it("should return user based on config select param", async () => { + await createUsers(MikroOrmWrapper.forkManager(), defaultUserData) + + const User = await service.retrieve(id, { + select: ["id"], + }) + + const serialized = JSON.parse(JSON.stringify(User)) + + expect(serialized).toEqual({ + id, + }) + }) + }) + + describe("delete", () => { + const id = "1" + + it("should delete the users given an id successfully", async () => { + await createUsers(MikroOrmWrapper.forkManager(), defaultUserData) + + await service.delete([id]) + + const users = await service.list({ + id: [id], + }) + + expect(users).toHaveLength(0) + }) + }) + + describe("update", () => { + it("should throw an error when a id does not exist", async () => { + const error = await service + .update([ + { + id: "does-not-exist", + }, + ]) + .catch((e) => e) + + expect(error.message).toEqual( + 'User with id "does-not-exist" not found' + ) + }) + + it("should emit user created events", async () => { + const eventBusSpy = jest.spyOn(MockEventBusService.prototype, "emit") + await service.create(defaultUserData) + + jest.clearAllMocks() + + await service.update([ + { + id: "1", + first_name: "John", + }, + ]) + + expect(eventBusSpy).toHaveBeenCalledTimes(1) + expect(eventBusSpy).toHaveBeenCalledWith([ + expect.objectContaining({ + body: expect.objectContaining({ + data: { id: "1" }, + }), + eventName: UserEvents.updated, + }), + ]) + }) + }) + + describe("create", () => { + it("should create a user successfully", async () => { + await service.create(defaultUserData) + + const [User, count] = await service.listAndCount({ + id: ["1"], + }) + + expect(count).toEqual(1) + expect(User[0]).toEqual( + expect.objectContaining({ + id: "1", + }) + ) + }) + + it("should emit user created events", async () => { + const eventBusSpy = jest.spyOn(MockEventBusService.prototype, "emit") + await service.create(defaultUserData) + + expect(eventBusSpy).toHaveBeenCalledTimes(1) + expect(eventBusSpy).toHaveBeenCalledWith([ + expect.objectContaining({ + body: expect.objectContaining({ + data: { id: "1" }, + }), + eventName: UserEvents.created, + }), + expect.objectContaining({ + body: expect.objectContaining({ + data: { id: "2" }, + }), + eventName: UserEvents.created, + }), + ]) + }) + }) }) - }) + }, }) diff --git a/packages/user/integration-tests/setup-env.js b/packages/user/integration-tests/setup-env.js deleted file mode 100644 index ef21d71d85..0000000000 --- a/packages/user/integration-tests/setup-env.js +++ /dev/null @@ -1,6 +0,0 @@ -if (typeof process.env.DB_TEMP_NAME === "undefined") { - const tempName = parseInt(process.env.JEST_WORKER_ID || "1") - process.env.DB_TEMP_NAME = `medusa-user-integration-${tempName}` -} - -process.env.MEDUSA_USER_DB_SCHEMA = "public" diff --git a/packages/user/integration-tests/setup.js b/packages/user/integration-tests/setup.js deleted file mode 100644 index 43f99aab4a..0000000000 --- a/packages/user/integration-tests/setup.js +++ /dev/null @@ -1,3 +0,0 @@ -import { JestUtils } from "medusa-test-utils" - -JestUtils.afterAllHookDropDatabase() diff --git a/packages/user/integration-tests/utils/config.ts b/packages/user/integration-tests/utils/config.ts deleted file mode 100644 index d58b9ff622..0000000000 --- a/packages/user/integration-tests/utils/config.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { ModuleServiceInitializeOptions } from "@medusajs/types" - -export const databaseOptions: ModuleServiceInitializeOptions["database"] = { - schema: "public", - clientUrl: "medusa-user-test", -} diff --git a/packages/user/integration-tests/utils/database.ts b/packages/user/integration-tests/utils/database.ts deleted file mode 100644 index 507ff58bd2..0000000000 --- a/packages/user/integration-tests/utils/database.ts +++ /dev/null @@ -1,18 +0,0 @@ -import * as UserModels from "@models" - -import {TestDatabaseUtils} from "medusa-test-utils" - -const pathToMigrations = "../../src/migrations" -const mikroOrmEntities = UserModels as unknown as any[] - -export const MikroOrmWrapper = TestDatabaseUtils.getMikroOrmWrapper({ - mikroOrmEntities, - pathToMigrations, -}) - -export const MikroOrmConfig = TestDatabaseUtils.getMikroOrmConfig({ - mikroOrmEntities, - pathToMigrations, -}) - -export const DB_URL = TestDatabaseUtils.getDatabaseURL() diff --git a/packages/user/integration-tests/utils/get-init-module-config.ts b/packages/user/integration-tests/utils/get-init-module-config.ts deleted file mode 100644 index 5bd27dc5b8..0000000000 --- a/packages/user/integration-tests/utils/get-init-module-config.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { Modules, ModulesDefinition } from "@medusajs/modules-sdk" - -import { DB_URL } from "./database" -import { MockEventBusService } from "medusa-test-utils" - -export function getInitModuleConfig() { - const moduleOptions = { - defaultAdapterOptions: { - database: { - clientUrl: DB_URL, - schema: process.env.MEDUSA_USER_DB_SCHEMA, - }, - }, - jwt_secret: "test", - } - - const injectedDependencies = { - eventBusModuleService: new MockEventBusService(), - } - - const modulesConfig_ = { - [Modules.USER]: { - definition: ModulesDefinition[Modules.USER], - options: moduleOptions, - }, - } - - return { - injectedDependencies, - modulesConfig: modulesConfig_, - databaseConfig: { - clientUrl: DB_URL, - schema: process.env.MEDUSA_USER_DB_SCHEMA, - }, - joinerConfig: [], - } -} diff --git a/packages/user/integration-tests/utils/index.ts b/packages/user/integration-tests/utils/index.ts deleted file mode 100644 index 5ca5d1bdc0..0000000000 --- a/packages/user/integration-tests/utils/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from "./config" -export * from "./database" diff --git a/packages/user/jest.config.js b/packages/user/jest.config.js index 456054fe8a..58c887c1c3 100644 --- a/packages/user/jest.config.js +++ b/packages/user/jest.config.js @@ -17,6 +17,4 @@ module.exports = { testEnvironment: `node`, moduleFileExtensions: [`js`, `ts`], modulePathIgnorePatterns: ["dist/"], - setupFiles: ["/integration-tests/setup-env.js"], - setupFilesAfterEnv: ["/integration-tests/setup.js"], }