-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(re-implementation port): Re-implement existing functionality fro…
…m simple-event-store Tests - Event Data Tests - Storage Event Tests - Guard Tests - Event Stream Appending Tests - Event Stream Appending - Concurrency Checks Tests - Event Stream Appending - Concurrency Check on existing stream Tests - Event Stream Appending - Meta data integrity Tests - Event Store Reading - Empty stream Tests - Event Store Reading - Read full stream Tests - Event Store Reading - Read partial stream Tests - Event Store Reading - Invalid Stream Id
- Loading branch information
1 parent
d229f75
commit c2e9092
Showing
17 changed files
with
368 additions
and
24 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
import { Guard } from './errors/Guard' | ||
|
||
class EventData { | ||
constructor( | ||
public readonly eventId: string, | ||
public readonly body: any, | ||
public readonly metaData: any = undefined | ||
) { | ||
Guard.againstNull('body', body) | ||
} | ||
} | ||
|
||
export { EventData } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,21 +1,20 @@ | ||
import { Guard } from './errors/Guard' | ||
import { EventData } from './EventData' | ||
|
||
// TODO: Rename as clashes with native type | ||
class StorageEvent { | ||
public eventBody: any | ||
public metaData: any | ||
public eventId: string | ||
|
||
constructor( | ||
public readonly streamId: string, | ||
public readonly data: EventData, | ||
eventData: EventData, | ||
public readonly eventNumber: number | ||
) {} | ||
} | ||
|
||
class EventData { | ||
constructor( | ||
public readonly eventId: string, | ||
public readonly body: any, | ||
public readonly metaData: any = undefined | ||
) { | ||
Guard.againstNull('body', body) | ||
this.eventBody = eventData.body | ||
this.metaData = eventData.metaData | ||
this.eventId = eventData.eventId | ||
} | ||
} | ||
|
||
export { StorageEvent, EventData } | ||
export { StorageEvent } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
class ArgumentError extends Error { | ||
constructor(message: string) { | ||
super(message) | ||
} | ||
} | ||
|
||
export { ArgumentError } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,3 @@ | ||
class ConcurrencyError extends Error {} | ||
|
||
export { ConcurrencyError } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
import { EventData } from '../src/EventData' | ||
import * as uuid from 'uuid' | ||
|
||
describe('When the body is not provided', () => { | ||
it('It should throw an error', () => { | ||
expect(() => new EventData(uuid.v4(), undefined)).toThrowError('body can not be null') | ||
}) | ||
}) | ||
describe('When creating an instance of an EventData object', () => { | ||
const expectedEventId = uuid.v4() | ||
const expectedMetaData = 'METADATA' | ||
const expectedBody = 'BODY' | ||
|
||
const eventData = new EventData(expectedEventId, expectedBody, expectedMetaData) | ||
|
||
it('It should map the event Id correctly', () => { | ||
expect(eventData.eventId).toEqual(expectedEventId) | ||
}) | ||
it('It should map the body correctly', () => { | ||
expect(eventData.body).toEqual(expectedBody) | ||
}) | ||
it('It should map the meta data correctly', () => { | ||
expect(eventData.metaData).toEqual(expectedMetaData) | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,144 @@ | ||
import { InMemoryStorageEngine } from '../src/inMemory/InMemoryStorageEngine' | ||
import { EventStore } from '../src/EventStore' | ||
import { IStorageEngine } from '../src/IStorageEngine' | ||
import { StorageEvent } from '../src/StorageEvent' | ||
import * as uuid from 'uuid' | ||
import { EventData } from '../src/EventData' | ||
import { OrderCreated } from './Events/OrderCreated' | ||
import { OrderDispatched } from './Events/OrderDispatched' | ||
|
||
describe('Given a set of engines to test against', () => { | ||
const engineFactories: (() => IStorageEngine)[] = [() => new InMemoryStorageEngine()] | ||
|
||
const newGuid = () => uuid.v4() | ||
|
||
engineFactories.forEach(getEngine => { | ||
const engine = getEngine() | ||
|
||
const getStore = async () => { | ||
await engine.initialise() | ||
return new EventStore(engine) | ||
} | ||
describe('When appending to a new stream', () => { | ||
describe('And the stream id is invalid', () => { | ||
const invalidStreamIds = [undefined, null, '', ' '] | ||
invalidStreamIds.forEach(invalidStreamId => { | ||
it(`It should throw an error for stream id: '${invalidStreamId}'`, async () => { | ||
const eventStore = await getStore() | ||
const event = new EventData(newGuid(), 'BODY') | ||
try { | ||
await eventStore.AppendToStream(invalidStreamId as string, 0, event) | ||
} catch (e) { | ||
expect(e.message).toEqual( | ||
'streamId can not be null, empty string or contain only whitespace' | ||
) | ||
} | ||
}) | ||
}) | ||
}) | ||
describe('And we have multiple events to save', () => { | ||
const streamId = newGuid() | ||
const firstEvent = new EventData(newGuid(), new OrderCreated(streamId)) | ||
const secondEvent = new EventData(newGuid(), new OrderDispatched(streamId)) | ||
const eventsToSave = [firstEvent, secondEvent] | ||
|
||
it('It should save both events and allow them to be retrievable', async () => { | ||
const sut = await getStore() | ||
await sut.AppendToStream(streamId, 0, ...eventsToSave) | ||
|
||
const savedEvents = await sut.readStreamForwards(streamId) | ||
|
||
expect(savedEvents.length).toEqual(2) | ||
const firstSavedEvent = savedEvents.shift() as StorageEvent | ||
const secondSavedEvent = savedEvents.shift() as StorageEvent | ||
|
||
expect(firstSavedEvent.streamId).toEqual(streamId) | ||
expect(firstSavedEvent.eventNumber).toEqual(1) | ||
|
||
expect(secondSavedEvent.streamId).toEqual(streamId) | ||
expect(secondSavedEvent.eventNumber).toEqual(2) | ||
}) | ||
}) | ||
it('It should save the event', async () => { | ||
const streamId = newGuid() | ||
const sut = await getStore() | ||
const event = new EventData(newGuid(), new OrderCreated(streamId)) | ||
|
||
await sut.AppendToStream(streamId, 0, event) | ||
|
||
const stream = await sut.readStreamForwards(streamId) | ||
expect(stream.length).toEqual(1) | ||
const savedEvent = stream[0] | ||
expect(savedEvent.streamId).toEqual(streamId) | ||
expect(savedEvent.eventId).toEqual(event.eventId) | ||
expect(savedEvent.eventNumber).toEqual(1) | ||
}) | ||
it('It should save the meta data correctly', async () => { | ||
interface SomeMetaData { | ||
value: string | ||
} | ||
|
||
const metaData: SomeMetaData = { | ||
value: 'foo' | ||
} | ||
|
||
const streamId = newGuid() | ||
const sut = await getStore() | ||
const event = new EventData(newGuid(), new OrderCreated(streamId), metaData) | ||
|
||
await sut.AppendToStream(streamId, 0, event) | ||
const stream = await sut.readStreamForwards(streamId) | ||
const savedEvent = stream.pop() as StorageEvent | ||
expect(savedEvent.metaData as SomeMetaData).toEqual(metaData) | ||
}) | ||
}) | ||
describe('When appending to an existing stream', () => { | ||
it('It should save the event', async () => { | ||
const streamId = newGuid() | ||
const sut = await getStore() | ||
const firstEvent = new EventData(newGuid(), new OrderCreated(streamId)) | ||
const secondEvent = new EventData(newGuid(), new OrderDispatched(streamId)) | ||
await sut.AppendToStream(streamId, 0, firstEvent) | ||
|
||
await sut.AppendToStream(streamId, 1, secondEvent) | ||
|
||
const stream = await sut.readStreamForwards(streamId) | ||
|
||
expect(stream.length).toEqual(2) | ||
const lastEvent = stream.pop() as StorageEvent | ||
expect(lastEvent.eventId).toEqual(secondEvent.eventId) | ||
expect(lastEvent.eventNumber).toEqual(2) | ||
}) | ||
}) | ||
describe('When appending to a new stream with an unexpected version', () => { | ||
const invalidRevisions = [-1, 1, 2, 99] | ||
invalidRevisions.forEach(invalidRevision => { | ||
it(`It should throw a concurrency error with revision number: '${invalidRevision}'`, async () => { | ||
const streamId = newGuid() | ||
const sut = await getStore() | ||
const event = new EventData(newGuid(), new OrderDispatched(streamId)) | ||
|
||
await expect(sut.AppendToStream(streamId, invalidRevision, event)).rejects.toThrow( | ||
'Concurrency conflict' | ||
) | ||
}) | ||
}) | ||
}) | ||
describe('When appending to an existing stream with an unexpected version', () => { | ||
const invalidRevisions = [0, 2] | ||
invalidRevisions.forEach(invalidRevision => { | ||
it(`It should throw a concurrency error with revision number: '${invalidRevision}'`, async () => { | ||
const streamId = newGuid() | ||
const sut = await getStore() | ||
|
||
const existingEvent = new EventData(newGuid(), new OrderCreated(streamId)) | ||
const newEvent = new EventData(newGuid(), new OrderDispatched(streamId)) | ||
await sut.AppendToStream(streamId, 0, existingEvent) | ||
await expect(sut.AppendToStream(streamId, invalidRevision, newEvent)).rejects.toThrow( | ||
'Concurrency conflict' | ||
) | ||
}) | ||
}) | ||
}) | ||
}) | ||
}) |
Oops, something went wrong.