eventric
Version:
behavior-first application development
345 lines (241 loc) • 11.3 kB
text/coffeescript
describe 'Remote Projection Feature', ->
exampleContext = null
exampleRemote = null
projectionId = null
describe 'given a context with one aggregate with two commands', ->
beforeEach ->
exampleContext = eventric.context 'Example'
exampleContext.defineDomainEvents
ExampleCreated: ->
ExampleUpdated: ->
exampleContext.addCommandHandlers
CreateExample: (params) ->
.create 'Example'
.then (example) ->
example.$save()
UpdateExample: (params) ->
.load 'Example', params.id
.then (example) ->
example.update()
example.$save()
class Example
create: ->
'ExampleCreated'
update: ->
'ExampleUpdated'
exampleContext.addAggregate 'Example', Example
exampleContext.initialize()
describe 'adding a projection to the remote', ->
beforeEach ->
exampleRemote = eventric.remote 'Example'
class ExampleProjection
initialize: (params, done) ->
done()
handleExampleCreated: (domainEvent) ->
= true
handleToCheckCorrectFunctionCall: (domainEvent) ->
exampleRemote.addProjection 'ExampleProjection', ExampleProjection
exampleRemote.initializeProjectionInstance 'ExampleProjection'
.then (_projectionId) ->
projectionId = _projectionId
it 'should update the projection state as expected', ->
exampleRemote.command 'CreateExample'
.then ->
exampleProjection = exampleRemote.getProjectionInstance projectionId
expect(exampleProjection.created).to.be.true
it 'should be possible to remove the projection', ->
sandbox.spy exampleRemote, 'unsubscribeFromDomainEvent'
exampleRemote.destroyProjectionInstance projectionId
expect(exampleRemote.getProjectionInstance projectionId).to.be.undefined
expect(exampleRemote.unsubscribeFromDomainEvent).to.have.been.called
describe 'initializing a projection as object on the remote', ->
beforeEach ->
exampleRemote = eventric.remote 'Example'
exampleRemote.initializeProjection
initialize: (params, done) ->
done()
handleExampleCreated: (domainEvent) ->
= true
handleToCheckCorrectFunctionCall: (domainEvent) ->
.then (_projectionId) ->
projectionId = _projectionId
it 'should update the projection state as expected', ->
exampleRemote.command 'CreateExample'
.then ->
exampleProjection = exampleRemote.getProjectionInstance projectionId
expect(exampleProjection.created).to.be.true
describe 'adding a remote projection to the remote which subscribes to a specific aggregate', ->
projectionId = null
beforeEach ->
exampleRemote = eventric.remote 'Example'
class ExampleProjection
initialize: (params, done) ->
params.aggregateId
done()
handleExampleUpdated: (domainEvent) ->
= true
exampleRemote.addProjection 'ExampleProjection', ExampleProjection
it 'should update the projection state as expected', ->
testExampleId = null
exampleRemote.command 'CreateExample'
.then (exampleId) ->
testExampleId = exampleId
exampleRemote.initializeProjectionInstance 'ExampleProjection', aggregateId: exampleId
.then (_projectionId) ->
projectionId = _projectionId
exampleRemote.command 'CreateExample'
.then (exampleId) ->
exampleRemote.command 'UpdateExample',
id: exampleId
.then ->
exampleProjection = exampleRemote.getProjectionInstance projectionId
expect(exampleProjection.updated).not.to.be.true
exampleRemote.command 'UpdateExample',
id: testExampleId
.then ->
exampleProjection = exampleRemote.getProjectionInstance projectionId
expect(exampleProjection.updated).to.be.true
it 'should publish an event whenever a remote projection is updated', (done) ->
testExampleId = null
exampleRemote.command 'CreateExample'
.then (exampleId) ->
testExampleId = exampleId
exampleRemote.initializeProjectionInstance 'ExampleProjection', aggregateId: exampleId
.then (projectionId) ->
exampleProjection = exampleRemote.getProjectionInstance projectionId
exampleRemote.subscribe 'projection:ExampleProjection:changed', ->
done()
exampleRemote.command 'UpdateExample',
id: testExampleId
it 'should be possible to remove the projection', ->
exampleRemote.initializeProjectionInstance 'ExampleProjection', aggregateId: '123'
.then (projectionId) ->
sandbox.spy exampleRemote, 'unsubscribeFromDomainEvent'
exampleRemote.destroyProjectionInstance projectionId
expect(exampleRemote.getProjectionInstance projectionId).to.be.undefined
expect(exampleRemote.unsubscribeFromDomainEvent).to.have.been.called
describe 'adding a remote projection for which matching domain events already exist', ->
beforeEach ->
exampleRemote = eventric.remote 'Example'
class ExampleProjection
constructor: ->
= 0
handleExampleCreated: ->
++
exampleRemote.addProjection 'ExampleProjection', ExampleProjection
it 'should apply the already existing domain events immediately to the projection', ->
exampleContext.command 'CreateExample'
.then (id) ->
exampleRemote.initializeProjectionInstance 'ExampleProjection'
.then (projectionId) ->
projection = exampleRemote.getProjectionInstance projectionId
expect(projection.exampleCount).to.equal 1
describe 'adding a remote projection for a specific aggregate id for which matching domain events already exist', ->
beforeEach ->
exampleRemote = eventric.remote 'Example'
class ExampleProjection
constructor: ->
= false
initialize: (params, done) ->
params.aggregateId
done()
handleExampleUpdated: ->
= true
exampleRemote.addProjection 'ExampleProjection', ExampleProjection
it 'should apply the already existing domain events immediately to the projection', ->
exampleContext.command 'CreateExample'
.then (exampleId) ->
exampleContext.command 'UpdateExample', id: exampleId
.then (exampleId) ->
exampleRemote.initializeProjectionInstance 'ExampleProjection', aggregateId: exampleId
.then (projectionId) ->
projection = exampleRemote.getProjectionInstance projectionId
expect(projection.updated).to.be.true
it 'should only apply the already existing domain events for the specific aggregate', ->
firstExampleId = null
exampleContext.command 'CreateExample'
.then (_firstExampleId) ->
firstExampleId = _firstExampleId
exampleContext.command 'CreateExample'
.then (secondExampleId) ->
exampleContext.command 'UpdateExample', id: secondExampleId
.then ->
exampleRemote.initializeProjectionInstance 'ExampleProjection', aggregateId: firstExampleId
.then (projectionId) ->
projection = exampleRemote.getProjectionInstance projectionId
expect(projection.updated).to.be.false
describe 'adding a remote projection for which multiple matching domain events already exist', ->
beforeEach ->
exampleRemote = eventric.remote 'Example'
class ExampleProjection
constructor: ->
= []
handleExampleCreated: (domainEvent) ->
.push domainEvent.name
handleExampleUpdated: (domainEvent) ->
.push domainEvent.name
exampleRemote.addProjection 'ExampleProjection', ExampleProjection
it 'should apply the already existing domain events in the correct order', ->
exampleContext.command 'CreateExample'
.then (id) ->
exampleContext.command 'UpdateExample', id: id
.then (id) ->
exampleRemote.initializeProjectionInstance 'ExampleProjection'
.then (projectionId) ->
projection = exampleRemote.getProjectionInstance projectionId
expect(projection.events).to.deep.equal ['ExampleCreated', 'ExampleUpdated']
describe 'given a context with one aggregate which emits two domain events in one behavior', ->
beforeEach ->
exampleContext = eventric.context 'Example'
exampleContext.defineDomainEvents
ExampleCreated: ->
ExampleModified: ->
exampleContext.addAggregate 'Example', ->
create: (params) ->
'ExampleCreated'
params
modify: (params) ->
'ExampleModified'
exampleContext.addCommandHandlers
CreateExample: (params) ->
exampleId = null
.create 'Example',
foo: 'bar'
.then (example) ->
example.$save()
exampleContext.initialize()
describe 'given two projections where the first one listen to one event and the second one to both events', ->
beforeEach ->
exampleRemote = eventric.remote 'Example'
exampleRemote.addProjection 'FirstProjection', ->
initialize: (params, done) ->
= []
done()
handleExampleCreated: (domainEvent) ->
.push 'created'
exampleRemote.addProjection 'SecondProjection', ->
initialize: (params, done) ->
= []
done()
handleExampleCreated: (domainEvent) ->
.push 'created'
handleExampleModified: (domainEvent) ->
.push 'modified'
describe 'when emitting domain events the projections subscribed to', ->
secondProjection = null
beforeEach ->
exampleRemote.initializeProjectionInstance 'FirstProjection'
.then (firstProjectionId) ->
exampleRemote.initializeProjectionInstance 'SecondProjection'
.then (secondProjectionId) ->
secondProjection = exampleRemote.getProjectionInstance secondProjectionId
it 'should execute the second projection\'s event handlers in the correct order', ->
new Promise (resolve, reject) ->
exampleRemote.subscribeToDomainEvent 'ExampleModified', ->
try
expect(secondProjection.actions[0]).to.equal 'created'
expect(secondProjection.actions[1]).to.equal 'modified'
resolve()
catch error
reject error
exampleRemote.command 'CreateExample'