@woltz/rich-domain 1.2.0 → 1.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.
Files changed (105) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/dist/aggregate-changes.d.ts +164 -0
  3. package/dist/aggregate-changes.d.ts.map +1 -0
  4. package/dist/aggregate-changes.js +281 -0
  5. package/dist/aggregate-changes.js.map +1 -0
  6. package/dist/base-entity.d.ts +32 -8
  7. package/dist/base-entity.d.ts.map +1 -1
  8. package/dist/base-entity.js +117 -86
  9. package/dist/base-entity.js.map +1 -1
  10. package/dist/criteria.d.ts +3 -3
  11. package/dist/criteria.d.ts.map +1 -1
  12. package/dist/criteria.js.map +1 -1
  13. package/dist/crypto.d.ts +3 -0
  14. package/dist/crypto.d.ts.map +1 -0
  15. package/dist/crypto.js +29 -0
  16. package/dist/crypto.js.map +1 -0
  17. package/dist/entity-changes.d.ts +84 -0
  18. package/dist/entity-changes.d.ts.map +1 -0
  19. package/dist/entity-changes.js +135 -0
  20. package/dist/entity-changes.js.map +1 -0
  21. package/dist/entity-schema-registry.d.ts +148 -0
  22. package/dist/entity-schema-registry.d.ts.map +1 -0
  23. package/dist/entity-schema-registry.js +219 -0
  24. package/dist/entity-schema-registry.js.map +1 -0
  25. package/dist/history-tracker.d.ts +97 -0
  26. package/dist/history-tracker.d.ts.map +1 -0
  27. package/dist/history-tracker.js +805 -0
  28. package/dist/history-tracker.js.map +1 -0
  29. package/dist/id.d.ts +11 -10
  30. package/dist/id.d.ts.map +1 -1
  31. package/dist/id.js +4 -28
  32. package/dist/id.js.map +1 -1
  33. package/dist/index.d.ts +1 -0
  34. package/dist/index.d.ts.map +1 -1
  35. package/dist/index.js +1 -0
  36. package/dist/index.js.map +1 -1
  37. package/dist/mapper.d.ts +1 -1
  38. package/dist/mapper.d.ts.map +1 -1
  39. package/dist/mapper.js.map +1 -1
  40. package/dist/repository/base-repository.d.ts +6 -32
  41. package/dist/repository/base-repository.d.ts.map +1 -1
  42. package/dist/repository/base-repository.js +0 -27
  43. package/dist/repository/base-repository.js.map +1 -1
  44. package/dist/repository/unit-of-work.d.ts +0 -25
  45. package/dist/repository/unit-of-work.d.ts.map +1 -1
  46. package/dist/repository/unit-of-work.js +0 -25
  47. package/dist/repository/unit-of-work.js.map +1 -1
  48. package/dist/types/change-tracker.d.ts +186 -0
  49. package/dist/types/change-tracker.d.ts.map +1 -0
  50. package/dist/types/change-tracker.js +2 -0
  51. package/dist/types/change-tracker.js.map +1 -0
  52. package/dist/types/criteria.d.ts +5 -1
  53. package/dist/types/criteria.d.ts.map +1 -1
  54. package/dist/types/history-tracker.d.ts +11 -0
  55. package/dist/types/history-tracker.d.ts.map +1 -1
  56. package/dist/types/utils.d.ts +0 -1
  57. package/dist/types/utils.d.ts.map +1 -1
  58. package/dist/validation-error.d.ts.map +1 -1
  59. package/dist/validation-error.js +0 -3
  60. package/dist/validation-error.js.map +1 -1
  61. package/dist/value-object.d.ts +57 -8
  62. package/dist/value-object.d.ts.map +1 -1
  63. package/dist/value-object.js +49 -21
  64. package/dist/value-object.js.map +1 -1
  65. package/package.json +2 -1
  66. package/src/aggregate-changes.ts +335 -0
  67. package/src/base-entity.ts +140 -100
  68. package/src/criteria.ts +2 -1
  69. package/src/crypto.ts +31 -0
  70. package/src/entity-changes.ts +151 -0
  71. package/src/entity-schema-registry.ts +275 -0
  72. package/src/history-tracker.ts +1114 -0
  73. package/src/id.ts +17 -26
  74. package/src/index.ts +1 -0
  75. package/src/mapper.ts +4 -1
  76. package/src/repository/base-repository.ts +6 -37
  77. package/src/repository/unit-of-work.ts +0 -25
  78. package/src/types/change-tracker.ts +221 -0
  79. package/src/types/criteria.ts +6 -1
  80. package/src/types/history-tracker.ts +13 -0
  81. package/src/types/utils.ts +0 -9
  82. package/src/validation-error.ts +0 -4
  83. package/src/value-object.ts +84 -23
  84. package/tests/aggregate-changes.test.ts +284 -0
  85. package/tests/criteria.test.ts +122 -161
  86. package/tests/entity-equality.test.ts +38 -61
  87. package/tests/entity-schema-registry.test.ts +382 -0
  88. package/tests/entity-validation.test.ts +7 -94
  89. package/tests/history-tracker.spec.ts +349 -617
  90. package/tests/id.test.ts +41 -44
  91. package/tests/load-test/data.json +346041 -0
  92. package/tests/load-test/entities.ts +97 -0
  93. package/tests/load-test/generate-data.ts +81 -0
  94. package/tests/load-test/lead-to-domain.mapper.ts +24 -0
  95. package/tests/load-test/load.test.ts +38 -0
  96. package/tests/repository.test.ts +30 -54
  97. package/tests/to-json.test.ts +14 -18
  98. package/tests/utils.ts +138 -102
  99. package/tests/value-objects.test.ts +57 -29
  100. package/dist/deep-proxy.d.ts +0 -36
  101. package/dist/deep-proxy.d.ts.map +0 -1
  102. package/dist/deep-proxy.js +0 -384
  103. package/dist/deep-proxy.js.map +0 -1
  104. package/src/deep-proxy.ts +0 -447
  105. package/tests/entity.test.ts +0 -33
@@ -10,7 +10,7 @@ class Order extends Entity<{ id: Id; total: number; status: string }> {}
10
10
  describe("Entity Equality by ID", () => {
11
11
  describe("Basic Equality", () => {
12
12
  it("should be equal when entities have the same ID", () => {
13
- const id = new Id("user-123");
13
+ const id = Id.from("user-123");
14
14
  const user1 = new User({
15
15
  id,
16
16
  name: "John",
@@ -18,10 +18,9 @@ describe("Entity Equality by ID", () => {
18
18
  address: new Address({
19
19
  street: "Main St",
20
20
  city: "NYC",
21
- zipCode: "10001",
22
21
  }),
23
- comments: [],
24
22
  posts: [],
23
+ tags: [],
25
24
  });
26
25
  const user2 = new User({
27
26
  id,
@@ -30,10 +29,9 @@ describe("Entity Equality by ID", () => {
30
29
  address: new Address({
31
30
  street: "Main St",
32
31
  city: "NYC",
33
- zipCode: "10001",
34
32
  }),
35
- comments: [],
36
33
  posts: [],
34
+ tags: [],
37
35
  });
38
36
 
39
37
  expect(user1.equals(user2)).toBe(true);
@@ -49,9 +47,8 @@ describe("Entity Equality by ID", () => {
49
47
  address: new Address({
50
48
  street: "Main St",
51
49
  city: "NYC",
52
- zipCode: "10001",
53
50
  }),
54
- comments: [],
51
+ tags: [],
55
52
  posts: [],
56
53
  });
57
54
  const user2 = new User({
@@ -61,9 +58,8 @@ describe("Entity Equality by ID", () => {
61
58
  address: new Address({
62
59
  street: "Main St",
63
60
  city: "NYC",
64
- zipCode: "10001",
65
61
  }),
66
- comments: [],
62
+ tags: [],
67
63
  posts: [],
68
64
  });
69
65
 
@@ -78,9 +74,8 @@ describe("Entity Equality by ID", () => {
78
74
  address: new Address({
79
75
  street: "Main St",
80
76
  city: "NYC",
81
- zipCode: "10001",
82
77
  }),
83
- comments: [],
78
+ tags: [],
84
79
  posts: [],
85
80
  });
86
81
  const user2 = new User({
@@ -90,9 +85,8 @@ describe("Entity Equality by ID", () => {
90
85
  address: new Address({
91
86
  street: "Main St",
92
87
  city: "NYC",
93
- zipCode: "10001",
94
88
  }),
95
- comments: [],
89
+ tags: [],
96
90
  posts: [],
97
91
  });
98
92
 
@@ -108,12 +102,10 @@ describe("Entity Equality by ID", () => {
108
102
  address: new Address({
109
103
  street: "Main St",
110
104
  city: "NYC",
111
- zipCode: "10001",
112
105
  }),
113
- comments: [],
106
+ tags: [],
114
107
  posts: [],
115
108
  });
116
-
117
109
  expect(user.equals(user)).toBe(true);
118
110
  });
119
111
  });
@@ -128,9 +120,8 @@ describe("Entity Equality by ID", () => {
128
120
  address: new Address({
129
121
  street: "Main St",
130
122
  city: "NYC",
131
- zipCode: "10001",
132
123
  }),
133
- comments: [],
124
+ tags: [],
134
125
  posts: [],
135
126
  });
136
127
 
@@ -146,9 +137,8 @@ describe("Entity Equality by ID", () => {
146
137
  address: new Address({
147
138
  street: "Main St",
148
139
  city: "NYC",
149
- zipCode: "10001",
150
140
  }),
151
- comments: [],
141
+ tags: [],
152
142
  posts: [],
153
143
  });
154
144
 
@@ -165,9 +155,8 @@ describe("Entity Equality by ID", () => {
165
155
  address: new Address({
166
156
  street: "Main St",
167
157
  city: "NYC",
168
- zipCode: "10001",
169
158
  }),
170
- comments: [],
159
+ tags: [],
171
160
  posts: [],
172
161
  });
173
162
 
@@ -182,9 +171,8 @@ describe("Entity Equality by ID", () => {
182
171
  address: new Address({
183
172
  street: "Main St",
184
173
  city: "NYC",
185
- zipCode: "10001",
186
174
  }),
187
- comments: [],
175
+ tags: [],
188
176
  posts: [],
189
177
  });
190
178
 
@@ -201,9 +189,8 @@ describe("Entity Equality by ID", () => {
201
189
  address: new Address({
202
190
  street: "Main St",
203
191
  city: "NYC",
204
- zipCode: "10001",
205
192
  }),
206
- comments: [],
193
+ tags: [],
207
194
  posts: [],
208
195
  });
209
196
 
@@ -218,9 +205,8 @@ describe("Entity Equality by ID", () => {
218
205
  address: new Address({
219
206
  street: "Main St",
220
207
  city: "NYC",
221
- zipCode: "10001",
222
208
  }),
223
- comments: [],
209
+ tags: [],
224
210
  posts: [],
225
211
  });
226
212
 
@@ -234,9 +220,8 @@ describe("Entity Equality by ID", () => {
234
220
  address: new Address({
235
221
  street: "Main St",
236
222
  city: "NYC",
237
- zipCode: "10001",
238
223
  }),
239
- comments: [],
224
+ tags: [],
240
225
  posts: [],
241
226
  });
242
227
  const user2 = new User({
@@ -245,9 +230,8 @@ describe("Entity Equality by ID", () => {
245
230
  address: new Address({
246
231
  street: "Main St",
247
232
  city: "NYC",
248
- zipCode: "10001",
249
233
  }),
250
- comments: [],
234
+ tags: [],
251
235
  posts: [],
252
236
  });
253
237
 
@@ -269,9 +253,8 @@ describe("Entity Equality by ID", () => {
269
253
  address: new Address({
270
254
  street: "Main St",
271
255
  city: "NYC",
272
- zipCode: "10001",
273
256
  }),
274
- comments: [],
257
+ tags: [],
275
258
  posts: [],
276
259
  });
277
260
  const order = new Order({ id, total: 100, status: "pending" });
@@ -291,9 +274,8 @@ describe("Entity Equality by ID", () => {
291
274
  address: new Address({
292
275
  street: "Main St",
293
276
  city: "NYC",
294
- zipCode: "10001",
295
277
  }),
296
- comments: [],
278
+ tags: [],
297
279
  posts: [],
298
280
  });
299
281
  const user2 = new User({
@@ -303,18 +285,15 @@ describe("Entity Equality by ID", () => {
303
285
  address: new Address({
304
286
  street: "Main St",
305
287
  city: "NYC",
306
- zipCode: "10001",
307
288
  }),
308
- comments: [],
289
+ tags: [],
309
290
  posts: [],
310
291
  });
311
292
 
312
293
  expect(user1.equals(user2)).toBe(true);
313
294
 
314
- // Change properties
315
- user1.name = "Johnny";
295
+ user1.changeName("Johnny");
316
296
 
317
- // Still equal because ID is the same
318
297
  expect(user1.equals(user2)).toBe(true);
319
298
  });
320
299
  });
@@ -329,9 +308,9 @@ describe("Entity Equality by ID", () => {
329
308
  address: new Address({
330
309
  street: "Main St",
331
310
  city: "NYC",
332
- zipCode: "10001",
333
311
  }),
334
- comments: [],
312
+
313
+ tags: [],
335
314
  posts: [],
336
315
  }),
337
316
  new User({
@@ -341,9 +320,9 @@ describe("Entity Equality by ID", () => {
341
320
  address: new Address({
342
321
  street: "Main St",
343
322
  city: "NYC",
344
- zipCode: "10001",
345
323
  }),
346
- comments: [],
324
+
325
+ tags: [],
347
326
  posts: [],
348
327
  }),
349
328
  new User({
@@ -353,9 +332,9 @@ describe("Entity Equality by ID", () => {
353
332
  address: new Address({
354
333
  street: "Main St",
355
334
  city: "NYC",
356
- zipCode: "10001",
357
335
  }),
358
- comments: [],
336
+
337
+ tags: [],
359
338
  posts: [],
360
339
  }),
361
340
  ];
@@ -376,9 +355,9 @@ describe("Entity Equality by ID", () => {
376
355
  address: new Address({
377
356
  street: "Main St",
378
357
  city: "NYC",
379
- zipCode: "10001",
380
358
  }),
381
- comments: [],
359
+
360
+ tags: [],
382
361
  posts: [],
383
362
  }),
384
363
  new User({
@@ -388,9 +367,9 @@ describe("Entity Equality by ID", () => {
388
367
  address: new Address({
389
368
  street: "Main St",
390
369
  city: "NYC",
391
- zipCode: "10001",
392
370
  }),
393
- comments: [],
371
+
372
+ tags: [],
394
373
  posts: [],
395
374
  }),
396
375
  new User({
@@ -400,9 +379,9 @@ describe("Entity Equality by ID", () => {
400
379
  address: new Address({
401
380
  street: "Main St",
402
381
  city: "NYC",
403
- zipCode: "10001",
404
382
  }),
405
- comments: [],
383
+
384
+ tags: [],
406
385
  posts: [],
407
386
  }),
408
387
  ];
@@ -424,9 +403,9 @@ describe("Entity Equality by ID", () => {
424
403
  address: new Address({
425
404
  street: "Main St",
426
405
  city: "NYC",
427
- zipCode: "10001",
428
406
  }),
429
- comments: [],
407
+
408
+ tags: [],
430
409
  posts: [],
431
410
  }),
432
411
  new User({
@@ -436,9 +415,9 @@ describe("Entity Equality by ID", () => {
436
415
  address: new Address({
437
416
  street: "Main St",
438
417
  city: "NYC",
439
- zipCode: "10001",
440
418
  }),
441
- comments: [],
419
+
420
+ tags: [],
442
421
  posts: [],
443
422
  }),
444
423
  ];
@@ -450,9 +429,8 @@ describe("Entity Equality by ID", () => {
450
429
  address: new Address({
451
430
  street: "Main St",
452
431
  city: "NYC",
453
- zipCode: "10001",
454
432
  }),
455
- comments: [],
433
+ tags: [],
456
434
  posts: [],
457
435
  });
458
436
  const user3 = new User({
@@ -462,9 +440,8 @@ describe("Entity Equality by ID", () => {
462
440
  address: new Address({
463
441
  street: "Main St",
464
442
  city: "NYC",
465
- zipCode: "10001",
466
443
  }),
467
- comments: [],
444
+ tags: [],
468
445
  posts: [],
469
446
  });
470
447
 
@@ -0,0 +1,382 @@
1
+ // ============================================================================
2
+ // Tests: EntitySchemaRegistry
3
+ // ============================================================================
4
+
5
+ import { EntitySchemaRegistry } from "../src/entity-schema-registry";
6
+
7
+ describe("EntitySchemaRegistry", () => {
8
+ let registry: EntitySchemaRegistry;
9
+
10
+ beforeEach(() => {
11
+ registry = new EntitySchemaRegistry();
12
+ });
13
+
14
+ describe("register", () => {
15
+ it("should register a schema", () => {
16
+ registry.register({
17
+ entity: "User",
18
+ table: "users",
19
+ });
20
+
21
+ expect(registry.has("User")).toBe(true);
22
+ });
23
+
24
+ it("should allow chaining", () => {
25
+ const result = registry
26
+ .register({ entity: "User", table: "users" })
27
+ .register({ entity: "Post", table: "posts" });
28
+
29
+ expect(result).toBe(registry);
30
+ expect(registry.has("User")).toBe(true);
31
+ expect(registry.has("Post")).toBe(true);
32
+ });
33
+
34
+ it("should register with fields mapping", () => {
35
+ registry.register({
36
+ entity: "User",
37
+ table: "users",
38
+ fields: {
39
+ email: "user_email",
40
+ name: "user_name",
41
+ },
42
+ });
43
+
44
+ expect(registry.getFieldsMap("User")).toEqual({
45
+ email: "user_email",
46
+ name: "user_name",
47
+ });
48
+ });
49
+
50
+ it("should register with parent FK", () => {
51
+ registry.register({
52
+ entity: "Post",
53
+ table: "posts",
54
+ parentFk: {
55
+ field: "author_id",
56
+ parentEntity: "User",
57
+ },
58
+ });
59
+
60
+ expect(registry.getParentEntity("Post")).toBe("User");
61
+ expect(registry.getParentFkField("Post")).toBe("author_id");
62
+ });
63
+ });
64
+
65
+ describe("registerAll", () => {
66
+ it("should register multiple schemas at once", () => {
67
+ registry.registerAll([
68
+ { entity: "User", table: "users" },
69
+ { entity: "Post", table: "posts" },
70
+ { entity: "Comment", table: "comments" },
71
+ ]);
72
+
73
+ expect(registry.getRegisteredEntities()).toEqual([
74
+ "User",
75
+ "Post",
76
+ "Comment",
77
+ ]);
78
+ });
79
+ });
80
+
81
+ describe("getSchema", () => {
82
+ it("should return registered schema", () => {
83
+ registry.register({
84
+ entity: "User",
85
+ table: "users",
86
+ fields: { email: "user_email" },
87
+ });
88
+
89
+ const schema = registry.getSchema("User");
90
+
91
+ expect(schema.entity).toBe("User");
92
+ expect(schema.table).toBe("users");
93
+ expect(schema.fields).toEqual({ email: "user_email" });
94
+ });
95
+
96
+ it("should throw for unregistered entity", () => {
97
+ expect(() => registry.getSchema("Unknown")).toThrow(
98
+ "No schema registered for entity 'Unknown'"
99
+ );
100
+ });
101
+ });
102
+
103
+ describe("getTable", () => {
104
+ it("should return table name", () => {
105
+ registry.register({ entity: "User", table: "app_users" });
106
+
107
+ expect(registry.getTable("User")).toBe("app_users");
108
+ });
109
+ });
110
+
111
+ describe("mapFieldName", () => {
112
+ beforeEach(() => {
113
+ registry.register({
114
+ entity: "User",
115
+ table: "users",
116
+ fields: {
117
+ email: "user_email",
118
+ createdAt: "created_at",
119
+ },
120
+ });
121
+ });
122
+
123
+ it("should map field with different name", () => {
124
+ expect(registry.mapFieldName("User", "email")).toBe("user_email");
125
+ expect(registry.mapFieldName("User", "createdAt")).toBe("created_at");
126
+ });
127
+
128
+ it("should return original name if not mapped", () => {
129
+ expect(registry.mapFieldName("User", "name")).toBe("name");
130
+ });
131
+ });
132
+
133
+ describe("mapFields", () => {
134
+ beforeEach(() => {
135
+ registry.register({
136
+ entity: "User",
137
+ table: "users",
138
+ fields: {
139
+ email: "user_email",
140
+ name: "user_name",
141
+ },
142
+ });
143
+ });
144
+
145
+ it("should map all fields", () => {
146
+ const data = {
147
+ email: "test@test.com",
148
+ name: "Test User",
149
+ age: 25,
150
+ };
151
+
152
+ const mapped = registry.mapFields("User", data);
153
+
154
+ expect(mapped).toEqual({
155
+ user_email: "test@test.com",
156
+ user_name: "Test User",
157
+ age: 25,
158
+ });
159
+ });
160
+
161
+ it("should ignore arrays", () => {
162
+ const data = {
163
+ email: "test@test.com",
164
+ posts: [{ id: "1" }, { id: "2" }],
165
+ };
166
+
167
+ const mapped = registry.mapFields("User", data);
168
+
169
+ expect(mapped).toEqual({ user_email: "test@test.com" });
170
+ expect(mapped.posts).toBeUndefined();
171
+ });
172
+
173
+ it("should handle null and undefined values", () => {
174
+ const data = {
175
+ email: null,
176
+ name: undefined,
177
+ };
178
+
179
+ const mapped = registry.mapFields("User", data);
180
+
181
+ expect(mapped.user_email).toBeNull();
182
+ expect(mapped.user_name).toBeUndefined();
183
+ });
184
+ });
185
+
186
+ describe("mapEntity", () => {
187
+ beforeEach(() => {
188
+ registry.register({
189
+ entity: "User",
190
+ table: "users",
191
+ fields: {
192
+ email: "user_email",
193
+ },
194
+ });
195
+ });
196
+
197
+ it("should map entity with ID", () => {
198
+ // Mock Entity
199
+ const mockEntity = {
200
+ id: { value: "user-123" },
201
+ props: {
202
+ email: "test@test.com",
203
+ name: "Test",
204
+ },
205
+ };
206
+
207
+ const mapped = registry.mapEntity("User", mockEntity as any);
208
+
209
+ expect(mapped).toEqual({
210
+ id: "user-123",
211
+ user_email: "test@test.com",
212
+ name: "Test",
213
+ });
214
+ });
215
+
216
+ it("should skip nested entities and arrays", () => {
217
+ const mockEntity = {
218
+ id: { value: "user-123" },
219
+ props: {
220
+ email: "test@test.com",
221
+ posts: [],
222
+ address: { id: { value: "addr-1" } },
223
+ },
224
+ };
225
+
226
+ const mapped = registry.mapEntity("User", mockEntity as any);
227
+
228
+ expect(mapped.id).toBe("user-123");
229
+ expect(mapped.user_email).toBe("test@test.com");
230
+ expect(mapped.posts).toBeUndefined();
231
+ expect(mapped.address).toBeUndefined();
232
+ });
233
+ });
234
+
235
+ describe("getParentFk", () => {
236
+ beforeEach(() => {
237
+ registry.register({
238
+ entity: "Post",
239
+ table: "posts",
240
+ parentFk: {
241
+ field: "author_id",
242
+ parentEntity: "User",
243
+ },
244
+ });
245
+ registry.register({
246
+ entity: "User",
247
+ table: "users",
248
+ });
249
+ });
250
+
251
+ it("should return FK object", () => {
252
+ const fk = registry.getParentFk("Post", "user-123");
253
+
254
+ expect(fk).toEqual({ author_id: "user-123" });
255
+ });
256
+
257
+ it("should return null if no parent FK defined", () => {
258
+ const fk = registry.getParentFk("User", "user-123");
259
+
260
+ expect(fk).toBeNull();
261
+ });
262
+ });
263
+
264
+ describe("getParentEntity", () => {
265
+ it("should return parent entity name", () => {
266
+ registry.register({
267
+ entity: "Post",
268
+ table: "posts",
269
+ parentFk: { field: "author_id", parentEntity: "User" },
270
+ });
271
+
272
+ expect(registry.getParentEntity("Post")).toBe("User");
273
+ });
274
+
275
+ it("should return null if no parent", () => {
276
+ registry.register({ entity: "User", table: "users" });
277
+
278
+ expect(registry.getParentEntity("User")).toBeNull();
279
+ });
280
+ });
281
+
282
+ describe("getRegisteredEntities", () => {
283
+ it("should return all registered entity names", () => {
284
+ registry
285
+ .register({ entity: "User", table: "users" })
286
+ .register({ entity: "Post", table: "posts" })
287
+ .register({ entity: "Comment", table: "comments" });
288
+
289
+ expect(registry.getRegisteredEntities()).toEqual([
290
+ "User",
291
+ "Post",
292
+ "Comment",
293
+ ]);
294
+ });
295
+
296
+ it("should return empty array if none registered", () => {
297
+ expect(registry.getRegisteredEntities()).toEqual([]);
298
+ });
299
+ });
300
+
301
+ describe("clear", () => {
302
+ it("should remove all schemas", () => {
303
+ registry
304
+ .register({ entity: "User", table: "users" })
305
+ .register({ entity: "Post", table: "posts" });
306
+
307
+ registry.clear();
308
+
309
+ expect(registry.getRegisteredEntities()).toEqual([]);
310
+ expect(registry.has("User")).toBe(false);
311
+ });
312
+ });
313
+
314
+ describe("complex scenario", () => {
315
+ beforeEach(() => {
316
+ registry
317
+ .register({
318
+ entity: "User",
319
+ table: "users",
320
+ fields: {
321
+ email: "user_email",
322
+ name: "user_name",
323
+ },
324
+ })
325
+ .register({
326
+ entity: "Post",
327
+ table: "blog_posts",
328
+ fields: {
329
+ content: "post_content",
330
+ },
331
+ parentFk: { field: "author_id", parentEntity: "User" },
332
+ })
333
+ .register({
334
+ entity: "Comment",
335
+ table: "post_comments",
336
+ fields: {
337
+ text: "comment_text",
338
+ },
339
+ parentFk: { field: "post_id", parentEntity: "Post" },
340
+ })
341
+ .register({
342
+ entity: "Like",
343
+ table: "comment_likes",
344
+ fields: {
345
+ userId: "user_id",
346
+ },
347
+ parentFk: { field: "comment_id", parentEntity: "Comment" },
348
+ });
349
+ });
350
+
351
+ it("should handle full hierarchy", () => {
352
+ expect(registry.getTable("User")).toBe("users");
353
+ expect(registry.getTable("Post")).toBe("blog_posts");
354
+ expect(registry.getTable("Comment")).toBe("post_comments");
355
+ expect(registry.getTable("Like")).toBe("comment_likes");
356
+
357
+ expect(registry.getParentEntity("Post")).toBe("User");
358
+ expect(registry.getParentEntity("Comment")).toBe("Post");
359
+ expect(registry.getParentEntity("Like")).toBe("Comment");
360
+ expect(registry.getParentEntity("User")).toBeNull();
361
+ });
362
+
363
+ it("should map fields correctly for each entity", () => {
364
+ expect(registry.mapFieldName("User", "email")).toBe("user_email");
365
+ expect(registry.mapFieldName("Post", "content")).toBe("post_content");
366
+ expect(registry.mapFieldName("Comment", "text")).toBe("comment_text");
367
+ expect(registry.mapFieldName("Like", "userId")).toBe("user_id");
368
+ });
369
+
370
+ it("should build FK chain", () => {
371
+ expect(registry.getParentFk("Post", "user-1")).toEqual({
372
+ author_id: "user-1",
373
+ });
374
+ expect(registry.getParentFk("Comment", "post-1")).toEqual({
375
+ post_id: "post-1",
376
+ });
377
+ expect(registry.getParentFk("Like", "comment-1")).toEqual({
378
+ comment_id: "comment-1",
379
+ });
380
+ });
381
+ });
382
+ });