diff --git a/packages/framework-provider-local-infrastructure/src/scheduler.ts b/packages/framework-provider-local-infrastructure/src/scheduler.ts index fa9b37bb4..773d2ded8 100644 --- a/packages/framework-provider-local-infrastructure/src/scheduler.ts +++ b/packages/framework-provider-local-infrastructure/src/scheduler.ts @@ -19,8 +19,15 @@ export function configureScheduler(config: BoosterConfig, userProject: any): voi } function createCronExpression(scheduledCommandMetadata: ScheduledCommandMetadata): string { - const { minute = '*', hour = '*', day = '*', month = '*', weekDay = '*' } = scheduledCommandMetadata.scheduledOn - return `${minute} ${hour} ${day} ${month} ${weekDay}` + const { + second = '*', + minute = '*', + hour = '*', + day = '*', + month = '*', + weekDay = '*', + } = scheduledCommandMetadata.scheduledOn + return `${second} ${minute} ${hour} ${day} ${month} ${weekDay}` } function buildScheduledCommandInfo(config: BoosterConfig, scheduledCommandName: string): ScheduledCommandInfo { 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..539b778af --- /dev/null +++ b/packages/framework-provider-local-infrastructure/test/scheduler.test.ts @@ -0,0 +1,102 @@ +import { BoosterConfig } from '@boostercloud/framework-types' +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 mockConfig: BoosterConfig + let mockScheduledCommandName: string + + beforeEach(() => { + class CheckCart { + public static async handle(): Promise { + console.log('handle') + } + } + class UserProject { + boosterTriggerScheduledCommand(rawRequest: unknown): void { + console.log('rawRequest: ', rawRequest) + } + } + + mockScheduledCommandName = random.word() + + mockUserProjectStub = createStubInstance(UserProject) + + mockConfig = buildConfig() + mockConfig.scheduledCommandHandlers[mockScheduledCommandName] = { + class: CheckCart, + scheduledOn: {}, + } + }) + + 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 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 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, + }) + }) + }) + + function buildConfig(): BoosterConfig { + return new BoosterConfig('test') + } +}) 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 + }) + }) + }) +})