@slingr/cli 0.0.3 → 0.0.4
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/LICENSE.txt +202 -0
- package/README.md +490 -319
- package/bin/dev.cmd +2 -2
- package/bin/dev.js +5 -5
- package/bin/run.cmd +2 -2
- package/bin/run.js +4 -4
- package/bin/slingr +1 -0
- package/dist/commands/build.d.ts +20 -0
- package/dist/commands/build.d.ts.map +1 -0
- package/dist/commands/build.js +206 -0
- package/dist/commands/build.js.map +1 -0
- package/dist/commands/create-app.d.ts +0 -1
- package/dist/commands/create-app.d.ts.map +1 -1
- package/dist/commands/create-app.js +38 -57
- package/dist/commands/create-app.js.map +1 -1
- package/dist/commands/debug.d.ts +28 -0
- package/dist/commands/debug.d.ts.map +1 -0
- package/dist/commands/debug.js +474 -0
- package/dist/commands/debug.js.map +1 -0
- package/dist/commands/ds.d.ts +14 -1
- package/dist/commands/ds.d.ts.map +1 -1
- package/dist/commands/ds.js +450 -121
- package/dist/commands/ds.js.map +1 -1
- package/dist/commands/gql.d.ts +1 -1
- package/dist/commands/gql.d.ts.map +1 -1
- package/dist/commands/gql.js +190 -184
- package/dist/commands/gql.js.map +1 -1
- package/dist/commands/infra/down.d.ts.map +1 -1
- package/dist/commands/infra/down.js +8 -7
- package/dist/commands/infra/down.js.map +1 -1
- package/dist/commands/infra/up.d.ts.map +1 -1
- package/dist/commands/infra/up.js +8 -7
- package/dist/commands/infra/up.js.map +1 -1
- package/dist/commands/infra/update.d.ts +1 -0
- package/dist/commands/infra/update.d.ts.map +1 -1
- package/dist/commands/infra/update.js +33 -69
- package/dist/commands/infra/update.js.map +1 -1
- package/dist/commands/run.d.ts +29 -2
- package/dist/commands/run.d.ts.map +1 -1
- package/dist/commands/run.js +628 -130
- package/dist/commands/run.js.map +1 -1
- package/dist/commands/setup.d.ts +1 -1
- package/dist/commands/setup.d.ts.map +1 -1
- package/dist/commands/setup.js +34 -71
- package/dist/commands/setup.js.map +1 -1
- package/dist/commands/sync-metadata.d.ts +15 -0
- package/dist/commands/sync-metadata.d.ts.map +1 -0
- package/dist/commands/sync-metadata.js +225 -0
- package/dist/commands/sync-metadata.js.map +1 -0
- package/dist/commands/users.d.ts +30 -0
- package/dist/commands/users.d.ts.map +1 -0
- package/dist/commands/users.js +472 -0
- package/dist/commands/users.js.map +1 -0
- package/dist/commands/views.d.ts +11 -0
- package/dist/commands/views.d.ts.map +1 -0
- package/dist/commands/views.js +73 -0
- package/dist/commands/views.js.map +1 -0
- package/dist/projectStructure.d.ts +2 -2
- package/dist/projectStructure.d.ts.map +1 -1
- package/dist/projectStructure.js +281 -69
- package/dist/projectStructure.js.map +1 -1
- package/dist/scripts/generate-metadata.d.ts +13 -0
- package/dist/scripts/generate-metadata.d.ts.map +1 -0
- package/dist/scripts/generate-metadata.js +412 -0
- package/dist/scripts/generate-metadata.js.map +1 -0
- package/dist/scripts/generate-metadata.ts +498 -0
- package/dist/scripts/generate-schema.d.ts +1 -1
- package/dist/scripts/generate-schema.js +168 -74
- package/dist/scripts/generate-schema.js.map +1 -1
- package/dist/scripts/generate-schema.ts +258 -143
- package/dist/templates/.env.template +23 -0
- package/dist/templates/.firebaserc.template +5 -0
- package/dist/templates/.github/copilot-instructions.md.template +652 -17
- package/dist/templates/backend/Dockerfile.template +30 -0
- package/dist/templates/config/datasource.ts.template +12 -9
- package/dist/templates/config/jest.config.ts +30 -30
- package/dist/templates/config/jest.setup.ts +1 -1
- package/dist/templates/config/tsconfig.json.template +50 -29
- package/dist/templates/dataSources/mysql.ts.template +16 -13
- package/dist/templates/dataSources/postgres.ts.template +15 -13
- package/dist/templates/dataset-generator-script.ts.template +139 -139
- package/dist/templates/datasets/mysql-default/.slingr-schema.json.template +5 -0
- package/dist/templates/datasets/mysql-default/Address.jsonl.template +3 -3
- package/dist/templates/datasets/mysql-default/App.jsonl.template +4 -4
- package/dist/templates/datasets/mysql-default/Company.jsonl.template +3 -3
- package/dist/templates/datasets/mysql-default/Person.jsonl.template +2 -2
- package/dist/templates/datasets/mysql-default/User.jsonl.template +1 -0
- package/dist/templates/datasets/mysql-default/instructions.md.template +1 -0
- package/dist/templates/datasets/postgres-default/.slingr-schema.json.template +5 -0
- package/dist/templates/datasets/postgres-default/Address.jsonl.template +3 -3
- package/dist/templates/datasets/postgres-default/App.jsonl.template +4 -4
- package/dist/templates/datasets/postgres-default/Company.jsonl.template +3 -3
- package/dist/templates/datasets/postgres-default/Person.jsonl.template +2 -2
- package/dist/templates/datasets/postgres-default/User.jsonl.template +1 -0
- package/dist/templates/datasets/postgres-default/instructions.md.template +1 -0
- package/dist/templates/docker-compose.prod-test.yml.template +32 -0
- package/dist/templates/docker-compose.yml.template +24 -0
- package/dist/templates/docs/app-description.md.template +33 -33
- package/dist/templates/firebase.json.template +68 -0
- package/dist/templates/frontend/.umirc.ts.template +23 -0
- package/dist/templates/frontend/package.json.template +45 -0
- package/dist/templates/frontend/public/config.json +6 -0
- package/dist/templates/frontend/public/logo.svg +6 -0
- package/dist/templates/frontend/src/app.tsx.template +44 -0
- package/dist/templates/frontend/src/global.less.template +117 -0
- package/dist/templates/frontend/src/layouts/MainLayout.tsx.template +75 -0
- package/dist/templates/frontend/src/types/graphql-augmentation.d.ts.template +44 -0
- package/dist/templates/frontend/src/views/customViews/user/UserCreateView.tsx.template +18 -0
- package/dist/templates/frontend/src/views/customViews/user/UserEditView.tsx.template +29 -0
- package/dist/templates/frontend/src/views/customViews/user/UserReadView.tsx.template +24 -0
- package/dist/templates/frontend/src/views/customViews/user/UserTableView.tsx.template +38 -0
- package/dist/templates/frontend/src/views/customViews/welcome.tsx.template +34 -0
- package/dist/templates/frontend/tsconfig.json.template +50 -0
- package/dist/templates/gql/codegen.yml.template +25 -25
- package/dist/templates/gql/index.ts.template +17 -24
- package/dist/templates/gql/operations.graphql.template +30 -30
- package/dist/templates/ops/README.md.template +1045 -0
- package/dist/templates/ops/cloudbuild.yaml.template +161 -0
- package/dist/templates/ops/scripts/_utils.js.template +217 -0
- package/dist/templates/ops/scripts/deploy.js.template +145 -0
- package/dist/templates/ops/scripts/setup-gcp.js.template +330 -0
- package/dist/templates/ops/scripts/setup-secrets.js.template +76 -0
- package/dist/templates/ops/scripts/test-prod-local.js.template +49 -0
- package/dist/templates/package.json.template +50 -38
- package/dist/templates/pnpm-workspace.yaml.template +3 -0
- package/dist/templates/prompt-analysis.md.template +110 -110
- package/dist/templates/prompt-script-generation.md.template +258 -258
- package/dist/templates/src/Address.ts.template +28 -31
- package/dist/templates/src/App.ts.template +17 -61
- package/dist/templates/src/Company.ts.template +41 -47
- package/dist/templates/src/Models.test.ts.template +654 -654
- package/dist/templates/src/Person.test.ts.template +289 -289
- package/dist/templates/src/Person.ts.template +90 -105
- package/dist/templates/src/actions/index.ts.template +11 -11
- package/dist/templates/src/auth/permissions.ts.template +34 -0
- package/dist/templates/src/data/App.ts.template +48 -0
- package/dist/templates/src/data/User.ts.template +35 -0
- package/dist/templates/src/types/gql.d.ts.template +17 -17
- package/dist/templates/vscode/extensions.json +4 -3
- package/dist/templates/vscode/settings.json +17 -11
- package/dist/templates/workspace-package.json.template +21 -0
- package/dist/utils/buildCache.d.ts +12 -0
- package/dist/utils/buildCache.d.ts.map +1 -0
- package/dist/utils/buildCache.js +102 -0
- package/dist/utils/buildCache.js.map +1 -0
- package/dist/utils/checkFramework.d.ts +27 -0
- package/dist/utils/checkFramework.d.ts.map +1 -0
- package/dist/utils/checkFramework.js +104 -0
- package/dist/utils/checkFramework.js.map +1 -0
- package/dist/utils/datasourceParser.d.ts +11 -0
- package/dist/utils/datasourceParser.d.ts.map +1 -1
- package/dist/utils/datasourceParser.js +154 -56
- package/dist/utils/datasourceParser.js.map +1 -1
- package/dist/utils/dockerManager.d.ts +25 -0
- package/dist/utils/dockerManager.d.ts.map +1 -0
- package/dist/utils/dockerManager.js +281 -0
- package/dist/utils/dockerManager.js.map +1 -0
- package/dist/utils/infraFileParser.d.ts +26 -0
- package/dist/utils/infraFileParser.d.ts.map +1 -0
- package/dist/utils/infraFileParser.js +75 -0
- package/dist/utils/infraFileParser.js.map +1 -0
- package/dist/utils/jsonlLoader.d.ts +91 -12
- package/dist/utils/jsonlLoader.d.ts.map +1 -1
- package/dist/utils/jsonlLoader.js +674 -63
- package/dist/utils/jsonlLoader.js.map +1 -1
- package/dist/utils/model-analyzer.d.ts.map +1 -1
- package/dist/utils/model-analyzer.js +67 -13
- package/dist/utils/model-analyzer.js.map +1 -1
- package/dist/utils/userManagement.d.ts +57 -0
- package/dist/utils/userManagement.d.ts.map +1 -0
- package/dist/utils/userManagement.js +288 -0
- package/dist/utils/userManagement.js.map +1 -0
- package/dist/utils/viewsGenerator.d.ts +15 -0
- package/dist/utils/viewsGenerator.d.ts.map +1 -0
- package/dist/utils/viewsGenerator.js +311 -0
- package/dist/utils/viewsGenerator.js.map +1 -0
- package/oclif.manifest.json +445 -20
- package/package.json +29 -26
- package/src/templates/.env.template +23 -0
- package/src/templates/.firebaserc.template +5 -0
- package/src/templates/.github/copilot-instructions.md.template +652 -17
- package/src/templates/backend/Dockerfile.template +30 -0
- package/src/templates/config/datasource.ts.template +12 -9
- package/src/templates/config/jest.config.ts +30 -30
- package/src/templates/config/jest.setup.ts +1 -1
- package/src/templates/config/tsconfig.json.template +50 -29
- package/src/templates/dataSources/mysql.ts.template +16 -13
- package/src/templates/dataSources/postgres.ts.template +15 -13
- package/src/templates/dataset-generator-script.ts.template +139 -139
- package/src/templates/datasets/mysql-default/.slingr-schema.json.template +5 -0
- package/src/templates/datasets/mysql-default/Address.jsonl.template +3 -3
- package/src/templates/datasets/mysql-default/App.jsonl.template +4 -4
- package/src/templates/datasets/mysql-default/Company.jsonl.template +3 -3
- package/src/templates/datasets/mysql-default/Person.jsonl.template +2 -2
- package/src/templates/datasets/mysql-default/User.jsonl.template +1 -0
- package/src/templates/datasets/mysql-default/instructions.md.template +1 -0
- package/src/templates/datasets/postgres-default/.slingr-schema.json.template +5 -0
- package/src/templates/datasets/postgres-default/Address.jsonl.template +3 -3
- package/src/templates/datasets/postgres-default/App.jsonl.template +4 -4
- package/src/templates/datasets/postgres-default/Company.jsonl.template +3 -3
- package/src/templates/datasets/postgres-default/Person.jsonl.template +2 -2
- package/src/templates/datasets/postgres-default/User.jsonl.template +1 -0
- package/src/templates/datasets/postgres-default/instructions.md.template +1 -0
- package/src/templates/docker-compose.prod-test.yml.template +32 -0
- package/src/templates/docker-compose.yml.template +24 -0
- package/src/templates/docs/app-description.md.template +33 -33
- package/src/templates/firebase.json.template +68 -0
- package/src/templates/frontend/.umirc.ts.template +23 -0
- package/src/templates/frontend/package.json.template +45 -0
- package/src/templates/frontend/public/config.json +6 -0
- package/src/templates/frontend/public/logo.svg +6 -0
- package/src/templates/frontend/src/app.tsx.template +44 -0
- package/src/templates/frontend/src/global.less.template +117 -0
- package/src/templates/frontend/src/layouts/MainLayout.tsx.template +75 -0
- package/src/templates/frontend/src/types/graphql-augmentation.d.ts.template +44 -0
- package/src/templates/frontend/src/views/customViews/user/UserCreateView.tsx.template +18 -0
- package/src/templates/frontend/src/views/customViews/user/UserEditView.tsx.template +29 -0
- package/src/templates/frontend/src/views/customViews/user/UserReadView.tsx.template +24 -0
- package/src/templates/frontend/src/views/customViews/user/UserTableView.tsx.template +38 -0
- package/src/templates/frontend/src/views/customViews/welcome.tsx.template +34 -0
- package/src/templates/frontend/tsconfig.json.template +50 -0
- package/src/templates/gql/codegen.yml.template +25 -25
- package/src/templates/gql/index.ts.template +17 -24
- package/src/templates/gql/operations.graphql.template +30 -30
- package/src/templates/ops/README.md.template +1045 -0
- package/src/templates/ops/cloudbuild.yaml.template +161 -0
- package/src/templates/ops/scripts/_utils.js.template +217 -0
- package/src/templates/ops/scripts/deploy.js.template +145 -0
- package/src/templates/ops/scripts/setup-gcp.js.template +330 -0
- package/src/templates/ops/scripts/setup-secrets.js.template +76 -0
- package/src/templates/ops/scripts/test-prod-local.js.template +49 -0
- package/src/templates/package.json.template +50 -38
- package/src/templates/pnpm-workspace.yaml.template +3 -0
- package/src/templates/prompt-analysis.md.template +110 -110
- package/src/templates/prompt-script-generation.md.template +258 -258
- package/src/templates/src/Address.ts.template +28 -31
- package/src/templates/src/App.ts.template +17 -61
- package/src/templates/src/Company.ts.template +41 -47
- package/src/templates/src/Models.test.ts.template +654 -654
- package/src/templates/src/Person.test.ts.template +289 -289
- package/src/templates/src/Person.ts.template +90 -105
- package/src/templates/src/actions/index.ts.template +11 -11
- package/src/templates/src/auth/permissions.ts.template +34 -0
- package/src/templates/src/data/App.ts.template +48 -0
- package/src/templates/src/data/User.ts.template +35 -0
- package/src/templates/src/types/gql.d.ts.template +17 -17
- package/src/templates/vscode/extensions.json +4 -3
- package/src/templates/vscode/settings.json +17 -11
- package/src/templates/workspace-package.json.template +21 -0
- package/dist/templates/src/index.ts +0 -66
- package/src/templates/src/index.ts +0 -66
|
@@ -1,655 +1,655 @@
|
|
|
1
|
-
import { App } from './App';
|
|
2
|
-
import { Person } from './Person';
|
|
3
|
-
import { Company } from './Company';
|
|
4
|
-
import { Address } from './Address';
|
|
5
|
-
|
|
6
|
-
describe('Models Integration Tests', () => {
|
|
7
|
-
describe('Person Model', () => {
|
|
8
|
-
it('should validate a valid adult person', async () => {
|
|
9
|
-
const personData = {
|
|
10
|
-
firstName: 'John',
|
|
11
|
-
lastName: 'Doe',
|
|
12
|
-
email: 'john@example.com',
|
|
13
|
-
age: 25,
|
|
14
|
-
phoneNumber: '123-456-7890',
|
|
15
|
-
additionalInfo: '<p>Senior Developer</p>',
|
|
16
|
-
isActive: true
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
const person = Person.fromJSON(personData);
|
|
20
|
-
const errors = await person.validate();
|
|
21
|
-
|
|
22
|
-
expect(errors.length).toBe(0);
|
|
23
|
-
expect(person.firstName).toBe('John');
|
|
24
|
-
expect(person.lastName).toBe('Doe');
|
|
25
|
-
expect(person.age).toBe(25);
|
|
26
|
-
expect(person.isActive).toBe(true);
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
it('should validate a valid minor person with parent email', async () => {
|
|
30
|
-
const personData = {
|
|
31
|
-
firstName: 'Jane',
|
|
32
|
-
lastName: 'Smith',
|
|
33
|
-
email: 'jane@example.com',
|
|
34
|
-
age: 16,
|
|
35
|
-
parentEmail: 'parent@example.com',
|
|
36
|
-
additionalInfo: '<p>Student</p>',
|
|
37
|
-
isActive: true
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
const person = Person.fromJSON(personData);
|
|
41
|
-
const errors = await person.validate();
|
|
42
|
-
|
|
43
|
-
expect(errors.length).toBe(0);
|
|
44
|
-
expect(person.parentEmail).toBe('parent@example.com');
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
it('should fail validation when firstName is too short', async () => {
|
|
48
|
-
const personData = {
|
|
49
|
-
firstName: 'J',
|
|
50
|
-
lastName: 'Doe',
|
|
51
|
-
email: 'john@example.com',
|
|
52
|
-
age: 25
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
const person = Person.fromJSON(personData);
|
|
56
|
-
const errors = await person.validate();
|
|
57
|
-
|
|
58
|
-
expect(errors.length).toBeGreaterThan(0);
|
|
59
|
-
const firstNameError = errors.find(e => e.property === 'firstName');
|
|
60
|
-
expect(firstNameError).toBeDefined();
|
|
61
|
-
expect(firstNameError?.constraints).toHaveProperty('minLength');
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
it('should fail validation when firstName contains numbers', async () => {
|
|
65
|
-
const personData = {
|
|
66
|
-
firstName: 'John123',
|
|
67
|
-
lastName: 'Doe',
|
|
68
|
-
email: 'john@example.com',
|
|
69
|
-
age: 25
|
|
70
|
-
};
|
|
71
|
-
|
|
72
|
-
const person = Person.fromJSON(personData);
|
|
73
|
-
const errors = await person.validate();
|
|
74
|
-
|
|
75
|
-
expect(errors.length).toBeGreaterThan(0);
|
|
76
|
-
const firstNameError = errors.find(e => e.property === 'firstName');
|
|
77
|
-
expect(firstNameError).toBeDefined();
|
|
78
|
-
expect(firstNameError?.constraints).toHaveProperty('matches');
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
it('should fail validation when email is invalid', async () => {
|
|
82
|
-
const personData = {
|
|
83
|
-
firstName: 'John',
|
|
84
|
-
lastName: 'Doe',
|
|
85
|
-
email: 'invalid-email',
|
|
86
|
-
age: 25
|
|
87
|
-
};
|
|
88
|
-
|
|
89
|
-
const person = Person.fromJSON(personData);
|
|
90
|
-
const errors = await person.validate();
|
|
91
|
-
|
|
92
|
-
expect(errors.length).toBeGreaterThan(0);
|
|
93
|
-
const emailError = errors.find(e => e.property === 'email');
|
|
94
|
-
expect(emailError).toBeDefined();
|
|
95
|
-
expect(emailError?.constraints).toHaveProperty('isEmail');
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
it('should fail validation when age is negative', async () => {
|
|
99
|
-
const personData = {
|
|
100
|
-
firstName: 'John',
|
|
101
|
-
lastName: 'Doe',
|
|
102
|
-
email: 'john@example.com',
|
|
103
|
-
age: -5
|
|
104
|
-
};
|
|
105
|
-
|
|
106
|
-
const person = Person.fromJSON(personData);
|
|
107
|
-
const errors = await person.validate();
|
|
108
|
-
|
|
109
|
-
expect(errors.length).toBeGreaterThan(0);
|
|
110
|
-
const ageError = errors.find(e => e.property === 'age');
|
|
111
|
-
expect(ageError).toBeDefined();
|
|
112
|
-
expect(ageError?.constraints).toHaveProperty('invalidAge');
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
it('should require parentEmail for minors', async () => {
|
|
116
|
-
const personData = {
|
|
117
|
-
firstName: 'Jane',
|
|
118
|
-
lastName: 'Smith',
|
|
119
|
-
email: 'jane@example.com',
|
|
120
|
-
age: 16
|
|
121
|
-
// parentEmail missing
|
|
122
|
-
};
|
|
123
|
-
|
|
124
|
-
const person = Person.fromJSON(personData);
|
|
125
|
-
const errors = await person.validate();
|
|
126
|
-
|
|
127
|
-
expect(errors.length).toBeGreaterThan(0);
|
|
128
|
-
const parentEmailError = errors.find(e => e.property === 'parentEmail');
|
|
129
|
-
expect(parentEmailError).toBeDefined();
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
it('should exclude internalId from JSON output', () => {
|
|
133
|
-
const personData = {
|
|
134
|
-
firstName: 'John',
|
|
135
|
-
lastName: 'Doe',
|
|
136
|
-
email: 'john@example.com',
|
|
137
|
-
age: 25,
|
|
138
|
-
internalId: 'secret-123',
|
|
139
|
-
isActive: true
|
|
140
|
-
};
|
|
141
|
-
|
|
142
|
-
const person = Person.fromJSON(personData);
|
|
143
|
-
const json = person.toJSON();
|
|
144
|
-
|
|
145
|
-
expect(json).not.toHaveProperty('internalId');
|
|
146
|
-
expect(json).toHaveProperty('firstName', 'John');
|
|
147
|
-
});
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
describe('Company Model', () => {
|
|
151
|
-
let validCEO: Person;
|
|
152
|
-
|
|
153
|
-
beforeEach(() => {
|
|
154
|
-
const ceoData = {
|
|
155
|
-
firstName: 'Robert',
|
|
156
|
-
lastName: 'Johnson',
|
|
157
|
-
email: 'robert@company.com',
|
|
158
|
-
age: 45,
|
|
159
|
-
phoneNumber: '+1987654321',
|
|
160
|
-
additionalInfo: '<p>CEO and Founder</p>',
|
|
161
|
-
isActive: true
|
|
162
|
-
};
|
|
163
|
-
validCEO = Person.fromJSON(ceoData);
|
|
164
|
-
});
|
|
165
|
-
|
|
166
|
-
it('should validate a valid company with CEO composition', async () => {
|
|
167
|
-
const companyData = {
|
|
168
|
-
name: "TechCorp Solutions",
|
|
169
|
-
ceo: validCEO
|
|
170
|
-
};
|
|
171
|
-
|
|
172
|
-
const company = Company.fromJSON(companyData);
|
|
173
|
-
const errors = await company.validate();
|
|
174
|
-
|
|
175
|
-
expect(errors.length).toBe(0);
|
|
176
|
-
expect(company.name).toBe("TechCorp Solutions");
|
|
177
|
-
expect(company.ceo).toBeDefined();
|
|
178
|
-
expect(company.ceo.firstName).toBe("Robert");
|
|
179
|
-
expect(company.ceo.lastName).toBe("Johnson");
|
|
180
|
-
});
|
|
181
|
-
|
|
182
|
-
it('should fail validation with invalid company name (too short)', async () => {
|
|
183
|
-
const companyData = {
|
|
184
|
-
name: "X", // Too short
|
|
185
|
-
ceo: validCEO
|
|
186
|
-
};
|
|
187
|
-
|
|
188
|
-
const company = Company.fromJSON(companyData);
|
|
189
|
-
const errors = await company.validate();
|
|
190
|
-
|
|
191
|
-
expect(errors.length).toBeGreaterThan(0);
|
|
192
|
-
const nameError = errors.find(e => e.property === 'name');
|
|
193
|
-
expect(nameError).toBeDefined();
|
|
194
|
-
expect(nameError?.constraints).toHaveProperty('minLength');
|
|
195
|
-
});
|
|
196
|
-
|
|
197
|
-
it('should fail validation with invalid company name (special chars)', async () => {
|
|
198
|
-
const companyData = {
|
|
199
|
-
name: "Company@#$%", // Invalid characters
|
|
200
|
-
ceo: validCEO
|
|
201
|
-
};
|
|
202
|
-
|
|
203
|
-
const company = Company.fromJSON(companyData);
|
|
204
|
-
const errors = await company.validate();
|
|
205
|
-
|
|
206
|
-
expect(errors.length).toBeGreaterThan(0);
|
|
207
|
-
const nameError = errors.find(e => e.property === 'name');
|
|
208
|
-
expect(nameError).toBeDefined();
|
|
209
|
-
expect(nameError?.constraints).toHaveProperty('matches');
|
|
210
|
-
});
|
|
211
|
-
|
|
212
|
-
it('should fail validation when CEO is missing', async () => {
|
|
213
|
-
const companyData = {
|
|
214
|
-
name: "ValidCompany"
|
|
215
|
-
// ceo is missing
|
|
216
|
-
};
|
|
217
|
-
|
|
218
|
-
const company = Company.fromJSON(companyData);
|
|
219
|
-
const errors = await company.validate();
|
|
220
|
-
|
|
221
|
-
expect(errors.length).toBeGreaterThan(0);
|
|
222
|
-
const ceoError = errors.find(e => e.property === 'ceo');
|
|
223
|
-
expect(ceoError).toBeDefined();
|
|
224
|
-
});
|
|
225
|
-
|
|
226
|
-
it('should validate company with valid name containing dots and ampersands', async () => {
|
|
227
|
-
const companyData = {
|
|
228
|
-
name: "Tech & Solutions Co.",
|
|
229
|
-
ceo: validCEO
|
|
230
|
-
};
|
|
231
|
-
|
|
232
|
-
const company = Company.fromJSON(companyData);
|
|
233
|
-
const errors = await company.validate();
|
|
234
|
-
|
|
235
|
-
expect(errors.length).toBe(0);
|
|
236
|
-
expect(company.name).toBe("Tech & Solutions Co.");
|
|
237
|
-
});
|
|
238
|
-
});
|
|
239
|
-
|
|
240
|
-
describe('App Model', () => {
|
|
241
|
-
let validCompany: Company;
|
|
242
|
-
|
|
243
|
-
beforeEach(() => {
|
|
244
|
-
const ceoData = {
|
|
245
|
-
firstName: 'Sarah',
|
|
246
|
-
lastName: 'Davis',
|
|
247
|
-
email: 'sarah@company.com',
|
|
248
|
-
age: 40,
|
|
249
|
-
phoneNumber: '+1111222333',
|
|
250
|
-
additionalInfo: '<p>Visionary CEO</p>',
|
|
251
|
-
isActive: true
|
|
252
|
-
};
|
|
253
|
-
const ceo = Person.fromJSON(ceoData);
|
|
254
|
-
|
|
255
|
-
const companyData = {
|
|
256
|
-
name: "TechCorp Solutions",
|
|
257
|
-
ceo: ceo
|
|
258
|
-
};
|
|
259
|
-
validCompany = Company.fromJSON(companyData);
|
|
260
|
-
});
|
|
261
|
-
|
|
262
|
-
it('should validate a valid app with owner company reference', async () => {
|
|
263
|
-
const appData = {
|
|
264
|
-
name: "SlingrApp",
|
|
265
|
-
version: "01.00.01",
|
|
266
|
-
description: "A comprehensive application framework for building modern web applications",
|
|
267
|
-
ownerCompany: validCompany
|
|
268
|
-
};
|
|
269
|
-
|
|
270
|
-
const app = App.fromJSON(appData);
|
|
271
|
-
const errors = await app.validate();
|
|
272
|
-
|
|
273
|
-
expect(errors.length).toBe(0);
|
|
274
|
-
expect(app.name).toBe("SlingrApp");
|
|
275
|
-
expect(app.version).toBe("01.00.01");
|
|
276
|
-
expect(app.ownerCompany).toBeDefined();
|
|
277
|
-
expect(app.ownerCompany.name).toBe("TechCorp Solutions");
|
|
278
|
-
});
|
|
279
|
-
|
|
280
|
-
it('should fail validation with invalid name format (starts with dot)', async () => {
|
|
281
|
-
const appData = {
|
|
282
|
-
name: ".InvalidApp",
|
|
283
|
-
version: "01.00.01",
|
|
284
|
-
description: "A comprehensive application framework",
|
|
285
|
-
ownerCompany: validCompany
|
|
286
|
-
};
|
|
287
|
-
|
|
288
|
-
const app = App.fromJSON(appData);
|
|
289
|
-
const errors = await app.validate();
|
|
290
|
-
|
|
291
|
-
expect(errors.length).toBeGreaterThan(0);
|
|
292
|
-
const nameError = errors.find(e => e.property === 'name');
|
|
293
|
-
expect(nameError).toBeDefined();
|
|
294
|
-
expect(nameError?.constraints).toHaveProperty('matches');
|
|
295
|
-
});
|
|
296
|
-
|
|
297
|
-
it('should fail validation with invalid version format', async () => {
|
|
298
|
-
const appData = {
|
|
299
|
-
name: "ValidApp",
|
|
300
|
-
version: "1.0.1", // Should be 01.00.01
|
|
301
|
-
description: "A comprehensive application framework",
|
|
302
|
-
ownerCompany: validCompany
|
|
303
|
-
};
|
|
304
|
-
|
|
305
|
-
const app = App.fromJSON(appData);
|
|
306
|
-
const errors = await app.validate();
|
|
307
|
-
|
|
308
|
-
expect(errors.length).toBeGreaterThan(0);
|
|
309
|
-
const versionError = errors.find(e => e.property === 'version');
|
|
310
|
-
expect(versionError).toBeDefined();
|
|
311
|
-
expect(versionError?.constraints).toHaveProperty('matches');
|
|
312
|
-
});
|
|
313
|
-
|
|
314
|
-
it('should fail validation when owner company is missing', async () => {
|
|
315
|
-
const appData = {
|
|
316
|
-
name: "ValidApp",
|
|
317
|
-
version: "01.00.01",
|
|
318
|
-
description: "A comprehensive application framework"
|
|
319
|
-
// ownerCompany is missing
|
|
320
|
-
};
|
|
321
|
-
|
|
322
|
-
const app = App.fromJSON(appData);
|
|
323
|
-
const errors = await app.validate();
|
|
324
|
-
|
|
325
|
-
expect(errors.length).toBeGreaterThan(0);
|
|
326
|
-
const ownerError = errors.find(e => e.property === 'ownerCompany');
|
|
327
|
-
expect(ownerError).toBeDefined();
|
|
328
|
-
});
|
|
329
|
-
|
|
330
|
-
it('should validate app with valid name containing dots', async () => {
|
|
331
|
-
const appData = {
|
|
332
|
-
name: "App.Module.Core",
|
|
333
|
-
version: "02.05.12",
|
|
334
|
-
description: "A modular application core component",
|
|
335
|
-
ownerCompany: validCompany
|
|
336
|
-
};
|
|
337
|
-
|
|
338
|
-
const app = App.fromJSON(appData);
|
|
339
|
-
const errors = await app.validate();
|
|
340
|
-
|
|
341
|
-
expect(errors.length).toBe(0);
|
|
342
|
-
expect(app.name).toBe("App.Module.Core");
|
|
343
|
-
});
|
|
344
|
-
});
|
|
345
|
-
|
|
346
|
-
describe('Address Model', () => {
|
|
347
|
-
it('should validate a valid address', async () => {
|
|
348
|
-
const addressData = {
|
|
349
|
-
street: '123 Main Street',
|
|
350
|
-
zipCode: '12345',
|
|
351
|
-
country: 'United States'
|
|
352
|
-
};
|
|
353
|
-
|
|
354
|
-
const address = Address.fromJSON(addressData);
|
|
355
|
-
const errors = await address.validate();
|
|
356
|
-
|
|
357
|
-
expect(errors.length).toBe(0);
|
|
358
|
-
expect(address.street).toBe('123 Main Street');
|
|
359
|
-
expect(address.zipCode).toBe('12345');
|
|
360
|
-
expect(address.country).toBe('United States');
|
|
361
|
-
});
|
|
362
|
-
|
|
363
|
-
it('should fail validation when street is missing', async () => {
|
|
364
|
-
const addressData = {
|
|
365
|
-
zipCode: '12345',
|
|
366
|
-
country: 'United States'
|
|
367
|
-
// street is missing
|
|
368
|
-
};
|
|
369
|
-
|
|
370
|
-
const address = Address.fromJSON(addressData);
|
|
371
|
-
const errors = await address.validate();
|
|
372
|
-
|
|
373
|
-
expect(errors.length).toBeGreaterThan(0);
|
|
374
|
-
const streetError = errors.find(e => e.property === 'street');
|
|
375
|
-
expect(streetError).toBeDefined();
|
|
376
|
-
});
|
|
377
|
-
|
|
378
|
-
});
|
|
379
|
-
|
|
380
|
-
describe('Person with Address (SharedComposition)', () => {
|
|
381
|
-
let validAddress: Address;
|
|
382
|
-
|
|
383
|
-
beforeEach(() => {
|
|
384
|
-
const addressData = {
|
|
385
|
-
street: '456 Elm Avenue',
|
|
386
|
-
zipCode: '67890',
|
|
387
|
-
country: 'Canada'
|
|
388
|
-
};
|
|
389
|
-
validAddress = Address.fromJSON(addressData);
|
|
390
|
-
});
|
|
391
|
-
|
|
392
|
-
it('should validate a person with shared address composition', async () => {
|
|
393
|
-
const personData = {
|
|
394
|
-
firstName: 'John',
|
|
395
|
-
lastName: 'Doe',
|
|
396
|
-
email: 'john@example.com',
|
|
397
|
-
age: 25,
|
|
398
|
-
phoneNumber: '123-456-7890',
|
|
399
|
-
isActive: true,
|
|
400
|
-
address: validAddress
|
|
401
|
-
};
|
|
402
|
-
|
|
403
|
-
const person = Person.fromJSON(personData);
|
|
404
|
-
const errors = await person.validate();
|
|
405
|
-
|
|
406
|
-
expect(errors.length).toBe(0);
|
|
407
|
-
expect(person.address).toBeDefined();
|
|
408
|
-
expect(person.address.street).toBe('456 Elm Avenue');
|
|
409
|
-
expect(person.address.zipCode).toBe('67890');
|
|
410
|
-
expect(person.address.country).toBe('Canada');
|
|
411
|
-
});
|
|
412
|
-
|
|
413
|
-
it('should validate a person without address (optional shared composition)', async () => {
|
|
414
|
-
const personData = {
|
|
415
|
-
firstName: 'Jane',
|
|
416
|
-
lastName: 'Smith',
|
|
417
|
-
email: 'jane@example.com',
|
|
418
|
-
age: 30,
|
|
419
|
-
phoneNumber: '987-654-3210',
|
|
420
|
-
isActive: true
|
|
421
|
-
// address is optional
|
|
422
|
-
};
|
|
423
|
-
|
|
424
|
-
const person = Person.fromJSON(personData);
|
|
425
|
-
const errors = await person.validate();
|
|
426
|
-
|
|
427
|
-
expect(errors.length).toBe(0);
|
|
428
|
-
expect(person.address).toBeUndefined();
|
|
429
|
-
});
|
|
430
|
-
|
|
431
|
-
it('should fail validation when person has invalid address', async () => {
|
|
432
|
-
const invalidAddressData = {
|
|
433
|
-
street: '789 Oak Street',
|
|
434
|
-
zipCode: '11111'
|
|
435
|
-
// country is missing, making address invalid
|
|
436
|
-
};
|
|
437
|
-
const invalidAddress = Address.fromJSON(invalidAddressData);
|
|
438
|
-
|
|
439
|
-
const personData = {
|
|
440
|
-
firstName: 'Bob',
|
|
441
|
-
lastName: 'Wilson',
|
|
442
|
-
email: 'bob@example.com',
|
|
443
|
-
age: 35,
|
|
444
|
-
phoneNumber: '555-123-4567',
|
|
445
|
-
isActive: true,
|
|
446
|
-
address: invalidAddress
|
|
447
|
-
};
|
|
448
|
-
|
|
449
|
-
const person = Person.fromJSON(personData);
|
|
450
|
-
const errors = await person.validate();
|
|
451
|
-
|
|
452
|
-
expect(errors.length).toBeGreaterThan(0);
|
|
453
|
-
// Should contain nested validation errors from address
|
|
454
|
-
const addressErrors = errors.filter(e => e.property?.includes('address'));
|
|
455
|
-
expect(addressErrors.length).toBeGreaterThan(0);
|
|
456
|
-
});
|
|
457
|
-
|
|
458
|
-
it('should properly serialize person with address to JSON', () => {
|
|
459
|
-
const personData = {
|
|
460
|
-
firstName: 'Alice',
|
|
461
|
-
lastName: 'Johnson',
|
|
462
|
-
email: 'alice@example.com',
|
|
463
|
-
age: 28,
|
|
464
|
-
phoneNumber: '444-555-6666',
|
|
465
|
-
isActive: true,
|
|
466
|
-
address: validAddress
|
|
467
|
-
};
|
|
468
|
-
|
|
469
|
-
const person = Person.fromJSON(personData);
|
|
470
|
-
const json = person.toJSON();
|
|
471
|
-
|
|
472
|
-
expect(json).toHaveProperty('address');
|
|
473
|
-
expect(json.address).toHaveProperty('street', '456 Elm Avenue');
|
|
474
|
-
expect(json.address).toHaveProperty('zipCode', '67890');
|
|
475
|
-
expect(json.address).toHaveProperty('country', 'Canada');
|
|
476
|
-
});
|
|
477
|
-
});
|
|
478
|
-
|
|
479
|
-
describe('Company with Address (SharedComposition)', () => {
|
|
480
|
-
let validCEO: Person;
|
|
481
|
-
let validAddress: Address;
|
|
482
|
-
|
|
483
|
-
beforeEach(() => {
|
|
484
|
-
const ceoData = {
|
|
485
|
-
firstName: 'Robert',
|
|
486
|
-
lastName: 'Johnson',
|
|
487
|
-
email: 'robert@company.com',
|
|
488
|
-
age: 45,
|
|
489
|
-
phoneNumber: '+1987654321',
|
|
490
|
-
additionalInfo: '<p>CEO and Founder</p>',
|
|
491
|
-
isActive: true
|
|
492
|
-
};
|
|
493
|
-
validCEO = Person.fromJSON(ceoData);
|
|
494
|
-
|
|
495
|
-
const addressData = {
|
|
496
|
-
street: '100 Corporate Plaza',
|
|
497
|
-
zipCode: '98765',
|
|
498
|
-
country: 'United States'
|
|
499
|
-
};
|
|
500
|
-
validAddress = Address.fromJSON(addressData);
|
|
501
|
-
});
|
|
502
|
-
|
|
503
|
-
it('should validate a company with shared address composition', async () => {
|
|
504
|
-
const companyData = {
|
|
505
|
-
name: "TechCorp Solutions",
|
|
506
|
-
ceo: validCEO,
|
|
507
|
-
address: validAddress
|
|
508
|
-
};
|
|
509
|
-
|
|
510
|
-
const company = Company.fromJSON(companyData);
|
|
511
|
-
const errors = await company.validate();
|
|
512
|
-
|
|
513
|
-
expect(errors.length).toBe(0);
|
|
514
|
-
expect(company.address).toBeDefined();
|
|
515
|
-
expect(company.address.street).toBe('100 Corporate Plaza');
|
|
516
|
-
expect(company.address.zipCode).toBe('98765');
|
|
517
|
-
expect(company.address.country).toBe('United States');
|
|
518
|
-
});
|
|
519
|
-
|
|
520
|
-
it('should validate a company without address (optional shared composition)', async () => {
|
|
521
|
-
const companyData = {
|
|
522
|
-
name: "StartupCorp",
|
|
523
|
-
ceo: validCEO
|
|
524
|
-
// address is optional
|
|
525
|
-
};
|
|
526
|
-
|
|
527
|
-
const company = Company.fromJSON(companyData);
|
|
528
|
-
const errors = await company.validate();
|
|
529
|
-
|
|
530
|
-
expect(errors.length).toBe(0);
|
|
531
|
-
expect(company.address).toBeUndefined();
|
|
532
|
-
});
|
|
533
|
-
|
|
534
|
-
it('should fail validation when company has invalid address', async () => {
|
|
535
|
-
const invalidAddressData = {
|
|
536
|
-
street: '200 Business Street'
|
|
537
|
-
// zipCode and country are missing, making address invalid
|
|
538
|
-
};
|
|
539
|
-
const invalidAddress = Address.fromJSON(invalidAddressData);
|
|
540
|
-
|
|
541
|
-
const companyData = {
|
|
542
|
-
name: "FailCorp",
|
|
543
|
-
ceo: validCEO,
|
|
544
|
-
address: invalidAddress
|
|
545
|
-
};
|
|
546
|
-
|
|
547
|
-
const company = Company.fromJSON(companyData);
|
|
548
|
-
const errors = await company.validate();
|
|
549
|
-
|
|
550
|
-
expect(errors.length).toBeGreaterThan(0);
|
|
551
|
-
// Should contain nested validation errors from address
|
|
552
|
-
const addressErrors = errors.filter(e => e.property?.includes('address'));
|
|
553
|
-
expect(addressErrors.length).toBeGreaterThan(0);
|
|
554
|
-
});
|
|
555
|
-
|
|
556
|
-
it('should properly serialize company with address to JSON', () => {
|
|
557
|
-
const companyData = {
|
|
558
|
-
name: "GlobalTech Inc",
|
|
559
|
-
ceo: validCEO,
|
|
560
|
-
address: validAddress
|
|
561
|
-
};
|
|
562
|
-
|
|
563
|
-
const company = Company.fromJSON(companyData);
|
|
564
|
-
const json = company.toJSON();
|
|
565
|
-
|
|
566
|
-
expect(json).toHaveProperty('address');
|
|
567
|
-
expect(json.address).toHaveProperty('street', '100 Corporate Plaza');
|
|
568
|
-
expect(json.address).toHaveProperty('zipCode', '98765');
|
|
569
|
-
expect(json.address).toHaveProperty('country', 'United States');
|
|
570
|
-
});
|
|
571
|
-
});
|
|
572
|
-
|
|
573
|
-
describe('Full Integration Tests', () => {
|
|
574
|
-
it('should handle complete model relationships', async () => {
|
|
575
|
-
// Create a CEO
|
|
576
|
-
const ceoData = {
|
|
577
|
-
firstName: 'John',
|
|
578
|
-
lastName: 'Smith',
|
|
579
|
-
email: 'john.smith@techcorp.com',
|
|
580
|
-
age: 45,
|
|
581
|
-
phoneNumber: '+1555123456',
|
|
582
|
-
additionalInfo: '<p>Experienced technology leader</p>',
|
|
583
|
-
isActive: true
|
|
584
|
-
};
|
|
585
|
-
const ceo = Person.fromJSON(ceoData);
|
|
586
|
-
|
|
587
|
-
// Create a company with the CEO (composition)
|
|
588
|
-
const companyData = {
|
|
589
|
-
name: "TechCorp Innovation Labs",
|
|
590
|
-
ceo: ceo
|
|
591
|
-
};
|
|
592
|
-
const company = Company.fromJSON(companyData);
|
|
593
|
-
|
|
594
|
-
// Create an app that references the company
|
|
595
|
-
const appData = {
|
|
596
|
-
name: "InnovationPlatform",
|
|
597
|
-
version: "03.01.05",
|
|
598
|
-
description: "A cutting-edge platform for technological innovation and development",
|
|
599
|
-
ownerCompany: company
|
|
600
|
-
};
|
|
601
|
-
const app = App.fromJSON(appData);
|
|
602
|
-
|
|
603
|
-
// Validate all models
|
|
604
|
-
const ceoErrors = await ceo.validate();
|
|
605
|
-
const companyErrors = await company.validate();
|
|
606
|
-
const appErrors = await app.validate();
|
|
607
|
-
|
|
608
|
-
expect(ceoErrors.length).toBe(0);
|
|
609
|
-
expect(companyErrors.length).toBe(0);
|
|
610
|
-
expect(appErrors.length).toBe(0);
|
|
611
|
-
|
|
612
|
-
// Verify relationships
|
|
613
|
-
expect(app.ownerCompany.name).toBe("TechCorp Innovation Labs");
|
|
614
|
-
expect(app.ownerCompany.ceo.firstName).toBe("John");
|
|
615
|
-
expect(company.ceo.email).toBe("john.smith@techcorp.com");
|
|
616
|
-
});
|
|
617
|
-
|
|
618
|
-
it('should properly serialize complete model hierarchy', () => {
|
|
619
|
-
const ceoData = {
|
|
620
|
-
firstName: 'Alice',
|
|
621
|
-
lastName: 'Wilson',
|
|
622
|
-
email: 'alice@startup.com',
|
|
623
|
-
age: 35,
|
|
624
|
-
phoneNumber: '+1444555666',
|
|
625
|
-
isActive: true
|
|
626
|
-
};
|
|
627
|
-
const ceo = Person.fromJSON(ceoData);
|
|
628
|
-
|
|
629
|
-
const companyData = {
|
|
630
|
-
name: "StartupCorp",
|
|
631
|
-
ceo: ceo
|
|
632
|
-
};
|
|
633
|
-
const company = Company.fromJSON(companyData);
|
|
634
|
-
|
|
635
|
-
const appData = {
|
|
636
|
-
name: "StartupApp",
|
|
637
|
-
version: "01.00.01",
|
|
638
|
-
description: "A revolutionary startup application",
|
|
639
|
-
ownerCompany: company
|
|
640
|
-
};
|
|
641
|
-
const app = App.fromJSON(appData);
|
|
642
|
-
|
|
643
|
-
// Serialize to JSON
|
|
644
|
-
const appJson = app.toJSON();
|
|
645
|
-
|
|
646
|
-
// Verify the JSON structure
|
|
647
|
-
expect(appJson).toHaveProperty('name', 'StartupApp');
|
|
648
|
-
expect(appJson).toHaveProperty('version', '01.00.01');
|
|
649
|
-
expect(appJson).toHaveProperty('ownerCompany');
|
|
650
|
-
expect(appJson.ownerCompany).toHaveProperty('name', 'StartupCorp');
|
|
651
|
-
expect(appJson.ownerCompany).toHaveProperty('ceo');
|
|
652
|
-
expect(appJson.ownerCompany.ceo).toHaveProperty('firstName', 'Alice');
|
|
653
|
-
});
|
|
654
|
-
});
|
|
1
|
+
import { App } from './App';
|
|
2
|
+
import { Person } from './Person';
|
|
3
|
+
import { Company } from './Company';
|
|
4
|
+
import { Address } from './Address';
|
|
5
|
+
|
|
6
|
+
describe('Models Integration Tests', () => {
|
|
7
|
+
describe('Person Model', () => {
|
|
8
|
+
it('should validate a valid adult person', async () => {
|
|
9
|
+
const personData = {
|
|
10
|
+
firstName: 'John',
|
|
11
|
+
lastName: 'Doe',
|
|
12
|
+
email: 'john@example.com',
|
|
13
|
+
age: 25,
|
|
14
|
+
phoneNumber: '123-456-7890',
|
|
15
|
+
additionalInfo: '<p>Senior Developer</p>',
|
|
16
|
+
isActive: true
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const person = Person.fromJSON(personData);
|
|
20
|
+
const errors = await person.validate();
|
|
21
|
+
|
|
22
|
+
expect(errors.length).toBe(0);
|
|
23
|
+
expect(person.firstName).toBe('John');
|
|
24
|
+
expect(person.lastName).toBe('Doe');
|
|
25
|
+
expect(person.age).toBe(25);
|
|
26
|
+
expect(person.isActive).toBe(true);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('should validate a valid minor person with parent email', async () => {
|
|
30
|
+
const personData = {
|
|
31
|
+
firstName: 'Jane',
|
|
32
|
+
lastName: 'Smith',
|
|
33
|
+
email: 'jane@example.com',
|
|
34
|
+
age: 16,
|
|
35
|
+
parentEmail: 'parent@example.com',
|
|
36
|
+
additionalInfo: '<p>Student</p>',
|
|
37
|
+
isActive: true
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const person = Person.fromJSON(personData);
|
|
41
|
+
const errors = await person.validate();
|
|
42
|
+
|
|
43
|
+
expect(errors.length).toBe(0);
|
|
44
|
+
expect(person.parentEmail).toBe('parent@example.com');
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('should fail validation when firstName is too short', async () => {
|
|
48
|
+
const personData = {
|
|
49
|
+
firstName: 'J',
|
|
50
|
+
lastName: 'Doe',
|
|
51
|
+
email: 'john@example.com',
|
|
52
|
+
age: 25
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const person = Person.fromJSON(personData);
|
|
56
|
+
const errors = await person.validate();
|
|
57
|
+
|
|
58
|
+
expect(errors.length).toBeGreaterThan(0);
|
|
59
|
+
const firstNameError = errors.find(e => e.property === 'firstName');
|
|
60
|
+
expect(firstNameError).toBeDefined();
|
|
61
|
+
expect(firstNameError?.constraints).toHaveProperty('minLength');
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('should fail validation when firstName contains numbers', async () => {
|
|
65
|
+
const personData = {
|
|
66
|
+
firstName: 'John123',
|
|
67
|
+
lastName: 'Doe',
|
|
68
|
+
email: 'john@example.com',
|
|
69
|
+
age: 25
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const person = Person.fromJSON(personData);
|
|
73
|
+
const errors = await person.validate();
|
|
74
|
+
|
|
75
|
+
expect(errors.length).toBeGreaterThan(0);
|
|
76
|
+
const firstNameError = errors.find(e => e.property === 'firstName');
|
|
77
|
+
expect(firstNameError).toBeDefined();
|
|
78
|
+
expect(firstNameError?.constraints).toHaveProperty('matches');
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it('should fail validation when email is invalid', async () => {
|
|
82
|
+
const personData = {
|
|
83
|
+
firstName: 'John',
|
|
84
|
+
lastName: 'Doe',
|
|
85
|
+
email: 'invalid-email',
|
|
86
|
+
age: 25
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
const person = Person.fromJSON(personData);
|
|
90
|
+
const errors = await person.validate();
|
|
91
|
+
|
|
92
|
+
expect(errors.length).toBeGreaterThan(0);
|
|
93
|
+
const emailError = errors.find(e => e.property === 'email');
|
|
94
|
+
expect(emailError).toBeDefined();
|
|
95
|
+
expect(emailError?.constraints).toHaveProperty('isEmail');
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it('should fail validation when age is negative', async () => {
|
|
99
|
+
const personData = {
|
|
100
|
+
firstName: 'John',
|
|
101
|
+
lastName: 'Doe',
|
|
102
|
+
email: 'john@example.com',
|
|
103
|
+
age: -5
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
const person = Person.fromJSON(personData);
|
|
107
|
+
const errors = await person.validate();
|
|
108
|
+
|
|
109
|
+
expect(errors.length).toBeGreaterThan(0);
|
|
110
|
+
const ageError = errors.find(e => e.property === 'age');
|
|
111
|
+
expect(ageError).toBeDefined();
|
|
112
|
+
expect(ageError?.constraints).toHaveProperty('invalidAge');
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it('should require parentEmail for minors', async () => {
|
|
116
|
+
const personData = {
|
|
117
|
+
firstName: 'Jane',
|
|
118
|
+
lastName: 'Smith',
|
|
119
|
+
email: 'jane@example.com',
|
|
120
|
+
age: 16
|
|
121
|
+
// parentEmail missing
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
const person = Person.fromJSON(personData);
|
|
125
|
+
const errors = await person.validate();
|
|
126
|
+
|
|
127
|
+
expect(errors.length).toBeGreaterThan(0);
|
|
128
|
+
const parentEmailError = errors.find(e => e.property === 'parentEmail');
|
|
129
|
+
expect(parentEmailError).toBeDefined();
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it('should exclude internalId from JSON output', () => {
|
|
133
|
+
const personData = {
|
|
134
|
+
firstName: 'John',
|
|
135
|
+
lastName: 'Doe',
|
|
136
|
+
email: 'john@example.com',
|
|
137
|
+
age: 25,
|
|
138
|
+
internalId: 'secret-123',
|
|
139
|
+
isActive: true
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
const person = Person.fromJSON(personData);
|
|
143
|
+
const json = person.toJSON();
|
|
144
|
+
|
|
145
|
+
expect(json).not.toHaveProperty('internalId');
|
|
146
|
+
expect(json).toHaveProperty('firstName', 'John');
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
describe('Company Model', () => {
|
|
151
|
+
let validCEO: Person;
|
|
152
|
+
|
|
153
|
+
beforeEach(() => {
|
|
154
|
+
const ceoData = {
|
|
155
|
+
firstName: 'Robert',
|
|
156
|
+
lastName: 'Johnson',
|
|
157
|
+
email: 'robert@company.com',
|
|
158
|
+
age: 45,
|
|
159
|
+
phoneNumber: '+1987654321',
|
|
160
|
+
additionalInfo: '<p>CEO and Founder</p>',
|
|
161
|
+
isActive: true
|
|
162
|
+
};
|
|
163
|
+
validCEO = Person.fromJSON(ceoData);
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
it('should validate a valid company with CEO composition', async () => {
|
|
167
|
+
const companyData = {
|
|
168
|
+
name: "TechCorp Solutions",
|
|
169
|
+
ceo: validCEO
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
const company = Company.fromJSON(companyData);
|
|
173
|
+
const errors = await company.validate();
|
|
174
|
+
|
|
175
|
+
expect(errors.length).toBe(0);
|
|
176
|
+
expect(company.name).toBe("TechCorp Solutions");
|
|
177
|
+
expect(company.ceo).toBeDefined();
|
|
178
|
+
expect(company.ceo.firstName).toBe("Robert");
|
|
179
|
+
expect(company.ceo.lastName).toBe("Johnson");
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
it('should fail validation with invalid company name (too short)', async () => {
|
|
183
|
+
const companyData = {
|
|
184
|
+
name: "X", // Too short
|
|
185
|
+
ceo: validCEO
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
const company = Company.fromJSON(companyData);
|
|
189
|
+
const errors = await company.validate();
|
|
190
|
+
|
|
191
|
+
expect(errors.length).toBeGreaterThan(0);
|
|
192
|
+
const nameError = errors.find(e => e.property === 'name');
|
|
193
|
+
expect(nameError).toBeDefined();
|
|
194
|
+
expect(nameError?.constraints).toHaveProperty('minLength');
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
it('should fail validation with invalid company name (special chars)', async () => {
|
|
198
|
+
const companyData = {
|
|
199
|
+
name: "Company@#$%", // Invalid characters
|
|
200
|
+
ceo: validCEO
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
const company = Company.fromJSON(companyData);
|
|
204
|
+
const errors = await company.validate();
|
|
205
|
+
|
|
206
|
+
expect(errors.length).toBeGreaterThan(0);
|
|
207
|
+
const nameError = errors.find(e => e.property === 'name');
|
|
208
|
+
expect(nameError).toBeDefined();
|
|
209
|
+
expect(nameError?.constraints).toHaveProperty('matches');
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
it('should fail validation when CEO is missing', async () => {
|
|
213
|
+
const companyData = {
|
|
214
|
+
name: "ValidCompany"
|
|
215
|
+
// ceo is missing
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
const company = Company.fromJSON(companyData);
|
|
219
|
+
const errors = await company.validate();
|
|
220
|
+
|
|
221
|
+
expect(errors.length).toBeGreaterThan(0);
|
|
222
|
+
const ceoError = errors.find(e => e.property === 'ceo');
|
|
223
|
+
expect(ceoError).toBeDefined();
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
it('should validate company with valid name containing dots and ampersands', async () => {
|
|
227
|
+
const companyData = {
|
|
228
|
+
name: "Tech & Solutions Co.",
|
|
229
|
+
ceo: validCEO
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
const company = Company.fromJSON(companyData);
|
|
233
|
+
const errors = await company.validate();
|
|
234
|
+
|
|
235
|
+
expect(errors.length).toBe(0);
|
|
236
|
+
expect(company.name).toBe("Tech & Solutions Co.");
|
|
237
|
+
});
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
describe('App Model', () => {
|
|
241
|
+
let validCompany: Company;
|
|
242
|
+
|
|
243
|
+
beforeEach(() => {
|
|
244
|
+
const ceoData = {
|
|
245
|
+
firstName: 'Sarah',
|
|
246
|
+
lastName: 'Davis',
|
|
247
|
+
email: 'sarah@company.com',
|
|
248
|
+
age: 40,
|
|
249
|
+
phoneNumber: '+1111222333',
|
|
250
|
+
additionalInfo: '<p>Visionary CEO</p>',
|
|
251
|
+
isActive: true
|
|
252
|
+
};
|
|
253
|
+
const ceo = Person.fromJSON(ceoData);
|
|
254
|
+
|
|
255
|
+
const companyData = {
|
|
256
|
+
name: "TechCorp Solutions",
|
|
257
|
+
ceo: ceo
|
|
258
|
+
};
|
|
259
|
+
validCompany = Company.fromJSON(companyData);
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
it('should validate a valid app with owner company reference', async () => {
|
|
263
|
+
const appData = {
|
|
264
|
+
name: "SlingrApp",
|
|
265
|
+
version: "01.00.01",
|
|
266
|
+
description: "A comprehensive application framework for building modern web applications",
|
|
267
|
+
ownerCompany: validCompany
|
|
268
|
+
};
|
|
269
|
+
|
|
270
|
+
const app = App.fromJSON(appData);
|
|
271
|
+
const errors = await app.validate();
|
|
272
|
+
|
|
273
|
+
expect(errors.length).toBe(0);
|
|
274
|
+
expect(app.name).toBe("SlingrApp");
|
|
275
|
+
expect(app.version).toBe("01.00.01");
|
|
276
|
+
expect(app.ownerCompany).toBeDefined();
|
|
277
|
+
expect(app.ownerCompany.name).toBe("TechCorp Solutions");
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
it('should fail validation with invalid name format (starts with dot)', async () => {
|
|
281
|
+
const appData = {
|
|
282
|
+
name: ".InvalidApp",
|
|
283
|
+
version: "01.00.01",
|
|
284
|
+
description: "A comprehensive application framework",
|
|
285
|
+
ownerCompany: validCompany
|
|
286
|
+
};
|
|
287
|
+
|
|
288
|
+
const app = App.fromJSON(appData);
|
|
289
|
+
const errors = await app.validate();
|
|
290
|
+
|
|
291
|
+
expect(errors.length).toBeGreaterThan(0);
|
|
292
|
+
const nameError = errors.find(e => e.property === 'name');
|
|
293
|
+
expect(nameError).toBeDefined();
|
|
294
|
+
expect(nameError?.constraints).toHaveProperty('matches');
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
it('should fail validation with invalid version format', async () => {
|
|
298
|
+
const appData = {
|
|
299
|
+
name: "ValidApp",
|
|
300
|
+
version: "1.0.1", // Should be 01.00.01
|
|
301
|
+
description: "A comprehensive application framework",
|
|
302
|
+
ownerCompany: validCompany
|
|
303
|
+
};
|
|
304
|
+
|
|
305
|
+
const app = App.fromJSON(appData);
|
|
306
|
+
const errors = await app.validate();
|
|
307
|
+
|
|
308
|
+
expect(errors.length).toBeGreaterThan(0);
|
|
309
|
+
const versionError = errors.find(e => e.property === 'version');
|
|
310
|
+
expect(versionError).toBeDefined();
|
|
311
|
+
expect(versionError?.constraints).toHaveProperty('matches');
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
it('should fail validation when owner company is missing', async () => {
|
|
315
|
+
const appData = {
|
|
316
|
+
name: "ValidApp",
|
|
317
|
+
version: "01.00.01",
|
|
318
|
+
description: "A comprehensive application framework"
|
|
319
|
+
// ownerCompany is missing
|
|
320
|
+
};
|
|
321
|
+
|
|
322
|
+
const app = App.fromJSON(appData);
|
|
323
|
+
const errors = await app.validate();
|
|
324
|
+
|
|
325
|
+
expect(errors.length).toBeGreaterThan(0);
|
|
326
|
+
const ownerError = errors.find(e => e.property === 'ownerCompany');
|
|
327
|
+
expect(ownerError).toBeDefined();
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
it('should validate app with valid name containing dots', async () => {
|
|
331
|
+
const appData = {
|
|
332
|
+
name: "App.Module.Core",
|
|
333
|
+
version: "02.05.12",
|
|
334
|
+
description: "A modular application core component",
|
|
335
|
+
ownerCompany: validCompany
|
|
336
|
+
};
|
|
337
|
+
|
|
338
|
+
const app = App.fromJSON(appData);
|
|
339
|
+
const errors = await app.validate();
|
|
340
|
+
|
|
341
|
+
expect(errors.length).toBe(0);
|
|
342
|
+
expect(app.name).toBe("App.Module.Core");
|
|
343
|
+
});
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
describe('Address Model', () => {
|
|
347
|
+
it('should validate a valid address', async () => {
|
|
348
|
+
const addressData = {
|
|
349
|
+
street: '123 Main Street',
|
|
350
|
+
zipCode: '12345',
|
|
351
|
+
country: 'United States'
|
|
352
|
+
};
|
|
353
|
+
|
|
354
|
+
const address = Address.fromJSON(addressData);
|
|
355
|
+
const errors = await address.validate();
|
|
356
|
+
|
|
357
|
+
expect(errors.length).toBe(0);
|
|
358
|
+
expect(address.street).toBe('123 Main Street');
|
|
359
|
+
expect(address.zipCode).toBe('12345');
|
|
360
|
+
expect(address.country).toBe('United States');
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
it('should fail validation when street is missing', async () => {
|
|
364
|
+
const addressData = {
|
|
365
|
+
zipCode: '12345',
|
|
366
|
+
country: 'United States'
|
|
367
|
+
// street is missing
|
|
368
|
+
};
|
|
369
|
+
|
|
370
|
+
const address = Address.fromJSON(addressData);
|
|
371
|
+
const errors = await address.validate();
|
|
372
|
+
|
|
373
|
+
expect(errors.length).toBeGreaterThan(0);
|
|
374
|
+
const streetError = errors.find(e => e.property === 'street');
|
|
375
|
+
expect(streetError).toBeDefined();
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
describe('Person with Address (SharedComposition)', () => {
|
|
381
|
+
let validAddress: Address;
|
|
382
|
+
|
|
383
|
+
beforeEach(() => {
|
|
384
|
+
const addressData = {
|
|
385
|
+
street: '456 Elm Avenue',
|
|
386
|
+
zipCode: '67890',
|
|
387
|
+
country: 'Canada'
|
|
388
|
+
};
|
|
389
|
+
validAddress = Address.fromJSON(addressData);
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
it('should validate a person with shared address composition', async () => {
|
|
393
|
+
const personData = {
|
|
394
|
+
firstName: 'John',
|
|
395
|
+
lastName: 'Doe',
|
|
396
|
+
email: 'john@example.com',
|
|
397
|
+
age: 25,
|
|
398
|
+
phoneNumber: '123-456-7890',
|
|
399
|
+
isActive: true,
|
|
400
|
+
address: validAddress
|
|
401
|
+
};
|
|
402
|
+
|
|
403
|
+
const person = Person.fromJSON(personData);
|
|
404
|
+
const errors = await person.validate();
|
|
405
|
+
|
|
406
|
+
expect(errors.length).toBe(0);
|
|
407
|
+
expect(person.address).toBeDefined();
|
|
408
|
+
expect(person.address.street).toBe('456 Elm Avenue');
|
|
409
|
+
expect(person.address.zipCode).toBe('67890');
|
|
410
|
+
expect(person.address.country).toBe('Canada');
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
it('should validate a person without address (optional shared composition)', async () => {
|
|
414
|
+
const personData = {
|
|
415
|
+
firstName: 'Jane',
|
|
416
|
+
lastName: 'Smith',
|
|
417
|
+
email: 'jane@example.com',
|
|
418
|
+
age: 30,
|
|
419
|
+
phoneNumber: '987-654-3210',
|
|
420
|
+
isActive: true
|
|
421
|
+
// address is optional
|
|
422
|
+
};
|
|
423
|
+
|
|
424
|
+
const person = Person.fromJSON(personData);
|
|
425
|
+
const errors = await person.validate();
|
|
426
|
+
|
|
427
|
+
expect(errors.length).toBe(0);
|
|
428
|
+
expect(person.address).toBeUndefined();
|
|
429
|
+
});
|
|
430
|
+
|
|
431
|
+
it('should fail validation when person has invalid address', async () => {
|
|
432
|
+
const invalidAddressData = {
|
|
433
|
+
street: '789 Oak Street',
|
|
434
|
+
zipCode: '11111'
|
|
435
|
+
// country is missing, making address invalid
|
|
436
|
+
};
|
|
437
|
+
const invalidAddress = Address.fromJSON(invalidAddressData);
|
|
438
|
+
|
|
439
|
+
const personData = {
|
|
440
|
+
firstName: 'Bob',
|
|
441
|
+
lastName: 'Wilson',
|
|
442
|
+
email: 'bob@example.com',
|
|
443
|
+
age: 35,
|
|
444
|
+
phoneNumber: '555-123-4567',
|
|
445
|
+
isActive: true,
|
|
446
|
+
address: invalidAddress
|
|
447
|
+
};
|
|
448
|
+
|
|
449
|
+
const person = Person.fromJSON(personData);
|
|
450
|
+
const errors = await person.validate();
|
|
451
|
+
|
|
452
|
+
expect(errors.length).toBeGreaterThan(0);
|
|
453
|
+
// Should contain nested validation errors from address
|
|
454
|
+
const addressErrors = errors.filter(e => e.property?.includes('address'));
|
|
455
|
+
expect(addressErrors.length).toBeGreaterThan(0);
|
|
456
|
+
});
|
|
457
|
+
|
|
458
|
+
it('should properly serialize person with address to JSON', () => {
|
|
459
|
+
const personData = {
|
|
460
|
+
firstName: 'Alice',
|
|
461
|
+
lastName: 'Johnson',
|
|
462
|
+
email: 'alice@example.com',
|
|
463
|
+
age: 28,
|
|
464
|
+
phoneNumber: '444-555-6666',
|
|
465
|
+
isActive: true,
|
|
466
|
+
address: validAddress
|
|
467
|
+
};
|
|
468
|
+
|
|
469
|
+
const person = Person.fromJSON(personData);
|
|
470
|
+
const json = person.toJSON();
|
|
471
|
+
|
|
472
|
+
expect(json).toHaveProperty('address');
|
|
473
|
+
expect(json.address).toHaveProperty('street', '456 Elm Avenue');
|
|
474
|
+
expect(json.address).toHaveProperty('zipCode', '67890');
|
|
475
|
+
expect(json.address).toHaveProperty('country', 'Canada');
|
|
476
|
+
});
|
|
477
|
+
});
|
|
478
|
+
|
|
479
|
+
describe('Company with Address (SharedComposition)', () => {
|
|
480
|
+
let validCEO: Person;
|
|
481
|
+
let validAddress: Address;
|
|
482
|
+
|
|
483
|
+
beforeEach(() => {
|
|
484
|
+
const ceoData = {
|
|
485
|
+
firstName: 'Robert',
|
|
486
|
+
lastName: 'Johnson',
|
|
487
|
+
email: 'robert@company.com',
|
|
488
|
+
age: 45,
|
|
489
|
+
phoneNumber: '+1987654321',
|
|
490
|
+
additionalInfo: '<p>CEO and Founder</p>',
|
|
491
|
+
isActive: true
|
|
492
|
+
};
|
|
493
|
+
validCEO = Person.fromJSON(ceoData);
|
|
494
|
+
|
|
495
|
+
const addressData = {
|
|
496
|
+
street: '100 Corporate Plaza',
|
|
497
|
+
zipCode: '98765',
|
|
498
|
+
country: 'United States'
|
|
499
|
+
};
|
|
500
|
+
validAddress = Address.fromJSON(addressData);
|
|
501
|
+
});
|
|
502
|
+
|
|
503
|
+
it('should validate a company with shared address composition', async () => {
|
|
504
|
+
const companyData = {
|
|
505
|
+
name: "TechCorp Solutions",
|
|
506
|
+
ceo: validCEO,
|
|
507
|
+
address: validAddress
|
|
508
|
+
};
|
|
509
|
+
|
|
510
|
+
const company = Company.fromJSON(companyData);
|
|
511
|
+
const errors = await company.validate();
|
|
512
|
+
|
|
513
|
+
expect(errors.length).toBe(0);
|
|
514
|
+
expect(company.address).toBeDefined();
|
|
515
|
+
expect(company.address.street).toBe('100 Corporate Plaza');
|
|
516
|
+
expect(company.address.zipCode).toBe('98765');
|
|
517
|
+
expect(company.address.country).toBe('United States');
|
|
518
|
+
});
|
|
519
|
+
|
|
520
|
+
it('should validate a company without address (optional shared composition)', async () => {
|
|
521
|
+
const companyData = {
|
|
522
|
+
name: "StartupCorp",
|
|
523
|
+
ceo: validCEO
|
|
524
|
+
// address is optional
|
|
525
|
+
};
|
|
526
|
+
|
|
527
|
+
const company = Company.fromJSON(companyData);
|
|
528
|
+
const errors = await company.validate();
|
|
529
|
+
|
|
530
|
+
expect(errors.length).toBe(0);
|
|
531
|
+
expect(company.address).toBeUndefined();
|
|
532
|
+
});
|
|
533
|
+
|
|
534
|
+
it('should fail validation when company has invalid address', async () => {
|
|
535
|
+
const invalidAddressData = {
|
|
536
|
+
street: '200 Business Street'
|
|
537
|
+
// zipCode and country are missing, making address invalid
|
|
538
|
+
};
|
|
539
|
+
const invalidAddress = Address.fromJSON(invalidAddressData);
|
|
540
|
+
|
|
541
|
+
const companyData = {
|
|
542
|
+
name: "FailCorp",
|
|
543
|
+
ceo: validCEO,
|
|
544
|
+
address: invalidAddress
|
|
545
|
+
};
|
|
546
|
+
|
|
547
|
+
const company = Company.fromJSON(companyData);
|
|
548
|
+
const errors = await company.validate();
|
|
549
|
+
|
|
550
|
+
expect(errors.length).toBeGreaterThan(0);
|
|
551
|
+
// Should contain nested validation errors from address
|
|
552
|
+
const addressErrors = errors.filter(e => e.property?.includes('address'));
|
|
553
|
+
expect(addressErrors.length).toBeGreaterThan(0);
|
|
554
|
+
});
|
|
555
|
+
|
|
556
|
+
it('should properly serialize company with address to JSON', () => {
|
|
557
|
+
const companyData = {
|
|
558
|
+
name: "GlobalTech Inc",
|
|
559
|
+
ceo: validCEO,
|
|
560
|
+
address: validAddress
|
|
561
|
+
};
|
|
562
|
+
|
|
563
|
+
const company = Company.fromJSON(companyData);
|
|
564
|
+
const json = company.toJSON();
|
|
565
|
+
|
|
566
|
+
expect(json).toHaveProperty('address');
|
|
567
|
+
expect(json.address).toHaveProperty('street', '100 Corporate Plaza');
|
|
568
|
+
expect(json.address).toHaveProperty('zipCode', '98765');
|
|
569
|
+
expect(json.address).toHaveProperty('country', 'United States');
|
|
570
|
+
});
|
|
571
|
+
});
|
|
572
|
+
|
|
573
|
+
describe('Full Integration Tests', () => {
|
|
574
|
+
it('should handle complete model relationships', async () => {
|
|
575
|
+
// Create a CEO
|
|
576
|
+
const ceoData = {
|
|
577
|
+
firstName: 'John',
|
|
578
|
+
lastName: 'Smith',
|
|
579
|
+
email: 'john.smith@techcorp.com',
|
|
580
|
+
age: 45,
|
|
581
|
+
phoneNumber: '+1555123456',
|
|
582
|
+
additionalInfo: '<p>Experienced technology leader</p>',
|
|
583
|
+
isActive: true
|
|
584
|
+
};
|
|
585
|
+
const ceo = Person.fromJSON(ceoData);
|
|
586
|
+
|
|
587
|
+
// Create a company with the CEO (composition)
|
|
588
|
+
const companyData = {
|
|
589
|
+
name: "TechCorp Innovation Labs",
|
|
590
|
+
ceo: ceo
|
|
591
|
+
};
|
|
592
|
+
const company = Company.fromJSON(companyData);
|
|
593
|
+
|
|
594
|
+
// Create an app that references the company
|
|
595
|
+
const appData = {
|
|
596
|
+
name: "InnovationPlatform",
|
|
597
|
+
version: "03.01.05",
|
|
598
|
+
description: "A cutting-edge platform for technological innovation and development",
|
|
599
|
+
ownerCompany: company
|
|
600
|
+
};
|
|
601
|
+
const app = App.fromJSON(appData);
|
|
602
|
+
|
|
603
|
+
// Validate all models
|
|
604
|
+
const ceoErrors = await ceo.validate();
|
|
605
|
+
const companyErrors = await company.validate();
|
|
606
|
+
const appErrors = await app.validate();
|
|
607
|
+
|
|
608
|
+
expect(ceoErrors.length).toBe(0);
|
|
609
|
+
expect(companyErrors.length).toBe(0);
|
|
610
|
+
expect(appErrors.length).toBe(0);
|
|
611
|
+
|
|
612
|
+
// Verify relationships
|
|
613
|
+
expect(app.ownerCompany.name).toBe("TechCorp Innovation Labs");
|
|
614
|
+
expect(app.ownerCompany.ceo.firstName).toBe("John");
|
|
615
|
+
expect(company.ceo.email).toBe("john.smith@techcorp.com");
|
|
616
|
+
});
|
|
617
|
+
|
|
618
|
+
it('should properly serialize complete model hierarchy', () => {
|
|
619
|
+
const ceoData = {
|
|
620
|
+
firstName: 'Alice',
|
|
621
|
+
lastName: 'Wilson',
|
|
622
|
+
email: 'alice@startup.com',
|
|
623
|
+
age: 35,
|
|
624
|
+
phoneNumber: '+1444555666',
|
|
625
|
+
isActive: true
|
|
626
|
+
};
|
|
627
|
+
const ceo = Person.fromJSON(ceoData);
|
|
628
|
+
|
|
629
|
+
const companyData = {
|
|
630
|
+
name: "StartupCorp",
|
|
631
|
+
ceo: ceo
|
|
632
|
+
};
|
|
633
|
+
const company = Company.fromJSON(companyData);
|
|
634
|
+
|
|
635
|
+
const appData = {
|
|
636
|
+
name: "StartupApp",
|
|
637
|
+
version: "01.00.01",
|
|
638
|
+
description: "A revolutionary startup application",
|
|
639
|
+
ownerCompany: company
|
|
640
|
+
};
|
|
641
|
+
const app = App.fromJSON(appData);
|
|
642
|
+
|
|
643
|
+
// Serialize to JSON
|
|
644
|
+
const appJson = app.toJSON();
|
|
645
|
+
|
|
646
|
+
// Verify the JSON structure
|
|
647
|
+
expect(appJson).toHaveProperty('name', 'StartupApp');
|
|
648
|
+
expect(appJson).toHaveProperty('version', '01.00.01');
|
|
649
|
+
expect(appJson).toHaveProperty('ownerCompany');
|
|
650
|
+
expect(appJson.ownerCompany).toHaveProperty('name', 'StartupCorp');
|
|
651
|
+
expect(appJson.ownerCompany).toHaveProperty('ceo');
|
|
652
|
+
expect(appJson.ownerCompany.ceo).toHaveProperty('firstName', 'Alice');
|
|
653
|
+
});
|
|
654
|
+
});
|
|
655
655
|
});
|