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"], }