@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
@@ -0,0 +1,97 @@
1
+ import { z } from "zod";
2
+ import {
3
+ Aggregate,
4
+ Entity,
5
+ EntityValidation,
6
+ Id,
7
+ ValueObject,
8
+ VOValidation,
9
+ } from "../../src";
10
+
11
+ const DateSchema = z.union([z.date(), z.string().datetime()]);
12
+
13
+ const AddressProps = z.object({
14
+ street: z.string(),
15
+ city: z.string(),
16
+ state: z.string(),
17
+ zip: z.string(),
18
+ country: z.string(),
19
+ });
20
+
21
+ type AddressProps = z.infer<typeof AddressProps>;
22
+
23
+ export class Address extends ValueObject<AddressProps> {
24
+ protected static validation: VOValidation<AddressProps> = {
25
+ schema: AddressProps,
26
+ };
27
+ }
28
+
29
+ const ProposalProps = z.object({
30
+ id: z.instanceof(Id),
31
+ leadId: z.string(),
32
+ contractId: z.string(),
33
+ status: z.enum(["pending", "approved", "rejected"]),
34
+ createdAt: DateSchema,
35
+ updatedAt: DateSchema,
36
+ value: z.number(),
37
+ });
38
+
39
+ type ProposalProps = z.infer<typeof ProposalProps>;
40
+
41
+ export class Proposal extends Entity<ProposalProps> {
42
+ protected static validation: EntityValidation<ProposalProps> = {
43
+ schema: ProposalProps,
44
+ };
45
+ }
46
+
47
+ const ContactProps = z.object({
48
+ id: z.instanceof(Id),
49
+ userId: z.string().optional(),
50
+ leadId: z.string().optional(),
51
+ name: z.string(),
52
+ email: z.string().email(),
53
+ phone: z.string(),
54
+ });
55
+
56
+ type ContactProps = z.infer<typeof ContactProps>;
57
+
58
+ export class Contact extends Entity<ContactProps> {
59
+ protected static validation: EntityValidation<ContactProps> = {
60
+ schema: ContactProps,
61
+ };
62
+ }
63
+
64
+ const ContractProps = z.object({
65
+ id: z.instanceof(Id),
66
+ proposalId: z.string(),
67
+ proposal: z.instanceof(Proposal),
68
+ signes: z.array(ContactProps),
69
+ createdAt: DateSchema,
70
+ updatedAt: DateSchema,
71
+ signedAt: DateSchema.optional(),
72
+ });
73
+
74
+ type ContractProps = z.infer<typeof ContractProps>;
75
+
76
+ export class Contract extends Aggregate<ContractProps> {
77
+ protected static validation: EntityValidation<ContractProps> = {
78
+ schema: ContractProps,
79
+ };
80
+ }
81
+
82
+ const LeadProps = z.object({
83
+ id: z.instanceof(Id),
84
+ capturedName: z.string(),
85
+ capturedEmail: z.string().email(),
86
+ proposals: z.array(z.instanceof(Proposal)),
87
+ address: z.instanceof(Address),
88
+ contact: z.instanceof(Contact),
89
+ });
90
+
91
+ export type LeadProps = z.infer<typeof LeadProps>;
92
+
93
+ export class Lead extends Aggregate<LeadProps> {
94
+ protected static validation: EntityValidation<LeadProps> = {
95
+ schema: LeadProps,
96
+ };
97
+ }
@@ -0,0 +1,81 @@
1
+ import { faker } from "@faker-js/faker";
2
+ import fs from "fs";
3
+
4
+ const LEADS_COUNT = 10000;
5
+
6
+ const createId = () => faker.string.uuid();
7
+
8
+ const createAddress = () => {
9
+ return {
10
+ street: faker.location.streetAddress(),
11
+ city: faker.location.city(),
12
+ state: faker.location.state(),
13
+ zip: faker.location.zipCode(),
14
+ country: faker.location.country(),
15
+ };
16
+ };
17
+
18
+ const createProposal = (leadId: string) => {
19
+ const createdDate = faker.date.past();
20
+ return {
21
+ id: createId(),
22
+ leadId: leadId,
23
+ contractId: createId(),
24
+ status: faker.helpers.arrayElement(["pending", "approved", "rejected"]),
25
+ createdAt: createdDate,
26
+ updatedAt: faker.date.between({ from: createdDate, to: new Date() }),
27
+ value: parseFloat(faker.finance.amount({ min: 1000, max: 50000, dec: 2 })),
28
+ };
29
+ };
30
+
31
+ const createContact = (leadId: string) => {
32
+ return {
33
+ id: createId(),
34
+ userId: faker.datatype.boolean() ? createId() : undefined,
35
+ leadId: leadId,
36
+ name: faker.person.fullName(),
37
+ email: faker.internet.email(),
38
+ phone: faker.phone.number(),
39
+ };
40
+ };
41
+
42
+ const createLead = () => {
43
+ const leadId = createId();
44
+ const firstName = faker.person.firstName();
45
+ const lastName = faker.person.lastName();
46
+
47
+ const numProposals = faker.number.int({ min: 0, max: 3 });
48
+
49
+ const proposalsArray = Array.from({ length: numProposals }).map(() =>
50
+ createProposal(leadId)
51
+ );
52
+
53
+ return {
54
+ id: leadId,
55
+ capturedName: `${firstName} ${lastName}`,
56
+ capturedEmail: faker.internet.email({ firstName, lastName }),
57
+
58
+ address: createAddress(),
59
+
60
+ contact: createContact(leadId),
61
+ proposals: proposalsArray,
62
+ };
63
+ };
64
+
65
+ export async function generateData() {
66
+ return await new Promise((resolve, reject) => {
67
+ try {
68
+ console.log(`Generating ${LEADS_COUNT} leads...`);
69
+ const leads: any[] = [];
70
+ for (let i = 0; i < LEADS_COUNT; i++) {
71
+ leads.push(createLead());
72
+ }
73
+ const jsonOutput = JSON.stringify(leads, null, 2);
74
+ fs.writeFileSync(`${__dirname}/data.json`, jsonOutput);
75
+ console.log(`Success! data.json created with ${leads.length} items.`);
76
+ resolve(true);
77
+ } catch (error) {
78
+ reject(error);
79
+ }
80
+ });
81
+ }
@@ -0,0 +1,24 @@
1
+ import { Id, Mapper } from "../../src";
2
+ import { Address, Contact, Lead, Proposal } from "./entities";
3
+
4
+ export class LeadToDomainMapper extends Mapper<any, Lead> {
5
+ public build(props: any): Lead {
6
+ return new Lead({
7
+ id: Id.from(props.id),
8
+ capturedName: props.capturedName,
9
+ capturedEmail: props.capturedEmail,
10
+ address: new Address(props.address),
11
+ contact: new Contact({
12
+ ...props.contact,
13
+ id: Id.from(props.contact.id),
14
+ }),
15
+ proposals: props.proposals.map(
16
+ (proposal: any) =>
17
+ new Proposal({
18
+ ...proposal,
19
+ id: Id.from(proposal.id),
20
+ })
21
+ ),
22
+ });
23
+ }
24
+ }
@@ -0,0 +1,38 @@
1
+ import { Lead } from "./entities";
2
+ import fs from "fs";
3
+ import { generateData } from "./generate-data";
4
+ import { LeadToDomainMapper } from "./lead-to-domain.mapper";
5
+ import { PaginatedResult } from "../../src";
6
+
7
+ describe("Big Data", () => {
8
+ let leads: Lead[] = [];
9
+
10
+ function populateLeads() {
11
+ const data = fs.readFileSync(`${__dirname}/data.json`, "utf8");
12
+ const leadsData = JSON.parse(data);
13
+ const toDomainMapper = new LeadToDomainMapper();
14
+ leads = leadsData.map((lead: any) => toDomainMapper.build(lead));
15
+ }
16
+
17
+ beforeAll(async () => {
18
+ const hasData = fs.existsSync(`${__dirname}/data.json`);
19
+ if (!hasData) {
20
+ await generateData();
21
+ }
22
+ populateLeads();
23
+ });
24
+
25
+ it("should have 10000 leads", () => {
26
+ expect(leads.length).toBe(10000);
27
+ });
28
+
29
+ it("should deserialize to JSON correctly", () => {
30
+ const paginationResult = PaginatedResult.create(
31
+ leads,
32
+ { page: 1, limit: 10, offset: 0 },
33
+ 1000
34
+ );
35
+ const json = paginationResult.toJSON();
36
+ expect(json.data.length).toBe(10000);
37
+ });
38
+ });
@@ -4,10 +4,6 @@ import { Repository } from "../src/repository/base-repository";
4
4
  import { BaseProps } from "../src/types";
5
5
  import { InMemoryRepository } from "./utils";
6
6
 
7
- // ============================================================================
8
- // Test Domain Models
9
- // ============================================================================
10
-
11
7
  interface UserProps extends BaseProps {
12
8
  name: string;
13
9
  email: string;
@@ -55,10 +51,6 @@ class User extends Aggregate<UserProps> {
55
51
  }
56
52
  }
57
53
 
58
- // ============================================================================
59
- // Mock Persistence Model
60
- // ============================================================================
61
-
62
54
  type UserPersistence = {
63
55
  id: string;
64
56
  name: string;
@@ -69,9 +61,6 @@ type UserPersistence = {
69
61
  updatedAt: Date;
70
62
  };
71
63
 
72
- // ============================================================================
73
- // Mapper Implementation
74
- // ============================================================================
75
64
  class UserToDomainMapper extends Mapper<UserPersistence, User> {
76
65
  public build(persistence: UserPersistence): User {
77
66
  return User.create({
@@ -98,10 +87,6 @@ class UserToPersistenceMapper extends Mapper<User, UserPersistence> {
98
87
  }
99
88
  }
100
89
 
101
- // ============================================================================
102
- // Mock Repository Implementation
103
- // ============================================================================
104
-
105
90
  class MockUserRepository extends Repository<User> {
106
91
  private store: Map<string, UserPersistence> = new Map();
107
92
 
@@ -116,8 +101,8 @@ class MockUserRepository extends Repository<User> {
116
101
  return "users";
117
102
  }
118
103
 
119
- async create(entity: User) {
120
- const persistence = this.mapperToPersistence.build(entity);
104
+ async save(entity: User) {
105
+ const persistence = await this.mapperToPersistence.build(entity);
121
106
  this.store.set(entity.id.value, persistence);
122
107
  }
123
108
 
@@ -135,18 +120,13 @@ class MockUserRepository extends Repository<User> {
135
120
 
136
121
  async find(criteria: Criteria<User>): Promise<PaginatedResult<User>> {
137
122
  const result = await this.applyCriteria(criteria);
138
- return result;
123
+ return result as PaginatedResult<User>;
139
124
  }
140
125
 
141
126
  async findById(id: string): Promise<User | null> {
142
127
  const persistence = this.store.get(id);
143
128
  if (!persistence) return null;
144
- return this.mapperToDomain.build(persistence);
145
- }
146
-
147
- async update(entity: User): Promise<void> {
148
- const persistence = this.mapperToPersistence.build(entity);
149
- this.store.set(entity.id.value, persistence);
129
+ return await this.mapperToDomain.build(persistence);
150
130
  }
151
131
 
152
132
  protected async applyCriteria(criteria: Criteria<User>) {
@@ -217,10 +197,6 @@ class MockUserRepository extends Repository<User> {
217
197
  }
218
198
  }
219
199
 
220
- // ============================================================================
221
- // Tests
222
- // ============================================================================
223
-
224
200
  describe("Repository", () => {
225
201
  describe("InMemoryRepository", () => {
226
202
  let repository: InMemoryRepository<User>;
@@ -262,7 +238,7 @@ describe("Repository", () => {
262
238
 
263
239
  describe("save and findById", () => {
264
240
  it("should save and retrieve user", async () => {
265
- await repository.create(user1);
241
+ await repository.save(user1);
266
242
  const found = await repository.findById(user1.id.value);
267
243
 
268
244
  expect(found).toBeDefined();
@@ -276,9 +252,9 @@ describe("Repository", () => {
276
252
  });
277
253
 
278
254
  it("should update existing user", async () => {
279
- await repository.create(user1);
255
+ await repository.save(user1);
280
256
  user1.setName("Alice Updated");
281
- await repository.create(user1);
257
+ await repository.save(user1);
282
258
 
283
259
  const found = await repository.findById(user1.id.value);
284
260
  expect(found?.name).toBe("Alice Updated");
@@ -292,18 +268,18 @@ describe("Repository", () => {
292
268
  });
293
269
 
294
270
  it("should return all users", async () => {
295
- await repository.create(user1);
296
- await repository.create(user2);
297
- await repository.create(user3);
271
+ await repository.save(user1);
272
+ await repository.save(user2);
273
+ await repository.save(user3);
298
274
 
299
275
  const users = await repository.findAll();
300
276
  expect(users).toHaveLength(3);
301
277
  });
302
278
 
303
279
  it("should return users matching criteria", async () => {
304
- await repository.create(user1);
305
- await repository.create(user2);
306
- await repository.create(user3);
280
+ await repository.save(user1);
281
+ await repository.save(user2);
282
+ await repository.save(user3);
307
283
 
308
284
  const criteria = Criteria.create<User>().whereEquals(
309
285
  "status",
@@ -318,9 +294,9 @@ describe("Repository", () => {
318
294
 
319
295
  describe("find with Criteria", () => {
320
296
  beforeEach(async () => {
321
- await repository.create(user1);
322
- await repository.create(user2);
323
- await repository.create(user3);
297
+ await repository.save(user1);
298
+ await repository.save(user2);
299
+ await repository.save(user3);
324
300
  });
325
301
 
326
302
  it("should filter by equals", async () => {
@@ -375,8 +351,8 @@ describe("Repository", () => {
375
351
 
376
352
  describe("findOne", () => {
377
353
  it("should find first matching user", async () => {
378
- await repository.create(user1);
379
- await repository.create(user2);
354
+ await repository.save(user1);
355
+ await repository.save(user2);
380
356
 
381
357
  const criteria = Criteria.create<User>().whereEquals(
382
358
  "status",
@@ -401,7 +377,7 @@ describe("Repository", () => {
401
377
 
402
378
  describe("delete", () => {
403
379
  it("should delete by aggregate", async () => {
404
- await repository.create(user1);
380
+ await repository.save(user1);
405
381
  await repository.delete(user1);
406
382
 
407
383
  const found = await repository.findById(user1.id.value);
@@ -409,7 +385,7 @@ describe("Repository", () => {
409
385
  });
410
386
 
411
387
  it("should delete by id", async () => {
412
- await repository.create(user1);
388
+ await repository.save(user1);
413
389
  await repository.delete(user1);
414
390
 
415
391
  const found = await repository.findById(user1.id.value);
@@ -419,7 +395,7 @@ describe("Repository", () => {
419
395
 
420
396
  describe("exists", () => {
421
397
  it("should return true when user exists", async () => {
422
- await repository.create(user1);
398
+ await repository.save(user1);
423
399
  const exists = await repository.exists(user1.id.value);
424
400
  expect(exists).toBe(true);
425
401
  });
@@ -432,9 +408,9 @@ describe("Repository", () => {
432
408
 
433
409
  describe("count", () => {
434
410
  beforeEach(async () => {
435
- await repository.create(user1);
436
- await repository.create(user2);
437
- await repository.create(user3);
411
+ await repository.save(user1);
412
+ await repository.save(user2);
413
+ await repository.save(user3);
438
414
  });
439
415
 
440
416
  it("should count all users", async () => {
@@ -452,7 +428,7 @@ describe("Repository", () => {
452
428
  });
453
429
  });
454
430
 
455
- describe("saveMany", () => {
431
+ describe("createMany", () => {
456
432
  it("should save multiple users", async () => {
457
433
  await repository.createMany([user1, user2, user3]);
458
434
 
@@ -485,7 +461,7 @@ describe("Repository", () => {
485
461
 
486
462
  it("should save new user (insert)", async () => {
487
463
  expect(user.isNew()).toBe(true);
488
- await repository.create(user);
464
+ await repository.save(user);
489
465
 
490
466
  const found = await repository.findById(user.id.value);
491
467
  expect(found).toBeDefined();
@@ -493,7 +469,7 @@ describe("Repository", () => {
493
469
  });
494
470
 
495
471
  it("should update existing user", async () => {
496
- await repository.create(user);
472
+ await repository.save(user);
497
473
 
498
474
  // Simulate existing user
499
475
  const existingUser = User.create({
@@ -504,7 +480,7 @@ describe("Repository", () => {
504
480
  status: "inactive",
505
481
  });
506
482
 
507
- await repository.create(existingUser);
483
+ await repository.save(existingUser);
508
484
 
509
485
  const found = await repository.findById(user.id.value);
510
486
  expect(found?.name).toBe("John Updated");
@@ -512,7 +488,7 @@ describe("Repository", () => {
512
488
  });
513
489
 
514
490
  it("should convert between domain and persistence", async () => {
515
- await repository.create(user);
491
+ await repository.save(user);
516
492
  const found = await repository.findById(user.id.value);
517
493
 
518
494
  // Should be a domain object
@@ -534,7 +510,7 @@ describe("Repository", () => {
534
510
  status: "inactive",
535
511
  });
536
512
 
537
- await Promise.all([repository.create(user1), repository.create(user2)]);
513
+ await Promise.all([repository.save(user1), repository.save(user2)]);
538
514
 
539
515
  const result = await repository.find(
540
516
  Criteria.create<User>()
@@ -1,5 +1,5 @@
1
1
  import { Entity, Id } from "../src";
2
- import { Post, User, Address, Comment } from "./utils";
2
+ import { Post, User, Address } from "./utils";
3
3
 
4
4
  describe("toJson Functionality", () => {
5
5
  it("should convert simple entity to JSON", () => {
@@ -7,7 +7,8 @@ describe("toJson Functionality", () => {
7
7
  id: new Id("1"),
8
8
  title: "First Post",
9
9
  content: "Hello World",
10
- likes: 5,
10
+ published: false,
11
+ comments: [],
11
12
  });
12
13
 
13
14
  const json = post.toJson();
@@ -15,7 +16,8 @@ describe("toJson Functionality", () => {
15
16
  id: "1",
16
17
  title: "First Post",
17
18
  content: "Hello World",
18
- likes: 5,
19
+ published: false,
20
+ comments: [],
19
21
  });
20
22
  });
21
23
 
@@ -29,27 +31,22 @@ describe("toJson Functionality", () => {
29
31
  id: new Id("1"),
30
32
  title: "Post 1",
31
33
  content: "Content 1",
32
- likes: 0,
34
+ published: false,
35
+ comments: [],
33
36
  }),
34
37
  new Post({
35
38
  id: new Id("2"),
36
39
  title: "Post 2",
37
40
  content: "Content 2",
38
- likes: 5,
41
+ published: false,
42
+ comments: [],
39
43
  }),
40
44
  ],
41
45
  address: new Address({
42
46
  street: "Main St",
43
47
  city: "NYC",
44
- zipCode: "10001",
45
48
  }),
46
- comments: [
47
- new Comment({
48
- id: new Id("1"),
49
- text: "Great post!",
50
- author: "Alice",
51
- }),
52
- ],
49
+ tags: [],
53
50
  });
54
51
 
55
52
  const json = user.toJson();
@@ -58,8 +55,7 @@ describe("toJson Functionality", () => {
58
55
  expect(json.name).toBe("John Doe");
59
56
  expect(json.posts).toHaveLength(2);
60
57
  expect(json.posts[0].title).toBe("Post 1");
61
- expect(json.address.city).toBe("NYC");
62
- expect(json.comments[0].text).toBe("Great post!");
58
+ expect(json.address?.city).toBe("NYC");
63
59
  });
64
60
 
65
61
  it("should handle deeply nested structures", () => {
@@ -72,15 +68,15 @@ describe("toJson Functionality", () => {
72
68
  id: new Id("1"),
73
69
  title: "Post 1",
74
70
  content: "Content 1",
75
- likes: 0,
71
+ published: false,
72
+ comments: [],
76
73
  }),
77
74
  ],
78
75
  address: new Address({
79
76
  street: "Main St",
80
77
  city: "NYC",
81
- zipCode: "10001",
82
78
  }),
83
- comments: [],
79
+ tags: [],
84
80
  });
85
81
 
86
82
  const json = user.toJson();