@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.
Files changed (65) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +45 -0
  3. package/dist/constants.d.ts +82 -1
  4. package/dist/constants.d.ts.map +1 -1
  5. package/dist/field-name-matcher.d.ts +19 -0
  6. package/dist/field-name-matcher.d.ts.map +1 -1
  7. package/dist/index.d.ts +3 -0
  8. package/dist/index.d.ts.map +1 -1
  9. package/dist/index.js +4 -1
  10. package/dist/jsf-config.d.ts +19 -4
  11. package/dist/jsf-config.d.ts.map +1 -1
  12. package/dist/output-limits.d.ts +3 -0
  13. package/dist/output-limits.d.ts.map +1 -0
  14. package/dist/overrides.d.ts +2 -1
  15. package/dist/overrides.d.ts.map +1 -1
  16. package/dist/schema-children.d.ts +14 -0
  17. package/dist/schema-children.d.ts.map +1 -0
  18. package/dist/schema-enhancement.d.ts +1 -0
  19. package/dist/schema-enhancement.d.ts.map +1 -1
  20. package/dist/validation.d.ts +24 -5
  21. package/dist/validation.d.ts.map +1 -1
  22. package/package.json +21 -7
  23. package/dist/constants.js +0 -8
  24. package/dist/field-mappings.js +0 -1114
  25. package/dist/field-name-matcher.js +0 -148
  26. package/dist/jsf-config.js +0 -117
  27. package/dist/overrides.js +0 -137
  28. package/dist/schema-enhancement.js +0 -104
  29. package/dist/test-utils.d.ts +0 -57
  30. package/dist/test-utils.d.ts.map +0 -1
  31. package/dist/test-utils.js +0 -284
  32. package/dist/validation.js +0 -300
  33. package/src/advanced-features.test.ts +0 -912
  34. package/src/audit-faker-args.test.ts +0 -97
  35. package/src/constants.d.ts.map +0 -1
  36. package/src/constants.ts +0 -9
  37. package/src/data-quality.test.ts +0 -415
  38. package/src/error-handling.test.ts +0 -504
  39. package/src/field-mappings.test.ts +0 -351
  40. package/src/field-mappings.ts +0 -1148
  41. package/src/field-name-matcher.test.ts +0 -292
  42. package/src/field-name-matcher.ts +0 -187
  43. package/src/index.d.ts.map +0 -1
  44. package/src/index.test.ts +0 -1320
  45. package/src/index.ts +0 -185
  46. package/src/integration.test.ts +0 -634
  47. package/src/jsf-config.d.ts.map +0 -1
  48. package/src/jsf-config.test.ts +0 -65
  49. package/src/jsf-config.ts +0 -156
  50. package/src/overrides.d.ts.map +0 -1
  51. package/src/overrides.property.test.ts +0 -172
  52. package/src/overrides.test.ts +0 -38
  53. package/src/overrides.ts +0 -179
  54. package/src/performance.test.ts +0 -441
  55. package/src/plugin-integration.test.ts +0 -575
  56. package/src/post-process.test.ts +0 -177
  57. package/src/real-world.test.ts +0 -636
  58. package/src/schema-enhancement.d.ts.map +0 -1
  59. package/src/schema-enhancement.test.ts +0 -222
  60. package/src/schema-enhancement.ts +0 -144
  61. package/src/steps/deterministic-seeds.steps.ts +0 -112
  62. package/src/steps/faker-plugin.steps.ts +0 -301
  63. package/src/test-utils.ts +0 -385
  64. package/src/validation.d.ts.map +0 -1
  65. package/src/validation.ts +0 -417
@@ -1,575 +0,0 @@
1
- import { describe, expect, it } from "vitest";
2
- import { version as packageVersion } from "../package.json";
3
- import { fakerPlugin } from "./index";
4
- import { schemas, validators } from "./test-utils";
5
-
6
- describe("Schema Plugin Integration", () => {
7
- describe("Plugin Lifecycle", () => {
8
- it("validates schema during plugin creation", () => {
9
- // Valid schema should create plugin
10
- const validPlugin = fakerPlugin({
11
- schema: schemas.complex.user(),
12
- });
13
- expect(validPlugin).toBeDefined();
14
- expect(validPlugin.name).toBe("faker");
15
- expect(validPlugin.version).toBe(packageVersion);
16
-
17
- // Invalid schema should throw immediately
18
- expect(() => {
19
- fakerPlugin({
20
- schema: { type: "invalid" as any },
21
- });
22
- }).toThrow("Invalid schema type");
23
- });
24
-
25
- it("processes context through plugin pipeline", async () => {
26
- const plugin = fakerPlugin({
27
- schema: schemas.simple.object({
28
- requestId: { type: "string" },
29
- timestamp: { type: "string" },
30
- data: { type: "object" },
31
- }),
32
- });
33
-
34
- const context = {
35
- method: "POST",
36
- path: "/api/data",
37
- params: {},
38
- query: {},
39
- state: { requestId: "req-123" },
40
- headers: { "content-type": "application/json" },
41
- body: { value: 42 },
42
- route: { pattern: "/api/data" },
43
- };
44
-
45
- const result = await plugin.process(context);
46
-
47
- expect(result).toHaveProperty("context");
48
- expect(result).toHaveProperty("response");
49
- expect(result.context).toBe(context); // Context passed through
50
- expect(result.response).toHaveProperty("requestId");
51
- expect(result.response).toHaveProperty("timestamp");
52
- expect(result.response).toHaveProperty("data");
53
- });
54
-
55
- it("preserves existing responses when present", async () => {
56
- const plugin = fakerPlugin({
57
- schema: schemas.simple.object({ id: schemas.simple.number() }),
58
- });
59
-
60
- const context = {
61
- method: "GET",
62
- path: "/test",
63
- params: {},
64
- query: {},
65
- state: {},
66
- headers: {},
67
- body: null,
68
- route: {},
69
- };
70
-
71
- const existingResponse = {
72
- cached: true,
73
- data: [1, 2, 3],
74
- metadata: { source: "cache" },
75
- };
76
-
77
- const result = await plugin.process(context, existingResponse);
78
-
79
- expect(result.response).toBe(existingResponse);
80
- expect(result.response).toEqual(existingResponse);
81
- });
82
- });
83
-
84
- describe("Context Integration", () => {
85
- it("uses params in template overrides", async () => {
86
- const plugin = fakerPlugin({
87
- schema: schemas.simple.object({
88
- userId: { type: "string" },
89
- postId: { type: "string" },
90
- action: { type: "string" },
91
- }),
92
- overrides: {
93
- userId: "{{params.userId}}",
94
- postId: "{{params.postId}}",
95
- action: "view",
96
- },
97
- });
98
-
99
- const result = await plugin.process({
100
- method: "GET",
101
- path: "/users/123/posts/456",
102
- params: { userId: "123", postId: "456" },
103
- query: {},
104
- state: {},
105
- headers: {},
106
- body: null,
107
- route: {},
108
- });
109
-
110
- expect(result.response.userId).toBe("123"); // Template values are strings
111
- expect(result.response.postId).toBe("456");
112
- expect(result.response.action).toBe("view");
113
- });
114
-
115
- it("uses query parameters in templates", async () => {
116
- const plugin = fakerPlugin({
117
- schema: schemas.simple.object({
118
- page: { type: "number" },
119
- limit: { type: "number" },
120
- sort: { type: "string" },
121
- filter: { type: "string" },
122
- }),
123
- overrides: {
124
- page: "{{query.page}}",
125
- limit: "{{query.limit}}",
126
- sort: "{{query.sort}}",
127
- filter: "{{query.filter}}",
128
- },
129
- });
130
-
131
- const result = await plugin.process({
132
- method: "GET",
133
- path: "/api/items",
134
- params: {},
135
- query: {
136
- page: "2",
137
- limit: "50",
138
- sort: "name",
139
- filter: "active",
140
- },
141
- state: {},
142
- headers: {},
143
- body: null,
144
- route: {},
145
- });
146
-
147
- expect(result.response.page).toBe("2"); // Query values are strings
148
- expect(result.response.limit).toBe("50");
149
- expect(result.response.sort).toBe("name");
150
- expect(result.response.filter).toBe("active");
151
- });
152
-
153
- it("uses state in template processing", async () => {
154
- const plugin = fakerPlugin({
155
- schema: schemas.simple.object({
156
- currentUser: { type: "string" },
157
- sessionId: { type: "string" },
158
- permissions: { type: "array", items: { type: "string" } },
159
- }),
160
- overrides: {
161
- currentUser: "{{state.user.name}}",
162
- sessionId: "{{state.session.id}}",
163
- permissions: ["read", "write"],
164
- },
165
- });
166
-
167
- const result = await plugin.process({
168
- method: "GET",
169
- path: "/api/profile",
170
- params: {},
171
- query: {},
172
- state: new Map(),
173
- routeState: {
174
- user: { id: 1, name: "John Doe" },
175
- session: { id: "sess-123", expires: "2024-12-31" },
176
- },
177
- headers: {},
178
- body: null,
179
- route: {},
180
- });
181
-
182
- expect(result.response.currentUser).toBe("John Doe");
183
- expect(result.response.sessionId).toBe("sess-123");
184
- expect(result.response.permissions).toEqual(["read", "write"]);
185
- });
186
-
187
- it("handles missing template values gracefully", async () => {
188
- const plugin = fakerPlugin({
189
- schema: schemas.simple.object({
190
- value1: { type: "string" },
191
- value2: { type: "string" },
192
- value3: { type: "string" },
193
- }),
194
- overrides: {
195
- value1: "{{params.missing}}",
196
- value2: "{{state.nonexistent.nested}}",
197
- value3: "default",
198
- },
199
- });
200
-
201
- const result = await plugin.process({
202
- method: "GET",
203
- path: "/test",
204
- params: { other: "value" },
205
- query: {},
206
- state: new Map(),
207
- routeState: { different: "data" },
208
- headers: {},
209
- body: null,
210
- route: {},
211
- });
212
-
213
- // Missing values return original template
214
- expect(result.response.value1).toBe("{{params.missing}}");
215
- expect(result.response.value2).toBe("{{state.nonexistent.nested}}");
216
- expect(result.response.value3).toBe("default");
217
- });
218
- });
219
-
220
- describe("Array Generation with Plugin", () => {
221
- it("generates arrays with count parameter", async () => {
222
- const plugin = fakerPlugin({
223
- schema: schemas.simple.array(
224
- schemas.simple.object({
225
- id: { type: "number" },
226
- name: { type: "string" },
227
- }),
228
- ),
229
- count: 5,
230
- });
231
-
232
- const result = await plugin.process({
233
- method: "GET",
234
- path: "/api/items",
235
- params: {},
236
- query: {},
237
- state: {},
238
- headers: {},
239
- body: null,
240
- route: {},
241
- });
242
-
243
- expect(Array.isArray(result.response)).toBe(true);
244
- expect(result.response).toHaveLength(5);
245
- result.response.forEach((item) => {
246
- expect(item).toHaveProperty("id");
247
- expect(item).toHaveProperty("name");
248
- });
249
- });
250
-
251
- it("applies overrides to array items", async () => {
252
- const plugin = fakerPlugin({
253
- schema: schemas.simple.array(
254
- schemas.simple.object({
255
- index: { type: "number" },
256
- category: { type: "string" },
257
- }),
258
- ),
259
- count: 3,
260
- overrides: {
261
- category: "{{state.defaultCategory}}",
262
- },
263
- });
264
-
265
- const result = await plugin.process({
266
- method: "GET",
267
- path: "/api/items",
268
- params: {},
269
- query: {},
270
- state: new Map(),
271
- routeState: { defaultCategory: "electronics" },
272
- headers: {},
273
- body: null,
274
- route: {},
275
- });
276
-
277
- expect(result.response).toHaveLength(3);
278
- result.response.forEach((item) => {
279
- expect(item.category).toBe("electronics");
280
- });
281
- });
282
- });
283
-
284
- describe("Error Handling in Plugin", () => {
285
- it("wraps generation errors with context", async () => {
286
- const plugin = fakerPlugin({
287
- schema: {
288
- type: "string",
289
- pattern: "[", // Invalid regex
290
- },
291
- });
292
-
293
- const context = {
294
- method: "GET",
295
- path: "/api/test/123",
296
- params: { id: "123" },
297
- query: {},
298
- state: {},
299
- headers: {},
300
- body: null,
301
- route: {},
302
- };
303
-
304
- try {
305
- await plugin.process(context);
306
- expect.fail("Should have thrown");
307
- } catch (error: any) {
308
- // Should throw some kind of error for invalid pattern
309
- expect(error).toBeDefined();
310
- expect(error.name).toContain("Error");
311
- }
312
- });
313
-
314
- it("handles null or undefined context properties", async () => {
315
- const plugin = fakerPlugin({
316
- schema: schemas.simple.object({
317
- data: { type: "string" },
318
- }),
319
- });
320
-
321
- // Should not crash with null/undefined properties
322
- const result = await plugin.process({
323
- method: "GET",
324
- path: "/test",
325
- params: null as any,
326
- query: undefined as any,
327
- state: null as any,
328
- headers: {},
329
- body: null,
330
- route: {},
331
- });
332
-
333
- expect(result.response).toHaveProperty("data");
334
- expect(typeof result.response.data).toBe("string");
335
- });
336
- });
337
-
338
- describe("Complex Schema Scenarios", () => {
339
- it("handles conditional schemas in plugin", async () => {
340
- const plugin = fakerPlugin({
341
- schema: {
342
- type: "object",
343
- properties: {
344
- type: { type: "string", enum: ["user", "admin"] },
345
- data: { type: "object" },
346
- },
347
- required: ["type", "data"],
348
- },
349
- });
350
-
351
- const result = await plugin.process({
352
- method: "GET",
353
- path: "/api/profile",
354
- params: {},
355
- query: {},
356
- state: {},
357
- headers: {},
358
- body: null,
359
- route: {},
360
- });
361
-
362
- expect(["user", "admin"]).toContain(result.response.type);
363
- expect(result.response).toHaveProperty("data");
364
- });
365
-
366
- it("generates nested data with references", async () => {
367
- const plugin = fakerPlugin({
368
- schema: {
369
- $defs: {
370
- timestamp: { type: "string", format: "date-time" },
371
- user: {
372
- type: "object",
373
- properties: {
374
- id: { type: "string", format: "uuid" },
375
- name: { type: "string" },
376
- },
377
- },
378
- },
379
- type: "object",
380
- properties: {
381
- created: { $ref: "#/$defs/timestamp" },
382
- updated: { $ref: "#/$defs/timestamp" },
383
- author: { $ref: "#/$defs/user" },
384
- editor: { $ref: "#/$defs/user" },
385
- },
386
- },
387
- });
388
-
389
- const result = await plugin.process({
390
- method: "GET",
391
- path: "/api/document",
392
- params: {},
393
- query: {},
394
- state: {},
395
- headers: {},
396
- body: null,
397
- route: {},
398
- });
399
-
400
- expect(
401
- validators.appearsToBeFromCategory([result.response.created], "date"),
402
- ).toBe(true);
403
- expect(
404
- validators.appearsToBeFromCategory([result.response.updated], "date"),
405
- ).toBe(true);
406
- // $ref-resolved schemas may not preserve format through JSF resolution
407
- expect(result.response.author).toHaveProperty("id");
408
- expect(typeof result.response.author.id).toBe("string");
409
- expect(result.response.editor).toHaveProperty("id");
410
- expect(typeof result.response.editor.id).toBe("string");
411
- });
412
- });
413
-
414
- describe("Performance Characteristics", () => {
415
- it("maintains consistent performance across multiple calls", async () => {
416
- const plugin = fakerPlugin({
417
- schema: schemas.complex.user(),
418
- });
419
-
420
- const context = {
421
- method: "GET",
422
- path: "/api/user",
423
- params: {},
424
- query: {},
425
- state: {},
426
- headers: {},
427
- body: null,
428
- route: {},
429
- };
430
-
431
- // Generate multiple times
432
- const results: any[] = [];
433
- for (let i = 0; i < 10; i++) {
434
- const r = await plugin.process(context);
435
- results.push(r.response);
436
- }
437
-
438
- // All should be valid but different
439
- results.forEach((result) => {
440
- expect(result).toHaveProperty("id");
441
- expect(result).toHaveProperty("email");
442
- });
443
-
444
- // Should generate different data each time
445
- const emails = results.map((r) => r.email);
446
- const uniqueEmails = new Set(emails);
447
- expect(uniqueEmails.size).toBeGreaterThan(5);
448
- });
449
-
450
- it("handles large schemas efficiently", async () => {
451
- const plugin = fakerPlugin({
452
- schema: {
453
- type: "object",
454
- properties: Object.fromEntries(
455
- Array.from({ length: 50 }, (_, i) => [
456
- `field${i}`,
457
- { type: i % 2 === 0 ? "string" : "number" },
458
- ]),
459
- ),
460
- },
461
- });
462
-
463
- const context = {
464
- method: "GET",
465
- path: "/api/data",
466
- params: {},
467
- query: {},
468
- state: {},
469
- headers: {},
470
- body: null,
471
- route: {},
472
- };
473
-
474
- const result = await plugin.process(context);
475
-
476
- // Should have all fields
477
- expect(Object.keys(result.response).length).toBeGreaterThanOrEqual(50);
478
- });
479
- });
480
-
481
- describe("Real-world Plugin Usage", () => {
482
- it("generates mock API responses for testing", async () => {
483
- const plugin = fakerPlugin({
484
- schema: {
485
- type: "object",
486
- properties: {
487
- success: { type: "boolean", const: true },
488
- data: {
489
- type: "object",
490
- properties: {
491
- id: { type: "string", format: "uuid" },
492
- email: { type: "string", format: "email" },
493
- profile: {
494
- type: "object",
495
- properties: {
496
- name: { type: "string" },
497
- avatar: { type: "string", format: "uri" },
498
- },
499
- },
500
- },
501
- },
502
- timestamp: { type: "string", format: "date-time" },
503
- },
504
- },
505
- overrides: {
506
- "data.id": "{{params.userId}}",
507
- timestamp: "{{state.currentTime}}",
508
- },
509
- });
510
-
511
- const result = await plugin.process({
512
- method: "GET",
513
- path: "/api/users/user-123",
514
- params: { userId: "user-123" },
515
- query: {},
516
- state: new Map(),
517
- routeState: { currentTime: "2024-01-01T12:00:00Z" },
518
- headers: {},
519
- body: null,
520
- route: {},
521
- });
522
-
523
- expect(result.response.success).toBe(true);
524
- expect(result.response.data.id).toBe("user-123");
525
- expect(result.response.timestamp).toBe("2024-01-01T12:00:00Z");
526
- expect(
527
- validators.appearsToBeFromCategory(
528
- [result.response.data.email],
529
- "email",
530
- ),
531
- ).toBe(true);
532
- });
533
-
534
- it("works with schmock plugin pipeline", async () => {
535
- const schemaPlug = fakerPlugin({
536
- schema: schemas.simple.object({
537
- message: { type: "string" },
538
- code: { type: "number" },
539
- }),
540
- });
541
-
542
- // Mock another plugin in the pipeline
543
- const loggingPlugin = {
544
- name: "logger",
545
- version: "1.0.0",
546
- process: (ctx: any, response: any) => {
547
- console.log(`Processing ${ctx.path}`);
548
- return { context: ctx, response };
549
- },
550
- };
551
-
552
- // Simulate pipeline execution
553
- const context = {
554
- method: "GET",
555
- path: "/api/status",
556
- params: {},
557
- query: {},
558
- state: {},
559
- headers: {},
560
- body: null,
561
- route: {},
562
- };
563
-
564
- // First plugin generates response
565
- const result1 = await schemaPlug.process(context);
566
-
567
- // Second plugin receives generated response
568
- const result2 = loggingPlugin.process(result1.context, result1.response);
569
-
570
- expect(result2.response).toHaveProperty("message");
571
- expect(result2.response).toHaveProperty("code");
572
- expect(result2.response).toBe(result1.response); // Same reference
573
- });
574
- });
575
- });