@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.
- package/CHANGELOG.md +33 -0
- package/dist/aggregate-changes.d.ts +164 -0
- package/dist/aggregate-changes.d.ts.map +1 -0
- package/dist/aggregate-changes.js +281 -0
- package/dist/aggregate-changes.js.map +1 -0
- package/dist/base-entity.d.ts +32 -8
- package/dist/base-entity.d.ts.map +1 -1
- package/dist/base-entity.js +117 -86
- package/dist/base-entity.js.map +1 -1
- package/dist/criteria.d.ts +3 -3
- package/dist/criteria.d.ts.map +1 -1
- package/dist/criteria.js.map +1 -1
- package/dist/crypto.d.ts +3 -0
- package/dist/crypto.d.ts.map +1 -0
- package/dist/crypto.js +29 -0
- package/dist/crypto.js.map +1 -0
- package/dist/entity-changes.d.ts +84 -0
- package/dist/entity-changes.d.ts.map +1 -0
- package/dist/entity-changes.js +135 -0
- package/dist/entity-changes.js.map +1 -0
- package/dist/entity-schema-registry.d.ts +148 -0
- package/dist/entity-schema-registry.d.ts.map +1 -0
- package/dist/entity-schema-registry.js +219 -0
- package/dist/entity-schema-registry.js.map +1 -0
- package/dist/history-tracker.d.ts +97 -0
- package/dist/history-tracker.d.ts.map +1 -0
- package/dist/history-tracker.js +805 -0
- package/dist/history-tracker.js.map +1 -0
- package/dist/id.d.ts +11 -10
- package/dist/id.d.ts.map +1 -1
- package/dist/id.js +4 -28
- package/dist/id.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/mapper.d.ts +1 -1
- package/dist/mapper.d.ts.map +1 -1
- package/dist/mapper.js.map +1 -1
- package/dist/repository/base-repository.d.ts +6 -32
- package/dist/repository/base-repository.d.ts.map +1 -1
- package/dist/repository/base-repository.js +0 -27
- package/dist/repository/base-repository.js.map +1 -1
- package/dist/repository/unit-of-work.d.ts +0 -25
- package/dist/repository/unit-of-work.d.ts.map +1 -1
- package/dist/repository/unit-of-work.js +0 -25
- package/dist/repository/unit-of-work.js.map +1 -1
- package/dist/types/change-tracker.d.ts +186 -0
- package/dist/types/change-tracker.d.ts.map +1 -0
- package/dist/types/change-tracker.js +2 -0
- package/dist/types/change-tracker.js.map +1 -0
- package/dist/types/criteria.d.ts +5 -1
- package/dist/types/criteria.d.ts.map +1 -1
- package/dist/types/history-tracker.d.ts +11 -0
- package/dist/types/history-tracker.d.ts.map +1 -1
- package/dist/types/utils.d.ts +0 -1
- package/dist/types/utils.d.ts.map +1 -1
- package/dist/validation-error.d.ts.map +1 -1
- package/dist/validation-error.js +0 -3
- package/dist/validation-error.js.map +1 -1
- package/dist/value-object.d.ts +57 -8
- package/dist/value-object.d.ts.map +1 -1
- package/dist/value-object.js +49 -21
- package/dist/value-object.js.map +1 -1
- package/package.json +2 -1
- package/src/aggregate-changes.ts +335 -0
- package/src/base-entity.ts +140 -100
- package/src/criteria.ts +2 -1
- package/src/crypto.ts +31 -0
- package/src/entity-changes.ts +151 -0
- package/src/entity-schema-registry.ts +275 -0
- package/src/history-tracker.ts +1114 -0
- package/src/id.ts +17 -26
- package/src/index.ts +1 -0
- package/src/mapper.ts +4 -1
- package/src/repository/base-repository.ts +6 -37
- package/src/repository/unit-of-work.ts +0 -25
- package/src/types/change-tracker.ts +221 -0
- package/src/types/criteria.ts +6 -1
- package/src/types/history-tracker.ts +13 -0
- package/src/types/utils.ts +0 -9
- package/src/validation-error.ts +0 -4
- package/src/value-object.ts +84 -23
- package/tests/aggregate-changes.test.ts +284 -0
- package/tests/criteria.test.ts +122 -161
- package/tests/entity-equality.test.ts +38 -61
- package/tests/entity-schema-registry.test.ts +382 -0
- package/tests/entity-validation.test.ts +7 -94
- package/tests/history-tracker.spec.ts +349 -617
- package/tests/id.test.ts +41 -44
- package/tests/load-test/data.json +346041 -0
- package/tests/load-test/entities.ts +97 -0
- package/tests/load-test/generate-data.ts +81 -0
- package/tests/load-test/lead-to-domain.mapper.ts +24 -0
- package/tests/load-test/load.test.ts +38 -0
- package/tests/repository.test.ts +30 -54
- package/tests/to-json.test.ts +14 -18
- package/tests/utils.ts +138 -102
- package/tests/value-objects.test.ts +57 -29
- package/dist/deep-proxy.d.ts +0 -36
- package/dist/deep-proxy.d.ts.map +0 -1
- package/dist/deep-proxy.js +0 -384
- package/dist/deep-proxy.js.map +0 -1
- package/src/deep-proxy.ts +0 -447
- 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
|
+
});
|
package/tests/repository.test.ts
CHANGED
|
@@ -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
|
|
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.
|
|
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.
|
|
255
|
+
await repository.save(user1);
|
|
280
256
|
user1.setName("Alice Updated");
|
|
281
|
-
await repository.
|
|
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.
|
|
296
|
-
await repository.
|
|
297
|
-
await repository.
|
|
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.
|
|
305
|
-
await repository.
|
|
306
|
-
await repository.
|
|
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.
|
|
322
|
-
await repository.
|
|
323
|
-
await repository.
|
|
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.
|
|
379
|
-
await repository.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
436
|
-
await repository.
|
|
437
|
-
await repository.
|
|
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("
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
513
|
+
await Promise.all([repository.save(user1), repository.save(user2)]);
|
|
538
514
|
|
|
539
515
|
const result = await repository.find(
|
|
540
516
|
Criteria.create<User>()
|
package/tests/to-json.test.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Entity, Id } from "../src";
|
|
2
|
-
import { Post, User, Address
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
79
|
+
tags: [],
|
|
84
80
|
});
|
|
85
81
|
|
|
86
82
|
const json = user.toJson();
|