@woltz/rich-domain 0.2.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/.github/workflows/ci.yml +40 -0
- package/.husky/commit-msg +1 -0
- package/.husky/pre-commit +1 -0
- package/.versionrc.json +21 -0
- package/.vscode/settings.json +3 -0
- package/CHANGELOG.md +81 -0
- package/LICENSE +21 -0
- package/README.md +712 -0
- package/commitlint.config.js +23 -0
- package/dist/base-entity.d.ts +67 -0
- package/dist/base-entity.d.ts.map +1 -0
- package/dist/base-entity.js +309 -0
- package/dist/base-entity.js.map +1 -0
- package/dist/constants.d.ts +3 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +6 -0
- package/dist/constants.js.map +1 -0
- package/dist/criteria.d.ts +60 -0
- package/dist/criteria.d.ts.map +1 -0
- package/dist/criteria.js +214 -0
- package/dist/criteria.js.map +1 -0
- package/dist/deep-proxy.d.ts +34 -0
- package/dist/deep-proxy.d.ts.map +1 -0
- package/dist/deep-proxy.js +297 -0
- package/dist/deep-proxy.js.map +1 -0
- package/dist/domain-event-bus.d.ts +57 -0
- package/dist/domain-event-bus.d.ts.map +1 -0
- package/dist/domain-event-bus.js +112 -0
- package/dist/domain-event-bus.js.map +1 -0
- package/dist/domain-event.d.ts +55 -0
- package/dist/domain-event.d.ts.map +1 -0
- package/dist/domain-event.js +42 -0
- package/dist/domain-event.js.map +1 -0
- package/dist/entity.d.ts +13 -0
- package/dist/entity.d.ts.map +1 -0
- package/dist/entity.js +15 -0
- package/dist/entity.js.map +1 -0
- package/dist/filtering.d.ts +107 -0
- package/dist/filtering.d.ts.map +1 -0
- package/dist/filtering.js +202 -0
- package/dist/filtering.js.map +1 -0
- package/dist/id.d.ts +51 -0
- package/dist/id.d.ts.map +1 -0
- package/dist/id.js +84 -0
- package/dist/id.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +25 -0
- package/dist/index.js.map +1 -0
- package/dist/ordering.d.ts +93 -0
- package/dist/ordering.d.ts.map +1 -0
- package/dist/ordering.js +154 -0
- package/dist/ordering.js.map +1 -0
- package/dist/paginated-result.d.ts +62 -0
- package/dist/paginated-result.d.ts.map +1 -0
- package/dist/paginated-result.js +201 -0
- package/dist/paginated-result.js.map +1 -0
- package/dist/pagination.d.ts +218 -0
- package/dist/pagination.d.ts.map +1 -0
- package/dist/pagination.js +281 -0
- package/dist/pagination.js.map +1 -0
- package/dist/repository/base-repository.d.ts +77 -0
- package/dist/repository/base-repository.d.ts.map +1 -0
- package/dist/repository/base-repository.js +80 -0
- package/dist/repository/base-repository.js.map +1 -0
- package/dist/repository/in-memory-repository.d.ts +46 -0
- package/dist/repository/in-memory-repository.d.ts.map +1 -0
- package/dist/repository/in-memory-repository.js +85 -0
- package/dist/repository/in-memory-repository.js.map +1 -0
- package/dist/repository/index.d.ts +42 -0
- package/dist/repository/index.d.ts.map +1 -0
- package/dist/repository/index.js +47 -0
- package/dist/repository/index.js.map +1 -0
- package/dist/repository/mapper.d.ts +56 -0
- package/dist/repository/mapper.d.ts.map +1 -0
- package/dist/repository/mapper.js +15 -0
- package/dist/repository/mapper.js.map +1 -0
- package/dist/repository/types.d.ts +87 -0
- package/dist/repository/types.d.ts.map +1 -0
- package/dist/repository/types.js +6 -0
- package/dist/repository/types.js.map +1 -0
- package/dist/repository/unit-of-work.d.ts +70 -0
- package/dist/repository/unit-of-work.d.ts.map +1 -0
- package/dist/repository/unit-of-work.js +122 -0
- package/dist/repository/unit-of-work.js.map +1 -0
- package/dist/repository.d.ts +2 -0
- package/dist/repository.d.ts.map +1 -0
- package/dist/repository.js +21 -0
- package/dist/repository.js.map +1 -0
- package/dist/specification.d.ts +102 -0
- package/dist/specification.d.ts.map +1 -0
- package/dist/specification.js +187 -0
- package/dist/specification.js.map +1 -0
- package/dist/types/criteria.d.ts +35 -0
- package/dist/types/criteria.d.ts.map +1 -0
- package/dist/types/criteria.js +17 -0
- package/dist/types/criteria.js.map +1 -0
- package/dist/types/domain.d.ts +30 -0
- package/dist/types/domain.d.ts.map +1 -0
- package/dist/types/domain.js +2 -0
- package/dist/types/domain.js.map +1 -0
- package/dist/types/history-tracker.d.ts +36 -0
- package/dist/types/history-tracker.d.ts.map +1 -0
- package/dist/types/history-tracker.js +2 -0
- package/dist/types/history-tracker.js.map +1 -0
- package/dist/types/index.d.ts +8 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +8 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/repository.d.ts +43 -0
- package/dist/types/repository.d.ts.map +1 -0
- package/dist/types/repository.js +2 -0
- package/dist/types/repository.js.map +1 -0
- package/dist/types/standard-schema.d.ts +15 -0
- package/dist/types/standard-schema.d.ts.map +1 -0
- package/dist/types/standard-schema.js +2 -0
- package/dist/types/standard-schema.js.map +1 -0
- package/dist/types/unit-of-work.d.ts +39 -0
- package/dist/types/unit-of-work.d.ts.map +1 -0
- package/dist/types/unit-of-work.js +2 -0
- package/dist/types/unit-of-work.js.map +1 -0
- package/dist/types/utils.d.ts +14 -0
- package/dist/types/utils.d.ts.map +1 -0
- package/dist/types/utils.js +2 -0
- package/dist/types/utils.js.map +1 -0
- package/dist/types.d.ts +88 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +12 -0
- package/dist/types.js.map +1 -0
- package/dist/validation-error.d.ts +42 -0
- package/dist/validation-error.d.ts.map +1 -0
- package/dist/validation-error.js +73 -0
- package/dist/validation-error.js.map +1 -0
- package/dist/value-object.d.ts +47 -0
- package/dist/value-object.d.ts.map +1 -0
- package/dist/value-object.js +136 -0
- package/dist/value-object.js.map +1 -0
- package/eslint.config.js +51 -0
- package/jest.config.js +21 -0
- package/package.json +58 -0
- package/src/base-entity.ts +401 -0
- package/src/constants.ts +7 -0
- package/src/criteria.ts +291 -0
- package/src/deep-proxy.ts +339 -0
- package/src/domain-event-bus.ts +166 -0
- package/src/domain-event.ts +90 -0
- package/src/entity.ts +16 -0
- package/src/id.ts +94 -0
- package/src/index.ts +33 -0
- package/src/paginated-result.ts +274 -0
- package/src/repository/base-repository.ts +152 -0
- package/src/repository/in-memory-repository.ts +104 -0
- package/src/repository/index.ts +55 -0
- package/src/repository/mapper.ts +74 -0
- package/src/repository/unit-of-work.ts +148 -0
- package/src/types/criteria.ts +79 -0
- package/src/types/domain.ts +37 -0
- package/src/types/history-tracker.ts +45 -0
- package/src/types/index.ts +7 -0
- package/src/types/repository.ts +51 -0
- package/src/types/standard-schema.ts +19 -0
- package/src/types/unit-of-work.ts +46 -0
- package/src/types/utils.ts +29 -0
- package/src/validation-error.ts +97 -0
- package/src/value-object.ts +187 -0
- package/tests/criteria.test.ts +432 -0
- package/tests/domain-events.test.ts +445 -0
- package/tests/entity-equality.test.ts +487 -0
- package/tests/entity-validation.test.ts +339 -0
- package/tests/entity.test.ts +33 -0
- package/tests/history-tracker.spec.ts +667 -0
- package/tests/id.test.ts +341 -0
- package/tests/repository.test.ts +641 -0
- package/tests/to-json.test.ts +91 -0
- package/tests/utils.ts +151 -0
- package/tests/value-object-validation.test.ts +228 -0
- package/tests/value-objects.test.ts +52 -0
- package/tsconfig.json +31 -0
|
@@ -0,0 +1,667 @@
|
|
|
1
|
+
import { Id } from "../src";
|
|
2
|
+
import { Post, User, Address, Comment } from "./utils";
|
|
3
|
+
|
|
4
|
+
describe("History Tracker Tests", () => {
|
|
5
|
+
describe("Simple Property Changes", () => {
|
|
6
|
+
it("should track simple property changes", (done) => {
|
|
7
|
+
const post = new Post({
|
|
8
|
+
id: new Id("1"),
|
|
9
|
+
title: "First Post",
|
|
10
|
+
content: "Hello World",
|
|
11
|
+
likes: 0,
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
let changeCount = 0;
|
|
15
|
+
|
|
16
|
+
post.subscribe({
|
|
17
|
+
title: {
|
|
18
|
+
onChange: ({ previous, current, path }) => {
|
|
19
|
+
changeCount++;
|
|
20
|
+
expect(previous).toBe("First Post");
|
|
21
|
+
expect(current).toBe("Updated Title");
|
|
22
|
+
expect(path).toBe("title");
|
|
23
|
+
done();
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
post.title = "Updated Title";
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it("should track multiple property changes", () => {
|
|
32
|
+
const post = new Post({
|
|
33
|
+
id: new Id("1"),
|
|
34
|
+
title: "First Post",
|
|
35
|
+
content: "Hello World",
|
|
36
|
+
likes: 0,
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
const changes: any[] = [];
|
|
40
|
+
|
|
41
|
+
post.subscribe({
|
|
42
|
+
title: {
|
|
43
|
+
onChange: (event) => changes.push({ property: "title", ...event }),
|
|
44
|
+
},
|
|
45
|
+
likes: {
|
|
46
|
+
onChange: (event) => changes.push({ property: "likes", ...event }),
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
post.title = "New Title";
|
|
51
|
+
post.likes = 10;
|
|
52
|
+
|
|
53
|
+
expect(changes).toHaveLength(2);
|
|
54
|
+
expect(changes[0].property).toBe("title");
|
|
55
|
+
expect(changes[1].property).toBe("likes");
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
// ==========================================================================
|
|
60
|
+
// Array Changes - Create Tests
|
|
61
|
+
// ==========================================================================
|
|
62
|
+
|
|
63
|
+
describe("Array Changes - Create", () => {
|
|
64
|
+
it("should detect new items added to array", (done) => {
|
|
65
|
+
const user = new User({
|
|
66
|
+
id: new Id("1"),
|
|
67
|
+
name: "John Doe",
|
|
68
|
+
email: "john@example.com",
|
|
69
|
+
posts: [],
|
|
70
|
+
address: new Address({
|
|
71
|
+
street: "Main St",
|
|
72
|
+
city: "NYC",
|
|
73
|
+
zipCode: "10001",
|
|
74
|
+
}),
|
|
75
|
+
comments: [],
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
user.subscribe({
|
|
79
|
+
posts: {
|
|
80
|
+
onChange: ({ toCreate, toUpdate, toDelete }) => {
|
|
81
|
+
expect(toCreate).toHaveLength(2);
|
|
82
|
+
expect(toUpdate).toHaveLength(0);
|
|
83
|
+
expect(toDelete).toHaveLength(0);
|
|
84
|
+
expect(toCreate[0].title).toBe("Post 1");
|
|
85
|
+
done();
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
user.addManyPosts([
|
|
91
|
+
new Post({
|
|
92
|
+
id: new Id("1"),
|
|
93
|
+
title: "Post 1",
|
|
94
|
+
content: "Content 1",
|
|
95
|
+
likes: 0,
|
|
96
|
+
}),
|
|
97
|
+
new Post({
|
|
98
|
+
id: new Id("2"),
|
|
99
|
+
title: "Post 2",
|
|
100
|
+
content: "Content 2",
|
|
101
|
+
likes: 0,
|
|
102
|
+
}),
|
|
103
|
+
]);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it("should detect items pushed to array", (done) => {
|
|
107
|
+
const user = new User({
|
|
108
|
+
id: new Id("1"),
|
|
109
|
+
name: "John Doe",
|
|
110
|
+
email: "john@example.com",
|
|
111
|
+
posts: [
|
|
112
|
+
new Post({
|
|
113
|
+
id: new Id("1"),
|
|
114
|
+
title: "Post 1",
|
|
115
|
+
content: "Content 1",
|
|
116
|
+
likes: 0,
|
|
117
|
+
}),
|
|
118
|
+
],
|
|
119
|
+
address: new Address({
|
|
120
|
+
street: "Main St",
|
|
121
|
+
city: "NYC",
|
|
122
|
+
zipCode: "10001",
|
|
123
|
+
}),
|
|
124
|
+
comments: [],
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
user.subscribe({
|
|
128
|
+
posts: {
|
|
129
|
+
onChange: ({ toCreate }) => {
|
|
130
|
+
expect(toCreate).toHaveLength(1);
|
|
131
|
+
expect(toCreate[0].title).toBe("Post 2");
|
|
132
|
+
done();
|
|
133
|
+
},
|
|
134
|
+
},
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
user.posts.push(
|
|
138
|
+
new Post({
|
|
139
|
+
id: new Id("2"),
|
|
140
|
+
title: "Post 2",
|
|
141
|
+
content: "Content 2",
|
|
142
|
+
likes: 0,
|
|
143
|
+
})
|
|
144
|
+
);
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
// ==========================================================================
|
|
149
|
+
// Array Changes - Update Tests
|
|
150
|
+
// ==========================================================================
|
|
151
|
+
|
|
152
|
+
describe("Array Changes - Update", () => {
|
|
153
|
+
it("should detect updated items in array", (done) => {
|
|
154
|
+
const id1 = new Id("1");
|
|
155
|
+
const post1 = new Post({
|
|
156
|
+
id: id1,
|
|
157
|
+
title: "Post 1",
|
|
158
|
+
content: "Content 1",
|
|
159
|
+
likes: 0,
|
|
160
|
+
});
|
|
161
|
+
const post2 = new Post({
|
|
162
|
+
id: new Id("2"),
|
|
163
|
+
title: "Post 2",
|
|
164
|
+
content: "Content 2",
|
|
165
|
+
likes: 0,
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
const user = new User({
|
|
169
|
+
id: new Id("1"),
|
|
170
|
+
name: "John Doe",
|
|
171
|
+
email: "john@example.com",
|
|
172
|
+
posts: [post1, post2],
|
|
173
|
+
address: new Address({
|
|
174
|
+
street: "Main St",
|
|
175
|
+
city: "NYC",
|
|
176
|
+
zipCode: "10001",
|
|
177
|
+
}),
|
|
178
|
+
comments: [],
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
user.subscribe({
|
|
182
|
+
posts: {
|
|
183
|
+
onChange: ({ toCreate, toUpdate, toDelete }) => {
|
|
184
|
+
expect(toCreate).toHaveLength(0);
|
|
185
|
+
expect(toUpdate).toHaveLength(1);
|
|
186
|
+
expect(toDelete).toHaveLength(0);
|
|
187
|
+
expect(toUpdate[0].id).toBe(id1);
|
|
188
|
+
done();
|
|
189
|
+
},
|
|
190
|
+
},
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
// Modify existing post
|
|
194
|
+
post1.title = "Updated Post 1";
|
|
195
|
+
user.changeEmail("new@example.com");
|
|
196
|
+
user.posts = [...user.posts]; // Trigger change detection
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
it("should detect multiple updates in array", (done) => {
|
|
200
|
+
const post1 = new Post({
|
|
201
|
+
id: new Id("1"),
|
|
202
|
+
title: "Post 1",
|
|
203
|
+
content: "Content 1",
|
|
204
|
+
likes: 0,
|
|
205
|
+
});
|
|
206
|
+
const post2 = new Post({
|
|
207
|
+
id: new Id("2"),
|
|
208
|
+
title: "Post 2",
|
|
209
|
+
content: "Content 2",
|
|
210
|
+
likes: 0,
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
const user = new User({
|
|
214
|
+
id: new Id("1"),
|
|
215
|
+
name: "John Doe",
|
|
216
|
+
email: "john@example.com",
|
|
217
|
+
posts: [post1, post2],
|
|
218
|
+
address: new Address({
|
|
219
|
+
street: "Main St",
|
|
220
|
+
city: "NYC",
|
|
221
|
+
zipCode: "10001",
|
|
222
|
+
}),
|
|
223
|
+
comments: [],
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
user.subscribe({
|
|
227
|
+
posts: {
|
|
228
|
+
onChange: ({ toUpdate }) => {
|
|
229
|
+
expect(toUpdate).toHaveLength(2);
|
|
230
|
+
done();
|
|
231
|
+
},
|
|
232
|
+
},
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
post1.title = "Updated Post 1";
|
|
236
|
+
post2.likes = 100;
|
|
237
|
+
user.posts = [...user.posts];
|
|
238
|
+
});
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
// ==========================================================================
|
|
242
|
+
// Array Changes - Delete Tests
|
|
243
|
+
// ==========================================================================
|
|
244
|
+
|
|
245
|
+
describe("Array Changes - Delete", () => {
|
|
246
|
+
it("should detect deleted items from array", (done) => {
|
|
247
|
+
const post1 = new Post({
|
|
248
|
+
id: new Id("1"),
|
|
249
|
+
title: "Post 1",
|
|
250
|
+
content: "Content 1",
|
|
251
|
+
likes: 0,
|
|
252
|
+
});
|
|
253
|
+
const post2 = new Post({
|
|
254
|
+
id: new Id("2"),
|
|
255
|
+
title: "Post 2",
|
|
256
|
+
content: "Content 2",
|
|
257
|
+
likes: 0,
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
const user = new User({
|
|
261
|
+
id: new Id("1"),
|
|
262
|
+
name: "John Doe",
|
|
263
|
+
email: "john@example.com",
|
|
264
|
+
posts: [post1, post2],
|
|
265
|
+
address: new Address({
|
|
266
|
+
street: "Main St",
|
|
267
|
+
city: "NYC",
|
|
268
|
+
zipCode: "10001",
|
|
269
|
+
}),
|
|
270
|
+
comments: [],
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
user.subscribe({
|
|
274
|
+
posts: {
|
|
275
|
+
onChange: ({ toCreate, toUpdate, toDelete }) => {
|
|
276
|
+
expect(toCreate).toHaveLength(0);
|
|
277
|
+
expect(toUpdate).toHaveLength(0);
|
|
278
|
+
expect(toDelete).toHaveLength(1);
|
|
279
|
+
done();
|
|
280
|
+
},
|
|
281
|
+
},
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
user.posts = [post2];
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
it("should detect items removed with splice", (done) => {
|
|
288
|
+
const id1 = new Id("1");
|
|
289
|
+
const user = new User({
|
|
290
|
+
id: new Id("1"),
|
|
291
|
+
name: "John Doe",
|
|
292
|
+
email: "john@example.com",
|
|
293
|
+
posts: [
|
|
294
|
+
new Post({
|
|
295
|
+
id: id1,
|
|
296
|
+
title: "Post 1",
|
|
297
|
+
content: "Content 1",
|
|
298
|
+
likes: 0,
|
|
299
|
+
}),
|
|
300
|
+
new Post({
|
|
301
|
+
id: new Id("2"),
|
|
302
|
+
title: "Post 2",
|
|
303
|
+
content: "Content 2",
|
|
304
|
+
likes: 0,
|
|
305
|
+
}),
|
|
306
|
+
],
|
|
307
|
+
address: new Address({
|
|
308
|
+
street: "Main St",
|
|
309
|
+
city: "NYC",
|
|
310
|
+
zipCode: "10001",
|
|
311
|
+
}),
|
|
312
|
+
comments: [],
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
user.subscribe({
|
|
316
|
+
posts: {
|
|
317
|
+
onChange: ({ toDelete }) => {
|
|
318
|
+
expect(toDelete).toHaveLength(1);
|
|
319
|
+
expect(toDelete[0].id.value).toBe(id1.value);
|
|
320
|
+
done();
|
|
321
|
+
},
|
|
322
|
+
},
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
user.posts.splice(0, 1);
|
|
326
|
+
});
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
// ==========================================================================
|
|
330
|
+
// Array Changes - Mixed Operations Tests
|
|
331
|
+
// ==========================================================================
|
|
332
|
+
|
|
333
|
+
describe("Array Changes - Mixed Operations", () => {
|
|
334
|
+
it("should detect mixed create and update operations", (done) => {
|
|
335
|
+
const post1 = new Post({
|
|
336
|
+
id: new Id("1"),
|
|
337
|
+
title: "Post 1",
|
|
338
|
+
content: "Content 1",
|
|
339
|
+
likes: 0,
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
const user = new User({
|
|
343
|
+
id: new Id("1"),
|
|
344
|
+
name: "John Doe",
|
|
345
|
+
email: "john@example.com",
|
|
346
|
+
posts: [post1],
|
|
347
|
+
address: new Address({
|
|
348
|
+
street: "Main St",
|
|
349
|
+
city: "NYC",
|
|
350
|
+
zipCode: "10001",
|
|
351
|
+
}),
|
|
352
|
+
comments: [],
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
user.subscribe({
|
|
356
|
+
posts: {
|
|
357
|
+
onChange: ({ toCreate, toUpdate, toDelete }) => {
|
|
358
|
+
expect(toCreate).toHaveLength(2);
|
|
359
|
+
expect(toUpdate).toHaveLength(1);
|
|
360
|
+
expect(toDelete).toHaveLength(0);
|
|
361
|
+
done();
|
|
362
|
+
},
|
|
363
|
+
},
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
post1.title = "Updated Post 1";
|
|
367
|
+
user.posts = [
|
|
368
|
+
post1,
|
|
369
|
+
new Post({
|
|
370
|
+
id: new Id("2"),
|
|
371
|
+
title: "Post 2",
|
|
372
|
+
content: "Content 2",
|
|
373
|
+
likes: 0,
|
|
374
|
+
}),
|
|
375
|
+
new Post({
|
|
376
|
+
id: new Id("3"),
|
|
377
|
+
title: "Post 3",
|
|
378
|
+
content: "Content 3",
|
|
379
|
+
likes: 0,
|
|
380
|
+
}),
|
|
381
|
+
];
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
it("should detect mixed create, update, and delete operations", (done) => {
|
|
385
|
+
const post1 = new Post({
|
|
386
|
+
id: new Id("1"),
|
|
387
|
+
title: "Post 1",
|
|
388
|
+
content: "Content 1",
|
|
389
|
+
likes: 0,
|
|
390
|
+
});
|
|
391
|
+
const post2 = new Post({
|
|
392
|
+
id: new Id("2"),
|
|
393
|
+
title: "Post 2",
|
|
394
|
+
content: "Content 2",
|
|
395
|
+
likes: 0,
|
|
396
|
+
});
|
|
397
|
+
const post3 = new Post({
|
|
398
|
+
id: new Id("3"),
|
|
399
|
+
title: "Post 3",
|
|
400
|
+
content: "Content 3",
|
|
401
|
+
likes: 0,
|
|
402
|
+
});
|
|
403
|
+
|
|
404
|
+
const user = new User({
|
|
405
|
+
id: new Id("1"),
|
|
406
|
+
name: "John Doe",
|
|
407
|
+
email: "john@example.com",
|
|
408
|
+
posts: [post1, post2, post3],
|
|
409
|
+
address: new Address({
|
|
410
|
+
street: "Main St",
|
|
411
|
+
city: "NYC",
|
|
412
|
+
zipCode: "10001",
|
|
413
|
+
}),
|
|
414
|
+
comments: [],
|
|
415
|
+
});
|
|
416
|
+
|
|
417
|
+
user.subscribe({
|
|
418
|
+
posts: {
|
|
419
|
+
onChange: ({ toCreate, toUpdate, toDelete }) => {
|
|
420
|
+
expect(toCreate).toHaveLength(1); // post4
|
|
421
|
+
expect(toUpdate).toHaveLength(1); // post2 modified
|
|
422
|
+
expect(toDelete).toHaveLength(2); // post1 and post3 removed
|
|
423
|
+
done();
|
|
424
|
+
},
|
|
425
|
+
},
|
|
426
|
+
});
|
|
427
|
+
|
|
428
|
+
post2.likes = 50;
|
|
429
|
+
user.posts = [
|
|
430
|
+
post2,
|
|
431
|
+
new Post({
|
|
432
|
+
id: new Id("4"),
|
|
433
|
+
title: "Post 4",
|
|
434
|
+
content: "Content 4",
|
|
435
|
+
likes: 0,
|
|
436
|
+
}),
|
|
437
|
+
];
|
|
438
|
+
});
|
|
439
|
+
});
|
|
440
|
+
|
|
441
|
+
// ==========================================================================
|
|
442
|
+
// Nested Entity Tests
|
|
443
|
+
// ==========================================================================
|
|
444
|
+
|
|
445
|
+
describe("Nested Entity Changes", () => {
|
|
446
|
+
it("should track changes in nested value objects", (done) => {
|
|
447
|
+
const user = new User({
|
|
448
|
+
id: new Id("1"),
|
|
449
|
+
name: "John Doe",
|
|
450
|
+
email: "john@example.com",
|
|
451
|
+
posts: [],
|
|
452
|
+
address: new Address({
|
|
453
|
+
street: "Main St",
|
|
454
|
+
city: "NYC",
|
|
455
|
+
zipCode: "10001",
|
|
456
|
+
}),
|
|
457
|
+
comments: [],
|
|
458
|
+
});
|
|
459
|
+
|
|
460
|
+
user.subscribe({
|
|
461
|
+
address: {
|
|
462
|
+
onChange: ({ previous, current }) => {
|
|
463
|
+
expect(previous).toBeInstanceOf(Address);
|
|
464
|
+
expect(current).toBeInstanceOf(Address);
|
|
465
|
+
expect(current.city).toBe("LA");
|
|
466
|
+
done();
|
|
467
|
+
},
|
|
468
|
+
},
|
|
469
|
+
});
|
|
470
|
+
|
|
471
|
+
user.address = new Address({
|
|
472
|
+
street: "Broadway",
|
|
473
|
+
city: "LA",
|
|
474
|
+
zipCode: "90001",
|
|
475
|
+
});
|
|
476
|
+
});
|
|
477
|
+
|
|
478
|
+
it("should track correct changes when assign all value", (done) => {
|
|
479
|
+
const user = new User({
|
|
480
|
+
id: new Id("1"),
|
|
481
|
+
name: "John Doe",
|
|
482
|
+
email: "john@example.com",
|
|
483
|
+
posts: [],
|
|
484
|
+
address: new Address({
|
|
485
|
+
street: "Main St",
|
|
486
|
+
city: "NYC",
|
|
487
|
+
zipCode: "10001",
|
|
488
|
+
}),
|
|
489
|
+
comments: [
|
|
490
|
+
new Comment({
|
|
491
|
+
text: "Nice post!",
|
|
492
|
+
author: "Alice",
|
|
493
|
+
}),
|
|
494
|
+
],
|
|
495
|
+
});
|
|
496
|
+
|
|
497
|
+
let count = 0;
|
|
498
|
+
const MAX_COUNT_TO_EXPECT = 3;
|
|
499
|
+
user.subscribe({
|
|
500
|
+
comments: {
|
|
501
|
+
onChange: ({ toCreate, toDelete, toUpdate }) => {
|
|
502
|
+
count++;
|
|
503
|
+
if (count === MAX_COUNT_TO_EXPECT) {
|
|
504
|
+
expect(toCreate).toHaveLength(2);
|
|
505
|
+
expect(toDelete).toHaveLength(1);
|
|
506
|
+
expect(toUpdate).toHaveLength(0);
|
|
507
|
+
done();
|
|
508
|
+
}
|
|
509
|
+
},
|
|
510
|
+
},
|
|
511
|
+
});
|
|
512
|
+
|
|
513
|
+
user.comments.push(
|
|
514
|
+
new Comment({
|
|
515
|
+
text: "Nice post2!",
|
|
516
|
+
author: "Alice2",
|
|
517
|
+
})
|
|
518
|
+
);
|
|
519
|
+
user.comments.push(
|
|
520
|
+
new Comment({
|
|
521
|
+
text: "Nice post3!",
|
|
522
|
+
author: "Alice3",
|
|
523
|
+
})
|
|
524
|
+
);
|
|
525
|
+
|
|
526
|
+
user.comments = [
|
|
527
|
+
new Comment({
|
|
528
|
+
text: "Nice post2!",
|
|
529
|
+
author: "Alice2",
|
|
530
|
+
}),
|
|
531
|
+
new Comment({
|
|
532
|
+
text: "Nice post3!",
|
|
533
|
+
author: "Alice3",
|
|
534
|
+
}),
|
|
535
|
+
];
|
|
536
|
+
});
|
|
537
|
+
});
|
|
538
|
+
|
|
539
|
+
// ==========================================================================
|
|
540
|
+
// History Tracking Tests
|
|
541
|
+
// ==========================================================================
|
|
542
|
+
|
|
543
|
+
describe("History Tracking", () => {
|
|
544
|
+
it("should record history of changes", () => {
|
|
545
|
+
const post = new Post({
|
|
546
|
+
id: new Id("1"),
|
|
547
|
+
title: "First Post",
|
|
548
|
+
content: "Hello World",
|
|
549
|
+
likes: 0,
|
|
550
|
+
});
|
|
551
|
+
|
|
552
|
+
post.title = "Second Title";
|
|
553
|
+
post.likes = 10;
|
|
554
|
+
post.content = "Updated Content";
|
|
555
|
+
|
|
556
|
+
const history = post.getHistory();
|
|
557
|
+
expect(history).toHaveLength(3);
|
|
558
|
+
expect(history[0].path).toBe("title");
|
|
559
|
+
expect(history[1].path).toBe("likes");
|
|
560
|
+
expect(history[2].path).toBe("content");
|
|
561
|
+
});
|
|
562
|
+
|
|
563
|
+
it("should clear history", () => {
|
|
564
|
+
const post = new Post({
|
|
565
|
+
id: new Id("1"),
|
|
566
|
+
title: "First Post",
|
|
567
|
+
content: "Hello World",
|
|
568
|
+
likes: 0,
|
|
569
|
+
});
|
|
570
|
+
|
|
571
|
+
post.title = "Second Title";
|
|
572
|
+
expect(post.getHistory()).toHaveLength(1);
|
|
573
|
+
|
|
574
|
+
post.clearHistory();
|
|
575
|
+
expect(post.getHistory()).toHaveLength(0);
|
|
576
|
+
});
|
|
577
|
+
});
|
|
578
|
+
|
|
579
|
+
// ==========================================================================
|
|
580
|
+
// Multiple Subscribers Test
|
|
581
|
+
// ==========================================================================
|
|
582
|
+
|
|
583
|
+
describe("Multiple Subscribers", () => {
|
|
584
|
+
it("should notify all subscribers on change", () => {
|
|
585
|
+
const post = new Post({
|
|
586
|
+
id: new Id("1"),
|
|
587
|
+
title: "First Post",
|
|
588
|
+
content: "Hello World",
|
|
589
|
+
likes: 0,
|
|
590
|
+
});
|
|
591
|
+
|
|
592
|
+
let subscriber1Called = false;
|
|
593
|
+
let subscriber2Called = false;
|
|
594
|
+
|
|
595
|
+
post.subscribe({
|
|
596
|
+
title: {
|
|
597
|
+
onChange: () => {
|
|
598
|
+
subscriber1Called = true;
|
|
599
|
+
},
|
|
600
|
+
},
|
|
601
|
+
});
|
|
602
|
+
|
|
603
|
+
post.subscribe({
|
|
604
|
+
title: {
|
|
605
|
+
onChange: () => {
|
|
606
|
+
subscriber2Called = true;
|
|
607
|
+
},
|
|
608
|
+
},
|
|
609
|
+
});
|
|
610
|
+
|
|
611
|
+
post.title = "Updated Title";
|
|
612
|
+
|
|
613
|
+
expect(subscriber1Called).toBe(true);
|
|
614
|
+
expect(subscriber2Called).toBe(true);
|
|
615
|
+
});
|
|
616
|
+
});
|
|
617
|
+
|
|
618
|
+
// ==========================================================================
|
|
619
|
+
// Plain Object Tests
|
|
620
|
+
// ==========================================================================
|
|
621
|
+
|
|
622
|
+
describe("Plain Object", () => {
|
|
623
|
+
it("should create a plain object", () => {
|
|
624
|
+
const user = new User({
|
|
625
|
+
id: new Id("1"),
|
|
626
|
+
name: "John Doe",
|
|
627
|
+
email: "john@example.com",
|
|
628
|
+
posts: [],
|
|
629
|
+
address: new Address({
|
|
630
|
+
street: "Main St",
|
|
631
|
+
city: "NYC",
|
|
632
|
+
zipCode: "10001",
|
|
633
|
+
}),
|
|
634
|
+
comments: [],
|
|
635
|
+
});
|
|
636
|
+
|
|
637
|
+
user.subscribe({
|
|
638
|
+
email: {
|
|
639
|
+
onChange: ({ previous, current }) => {
|
|
640
|
+
expect(previous).toBe("john@example.com");
|
|
641
|
+
expect(current).toBe("new@example.com");
|
|
642
|
+
},
|
|
643
|
+
},
|
|
644
|
+
extra: {
|
|
645
|
+
onChange: ({ previous, current }) => {
|
|
646
|
+
expect(previous).toBe(undefined);
|
|
647
|
+
expect(current).toEqual({
|
|
648
|
+
age: 20,
|
|
649
|
+
height: 180,
|
|
650
|
+
});
|
|
651
|
+
},
|
|
652
|
+
},
|
|
653
|
+
address: {
|
|
654
|
+
onChange: ({ previous, current }) => {
|
|
655
|
+
expect(previous).toBeInstanceOf(Address);
|
|
656
|
+
expect(current).toBeInstanceOf(Address);
|
|
657
|
+
},
|
|
658
|
+
},
|
|
659
|
+
});
|
|
660
|
+
|
|
661
|
+
user.changeExtra({
|
|
662
|
+
age: 20,
|
|
663
|
+
height: 180,
|
|
664
|
+
});
|
|
665
|
+
});
|
|
666
|
+
});
|
|
667
|
+
});
|