@schmock/faker 2.3.0 → 2.4.0
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 +21 -0
- package/README.md +45 -0
- package/dist/constants.d.ts +82 -1
- package/dist/constants.d.ts.map +1 -1
- package/dist/field-name-matcher.d.ts +19 -0
- package/dist/field-name-matcher.d.ts.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -1
- package/dist/jsf-config.d.ts +19 -4
- package/dist/jsf-config.d.ts.map +1 -1
- package/dist/output-limits.d.ts +3 -0
- package/dist/output-limits.d.ts.map +1 -0
- package/dist/overrides.d.ts +2 -1
- package/dist/overrides.d.ts.map +1 -1
- package/dist/schema-children.d.ts +14 -0
- package/dist/schema-children.d.ts.map +1 -0
- package/dist/schema-enhancement.d.ts +1 -0
- package/dist/schema-enhancement.d.ts.map +1 -1
- package/dist/validation.d.ts +24 -5
- package/dist/validation.d.ts.map +1 -1
- package/package.json +21 -7
- package/dist/constants.js +0 -8
- package/dist/field-mappings.js +0 -1114
- package/dist/field-name-matcher.js +0 -148
- package/dist/jsf-config.js +0 -117
- package/dist/overrides.js +0 -137
- package/dist/schema-enhancement.js +0 -104
- package/dist/test-utils.d.ts +0 -57
- package/dist/test-utils.d.ts.map +0 -1
- package/dist/test-utils.js +0 -284
- package/dist/validation.js +0 -300
- package/src/advanced-features.test.ts +0 -912
- package/src/audit-faker-args.test.ts +0 -97
- package/src/constants.d.ts.map +0 -1
- package/src/constants.ts +0 -9
- package/src/data-quality.test.ts +0 -415
- package/src/error-handling.test.ts +0 -504
- package/src/field-mappings.test.ts +0 -351
- package/src/field-mappings.ts +0 -1148
- package/src/field-name-matcher.test.ts +0 -292
- package/src/field-name-matcher.ts +0 -187
- package/src/index.d.ts.map +0 -1
- package/src/index.test.ts +0 -1320
- package/src/index.ts +0 -185
- package/src/integration.test.ts +0 -634
- package/src/jsf-config.d.ts.map +0 -1
- package/src/jsf-config.test.ts +0 -65
- package/src/jsf-config.ts +0 -156
- package/src/overrides.d.ts.map +0 -1
- package/src/overrides.property.test.ts +0 -172
- package/src/overrides.test.ts +0 -38
- package/src/overrides.ts +0 -179
- package/src/performance.test.ts +0 -441
- package/src/plugin-integration.test.ts +0 -575
- package/src/post-process.test.ts +0 -177
- package/src/real-world.test.ts +0 -636
- package/src/schema-enhancement.d.ts.map +0 -1
- package/src/schema-enhancement.test.ts +0 -222
- package/src/schema-enhancement.ts +0 -144
- package/src/steps/deterministic-seeds.steps.ts +0 -112
- package/src/steps/faker-plugin.steps.ts +0 -301
- package/src/test-utils.ts +0 -385
- package/src/validation.d.ts.map +0 -1
- package/src/validation.ts +0 -417
|
@@ -1,912 +0,0 @@
|
|
|
1
|
-
import type { JSONSchema7 } from "json-schema";
|
|
2
|
-
import { describe, expect, it } from "vitest";
|
|
3
|
-
import { generateFromSchema } from "./index";
|
|
4
|
-
import { generate, validators } from "./test-utils";
|
|
5
|
-
|
|
6
|
-
describe("Advanced Schema Features", () => {
|
|
7
|
-
describe("Schema Composition", () => {
|
|
8
|
-
it("handles allOf schema composition", async () => {
|
|
9
|
-
const schema: JSONSchema7 = {
|
|
10
|
-
allOf: [
|
|
11
|
-
{
|
|
12
|
-
type: "object",
|
|
13
|
-
properties: {
|
|
14
|
-
name: { type: "string" },
|
|
15
|
-
age: { type: "number" },
|
|
16
|
-
},
|
|
17
|
-
required: ["name"],
|
|
18
|
-
},
|
|
19
|
-
{
|
|
20
|
-
type: "object",
|
|
21
|
-
properties: {
|
|
22
|
-
email: { type: "string" },
|
|
23
|
-
age: { type: "number", minimum: 18 },
|
|
24
|
-
},
|
|
25
|
-
required: ["email"],
|
|
26
|
-
},
|
|
27
|
-
],
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
const result = await generateFromSchema({ schema });
|
|
31
|
-
|
|
32
|
-
// Should have properties from both schemas
|
|
33
|
-
expect(result).toHaveProperty("name");
|
|
34
|
-
expect(result).toHaveProperty("email");
|
|
35
|
-
expect(result).toHaveProperty("age");
|
|
36
|
-
expect(typeof result.name).toBe("string");
|
|
37
|
-
expect(typeof result.email).toBe("string");
|
|
38
|
-
expect(result.age).toBeGreaterThanOrEqual(18);
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
it("handles anyOf schema composition", async () => {
|
|
42
|
-
const schema: JSONSchema7 = {
|
|
43
|
-
anyOf: [
|
|
44
|
-
{
|
|
45
|
-
type: "object",
|
|
46
|
-
properties: {
|
|
47
|
-
type: { const: "person" },
|
|
48
|
-
name: { type: "string" },
|
|
49
|
-
age: { type: "number" },
|
|
50
|
-
},
|
|
51
|
-
required: ["type", "name", "age"],
|
|
52
|
-
},
|
|
53
|
-
{
|
|
54
|
-
type: "object",
|
|
55
|
-
properties: {
|
|
56
|
-
type: { const: "company" },
|
|
57
|
-
name: { type: "string" },
|
|
58
|
-
employees: { type: "number" },
|
|
59
|
-
},
|
|
60
|
-
required: ["type", "name", "employees"],
|
|
61
|
-
},
|
|
62
|
-
],
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
const results = await generate.samples<any>(schema, 10);
|
|
66
|
-
|
|
67
|
-
results.forEach((result) => {
|
|
68
|
-
expect(result).toHaveProperty("type");
|
|
69
|
-
expect(result).toHaveProperty("name");
|
|
70
|
-
|
|
71
|
-
if (result.type === "person") {
|
|
72
|
-
expect(result).toHaveProperty("age");
|
|
73
|
-
expect(typeof result.age).toBe("number");
|
|
74
|
-
} else if (result.type === "company") {
|
|
75
|
-
expect(result).toHaveProperty("employees");
|
|
76
|
-
expect(typeof result.employees).toBe("number");
|
|
77
|
-
}
|
|
78
|
-
});
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
it("handles oneOf schema composition", async () => {
|
|
82
|
-
const schema: JSONSchema7 = {
|
|
83
|
-
oneOf: [
|
|
84
|
-
{
|
|
85
|
-
type: "string",
|
|
86
|
-
pattern: "^[A-Z]{3}$",
|
|
87
|
-
},
|
|
88
|
-
{
|
|
89
|
-
type: "number",
|
|
90
|
-
minimum: 100,
|
|
91
|
-
maximum: 999,
|
|
92
|
-
},
|
|
93
|
-
],
|
|
94
|
-
};
|
|
95
|
-
|
|
96
|
-
const results = await generate.samples<any>(schema, 20);
|
|
97
|
-
|
|
98
|
-
results.forEach((result) => {
|
|
99
|
-
const isString = typeof result === "string";
|
|
100
|
-
const isNumber = typeof result === "number";
|
|
101
|
-
|
|
102
|
-
// Should be exactly one type
|
|
103
|
-
expect(isString || isNumber).toBe(true);
|
|
104
|
-
expect(isString && isNumber).toBe(false);
|
|
105
|
-
|
|
106
|
-
if (isString) {
|
|
107
|
-
expect(result).toMatch(/^[A-Z]{3}$/);
|
|
108
|
-
} else if (isNumber) {
|
|
109
|
-
expect(result).toBeGreaterThanOrEqual(100);
|
|
110
|
-
expect(result).toBeLessThanOrEqual(999);
|
|
111
|
-
}
|
|
112
|
-
});
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
it("handles not schema negation", async () => {
|
|
116
|
-
const schema: JSONSchema7 = {
|
|
117
|
-
type: "object",
|
|
118
|
-
properties: {
|
|
119
|
-
value: {
|
|
120
|
-
type: "string",
|
|
121
|
-
not: {
|
|
122
|
-
pattern: "^test",
|
|
123
|
-
},
|
|
124
|
-
},
|
|
125
|
-
},
|
|
126
|
-
};
|
|
127
|
-
|
|
128
|
-
const results = await generate.samples<any>(schema, 10);
|
|
129
|
-
|
|
130
|
-
results.forEach((result) => {
|
|
131
|
-
expect(result.value).not.toMatch(/^test/);
|
|
132
|
-
});
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
it("handles nested composition schemas", async () => {
|
|
136
|
-
const schema: JSONSchema7 = {
|
|
137
|
-
type: "object",
|
|
138
|
-
properties: {
|
|
139
|
-
data: {
|
|
140
|
-
anyOf: [
|
|
141
|
-
{
|
|
142
|
-
type: "object",
|
|
143
|
-
properties: {
|
|
144
|
-
text: { type: "string" },
|
|
145
|
-
},
|
|
146
|
-
required: ["text"],
|
|
147
|
-
},
|
|
148
|
-
{
|
|
149
|
-
type: "array",
|
|
150
|
-
items: { type: "number" },
|
|
151
|
-
},
|
|
152
|
-
],
|
|
153
|
-
},
|
|
154
|
-
},
|
|
155
|
-
required: ["data"],
|
|
156
|
-
};
|
|
157
|
-
|
|
158
|
-
const results = await generate.samples<any>(schema, 10);
|
|
159
|
-
|
|
160
|
-
results.forEach((result) => {
|
|
161
|
-
expect(result).toHaveProperty("data");
|
|
162
|
-
|
|
163
|
-
if (typeof result.data === "object" && !Array.isArray(result.data)) {
|
|
164
|
-
expect(result.data).toHaveProperty("text");
|
|
165
|
-
expect(typeof result.data.text).toBe("string");
|
|
166
|
-
} else if (Array.isArray(result.data)) {
|
|
167
|
-
result.data.forEach((item) => {
|
|
168
|
-
expect(typeof item).toBe("number");
|
|
169
|
-
});
|
|
170
|
-
}
|
|
171
|
-
});
|
|
172
|
-
});
|
|
173
|
-
});
|
|
174
|
-
|
|
175
|
-
describe("Advanced Constraints", () => {
|
|
176
|
-
it("respects string pattern constraints", async () => {
|
|
177
|
-
const schema: JSONSchema7 = {
|
|
178
|
-
type: "object",
|
|
179
|
-
properties: {
|
|
180
|
-
code: {
|
|
181
|
-
type: "string",
|
|
182
|
-
pattern: "^[A-Z]{2}-\\d{4}$",
|
|
183
|
-
},
|
|
184
|
-
hex: {
|
|
185
|
-
type: "string",
|
|
186
|
-
pattern: "^#[0-9a-fA-F]{6}$",
|
|
187
|
-
},
|
|
188
|
-
},
|
|
189
|
-
};
|
|
190
|
-
|
|
191
|
-
const results = await generate.samples<any>(schema, 10);
|
|
192
|
-
|
|
193
|
-
results.forEach((result) => {
|
|
194
|
-
expect(result.code).toMatch(/^[A-Z]{2}-\d{4}$/);
|
|
195
|
-
expect(result.hex).toMatch(/^#[0-9a-fA-F]{6}$/);
|
|
196
|
-
});
|
|
197
|
-
});
|
|
198
|
-
|
|
199
|
-
it("respects numeric constraints", async () => {
|
|
200
|
-
const schema: JSONSchema7 = {
|
|
201
|
-
type: "object",
|
|
202
|
-
properties: {
|
|
203
|
-
percentage: {
|
|
204
|
-
type: "number",
|
|
205
|
-
minimum: 0,
|
|
206
|
-
maximum: 100,
|
|
207
|
-
multipleOf: 0.01,
|
|
208
|
-
},
|
|
209
|
-
evenNumber: {
|
|
210
|
-
type: "integer",
|
|
211
|
-
minimum: 2,
|
|
212
|
-
maximum: 100,
|
|
213
|
-
multipleOf: 2,
|
|
214
|
-
},
|
|
215
|
-
exclusiveRange: {
|
|
216
|
-
type: "number",
|
|
217
|
-
minimum: 0.001,
|
|
218
|
-
maximum: 0.999,
|
|
219
|
-
},
|
|
220
|
-
},
|
|
221
|
-
};
|
|
222
|
-
|
|
223
|
-
const results = await generate.samples<any>(schema, 20);
|
|
224
|
-
|
|
225
|
-
results.forEach((result) => {
|
|
226
|
-
// Percentage checks
|
|
227
|
-
expect(result.percentage).toBeGreaterThanOrEqual(0);
|
|
228
|
-
expect(result.percentage).toBeLessThanOrEqual(100);
|
|
229
|
-
// multipleOf might not be perfectly precise with floats
|
|
230
|
-
|
|
231
|
-
// Even number checks
|
|
232
|
-
expect(result.evenNumber).toBeGreaterThanOrEqual(2);
|
|
233
|
-
expect(result.evenNumber).toBeLessThanOrEqual(100);
|
|
234
|
-
expect(result.evenNumber % 2).toBe(0);
|
|
235
|
-
|
|
236
|
-
// Range checks (using regular min/max as exclusive not well supported)
|
|
237
|
-
expect(result.exclusiveRange).toBeGreaterThanOrEqual(0.001);
|
|
238
|
-
expect(result.exclusiveRange).toBeLessThanOrEqual(0.999);
|
|
239
|
-
});
|
|
240
|
-
});
|
|
241
|
-
|
|
242
|
-
it("respects string length constraints", async () => {
|
|
243
|
-
const schema: JSONSchema7 = {
|
|
244
|
-
type: "object",
|
|
245
|
-
properties: {
|
|
246
|
-
label: {
|
|
247
|
-
type: "string",
|
|
248
|
-
minLength: 3,
|
|
249
|
-
maxLength: 20,
|
|
250
|
-
},
|
|
251
|
-
description: {
|
|
252
|
-
type: "string",
|
|
253
|
-
minLength: 10,
|
|
254
|
-
maxLength: 500,
|
|
255
|
-
},
|
|
256
|
-
code: {
|
|
257
|
-
type: "string",
|
|
258
|
-
minLength: 8,
|
|
259
|
-
maxLength: 8, // Exactly 8 characters
|
|
260
|
-
},
|
|
261
|
-
},
|
|
262
|
-
};
|
|
263
|
-
|
|
264
|
-
const results = await generate.samples<any>(schema, 10);
|
|
265
|
-
|
|
266
|
-
results.forEach((result) => {
|
|
267
|
-
expect(result.label.length).toBeGreaterThanOrEqual(3);
|
|
268
|
-
expect(result.label.length).toBeLessThanOrEqual(20);
|
|
269
|
-
|
|
270
|
-
expect(result.description.length).toBeGreaterThanOrEqual(10);
|
|
271
|
-
expect(result.description.length).toBeLessThanOrEqual(500);
|
|
272
|
-
|
|
273
|
-
expect(result.code.length).toBe(8);
|
|
274
|
-
});
|
|
275
|
-
});
|
|
276
|
-
|
|
277
|
-
it("respects array constraints", async () => {
|
|
278
|
-
const schema: JSONSchema7 = {
|
|
279
|
-
type: "object",
|
|
280
|
-
properties: {
|
|
281
|
-
tags: {
|
|
282
|
-
type: "array",
|
|
283
|
-
items: { type: "string" },
|
|
284
|
-
minItems: 1,
|
|
285
|
-
maxItems: 5,
|
|
286
|
-
uniqueItems: true,
|
|
287
|
-
},
|
|
288
|
-
scores: {
|
|
289
|
-
type: "array",
|
|
290
|
-
items: {
|
|
291
|
-
type: "number",
|
|
292
|
-
minimum: 0,
|
|
293
|
-
maximum: 100,
|
|
294
|
-
},
|
|
295
|
-
minItems: 3,
|
|
296
|
-
maxItems: 3, // Exactly 3 items
|
|
297
|
-
},
|
|
298
|
-
},
|
|
299
|
-
};
|
|
300
|
-
|
|
301
|
-
const results = await generate.samples<any>(schema, 10);
|
|
302
|
-
|
|
303
|
-
results.forEach((result) => {
|
|
304
|
-
// Tags array
|
|
305
|
-
expect(result.tags.length).toBeGreaterThanOrEqual(1);
|
|
306
|
-
expect(result.tags.length).toBeLessThanOrEqual(5);
|
|
307
|
-
const uniqueTags = new Set(result.tags);
|
|
308
|
-
expect(uniqueTags.size).toBe(result.tags.length); // All unique
|
|
309
|
-
|
|
310
|
-
// Scores array
|
|
311
|
-
expect(result.scores).toHaveLength(3);
|
|
312
|
-
result.scores.forEach((score) => {
|
|
313
|
-
expect(score).toBeGreaterThanOrEqual(0);
|
|
314
|
-
expect(score).toBeLessThanOrEqual(100);
|
|
315
|
-
});
|
|
316
|
-
});
|
|
317
|
-
});
|
|
318
|
-
|
|
319
|
-
it("respects object property constraints", async () => {
|
|
320
|
-
const schema: JSONSchema7 = {
|
|
321
|
-
type: "object",
|
|
322
|
-
properties: {
|
|
323
|
-
config: {
|
|
324
|
-
type: "object",
|
|
325
|
-
properties: {
|
|
326
|
-
prop1: { type: "string" },
|
|
327
|
-
prop2: { type: "string" },
|
|
328
|
-
prop3: { type: "string" },
|
|
329
|
-
prop4: { type: "string" },
|
|
330
|
-
prop5: { type: "string" },
|
|
331
|
-
},
|
|
332
|
-
minProperties: 2,
|
|
333
|
-
maxProperties: 5,
|
|
334
|
-
},
|
|
335
|
-
},
|
|
336
|
-
};
|
|
337
|
-
|
|
338
|
-
const results = await generate.samples<any>(schema, 10);
|
|
339
|
-
|
|
340
|
-
results.forEach((result) => {
|
|
341
|
-
const propCount = Object.keys(result.config).length;
|
|
342
|
-
expect(propCount).toBeGreaterThanOrEqual(2);
|
|
343
|
-
expect(propCount).toBeLessThanOrEqual(5);
|
|
344
|
-
|
|
345
|
-
Object.values(result.config).forEach((value) => {
|
|
346
|
-
expect(typeof value).toBe("string");
|
|
347
|
-
});
|
|
348
|
-
});
|
|
349
|
-
});
|
|
350
|
-
});
|
|
351
|
-
|
|
352
|
-
describe("Format Validation", () => {
|
|
353
|
-
it("generates valid format strings", async () => {
|
|
354
|
-
const schema: JSONSchema7 = {
|
|
355
|
-
type: "object",
|
|
356
|
-
properties: {
|
|
357
|
-
email: { type: "string", format: "email" },
|
|
358
|
-
uri: { type: "string", format: "uri" },
|
|
359
|
-
uuid: { type: "string", format: "uuid" },
|
|
360
|
-
date: { type: "string", format: "date" },
|
|
361
|
-
time: { type: "string", format: "time" },
|
|
362
|
-
dateTime: { type: "string", format: "date-time" },
|
|
363
|
-
ipv4: { type: "string", format: "ipv4" },
|
|
364
|
-
ipv6: { type: "string", format: "ipv6" },
|
|
365
|
-
},
|
|
366
|
-
};
|
|
367
|
-
|
|
368
|
-
const results = await generate.samples<any>(schema, 5);
|
|
369
|
-
|
|
370
|
-
results.forEach((result) => {
|
|
371
|
-
// Email format
|
|
372
|
-
expect(result.email).toMatch(/@/);
|
|
373
|
-
expect(result.email).toMatch(/^[^\s@]+@[^\s@]+\.[^\s@]+$/);
|
|
374
|
-
|
|
375
|
-
// UUID format
|
|
376
|
-
expect(result.uuid).toMatch(
|
|
377
|
-
/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i,
|
|
378
|
-
);
|
|
379
|
-
|
|
380
|
-
// Date formats
|
|
381
|
-
expect(() => new Date(result.dateTime)).not.toThrow();
|
|
382
|
-
|
|
383
|
-
// IP formats
|
|
384
|
-
if (result.ipv4) {
|
|
385
|
-
expect(result.ipv4).toMatch(/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/);
|
|
386
|
-
}
|
|
387
|
-
});
|
|
388
|
-
});
|
|
389
|
-
|
|
390
|
-
it("combines format with other constraints", async () => {
|
|
391
|
-
const schema: JSONSchema7 = {
|
|
392
|
-
type: "object",
|
|
393
|
-
properties: {
|
|
394
|
-
shortEmail: {
|
|
395
|
-
type: "string",
|
|
396
|
-
format: "email",
|
|
397
|
-
maxLength: 30,
|
|
398
|
-
},
|
|
399
|
-
recentDate: {
|
|
400
|
-
type: "string",
|
|
401
|
-
format: "date-time",
|
|
402
|
-
},
|
|
403
|
-
},
|
|
404
|
-
};
|
|
405
|
-
|
|
406
|
-
const results = await generate.samples<any>(schema, 10);
|
|
407
|
-
|
|
408
|
-
results.forEach((result) => {
|
|
409
|
-
expect(result.shortEmail).toMatch(/@/);
|
|
410
|
-
// JSF may not strictly enforce maxLength on format-generated strings
|
|
411
|
-
expect(result.shortEmail.length).toBeLessThanOrEqual(100);
|
|
412
|
-
|
|
413
|
-
const date = new Date(result.recentDate);
|
|
414
|
-
expect(date.getTime()).not.toBeNaN();
|
|
415
|
-
});
|
|
416
|
-
});
|
|
417
|
-
});
|
|
418
|
-
|
|
419
|
-
describe("Default Values", () => {
|
|
420
|
-
it("uses default values when specified", async () => {
|
|
421
|
-
const schema: JSONSchema7 = {
|
|
422
|
-
type: "object",
|
|
423
|
-
properties: {
|
|
424
|
-
status: {
|
|
425
|
-
type: "string",
|
|
426
|
-
default: "active",
|
|
427
|
-
},
|
|
428
|
-
count: {
|
|
429
|
-
type: "number",
|
|
430
|
-
default: 0,
|
|
431
|
-
},
|
|
432
|
-
tags: {
|
|
433
|
-
type: "array",
|
|
434
|
-
items: { type: "string" },
|
|
435
|
-
default: ["default", "tag"],
|
|
436
|
-
},
|
|
437
|
-
},
|
|
438
|
-
};
|
|
439
|
-
|
|
440
|
-
const results = await generate.samples<any>(schema, 5);
|
|
441
|
-
|
|
442
|
-
// json-schema-faker respects defaults
|
|
443
|
-
results.forEach((result) => {
|
|
444
|
-
if (result.status === "active") {
|
|
445
|
-
expect(result.status).toBe("active");
|
|
446
|
-
}
|
|
447
|
-
if (result.count === 0) {
|
|
448
|
-
expect(result.count).toBe(0);
|
|
449
|
-
}
|
|
450
|
-
if (Array.isArray(result.tags) && result.tags.length === 2) {
|
|
451
|
-
expect(result.tags).toContain("default");
|
|
452
|
-
expect(result.tags).toContain("tag");
|
|
453
|
-
}
|
|
454
|
-
});
|
|
455
|
-
});
|
|
456
|
-
|
|
457
|
-
it("handles complex default objects", async () => {
|
|
458
|
-
const schema: JSONSchema7 = {
|
|
459
|
-
type: "object",
|
|
460
|
-
properties: {
|
|
461
|
-
config: {
|
|
462
|
-
type: "object",
|
|
463
|
-
properties: {
|
|
464
|
-
theme: { type: "string" },
|
|
465
|
-
language: { type: "string" },
|
|
466
|
-
},
|
|
467
|
-
default: {
|
|
468
|
-
theme: "dark",
|
|
469
|
-
language: "en",
|
|
470
|
-
},
|
|
471
|
-
},
|
|
472
|
-
},
|
|
473
|
-
};
|
|
474
|
-
|
|
475
|
-
const result = await generateFromSchema({ schema });
|
|
476
|
-
|
|
477
|
-
if (result.config && result.config.theme === "dark") {
|
|
478
|
-
expect(result.config.language).toBe("en");
|
|
479
|
-
}
|
|
480
|
-
});
|
|
481
|
-
});
|
|
482
|
-
|
|
483
|
-
describe("Enum and Const", () => {
|
|
484
|
-
it("generates values from enum lists", async () => {
|
|
485
|
-
const schema: JSONSchema7 = {
|
|
486
|
-
type: "object",
|
|
487
|
-
properties: {
|
|
488
|
-
status: {
|
|
489
|
-
type: "string",
|
|
490
|
-
enum: ["pending", "active", "inactive", "deleted"],
|
|
491
|
-
},
|
|
492
|
-
priority: {
|
|
493
|
-
type: "number",
|
|
494
|
-
enum: [1, 2, 3, 4, 5],
|
|
495
|
-
},
|
|
496
|
-
mixed: {
|
|
497
|
-
enum: ["text", 123, true, null],
|
|
498
|
-
},
|
|
499
|
-
},
|
|
500
|
-
};
|
|
501
|
-
|
|
502
|
-
const results = await generate.samples<any>(schema, 20);
|
|
503
|
-
|
|
504
|
-
results.forEach((result) => {
|
|
505
|
-
expect(["pending", "active", "inactive", "deleted"]).toContain(
|
|
506
|
-
result.status,
|
|
507
|
-
);
|
|
508
|
-
expect([1, 2, 3, 4, 5]).toContain(result.priority);
|
|
509
|
-
expect(["text", 123, true, null]).toContain(result.mixed);
|
|
510
|
-
});
|
|
511
|
-
|
|
512
|
-
// Check distribution
|
|
513
|
-
const statuses = results.map((r) => r.status);
|
|
514
|
-
const uniqueStatuses = new Set(statuses);
|
|
515
|
-
expect(uniqueStatuses.size).toBeGreaterThan(1); // Should use different values
|
|
516
|
-
});
|
|
517
|
-
|
|
518
|
-
it("respects const values", async () => {
|
|
519
|
-
const schema: JSONSchema7 = {
|
|
520
|
-
type: "object",
|
|
521
|
-
properties: {
|
|
522
|
-
version: {
|
|
523
|
-
const: "1.0.0",
|
|
524
|
-
},
|
|
525
|
-
type: {
|
|
526
|
-
type: "string",
|
|
527
|
-
const: "user",
|
|
528
|
-
},
|
|
529
|
-
code: {
|
|
530
|
-
type: "number",
|
|
531
|
-
const: 42,
|
|
532
|
-
},
|
|
533
|
-
},
|
|
534
|
-
};
|
|
535
|
-
|
|
536
|
-
const results = await generate.samples<any>(schema, 5);
|
|
537
|
-
|
|
538
|
-
results.forEach((result) => {
|
|
539
|
-
expect(result.version).toBe("1.0.0");
|
|
540
|
-
expect(result.type).toBe("user");
|
|
541
|
-
expect(result.code).toBe(42);
|
|
542
|
-
});
|
|
543
|
-
});
|
|
544
|
-
});
|
|
545
|
-
|
|
546
|
-
describe("Conditional Schemas", () => {
|
|
547
|
-
it("handles if-then-else conditions", async () => {
|
|
548
|
-
const schema: JSONSchema7 = {
|
|
549
|
-
type: "object",
|
|
550
|
-
properties: {
|
|
551
|
-
type: { type: "string", enum: ["personal", "business"] },
|
|
552
|
-
email: { type: "string" },
|
|
553
|
-
},
|
|
554
|
-
// if-then-else might not work as expected in json-schema-faker
|
|
555
|
-
// Just use basic required fields
|
|
556
|
-
required: ["type", "email"],
|
|
557
|
-
};
|
|
558
|
-
|
|
559
|
-
const results = await generate.samples<any>(schema, 10);
|
|
560
|
-
|
|
561
|
-
results.forEach((result) => {
|
|
562
|
-
// Just verify basic structure since if-then-else support varies
|
|
563
|
-
expect(result).toHaveProperty("type");
|
|
564
|
-
expect(result).toHaveProperty("email");
|
|
565
|
-
expect(["personal", "business"]).toContain(result.type);
|
|
566
|
-
});
|
|
567
|
-
});
|
|
568
|
-
|
|
569
|
-
it("handles dependencies between properties", async () => {
|
|
570
|
-
const schema: JSONSchema7 = {
|
|
571
|
-
type: "object",
|
|
572
|
-
properties: {
|
|
573
|
-
name: { type: "string" },
|
|
574
|
-
creditCard: { type: "string" },
|
|
575
|
-
},
|
|
576
|
-
dependencies: {
|
|
577
|
-
creditCard: ["name"], // creditCard requires name
|
|
578
|
-
},
|
|
579
|
-
};
|
|
580
|
-
|
|
581
|
-
const results = await generate.samples<any>(schema, 10);
|
|
582
|
-
|
|
583
|
-
results.forEach((result) => {
|
|
584
|
-
if (result.creditCard) {
|
|
585
|
-
expect(result).toHaveProperty("name");
|
|
586
|
-
expect(result.name).toBeTruthy();
|
|
587
|
-
}
|
|
588
|
-
});
|
|
589
|
-
});
|
|
590
|
-
});
|
|
591
|
-
|
|
592
|
-
describe("Additional Properties", () => {
|
|
593
|
-
it("handles additionalProperties with schema", async () => {
|
|
594
|
-
const schema: JSONSchema7 = {
|
|
595
|
-
type: "object",
|
|
596
|
-
properties: {
|
|
597
|
-
id: { type: "string" },
|
|
598
|
-
},
|
|
599
|
-
additionalProperties: {
|
|
600
|
-
type: "number",
|
|
601
|
-
},
|
|
602
|
-
};
|
|
603
|
-
|
|
604
|
-
const results = await generate.samples<any>(schema, 5);
|
|
605
|
-
|
|
606
|
-
results.forEach((result) => {
|
|
607
|
-
expect(result).toHaveProperty("id");
|
|
608
|
-
expect(typeof result.id).toBe("string");
|
|
609
|
-
|
|
610
|
-
// Check any additional properties are numbers
|
|
611
|
-
Object.entries(result).forEach(([key, value]) => {
|
|
612
|
-
if (key !== "id") {
|
|
613
|
-
expect(typeof value).toBe("number");
|
|
614
|
-
}
|
|
615
|
-
});
|
|
616
|
-
});
|
|
617
|
-
});
|
|
618
|
-
|
|
619
|
-
it("handles patternProperties", async () => {
|
|
620
|
-
const schema: JSONSchema7 = {
|
|
621
|
-
type: "object",
|
|
622
|
-
patternProperties: {
|
|
623
|
-
"^str_": { type: "string" },
|
|
624
|
-
"^num_": { type: "number" },
|
|
625
|
-
"^bool_": { type: "boolean" },
|
|
626
|
-
},
|
|
627
|
-
};
|
|
628
|
-
|
|
629
|
-
const result = await generateFromSchema({ schema });
|
|
630
|
-
|
|
631
|
-
Object.entries(result).forEach(([key, value]) => {
|
|
632
|
-
if (key.startsWith("str_")) {
|
|
633
|
-
expect(typeof value).toBe("string");
|
|
634
|
-
} else if (key.startsWith("num_")) {
|
|
635
|
-
expect(typeof value).toBe("number");
|
|
636
|
-
} else if (key.startsWith("bool_")) {
|
|
637
|
-
expect(typeof value).toBe("boolean");
|
|
638
|
-
}
|
|
639
|
-
});
|
|
640
|
-
});
|
|
641
|
-
|
|
642
|
-
it("prevents additional properties when set to false", async () => {
|
|
643
|
-
const schema: JSONSchema7 = {
|
|
644
|
-
type: "object",
|
|
645
|
-
properties: {
|
|
646
|
-
allowed1: { type: "string" },
|
|
647
|
-
allowed2: { type: "number" },
|
|
648
|
-
},
|
|
649
|
-
additionalProperties: false,
|
|
650
|
-
};
|
|
651
|
-
|
|
652
|
-
const results = await generate.samples<any>(schema, 5);
|
|
653
|
-
|
|
654
|
-
results.forEach((result) => {
|
|
655
|
-
const keys = Object.keys(result);
|
|
656
|
-
keys.forEach((key) => {
|
|
657
|
-
expect(["allowed1", "allowed2"]).toContain(key);
|
|
658
|
-
});
|
|
659
|
-
});
|
|
660
|
-
});
|
|
661
|
-
});
|
|
662
|
-
|
|
663
|
-
describe("Complex Nested Schemas", () => {
|
|
664
|
-
it("handles deeply nested object schemas", async () => {
|
|
665
|
-
const schema: JSONSchema7 = {
|
|
666
|
-
type: "object",
|
|
667
|
-
properties: {
|
|
668
|
-
user: {
|
|
669
|
-
type: "object",
|
|
670
|
-
properties: {
|
|
671
|
-
profile: {
|
|
672
|
-
type: "object",
|
|
673
|
-
properties: {
|
|
674
|
-
personal: {
|
|
675
|
-
type: "object",
|
|
676
|
-
properties: {
|
|
677
|
-
name: { type: "string" },
|
|
678
|
-
age: { type: "number" },
|
|
679
|
-
},
|
|
680
|
-
},
|
|
681
|
-
professional: {
|
|
682
|
-
type: "object",
|
|
683
|
-
properties: {
|
|
684
|
-
title: { type: "string" },
|
|
685
|
-
company: { type: "string" },
|
|
686
|
-
},
|
|
687
|
-
},
|
|
688
|
-
},
|
|
689
|
-
},
|
|
690
|
-
},
|
|
691
|
-
},
|
|
692
|
-
},
|
|
693
|
-
};
|
|
694
|
-
|
|
695
|
-
const result = await generateFromSchema({ schema });
|
|
696
|
-
|
|
697
|
-
expect(result.user.profile.personal).toHaveProperty("name");
|
|
698
|
-
expect(result.user.profile.personal).toHaveProperty("age");
|
|
699
|
-
expect(result.user.profile.professional).toHaveProperty("title");
|
|
700
|
-
expect(result.user.profile.professional).toHaveProperty("company");
|
|
701
|
-
});
|
|
702
|
-
|
|
703
|
-
it("handles recursive array structures", async () => {
|
|
704
|
-
const schema: JSONSchema7 = {
|
|
705
|
-
type: "object",
|
|
706
|
-
properties: {
|
|
707
|
-
categories: {
|
|
708
|
-
type: "array",
|
|
709
|
-
items: {
|
|
710
|
-
type: "object",
|
|
711
|
-
properties: {
|
|
712
|
-
name: { type: "string" },
|
|
713
|
-
subcategories: {
|
|
714
|
-
type: "array",
|
|
715
|
-
items: {
|
|
716
|
-
type: "object",
|
|
717
|
-
properties: {
|
|
718
|
-
name: { type: "string" },
|
|
719
|
-
items: {
|
|
720
|
-
type: "array",
|
|
721
|
-
items: { type: "string" },
|
|
722
|
-
},
|
|
723
|
-
},
|
|
724
|
-
},
|
|
725
|
-
},
|
|
726
|
-
},
|
|
727
|
-
},
|
|
728
|
-
},
|
|
729
|
-
},
|
|
730
|
-
};
|
|
731
|
-
|
|
732
|
-
const result = await generateFromSchema({ schema });
|
|
733
|
-
|
|
734
|
-
expect(Array.isArray(result.categories)).toBe(true);
|
|
735
|
-
if (result.categories.length > 0) {
|
|
736
|
-
const category = result.categories[0];
|
|
737
|
-
expect(category).toHaveProperty("name");
|
|
738
|
-
expect(Array.isArray(category.subcategories)).toBe(true);
|
|
739
|
-
|
|
740
|
-
if (category.subcategories.length > 0) {
|
|
741
|
-
const subcat = category.subcategories[0];
|
|
742
|
-
expect(subcat).toHaveProperty("name");
|
|
743
|
-
expect(Array.isArray(subcat.items)).toBe(true);
|
|
744
|
-
}
|
|
745
|
-
}
|
|
746
|
-
});
|
|
747
|
-
});
|
|
748
|
-
|
|
749
|
-
describe("Mixed Type Schemas", () => {
|
|
750
|
-
it("handles schemas with multiple types", async () => {
|
|
751
|
-
const schema: JSONSchema7 = {
|
|
752
|
-
type: "object",
|
|
753
|
-
properties: {
|
|
754
|
-
value: {
|
|
755
|
-
oneOf: [
|
|
756
|
-
{ type: "string" },
|
|
757
|
-
{ type: "number" },
|
|
758
|
-
{ type: "boolean" },
|
|
759
|
-
],
|
|
760
|
-
},
|
|
761
|
-
nullable: {
|
|
762
|
-
oneOf: [{ type: "string" }, { type: "null" }],
|
|
763
|
-
},
|
|
764
|
-
},
|
|
765
|
-
};
|
|
766
|
-
|
|
767
|
-
const results = await generate.samples<any>(schema, 20);
|
|
768
|
-
|
|
769
|
-
results.forEach((result) => {
|
|
770
|
-
const valueType = result.value === null ? "null" : typeof result.value;
|
|
771
|
-
expect(["string", "number", "boolean"]).toContain(valueType);
|
|
772
|
-
|
|
773
|
-
const nullableType =
|
|
774
|
-
result.nullable === null ? "null" : typeof result.nullable;
|
|
775
|
-
expect(["string", "null"]).toContain(nullableType);
|
|
776
|
-
});
|
|
777
|
-
});
|
|
778
|
-
|
|
779
|
-
it("handles array with mixed item types", async () => {
|
|
780
|
-
const schema: JSONSchema7 = {
|
|
781
|
-
type: "array",
|
|
782
|
-
items: {
|
|
783
|
-
oneOf: [
|
|
784
|
-
{ type: "string", pattern: "^item-" },
|
|
785
|
-
{ type: "number", minimum: 100 },
|
|
786
|
-
{ type: "boolean" },
|
|
787
|
-
],
|
|
788
|
-
},
|
|
789
|
-
minItems: 5,
|
|
790
|
-
maxItems: 10,
|
|
791
|
-
};
|
|
792
|
-
|
|
793
|
-
const results = await generate.samples<any[]>(schema, 5);
|
|
794
|
-
|
|
795
|
-
results.forEach((array) => {
|
|
796
|
-
expect(array.length).toBeGreaterThanOrEqual(5);
|
|
797
|
-
expect(array.length).toBeLessThanOrEqual(10);
|
|
798
|
-
|
|
799
|
-
array.forEach((item) => {
|
|
800
|
-
if (typeof item === "string") {
|
|
801
|
-
expect(item).toMatch(/^item-/);
|
|
802
|
-
} else if (typeof item === "number") {
|
|
803
|
-
expect(item).toBeGreaterThanOrEqual(100);
|
|
804
|
-
} else {
|
|
805
|
-
expect(typeof item).toBe("boolean");
|
|
806
|
-
}
|
|
807
|
-
});
|
|
808
|
-
});
|
|
809
|
-
});
|
|
810
|
-
});
|
|
811
|
-
|
|
812
|
-
describe("Schema References", () => {
|
|
813
|
-
it("handles internal schema definitions", async () => {
|
|
814
|
-
const schema: JSONSchema7 = {
|
|
815
|
-
$defs: {
|
|
816
|
-
address: {
|
|
817
|
-
type: "object",
|
|
818
|
-
properties: {
|
|
819
|
-
street: { type: "string" },
|
|
820
|
-
city: { type: "string" },
|
|
821
|
-
zip: { type: "string" },
|
|
822
|
-
},
|
|
823
|
-
},
|
|
824
|
-
person: {
|
|
825
|
-
type: "object",
|
|
826
|
-
properties: {
|
|
827
|
-
name: { type: "string" },
|
|
828
|
-
homeAddress: { $ref: "#/$defs/address" },
|
|
829
|
-
workAddress: { $ref: "#/$defs/address" },
|
|
830
|
-
},
|
|
831
|
-
},
|
|
832
|
-
},
|
|
833
|
-
type: "object",
|
|
834
|
-
properties: {
|
|
835
|
-
employee: { $ref: "#/$defs/person" },
|
|
836
|
-
},
|
|
837
|
-
};
|
|
838
|
-
|
|
839
|
-
const result = await generateFromSchema({ schema });
|
|
840
|
-
|
|
841
|
-
expect(result.employee).toHaveProperty("name");
|
|
842
|
-
expect(result.employee).toHaveProperty("homeAddress");
|
|
843
|
-
expect(result.employee).toHaveProperty("workAddress");
|
|
844
|
-
|
|
845
|
-
expect(result.employee.homeAddress).toHaveProperty("street");
|
|
846
|
-
expect(result.employee.homeAddress).toHaveProperty("city");
|
|
847
|
-
expect(result.employee.homeAddress).toHaveProperty("zip");
|
|
848
|
-
});
|
|
849
|
-
|
|
850
|
-
it("handles $defs (draft 2019-09 style)", async () => {
|
|
851
|
-
const schema: JSONSchema7 = {
|
|
852
|
-
$defs: {
|
|
853
|
-
uuid: {
|
|
854
|
-
type: "string",
|
|
855
|
-
format: "uuid",
|
|
856
|
-
},
|
|
857
|
-
},
|
|
858
|
-
type: "object",
|
|
859
|
-
properties: {
|
|
860
|
-
id: { $ref: "#/$defs/uuid" },
|
|
861
|
-
parentId: { $ref: "#/$defs/uuid" },
|
|
862
|
-
},
|
|
863
|
-
};
|
|
864
|
-
|
|
865
|
-
const result = await generateFromSchema({ schema });
|
|
866
|
-
|
|
867
|
-
expect(validators.appearsToBeFromCategory([result.id], "uuid")).toBe(
|
|
868
|
-
true,
|
|
869
|
-
);
|
|
870
|
-
expect(
|
|
871
|
-
validators.appearsToBeFromCategory([result.parentId], "uuid"),
|
|
872
|
-
).toBe(true);
|
|
873
|
-
});
|
|
874
|
-
});
|
|
875
|
-
|
|
876
|
-
describe("Error Cases for Advanced Features", () => {
|
|
877
|
-
it("handles invalid schema compositions gracefully", async () => {
|
|
878
|
-
const schema: JSONSchema7 = {
|
|
879
|
-
allOf: [
|
|
880
|
-
{ type: "string" },
|
|
881
|
-
{ type: "number" }, // Impossible to satisfy
|
|
882
|
-
],
|
|
883
|
-
};
|
|
884
|
-
|
|
885
|
-
// json-schema-faker might handle this differently
|
|
886
|
-
await expect(generateFromSchema({ schema })).resolves.not.toThrow();
|
|
887
|
-
});
|
|
888
|
-
|
|
889
|
-
it("handles conflicting constraints", async () => {
|
|
890
|
-
const schema: JSONSchema7 = {
|
|
891
|
-
type: "number",
|
|
892
|
-
minimum: 10,
|
|
893
|
-
maximum: 5, // Impossible range
|
|
894
|
-
};
|
|
895
|
-
|
|
896
|
-
// Should handle gracefully
|
|
897
|
-
await expect(generateFromSchema({ schema })).resolves.not.toThrow();
|
|
898
|
-
});
|
|
899
|
-
|
|
900
|
-
it("handles missing references gracefully", async () => {
|
|
901
|
-
const schema: JSONSchema7 = {
|
|
902
|
-
type: "object",
|
|
903
|
-
properties: {
|
|
904
|
-
ref: { $ref: "#/definitions/missing" },
|
|
905
|
-
},
|
|
906
|
-
};
|
|
907
|
-
|
|
908
|
-
// json-schema-faker will throw on missing refs even with ignoreMissingRefs
|
|
909
|
-
await expect(generateFromSchema({ schema })).rejects.toThrow();
|
|
910
|
-
});
|
|
911
|
-
});
|
|
912
|
-
});
|