From cdc7c6ccc25353fe8ef95ceae16adee8a24fb637 Mon Sep 17 00:00:00 2001 From: Danny Lake Date: Fri, 22 Sep 2023 16:09:45 -0700 Subject: [PATCH 01/10] feat: more tests and some config --- talonone-service/jest.config.cjs | 8 +- talonone-service/package.json | 11 +- .../src/handlers/effects/order/index.test.ts | 22 ++++ .../src/handlers/event/cart-events.test.ts | 112 ++++++++++++++++++ 4 files changed, 144 insertions(+), 9 deletions(-) create mode 100644 talonone-service/src/handlers/effects/order/index.test.ts create mode 100644 talonone-service/src/handlers/event/cart-events.test.ts diff --git a/talonone-service/jest.config.cjs b/talonone-service/jest.config.cjs index 1cfc24f..9e82740 100644 --- a/talonone-service/jest.config.cjs +++ b/talonone-service/jest.config.cjs @@ -4,4 +4,10 @@ module.exports = { testMatch: ['**/tests/**/*.[jt]s?(x)', '**/?(*.)+(spec|test).[tj]s?(x)'], preset: 'ts-jest', testEnvironment: 'node', -}; \ No newline at end of file + collectCoverageFrom: [ + 'src/**/*.{ts,jxs}', + '!**/node_modules/**', + '!src/services/commercetools/validators/**', + '!src/utils/**' + ] +} diff --git a/talonone-service/package.json b/talonone-service/package.json index 68e581a..27846bc 100644 --- a/talonone-service/package.json +++ b/talonone-service/package.json @@ -15,7 +15,7 @@ "create-ct-types": "ts-node src/scripts/create-ct-types.ts", "test": "jest --config jest.config.cjs", "test:local": "NODE_ENV=local jest --ci -i --setupFiles dotenv/config", - "test:coverage": "jest --config jest.config.cjs --coverage --collectCoverageFrom='src/**/*.{ts,jxs}'", + "test:coverage": "jest --config jest.config.cjs --coverage", "connector:post-deploy": "node build/connector/post-deploy.js", "connector:pre-undeploy": "node build/connector/pre-undeploy.js" }, @@ -40,14 +40,9 @@ "zod": "^3.22.2" }, "nodemonConfig": { - "watch": [ - ".env", - "src" - ], + "watch": [".env", "src"], "ext": "ts", - "ignore": [ - "src/**/*.test.ts" - ], + "ignore": ["src/**/*.test.ts"], "exec": "DEBUG=App:* npx ts-node -r dotenv/config ./src/index" }, "devDependencies": { diff --git a/talonone-service/src/handlers/effects/order/index.test.ts b/talonone-service/src/handlers/effects/order/index.test.ts new file mode 100644 index 0000000..c721ce4 --- /dev/null +++ b/talonone-service/src/handlers/effects/order/index.test.ts @@ -0,0 +1,22 @@ +import addLoyaltyPointsHandler from './addLoyaltyPoints' +import rollbackAddedLoyaltyPointsHandler from './rollbackAddedLoyaltyPoints' +import getOrderEffectHandlers from './index' + +jest.mock('./addLoyaltyPoints', () => jest.fn()) +jest.mock('./rollbackAddedLoyaltyPoints', () => jest.fn()) + +describe('getOrderEffectHandlers', () => { + afterEach(() => { + jest.resetAllMocks() + jest.restoreAllMocks() + }) + + it('should return an object with the correct effect handlers', () => { + const handlers = getOrderEffectHandlers() + + expect(handlers).toEqual({ + addLoyaltyPoints: addLoyaltyPointsHandler, + rollbackAddedLoyaltyPoints: rollbackAddedLoyaltyPointsHandler + }) + }) +}) diff --git a/talonone-service/src/handlers/event/cart-events.test.ts b/talonone-service/src/handlers/event/cart-events.test.ts new file mode 100644 index 0000000..fa4d27f --- /dev/null +++ b/talonone-service/src/handlers/event/cart-events.test.ts @@ -0,0 +1,112 @@ +import { cartEventsHandler } from './cart-events' +import { CartReference } from '@commercetools/platform-sdk' + +import { getDiscountApplied } from '../../handlers/effects/cart/setDiscount' +import getCartEffectHandlers from '../effects/cart/index' +import { getCartActions } from '../actions' + +jest.mock('../../handlers/effects/cart/setDiscount') +jest.mock('../effects/cart/index') +jest.mock('../../services/utils/logger') +jest.mock('../actions') + +describe('cartEventsHandler', () => { + let mockTalonOneUtils: any + let mockResource: CartReference + let mockUpdateCustomerSession: typeof jest.fn + + afterEach(() => { + jest.resetAllMocks() + jest.restoreAllMocks() + }) + + beforeEach(() => { + // Reset mocks + jest.clearAllMocks() + jest.resetAllMocks() + jest.restoreAllMocks() + ;(getDiscountApplied as jest.Mock).mockReturnValue(null) + ;(getCartEffectHandlers as jest.Mock).mockReturnValue({}) + ;(getCartActions as jest.Mock).mockReturnValue([]) + + // Mock implementations + mockUpdateCustomerSession = jest.fn().mockReturnValue({ + effects: [] + }) + mockTalonOneUtils = { + updateCustomerSession: mockUpdateCustomerSession + } + + mockResource = { + obj: { + id: 'someCartId', + customerId: 'customerId123', + anonymousId: 'anonymousId123', + lineItems: [ + { + name: { 'en-US': 'productName' }, + variant: { id: 123, sku: 'sku123' }, + quantity: 1, + price: { value: { centAmount: 100 } } + } + ], + totalPrice: { currencyCode: 'USD' } + }, + id: 'cartId123' + } as unknown as CartReference + }) + + it('should return empty array if no cart provided', async () => { + const actions = await cartEventsHandler(mockTalonOneUtils, { + ...mockResource, + obj: undefined + }) + expect(actions).toEqual([]) + expect(mockUpdateCustomerSession).not.toHaveBeenCalled() + }) + + it('should handle no effects and existing discount', async () => { + mockTalonOneUtils.updateCustomerSession.mockResolvedValue({ effects: [] }) + ;(getDiscountApplied as jest.Mock).mockReturnValue({ id: 'discountId123' }) + + const actions = await cartEventsHandler(mockTalonOneUtils, mockResource) + expect(actions).toEqual([ + { action: 'removeCustomLineItem', customLineItemId: 'discountId123' } + ]) + }) + + it('should handle no effects and no discount', async () => { + mockTalonOneUtils.updateCustomerSession.mockResolvedValue({ effects: [] }) + ;(getDiscountApplied as jest.Mock).mockReturnValue(null) + + const actions = await cartEventsHandler(mockTalonOneUtils, mockResource) + expect(actions).toEqual([]) + }) + + it('should handle existing effects and no discount', async () => { + mockTalonOneUtils.updateCustomerSession.mockResolvedValue({ + effects: ['effect1'] + }) + + const actions = await cartEventsHandler(mockTalonOneUtils, mockResource) + + expect(actions).toEqual([]) + expect(getCartEffectHandlers).toHaveBeenCalled() + expect(getCartActions).toHaveBeenCalledWith( + mockResource.obj, + ['effect1'], + {} + ) + }) + + it('should return empty array if an error occurs', async () => { + mockTalonOneUtils.updateCustomerSession.mockRejectedValue( + new Error('error') + ) + const actions = await cartEventsHandler(mockTalonOneUtils, mockResource) + + expect(actions).toEqual([]) + }) + + // TODO: Add more test cases for effects handling logic and error scenarios. +}) From 2493e753d8b79702954f418828455949027f858c Mon Sep 17 00:00:00 2001 From: federicodelpiano Date: Sat, 23 Sep 2023 15:49:15 -0300 Subject: [PATCH 02/10] testing server class --- talonone-service/jest.config.cjs | 1 + talonone-service/jest.setup.js | 9 +++++ .../src/handlers/effects/cart/index.test.ts | 11 ++++-- .../src/handlers/effects/order/index.test.ts | 27 ++++++++++++++ talonone-service/src/network/index.test.ts | 37 +++++++++++++++++++ 5 files changed, 82 insertions(+), 3 deletions(-) create mode 100644 talonone-service/jest.setup.js create mode 100644 talonone-service/src/handlers/effects/order/index.test.ts create mode 100644 talonone-service/src/network/index.test.ts diff --git a/talonone-service/jest.config.cjs b/talonone-service/jest.config.cjs index 1cfc24f..5eec965 100644 --- a/talonone-service/jest.config.cjs +++ b/talonone-service/jest.config.cjs @@ -4,4 +4,5 @@ module.exports = { testMatch: ['**/tests/**/*.[jt]s?(x)', '**/?(*.)+(spec|test).[tj]s?(x)'], preset: 'ts-jest', testEnvironment: 'node', + setupFiles: ["./jest.setup.js"], }; \ No newline at end of file diff --git a/talonone-service/jest.setup.js b/talonone-service/jest.setup.js new file mode 100644 index 0000000..4e05553 --- /dev/null +++ b/talonone-service/jest.setup.js @@ -0,0 +1,9 @@ +const mockEnvironmentVariables = () => { + process.env.CTP_CLIENT_ID = 'xxxxxxxxxxxxxxxxxxxxxxxx'; + process.env.CTP_CLIENT_SECRET = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'; + process.env.CTP_PROJECT_KEY = 'xxxxxxxxxxx'; + process.env.CTP_SCOPE = 'xxxxxxxxxxx'; + process.env.CTP_REGION = 'us-central1.gcp'; +}; + +mockEnvironmentVariables(); diff --git a/talonone-service/src/handlers/effects/cart/index.test.ts b/talonone-service/src/handlers/effects/cart/index.test.ts index 2e339dc..a8363f3 100644 --- a/talonone-service/src/handlers/effects/cart/index.test.ts +++ b/talonone-service/src/handlers/effects/cart/index.test.ts @@ -1,5 +1,9 @@ +import { describe, expect, test, jest } from '@jest/globals'; import { EffectHandlers } from '../types' import getCartEffectHandlers from './index' +import getDiscountApplied from './setDiscount' + +jest.mock('./setDiscount') describe('getCartEffectHandlers', () => { let currencyCode: string @@ -16,13 +20,14 @@ describe('getCartEffectHandlers', () => { }) it('should return an object with the setDiscount effect handler', () => { + (getDiscountApplied as jest.Mock).mockReturnValue(null) + const effectHandlers: EffectHandlers = getCartEffectHandlers( currencyCode, taxCategoryId ) - expect(effectHandlers).toEqual({ - setDiscount: expect.any(Function) - }) + expect(effectHandlers).toHaveProperty('setDiscount') + expect(effectHandlers.setDiscount({})).toBeNull() }) }) diff --git a/talonone-service/src/handlers/effects/order/index.test.ts b/talonone-service/src/handlers/effects/order/index.test.ts new file mode 100644 index 0000000..0d44266 --- /dev/null +++ b/talonone-service/src/handlers/effects/order/index.test.ts @@ -0,0 +1,27 @@ +import { describe, expect, jest } from '@jest/globals'; +import { EffectHandlers } from '../types'; +import addLoyaltyPointsHandler from './addLoyaltyPoints'; +import getOrderEffectHandlers from './index'; +import rollbackAddedLoyaltyPointsHandler from './rollbackAddedLoyaltyPoints'; + +jest.mock('./addLoyaltyPoints') +jest.mock('./rollbackAddedLoyaltyPoints') + +describe('getOrderEffectHandlers', () => { + afterEach(() => { + jest.restoreAllMocks() + jest.resetAllMocks() + }) + + it('should return an object with the addLoyaltyPoints and rollbackAddedLoyaltyPoints effect handlers', () => { + (addLoyaltyPointsHandler as jest.Mock).mockReturnValue(null); + (rollbackAddedLoyaltyPointsHandler as jest.Mock).mockReturnValue(null); + + const effectHandlers: EffectHandlers = getOrderEffectHandlers() + + expect(effectHandlers).toHaveProperty('addLoyaltyPoints') + expect(effectHandlers).toHaveProperty('rollbackAddedLoyaltyPoints') + expect(effectHandlers.addLoyaltyPoints({})).toBeNull() + expect(effectHandlers.rollbackAddedLoyaltyPoints({})).toBeNull() + }) +}) diff --git a/talonone-service/src/network/index.test.ts b/talonone-service/src/network/index.test.ts new file mode 100644 index 0000000..7860856 --- /dev/null +++ b/talonone-service/src/network/index.test.ts @@ -0,0 +1,37 @@ +import { afterEach, describe, expect, it, jest } from '@jest/globals'; +import { Server } from './server'; + +jest.mock('express', () => { + const mockedExpress = () => { + return { + use: jest.fn(), + listen: jest.fn(), + } + }; + Object.defineProperty(mockedExpress, "json", { value: jest.fn() }); + Object.defineProperty(mockedExpress, "urlencoded", { value: jest.fn() }); + Object.defineProperty(mockedExpress, "Router", { value: jest.fn().mockReturnValue({route: jest.fn().mockReturnValue({post: jest.fn()})}) }); + return mockedExpress; +}) + +jest.mock('../services/commercetools/client/create.client', () => { + return { + getProject: jest.fn() + } +}) + +describe('Server', () => { + afterEach(() => { + jest.resetAllMocks() + jest.restoreAllMocks() + jest.resetModules() + }) + + it('run start function', async () => { + expect(Server.start()).resolves.not.toThrowError() + }) + + it('run stop function', async () => { + expect(Server.stop()).resolves.not.toThrowError() + }) +}) From a874cc36f3a688a62030b113fa292202f37a6948 Mon Sep 17 00:00:00 2001 From: federicodelpiano Date: Mon, 25 Sep 2023 11:46:19 -0300 Subject: [PATCH 03/10] pre and post scripts tests --- .../src/connector/post-deploy.test.ts | 36 ++++++++++++++++++ talonone-service/src/connector/post-deploy.ts | 2 +- .../src/connector/pre-undeploy.test.ts | 38 +++++++++++++++++++ .../src/connector/pre-undeploy.ts | 2 +- 4 files changed, 76 insertions(+), 2 deletions(-) create mode 100644 talonone-service/src/connector/post-deploy.test.ts create mode 100644 talonone-service/src/connector/pre-undeploy.test.ts diff --git a/talonone-service/src/connector/post-deploy.test.ts b/talonone-service/src/connector/post-deploy.test.ts new file mode 100644 index 0000000..22da159 --- /dev/null +++ b/talonone-service/src/connector/post-deploy.test.ts @@ -0,0 +1,36 @@ +import { afterEach, describe, expect, it, jest } from '@jest/globals'; +import { createType, createMyExtension } from './actions' +import { run } from './post-deploy' + +jest.mock('./actions') + +describe('Post-deploy', () => { + afterEach(() => { + jest.resetAllMocks() + jest.restoreAllMocks() + jest.resetModules() + }) + + it('should run start function', async () => { + (createMyExtension as jest.Mock).mockReturnValue({ + data: 'success', + }); + + await run() + expect(createMyExtension).toHaveBeenCalled() + expect(createType).toHaveBeenCalled() + }) + + it('should handle errors', async () => { + const errorMessage = 'Something went wrong'; + (createMyExtension as jest.Mock).mockImplementation(() => { + throw new Error(errorMessage); + }); + + const stderrSpy = jest.spyOn(process.stderr, 'write'); + await run(); + + expect(stderrSpy).toHaveBeenCalledWith(`Post-deploy failed: Error: ${errorMessage}`); + expect(process.exitCode).toBe(1); + }); +}) \ No newline at end of file diff --git a/talonone-service/src/connector/post-deploy.ts b/talonone-service/src/connector/post-deploy.ts index 4f20f87..e0926cc 100644 --- a/talonone-service/src/connector/post-deploy.ts +++ b/talonone-service/src/connector/post-deploy.ts @@ -22,7 +22,7 @@ async function postDeploy(properties: Map): Promise { await createType(apiRoot, lineItemMetadataType.key, lineItemMetadataType) } -async function run(): Promise { +export async function run(): Promise { try { const properties = new Map(Object.entries(process.env)) await postDeploy(properties) diff --git a/talonone-service/src/connector/pre-undeploy.test.ts b/talonone-service/src/connector/pre-undeploy.test.ts new file mode 100644 index 0000000..4d69def --- /dev/null +++ b/talonone-service/src/connector/pre-undeploy.test.ts @@ -0,0 +1,38 @@ +import { afterEach, describe, expect, it, jest } from '@jest/globals'; +import { deleteMyExtension } from './actions'; +import { run } from './pre-undeploy'; +import { createApiRoot } from '../services/commercetools/client/create.client' + +jest.mock('./actions') +jest.mock('../services/commercetools/client/create.client') + +describe('Pre-undeploy', () => { + afterEach(() => { + jest.resetAllMocks() + jest.restoreAllMocks() + jest.resetModules() + }) + + it('should run start function', async () => { + (deleteMyExtension as jest.Mock).mockReturnValue({ + data: 'success', + }); + + await run() + expect(createApiRoot).toHaveBeenCalled() + expect(deleteMyExtension).toHaveBeenCalled() + }) + + it('should handle errors', async () => { + const errorMessage = 'Something went wrong'; + (deleteMyExtension as jest.Mock).mockImplementation(() => { + throw new Error(errorMessage); + }); + + const stderrSpy = jest.spyOn(process.stderr, 'write'); + await run(); + + expect(stderrSpy).toHaveBeenCalledWith(`Pre-undeploy failed: Error: ${errorMessage}`); + expect(process.exitCode).toBe(1); + }); +}) \ No newline at end of file diff --git a/talonone-service/src/connector/pre-undeploy.ts b/talonone-service/src/connector/pre-undeploy.ts index c68c8ce..956d64b 100644 --- a/talonone-service/src/connector/pre-undeploy.ts +++ b/talonone-service/src/connector/pre-undeploy.ts @@ -9,7 +9,7 @@ async function preUndeploy(): Promise { await deleteMyExtension(apiRoot) } -async function run(): Promise { +export async function run(): Promise { try { await preUndeploy() } catch (error) { From 73b1fc7f97d8de584401b6b16fe6f1f34e3b4a3c Mon Sep 17 00:00:00 2001 From: federicodelpiano Date: Mon, 25 Sep 2023 12:37:44 -0300 Subject: [PATCH 04/10] delete unused files --- talonone-service/src/schemas/id.ts | 11 ----------- talonone-service/src/schemas/index.ts | 1 - 2 files changed, 12 deletions(-) delete mode 100644 talonone-service/src/schemas/id.ts delete mode 100644 talonone-service/src/schemas/index.ts diff --git a/talonone-service/src/schemas/id.ts b/talonone-service/src/schemas/id.ts deleted file mode 100644 index e534898..0000000 --- a/talonone-service/src/schemas/id.ts +++ /dev/null @@ -1,11 +0,0 @@ -import z from 'zod' - -const id = z.string() - -type Id = z.infer - -const idSchema = z.object({ id }) - -type IdSchema = z.infer - -export { id, Id, idSchema, IdSchema } diff --git a/talonone-service/src/schemas/index.ts b/talonone-service/src/schemas/index.ts deleted file mode 100644 index 849b1d9..0000000 --- a/talonone-service/src/schemas/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './id' From 870e9b623b8b42bc46f6ceb14225a00197555bfc Mon Sep 17 00:00:00 2001 From: federicodelpiano Date: Mon, 25 Sep 2023 12:37:51 -0300 Subject: [PATCH 05/10] ignore test files --- talonone-service/tsconfig.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/talonone-service/tsconfig.json b/talonone-service/tsconfig.json index fb87b83..2f02c20 100644 --- a/talonone-service/tsconfig.json +++ b/talonone-service/tsconfig.json @@ -1,6 +1,6 @@ { "extends": "@tsconfig/recommended/tsconfig.json", - "exclude": ["node_modules", "**/*.spec.ts"], + "exclude": ["node_modules", "**/*.spec.ts", "**/*.test.ts"], "compilerOptions": { "resolveJsonModule": true, "outDir": "./build", From abbe627406d51cfb1176bc02f7d6f9ea21b8a916 Mon Sep 17 00:00:00 2001 From: Danny Lake Date: Mon, 25 Sep 2023 10:01:05 -0700 Subject: [PATCH 06/10] feat: more tests --- talonone-service/jest.config.cjs | 8 +- talonone-service/package.json | 2 +- .../src/handlers/event/order-events.test.ts | 135 ++++++++++++++++++ .../src/handlers/event/order-events.ts | 4 +- 4 files changed, 139 insertions(+), 10 deletions(-) create mode 100644 talonone-service/src/handlers/event/order-events.test.ts diff --git a/talonone-service/jest.config.cjs b/talonone-service/jest.config.cjs index a8247b3..c59db77 100644 --- a/talonone-service/jest.config.cjs +++ b/talonone-service/jest.config.cjs @@ -4,11 +4,5 @@ module.exports = { testMatch: ['**/tests/**/*.[jt]s?(x)', '**/?(*.)+(spec|test).[tj]s?(x)'], preset: 'ts-jest', testEnvironment: 'node', - collectCoverageFrom: [ - 'src/**/*.{ts,jxs}', - '!**/node_modules/**', - '!src/services/commercetools/validators/**', - '!src/utils/**' - ], - setupFiles: ["./jest.setup.js"], + setupFiles: ['/jest.setup.js'] } diff --git a/talonone-service/package.json b/talonone-service/package.json index 27846bc..e38038a 100644 --- a/talonone-service/package.json +++ b/talonone-service/package.json @@ -15,7 +15,7 @@ "create-ct-types": "ts-node src/scripts/create-ct-types.ts", "test": "jest --config jest.config.cjs", "test:local": "NODE_ENV=local jest --ci -i --setupFiles dotenv/config", - "test:coverage": "jest --config jest.config.cjs --coverage", + "test:coverage": "jest --config jest.config.cjs --coverage --collectCoverageFrom='src/**/*.{ts,jsx}'", "connector:post-deploy": "node build/connector/post-deploy.js", "connector:pre-undeploy": "node build/connector/pre-undeploy.js" }, diff --git a/talonone-service/src/handlers/event/order-events.test.ts b/talonone-service/src/handlers/event/order-events.test.ts new file mode 100644 index 0000000..3680a23 --- /dev/null +++ b/talonone-service/src/handlers/event/order-events.test.ts @@ -0,0 +1,135 @@ +import { TalonOneUtils } from '../../services/TalonOne' +import { OrderReference } from '@commercetools/platform-sdk' + +import { OrderState, getState, orderEventsHandler } from './order-events' +import { NewCustomerSessionV2 } from 'talon_one' +import { logger } from '../../services/utils/logger' + +import { getOrderActions } from '../actions' + +describe('getState', () => { + afterEach(() => { + jest.resetAllMocks() + jest.restoreAllMocks() + }) + + beforeEach(() => { + jest.mock('../actions', () => jest.fn()) + jest.mock('../../services/utils/logger') + jest.mock('../../handlers/effects/order', () => + jest.fn().mockReturnValue({}) + ) + }) + + it('should return NewCustomerSessionV2.StateEnum.closed for OrderState.Open', () => { + const orderState = OrderState.Open + const result = getState(orderState) + + expect(result).toEqual(NewCustomerSessionV2.StateEnum.closed) + }) + it('should return NewCustomerSessionV2.StateEnum.closed for OrderState.Complete', () => { + const orderState = OrderState.Complete + const result = getState(orderState) + + expect(result).toEqual(NewCustomerSessionV2.StateEnum.closed) + }) + + it('should return NewCustomerSessionV2.StateEnum.closed for OrderState.Confirmed', () => { + const orderState = OrderState.Confirmed + const result = getState(orderState) + + expect(result).toEqual(NewCustomerSessionV2.StateEnum.closed) + }) + it('should return NewCustomerSessionV2.StateEnum.cancelled for OrderState.Cancelled', () => { + const orderState = OrderState.Cancelled + const result = getState(orderState) + + expect(result).toEqual(NewCustomerSessionV2.StateEnum.cancelled) + }) + it('should return NewCustomerSessionV2.StateEnum.open as a default', () => { + const orderState = 'someOtherState' + const result = getState(orderState) + + expect(result).toEqual(NewCustomerSessionV2.StateEnum.open) + }) +}) + +jest.mock('../actions') + +describe('orderEventsHandler', () => { + let mockTalonOneUtils: jest.Mocked + let mockResource: OrderReference + let warnSpy: jest.SpyInstance + + afterEach(() => { + jest.resetAllMocks() + jest.restoreAllMocks() + }) + + beforeEach(() => { + jest.resetAllMocks() + jest.restoreAllMocks() + + jest.mock('../actions') + jest.mock('../../services/utils/logger') + jest.mock('../../handlers/effects/order', () => + jest.fn().mockReturnValue({}) + ) + + warnSpy = jest.spyOn(logger, 'warn').mockImplementation(jest.fn()) + jest.mock('../../handlers/effects/order', () => jest.fn()) + + mockTalonOneUtils = { + updateCustomerSession: jest.fn().mockResolvedValue(['someEffect']) + } as unknown as jest.Mocked + + mockResource = { + obj: { + id: 'someOrderId', + orderState: 'Open', + cart: { + id: 'someCartId' + } + } + } as OrderReference + }) + + it('should return empty array if no order provided', async () => { + ;(getOrderActions as jest.Mock).mockResolvedValue(undefined) + + const actions = await orderEventsHandler(mockTalonOneUtils, { + ...mockResource, + obj: undefined + }) + expect(actions).toEqual([]) + }) + + it('should handle order with cart and state Open', async () => { + ;(getOrderActions as jest.Mock).mockResolvedValue(undefined) + + const actions = await orderEventsHandler(mockTalonOneUtils, mockResource) + expect(mockTalonOneUtils.updateCustomerSession).toHaveBeenCalledWith( + 'someCartId', + { state: 'closed' } + ) + + expect(actions).toEqual([]) + }) + + it('should return array of actions', async () => { + ;(getOrderActions as jest.Mock).mockResolvedValue(['someAction']) + const actions = await orderEventsHandler(mockTalonOneUtils, mockResource) + + expect(actions).toEqual(['someAction']) + }) + + it('should log warning on error', async () => { + mockTalonOneUtils.updateCustomerSession.mockRejectedValue( + new Error('Test error') + ) + await orderEventsHandler(mockTalonOneUtils, mockResource) + expect(warnSpy).toHaveBeenCalledWith( + JSON.stringify(new Error('Test error')) + ) + }) +}) diff --git a/talonone-service/src/handlers/event/order-events.ts b/talonone-service/src/handlers/event/order-events.ts index 568d855..139e37b 100644 --- a/talonone-service/src/handlers/event/order-events.ts +++ b/talonone-service/src/handlers/event/order-events.ts @@ -6,14 +6,14 @@ import getOrderEffectHandlers from '../../handlers/effects/order' import { getOrderActions } from '../actions' import { logger } from '../../services/utils/logger' -enum OrderState { +export enum OrderState { Open = 'Open', Confirmed = 'Confirmed', Complete = 'Complete', Cancelled = 'Cancelled' } -const getState = (orderState: string) => { +export const getState = (orderState: string) => { switch (orderState) { case OrderState.Open: case OrderState.Complete: From feae145e7bf22895999790952fde50fe14f2cc18 Mon Sep 17 00:00:00 2001 From: federicodelpiano Date: Mon, 25 Sep 2023 14:22:31 -0300 Subject: [PATCH 07/10] delete unused files --- .../src/@types/custom/params.d.ts | 3 - talonone-service/src/@types/index.d.ts | 4 - .../src/network/routes/utils/index.ts | 22 ------ talonone-service/src/services/BaseHttp.ts | 77 ------------------- 4 files changed, 106 deletions(-) delete mode 100644 talonone-service/src/@types/custom/params.d.ts delete mode 100644 talonone-service/src/@types/index.d.ts delete mode 100644 talonone-service/src/network/routes/utils/index.ts delete mode 100644 talonone-service/src/services/BaseHttp.ts diff --git a/talonone-service/src/@types/custom/params.d.ts b/talonone-service/src/@types/custom/params.d.ts deleted file mode 100644 index 46a36f6..0000000 --- a/talonone-service/src/@types/custom/params.d.ts +++ /dev/null @@ -1,3 +0,0 @@ -type Params = { - [key: string]: string -} diff --git a/talonone-service/src/@types/index.d.ts b/talonone-service/src/@types/index.d.ts deleted file mode 100644 index 0a18826..0000000 --- a/talonone-service/src/@types/index.d.ts +++ /dev/null @@ -1,4 +0,0 @@ -/* eslint-disable no-var */ -declare global {} - -export {} diff --git a/talonone-service/src/network/routes/utils/index.ts b/talonone-service/src/network/routes/utils/index.ts deleted file mode 100644 index f9019c9..0000000 --- a/talonone-service/src/network/routes/utils/index.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { type NextFunction, type Request, type Response } from 'express' -import httpErrors from 'http-errors' -import { ZodType } from 'zod' - -type Middleware = (req: Request, res: Response, next: NextFunction) => void - -const validatorCompiler = ( - schema: ZodType, - value: 'body' | 'params' -): Middleware => { - return (req: Request, res: Response, next: NextFunction) => { - const result = schema.safeParse(req[value]) - - if (result.success) return next() - - return next( - new httpErrors.UnprocessableEntity(JSON.stringify(result.error)) - ) - } -} - -export { validatorCompiler } diff --git a/talonone-service/src/services/BaseHttp.ts b/talonone-service/src/services/BaseHttp.ts deleted file mode 100644 index 37d3096..0000000 --- a/talonone-service/src/services/BaseHttp.ts +++ /dev/null @@ -1,77 +0,0 @@ -import { Debugger } from 'debug' -import httpErrors, { - HttpErrorConstructor, - NamedConstructors -} from 'http-errors' - -import { Log } from '../utils' -import { GE } from './utils' - -type FilterNumberKeys = { - [K in keyof T]: T[K] extends HttpErrorConstructor - ? N extends number - ? K - : never - : never -}[keyof T] - -type ErrorCodes = FilterNumberKeys - -class BaseHttpService implements Log { - #debug: Debugger - - constructor(debug: Debugger) { - this.#debug = debug - } - - log({ - method, - value, - content - }: { - method: string - value: string - content: unknown - }) { - this.#debug( - `Service invoked -> ${ - this.constructor.name - } ~ ${method} ~ value: ${value} ~ content: ${JSON.stringify(content)}` - ) - } - - errorHandling( - error: unknown, - { - message, - code - }: - | { - message?: string - code?: never - } - | { - message?: never - code: ErrorCodes - } = { - message: GE.INTERNAL_SERVER_ERROR - } - ): never { - this.log({ - method: this.errorHandling.name, - value: 'error', - content: error - }) - - if (code) - throw new httpErrors[code]( - message ?? (error as { message: string }).message - ) - - throw new httpErrors.InternalServerError( - message ?? (error as { message: string }).message - ) - } -} - -export { BaseHttpService } From 3238c31b29b3dbadd0cd34ae9907e88f24ac6502 Mon Sep 17 00:00:00 2001 From: Danny Lake Date: Mon, 25 Sep 2023 10:24:05 -0700 Subject: [PATCH 08/10] feat: more tests --- .../src/handlers/event/index.test.ts | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 talonone-service/src/handlers/event/index.test.ts diff --git a/talonone-service/src/handlers/event/index.test.ts b/talonone-service/src/handlers/event/index.test.ts new file mode 100644 index 0000000..7cd3d91 --- /dev/null +++ b/talonone-service/src/handlers/event/index.test.ts @@ -0,0 +1,51 @@ +import { getEventHandler } from './index' +import { cartEventsHandler } from './cart-events' +import { Request } from 'express' + +jest.mock('./cart-events') +jest.mock('./order-events') + +describe('getEventHandler', () => { + afterEach(() => { + jest.resetAllMocks() + jest.restoreAllMocks() + }) + + beforeEach(() => { + jest.resetAllMocks() + jest.restoreAllMocks() + ;(cartEventsHandler as jest.Mock).mockReturnValue('cartHandler') + }) + + it('should return a handler function', () => { + const req = { + body: { + resource: { + typeId: 'CART' + } + }, + talonOneUtils: {} + } as unknown as Request + + const result = getEventHandler(req) + + expect(result).toBeInstanceOf(Function) + const resultValue = (result as any)() + expect(resultValue).toEqual('cartHandler') + }) + it('should return an actions object with an empty array', () => { + const req = { + body: { + resource: { + typeId: 'invalidId' + } + }, + talonOneUtils: {} + } as unknown as Request + + const result = getEventHandler(req) + const resultValue = (result as any)() + + expect(resultValue).toEqual({ actions: [] }) + }) +}) From 0a9743093ba78ab135ff6e3048249ca12f74f085 Mon Sep 17 00:00:00 2001 From: Danny Lake Date: Mon, 25 Sep 2023 10:38:26 -0700 Subject: [PATCH 09/10] feat: more tests --- talonone-service/.prettierrc | 1 + .../src/handlers/actions/cart-actions.test.ts | 45 +++++++++++++++++++ .../src/handlers/event/cart-events.test.ts | 6 --- 3 files changed, 46 insertions(+), 6 deletions(-) create mode 100644 talonone-service/src/handlers/actions/cart-actions.test.ts diff --git a/talonone-service/.prettierrc b/talonone-service/.prettierrc index 346d0d2..dc1327e 100644 --- a/talonone-service/.prettierrc +++ b/talonone-service/.prettierrc @@ -6,6 +6,7 @@ "singleQuote": true, "parser": "typescript", "printWidth": 80, + "arrowParens": "avoid", "overrides": [ { "files": "*.json", diff --git a/talonone-service/src/handlers/actions/cart-actions.test.ts b/talonone-service/src/handlers/actions/cart-actions.test.ts new file mode 100644 index 0000000..7b7cb33 --- /dev/null +++ b/talonone-service/src/handlers/actions/cart-actions.test.ts @@ -0,0 +1,45 @@ +import { Effect } from 'talon_one' +import { Cart } from '@commercetools/platform-sdk' +import { EffectHandlers } from '../effects/types' +import { getCartActions } from './cart-actions' + +describe('getCartActions', () => { + beforeEach(() => { + jest.resetAllMocks() + jest.restoreAllMocks() + }) + it('should return empty array if no effects are given', () => { + const mockCart = {} as unknown as Cart + const mockEffects = [] as unknown as Effect[] + const mockHandlers = { + someEffectType: jest.fn().mockReturnValue('someActionResult') + } + + const result = getCartActions(mockCart, mockEffects, mockHandlers) + expect(result).toEqual([]) + }) + + it('should return the correct cart actions based on given effects and handlers', () => { + const mockCart = {} as unknown as Cart + + const mockEffects = [ + { effectType: 'someEffectType', props: { key: 'value' } }, + { effectType: 'unhandledEffectType', props: { key2: 'value2' } } + ] as unknown as Effect[] + + const mockHandlers = { + someEffectType: jest.fn().mockReturnValue('someActionResult') + } + + const result = getCartActions(mockCart, mockEffects, mockHandlers) + + // Assert that the handler for 'someEffectType' was called with correct properties + expect(mockHandlers.someEffectType).toHaveBeenCalledWith({ + key: 'value', + cart: mockCart + }) + + // Assert the expected result + expect(result).toEqual(['someActionResult']) + }) +}) diff --git a/talonone-service/src/handlers/event/cart-events.test.ts b/talonone-service/src/handlers/event/cart-events.test.ts index fa4d27f..7c27188 100644 --- a/talonone-service/src/handlers/event/cart-events.test.ts +++ b/talonone-service/src/handlers/event/cart-events.test.ts @@ -15,14 +15,8 @@ describe('cartEventsHandler', () => { let mockResource: CartReference let mockUpdateCustomerSession: typeof jest.fn - afterEach(() => { - jest.resetAllMocks() - jest.restoreAllMocks() - }) - beforeEach(() => { // Reset mocks - jest.clearAllMocks() jest.resetAllMocks() jest.restoreAllMocks() ;(getDiscountApplied as jest.Mock).mockReturnValue(null) From 30462b1a6559fdd4dd52ffe71616f6ff81febfcc Mon Sep 17 00:00:00 2001 From: Danny Lake Date: Mon, 25 Sep 2023 10:46:54 -0700 Subject: [PATCH 10/10] feat: more tests --- .../handlers/actions/order-actions.test.ts | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 talonone-service/src/handlers/actions/order-actions.test.ts diff --git a/talonone-service/src/handlers/actions/order-actions.test.ts b/talonone-service/src/handlers/actions/order-actions.test.ts new file mode 100644 index 0000000..01acaae --- /dev/null +++ b/talonone-service/src/handlers/actions/order-actions.test.ts @@ -0,0 +1,57 @@ +import { Effect } from 'talon_one' +import { Order } from '@commercetools/platform-sdk' +import { getOrderActions } from './order-actions' + +describe('getCartActions', () => { + beforeEach(() => { + jest.resetAllMocks() + jest.restoreAllMocks() + }) + + it('should return an empty array if no effects are provided', async () => { + const mockOrder = { + anonymousId: null + } as unknown as Order + + const result = await getOrderActions(mockOrder, [], {}) + expect(result).toEqual([]) + }) + + it('should return an empty array if order has an anonymousId', async () => { + const mockOrder = { + anonymousId: 'someId' + } as unknown as Order + + const mockEffects = [ + { effectType: 'someEffectType', props: { key: 'value' } } + ] as unknown as Effect[] + + const result = await getOrderActions(mockOrder, mockEffects, {}) + expect(result).toEqual([]) + }) + + it('should return the correct order actions based on given effects and handlers', async () => { + const mockOrder = { + anonymousId: null + } as unknown as Order + + const mockEffects = [ + { effectType: 'someEffectType', props: { key: 'value' } }, + { effectType: 'unhandledEffectType', props: { key2: 'value2' } } + ] as unknown as Effect[] + + const mockHandlerResult = 'someActionResult' + const mockHandlers = { + someEffectType: jest.fn().mockResolvedValue(mockHandlerResult) + } + + const result = await getOrderActions(mockOrder, mockEffects, mockHandlers) + + expect(mockHandlers.someEffectType).toHaveBeenCalledWith({ + key: 'value', + order: mockOrder + }) + + expect(result).toEqual([mockHandlerResult]) + }) +})