import { Test, TestingModule } from '@nestjs/testing'; import { Job } from 'bull'; import { join } from 'path'; import { ReposService } from '../repos/repos.service'; import { PipelineUnits } from './enums/pipeline-units.enum'; import { PipelineTaskConsumer } from './pipeline-task.consumer'; import { PipelineTask } from './pipeline-task.entity'; import { PipelineTasksService } from './pipeline-tasks.service'; import { PipelineTaskLogMessage } from './models/pipeline-task-log-message.module'; import { Pipeline } from '../pipelines/pipeline.entity'; import { Project } from '../projects/project.entity'; import { TaskStatuses } from './enums/task-statuses.enum'; import { PipelineTaskLogsService } from './pipeline-task-logs.service'; import { ApplicationException } from '../commons/exceptions/application.exception'; describe('PipelineTaskConsumer', () => { let consumer: PipelineTaskConsumer; let tasksService: PipelineTasksService; let logsService: PipelineTaskLogsService; const getJob = () => ({ data: { pipelineId: 'test', units: [PipelineUnits.checkout, PipelineUnits.test], }, } as Job); beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ providers: [ { provide: PipelineTasksService, useValue: { doNextTask: () => undefined, updateTask: async (value) => value, }, }, { provide: ReposService, useValue: { getWorkspaceRootByTask: () => 'workspace-root', checkout: async () => undefined, }, }, { provide: PipelineTaskLogsService, useValue: { recordLog: async () => undefined, readLogsAsPipelineTaskLogs: async () => [], }, }, PipelineTaskConsumer, ], }).compile(); tasksService = module.get(PipelineTasksService); logsService = module.get(PipelineTaskLogsService); consumer = module.get(PipelineTaskConsumer); }); it('should be defined', () => { expect(consumer).toBeDefined(); }); describe('onCompleted', () => { it('should call doNextTask()', () => { const job = getJob(); const doNextTask = jest.spyOn(tasksService, 'doNextTask'); consumer.onCompleted(job); expect(doNextTask).toHaveBeenCalledTimes(1); }); }); describe('runScript', () => { let logText: string; let errorText: string; let recordLog: jest.SpyInstance; beforeEach(() => { logText = ''; errorText = ''; recordLog = jest .spyOn(logsService, 'recordLog') .mockImplementation(async (log: PipelineTaskLogMessage) => { logText += log.message; if (log.isError) { errorText += log.message; } }); }); it('should success and log right message', async () => { await consumer.runScript( 'node one-second-work.js', join(__dirname, '../../test/data'), ); expect(logText).toMatch(/10.+20.+30.+40.+50.+60.+70.+80.+90/s); expect(recordLog).toHaveBeenCalledTimes(10); expect( ((recordLog.mock.calls[8][0] as unknown) as PipelineTaskLogMessage) .message, ).toMatch(/^90/); }); it('should failed and log right message', async () => { await expect( consumer.runScript( 'node bad-work.js', join(__dirname, '../../test/data'), ), ).rejects.toThrowError(/exec script failed/); expect(errorText).toMatch(/Error Message/); const logs = recordLog.mock.calls .map((call) => ((call[0] as unknown) as PipelineTaskLogMessage).message) .join(''); expect(logs).toMatch(/10.+20.+30.+40.+50/s); }); it('should log with task', async () => { const task = new PipelineTask(); task.id = 'test'; const recordLog = jest.spyOn(logsService, 'recordLog'); await expect( consumer.runScript( 'node bad-work.js', join(__dirname, '../../test/data'), task, ), ).rejects.toThrowError(/exec script failed/); expect(errorText).toMatch(/Error Message 2/); expect( ((recordLog.mock.calls[2][0] as unknown) as PipelineTaskLogMessage) .task, ).toMatchObject(task); }); }); describe('doTask', () => { let task: PipelineTask; beforeEach(() => { task = new PipelineTask(); task.id = 'test-id'; task.logs = []; task.pipeline = new Pipeline(); task.pipeline.workUnitMetadata = { version: 1, units: [ { type: PipelineUnits.checkout, scripts: [], }, { type: PipelineUnits.installDependencies, scripts: ["echo ' Hello, Fennec!'"], }, ], }; task.units = task.pipeline.workUnitMetadata.units.map( (unit) => unit.type, ); task.pipeline.project = new Project(); task.pipeline.project.name = 'test-project'; }); it('success and update task on db', async () => { const job: Job = ({ data: task, update: jest.fn().mockImplementation(() => undefined), } as unknown) as Job; jest .spyOn(consumer, 'runScript') .mockImplementation(async () => undefined); const updateTask = jest.spyOn(tasksService, 'updateTask'); await consumer.doTask(job); expect(updateTask).toHaveBeenCalledTimes(4); expect(updateTask.mock.calls[0][0].startedAt).toBeDefined(); expect(updateTask.mock.calls[1][0].endedAt).toBeDefined(); expect(updateTask.mock.calls[1][0].status).toEqual(TaskStatuses.success); }); it('failed and update task on db', async () => { const job: Job = ({ data: task, update: jest.fn().mockImplementation(() => undefined), } as unknown) as Job; jest.spyOn(consumer, 'runScript').mockImplementation(async () => { throw new ApplicationException('exec script failed'); }); const updateTask = jest.spyOn(tasksService, 'updateTask'); await consumer.doTask(job); expect(updateTask).toHaveBeenCalledTimes(4); expect(updateTask.mock.calls[0][0].startedAt).toBeDefined(); expect(updateTask.mock.calls[1][0].endedAt).toBeDefined(); expect(updateTask.mock.calls[1][0].status).toEqual(TaskStatuses.failed); }); it('should do all task', async () => { const job: Job = ({ data: task, update: jest.fn().mockImplementation(() => undefined), } as unknown) as Job; const runScript = jest .spyOn(consumer, 'runScript') .mockImplementation(async () => undefined); const updateTask = jest.spyOn(tasksService, 'updateTask'); await consumer.doTask(job); expect(runScript).toHaveBeenCalledTimes(1); expect(updateTask).toHaveBeenCalledTimes(4); const taskDto: PipelineTask = updateTask.mock.calls[0][0]; expect(taskDto.logs).toHaveLength(2); expect(taskDto.logs[0].status).toEqual(TaskStatuses.success); expect(taskDto.logs[0].unit).toEqual(PipelineUnits.checkout); }); it('should log error message', async () => { const job: Job = ({ data: task, update: jest.fn().mockImplementation(() => undefined), } as unknown) as Job; const runScript = jest .spyOn(consumer, 'runScript') .mockImplementation(async () => { throw new Error('bad message'); }); const updateTask = jest.spyOn(tasksService, 'updateTask'); await consumer.doTask(job); expect(updateTask).toHaveBeenCalledTimes(4); const taskDto: PipelineTask = updateTask.mock.calls[0][0]; expect(taskDto.logs).toHaveLength(2); expect(taskDto.logs[0].status).toEqual(TaskStatuses.success); expect(taskDto.logs[1].status).toEqual(TaskStatuses.failed); }); }); });