@woltz/rich-domain 1.2.4 → 1.3.1
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 +103 -23
- 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 +28 -13
- package/dist/base-entity.js.map +1 -1
- package/dist/change-tracker.d.ts +2 -1
- package/dist/change-tracker.d.ts.map +1 -1
- package/dist/change-tracker.js +61 -35
- 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 +105 -81
- package/dist/criteria.js.map +1 -1
- package/dist/domain-event-bus.js +4 -4
- package/dist/domain-event-bus.js.map +1 -1
- package/dist/domain-event.js +3 -0
- package/dist/domain-event.js.map +1 -1
- package/dist/entity-changes.js +1 -0
- package/dist/entity-changes.js.map +1 -1
- package/dist/entity-schema-registry.d.ts +137 -3
- package/dist/entity-schema-registry.d.ts.map +1 -1
- package/dist/entity-schema-registry.js +160 -7
- package/dist/entity-schema-registry.js.map +1 -1
- package/dist/exceptions.js +26 -1
- package/dist/exceptions.js.map +1 -1
- package/dist/id.js +2 -0
- package/dist/id.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.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 +14 -19
- package/dist/paginated-result.js.map +1 -1
- package/dist/repository/unit-of-work.js +3 -7
- package/dist/repository/unit-of-work.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 -1
- 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/validation-error.d.ts +15 -1
- package/dist/validation-error.d.ts.map +1 -1
- package/dist/validation-error.js +46 -3
- package/dist/validation-error.js.map +1 -1
- package/dist/value-object.d.ts +1 -1
- package/dist/value-object.d.ts.map +1 -1
- package/dist/value-object.js +30 -2
- package/dist/value-object.js.map +1 -1
- package/package.json +17 -3
- package/src/aggregate-changes.ts +133 -24
- package/src/base-entity.ts +22 -11
- package/src/change-tracker.ts +113 -54
- package/src/criteria.ts +151 -109
- package/src/entity-schema-registry.ts +256 -6
- package/src/index.ts +1 -1
- package/src/paginated-result.ts +21 -29
- package/src/types/change-tracker.ts +31 -0
- package/src/types/criteria.ts +1 -4
- package/src/types/domain.ts +2 -1
- package/src/types/utils.ts +2 -2
- package/src/utils/helpers.ts +28 -0
- package/src/validation-error.ts +54 -4
- package/src/value-object.ts +6 -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
package/tests/repository.test.ts
DELETED
|
@@ -1,635 +0,0 @@
|
|
|
1
|
-
import { Id, Aggregate, Criteria, PaginatedResult } from "../src";
|
|
2
|
-
import { Mapper } from "../src/repository";
|
|
3
|
-
import { Repository } from "../src/repository/base-repository";
|
|
4
|
-
import { BaseProps } from "../src/types";
|
|
5
|
-
import { InMemoryRepository } from "./utils";
|
|
6
|
-
|
|
7
|
-
interface UserProps extends BaseProps {
|
|
8
|
-
name: string;
|
|
9
|
-
email: string;
|
|
10
|
-
age: number;
|
|
11
|
-
status: "active" | "inactive";
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
class User extends Aggregate<UserProps> {
|
|
15
|
-
get name() {
|
|
16
|
-
return this.props.name;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
get email() {
|
|
20
|
-
return this.props.email;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
get age() {
|
|
24
|
-
return this.props.age;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
get status() {
|
|
28
|
-
return this.props.status;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
setName(name: string) {
|
|
32
|
-
this.props.name = name;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
activate() {
|
|
36
|
-
this.props.status = "active";
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
deactivate() {
|
|
40
|
-
this.props.status = "inactive";
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
static create(props: Omit<UserProps, "id"> & { id?: Id }): User {
|
|
44
|
-
return new User({
|
|
45
|
-
id: props.id || new Id(),
|
|
46
|
-
name: props.name,
|
|
47
|
-
email: props.email,
|
|
48
|
-
age: props.age,
|
|
49
|
-
status: props.status,
|
|
50
|
-
});
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
type UserPersistence = {
|
|
55
|
-
id: string;
|
|
56
|
-
name: string;
|
|
57
|
-
email: string;
|
|
58
|
-
age: number;
|
|
59
|
-
status: string;
|
|
60
|
-
createdAt: Date;
|
|
61
|
-
updatedAt: Date;
|
|
62
|
-
};
|
|
63
|
-
|
|
64
|
-
class UserToDomainMapper extends Mapper<UserPersistence, User> {
|
|
65
|
-
public build(persistence: UserPersistence): User {
|
|
66
|
-
return User.create({
|
|
67
|
-
id: Id.from(persistence.id),
|
|
68
|
-
name: persistence.name,
|
|
69
|
-
email: persistence.email,
|
|
70
|
-
age: persistence.age,
|
|
71
|
-
status: persistence.status as "active" | "inactive",
|
|
72
|
-
});
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
class UserToPersistenceMapper extends Mapper<User, UserPersistence> {
|
|
77
|
-
public build(domain: User): UserPersistence {
|
|
78
|
-
return {
|
|
79
|
-
id: domain.id.value,
|
|
80
|
-
name: domain.name,
|
|
81
|
-
email: domain.email,
|
|
82
|
-
age: domain.age,
|
|
83
|
-
status: domain.status,
|
|
84
|
-
createdAt: new Date(),
|
|
85
|
-
updatedAt: new Date(),
|
|
86
|
-
};
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
class MockUserRepository extends Repository<User> {
|
|
91
|
-
private store: Map<string, UserPersistence> = new Map();
|
|
92
|
-
|
|
93
|
-
constructor(
|
|
94
|
-
protected readonly mapperToDomain: Mapper<UserPersistence, User>,
|
|
95
|
-
protected readonly mapperToPersistence: Mapper<User, UserPersistence>
|
|
96
|
-
) {
|
|
97
|
-
super();
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
get model() {
|
|
101
|
-
return "users";
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
async save(entity: User) {
|
|
105
|
-
const persistence = await this.mapperToPersistence.build(entity);
|
|
106
|
-
this.store.set(entity.id.value, persistence);
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
async delete(entity: User): Promise<void> {
|
|
110
|
-
this.store.delete(entity.id.value);
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
async count(criteria: Criteria<User>): Promise<number> {
|
|
114
|
-
return this.applyCriteria(criteria).then((result) => result.meta.total);
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
async exists(id: string): Promise<boolean> {
|
|
118
|
-
return this.store.has(id);
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
async find(criteria: Criteria<User>): Promise<PaginatedResult<User>> {
|
|
122
|
-
const result = await this.applyCriteria(criteria);
|
|
123
|
-
return result as PaginatedResult<User>;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
async findById(id: string): Promise<User | null> {
|
|
127
|
-
const persistence = this.store.get(id);
|
|
128
|
-
if (!persistence) return null;
|
|
129
|
-
return await this.mapperToDomain.build(persistence);
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
protected async applyCriteria(criteria: Criteria<User>) {
|
|
133
|
-
let results = Array.from(this.store.values());
|
|
134
|
-
|
|
135
|
-
// Apply filters
|
|
136
|
-
for (const filter of criteria.getFilters()) {
|
|
137
|
-
results = results.filter((item: any) => {
|
|
138
|
-
const value = item[filter.field];
|
|
139
|
-
switch (filter.operator) {
|
|
140
|
-
case "equals":
|
|
141
|
-
return value === filter.value;
|
|
142
|
-
case "greaterThan":
|
|
143
|
-
return value > (filter.value as any);
|
|
144
|
-
case "lessThan":
|
|
145
|
-
return value < (filter.value as any);
|
|
146
|
-
case "contains":
|
|
147
|
-
return String(value)
|
|
148
|
-
.toLowerCase()
|
|
149
|
-
.includes(String(filter.value).toLowerCase());
|
|
150
|
-
default:
|
|
151
|
-
return true;
|
|
152
|
-
}
|
|
153
|
-
});
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
const total = results.length;
|
|
157
|
-
|
|
158
|
-
// Apply ordering
|
|
159
|
-
for (const order of criteria.getOrders()) {
|
|
160
|
-
results.sort((a: any, b: any) => {
|
|
161
|
-
const aVal = a[order.field];
|
|
162
|
-
const bVal = b[order.field];
|
|
163
|
-
const comparison = aVal < bVal ? -1 : aVal > bVal ? 1 : 0;
|
|
164
|
-
return order.direction === "desc" ? -comparison : comparison;
|
|
165
|
-
});
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
// Apply pagination
|
|
169
|
-
const pagination = criteria.getPagination();
|
|
170
|
-
|
|
171
|
-
results = results.slice(
|
|
172
|
-
pagination.offset,
|
|
173
|
-
pagination.offset + pagination.limit
|
|
174
|
-
);
|
|
175
|
-
|
|
176
|
-
const domains = results.map((p) => this.mapperToDomain.build(p));
|
|
177
|
-
|
|
178
|
-
return PaginatedResult.create(domains, pagination, total);
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
protected async countByCriteria(criteria?: Criteria<User>): Promise<number> {
|
|
182
|
-
if (!criteria) {
|
|
183
|
-
return this.store.size;
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
const result = await this.applyCriteria(criteria);
|
|
187
|
-
return result.meta.total;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
protected async existsById(id: string): Promise<boolean> {
|
|
191
|
-
return this.store.has(id);
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
// Test helper
|
|
195
|
-
clear() {
|
|
196
|
-
this.store.clear();
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
describe("Repository", () => {
|
|
201
|
-
describe("InMemoryRepository", () => {
|
|
202
|
-
let repository: InMemoryRepository<User>;
|
|
203
|
-
let user1: User;
|
|
204
|
-
let user2: User;
|
|
205
|
-
let user3: User;
|
|
206
|
-
|
|
207
|
-
beforeEach(() => {
|
|
208
|
-
repository = new InMemoryRepository<User>(
|
|
209
|
-
new UserToDomainMapper(),
|
|
210
|
-
new UserToPersistenceMapper()
|
|
211
|
-
);
|
|
212
|
-
|
|
213
|
-
user1 = User.create({
|
|
214
|
-
name: "Alice",
|
|
215
|
-
email: "alice@example.com",
|
|
216
|
-
age: 25,
|
|
217
|
-
status: "active",
|
|
218
|
-
});
|
|
219
|
-
|
|
220
|
-
user2 = User.create({
|
|
221
|
-
name: "Bob",
|
|
222
|
-
email: "bob@example.com",
|
|
223
|
-
age: 30,
|
|
224
|
-
status: "active",
|
|
225
|
-
});
|
|
226
|
-
|
|
227
|
-
user3 = User.create({
|
|
228
|
-
name: "Charlie",
|
|
229
|
-
email: "charlie@example.com",
|
|
230
|
-
age: 35,
|
|
231
|
-
status: "inactive",
|
|
232
|
-
});
|
|
233
|
-
});
|
|
234
|
-
|
|
235
|
-
afterEach(() => {
|
|
236
|
-
repository.clear();
|
|
237
|
-
});
|
|
238
|
-
|
|
239
|
-
describe("save and findById", () => {
|
|
240
|
-
it("should save and retrieve user", async () => {
|
|
241
|
-
await repository.save(user1);
|
|
242
|
-
const found = await repository.findById(user1.id.value);
|
|
243
|
-
|
|
244
|
-
expect(found).toBeDefined();
|
|
245
|
-
expect(found?.id.equals(user1.id)).toBe(true);
|
|
246
|
-
expect(found?.name).toBe("Alice");
|
|
247
|
-
});
|
|
248
|
-
|
|
249
|
-
it("should return null for non-existent id", async () => {
|
|
250
|
-
const found = await repository.findById(new Id().value);
|
|
251
|
-
expect(found).toBeNull();
|
|
252
|
-
});
|
|
253
|
-
|
|
254
|
-
it("should update existing user", async () => {
|
|
255
|
-
await repository.save(user1);
|
|
256
|
-
user1.setName("Alice Updated");
|
|
257
|
-
await repository.save(user1);
|
|
258
|
-
|
|
259
|
-
const found = await repository.findById(user1.id.value);
|
|
260
|
-
expect(found?.name).toBe("Alice Updated");
|
|
261
|
-
});
|
|
262
|
-
});
|
|
263
|
-
|
|
264
|
-
describe("findAll", () => {
|
|
265
|
-
it("should return empty array when no users", async () => {
|
|
266
|
-
const users = await repository.findAll();
|
|
267
|
-
expect(users).toHaveLength(0);
|
|
268
|
-
});
|
|
269
|
-
|
|
270
|
-
it("should return all users", async () => {
|
|
271
|
-
await repository.save(user1);
|
|
272
|
-
await repository.save(user2);
|
|
273
|
-
await repository.save(user3);
|
|
274
|
-
|
|
275
|
-
const users = await repository.findAll();
|
|
276
|
-
expect(users).toHaveLength(3);
|
|
277
|
-
});
|
|
278
|
-
|
|
279
|
-
it("should return users matching criteria", async () => {
|
|
280
|
-
await repository.save(user1);
|
|
281
|
-
await repository.save(user2);
|
|
282
|
-
await repository.save(user3);
|
|
283
|
-
|
|
284
|
-
const criteria = Criteria.create<User>().whereEquals(
|
|
285
|
-
"status",
|
|
286
|
-
"active"
|
|
287
|
-
);
|
|
288
|
-
const users = await repository.findAll(criteria);
|
|
289
|
-
|
|
290
|
-
expect(users).toHaveLength(2);
|
|
291
|
-
expect(users.every((u) => u.status === "active")).toBe(true);
|
|
292
|
-
});
|
|
293
|
-
});
|
|
294
|
-
|
|
295
|
-
describe("find with Criteria", () => {
|
|
296
|
-
beforeEach(async () => {
|
|
297
|
-
await repository.save(user1);
|
|
298
|
-
await repository.save(user2);
|
|
299
|
-
await repository.save(user3);
|
|
300
|
-
});
|
|
301
|
-
|
|
302
|
-
it("should filter by equals", async () => {
|
|
303
|
-
const criteria = Criteria.create<User>().whereEquals(
|
|
304
|
-
"status",
|
|
305
|
-
"active"
|
|
306
|
-
);
|
|
307
|
-
const result = await repository.find(criteria);
|
|
308
|
-
|
|
309
|
-
expect(result.data).toHaveLength(2);
|
|
310
|
-
expect(result.meta.total).toBe(2);
|
|
311
|
-
});
|
|
312
|
-
|
|
313
|
-
it("should order by field", async () => {
|
|
314
|
-
const criteria = Criteria.create<User>().orderByDesc("age");
|
|
315
|
-
const result = await repository.find(criteria);
|
|
316
|
-
|
|
317
|
-
expect(result.data[0].age).toBe(35);
|
|
318
|
-
expect(result.data[1].age).toBe(30);
|
|
319
|
-
expect(result.data[2].age).toBe(25);
|
|
320
|
-
});
|
|
321
|
-
|
|
322
|
-
it("should paginate results", async () => {
|
|
323
|
-
const criteria = Criteria.create<User>()
|
|
324
|
-
.orderByAsc("age")
|
|
325
|
-
.paginate(1, 2);
|
|
326
|
-
|
|
327
|
-
const result = await repository.find(criteria);
|
|
328
|
-
|
|
329
|
-
expect(result.data).toHaveLength(2);
|
|
330
|
-
expect(result.meta.page).toBe(1);
|
|
331
|
-
expect(result.meta.limit).toBe(2);
|
|
332
|
-
expect(result.meta.total).toBe(3);
|
|
333
|
-
expect(result.meta.totalPages).toBe(2);
|
|
334
|
-
expect(result.meta.hasNext).toBe(true);
|
|
335
|
-
expect(result.meta.hasPrevious).toBe(false);
|
|
336
|
-
});
|
|
337
|
-
|
|
338
|
-
it("should combine filter, order, and pagination", async () => {
|
|
339
|
-
const criteria = Criteria.create<User>()
|
|
340
|
-
.whereEquals("status", "active")
|
|
341
|
-
.orderByDesc("age")
|
|
342
|
-
.paginate(1, 1);
|
|
343
|
-
|
|
344
|
-
const result = await repository.find(criteria);
|
|
345
|
-
|
|
346
|
-
expect(result.data).toHaveLength(1);
|
|
347
|
-
expect(result.data[0].age).toBe(30); // Bob (highest age among active)
|
|
348
|
-
expect(result.meta.total).toBe(2); // Total active users
|
|
349
|
-
});
|
|
350
|
-
});
|
|
351
|
-
|
|
352
|
-
describe("findOne", () => {
|
|
353
|
-
it("should find first matching user", async () => {
|
|
354
|
-
await repository.save(user1);
|
|
355
|
-
await repository.save(user2);
|
|
356
|
-
|
|
357
|
-
const criteria = Criteria.create<User>().whereEquals(
|
|
358
|
-
"status",
|
|
359
|
-
"active"
|
|
360
|
-
);
|
|
361
|
-
const found = await repository.findOne(criteria);
|
|
362
|
-
|
|
363
|
-
expect(found).toBeDefined();
|
|
364
|
-
expect(found?.status).toBe("active");
|
|
365
|
-
});
|
|
366
|
-
|
|
367
|
-
it("should return null when no match", async () => {
|
|
368
|
-
const criteria = Criteria.create<User>().whereEquals(
|
|
369
|
-
"status",
|
|
370
|
-
"active"
|
|
371
|
-
);
|
|
372
|
-
const found = await repository.findOne(criteria);
|
|
373
|
-
|
|
374
|
-
expect(found).toBeNull();
|
|
375
|
-
});
|
|
376
|
-
});
|
|
377
|
-
|
|
378
|
-
describe("delete", () => {
|
|
379
|
-
it("should delete by aggregate", async () => {
|
|
380
|
-
await repository.save(user1);
|
|
381
|
-
await repository.delete(user1);
|
|
382
|
-
|
|
383
|
-
const found = await repository.findById(user1.id.value);
|
|
384
|
-
expect(found).toBeNull();
|
|
385
|
-
});
|
|
386
|
-
|
|
387
|
-
it("should delete by id", async () => {
|
|
388
|
-
await repository.save(user1);
|
|
389
|
-
await repository.delete(user1);
|
|
390
|
-
|
|
391
|
-
const found = await repository.findById(user1.id.value);
|
|
392
|
-
expect(found).toBeNull();
|
|
393
|
-
});
|
|
394
|
-
});
|
|
395
|
-
|
|
396
|
-
describe("exists", () => {
|
|
397
|
-
it("should return true when user exists", async () => {
|
|
398
|
-
await repository.save(user1);
|
|
399
|
-
const exists = await repository.exists(user1.id.value);
|
|
400
|
-
expect(exists).toBe(true);
|
|
401
|
-
});
|
|
402
|
-
|
|
403
|
-
it("should return false when user does not exist", async () => {
|
|
404
|
-
const exists = await repository.exists(new Id().value);
|
|
405
|
-
expect(exists).toBe(false);
|
|
406
|
-
});
|
|
407
|
-
});
|
|
408
|
-
|
|
409
|
-
describe("count", () => {
|
|
410
|
-
beforeEach(async () => {
|
|
411
|
-
await repository.save(user1);
|
|
412
|
-
await repository.save(user2);
|
|
413
|
-
await repository.save(user3);
|
|
414
|
-
});
|
|
415
|
-
|
|
416
|
-
it("should count all users", async () => {
|
|
417
|
-
const count = await repository.count();
|
|
418
|
-
expect(count).toBe(3);
|
|
419
|
-
});
|
|
420
|
-
|
|
421
|
-
it("should count users matching criteria", async () => {
|
|
422
|
-
const criteria = Criteria.create<User>().whereEquals(
|
|
423
|
-
"status",
|
|
424
|
-
"active"
|
|
425
|
-
);
|
|
426
|
-
const count = await repository.count(criteria);
|
|
427
|
-
expect(count).toBe(2);
|
|
428
|
-
});
|
|
429
|
-
});
|
|
430
|
-
|
|
431
|
-
describe("createMany", () => {
|
|
432
|
-
it("should save multiple users", async () => {
|
|
433
|
-
await repository.createMany([user1, user2, user3]);
|
|
434
|
-
|
|
435
|
-
const users = await repository.findAll();
|
|
436
|
-
expect(users).toHaveLength(3);
|
|
437
|
-
});
|
|
438
|
-
});
|
|
439
|
-
});
|
|
440
|
-
|
|
441
|
-
describe("BaseRepository with Mapper", () => {
|
|
442
|
-
let repository: MockUserRepository;
|
|
443
|
-
let user: User;
|
|
444
|
-
|
|
445
|
-
beforeEach(() => {
|
|
446
|
-
repository = new MockUserRepository(
|
|
447
|
-
new UserToDomainMapper(),
|
|
448
|
-
new UserToPersistenceMapper()
|
|
449
|
-
);
|
|
450
|
-
user = User.create({
|
|
451
|
-
name: "John",
|
|
452
|
-
email: "john@example.com",
|
|
453
|
-
age: 28,
|
|
454
|
-
status: "active",
|
|
455
|
-
});
|
|
456
|
-
});
|
|
457
|
-
|
|
458
|
-
afterEach(() => {
|
|
459
|
-
repository.clear();
|
|
460
|
-
});
|
|
461
|
-
|
|
462
|
-
it("should save new user (insert)", async () => {
|
|
463
|
-
expect(user.isNew()).toBe(true);
|
|
464
|
-
await repository.save(user);
|
|
465
|
-
|
|
466
|
-
const found = await repository.findById(user.id.value);
|
|
467
|
-
expect(found).toBeDefined();
|
|
468
|
-
expect(found?.name).toBe("John");
|
|
469
|
-
});
|
|
470
|
-
|
|
471
|
-
it("should update existing user", async () => {
|
|
472
|
-
await repository.save(user);
|
|
473
|
-
|
|
474
|
-
// Simulate existing user
|
|
475
|
-
const existingUser = User.create({
|
|
476
|
-
id: user.id, // Same ID
|
|
477
|
-
name: "John Updated",
|
|
478
|
-
email: "john@example.com",
|
|
479
|
-
age: 29,
|
|
480
|
-
status: "inactive",
|
|
481
|
-
});
|
|
482
|
-
|
|
483
|
-
await repository.save(existingUser);
|
|
484
|
-
|
|
485
|
-
const found = await repository.findById(user.id.value);
|
|
486
|
-
expect(found?.name).toBe("John Updated");
|
|
487
|
-
expect(found?.age).toBe(29);
|
|
488
|
-
});
|
|
489
|
-
|
|
490
|
-
it("should convert between domain and persistence", async () => {
|
|
491
|
-
await repository.save(user);
|
|
492
|
-
const found = await repository.findById(user.id.value);
|
|
493
|
-
|
|
494
|
-
// Should be a domain object
|
|
495
|
-
expect(found).toBeInstanceOf(User);
|
|
496
|
-
expect(found?.id).toBeInstanceOf(Id);
|
|
497
|
-
});
|
|
498
|
-
|
|
499
|
-
it("should apply criteria correctly", async () => {
|
|
500
|
-
const user1 = User.create({
|
|
501
|
-
name: "Alice",
|
|
502
|
-
email: "alice@example.com",
|
|
503
|
-
age: 25,
|
|
504
|
-
status: "active",
|
|
505
|
-
});
|
|
506
|
-
const user2 = User.create({
|
|
507
|
-
name: "Bob",
|
|
508
|
-
email: "bob@example.com",
|
|
509
|
-
age: 30,
|
|
510
|
-
status: "inactive",
|
|
511
|
-
});
|
|
512
|
-
|
|
513
|
-
await Promise.all([repository.save(user1), repository.save(user2)]);
|
|
514
|
-
|
|
515
|
-
const result = await repository.find(
|
|
516
|
-
Criteria.create<User>()
|
|
517
|
-
.whereEquals("status", "active")
|
|
518
|
-
.orderByDesc("age")
|
|
519
|
-
.paginate(1, 10)
|
|
520
|
-
);
|
|
521
|
-
|
|
522
|
-
expect(result.data).toHaveLength(1);
|
|
523
|
-
expect(result.data[0].name).toBe("Alice");
|
|
524
|
-
});
|
|
525
|
-
});
|
|
526
|
-
|
|
527
|
-
describe("Mapper", () => {
|
|
528
|
-
let toDomainMapper: UserToDomainMapper;
|
|
529
|
-
let toPersistenceMapper: UserToPersistenceMapper;
|
|
530
|
-
|
|
531
|
-
beforeEach(() => {
|
|
532
|
-
toDomainMapper = new UserToDomainMapper();
|
|
533
|
-
toPersistenceMapper = new UserToPersistenceMapper();
|
|
534
|
-
});
|
|
535
|
-
|
|
536
|
-
it("should convert from persistence to domain", () => {
|
|
537
|
-
const persistence: UserPersistence = {
|
|
538
|
-
id: "123",
|
|
539
|
-
name: "John",
|
|
540
|
-
email: "john@example.com",
|
|
541
|
-
age: 30,
|
|
542
|
-
status: "active",
|
|
543
|
-
createdAt: new Date(),
|
|
544
|
-
updatedAt: new Date(),
|
|
545
|
-
};
|
|
546
|
-
|
|
547
|
-
const domain = toDomainMapper.build(persistence);
|
|
548
|
-
|
|
549
|
-
expect(domain).toBeInstanceOf(User);
|
|
550
|
-
expect(domain.id.value).toBe("123");
|
|
551
|
-
expect(domain.name).toBe("John");
|
|
552
|
-
expect(domain.email).toBe("john@example.com");
|
|
553
|
-
expect(domain.age).toBe(30);
|
|
554
|
-
expect(domain.status).toBe("active");
|
|
555
|
-
});
|
|
556
|
-
|
|
557
|
-
it("should convert from domain to persistence", () => {
|
|
558
|
-
const domain = User.create({
|
|
559
|
-
id: Id.from("123"),
|
|
560
|
-
name: "John",
|
|
561
|
-
email: "john@example.com",
|
|
562
|
-
age: 30,
|
|
563
|
-
status: "active",
|
|
564
|
-
});
|
|
565
|
-
|
|
566
|
-
const persistence = toPersistenceMapper.build(domain);
|
|
567
|
-
|
|
568
|
-
expect(persistence.id).toBe("123");
|
|
569
|
-
expect(persistence.name).toBe("John");
|
|
570
|
-
expect(persistence.email).toBe("john@example.com");
|
|
571
|
-
expect(persistence.age).toBe(30);
|
|
572
|
-
expect(persistence.status).toBe("active");
|
|
573
|
-
expect(persistence.createdAt).toBeInstanceOf(Date);
|
|
574
|
-
expect(persistence.updatedAt).toBeInstanceOf(Date);
|
|
575
|
-
});
|
|
576
|
-
|
|
577
|
-
it("should convert list from persistence to domain", () => {
|
|
578
|
-
const persistenceList: UserPersistence[] = [
|
|
579
|
-
{
|
|
580
|
-
id: "1",
|
|
581
|
-
name: "Alice",
|
|
582
|
-
email: "alice@example.com",
|
|
583
|
-
age: 25,
|
|
584
|
-
status: "active",
|
|
585
|
-
createdAt: new Date(),
|
|
586
|
-
updatedAt: new Date(),
|
|
587
|
-
},
|
|
588
|
-
{
|
|
589
|
-
id: "2",
|
|
590
|
-
name: "Bob",
|
|
591
|
-
email: "bob@example.com",
|
|
592
|
-
age: 30,
|
|
593
|
-
status: "inactive",
|
|
594
|
-
createdAt: new Date(),
|
|
595
|
-
updatedAt: new Date(),
|
|
596
|
-
},
|
|
597
|
-
];
|
|
598
|
-
|
|
599
|
-
const domainList = persistenceList.map((p) => toDomainMapper.build(p));
|
|
600
|
-
|
|
601
|
-
expect(domainList).toHaveLength(2);
|
|
602
|
-
expect(domainList[0]).toBeInstanceOf(User);
|
|
603
|
-
expect(domainList[0].name).toBe("Alice");
|
|
604
|
-
expect(domainList[1].name).toBe("Bob");
|
|
605
|
-
});
|
|
606
|
-
|
|
607
|
-
it("should convert list from domain to persistence", () => {
|
|
608
|
-
const domainList = [
|
|
609
|
-
User.create({
|
|
610
|
-
id: Id.from("1"),
|
|
611
|
-
name: "Alice",
|
|
612
|
-
email: "alice@example.com",
|
|
613
|
-
age: 25,
|
|
614
|
-
status: "active",
|
|
615
|
-
}),
|
|
616
|
-
User.create({
|
|
617
|
-
id: Id.from("2"),
|
|
618
|
-
name: "Bob",
|
|
619
|
-
email: "bob@example.com",
|
|
620
|
-
age: 30,
|
|
621
|
-
status: "inactive",
|
|
622
|
-
}),
|
|
623
|
-
];
|
|
624
|
-
|
|
625
|
-
const persistenceList = domainList.map((d) =>
|
|
626
|
-
toPersistenceMapper.build(d)
|
|
627
|
-
);
|
|
628
|
-
|
|
629
|
-
expect(persistenceList).toHaveLength(2);
|
|
630
|
-
expect(persistenceList[0].id).toBe("1");
|
|
631
|
-
expect(persistenceList[0].name).toBe("Alice");
|
|
632
|
-
expect(persistenceList[1].name).toBe("Bob");
|
|
633
|
-
});
|
|
634
|
-
});
|
|
635
|
-
});
|