import { Container } from 'inversify' import { BusModule, Bus, BUS_SYMBOLS, ApplicationBootstrap } from '@node-ts/bus-core' import { Persistence } from './persistence' import { BUS_WORKFLOW_SYMBOLS } from '../bus-workflow-symbols' import { TestCommand, TestWorkflowData, TestWorkflow, TaskRan, FinalTask } from '../test' import { MessageWorkflowMapping } from './message-workflow-mapping' import { sleep } from '../utility' import { WorkflowStatus } from './workflow-data' import { WorkflowRegistry } from './registry/workflow-registry' import { BusWorkflowModule } from '../bus-workflow-module' import { LoggerModule, LOGGER_SYMBOLS, Logger } from '@node-ts/logger-core' import { TestWorkflowStartedByCompletes, TestWorkflowStartedByCompletesData } from '../test/test-workflow-startedby-completes' import { TestWorkflowStartedByDiscard, TestWorkflowStartedByDiscardData } from '../test/test-workflow-startedby-discard' import { Mock } from 'typemoq' import { MessageAttributes } from '@node-ts/bus-messages' describe('Workflow', () => { let container: Container let persistence: Persistence let bootstrap: ApplicationBootstrap const command = new TestCommand('abc') let bus: Bus const CONSUME_TIMEOUT = 500 beforeAll(async () => { container = new Container() container.load(new LoggerModule()) container.load(new BusModule()) container.load(new BusWorkflowModule()) container.rebind(LOGGER_SYMBOLS.Logger).toConstantValue(Mock.ofType().object) persistence = container.get(BUS_WORKFLOW_SYMBOLS.Persistence) const workflowRegistry = container.get(BUS_WORKFLOW_SYMBOLS.WorkflowRegistry) workflowRegistry.register(TestWorkflow, TestWorkflowData) workflowRegistry.register(TestWorkflowStartedByCompletes, TestWorkflowStartedByCompletesData) workflowRegistry.register(TestWorkflowStartedByDiscard, TestWorkflowStartedByDiscardData) await workflowRegistry.initializeWorkflows() bootstrap = container.get(BUS_SYMBOLS.ApplicationBootstrap) await bootstrap.initialize(container) bus = container.get(BUS_SYMBOLS.Bus) await bus.send(command) await sleep(CONSUME_TIMEOUT) }) afterAll(async () => { await bootstrap.dispose() }) describe('when a message that starts a workflow is received', () => { const propertyMapping = new MessageWorkflowMapping ( cmd => cmd.property1, 'property1' ) let workflowData: TestWorkflowData[] const messageOptions = new MessageAttributes() beforeAll(async () => { workflowData = await persistence.getWorkflowData( TestWorkflowData, propertyMapping, command, messageOptions ) }) it('should start a new workflow', () => { expect(workflowData).toHaveLength(1) const data = workflowData[0] expect(data.$status).toEqual(WorkflowStatus.Running) expect(data.$version).toEqual(0) expect(data).toMatchObject({ property1: command.property1 }) }) describe('and then a message for the next step is received', () => { const event = new TaskRan('abc') let nextWorkflowData: TestWorkflowData[] beforeAll(async () => { await bus.publish(event) await sleep(CONSUME_TIMEOUT) nextWorkflowData = await persistence.getWorkflowData( TestWorkflowData, propertyMapping, command, messageOptions, true ) }) it('should handle that message', () => { expect(nextWorkflowData).toHaveLength(1) }) describe('and then a final message arrives', () => { const finalTask = new FinalTask() let finalWorkflowData: TestWorkflowData[] beforeAll(async () => { await bus.publish( finalTask, new MessageAttributes({ correlationId: nextWorkflowData[0].$workflowId }) ) await sleep(CONSUME_TIMEOUT) finalWorkflowData = await persistence.getWorkflowData( TestWorkflowData, propertyMapping, command, messageOptions, true ) }) it('should mark the workflow as complete', () => { expect(finalWorkflowData).toHaveLength(1) const data = finalWorkflowData[0] expect(data.$status).toEqual(WorkflowStatus.Complete) }) }) }) }) describe('when a workflow is completed in a StartedBy handler', () => { const messageOptions = new MessageAttributes() const propertyMapping = new MessageWorkflowMapping ( cmd => cmd.property1, 'property1' ) it('should persist the workflow as completed', async () => { const workflowData = await persistence.getWorkflowData( TestWorkflowStartedByCompletesData, propertyMapping, command, messageOptions, true ) expect(workflowData).toHaveLength(1) const data = workflowData[0] expect(data.$status).toEqual(WorkflowStatus.Complete) }) }) describe('when a StartedBy handler returns a discardStep', () => { const messageOptions = new MessageAttributes() const propertyMapping = new MessageWorkflowMapping ( cmd => cmd.property1, 'property1' ) it('should not persist the workflow', async () => { const workflowData = await persistence.getWorkflowData( TestWorkflowStartedByDiscardData, propertyMapping, command, messageOptions, true ) expect(workflowData).toHaveLength(0) }) }) })