feat: update aggregate implementation
This commit is contained in:
@@ -6,8 +6,7 @@ import { afterAll, describe } from "@std/testing/bdd";
|
||||
import { BrowserAdapter } from "../adapters/browser/adapter.ts";
|
||||
import { EventStore, EventStoreHooks } from "../libraries/event-store.ts";
|
||||
import { Projector } from "../libraries/projector.ts";
|
||||
import { aggregates } from "./mocks/aggregates.ts";
|
||||
import { events, EventStoreFactory } from "./mocks/events.ts";
|
||||
import { Events, events } from "./mocks/events.ts";
|
||||
import testAddEvent from "./store/add-event.ts";
|
||||
import testCreateSnapshot from "./store/create-snapshot.ts";
|
||||
import testMakeAggregateReducer from "./store/make-aggregate-reducer.ts";
|
||||
@@ -18,7 +17,7 @@ import testPushManyAggregates from "./store/push-many-aggregates.ts";
|
||||
import testReduce from "./store/reduce.ts";
|
||||
import testReplayEvents from "./store/replay-events.ts";
|
||||
|
||||
const eventStoreFn = async (options: { hooks?: EventStoreHooks<EventStoreFactory> } = {}) => getEventStore(options);
|
||||
const eventStoreFn = async (options: { hooks?: EventStoreHooks<Events> } = {}) => getEventStore(options);
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------------
|
||||
@@ -44,7 +43,6 @@ describe("Adapter > Browser (IndexedDb)", () => {
|
||||
testReplayEvents(eventStoreFn);
|
||||
testReduce(eventStoreFn);
|
||||
testOnceProjection(eventStoreFn);
|
||||
|
||||
testPushAggregate(eventStoreFn);
|
||||
testPushManyAggregates(eventStoreFn);
|
||||
});
|
||||
@@ -55,15 +53,14 @@ describe("Adapter > Browser (IndexedDb)", () => {
|
||||
|--------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
function getEventStore({ hooks = {} }: { hooks?: EventStoreHooks<EventStoreFactory> }) {
|
||||
function getEventStore({ hooks = {} }: { hooks?: EventStoreHooks<Events> }) {
|
||||
const store = new EventStore({
|
||||
adapter: new BrowserAdapter("indexeddb"),
|
||||
events,
|
||||
aggregates,
|
||||
hooks,
|
||||
});
|
||||
|
||||
const projector = new Projector<EventStoreFactory>();
|
||||
const projector = new Projector<Events>();
|
||||
|
||||
if (hooks.onEventsInserted === undefined) {
|
||||
store.onEventsInserted(async (records, { batch }) => {
|
||||
|
||||
@@ -5,8 +5,7 @@ import { describe } from "@std/testing/bdd";
|
||||
import { BrowserAdapter } from "../adapters/browser/adapter.ts";
|
||||
import { EventStore, EventStoreHooks } from "../libraries/event-store.ts";
|
||||
import { Projector } from "../libraries/projector.ts";
|
||||
import { aggregates } from "./mocks/aggregates.ts";
|
||||
import { events, EventStoreFactory } from "./mocks/events.ts";
|
||||
import { Events, events } from "./mocks/events.ts";
|
||||
import testAddEvent from "./store/add-event.ts";
|
||||
import testCreateSnapshot from "./store/create-snapshot.ts";
|
||||
import testMakeAggregateReducer from "./store/make-aggregate-reducer.ts";
|
||||
@@ -17,7 +16,7 @@ import testPushManyAggregates from "./store/push-many-aggregates.ts";
|
||||
import testReduce from "./store/reduce.ts";
|
||||
import testReplayEvents from "./store/replay-events.ts";
|
||||
|
||||
const eventStoreFn = async (options: { hooks?: EventStoreHooks<EventStoreFactory> } = {}) => getEventStore(options);
|
||||
const eventStoreFn = async (options: { hooks?: EventStoreHooks<Events> } = {}) => getEventStore(options);
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------------
|
||||
@@ -33,7 +32,6 @@ describe("Adapter > Browser (memory)", () => {
|
||||
testReplayEvents(eventStoreFn);
|
||||
testReduce(eventStoreFn);
|
||||
testOnceProjection(eventStoreFn);
|
||||
|
||||
testPushAggregate(eventStoreFn);
|
||||
testPushManyAggregates(eventStoreFn);
|
||||
});
|
||||
@@ -44,15 +42,14 @@ describe("Adapter > Browser (memory)", () => {
|
||||
|--------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
function getEventStore({ hooks = {} }: { hooks?: EventStoreHooks<EventStoreFactory> }) {
|
||||
function getEventStore({ hooks = {} }: { hooks?: EventStoreHooks<Events> }) {
|
||||
const store = new EventStore({
|
||||
adapter: new BrowserAdapter("memorydb"),
|
||||
events,
|
||||
aggregates,
|
||||
hooks,
|
||||
});
|
||||
|
||||
const projector = new Projector<EventStoreFactory>();
|
||||
const projector = new Projector<Events>();
|
||||
|
||||
if (hooks.onEventsInserted === undefined) {
|
||||
store.onEventsInserted(async (records, { batch }) => {
|
||||
|
||||
@@ -1,13 +1,9 @@
|
||||
import { AggregateRoot } from "../../libraries/aggregate.ts";
|
||||
import { AggregateFactory } from "../../libraries/aggregate-factory.ts";
|
||||
import { makeId } from "../../libraries/nanoid.ts";
|
||||
import { makeAggregateReducer } from "../../libraries/reducer.ts";
|
||||
import { EventStoreFactory } from "./events.ts";
|
||||
import { Events } from "./events.ts";
|
||||
|
||||
export class User extends AggregateRoot<EventStoreFactory> {
|
||||
export class User extends AggregateRoot<Events> {
|
||||
static override readonly name = "user";
|
||||
|
||||
id: string = "";
|
||||
name: Name = {
|
||||
given: "",
|
||||
family: "",
|
||||
@@ -19,40 +15,12 @@ export class User extends AggregateRoot<EventStoreFactory> {
|
||||
count: 0,
|
||||
};
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Factories
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
static reducer = makeAggregateReducer(User);
|
||||
|
||||
static create(name: Name, email: string): User {
|
||||
const user = new User();
|
||||
user.push({
|
||||
type: "user:created",
|
||||
stream: makeId(),
|
||||
data: { name, email },
|
||||
meta: { auditor: "foo" },
|
||||
});
|
||||
return user;
|
||||
}
|
||||
|
||||
static async getById(userId: string): Promise<User | undefined> {
|
||||
return this.$store.reduce({ name: "user", stream: userId, reducer: this.reducer });
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Reducer
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
with(event: EventStoreFactory["$events"][number]["$record"]) {
|
||||
with(event: Events["$events"][number]["$record"]) {
|
||||
switch (event.type) {
|
||||
case "user:created": {
|
||||
this.id = event.stream;
|
||||
this.name.given = event.data.name?.given ?? "";
|
||||
this.name.family = event.data.name?.family ?? "";
|
||||
this.email = event.data.email;
|
||||
break;
|
||||
}
|
||||
case "user:name:given-set": {
|
||||
this.name.given = event.data;
|
||||
break;
|
||||
@@ -107,11 +75,6 @@ export class User extends AggregateRoot<EventStoreFactory> {
|
||||
});
|
||||
}
|
||||
|
||||
async snapshot(): Promise<this> {
|
||||
await this.$store.createSnapshot({ name: "user", stream: this.id, reducer: User.reducer });
|
||||
return this;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Helpers
|
||||
// -------------------------------------------------------------------------
|
||||
@@ -121,8 +84,6 @@ export class User extends AggregateRoot<EventStoreFactory> {
|
||||
}
|
||||
}
|
||||
|
||||
export const aggregates = new AggregateFactory([User]);
|
||||
|
||||
type Name = {
|
||||
given: string;
|
||||
family: string;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import z from "zod/v4";
|
||||
import z from "zod";
|
||||
|
||||
import { event } from "../../libraries/event.ts";
|
||||
import { EventFactory } from "../../libraries/event-factory.ts";
|
||||
@@ -32,4 +32,4 @@ export const events = new EventFactory([
|
||||
event.type("post:removed").meta(auditor),
|
||||
]);
|
||||
|
||||
export type EventStoreFactory = typeof events;
|
||||
export type Events = typeof events;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { makeReducer } from "../../libraries/reducer.ts";
|
||||
import { EventStoreFactory } from "./events.ts";
|
||||
import { Events } from "./events.ts";
|
||||
|
||||
export const userPostReducer = makeReducer<EventStoreFactory, UserPostState>(
|
||||
export const userPostReducer = makeReducer<Events, UserPostState>(
|
||||
(state, event) => {
|
||||
switch (event.type) {
|
||||
case "post:created": {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { makeReducer } from "../../libraries/reducer.ts";
|
||||
import { EventStoreFactory } from "./events.ts";
|
||||
import { Events } from "./events.ts";
|
||||
|
||||
export const userReducer = makeReducer<EventStoreFactory, UserState>(
|
||||
export const userReducer = makeReducer<Events, UserState>(
|
||||
(state, event) => {
|
||||
switch (event.type) {
|
||||
case "user:created": {
|
||||
|
||||
@@ -4,8 +4,7 @@ import { MongoTestContainer } from "@valkyr/testcontainers/mongodb";
|
||||
import { MongoAdapter, register } from "../adapters/mongo/adapter.ts";
|
||||
import { EventStore, type EventStoreHooks } from "../libraries/event-store.ts";
|
||||
import { Projector } from "../libraries/projector.ts";
|
||||
import { aggregates } from "./mocks/aggregates.ts";
|
||||
import { events, type EventStoreFactory } from "./mocks/events.ts";
|
||||
import { type Events, events } from "./mocks/events.ts";
|
||||
import testAddEvent from "./store/add-event.ts";
|
||||
import testAddManyEvents from "./store/add-many-events.ts";
|
||||
import testCreateSnapshot from "./store/create-snapshot.ts";
|
||||
@@ -23,7 +22,7 @@ const DB_NAME = "sandbox";
|
||||
|
||||
const container = await MongoTestContainer.start();
|
||||
|
||||
const eventStoreFn = async (options: { hooks?: EventStoreHooks<EventStoreFactory> } = {}) => getEventStore(options);
|
||||
const eventStoreFn = async (options: { hooks?: EventStoreHooks<Events> } = {}) => getEventStore(options);
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------------
|
||||
@@ -66,7 +65,6 @@ describe("Adapter > MongoDb", () => {
|
||||
testReplayEvents(eventStoreFn);
|
||||
testReduce(eventStoreFn);
|
||||
testOnceProjection(eventStoreFn);
|
||||
|
||||
testPushAggregate(eventStoreFn);
|
||||
testPushManyAggregates(eventStoreFn);
|
||||
});
|
||||
@@ -77,15 +75,14 @@ describe("Adapter > MongoDb", () => {
|
||||
|--------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
async function getEventStore({ hooks = {} }: { hooks?: EventStoreHooks<EventStoreFactory> }) {
|
||||
async function getEventStore({ hooks = {} }: { hooks?: EventStoreHooks<Events> }) {
|
||||
const store = new EventStore({
|
||||
adapter: new MongoAdapter(() => container.client, DB_NAME),
|
||||
events,
|
||||
aggregates,
|
||||
hooks,
|
||||
});
|
||||
|
||||
const projector = new Projector<EventStoreFactory>();
|
||||
const projector = new Projector<Events>();
|
||||
|
||||
if (hooks.onEventsInserted === undefined) {
|
||||
store.onEventsInserted(async (records, { batch }) => {
|
||||
|
||||
@@ -6,8 +6,7 @@ import { PostgresAdapter } from "../adapters/postgres/adapter.ts";
|
||||
import type { PostgresConnection } from "../adapters/postgres/connection.ts";
|
||||
import { EventStore, type EventStoreHooks } from "../libraries/event-store.ts";
|
||||
import { Projector } from "../libraries/projector.ts";
|
||||
import { aggregates } from "./mocks/aggregates.ts";
|
||||
import { events, EventStoreFactory } from "./mocks/events.ts";
|
||||
import { Events, events } from "./mocks/events.ts";
|
||||
import testAddEvent from "./store/add-event.ts";
|
||||
import testAddManyEvents from "./store/add-many-events.ts";
|
||||
import testCreateSnapshot from "./store/create-snapshot.ts";
|
||||
@@ -26,8 +25,7 @@ const DB_NAME = "sandbox";
|
||||
const container = await PostgresTestContainer.start("postgres:17");
|
||||
const sql = postgres(container.url(DB_NAME));
|
||||
|
||||
const eventStoreFn = async (options: { hooks?: EventStoreHooks<EventStoreFactory> } = {}) =>
|
||||
getEventStore(sql, options);
|
||||
const eventStoreFn = async (options: { hooks?: EventStoreHooks<Events> } = {}) => getEventStore(sql, options);
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------------
|
||||
@@ -103,7 +101,6 @@ describe("Adapter > Postgres", () => {
|
||||
testReplayEvents(eventStoreFn);
|
||||
testReduce(eventStoreFn);
|
||||
testOnceProjection(eventStoreFn);
|
||||
|
||||
testPushAggregate(eventStoreFn);
|
||||
testPushManyAggregates(eventStoreFn);
|
||||
});
|
||||
@@ -114,18 +111,14 @@ describe("Adapter > Postgres", () => {
|
||||
|--------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
async function getEventStore(
|
||||
connection: PostgresConnection,
|
||||
{ hooks = {} }: { hooks?: EventStoreHooks<EventStoreFactory> },
|
||||
) {
|
||||
async function getEventStore(connection: PostgresConnection, { hooks = {} }: { hooks?: EventStoreHooks<Events> }) {
|
||||
const store = new EventStore({
|
||||
adapter: new PostgresAdapter(connection, { schema: "event_store" }),
|
||||
events,
|
||||
aggregates,
|
||||
hooks,
|
||||
});
|
||||
|
||||
const projector = new Projector<EventStoreFactory>();
|
||||
const projector = new Projector<Events>();
|
||||
|
||||
if (hooks.onEventsInserted === undefined) {
|
||||
store.onEventsInserted(async (records, { batch }) => {
|
||||
|
||||
@@ -3,10 +3,10 @@ import { it } from "@std/testing/bdd";
|
||||
|
||||
import { EventInsertionError, EventValidationError } from "../../libraries/errors.ts";
|
||||
import { makeId } from "../../libraries/nanoid.ts";
|
||||
import type { EventStoreFactory } from "../mocks/events.ts";
|
||||
import type { Events } from "../mocks/events.ts";
|
||||
import { describe } from "../utilities/describe.ts";
|
||||
|
||||
export default describe<EventStoreFactory>(".addEvent", (getEventStore) => {
|
||||
export default describe<Events>(".addEvent", (getEventStore) => {
|
||||
it("should throw a 'EventValidationError' when providing bad event data", async () => {
|
||||
const { store } = await getEventStore();
|
||||
|
||||
|
||||
@@ -3,11 +3,11 @@ import { it } from "@std/testing/bdd";
|
||||
import { nanoid } from "nanoid";
|
||||
|
||||
import { EventValidationError } from "../../mod.ts";
|
||||
import type { EventStoreFactory } from "../mocks/events.ts";
|
||||
import type { Events } from "../mocks/events.ts";
|
||||
import { userReducer } from "../mocks/user-reducer.ts";
|
||||
import { describe } from "../utilities/describe.ts";
|
||||
|
||||
export default describe<EventStoreFactory>(".addSequence", (getEventStore) => {
|
||||
export default describe<Events>(".addSequence", (getEventStore) => {
|
||||
it("should insert 'user:created', 'user:name:given-set', and 'user:email-set' in a sequence of events", async () => {
|
||||
const { store } = await getEventStore();
|
||||
const stream = nanoid();
|
||||
|
||||
@@ -2,11 +2,11 @@ import { assertEquals, assertNotEquals, assertObjectMatch } from "@std/assert";
|
||||
import { it } from "@std/testing/bdd";
|
||||
import { nanoid } from "nanoid";
|
||||
|
||||
import type { EventStoreFactory } from "../mocks/events.ts";
|
||||
import type { Events } from "../mocks/events.ts";
|
||||
import { userReducer } from "../mocks/user-reducer.ts";
|
||||
import { describe } from "../utilities/describe.ts";
|
||||
|
||||
export default describe<EventStoreFactory>(".createSnapshot", (getEventStore) => {
|
||||
export default describe<Events>(".createSnapshot", (getEventStore) => {
|
||||
it("should create a new snapshot", async () => {
|
||||
const { store } = await getEventStore();
|
||||
const stream = nanoid();
|
||||
|
||||
@@ -1,24 +1,26 @@
|
||||
import { assertEquals } from "@std/assert";
|
||||
import { it } from "@std/testing/bdd";
|
||||
|
||||
import type { EventStoreFactory } from "../mocks/events.ts";
|
||||
import { User } from "../mocks/aggregates.ts";
|
||||
import type { Events } from "../mocks/events.ts";
|
||||
import { describe } from "../utilities/describe.ts";
|
||||
|
||||
export default describe<EventStoreFactory>(".makeAggregateReducer", (getEventStore) => {
|
||||
export default describe<Events>(".makeAggregateReducer", (getEventStore) => {
|
||||
it("should reduce a user", async () => {
|
||||
const { store } = await getEventStore();
|
||||
|
||||
const userA = await store
|
||||
.aggregate("user")
|
||||
.create({ given: "John", family: "Doe" }, "john.doe@fixture.none")
|
||||
const userA = await store.aggregate
|
||||
.from(User)
|
||||
.setGivenName("Jane")
|
||||
.setFamilyName("Doe")
|
||||
.setEmail("john.doe@fixture.none", "auditor")
|
||||
.save();
|
||||
|
||||
await userA.snapshot();
|
||||
|
||||
await userA.setFamilyName("Smith").setEmail("jane.smith@fixture.none", "system").save();
|
||||
|
||||
const userB = await store.aggregate("user").getById(userA.id);
|
||||
const userB = await store.aggregate.getByStream(User, userA.id);
|
||||
if (userB === undefined) {
|
||||
throw new Error("Expected user to exist");
|
||||
}
|
||||
|
||||
@@ -2,10 +2,10 @@ import { assertEquals, assertLess } from "@std/assert";
|
||||
import { it } from "@std/testing/bdd";
|
||||
|
||||
import { RelationPayload } from "../../types/adapter.ts";
|
||||
import type { EventStoreFactory } from "../mocks/events.ts";
|
||||
import type { Events } from "../mocks/events.ts";
|
||||
import { describe } from "../utilities/describe.ts";
|
||||
|
||||
export default describe<EventStoreFactory>(".makeEvent", (getEventStore) => {
|
||||
export default describe<Events>(".makeEvent", (getEventStore) => {
|
||||
it("should make and performantly batch insert a list of events directly", async () => {
|
||||
const { store } = await getEventStore();
|
||||
|
||||
|
||||
@@ -3,12 +3,12 @@ import { it } from "@std/testing/bdd";
|
||||
import { nanoid } from "nanoid";
|
||||
|
||||
import { makeId } from "../../libraries/nanoid.ts";
|
||||
import type { EventStoreFactory } from "../mocks/events.ts";
|
||||
import type { Events } from "../mocks/events.ts";
|
||||
import { userPostReducer } from "../mocks/user-posts-reducer.ts";
|
||||
import { userReducer } from "../mocks/user-reducer.ts";
|
||||
import { describe } from "../utilities/describe.ts";
|
||||
|
||||
export default describe<EventStoreFactory>(".makeReducer", (getEventStore) => {
|
||||
export default describe<Events>(".makeReducer", (getEventStore) => {
|
||||
it("should create a 'user' reducer and only reduce filtered events", async () => {
|
||||
const { store } = await getEventStore();
|
||||
|
||||
|
||||
@@ -2,10 +2,10 @@ import { assertEquals, assertObjectMatch } from "@std/assert";
|
||||
import { it } from "@std/testing/bdd";
|
||||
|
||||
import { makeId } from "../../libraries/nanoid.ts";
|
||||
import type { EventStoreFactory } from "../mocks/events.ts";
|
||||
import type { Events } from "../mocks/events.ts";
|
||||
import { describe } from "../utilities/describe.ts";
|
||||
|
||||
export default describe<EventStoreFactory>("projector.once", (getEventStore) => {
|
||||
export default describe<Events>("projector.once", (getEventStore) => {
|
||||
it("should handle successfull projection", async () => {
|
||||
const { store, projector } = await getEventStore();
|
||||
|
||||
|
||||
@@ -2,10 +2,10 @@ import { assertEquals } from "@std/assert";
|
||||
import { it } from "@std/testing/bdd";
|
||||
import { nanoid } from "nanoid";
|
||||
|
||||
import type { EventStoreFactory } from "../../mocks/events.ts";
|
||||
import type { Events } from "../../mocks/events.ts";
|
||||
import { describe } from "../../utilities/describe.ts";
|
||||
|
||||
export default describe<EventStoreFactory>("relations", (getEventStore) => {
|
||||
export default describe<Events>("relations", (getEventStore) => {
|
||||
it("should create a new relation", async () => {
|
||||
const { store } = await getEventStore();
|
||||
|
||||
|
||||
@@ -1,36 +1,38 @@
|
||||
import { assertEquals, assertObjectMatch } from "@std/assert";
|
||||
import { it } from "@std/testing/bdd";
|
||||
|
||||
import type { EventStoreFactory } from "../mocks/events.ts";
|
||||
import { User } from "../mocks/aggregates.ts";
|
||||
import type { Events } from "../mocks/events.ts";
|
||||
import { userReducer } from "../mocks/user-reducer.ts";
|
||||
import { describe } from "../utilities/describe.ts";
|
||||
|
||||
export default describe<EventStoreFactory>(".pushAggregate", (getEventStore) => {
|
||||
export default describe<Events>(".pushAggregate", (getEventStore) => {
|
||||
it("should successfully commit pending aggregate events to the event store", async () => {
|
||||
const { store } = await getEventStore();
|
||||
|
||||
const user = store
|
||||
.aggregate("user")
|
||||
.create({ given: "Jane", family: "Doe" }, "jane.doe@fixture.none")
|
||||
const user = store.aggregate
|
||||
.from(User)
|
||||
.setGivenName("Jane")
|
||||
.setFamilyName("Doe")
|
||||
.setEmail("jane.doe@fixture.none", "admin")
|
||||
.setGivenName("John")
|
||||
.setEmail("john.doe@fixture.none", "admin");
|
||||
|
||||
assertEquals(user.toPending().length, 3);
|
||||
assertEquals(user.toPending().length, 5);
|
||||
|
||||
await store.pushAggregate(user);
|
||||
await user.save();
|
||||
|
||||
assertEquals(user.toPending().length, 0);
|
||||
|
||||
const records = await store.getEventsByStreams([user.id]);
|
||||
|
||||
assertEquals(records.length, 3);
|
||||
assertEquals(records.length, 5);
|
||||
|
||||
assertObjectMatch(records[0], {
|
||||
stream: user.id,
|
||||
data: { name: { given: "Jane", family: "Doe" }, email: "jane.doe@fixture.none" },
|
||||
});
|
||||
assertObjectMatch(records[1], { stream: user.id, data: "John" });
|
||||
assertObjectMatch(records[2], { stream: user.id, data: "john.doe@fixture.none", meta: { auditor: "admin" } });
|
||||
assertObjectMatch(records[0], { stream: user.id, data: "Jane" });
|
||||
assertObjectMatch(records[1], { stream: user.id, data: "Doe" });
|
||||
assertObjectMatch(records[2], { stream: user.id, data: "jane.doe@fixture.none", meta: { auditor: "admin" } });
|
||||
assertObjectMatch(records[3], { stream: user.id, data: "John" });
|
||||
assertObjectMatch(records[4], { stream: user.id, data: "john.doe@fixture.none", meta: { auditor: "admin" } });
|
||||
|
||||
const state = await store.reduce({ name: "user", stream: user.id, reducer: userReducer });
|
||||
|
||||
|
||||
@@ -1,50 +1,53 @@
|
||||
import { assertEquals, assertObjectMatch } from "@std/assert";
|
||||
import { it } from "@std/testing/bdd";
|
||||
|
||||
import type { EventStoreFactory } from "../mocks/events.ts";
|
||||
import { User } from "../mocks/aggregates.ts";
|
||||
import type { Events } from "../mocks/events.ts";
|
||||
import { userReducer } from "../mocks/user-reducer.ts";
|
||||
import { describe } from "../utilities/describe.ts";
|
||||
|
||||
export default describe<EventStoreFactory>(".pushManyAggregates", (getEventStore) => {
|
||||
export default describe<Events>(".pushManyAggregates", (getEventStore) => {
|
||||
it("should successfully commit pending aggregates events to the event store", async () => {
|
||||
const { store } = await getEventStore();
|
||||
|
||||
const userA = store
|
||||
.aggregate("user")
|
||||
.create({ given: "Jane", family: "Doe" }, "jane.doe@fixture.none")
|
||||
const userA = store.aggregate
|
||||
.from(User)
|
||||
.setGivenName("Jane")
|
||||
.setFamilyName("Doe")
|
||||
.setEmail("jane.doe@fixture.none", "admin")
|
||||
.setGivenName("John")
|
||||
.setEmail("john.doe@fixture.none", "admin");
|
||||
|
||||
const userB = store
|
||||
.aggregate("user")
|
||||
.create({ given: "Peter", family: "Doe" }, "peter.doe@fixture.none")
|
||||
const userB = store.aggregate
|
||||
.from(User)
|
||||
.setGivenName("Peter")
|
||||
.setFamilyName("Doe")
|
||||
.setEmail("peter.doe@fixture.none", "admin")
|
||||
.setGivenName("Barry")
|
||||
.setEmail("barry.doe@fixture.none", "admin");
|
||||
|
||||
assertEquals(userA.toPending().length, 3);
|
||||
assertEquals(userB.toPending().length, 3);
|
||||
assertEquals(userA.toPending().length, 5);
|
||||
assertEquals(userB.toPending().length, 5);
|
||||
|
||||
await store.pushManyAggregates([userA, userB]);
|
||||
await store.aggregate.push([userA, userB]);
|
||||
|
||||
assertEquals(userA.toPending().length, 0);
|
||||
assertEquals(userB.toPending().length, 0);
|
||||
|
||||
const records = await store.getEventsByStreams([userA.id, userB.id]);
|
||||
|
||||
assertEquals(records.length, 6);
|
||||
assertEquals(records.length, 10);
|
||||
|
||||
assertObjectMatch(records[0], {
|
||||
stream: userA.id,
|
||||
data: { name: { given: "Jane", family: "Doe" }, email: "jane.doe@fixture.none" },
|
||||
});
|
||||
assertObjectMatch(records[1], { stream: userA.id, data: "John" });
|
||||
assertObjectMatch(records[2], { stream: userA.id, data: "john.doe@fixture.none", meta: { auditor: "admin" } });
|
||||
assertObjectMatch(records[3], {
|
||||
stream: userB.id,
|
||||
data: { name: { given: "Peter", family: "Doe" }, email: "peter.doe@fixture.none" },
|
||||
});
|
||||
assertObjectMatch(records[4], { stream: userB.id, data: "Barry" });
|
||||
assertObjectMatch(records[5], { stream: userB.id, data: "barry.doe@fixture.none", meta: { auditor: "admin" } });
|
||||
assertObjectMatch(records[0], { stream: userA.id, data: "Jane" });
|
||||
assertObjectMatch(records[1], { stream: userA.id, data: "Doe" });
|
||||
assertObjectMatch(records[2], { stream: userA.id, data: "jane.doe@fixture.none", meta: { auditor: "admin" } });
|
||||
assertObjectMatch(records[3], { stream: userA.id, data: "John" });
|
||||
assertObjectMatch(records[4], { stream: userA.id, data: "john.doe@fixture.none", meta: { auditor: "admin" } });
|
||||
assertObjectMatch(records[5], { stream: userB.id, data: "Peter" });
|
||||
assertObjectMatch(records[6], { stream: userB.id, data: "Doe" });
|
||||
assertObjectMatch(records[7], { stream: userB.id, data: "peter.doe@fixture.none", meta: { auditor: "admin" } });
|
||||
assertObjectMatch(records[8], { stream: userB.id, data: "Barry" });
|
||||
assertObjectMatch(records[9], { stream: userB.id, data: "barry.doe@fixture.none", meta: { auditor: "admin" } });
|
||||
|
||||
const stateA = await store.reduce({ name: "user", stream: userA.id, reducer: userReducer });
|
||||
const stateB = await store.reduce({ name: "user", stream: userB.id, reducer: userReducer });
|
||||
|
||||
@@ -2,11 +2,11 @@ import { assertEquals } from "@std/assert";
|
||||
import { it } from "@std/testing/bdd";
|
||||
import { nanoid } from "nanoid";
|
||||
|
||||
import type { EventStoreFactory } from "../mocks/events.ts";
|
||||
import type { Events } from "../mocks/events.ts";
|
||||
import { userReducer } from "../mocks/user-reducer.ts";
|
||||
import { describe } from "../utilities/describe.ts";
|
||||
|
||||
export default describe<EventStoreFactory>(".reduce", (getEventStore) => {
|
||||
export default describe<Events>(".reduce", (getEventStore) => {
|
||||
it("should return reduced state", async () => {
|
||||
const { store } = await getEventStore();
|
||||
const stream = nanoid();
|
||||
|
||||
@@ -2,10 +2,10 @@ import { assertObjectMatch } from "@std/assert";
|
||||
import { it } from "@std/testing/bdd";
|
||||
import { nanoid } from "nanoid";
|
||||
|
||||
import { EventStoreFactory } from "../mocks/events.ts";
|
||||
import type { Events } from "../mocks/events.ts";
|
||||
import { describe } from "../utilities/describe.ts";
|
||||
|
||||
export default describe<EventStoreFactory>(".replayEvents", (getEventStore) => {
|
||||
export default describe<Events>(".replayEvents", (getEventStore) => {
|
||||
it("should replay events", async () => {
|
||||
const { store, projector } = await getEventStore();
|
||||
const stream = nanoid();
|
||||
|
||||
@@ -14,6 +14,6 @@ export function describe<TEventFactory extends EventFactory>(
|
||||
type EventStoreFn<TEventFactory extends EventFactory> = (options?: {
|
||||
hooks?: EventStoreHooks<TEventFactory>;
|
||||
}) => Promise<{
|
||||
store: EventStore<TEventFactory, any, any>;
|
||||
store: EventStore<TEventFactory, any>;
|
||||
projector: Projector<TEventFactory>;
|
||||
}>;
|
||||
|
||||
Reference in New Issue
Block a user