spec/stage/StageManager.spec.ts
import 'mocha';
import { AsyncOperationAttempted, AsyncOperationCompleted, AsyncOperationFailed, DomainEvent } from '../../src/events';
import { CorrelationId, Description, Duration } from '../../src/model';
import { Clock, StageManager } from '../../src/stage';
import { expect } from '../expect';
import { Recorder } from '../Recorder';
describe('StageManager', () => {
class TestEvent extends DomainEvent {
constructor() {
super();
}
}
const testEvent = new TestEvent();
it('broadcasts the domain event it receives to all the registered subscribers', () => {
const stageManager = new StageManager(Duration.ofMilliseconds(250), new Clock());
const crewMember1 = new Recorder();
const crewMember2 = new Recorder();
stageManager.register(crewMember1, crewMember2);
stageManager.notifyOf(testEvent);
expect(crewMember1.events).to.have.lengthOf(1);
expect(crewMember1.events[0]).to.be.instanceOf(TestEvent);
expect(crewMember2.events).to.have.lengthOf(1);
expect(crewMember2.events[0]).to.be.instanceOf(TestEvent);
});
it('keeps track of the work in progress', () => {
const stageManager = new StageManager(Duration.ofMilliseconds(250), new Clock());
const id = CorrelationId.create();
stageManager.notifyOf(new AsyncOperationAttempted(
new Description('Saving a file...'),
id,
));
stageManager.notifyOf(new AsyncOperationCompleted(
new Description('File saved'),
id,
));
return expect(stageManager.waitForNextCue()).to.be.fulfilled;
});
it('provides details should the work in progress fail to complete', () => {
const timeout = Duration.ofMilliseconds(50);
const stageManager = new StageManager(timeout, new Clock());
stageManager.notifyOf(new AsyncOperationAttempted(
new Description('[Service 1] Starting...'),
CorrelationId.create(),
));
stageManager.notifyOf(new AsyncOperationAttempted(
new Description('[Service 2] Starting...'),
CorrelationId.create(),
));
return expect(stageManager.waitForNextCue()).to.be.rejected.then(error => {
const lines = error.message.split('\n');
expect(lines, `message: \n${ error.message }`).to.have.lengthOf(3);
expect(lines[0]).to.equal('2 async operations have failed to complete within a 50ms cue timeout:');
expect(lines[1], error.message).to.match(/^\d+ms.*?- \[Service 1] Starting...$/);
expect(lines[2], error.message).to.match(/^\d+ms.*?- \[Service 2] Starting...$/);
});
});
it('provides details should the work in progress fail with an error', () => {
const timeout = Duration.ofMilliseconds(100);
const stageManager = new StageManager(timeout, new Clock());
const correlationId = CorrelationId.create();
stageManager.notifyOf(new AsyncOperationAttempted(
new Description('[Service 1] Starting...'),
correlationId,
));
stageManager.notifyOf(new AsyncOperationFailed(
new Error('Something happened'),
correlationId,
));
return expect(stageManager.waitForNextCue()).to.be.rejected.then(error => {
const lines = error.message.split('\n');
expect(lines[0]).to.equal('1 async operation has failed to complete:');
expect(lines[1]).to.equal('[Service 1] Starting... - Error: Something happened');
});
});
});