From 9704688c76c01369142cca1543704a274226108f Mon Sep 17 00:00:00 2001 From: gomarcopololead Date: Sat, 4 Jun 2022 00:04:13 +0800 Subject: [PATCH 1/3] feat: local provider scheduled command adapter unit test --- .../src/library/scheduled-adapter.ts | 2 +- .../test/helpers/scheduled-helper.ts | 11 ++++ .../test/library/scheduled-adapter.test.ts | 65 +++++++++++++++++++ 3 files changed, 77 insertions(+), 1 deletion(-) create mode 100644 packages/framework-provider-local/test/helpers/scheduled-helper.ts create mode 100644 packages/framework-provider-local/test/library/scheduled-adapter.test.ts diff --git a/packages/framework-provider-local/src/library/scheduled-adapter.ts b/packages/framework-provider-local/src/library/scheduled-adapter.ts index 11c12d5a6..93381d6bb 100644 --- a/packages/framework-provider-local/src/library/scheduled-adapter.ts +++ b/packages/framework-provider-local/src/library/scheduled-adapter.ts @@ -1,6 +1,6 @@ import { Logger, ScheduledCommandEnvelope, UUID } from '@boostercloud/framework-types' -interface LocalScheduleCommandEnvelope { +export interface LocalScheduleCommandEnvelope { typeName: string } diff --git a/packages/framework-provider-local/test/helpers/scheduled-helper.ts b/packages/framework-provider-local/test/helpers/scheduled-helper.ts new file mode 100644 index 000000000..585829a43 --- /dev/null +++ b/packages/framework-provider-local/test/helpers/scheduled-helper.ts @@ -0,0 +1,11 @@ +import { random } from 'faker' + +interface LocalScheduleCommandEnvelope { + typeName: string +} + +export function createMockLocalScheduleCommandEnvelope(): Partial { + return { + typeName: random.word() + } +} diff --git a/packages/framework-provider-local/test/library/scheduled-adapter.test.ts b/packages/framework-provider-local/test/library/scheduled-adapter.test.ts new file mode 100644 index 000000000..c4831975f --- /dev/null +++ b/packages/framework-provider-local/test/library/scheduled-adapter.test.ts @@ -0,0 +1,65 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { SinonStub, stub, replace, restore } from 'sinon' +import { rawScheduledInputToEnvelope, LocalScheduleCommandEnvelope } from '../../src/library/scheduled-adapter' +import { createMockLocalScheduleCommandEnvelope } from '../helpers/scheduled-helper' +import { UUID } from '@boostercloud/framework-types' +import { expect } from '../expect' +import { random } from 'faker' + +describe('Local provider scheduled-adapter', () => { + describe('rawScheduledInputToEnvelope', () => { + let mockScheduledEnvelop: Partial + let mockEmptyScheduledEnvelop: Partial + let mockUuid: string + + let debugStub: SinonStub + let generateStub: SinonStub + + let logger: any + + beforeEach(() => { + mockUuid = random.uuid() + + mockScheduledEnvelop = createMockLocalScheduleCommandEnvelope() + mockEmptyScheduledEnvelop = {} + + debugStub = stub() + generateStub = stub().returns(mockUuid) + + logger = { + debug: debugStub, + } + replace(UUID, 'generate', generateStub) + }) + + afterEach(() => { + restore() + }) + + it('should call logger.debug', async () => { + await rawScheduledInputToEnvelope(mockScheduledEnvelop, logger) + + expect(debugStub).to.have.been.calledOnceWith( + 'Received LocalScheduleCommand request: ', + mockScheduledEnvelop + ) + }) + + it('should thrown an exception for empty typeName', async () => { + const error = new Error( + `typeName is not defined or empty, scheduled command envelope should have the structure {typeName: string }, but you gave ${JSON.stringify( + mockEmptyScheduledEnvelop + )}` + ) + expect(rawScheduledInputToEnvelope(mockEmptyScheduledEnvelop, logger)).to.be.rejectedWith(error) + }) + + it('should generate expected envelop', async () => { + const result = await rawScheduledInputToEnvelope(mockScheduledEnvelop, logger) + expect(result).to.be.deep.equal({ + requestID: mockUuid, + typeName: mockScheduledEnvelop.typeName + }) + }) + }) +}) From 9e410c590369cbe36cf260345138d1a62da9bc3f Mon Sep 17 00:00:00 2001 From: gomarcopololead Date: Mon, 6 Jun 2022 15:24:04 +0800 Subject: [PATCH 2/3] feat: unit test for local provider infrastructure scheulder --- .../src/scheduler.ts | 30 +++++-- .../test/scheduler.test.ts | 84 +++++++++++++++++++ 2 files changed, 106 insertions(+), 8 deletions(-) create mode 100644 packages/framework-provider-local-infrastructure/test/scheduler.test.ts diff --git a/packages/framework-provider-local-infrastructure/src/scheduler.ts b/packages/framework-provider-local-infrastructure/src/scheduler.ts index fa9b37bb4..2335e653d 100644 --- a/packages/framework-provider-local-infrastructure/src/scheduler.ts +++ b/packages/framework-provider-local-infrastructure/src/scheduler.ts @@ -6,24 +6,38 @@ interface ScheduledCommandInfo { metadata: ScheduledCommandMetadata } -export function configureScheduler(config: BoosterConfig, userProject: any): void { +export function configureScheduler(config: BoosterConfig, userProject: any): scheduler.Job[] { const triggerScheduleCommand = userProject['boosterTriggerScheduledCommand'] + const cronJobs: scheduler.Job[] = [] Object.keys(config.scheduledCommandHandlers) .map((scheduledCommandName) => buildScheduledCommandInfo(config, scheduledCommandName)) .filter((scheduledCommandInfo) => scheduledCommandInfo.metadata.scheduledOn) .forEach((scheduledCommandInfo) => { - scheduler.scheduleJob(scheduledCommandInfo.name, createCronExpression(scheduledCommandInfo.metadata), () => { - triggerScheduleCommand({ typeName: scheduledCommandInfo.name }) - }) + const cronJob = scheduler.scheduleJob( + scheduledCommandInfo.name, + createCronExpression(scheduledCommandInfo.metadata), + () => { + triggerScheduleCommand({ typeName: scheduledCommandInfo.name }) + } + ) + cronJobs.push(cronJob) }) + return cronJobs } -function createCronExpression(scheduledCommandMetadata: ScheduledCommandMetadata): string { - const { minute = '*', hour = '*', day = '*', month = '*', weekDay = '*' } = scheduledCommandMetadata.scheduledOn - return `${minute} ${hour} ${day} ${month} ${weekDay}` +export function createCronExpression(scheduledCommandMetadata: ScheduledCommandMetadata): string { + const { + second = '*', + minute = '*', + hour = '*', + day = '*', + month = '*', + weekDay = '*', + } = scheduledCommandMetadata.scheduledOn + return `${second} ${minute} ${hour} ${day} ${month} ${weekDay}` } -function buildScheduledCommandInfo(config: BoosterConfig, scheduledCommandName: string): ScheduledCommandInfo { +export function buildScheduledCommandInfo(config: BoosterConfig, scheduledCommandName: string): ScheduledCommandInfo { return { name: scheduledCommandName, metadata: config.scheduledCommandHandlers[scheduledCommandName], diff --git a/packages/framework-provider-local-infrastructure/test/scheduler.test.ts b/packages/framework-provider-local-infrastructure/test/scheduler.test.ts new file mode 100644 index 000000000..8a95f6ae6 --- /dev/null +++ b/packages/framework-provider-local-infrastructure/test/scheduler.test.ts @@ -0,0 +1,84 @@ +import { BoosterConfig } from '@boostercloud/framework-types' +import { restore, SinonStub, SinonStubbedInstance, createStubInstance, replace, stub, useFakeTimers } from 'sinon' +import { configureScheduler, createCronExpression, buildScheduledCommandInfo } from '../src/scheduler' +import { expect } from './expect' +import { describe } from 'mocha' +import { random } from 'faker' + +declare class UserProject { + constructor() + boosterTriggerScheduledCommand: (rawRequest: unknown) => void +} +describe('Local Scheduler', () => { + let mockUserProjectStub: SinonStubbedInstance + let mockScheduledCommand: any + let mockConfig: BoosterConfig + let mockScheduledCommandName: string + let queryStub: SinonStub + let clock: any + + beforeEach(() => { + class CheckCart { + public static async handle(): Promise { + console.log('handle') + } + } + class UserProject { + boosterTriggerScheduledCommand(rawRequest: unknown): void { + console.log('rawRequest: ', rawRequest) + } + } + + clock = useFakeTimers() + + mockScheduledCommandName = random.word() + queryStub = stub() + + mockUserProjectStub = createStubInstance(UserProject) + replace(mockUserProjectStub, 'boosterTriggerScheduledCommand', queryStub as any) + + mockConfig = buildConfig() + mockConfig.scheduledCommandHandlers[mockScheduledCommandName] = { + class: CheckCart, + scheduledOn: {}, + } + + mockScheduledCommand = buildScheduledCommandInfo(mockConfig, mockScheduledCommandName) + }) + + afterEach(() => { + restore() + }) + + describe('buildScheduledCommandInfo', () => { + it('should return expected scheduled command info', async () => { + const result = await buildScheduledCommandInfo(mockConfig, mockScheduledCommandName) + expect(result).to.be.deep.equal({ + name: mockScheduledCommandName, + metadata: mockConfig.scheduledCommandHandlers[mockScheduledCommandName], + }) + }) + }) + + describe('createCronExpression', () => { + it('should return expected cron expression', async () => { + const result = await createCronExpression(mockScheduledCommand.metadata) + expect(result).to.be.equal('* * * * * *') + }) + }) + + describe('configureScheduler', () => { + it('should call scedule job', async () => { + const results = configureScheduler(mockConfig, mockUserProjectStub) + clock.tick(3250) + setTimeout(() => { + results.forEach((result) => result.cancel()) + }, 3250) + expect(queryStub).to.have.callCount(3) + }) + }) + + function buildConfig(): BoosterConfig { + return new BoosterConfig('test') + } +}) From 4a6edd3d9f4409faaf5a7b8c08ae668f0de4df43 Mon Sep 17 00:00:00 2001 From: gomarcopololead Date: Tue, 7 Jun 2022 18:59:21 +0800 Subject: [PATCH 3/3] chore: request changes from javiertoledo --- .../src/scheduler.ts | 19 ++----- .../test/scheduler.test.ts | 56 ++++++++++++------- 2 files changed, 43 insertions(+), 32 deletions(-) diff --git a/packages/framework-provider-local-infrastructure/src/scheduler.ts b/packages/framework-provider-local-infrastructure/src/scheduler.ts index 2335e653d..773d2ded8 100644 --- a/packages/framework-provider-local-infrastructure/src/scheduler.ts +++ b/packages/framework-provider-local-infrastructure/src/scheduler.ts @@ -6,26 +6,19 @@ interface ScheduledCommandInfo { metadata: ScheduledCommandMetadata } -export function configureScheduler(config: BoosterConfig, userProject: any): scheduler.Job[] { +export function configureScheduler(config: BoosterConfig, userProject: any): void { const triggerScheduleCommand = userProject['boosterTriggerScheduledCommand'] - const cronJobs: scheduler.Job[] = [] Object.keys(config.scheduledCommandHandlers) .map((scheduledCommandName) => buildScheduledCommandInfo(config, scheduledCommandName)) .filter((scheduledCommandInfo) => scheduledCommandInfo.metadata.scheduledOn) .forEach((scheduledCommandInfo) => { - const cronJob = scheduler.scheduleJob( - scheduledCommandInfo.name, - createCronExpression(scheduledCommandInfo.metadata), - () => { - triggerScheduleCommand({ typeName: scheduledCommandInfo.name }) - } - ) - cronJobs.push(cronJob) + scheduler.scheduleJob(scheduledCommandInfo.name, createCronExpression(scheduledCommandInfo.metadata), () => { + triggerScheduleCommand({ typeName: scheduledCommandInfo.name }) + }) }) - return cronJobs } -export function createCronExpression(scheduledCommandMetadata: ScheduledCommandMetadata): string { +function createCronExpression(scheduledCommandMetadata: ScheduledCommandMetadata): string { const { second = '*', minute = '*', @@ -37,7 +30,7 @@ export function createCronExpression(scheduledCommandMetadata: ScheduledCommandM return `${second} ${minute} ${hour} ${day} ${month} ${weekDay}` } -export function buildScheduledCommandInfo(config: BoosterConfig, scheduledCommandName: string): ScheduledCommandInfo { +function buildScheduledCommandInfo(config: BoosterConfig, scheduledCommandName: string): ScheduledCommandInfo { return { name: scheduledCommandName, metadata: config.scheduledCommandHandlers[scheduledCommandName], diff --git a/packages/framework-provider-local-infrastructure/test/scheduler.test.ts b/packages/framework-provider-local-infrastructure/test/scheduler.test.ts index 8a95f6ae6..539b778af 100644 --- a/packages/framework-provider-local-infrastructure/test/scheduler.test.ts +++ b/packages/framework-provider-local-infrastructure/test/scheduler.test.ts @@ -1,21 +1,27 @@ import { BoosterConfig } from '@boostercloud/framework-types' -import { restore, SinonStub, SinonStubbedInstance, createStubInstance, replace, stub, useFakeTimers } from 'sinon' -import { configureScheduler, createCronExpression, buildScheduledCommandInfo } from '../src/scheduler' +import { restore, SinonStubbedInstance, createStubInstance, replace, fake } from 'sinon' +import { configureScheduler } from '../src/scheduler' import { expect } from './expect' import { describe } from 'mocha' import { random } from 'faker' +import * as scheduler from 'node-schedule' +const rewire = require('rewire') +const schedule = rewire('../src/scheduler') +const createCronExpression = schedule.__get__('createCronExpression') +const buildScheduledCommandInfo = schedule.__get__('buildScheduledCommandInfo') +interface ScheduledCommandInfo { + typeName: string +} declare class UserProject { constructor() boosterTriggerScheduledCommand: (rawRequest: unknown) => void } + describe('Local Scheduler', () => { let mockUserProjectStub: SinonStubbedInstance - let mockScheduledCommand: any let mockConfig: BoosterConfig let mockScheduledCommandName: string - let queryStub: SinonStub - let clock: any beforeEach(() => { class CheckCart { @@ -29,21 +35,15 @@ describe('Local Scheduler', () => { } } - clock = useFakeTimers() - mockScheduledCommandName = random.word() - queryStub = stub() mockUserProjectStub = createStubInstance(UserProject) - replace(mockUserProjectStub, 'boosterTriggerScheduledCommand', queryStub as any) mockConfig = buildConfig() mockConfig.scheduledCommandHandlers[mockScheduledCommandName] = { class: CheckCart, scheduledOn: {}, } - - mockScheduledCommand = buildScheduledCommandInfo(mockConfig, mockScheduledCommandName) }) afterEach(() => { @@ -61,20 +61,38 @@ describe('Local Scheduler', () => { }) describe('createCronExpression', () => { - it('should return expected cron expression', async () => { - const result = await createCronExpression(mockScheduledCommand.metadata) + it('should return default cron expression', async () => { + const result = await createCronExpression({ scheduledOn: {} }) expect(result).to.be.equal('* * * * * *') }) + + it('should return expected cron expression', async () => { + const result = await createCronExpression({ + scheduledOn: { + minute: '30', + hour: '14', + weekDay: '0', + }, + }) + expect(result).to.be.equal('* 30 14 * * 0') + }) }) describe('configureScheduler', () => { it('should call scedule job', async () => { - const results = configureScheduler(mockConfig, mockUserProjectStub) - clock.tick(3250) - setTimeout(() => { - results.forEach((result) => result.cancel()) - }, 3250) - expect(queryStub).to.have.callCount(3) + const fakeScheduleJob = fake((name: string, cronExpression: string, scheduledFunction: () => void) => { + scheduledFunction() + }) + const fakeTriggerScheduleCommand = fake((command: ScheduledCommandInfo) => {}) + replace(scheduler, 'scheduleJob', fakeScheduleJob) + replace(mockUserProjectStub, 'boosterTriggerScheduledCommand', fakeTriggerScheduleCommand as any) + + configureScheduler(mockConfig, mockUserProjectStub) + + expect(scheduler.scheduleJob).to.have.been.calledWith(mockScheduledCommandName, '* * * * * *') + expect(mockUserProjectStub.boosterTriggerScheduledCommand).to.have.been.calledWith({ + typeName: mockScheduledCommandName, + }) }) })