@kravc/schema 2.7.6 → 2.8.0-alpha.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/README.md +19 -14
- package/dist/CredentialFactory.d.ts +345 -0
- package/dist/CredentialFactory.d.ts.map +1 -0
- package/dist/CredentialFactory.js +381 -0
- package/dist/CredentialFactory.js.map +1 -0
- package/dist/Schema.d.ts +448 -0
- package/dist/Schema.d.ts.map +1 -0
- package/dist/Schema.js +506 -0
- package/dist/Schema.js.map +1 -0
- package/dist/ValidationError.d.ts +70 -0
- package/dist/ValidationError.d.ts.map +1 -0
- package/dist/ValidationError.js +78 -0
- package/dist/ValidationError.js.map +1 -0
- package/dist/Validator.d.ts +483 -0
- package/dist/Validator.d.ts.map +1 -0
- package/dist/Validator.js +570 -0
- package/dist/Validator.js.map +1 -0
- package/dist/helpers/JsonSchema.d.ts +99 -0
- package/dist/helpers/JsonSchema.d.ts.map +1 -0
- package/dist/helpers/JsonSchema.js +3 -0
- package/dist/helpers/JsonSchema.js.map +1 -0
- package/dist/helpers/cleanupAttributes.d.ts +34 -0
- package/dist/helpers/cleanupAttributes.d.ts.map +1 -0
- package/dist/helpers/cleanupAttributes.js +113 -0
- package/dist/helpers/cleanupAttributes.js.map +1 -0
- package/dist/helpers/cleanupNulls.d.ts +27 -0
- package/dist/helpers/cleanupNulls.d.ts.map +1 -0
- package/dist/helpers/cleanupNulls.js +96 -0
- package/dist/helpers/cleanupNulls.js.map +1 -0
- package/dist/helpers/createSchemasMap.d.ts +67 -0
- package/dist/helpers/createSchemasMap.d.ts.map +1 -0
- package/dist/helpers/createSchemasMap.js +200 -0
- package/dist/helpers/createSchemasMap.js.map +1 -0
- package/dist/helpers/getReferenceIds.d.ts +169 -0
- package/dist/helpers/getReferenceIds.d.ts.map +1 -0
- package/dist/helpers/getReferenceIds.js +241 -0
- package/dist/helpers/getReferenceIds.js.map +1 -0
- package/dist/helpers/got.d.ts +60 -0
- package/dist/helpers/got.d.ts.map +1 -0
- package/dist/helpers/got.js +72 -0
- package/dist/helpers/got.js.map +1 -0
- package/dist/helpers/mapObjectProperties.d.ts +150 -0
- package/dist/helpers/mapObjectProperties.d.ts.map +1 -0
- package/dist/helpers/mapObjectProperties.js +229 -0
- package/dist/helpers/mapObjectProperties.js.map +1 -0
- package/dist/helpers/normalizeAttributes.d.ts +213 -0
- package/dist/helpers/normalizeAttributes.d.ts.map +1 -0
- package/dist/helpers/normalizeAttributes.js +243 -0
- package/dist/helpers/normalizeAttributes.js.map +1 -0
- package/dist/helpers/normalizeProperties.d.ts +168 -0
- package/dist/helpers/normalizeProperties.d.ts.map +1 -0
- package/dist/helpers/normalizeProperties.js +223 -0
- package/dist/helpers/normalizeProperties.js.map +1 -0
- package/dist/helpers/normalizeRequired.d.ts +159 -0
- package/dist/helpers/normalizeRequired.d.ts.map +1 -0
- package/dist/helpers/normalizeRequired.js +206 -0
- package/dist/helpers/normalizeRequired.js.map +1 -0
- package/dist/helpers/normalizeType.d.ts +81 -0
- package/dist/helpers/normalizeType.d.ts.map +1 -0
- package/dist/helpers/normalizeType.js +210 -0
- package/dist/helpers/normalizeType.js.map +1 -0
- package/dist/helpers/nullifyEmptyValues.d.ts +139 -0
- package/dist/helpers/nullifyEmptyValues.d.ts.map +1 -0
- package/dist/helpers/nullifyEmptyValues.js +191 -0
- package/dist/helpers/nullifyEmptyValues.js.map +1 -0
- package/dist/helpers/removeRequiredAndDefault.d.ts +106 -0
- package/dist/helpers/removeRequiredAndDefault.d.ts.map +1 -0
- package/dist/helpers/removeRequiredAndDefault.js +138 -0
- package/dist/helpers/removeRequiredAndDefault.js.map +1 -0
- package/dist/helpers/validateId.d.ts +39 -0
- package/dist/helpers/validateId.d.ts.map +1 -0
- package/dist/helpers/validateId.js +51 -0
- package/dist/helpers/validateId.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +21 -0
- package/dist/index.js.map +1 -0
- package/dist/ld/documentLoader.d.ts +8 -0
- package/dist/ld/documentLoader.d.ts.map +1 -0
- package/dist/ld/documentLoader.js +24 -0
- package/dist/ld/documentLoader.js.map +1 -0
- package/dist/ld/getLinkedDataAttributeType.d.ts +10 -0
- package/dist/ld/getLinkedDataAttributeType.d.ts.map +1 -0
- package/dist/ld/getLinkedDataAttributeType.js +32 -0
- package/dist/ld/getLinkedDataAttributeType.js.map +1 -0
- package/dist/ld/getLinkedDataContext.d.ts +19 -0
- package/dist/ld/getLinkedDataContext.d.ts.map +1 -0
- package/dist/ld/getLinkedDataContext.js +50 -0
- package/dist/ld/getLinkedDataContext.js.map +1 -0
- package/eslint.config.mjs +32 -52
- package/examples/credentials/createAccountCredential.ts +27 -0
- package/examples/credentials/createMineSweeperScoreCredential.ts +115 -0
- package/examples/index.ts +7 -0
- package/examples/schemas/FavoriteItemSchema.ts +27 -0
- package/examples/{Preferences.yaml → schemas/Preferences.yaml} +2 -0
- package/examples/schemas/PreferencesSchema.ts +29 -0
- package/examples/schemas/ProfileSchema.ts +91 -0
- package/examples/schemas/Status.yaml +3 -0
- package/examples/schemas/StatusSchema.ts +12 -0
- package/jest.config.mjs +5 -0
- package/package.json +27 -20
- package/src/CredentialFactory.ts +392 -0
- package/src/Schema.ts +583 -0
- package/src/ValidationError.ts +90 -0
- package/src/Validator.ts +603 -0
- package/src/__tests__/CredentialFactory.test.ts +588 -0
- package/src/__tests__/Schema.test.ts +371 -0
- package/src/__tests__/ValidationError.test.ts +235 -0
- package/src/__tests__/Validator.test.ts +787 -0
- package/src/helpers/JsonSchema.ts +119 -0
- package/src/helpers/__tests__/cleanupAttributes.test.ts +943 -0
- package/src/helpers/__tests__/cleanupNulls.test.ts +772 -0
- package/src/helpers/__tests__/createSchemasMap.test.ts +238 -0
- package/src/helpers/__tests__/getReferenceIds.test.ts +975 -0
- package/src/helpers/__tests__/got.test.ts +193 -0
- package/src/helpers/__tests__/mapObjectProperties.test.ts +1126 -0
- package/src/helpers/__tests__/normalizeAttributes.test.ts +1435 -0
- package/src/helpers/__tests__/normalizeProperties.test.ts +727 -0
- package/src/helpers/__tests__/normalizeRequired.test.ts +669 -0
- package/src/helpers/__tests__/normalizeType.test.ts +772 -0
- package/src/helpers/__tests__/nullifyEmptyValues.test.ts +735 -0
- package/src/helpers/__tests__/removeRequiredAndDefault.test.ts +734 -0
- package/src/helpers/__tests__/validateId.test.ts +118 -0
- package/src/helpers/cleanupAttributes.ts +151 -0
- package/src/helpers/cleanupNulls.ts +106 -0
- package/src/helpers/createSchemasMap.ts +212 -0
- package/src/helpers/getReferenceIds.ts +273 -0
- package/src/helpers/got.ts +73 -0
- package/src/helpers/mapObjectProperties.ts +272 -0
- package/src/helpers/normalizeAttributes.ts +247 -0
- package/src/helpers/normalizeProperties.ts +249 -0
- package/src/helpers/normalizeRequired.ts +233 -0
- package/src/helpers/normalizeType.ts +235 -0
- package/src/helpers/nullifyEmptyValues.ts +207 -0
- package/src/helpers/removeRequiredAndDefault.ts +151 -0
- package/src/helpers/validateId.ts +53 -0
- package/src/index.ts +17 -0
- package/src/ld/__tests__/documentLoader.test.ts +57 -0
- package/src/ld/__tests__/getLinkedDataAttributeType.test.ts +212 -0
- package/src/ld/__tests__/getLinkedDataContext.test.ts +378 -0
- package/src/ld/documentLoader.ts +28 -0
- package/src/ld/getLinkedDataAttributeType.ts +46 -0
- package/src/ld/getLinkedDataContext.ts +80 -0
- package/tsconfig.json +27 -0
- package/types/credentials-context.d.ts +14 -0
- package/types/security-context.d.ts +6 -0
- package/examples/Status.yaml +0 -3
- package/examples/createAccountCredential.js +0 -27
- package/examples/createMineSweeperScoreCredential.js +0 -63
- package/examples/index.js +0 -9
- package/src/CredentialFactory.js +0 -67
- package/src/CredentialFactory.spec.js +0 -131
- package/src/Schema.js +0 -104
- package/src/Schema.spec.js +0 -172
- package/src/ValidationError.js +0 -31
- package/src/Validator.js +0 -128
- package/src/Validator.spec.js +0 -355
- package/src/helpers/cleanupAttributes.js +0 -71
- package/src/helpers/cleanupNulls.js +0 -42
- package/src/helpers/getReferenceIds.js +0 -71
- package/src/helpers/mapObject.js +0 -65
- package/src/helpers/normalizeAttributes.js +0 -28
- package/src/helpers/normalizeProperties.js +0 -61
- package/src/helpers/normalizeRequired.js +0 -37
- package/src/helpers/normalizeType.js +0 -41
- package/src/helpers/nullifyEmptyValues.js +0 -57
- package/src/helpers/removeRequiredAndDefault.js +0 -30
- package/src/helpers/validateId.js +0 -19
- package/src/index.d.ts +0 -25
- package/src/index.js +0 -8
- package/src/ld/documentLoader.js +0 -25
- package/src/ld/documentLoader.spec.js +0 -12
- package/src/ld/getLinkedDataContext.js +0 -63
- package/src/ld/getLinkedDataType.js +0 -38
- /package/examples/{FavoriteItem.yaml → schemas/FavoriteItem.yaml} +0 -0
- /package/examples/{Profile.yaml → schemas/Profile.yaml} +0 -0
|
@@ -0,0 +1,1126 @@
|
|
|
1
|
+
import Schema from '../../Schema';
|
|
2
|
+
import mapObjectProperties from '../mapObjectProperties';
|
|
3
|
+
import type { JsonSchema } from '../JsonSchema';
|
|
4
|
+
|
|
5
|
+
describe('mapObjectProperties(object, jsonSchema, schemasMap, callback)', () => {
|
|
6
|
+
describe('enum schema', () => {
|
|
7
|
+
it('should not call callback for enum schema', () => {
|
|
8
|
+
const enumSchema = new Schema({ enum: ['value1', 'value2', 'value3'] }, 'enum-schema');
|
|
9
|
+
const object = { value: 'value1' };
|
|
10
|
+
const schemasMap = {};
|
|
11
|
+
const callback = jest.fn();
|
|
12
|
+
|
|
13
|
+
mapObjectProperties(object, enumSchema.jsonSchema, schemasMap, callback);
|
|
14
|
+
|
|
15
|
+
expect(callback).not.toHaveBeenCalled();
|
|
16
|
+
});
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
describe('empty object', () => {
|
|
20
|
+
it('should not call callback for empty object with no properties', () => {
|
|
21
|
+
const schema = new Schema({}, 'empty-schema');
|
|
22
|
+
const object = {};
|
|
23
|
+
const schemasMap = {};
|
|
24
|
+
const callback = jest.fn();
|
|
25
|
+
|
|
26
|
+
mapObjectProperties(object, schema.jsonSchema, schemasMap, callback);
|
|
27
|
+
|
|
28
|
+
expect(callback).not.toHaveBeenCalled();
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
describe('simple properties', () => {
|
|
33
|
+
it('should call callback for each property in object', () => {
|
|
34
|
+
const schema = new Schema(
|
|
35
|
+
{
|
|
36
|
+
stringField: { type: 'string' },
|
|
37
|
+
numberField: { type: 'number' },
|
|
38
|
+
booleanField: { type: 'boolean' }
|
|
39
|
+
},
|
|
40
|
+
'test-schema'
|
|
41
|
+
);
|
|
42
|
+
const object = {
|
|
43
|
+
stringField: 'test',
|
|
44
|
+
numberField: 42,
|
|
45
|
+
booleanField: true
|
|
46
|
+
};
|
|
47
|
+
const schemasMap = {};
|
|
48
|
+
const callback = jest.fn();
|
|
49
|
+
|
|
50
|
+
mapObjectProperties(object, schema.jsonSchema, schemasMap, callback);
|
|
51
|
+
|
|
52
|
+
expect(callback).toHaveBeenCalledTimes(3);
|
|
53
|
+
expect(callback).toHaveBeenCalledWith('stringField', expect.objectContaining({ type: 'string' }), object);
|
|
54
|
+
expect(callback).toHaveBeenCalledWith('numberField', expect.objectContaining({ type: 'number' }), object);
|
|
55
|
+
expect(callback).toHaveBeenCalledWith('booleanField', expect.objectContaining({ type: 'boolean' }), object);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('should call callback even when property value is null', () => {
|
|
59
|
+
const schema = new Schema(
|
|
60
|
+
{
|
|
61
|
+
nullField: { type: 'string' }
|
|
62
|
+
},
|
|
63
|
+
'test-schema'
|
|
64
|
+
);
|
|
65
|
+
const object = {
|
|
66
|
+
nullField: null
|
|
67
|
+
};
|
|
68
|
+
const schemasMap = {};
|
|
69
|
+
const callback = jest.fn();
|
|
70
|
+
|
|
71
|
+
mapObjectProperties(object, schema.jsonSchema, schemasMap, callback);
|
|
72
|
+
|
|
73
|
+
expect(callback).toHaveBeenCalledTimes(1);
|
|
74
|
+
expect(callback).toHaveBeenCalledWith('nullField', expect.any(Object), object);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('should skip undefined values and not recurse', () => {
|
|
78
|
+
const schema = new Schema(
|
|
79
|
+
{
|
|
80
|
+
definedField: { type: 'string' },
|
|
81
|
+
undefinedField: { type: 'string' }
|
|
82
|
+
},
|
|
83
|
+
'test-schema'
|
|
84
|
+
);
|
|
85
|
+
const object = {
|
|
86
|
+
definedField: 'value',
|
|
87
|
+
undefinedField: undefined
|
|
88
|
+
};
|
|
89
|
+
const schemasMap = {};
|
|
90
|
+
const callback = jest.fn();
|
|
91
|
+
|
|
92
|
+
mapObjectProperties(object, schema.jsonSchema, schemasMap, callback);
|
|
93
|
+
|
|
94
|
+
expect(callback).toHaveBeenCalledTimes(2);
|
|
95
|
+
expect(callback).toHaveBeenCalledWith('definedField', expect.any(Object), object);
|
|
96
|
+
expect(callback).toHaveBeenCalledWith('undefinedField', expect.any(Object), object);
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
describe('reference properties ($ref)', () => {
|
|
101
|
+
it('should call callback for reference property and recurse into referenced schema', () => {
|
|
102
|
+
const schema = new Schema(
|
|
103
|
+
{
|
|
104
|
+
refField: { $ref: 'referenced-schema' }
|
|
105
|
+
},
|
|
106
|
+
'test-schema'
|
|
107
|
+
);
|
|
108
|
+
const referencedSchema = new Schema(
|
|
109
|
+
{
|
|
110
|
+
nestedField: { type: 'string' }
|
|
111
|
+
},
|
|
112
|
+
'referenced-schema'
|
|
113
|
+
);
|
|
114
|
+
const object = {
|
|
115
|
+
refField: {
|
|
116
|
+
nestedField: 'nested-value'
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
const schemasMap = {
|
|
120
|
+
'referenced-schema': referencedSchema.jsonSchema
|
|
121
|
+
};
|
|
122
|
+
const callback = jest.fn();
|
|
123
|
+
|
|
124
|
+
mapObjectProperties(object, schema.jsonSchema, schemasMap, callback);
|
|
125
|
+
|
|
126
|
+
expect(callback).toHaveBeenCalledTimes(2);
|
|
127
|
+
expect(callback).toHaveBeenCalledWith('refField', expect.objectContaining({ $ref: 'referenced-schema' }), object);
|
|
128
|
+
expect(callback).toHaveBeenCalledWith('nestedField', expect.objectContaining({ type: 'string' }), object.refField);
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it('should handle nested references recursively', () => {
|
|
132
|
+
const schema = new Schema(
|
|
133
|
+
{
|
|
134
|
+
refField: { $ref: 'level1-schema' }
|
|
135
|
+
},
|
|
136
|
+
'test-schema'
|
|
137
|
+
);
|
|
138
|
+
const level1Schema = new Schema(
|
|
139
|
+
{
|
|
140
|
+
nestedRef: { $ref: 'level2-schema' }
|
|
141
|
+
},
|
|
142
|
+
'level1-schema'
|
|
143
|
+
);
|
|
144
|
+
const level2Schema = new Schema(
|
|
145
|
+
{
|
|
146
|
+
deepField: { type: 'string' }
|
|
147
|
+
},
|
|
148
|
+
'level2-schema'
|
|
149
|
+
);
|
|
150
|
+
const object = {
|
|
151
|
+
refField: {
|
|
152
|
+
nestedRef: {
|
|
153
|
+
deepField: 'deep-value'
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
const schemasMap = {
|
|
158
|
+
'level1-schema': level1Schema.jsonSchema,
|
|
159
|
+
'level2-schema': level2Schema.jsonSchema
|
|
160
|
+
};
|
|
161
|
+
const callback = jest.fn();
|
|
162
|
+
|
|
163
|
+
mapObjectProperties(object, schema.jsonSchema, schemasMap, callback);
|
|
164
|
+
|
|
165
|
+
expect(callback).toHaveBeenCalledTimes(3);
|
|
166
|
+
expect(callback).toHaveBeenCalledWith('refField', expect.objectContaining({ $ref: 'level1-schema' }), object);
|
|
167
|
+
expect(callback).toHaveBeenCalledWith('nestedRef', expect.objectContaining({ $ref: 'level2-schema' }), object.refField);
|
|
168
|
+
expect(callback).toHaveBeenCalledWith('deepField', expect.objectContaining({ type: 'string' }), object.refField.nestedRef);
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
it('should call callback but skip recursion when reference value is undefined', () => {
|
|
172
|
+
const schema = new Schema(
|
|
173
|
+
{
|
|
174
|
+
refField: { $ref: 'referenced-schema' }
|
|
175
|
+
},
|
|
176
|
+
'test-schema'
|
|
177
|
+
);
|
|
178
|
+
const referencedSchema = new Schema(
|
|
179
|
+
{
|
|
180
|
+
nestedField: { type: 'string' }
|
|
181
|
+
},
|
|
182
|
+
'referenced-schema'
|
|
183
|
+
);
|
|
184
|
+
const object = {
|
|
185
|
+
refField: undefined
|
|
186
|
+
};
|
|
187
|
+
const schemasMap = {
|
|
188
|
+
'referenced-schema': referencedSchema.jsonSchema
|
|
189
|
+
};
|
|
190
|
+
const callback = jest.fn();
|
|
191
|
+
|
|
192
|
+
mapObjectProperties(object, schema.jsonSchema, schemasMap, callback);
|
|
193
|
+
|
|
194
|
+
expect(callback).toHaveBeenCalledTimes(1);
|
|
195
|
+
expect(callback).toHaveBeenCalledWith('refField', expect.objectContaining({ $ref: 'referenced-schema' }), object);
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
it('should call callback but skip recursion when reference value is null', () => {
|
|
199
|
+
const schema = new Schema(
|
|
200
|
+
{
|
|
201
|
+
refField: { $ref: 'referenced-schema' }
|
|
202
|
+
},
|
|
203
|
+
'test-schema'
|
|
204
|
+
);
|
|
205
|
+
const referencedSchema = new Schema(
|
|
206
|
+
{
|
|
207
|
+
nestedField: { type: 'string' }
|
|
208
|
+
},
|
|
209
|
+
'referenced-schema'
|
|
210
|
+
);
|
|
211
|
+
const object = {
|
|
212
|
+
refField: null
|
|
213
|
+
};
|
|
214
|
+
const schemasMap = {
|
|
215
|
+
'referenced-schema': referencedSchema.jsonSchema
|
|
216
|
+
};
|
|
217
|
+
const callback = jest.fn();
|
|
218
|
+
|
|
219
|
+
mapObjectProperties(object, schema.jsonSchema, schemasMap, callback);
|
|
220
|
+
|
|
221
|
+
expect(callback).toHaveBeenCalledTimes(1);
|
|
222
|
+
expect(callback).toHaveBeenCalledWith('refField', expect.objectContaining({ $ref: 'referenced-schema' }), object);
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
it('should call callback but skip recursion when reference value is a primitive', () => {
|
|
226
|
+
const schema = new Schema(
|
|
227
|
+
{
|
|
228
|
+
refField: { $ref: 'referenced-schema' }
|
|
229
|
+
},
|
|
230
|
+
'test-schema'
|
|
231
|
+
);
|
|
232
|
+
const referencedSchema = new Schema(
|
|
233
|
+
{
|
|
234
|
+
nestedField: { type: 'string' }
|
|
235
|
+
},
|
|
236
|
+
'referenced-schema'
|
|
237
|
+
);
|
|
238
|
+
const object = {
|
|
239
|
+
refField: 'primitive-value'
|
|
240
|
+
};
|
|
241
|
+
const schemasMap = {
|
|
242
|
+
'referenced-schema': referencedSchema.jsonSchema
|
|
243
|
+
};
|
|
244
|
+
const callback = jest.fn();
|
|
245
|
+
|
|
246
|
+
mapObjectProperties(object, schema.jsonSchema, schemasMap, callback);
|
|
247
|
+
|
|
248
|
+
expect(callback).toHaveBeenCalledTimes(1);
|
|
249
|
+
expect(callback).toHaveBeenCalledWith('refField', expect.objectContaining({ $ref: 'referenced-schema' }), object);
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
it('should call callback but skip recursion when reference value is an array', () => {
|
|
253
|
+
const schema = new Schema(
|
|
254
|
+
{
|
|
255
|
+
refField: { $ref: 'referenced-schema' }
|
|
256
|
+
},
|
|
257
|
+
'test-schema'
|
|
258
|
+
);
|
|
259
|
+
const referencedSchema = new Schema(
|
|
260
|
+
{
|
|
261
|
+
nestedField: { type: 'string' }
|
|
262
|
+
},
|
|
263
|
+
'referenced-schema'
|
|
264
|
+
);
|
|
265
|
+
const object = {
|
|
266
|
+
refField: ['item1', 'item2']
|
|
267
|
+
};
|
|
268
|
+
const schemasMap = {
|
|
269
|
+
'referenced-schema': referencedSchema.jsonSchema
|
|
270
|
+
};
|
|
271
|
+
const callback = jest.fn();
|
|
272
|
+
|
|
273
|
+
mapObjectProperties(object, schema.jsonSchema, schemasMap, callback);
|
|
274
|
+
|
|
275
|
+
expect(callback).toHaveBeenCalledTimes(1);
|
|
276
|
+
expect(callback).toHaveBeenCalledWith('refField', expect.objectContaining({ $ref: 'referenced-schema' }), object);
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
it('should throw error when referenced schema is not found in schemasMap', () => {
|
|
280
|
+
const schema = new Schema(
|
|
281
|
+
{
|
|
282
|
+
refField: { $ref: 'non-existent-schema' }
|
|
283
|
+
},
|
|
284
|
+
'test-schema'
|
|
285
|
+
);
|
|
286
|
+
const object = {
|
|
287
|
+
refField: {
|
|
288
|
+
nestedField: 'value'
|
|
289
|
+
}
|
|
290
|
+
};
|
|
291
|
+
const schemasMap = {};
|
|
292
|
+
const callback = jest.fn();
|
|
293
|
+
|
|
294
|
+
expect(() => {
|
|
295
|
+
mapObjectProperties(object, schema.jsonSchema, schemasMap, callback);
|
|
296
|
+
}).toThrow('Schema "non-existent-schema" not found');
|
|
297
|
+
});
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
describe('nested object properties', () => {
|
|
301
|
+
it('should call callback for nested object property and recurse into nested object', () => {
|
|
302
|
+
const schema = new Schema(
|
|
303
|
+
{
|
|
304
|
+
nestedObject: {
|
|
305
|
+
type: 'object',
|
|
306
|
+
properties: {
|
|
307
|
+
nestedField: { type: 'string' }
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
},
|
|
311
|
+
'test-schema'
|
|
312
|
+
);
|
|
313
|
+
const object = {
|
|
314
|
+
nestedObject: {
|
|
315
|
+
nestedField: 'nested-value'
|
|
316
|
+
}
|
|
317
|
+
};
|
|
318
|
+
const schemasMap = {};
|
|
319
|
+
const callback = jest.fn();
|
|
320
|
+
|
|
321
|
+
mapObjectProperties(object, schema.jsonSchema, schemasMap, callback);
|
|
322
|
+
|
|
323
|
+
expect(callback).toHaveBeenCalledTimes(2);
|
|
324
|
+
expect(callback).toHaveBeenCalledWith('nestedObject', expect.objectContaining({ type: 'object' }), object);
|
|
325
|
+
expect(callback).toHaveBeenCalledWith('nestedField', expect.objectContaining({ type: 'string' }), object.nestedObject);
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
it('should handle deeply nested object properties', () => {
|
|
329
|
+
const schema = new Schema(
|
|
330
|
+
{
|
|
331
|
+
level1: {
|
|
332
|
+
type: 'object',
|
|
333
|
+
properties: {
|
|
334
|
+
level2: {
|
|
335
|
+
type: 'object',
|
|
336
|
+
properties: {
|
|
337
|
+
deepField: { type: 'string' }
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
},
|
|
343
|
+
'test-schema'
|
|
344
|
+
);
|
|
345
|
+
const object = {
|
|
346
|
+
level1: {
|
|
347
|
+
level2: {
|
|
348
|
+
deepField: 'deep-value'
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
};
|
|
352
|
+
const schemasMap = {};
|
|
353
|
+
const callback = jest.fn();
|
|
354
|
+
|
|
355
|
+
mapObjectProperties(object, schema.jsonSchema, schemasMap, callback);
|
|
356
|
+
|
|
357
|
+
expect(callback).toHaveBeenCalledTimes(3);
|
|
358
|
+
expect(callback).toHaveBeenCalledWith('level1', expect.objectContaining({ type: 'object' }), object);
|
|
359
|
+
expect(callback).toHaveBeenCalledWith('level2', expect.objectContaining({ type: 'object' }), object.level1);
|
|
360
|
+
expect(callback).toHaveBeenCalledWith('deepField', expect.objectContaining({ type: 'string' }), object.level1.level2);
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
it('should call callback but skip recursion when nested object value is undefined', () => {
|
|
364
|
+
const schema = new Schema(
|
|
365
|
+
{
|
|
366
|
+
nestedObject: {
|
|
367
|
+
type: 'object',
|
|
368
|
+
properties: {
|
|
369
|
+
nestedField: { type: 'string' }
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
},
|
|
373
|
+
'test-schema'
|
|
374
|
+
);
|
|
375
|
+
const object = {
|
|
376
|
+
nestedObject: undefined
|
|
377
|
+
};
|
|
378
|
+
const schemasMap = {};
|
|
379
|
+
const callback = jest.fn();
|
|
380
|
+
|
|
381
|
+
mapObjectProperties(object, schema.jsonSchema, schemasMap, callback);
|
|
382
|
+
|
|
383
|
+
expect(callback).toHaveBeenCalledTimes(1);
|
|
384
|
+
expect(callback).toHaveBeenCalledWith('nestedObject', expect.objectContaining({ type: 'object' }), object);
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
it('should call callback but skip recursion when nested object value is null', () => {
|
|
388
|
+
const schema = new Schema(
|
|
389
|
+
{
|
|
390
|
+
nestedObject: {
|
|
391
|
+
type: 'object',
|
|
392
|
+
properties: {
|
|
393
|
+
nestedField: { type: 'string' }
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
},
|
|
397
|
+
'test-schema'
|
|
398
|
+
);
|
|
399
|
+
const object = {
|
|
400
|
+
nestedObject: null
|
|
401
|
+
};
|
|
402
|
+
const schemasMap = {};
|
|
403
|
+
const callback = jest.fn();
|
|
404
|
+
|
|
405
|
+
mapObjectProperties(object, schema.jsonSchema, schemasMap, callback);
|
|
406
|
+
|
|
407
|
+
expect(callback).toHaveBeenCalledTimes(1);
|
|
408
|
+
expect(callback).toHaveBeenCalledWith('nestedObject', expect.objectContaining({ type: 'object' }), object);
|
|
409
|
+
});
|
|
410
|
+
|
|
411
|
+
it('should call callback but skip recursion when nested object value is a primitive', () => {
|
|
412
|
+
const schema = new Schema(
|
|
413
|
+
{
|
|
414
|
+
nestedObject: {
|
|
415
|
+
type: 'object',
|
|
416
|
+
properties: {
|
|
417
|
+
nestedField: { type: 'string' }
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
},
|
|
421
|
+
'test-schema'
|
|
422
|
+
);
|
|
423
|
+
const object = {
|
|
424
|
+
nestedObject: 'primitive-value'
|
|
425
|
+
};
|
|
426
|
+
const schemasMap = {};
|
|
427
|
+
const callback = jest.fn();
|
|
428
|
+
|
|
429
|
+
mapObjectProperties(object, schema.jsonSchema, schemasMap, callback);
|
|
430
|
+
|
|
431
|
+
expect(callback).toHaveBeenCalledTimes(1);
|
|
432
|
+
expect(callback).toHaveBeenCalledWith('nestedObject', expect.objectContaining({ type: 'object' }), object);
|
|
433
|
+
});
|
|
434
|
+
|
|
435
|
+
it('should call callback but skip recursion when nested object value is an array', () => {
|
|
436
|
+
const schema = new Schema(
|
|
437
|
+
{
|
|
438
|
+
nestedObject: {
|
|
439
|
+
type: 'object',
|
|
440
|
+
properties: {
|
|
441
|
+
nestedField: { type: 'string' }
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
},
|
|
445
|
+
'test-schema'
|
|
446
|
+
);
|
|
447
|
+
const object = {
|
|
448
|
+
nestedObject: ['item1', 'item2']
|
|
449
|
+
};
|
|
450
|
+
const schemasMap = {};
|
|
451
|
+
const callback = jest.fn();
|
|
452
|
+
|
|
453
|
+
mapObjectProperties(object, schema.jsonSchema, schemasMap, callback);
|
|
454
|
+
|
|
455
|
+
expect(callback).toHaveBeenCalledTimes(1);
|
|
456
|
+
expect(callback).toHaveBeenCalledWith('nestedObject', expect.objectContaining({ type: 'object' }), object);
|
|
457
|
+
});
|
|
458
|
+
|
|
459
|
+
it('should handle nested object with no properties', () => {
|
|
460
|
+
const schema = new Schema(
|
|
461
|
+
{
|
|
462
|
+
nestedObject: {
|
|
463
|
+
type: 'object',
|
|
464
|
+
properties: {}
|
|
465
|
+
}
|
|
466
|
+
},
|
|
467
|
+
'test-schema'
|
|
468
|
+
);
|
|
469
|
+
const object = {
|
|
470
|
+
nestedObject: {}
|
|
471
|
+
};
|
|
472
|
+
const schemasMap = {};
|
|
473
|
+
const callback = jest.fn();
|
|
474
|
+
|
|
475
|
+
mapObjectProperties(object, schema.jsonSchema, schemasMap, callback);
|
|
476
|
+
|
|
477
|
+
expect(callback).toHaveBeenCalledTimes(1);
|
|
478
|
+
expect(callback).toHaveBeenCalledWith('nestedObject', expect.objectContaining({ type: 'object' }), object);
|
|
479
|
+
});
|
|
480
|
+
|
|
481
|
+
it('should handle object type with undefined properties (tests destructuring default)', () => {
|
|
482
|
+
// Create a schema object manually without normalization to test the default destructuring
|
|
483
|
+
const jsonSchema = {
|
|
484
|
+
id: 'test-schema',
|
|
485
|
+
properties: {
|
|
486
|
+
nestedObject: {
|
|
487
|
+
type: 'object' as const,
|
|
488
|
+
// properties is intentionally undefined to test the default = {} on line 62
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
};
|
|
492
|
+
const object = {
|
|
493
|
+
nestedObject: {
|
|
494
|
+
someField: 'value'
|
|
495
|
+
}
|
|
496
|
+
};
|
|
497
|
+
const schemasMap = {};
|
|
498
|
+
const callback = jest.fn();
|
|
499
|
+
|
|
500
|
+
mapObjectProperties(object, jsonSchema, schemasMap, callback);
|
|
501
|
+
|
|
502
|
+
expect(callback).toHaveBeenCalledTimes(1);
|
|
503
|
+
expect(callback).toHaveBeenCalledWith('nestedObject', expect.objectContaining({ type: 'object' }), object);
|
|
504
|
+
});
|
|
505
|
+
});
|
|
506
|
+
|
|
507
|
+
describe('array properties', () => {
|
|
508
|
+
it('should call callback for array property and recurse into each array item with reference schema', () => {
|
|
509
|
+
const schema = new Schema(
|
|
510
|
+
{
|
|
511
|
+
arrayField: {
|
|
512
|
+
type: 'array',
|
|
513
|
+
items: { $ref: 'item-schema' }
|
|
514
|
+
}
|
|
515
|
+
},
|
|
516
|
+
'test-schema'
|
|
517
|
+
);
|
|
518
|
+
const itemSchema = new Schema(
|
|
519
|
+
{
|
|
520
|
+
itemField: { type: 'string' }
|
|
521
|
+
},
|
|
522
|
+
'item-schema'
|
|
523
|
+
);
|
|
524
|
+
const object = {
|
|
525
|
+
arrayField: [
|
|
526
|
+
{ itemField: 'value1' },
|
|
527
|
+
{ itemField: 'value2' }
|
|
528
|
+
]
|
|
529
|
+
};
|
|
530
|
+
const schemasMap = {
|
|
531
|
+
'item-schema': itemSchema.jsonSchema
|
|
532
|
+
};
|
|
533
|
+
const callback = jest.fn();
|
|
534
|
+
|
|
535
|
+
mapObjectProperties(object, schema.jsonSchema, schemasMap, callback);
|
|
536
|
+
|
|
537
|
+
expect(callback).toHaveBeenCalledTimes(3);
|
|
538
|
+
expect(callback).toHaveBeenCalledWith('arrayField', expect.objectContaining({ type: 'array' }), object);
|
|
539
|
+
expect(callback).toHaveBeenCalledWith('itemField', expect.objectContaining({ type: 'string' }), object.arrayField[0]);
|
|
540
|
+
expect(callback).toHaveBeenCalledWith('itemField', expect.objectContaining({ type: 'string' }), object.arrayField[1]);
|
|
541
|
+
});
|
|
542
|
+
|
|
543
|
+
it('should call callback for array property and recurse into each array item with object schema', () => {
|
|
544
|
+
const schema = new Schema(
|
|
545
|
+
{
|
|
546
|
+
arrayField: {
|
|
547
|
+
type: 'array',
|
|
548
|
+
items: {
|
|
549
|
+
type: 'object',
|
|
550
|
+
properties: {
|
|
551
|
+
itemField: { type: 'string' }
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
},
|
|
556
|
+
'test-schema'
|
|
557
|
+
);
|
|
558
|
+
const object = {
|
|
559
|
+
arrayField: [
|
|
560
|
+
{ itemField: 'value1' },
|
|
561
|
+
{ itemField: 'value2' }
|
|
562
|
+
]
|
|
563
|
+
};
|
|
564
|
+
const schemasMap = {};
|
|
565
|
+
const callback = jest.fn();
|
|
566
|
+
|
|
567
|
+
mapObjectProperties(object, schema.jsonSchema, schemasMap, callback);
|
|
568
|
+
|
|
569
|
+
expect(callback).toHaveBeenCalledTimes(3);
|
|
570
|
+
expect(callback).toHaveBeenCalledWith('arrayField', expect.objectContaining({ type: 'array' }), object);
|
|
571
|
+
expect(callback).toHaveBeenCalledWith('itemField', expect.objectContaining({ type: 'string' }), object.arrayField[0]);
|
|
572
|
+
expect(callback).toHaveBeenCalledWith('itemField', expect.objectContaining({ type: 'string' }), object.arrayField[1]);
|
|
573
|
+
});
|
|
574
|
+
|
|
575
|
+
it('should handle empty array', () => {
|
|
576
|
+
const schema = new Schema(
|
|
577
|
+
{
|
|
578
|
+
arrayField: {
|
|
579
|
+
type: 'array',
|
|
580
|
+
items: { $ref: 'item-schema' }
|
|
581
|
+
}
|
|
582
|
+
},
|
|
583
|
+
'test-schema'
|
|
584
|
+
);
|
|
585
|
+
const itemSchema = new Schema(
|
|
586
|
+
{
|
|
587
|
+
itemField: { type: 'string' }
|
|
588
|
+
},
|
|
589
|
+
'item-schema'
|
|
590
|
+
);
|
|
591
|
+
const object = {
|
|
592
|
+
arrayField: []
|
|
593
|
+
};
|
|
594
|
+
const schemasMap = {
|
|
595
|
+
'item-schema': itemSchema.jsonSchema
|
|
596
|
+
};
|
|
597
|
+
const callback = jest.fn();
|
|
598
|
+
|
|
599
|
+
mapObjectProperties(object, schema.jsonSchema, schemasMap, callback);
|
|
600
|
+
|
|
601
|
+
expect(callback).toHaveBeenCalledTimes(1);
|
|
602
|
+
expect(callback).toHaveBeenCalledWith('arrayField', expect.objectContaining({ type: 'array' }), object);
|
|
603
|
+
});
|
|
604
|
+
|
|
605
|
+
it('should call callback but skip recursion when array value is undefined', () => {
|
|
606
|
+
const schema = new Schema(
|
|
607
|
+
{
|
|
608
|
+
arrayField: {
|
|
609
|
+
type: 'array',
|
|
610
|
+
items: { $ref: 'item-schema' }
|
|
611
|
+
}
|
|
612
|
+
},
|
|
613
|
+
'test-schema'
|
|
614
|
+
);
|
|
615
|
+
const itemSchema = new Schema(
|
|
616
|
+
{
|
|
617
|
+
itemField: { type: 'string' }
|
|
618
|
+
},
|
|
619
|
+
'item-schema'
|
|
620
|
+
);
|
|
621
|
+
const object = {
|
|
622
|
+
arrayField: undefined
|
|
623
|
+
};
|
|
624
|
+
const schemasMap = {
|
|
625
|
+
'item-schema': itemSchema.jsonSchema
|
|
626
|
+
};
|
|
627
|
+
const callback = jest.fn();
|
|
628
|
+
|
|
629
|
+
mapObjectProperties(object, schema.jsonSchema, schemasMap, callback);
|
|
630
|
+
|
|
631
|
+
expect(callback).toHaveBeenCalledTimes(1);
|
|
632
|
+
expect(callback).toHaveBeenCalledWith('arrayField', expect.objectContaining({ type: 'array' }), object);
|
|
633
|
+
});
|
|
634
|
+
|
|
635
|
+
it('should call callback but skip recursion when array value is null', () => {
|
|
636
|
+
const schema = new Schema(
|
|
637
|
+
{
|
|
638
|
+
arrayField: {
|
|
639
|
+
type: 'array',
|
|
640
|
+
items: { $ref: 'item-schema' }
|
|
641
|
+
}
|
|
642
|
+
},
|
|
643
|
+
'test-schema'
|
|
644
|
+
);
|
|
645
|
+
const itemSchema = new Schema(
|
|
646
|
+
{
|
|
647
|
+
itemField: { type: 'string' }
|
|
648
|
+
},
|
|
649
|
+
'item-schema'
|
|
650
|
+
);
|
|
651
|
+
const object = {
|
|
652
|
+
arrayField: null
|
|
653
|
+
};
|
|
654
|
+
const schemasMap = {
|
|
655
|
+
'item-schema': itemSchema.jsonSchema
|
|
656
|
+
};
|
|
657
|
+
const callback = jest.fn();
|
|
658
|
+
|
|
659
|
+
mapObjectProperties(object, schema.jsonSchema, schemasMap, callback);
|
|
660
|
+
|
|
661
|
+
expect(callback).toHaveBeenCalledTimes(1);
|
|
662
|
+
expect(callback).toHaveBeenCalledWith('arrayField', expect.objectContaining({ type: 'array' }), object);
|
|
663
|
+
});
|
|
664
|
+
|
|
665
|
+
it('should call callback but skip recursion when array property has undefined items schema', () => {
|
|
666
|
+
const schema = new Schema(
|
|
667
|
+
{
|
|
668
|
+
arrayField: {
|
|
669
|
+
type: 'array'
|
|
670
|
+
// items is intentionally undefined
|
|
671
|
+
}
|
|
672
|
+
},
|
|
673
|
+
'test-schema'
|
|
674
|
+
);
|
|
675
|
+
const object = {
|
|
676
|
+
arrayField: [
|
|
677
|
+
{ itemField: 'value1' },
|
|
678
|
+
{ itemField: 'value2' }
|
|
679
|
+
]
|
|
680
|
+
};
|
|
681
|
+
const schemasMap = {};
|
|
682
|
+
const callback = jest.fn();
|
|
683
|
+
|
|
684
|
+
mapObjectProperties(object, schema.jsonSchema, schemasMap, callback);
|
|
685
|
+
|
|
686
|
+
expect(callback).toHaveBeenCalledTimes(1);
|
|
687
|
+
expect(callback).toHaveBeenCalledWith('arrayField', expect.objectContaining({ type: 'array' }), object);
|
|
688
|
+
});
|
|
689
|
+
|
|
690
|
+
it('should call callback but skip recursion for array items that are null', () => {
|
|
691
|
+
const schema = new Schema(
|
|
692
|
+
{
|
|
693
|
+
arrayField: {
|
|
694
|
+
type: 'array',
|
|
695
|
+
items: { $ref: 'item-schema' }
|
|
696
|
+
}
|
|
697
|
+
},
|
|
698
|
+
'test-schema'
|
|
699
|
+
);
|
|
700
|
+
const itemSchema = new Schema(
|
|
701
|
+
{
|
|
702
|
+
itemField: { type: 'string' }
|
|
703
|
+
},
|
|
704
|
+
'item-schema'
|
|
705
|
+
);
|
|
706
|
+
const object = {
|
|
707
|
+
arrayField: [
|
|
708
|
+
null,
|
|
709
|
+
{ itemField: 'value2' },
|
|
710
|
+
null
|
|
711
|
+
]
|
|
712
|
+
};
|
|
713
|
+
const schemasMap = {
|
|
714
|
+
'item-schema': itemSchema.jsonSchema
|
|
715
|
+
};
|
|
716
|
+
const callback = jest.fn();
|
|
717
|
+
|
|
718
|
+
mapObjectProperties(object, schema.jsonSchema, schemasMap, callback);
|
|
719
|
+
|
|
720
|
+
expect(callback).toHaveBeenCalledTimes(2);
|
|
721
|
+
expect(callback).toHaveBeenCalledWith('arrayField', expect.objectContaining({ type: 'array' }), object);
|
|
722
|
+
expect(callback).toHaveBeenCalledWith('itemField', expect.objectContaining({ type: 'string' }), object.arrayField[1]);
|
|
723
|
+
});
|
|
724
|
+
|
|
725
|
+
it('should call callback but skip recursion for array items that are primitives', () => {
|
|
726
|
+
const schema = new Schema(
|
|
727
|
+
{
|
|
728
|
+
arrayField: {
|
|
729
|
+
type: 'array',
|
|
730
|
+
items: { $ref: 'item-schema' }
|
|
731
|
+
}
|
|
732
|
+
},
|
|
733
|
+
'test-schema'
|
|
734
|
+
);
|
|
735
|
+
const itemSchema = new Schema(
|
|
736
|
+
{
|
|
737
|
+
itemField: { type: 'string' }
|
|
738
|
+
},
|
|
739
|
+
'item-schema'
|
|
740
|
+
);
|
|
741
|
+
const object = {
|
|
742
|
+
arrayField: [
|
|
743
|
+
'primitive1',
|
|
744
|
+
{ itemField: 'value2' },
|
|
745
|
+
'primitive3'
|
|
746
|
+
]
|
|
747
|
+
};
|
|
748
|
+
const schemasMap = {
|
|
749
|
+
'item-schema': itemSchema.jsonSchema
|
|
750
|
+
};
|
|
751
|
+
const callback = jest.fn();
|
|
752
|
+
|
|
753
|
+
mapObjectProperties(object, schema.jsonSchema, schemasMap, callback);
|
|
754
|
+
|
|
755
|
+
expect(callback).toHaveBeenCalledTimes(2);
|
|
756
|
+
expect(callback).toHaveBeenCalledWith('arrayField', expect.objectContaining({ type: 'array' }), object);
|
|
757
|
+
expect(callback).toHaveBeenCalledWith('itemField', expect.objectContaining({ type: 'string' }), object.arrayField[1]);
|
|
758
|
+
});
|
|
759
|
+
|
|
760
|
+
it('should call callback but skip recursion for array items that are arrays', () => {
|
|
761
|
+
const schema = new Schema(
|
|
762
|
+
{
|
|
763
|
+
arrayField: {
|
|
764
|
+
type: 'array',
|
|
765
|
+
items: { $ref: 'item-schema' }
|
|
766
|
+
}
|
|
767
|
+
},
|
|
768
|
+
'test-schema'
|
|
769
|
+
);
|
|
770
|
+
const itemSchema = new Schema(
|
|
771
|
+
{
|
|
772
|
+
itemField: { type: 'string' }
|
|
773
|
+
},
|
|
774
|
+
'item-schema'
|
|
775
|
+
);
|
|
776
|
+
const object = {
|
|
777
|
+
arrayField: [
|
|
778
|
+
['nested', 'array'],
|
|
779
|
+
{ itemField: 'value2' },
|
|
780
|
+
['another', 'array']
|
|
781
|
+
]
|
|
782
|
+
};
|
|
783
|
+
const schemasMap = {
|
|
784
|
+
'item-schema': itemSchema.jsonSchema
|
|
785
|
+
};
|
|
786
|
+
const callback = jest.fn();
|
|
787
|
+
|
|
788
|
+
mapObjectProperties(object, schema.jsonSchema, schemasMap, callback);
|
|
789
|
+
|
|
790
|
+
expect(callback).toHaveBeenCalledTimes(2);
|
|
791
|
+
expect(callback).toHaveBeenCalledWith('arrayField', expect.objectContaining({ type: 'array' }), object);
|
|
792
|
+
expect(callback).toHaveBeenCalledWith('itemField', expect.objectContaining({ type: 'string' }), object.arrayField[1]);
|
|
793
|
+
});
|
|
794
|
+
|
|
795
|
+
it('should handle nested references in array items', () => {
|
|
796
|
+
const schema = new Schema(
|
|
797
|
+
{
|
|
798
|
+
arrayField: {
|
|
799
|
+
type: 'array',
|
|
800
|
+
items: { $ref: 'item-schema' }
|
|
801
|
+
}
|
|
802
|
+
},
|
|
803
|
+
'test-schema'
|
|
804
|
+
);
|
|
805
|
+
const itemSchema = new Schema(
|
|
806
|
+
{
|
|
807
|
+
refField: { $ref: 'nested-item-schema' }
|
|
808
|
+
},
|
|
809
|
+
'item-schema'
|
|
810
|
+
);
|
|
811
|
+
const nestedItemSchema = new Schema(
|
|
812
|
+
{
|
|
813
|
+
nestedField: { type: 'string' }
|
|
814
|
+
},
|
|
815
|
+
'nested-item-schema'
|
|
816
|
+
);
|
|
817
|
+
const object = {
|
|
818
|
+
arrayField: [
|
|
819
|
+
{ refField: { nestedField: 'value1' } },
|
|
820
|
+
{ refField: { nestedField: 'value2' } }
|
|
821
|
+
]
|
|
822
|
+
};
|
|
823
|
+
const schemasMap = {
|
|
824
|
+
'item-schema': itemSchema.jsonSchema,
|
|
825
|
+
'nested-item-schema': nestedItemSchema.jsonSchema
|
|
826
|
+
};
|
|
827
|
+
const callback = jest.fn();
|
|
828
|
+
|
|
829
|
+
mapObjectProperties(object, schema.jsonSchema, schemasMap, callback);
|
|
830
|
+
|
|
831
|
+
expect(callback).toHaveBeenCalledTimes(5);
|
|
832
|
+
expect(callback).toHaveBeenCalledWith('arrayField', expect.objectContaining({ type: 'array' }), object);
|
|
833
|
+
expect(callback).toHaveBeenCalledWith('refField', expect.objectContaining({ $ref: 'nested-item-schema' }), object.arrayField[0]);
|
|
834
|
+
expect(callback).toHaveBeenCalledWith('nestedField', expect.objectContaining({ type: 'string' }), object.arrayField[0].refField);
|
|
835
|
+
expect(callback).toHaveBeenCalledWith('refField', expect.objectContaining({ $ref: 'nested-item-schema' }), object.arrayField[1]);
|
|
836
|
+
expect(callback).toHaveBeenCalledWith('nestedField', expect.objectContaining({ type: 'string' }), object.arrayField[1].refField);
|
|
837
|
+
});
|
|
838
|
+
});
|
|
839
|
+
|
|
840
|
+
describe('complex nested scenarios', () => {
|
|
841
|
+
it('should handle mix of reference, object, and array properties', () => {
|
|
842
|
+
const schema = new Schema(
|
|
843
|
+
{
|
|
844
|
+
refField: { $ref: 'ref-schema' },
|
|
845
|
+
nestedObject: {
|
|
846
|
+
type: 'object',
|
|
847
|
+
properties: {
|
|
848
|
+
nestedField: { type: 'string' }
|
|
849
|
+
}
|
|
850
|
+
},
|
|
851
|
+
arrayField: {
|
|
852
|
+
type: 'array',
|
|
853
|
+
items: { $ref: 'array-item-schema' }
|
|
854
|
+
}
|
|
855
|
+
},
|
|
856
|
+
'test-schema'
|
|
857
|
+
);
|
|
858
|
+
const refSchema = new Schema(
|
|
859
|
+
{
|
|
860
|
+
refFieldProperty: { type: 'string' }
|
|
861
|
+
},
|
|
862
|
+
'ref-schema'
|
|
863
|
+
);
|
|
864
|
+
const arrayItemSchema = new Schema(
|
|
865
|
+
{
|
|
866
|
+
itemField: { type: 'string' }
|
|
867
|
+
},
|
|
868
|
+
'array-item-schema'
|
|
869
|
+
);
|
|
870
|
+
const object = {
|
|
871
|
+
refField: {
|
|
872
|
+
refFieldProperty: 'ref-value'
|
|
873
|
+
},
|
|
874
|
+
nestedObject: {
|
|
875
|
+
nestedField: 'nested-value'
|
|
876
|
+
},
|
|
877
|
+
arrayField: [
|
|
878
|
+
{ itemField: 'item1' },
|
|
879
|
+
{ itemField: 'item2' }
|
|
880
|
+
]
|
|
881
|
+
};
|
|
882
|
+
const schemasMap = {
|
|
883
|
+
'ref-schema': refSchema.jsonSchema,
|
|
884
|
+
'array-item-schema': arrayItemSchema.jsonSchema
|
|
885
|
+
};
|
|
886
|
+
const callback = jest.fn();
|
|
887
|
+
|
|
888
|
+
mapObjectProperties(object, schema.jsonSchema, schemasMap, callback);
|
|
889
|
+
|
|
890
|
+
expect(callback).toHaveBeenCalledTimes(7);
|
|
891
|
+
expect(callback).toHaveBeenCalledWith('refField', expect.objectContaining({ $ref: 'ref-schema' }), object);
|
|
892
|
+
expect(callback).toHaveBeenCalledWith('refFieldProperty', expect.any(Object), object.refField);
|
|
893
|
+
expect(callback).toHaveBeenCalledWith('nestedObject', expect.objectContaining({ type: 'object' }), object);
|
|
894
|
+
expect(callback).toHaveBeenCalledWith('nestedField', expect.any(Object), object.nestedObject);
|
|
895
|
+
expect(callback).toHaveBeenCalledWith('arrayField', expect.objectContaining({ type: 'array' }), object);
|
|
896
|
+
expect(callback).toHaveBeenCalledWith('itemField', expect.any(Object), object.arrayField[0]);
|
|
897
|
+
expect(callback).toHaveBeenCalledWith('itemField', expect.any(Object), object.arrayField[1]);
|
|
898
|
+
});
|
|
899
|
+
|
|
900
|
+
it('should handle array of objects with nested references', () => {
|
|
901
|
+
const schema = new Schema(
|
|
902
|
+
{
|
|
903
|
+
arrayField: {
|
|
904
|
+
type: 'array',
|
|
905
|
+
items: {
|
|
906
|
+
type: 'object',
|
|
907
|
+
properties: {
|
|
908
|
+
refField: { $ref: 'array-nested-ref-schema' }
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
},
|
|
913
|
+
'test-schema'
|
|
914
|
+
);
|
|
915
|
+
const arrayNestedRefSchema = new Schema(
|
|
916
|
+
{
|
|
917
|
+
nestedRef: { $ref: 'deep-nested-schema' }
|
|
918
|
+
},
|
|
919
|
+
'array-nested-ref-schema'
|
|
920
|
+
);
|
|
921
|
+
const deepNestedSchema = new Schema(
|
|
922
|
+
{
|
|
923
|
+
deepField: { type: 'string' }
|
|
924
|
+
},
|
|
925
|
+
'deep-nested-schema'
|
|
926
|
+
);
|
|
927
|
+
const object = {
|
|
928
|
+
arrayField: [
|
|
929
|
+
{
|
|
930
|
+
refField: {
|
|
931
|
+
nestedRef: {
|
|
932
|
+
deepField: 'deep-value1'
|
|
933
|
+
}
|
|
934
|
+
}
|
|
935
|
+
},
|
|
936
|
+
{
|
|
937
|
+
refField: {
|
|
938
|
+
nestedRef: {
|
|
939
|
+
deepField: 'deep-value2'
|
|
940
|
+
}
|
|
941
|
+
}
|
|
942
|
+
}
|
|
943
|
+
]
|
|
944
|
+
};
|
|
945
|
+
const schemasMap = {
|
|
946
|
+
'array-nested-ref-schema': arrayNestedRefSchema.jsonSchema,
|
|
947
|
+
'deep-nested-schema': deepNestedSchema.jsonSchema
|
|
948
|
+
};
|
|
949
|
+
const callback = jest.fn();
|
|
950
|
+
|
|
951
|
+
mapObjectProperties(object, schema.jsonSchema, schemasMap, callback);
|
|
952
|
+
|
|
953
|
+
expect(callback).toHaveBeenCalledTimes(7);
|
|
954
|
+
expect(callback).toHaveBeenCalledWith('arrayField', expect.objectContaining({ type: 'array' }), object);
|
|
955
|
+
expect(callback).toHaveBeenCalledWith('refField', expect.objectContaining({ $ref: 'array-nested-ref-schema' }), object.arrayField[0]);
|
|
956
|
+
expect(callback).toHaveBeenCalledWith('nestedRef', expect.objectContaining({ $ref: 'deep-nested-schema' }), object.arrayField[0].refField);
|
|
957
|
+
expect(callback).toHaveBeenCalledWith('deepField', expect.any(Object), object.arrayField[0].refField.nestedRef);
|
|
958
|
+
expect(callback).toHaveBeenCalledWith('refField', expect.objectContaining({ $ref: 'array-nested-ref-schema' }), object.arrayField[1]);
|
|
959
|
+
expect(callback).toHaveBeenCalledWith('nestedRef', expect.objectContaining({ $ref: 'deep-nested-schema' }), object.arrayField[1].refField);
|
|
960
|
+
expect(callback).toHaveBeenCalledWith('deepField', expect.any(Object), object.arrayField[1].refField.nestedRef);
|
|
961
|
+
});
|
|
962
|
+
|
|
963
|
+
it('should pass correct object context to callback at each level', () => {
|
|
964
|
+
const schema = new Schema(
|
|
965
|
+
{
|
|
966
|
+
topField: { type: 'string' },
|
|
967
|
+
nestedObject: {
|
|
968
|
+
type: 'object',
|
|
969
|
+
properties: {
|
|
970
|
+
nestedField: { type: 'string' }
|
|
971
|
+
}
|
|
972
|
+
}
|
|
973
|
+
},
|
|
974
|
+
'test-schema'
|
|
975
|
+
);
|
|
976
|
+
const object = {
|
|
977
|
+
topField: 'top-value',
|
|
978
|
+
nestedObject: {
|
|
979
|
+
nestedField: 'nested-value'
|
|
980
|
+
}
|
|
981
|
+
};
|
|
982
|
+
const schemasMap = {};
|
|
983
|
+
const callback = jest.fn();
|
|
984
|
+
|
|
985
|
+
mapObjectProperties(object, schema.jsonSchema, schemasMap, callback);
|
|
986
|
+
|
|
987
|
+
expect(callback).toHaveBeenCalledTimes(3);
|
|
988
|
+
expect(callback).toHaveBeenNthCalledWith(1, 'topField', expect.any(Object), object);
|
|
989
|
+
expect(callback).toHaveBeenNthCalledWith(2, 'nestedObject', expect.any(Object), object);
|
|
990
|
+
expect(callback).toHaveBeenNthCalledWith(3, 'nestedField', expect.any(Object), object.nestedObject);
|
|
991
|
+
});
|
|
992
|
+
});
|
|
993
|
+
|
|
994
|
+
describe('edge cases', () => {
|
|
995
|
+
it('should handle object with only undefined values', () => {
|
|
996
|
+
const schema = new Schema(
|
|
997
|
+
{
|
|
998
|
+
field1: { type: 'string' },
|
|
999
|
+
field2: { type: 'number' }
|
|
1000
|
+
},
|
|
1001
|
+
'test-schema'
|
|
1002
|
+
);
|
|
1003
|
+
const object = {
|
|
1004
|
+
field1: undefined,
|
|
1005
|
+
field2: undefined
|
|
1006
|
+
};
|
|
1007
|
+
const schemasMap = {};
|
|
1008
|
+
const callback = jest.fn();
|
|
1009
|
+
|
|
1010
|
+
mapObjectProperties(object, schema.jsonSchema, schemasMap, callback);
|
|
1011
|
+
|
|
1012
|
+
expect(callback).toHaveBeenCalledTimes(2);
|
|
1013
|
+
expect(callback).toHaveBeenCalledWith('field1', expect.any(Object), object);
|
|
1014
|
+
expect(callback).toHaveBeenCalledWith('field2', expect.any(Object), object);
|
|
1015
|
+
});
|
|
1016
|
+
|
|
1017
|
+
it('should handle object with only null values', () => {
|
|
1018
|
+
const schema = new Schema(
|
|
1019
|
+
{
|
|
1020
|
+
field1: { type: 'string' },
|
|
1021
|
+
field2: { type: 'number' }
|
|
1022
|
+
},
|
|
1023
|
+
'test-schema'
|
|
1024
|
+
);
|
|
1025
|
+
const object = {
|
|
1026
|
+
field1: null,
|
|
1027
|
+
field2: null
|
|
1028
|
+
};
|
|
1029
|
+
const schemasMap = {};
|
|
1030
|
+
const callback = jest.fn();
|
|
1031
|
+
|
|
1032
|
+
mapObjectProperties(object, schema.jsonSchema, schemasMap, callback);
|
|
1033
|
+
|
|
1034
|
+
expect(callback).toHaveBeenCalledTimes(2);
|
|
1035
|
+
expect(callback).toHaveBeenCalledWith('field1', expect.any(Object), object);
|
|
1036
|
+
expect(callback).toHaveBeenCalledWith('field2', expect.any(Object), object);
|
|
1037
|
+
});
|
|
1038
|
+
|
|
1039
|
+
it('should handle array with items that have no properties', () => {
|
|
1040
|
+
const schema = new Schema(
|
|
1041
|
+
{
|
|
1042
|
+
arrayField: {
|
|
1043
|
+
type: 'array',
|
|
1044
|
+
items: {
|
|
1045
|
+
type: 'object'
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
1048
|
+
},
|
|
1049
|
+
'test-schema'
|
|
1050
|
+
);
|
|
1051
|
+
const object = {
|
|
1052
|
+
arrayField: [
|
|
1053
|
+
{},
|
|
1054
|
+
{}
|
|
1055
|
+
]
|
|
1056
|
+
};
|
|
1057
|
+
const schemasMap = {};
|
|
1058
|
+
const callback = jest.fn();
|
|
1059
|
+
|
|
1060
|
+
mapObjectProperties(object, schema.jsonSchema, schemasMap, callback);
|
|
1061
|
+
|
|
1062
|
+
expect(callback).toHaveBeenCalledTimes(1);
|
|
1063
|
+
expect(callback).toHaveBeenCalledWith('arrayField', expect.objectContaining({ type: 'array' }), object);
|
|
1064
|
+
});
|
|
1065
|
+
|
|
1066
|
+
it('should handle schema with undefined properties (malformed schema)', () => {
|
|
1067
|
+
const jsonSchema = {
|
|
1068
|
+
id: 'test-schema'
|
|
1069
|
+
// properties is intentionally undefined (malformed schema)
|
|
1070
|
+
} as unknown as JsonSchema;
|
|
1071
|
+
const object = {
|
|
1072
|
+
field1: 'value1',
|
|
1073
|
+
field2: 'value2'
|
|
1074
|
+
};
|
|
1075
|
+
const schemasMap = {};
|
|
1076
|
+
const callback = jest.fn();
|
|
1077
|
+
|
|
1078
|
+
mapObjectProperties(object, jsonSchema, schemasMap, callback);
|
|
1079
|
+
|
|
1080
|
+
expect(callback).not.toHaveBeenCalled();
|
|
1081
|
+
});
|
|
1082
|
+
|
|
1083
|
+
it('should handle callback that modifies the object', () => {
|
|
1084
|
+
const schema = new Schema(
|
|
1085
|
+
{
|
|
1086
|
+
field: { type: 'string' }
|
|
1087
|
+
},
|
|
1088
|
+
'test-schema'
|
|
1089
|
+
);
|
|
1090
|
+
const object = {
|
|
1091
|
+
field: 'original'
|
|
1092
|
+
};
|
|
1093
|
+
const schemasMap = {};
|
|
1094
|
+
const callback = jest.fn((propertyName, propertySchema, obj) => {
|
|
1095
|
+
if (propertyName === 'field') {
|
|
1096
|
+
obj[propertyName] = 'modified';
|
|
1097
|
+
}
|
|
1098
|
+
});
|
|
1099
|
+
|
|
1100
|
+
mapObjectProperties(object, schema.jsonSchema, schemasMap, callback);
|
|
1101
|
+
|
|
1102
|
+
expect(callback).toHaveBeenCalledTimes(1);
|
|
1103
|
+
expect(object.field).toBe('modified');
|
|
1104
|
+
});
|
|
1105
|
+
|
|
1106
|
+
it('should handle callback that throws an error', () => {
|
|
1107
|
+
const schema = new Schema(
|
|
1108
|
+
{
|
|
1109
|
+
field: { type: 'string' }
|
|
1110
|
+
},
|
|
1111
|
+
'test-schema'
|
|
1112
|
+
);
|
|
1113
|
+
const object = {
|
|
1114
|
+
field: 'value'
|
|
1115
|
+
};
|
|
1116
|
+
const schemasMap = {};
|
|
1117
|
+
const callback = jest.fn(() => {
|
|
1118
|
+
throw new Error('Callback error');
|
|
1119
|
+
});
|
|
1120
|
+
|
|
1121
|
+
expect(() => {
|
|
1122
|
+
mapObjectProperties(object, schema.jsonSchema, schemasMap, callback);
|
|
1123
|
+
}).toThrow('Callback error');
|
|
1124
|
+
});
|
|
1125
|
+
});
|
|
1126
|
+
});
|