@woltz/rich-domain 1.2.4 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/aggregate-changes.d.ts +56 -14
- package/dist/aggregate-changes.d.ts.map +1 -1
- package/dist/aggregate-changes.js +102 -22
- package/dist/aggregate-changes.js.map +1 -1
- package/dist/base-entity.d.ts +1 -1
- package/dist/base-entity.d.ts.map +1 -1
- package/dist/base-entity.js +11 -8
- package/dist/base-entity.js.map +1 -1
- package/dist/change-tracker.d.ts +1 -0
- package/dist/change-tracker.d.ts.map +1 -1
- package/dist/change-tracker.js +41 -27
- package/dist/change-tracker.js.map +1 -1
- package/dist/criteria.d.ts +7 -15
- package/dist/criteria.d.ts.map +1 -1
- package/dist/criteria.js +99 -76
- package/dist/criteria.js.map +1 -1
- package/dist/entity-schema-registry.d.ts +133 -3
- package/dist/entity-schema-registry.d.ts.map +1 -1
- package/dist/entity-schema-registry.js +155 -4
- package/dist/entity-schema-registry.js.map +1 -1
- package/dist/paginated-result.d.ts +4 -4
- package/dist/paginated-result.d.ts.map +1 -1
- package/dist/paginated-result.js +6 -20
- package/dist/paginated-result.js.map +1 -1
- package/dist/types/change-tracker.d.ts +30 -0
- package/dist/types/change-tracker.d.ts.map +1 -1
- package/dist/types/criteria.d.ts +1 -4
- package/dist/types/criteria.d.ts.map +1 -1
- package/dist/types/domain.d.ts +2 -0
- package/dist/types/domain.d.ts.map +1 -1
- package/dist/types/utils.d.ts +2 -2
- package/dist/utils/helpers.d.ts +1 -0
- package/dist/utils/helpers.d.ts.map +1 -1
- package/dist/utils/helpers.js +23 -0
- package/dist/utils/helpers.js.map +1 -1
- package/dist/value-object.d.ts +1 -1
- package/dist/value-object.js +1 -1
- package/package.json +17 -3
- package/src/aggregate-changes.ts +133 -24
- package/src/base-entity.ts +13 -8
- package/src/change-tracker.ts +107 -38
- package/src/criteria.ts +151 -109
- package/src/entity-schema-registry.ts +253 -6
- package/src/paginated-result.ts +10 -30
- package/src/types/change-tracker.ts +31 -0
- package/src/types/criteria.ts +1 -4
- package/src/types/domain.ts +2 -0
- package/src/types/utils.ts +2 -2
- package/src/utils/helpers.ts +28 -0
- package/src/value-object.ts +1 -1
- package/.versionrc.json +0 -21
- package/CHANGELOG.md +0 -163
- package/tests/aggregate-changes.test.ts +0 -284
- package/tests/criteria.test.ts +0 -716
- package/tests/depth/deep-tracking.test.ts +0 -554
- package/tests/domain-events.test.ts +0 -431
- package/tests/entity-equality.test.ts +0 -464
- package/tests/entity-schema-registry.test.ts +0 -382
- package/tests/entity-validation.test.ts +0 -252
- package/tests/history-tracker.spec.ts +0 -439
- package/tests/id.test.ts +0 -338
- package/tests/load-test/data.json +0 -347211
- package/tests/load-test/entities.ts +0 -97
- package/tests/load-test/generate-data.ts +0 -81
- package/tests/load-test/lead-to-domain.mapper.ts +0 -24
- package/tests/load-test/load.test.ts +0 -38
- package/tests/repository.test.ts +0 -635
- package/tests/to-json.test.ts +0 -99
- package/tests/utils.ts +0 -290
- package/tests/value-object-validation.test.ts +0 -219
- package/tests/value-objects.test.ts +0 -80
- package/tsconfig.json +0 -9
|
@@ -1,431 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
Id,
|
|
3
|
-
Aggregate,
|
|
4
|
-
DomainEvent,
|
|
5
|
-
DomainEventBus,
|
|
6
|
-
IDomainEventHandler,
|
|
7
|
-
} from "../src";
|
|
8
|
-
import { BaseProps } from "../src/types";
|
|
9
|
-
|
|
10
|
-
// ============================================================================
|
|
11
|
-
// Test Events
|
|
12
|
-
// ============================================================================
|
|
13
|
-
|
|
14
|
-
class UserCreatedEvent extends DomainEvent {
|
|
15
|
-
constructor(
|
|
16
|
-
aggregateId: Id,
|
|
17
|
-
public readonly email: string,
|
|
18
|
-
public readonly name: string
|
|
19
|
-
) {
|
|
20
|
-
super(aggregateId);
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
protected getPayload() {
|
|
24
|
-
return {
|
|
25
|
-
email: this.email,
|
|
26
|
-
name: this.name,
|
|
27
|
-
};
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
class UserActivatedEvent extends DomainEvent {
|
|
32
|
-
constructor(aggregateId: Id) {
|
|
33
|
-
super(aggregateId);
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
class UserDeactivatedEvent extends DomainEvent {
|
|
38
|
-
constructor(aggregateId: Id) {
|
|
39
|
-
super(aggregateId);
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
class OrderPlacedEvent extends DomainEvent {
|
|
44
|
-
constructor(aggregateId: Id, public readonly total: number) {
|
|
45
|
-
super(aggregateId);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
protected getPayload() {
|
|
49
|
-
return {
|
|
50
|
-
total: this.total,
|
|
51
|
-
};
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
// ============================================================================
|
|
56
|
-
// Test Aggregates
|
|
57
|
-
// ============================================================================
|
|
58
|
-
|
|
59
|
-
interface UserProps extends BaseProps {
|
|
60
|
-
id: Id;
|
|
61
|
-
email: string;
|
|
62
|
-
name: string;
|
|
63
|
-
status: "active" | "inactive";
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
class User extends Aggregate<UserProps> {
|
|
67
|
-
get email(): string {
|
|
68
|
-
return this.props.email;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
get name(): string {
|
|
72
|
-
return this.props.name;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
get status(): string {
|
|
76
|
-
return this.props.status;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
static create(email: string, name: string): User {
|
|
80
|
-
const user = new User({ email, name, status: "inactive" });
|
|
81
|
-
user.addDomainEvent(new UserCreatedEvent(user.id, email, name));
|
|
82
|
-
return user;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
activate(): void {
|
|
86
|
-
this.props.status = "active";
|
|
87
|
-
this.addDomainEvent(new UserActivatedEvent(this.id));
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
deactivate(): void {
|
|
91
|
-
this.props.status = "inactive";
|
|
92
|
-
this.addDomainEvent(new UserDeactivatedEvent(this.id));
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
describe("Domain Events", () => {
|
|
97
|
-
let eventBus: DomainEventBus;
|
|
98
|
-
|
|
99
|
-
beforeEach(() => {
|
|
100
|
-
eventBus = DomainEventBus.getInstance();
|
|
101
|
-
eventBus.clearAllHandlers();
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
afterEach(() => {
|
|
105
|
-
eventBus.clearAllHandlers();
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
describe("DomainEvent", () => {
|
|
109
|
-
it("should create event with auto-generated ID", () => {
|
|
110
|
-
const userId = new Id();
|
|
111
|
-
const event = new UserCreatedEvent(
|
|
112
|
-
userId,
|
|
113
|
-
"test@example.com",
|
|
114
|
-
"Test User"
|
|
115
|
-
);
|
|
116
|
-
|
|
117
|
-
expect(event.eventId).toBeDefined();
|
|
118
|
-
expect(event.eventName).toBe("UserCreatedEvent");
|
|
119
|
-
expect(event.aggregateId).toBe(userId.value);
|
|
120
|
-
expect(event.occurredOn).toBeInstanceOf(Date);
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
it("should serialize event to JSON", () => {
|
|
124
|
-
const userId = new Id("user-123");
|
|
125
|
-
const event = new UserCreatedEvent(
|
|
126
|
-
userId,
|
|
127
|
-
"test@example.com",
|
|
128
|
-
"Test User"
|
|
129
|
-
);
|
|
130
|
-
|
|
131
|
-
const json = event.toJSON();
|
|
132
|
-
|
|
133
|
-
expect(json.eventId).toBeDefined();
|
|
134
|
-
expect(json.eventName).toBe("UserCreatedEvent");
|
|
135
|
-
expect(json.aggregateId).toBe("user-123");
|
|
136
|
-
expect(json.email).toBe("test@example.com");
|
|
137
|
-
expect(json.name).toBe("Test User");
|
|
138
|
-
expect(json.occurredOn).toBeDefined();
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
it("should accept Id.from as aggregateId", () => {
|
|
142
|
-
const event = new UserCreatedEvent(
|
|
143
|
-
Id.from("user-123"),
|
|
144
|
-
"test@example.com",
|
|
145
|
-
"Test"
|
|
146
|
-
);
|
|
147
|
-
|
|
148
|
-
expect(event.aggregateId).toBe("user-123");
|
|
149
|
-
});
|
|
150
|
-
});
|
|
151
|
-
|
|
152
|
-
describe("Aggregate with Events", () => {
|
|
153
|
-
it("should add events to aggregate", () => {
|
|
154
|
-
const user = User.create("test@example.com", "Test User");
|
|
155
|
-
|
|
156
|
-
expect(user.hasUncommittedEvents()).toBe(true);
|
|
157
|
-
expect(user.getUncommittedEvents().length).toBe(1);
|
|
158
|
-
});
|
|
159
|
-
|
|
160
|
-
it("should get uncommitted events", () => {
|
|
161
|
-
const user = User.create("test@example.com", "Test User");
|
|
162
|
-
const events = user.getUncommittedEvents();
|
|
163
|
-
|
|
164
|
-
expect(events[0]).toBeInstanceOf(UserCreatedEvent);
|
|
165
|
-
expect(events[0].eventName).toBe("UserCreatedEvent");
|
|
166
|
-
});
|
|
167
|
-
|
|
168
|
-
it("should accumulate multiple events", () => {
|
|
169
|
-
const user = User.create("test@example.com", "Test User");
|
|
170
|
-
user.activate();
|
|
171
|
-
user.deactivate();
|
|
172
|
-
|
|
173
|
-
expect(user.getUncommittedEvents().length).toBe(3);
|
|
174
|
-
expect(user.getUncommittedEvents()[0]).toBeInstanceOf(UserCreatedEvent);
|
|
175
|
-
expect(user.getUncommittedEvents()[1]).toBeInstanceOf(UserActivatedEvent);
|
|
176
|
-
expect(user.getUncommittedEvents()[2]).toBeInstanceOf(
|
|
177
|
-
UserDeactivatedEvent
|
|
178
|
-
);
|
|
179
|
-
});
|
|
180
|
-
|
|
181
|
-
it("should clear events", () => {
|
|
182
|
-
const user = User.create("test@example.com", "Test User");
|
|
183
|
-
|
|
184
|
-
expect(user.hasUncommittedEvents()).toBe(true);
|
|
185
|
-
|
|
186
|
-
user.clearEvents();
|
|
187
|
-
|
|
188
|
-
expect(user.hasUncommittedEvents()).toBe(false);
|
|
189
|
-
expect(user.getUncommittedEvents().length).toBe(0);
|
|
190
|
-
});
|
|
191
|
-
});
|
|
192
|
-
|
|
193
|
-
describe("DomainEventBus", () => {
|
|
194
|
-
it("should get singleton instance", () => {
|
|
195
|
-
const bus1 = DomainEventBus.getInstance();
|
|
196
|
-
const bus2 = DomainEventBus.getInstance();
|
|
197
|
-
|
|
198
|
-
expect(bus1).toBe(bus2);
|
|
199
|
-
});
|
|
200
|
-
|
|
201
|
-
it("should subscribe and publish events with function handler", async () => {
|
|
202
|
-
const handler = jest.fn();
|
|
203
|
-
|
|
204
|
-
eventBus.subscribe({ event: UserCreatedEvent, handler });
|
|
205
|
-
|
|
206
|
-
const event = new UserCreatedEvent(new Id(), "test@example.com", "Test");
|
|
207
|
-
await eventBus.publish(event);
|
|
208
|
-
|
|
209
|
-
expect(handler).toHaveBeenCalledTimes(1);
|
|
210
|
-
expect(handler).toHaveBeenCalledWith(event);
|
|
211
|
-
});
|
|
212
|
-
|
|
213
|
-
it("should subscribe and publish events with class handler", async () => {
|
|
214
|
-
const handleFn = jest.fn();
|
|
215
|
-
|
|
216
|
-
class UserCreatedHandler
|
|
217
|
-
implements IDomainEventHandler<UserCreatedEvent>
|
|
218
|
-
{
|
|
219
|
-
async handle(event: UserCreatedEvent): Promise<void> {
|
|
220
|
-
handleFn(event);
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
const handler = new UserCreatedHandler();
|
|
225
|
-
eventBus.subscribe({ event: UserCreatedEvent, handler });
|
|
226
|
-
|
|
227
|
-
const event = new UserCreatedEvent(new Id(), "test@example.com", "Test");
|
|
228
|
-
await eventBus.publish(event);
|
|
229
|
-
|
|
230
|
-
expect(handleFn).toHaveBeenCalledTimes(1);
|
|
231
|
-
});
|
|
232
|
-
|
|
233
|
-
it("should subscribe by event name string", async () => {
|
|
234
|
-
const handler = jest.fn();
|
|
235
|
-
|
|
236
|
-
eventBus.subscribe({ event: "UserCreatedEvent", handler });
|
|
237
|
-
|
|
238
|
-
const event = new UserCreatedEvent(new Id(), "test@example.com", "Test");
|
|
239
|
-
await eventBus.publish(event);
|
|
240
|
-
|
|
241
|
-
expect(handler).toHaveBeenCalledTimes(1);
|
|
242
|
-
});
|
|
243
|
-
|
|
244
|
-
it("should handle multiple subscribers for same event", async () => {
|
|
245
|
-
const handler1 = jest.fn();
|
|
246
|
-
const handler2 = jest.fn();
|
|
247
|
-
const handler3 = jest.fn();
|
|
248
|
-
|
|
249
|
-
eventBus.subscribe({ event: UserCreatedEvent, handler: handler1 });
|
|
250
|
-
eventBus.subscribe({ event: UserCreatedEvent, handler: handler2 });
|
|
251
|
-
eventBus.subscribe({ event: UserCreatedEvent, handler: handler3 });
|
|
252
|
-
|
|
253
|
-
const event = new UserCreatedEvent(new Id(), "test@example.com", "Test");
|
|
254
|
-
await eventBus.publish(event);
|
|
255
|
-
|
|
256
|
-
expect(handler1).toHaveBeenCalledTimes(1);
|
|
257
|
-
expect(handler2).toHaveBeenCalledTimes(1);
|
|
258
|
-
expect(handler3).toHaveBeenCalledTimes(1);
|
|
259
|
-
});
|
|
260
|
-
|
|
261
|
-
it("should only trigger subscribers for specific event type", async () => {
|
|
262
|
-
const userHandler = jest.fn();
|
|
263
|
-
const orderHandler = jest.fn();
|
|
264
|
-
|
|
265
|
-
eventBus.subscribe({ event: UserCreatedEvent, handler: userHandler });
|
|
266
|
-
eventBus.subscribe({ event: OrderPlacedEvent, handler: orderHandler });
|
|
267
|
-
|
|
268
|
-
const userEvent = new UserCreatedEvent(
|
|
269
|
-
new Id(),
|
|
270
|
-
"test@example.com",
|
|
271
|
-
"Test"
|
|
272
|
-
);
|
|
273
|
-
await eventBus.publish(userEvent);
|
|
274
|
-
|
|
275
|
-
expect(userHandler).toHaveBeenCalledTimes(1);
|
|
276
|
-
expect(orderHandler).not.toHaveBeenCalled();
|
|
277
|
-
});
|
|
278
|
-
|
|
279
|
-
it("should subscribe to all events with wildcard", async () => {
|
|
280
|
-
const wildcardHandler = jest.fn();
|
|
281
|
-
|
|
282
|
-
eventBus.subscribeAll(wildcardHandler);
|
|
283
|
-
|
|
284
|
-
const event1 = new UserCreatedEvent(new Id(), "test@example.com", "Test");
|
|
285
|
-
const event2 = new OrderPlacedEvent(new Id(), 100);
|
|
286
|
-
|
|
287
|
-
await eventBus.publish(event1);
|
|
288
|
-
await eventBus.publish(event2);
|
|
289
|
-
|
|
290
|
-
expect(wildcardHandler).toHaveBeenCalledTimes(2);
|
|
291
|
-
});
|
|
292
|
-
|
|
293
|
-
it("should trigger both specific and wildcard handlers", async () => {
|
|
294
|
-
const specificHandler = jest.fn();
|
|
295
|
-
const wildcardHandler = jest.fn();
|
|
296
|
-
|
|
297
|
-
eventBus.subscribe({ event: UserCreatedEvent, handler: specificHandler });
|
|
298
|
-
eventBus.subscribeAll(wildcardHandler);
|
|
299
|
-
|
|
300
|
-
const event = new UserCreatedEvent(new Id(), "test@example.com", "Test");
|
|
301
|
-
await eventBus.publish(event);
|
|
302
|
-
|
|
303
|
-
expect(specificHandler).toHaveBeenCalledTimes(1);
|
|
304
|
-
expect(wildcardHandler).toHaveBeenCalledTimes(1);
|
|
305
|
-
});
|
|
306
|
-
|
|
307
|
-
it("should unsubscribe handler", async () => {
|
|
308
|
-
const handler = jest.fn();
|
|
309
|
-
|
|
310
|
-
eventBus.subscribe({ event: UserCreatedEvent, handler });
|
|
311
|
-
eventBus.unsubscribe(UserCreatedEvent, handler);
|
|
312
|
-
|
|
313
|
-
const event = new UserCreatedEvent(new Id(), "test@example.com", "Test");
|
|
314
|
-
await eventBus.publish(event);
|
|
315
|
-
|
|
316
|
-
expect(handler).not.toHaveBeenCalled();
|
|
317
|
-
});
|
|
318
|
-
|
|
319
|
-
it("should unsubscribe wildcard handler", async () => {
|
|
320
|
-
const handler = jest.fn();
|
|
321
|
-
|
|
322
|
-
eventBus.subscribeAll(handler);
|
|
323
|
-
eventBus.unsubscribeAll(handler);
|
|
324
|
-
|
|
325
|
-
const event = new UserCreatedEvent(new Id(), "test@example.com", "Test");
|
|
326
|
-
await eventBus.publish(event);
|
|
327
|
-
|
|
328
|
-
expect(handler).not.toHaveBeenCalled();
|
|
329
|
-
});
|
|
330
|
-
|
|
331
|
-
it("should publish multiple events", async () => {
|
|
332
|
-
const handler = jest.fn();
|
|
333
|
-
|
|
334
|
-
eventBus.subscribe({ event: UserCreatedEvent, handler });
|
|
335
|
-
eventBus.subscribe({ event: UserActivatedEvent, handler });
|
|
336
|
-
|
|
337
|
-
const events = [
|
|
338
|
-
new UserCreatedEvent(new Id(), "test@example.com", "Test"),
|
|
339
|
-
new UserActivatedEvent(new Id()),
|
|
340
|
-
];
|
|
341
|
-
|
|
342
|
-
await eventBus.publishAll(events);
|
|
343
|
-
|
|
344
|
-
expect(handler).toHaveBeenCalledTimes(2);
|
|
345
|
-
});
|
|
346
|
-
|
|
347
|
-
it("should get handler count", () => {
|
|
348
|
-
eventBus.subscribe({ event: UserCreatedEvent, handler: jest.fn() });
|
|
349
|
-
eventBus.subscribe({ event: UserCreatedEvent, handler: jest.fn() });
|
|
350
|
-
eventBus.subscribe({ event: OrderPlacedEvent, handler: jest.fn() });
|
|
351
|
-
|
|
352
|
-
expect(eventBus.getHandlerCount(UserCreatedEvent)).toBe(2);
|
|
353
|
-
expect(eventBus.getHandlerCount(OrderPlacedEvent)).toBe(1);
|
|
354
|
-
expect(eventBus.getHandlerCount(UserActivatedEvent)).toBe(0);
|
|
355
|
-
});
|
|
356
|
-
|
|
357
|
-
it("should handle async handlers", async () => {
|
|
358
|
-
const results: string[] = [];
|
|
359
|
-
|
|
360
|
-
const asyncHandler = async (event: UserCreatedEvent) => {
|
|
361
|
-
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
362
|
-
results.push(`Handled: ${event.email}`);
|
|
363
|
-
};
|
|
364
|
-
|
|
365
|
-
eventBus.subscribe({ event: UserCreatedEvent, handler: asyncHandler });
|
|
366
|
-
|
|
367
|
-
const event = new UserCreatedEvent(new Id(), "test@example.com", "Test");
|
|
368
|
-
await eventBus.publish(event);
|
|
369
|
-
|
|
370
|
-
expect(results).toContain("Handled: test@example.com");
|
|
371
|
-
});
|
|
372
|
-
|
|
373
|
-
it("should not throw if handler throws error", async () => {
|
|
374
|
-
const errorHandler = jest.fn(() => {
|
|
375
|
-
throw new Error("Handler error");
|
|
376
|
-
});
|
|
377
|
-
const goodHandler = jest.fn();
|
|
378
|
-
|
|
379
|
-
eventBus.subscribe({ event: UserCreatedEvent, handler: errorHandler });
|
|
380
|
-
eventBus.subscribe({ event: UserCreatedEvent, handler: goodHandler });
|
|
381
|
-
|
|
382
|
-
const event = new UserCreatedEvent(new Id(), "test@example.com", "Test");
|
|
383
|
-
|
|
384
|
-
// Should not throw
|
|
385
|
-
await expect(eventBus.publish(event)).resolves.not.toThrow();
|
|
386
|
-
|
|
387
|
-
// Both handlers should have been called
|
|
388
|
-
expect(errorHandler).toHaveBeenCalled();
|
|
389
|
-
expect(goodHandler).toHaveBeenCalled();
|
|
390
|
-
});
|
|
391
|
-
});
|
|
392
|
-
|
|
393
|
-
describe("End-to-End Workflow", () => {
|
|
394
|
-
it("should follow complete event workflow", async () => {
|
|
395
|
-
const emailsSent: string[] = [];
|
|
396
|
-
|
|
397
|
-
// Subscribe to user created events
|
|
398
|
-
eventBus.subscribe({
|
|
399
|
-
event: UserCreatedEvent,
|
|
400
|
-
handler: (event) => {
|
|
401
|
-
emailsSent.push(`Welcome email sent to ${event.email}`);
|
|
402
|
-
},
|
|
403
|
-
});
|
|
404
|
-
|
|
405
|
-
// Subscribe to user activated events
|
|
406
|
-
eventBus.subscribe({
|
|
407
|
-
event: UserActivatedEvent,
|
|
408
|
-
handler: async (event) => {
|
|
409
|
-
emailsSent.push(
|
|
410
|
-
`Activation email sent for user ${event.aggregateId}`
|
|
411
|
-
);
|
|
412
|
-
},
|
|
413
|
-
});
|
|
414
|
-
|
|
415
|
-
// Create and activate user
|
|
416
|
-
const user = User.create("john@example.com", "John Doe");
|
|
417
|
-
user.activate();
|
|
418
|
-
|
|
419
|
-
// Get events and publish them
|
|
420
|
-
await user.dispatchAll(eventBus);
|
|
421
|
-
|
|
422
|
-
// Clear events after publishing
|
|
423
|
-
user.clearEvents();
|
|
424
|
-
|
|
425
|
-
expect(emailsSent).toHaveLength(2);
|
|
426
|
-
expect(emailsSent[0]).toBe("Welcome email sent to john@example.com");
|
|
427
|
-
expect(emailsSent[1]).toContain("Activation email sent for user");
|
|
428
|
-
expect(user.hasUncommittedEvents()).toBe(false);
|
|
429
|
-
});
|
|
430
|
-
});
|
|
431
|
-
});
|