@schmock/faker 2.2.3 → 2.3.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.
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-mappings.d.ts.map +1 -1
  6. package/dist/field-name-matcher.d.ts +19 -0
  7. package/dist/field-name-matcher.d.ts.map +1 -1
  8. package/dist/index.d.ts +3 -0
  9. package/dist/index.d.ts.map +1 -1
  10. package/dist/index.js +4 -1
  11. package/dist/jsf-config.d.ts +27 -2
  12. package/dist/jsf-config.d.ts.map +1 -1
  13. package/dist/output-limits.d.ts +3 -0
  14. package/dist/output-limits.d.ts.map +1 -0
  15. package/dist/overrides.d.ts +2 -1
  16. package/dist/overrides.d.ts.map +1 -1
  17. package/dist/schema-children.d.ts +14 -0
  18. package/dist/schema-children.d.ts.map +1 -0
  19. package/dist/schema-enhancement.d.ts +1 -0
  20. package/dist/schema-enhancement.d.ts.map +1 -1
  21. package/dist/validation.d.ts +24 -5
  22. package/dist/validation.d.ts.map +1 -1
  23. package/package.json +19 -9
  24. package/dist/constants.js +0 -8
  25. package/dist/field-mappings.js +0 -1107
  26. package/dist/field-name-matcher.js +0 -149
  27. package/dist/jsf-config.js +0 -35
  28. package/dist/overrides.js +0 -131
  29. package/dist/schema-enhancement.js +0 -89
  30. package/dist/test-utils.d.ts +0 -57
  31. package/dist/test-utils.d.ts.map +0 -1
  32. package/dist/test-utils.js +0 -271
  33. package/dist/validation.js +0 -300
  34. package/src/advanced-features.test.ts +0 -912
  35. package/src/audit-faker-args.test.ts +0 -97
  36. package/src/constants.d.ts.map +0 -1
  37. package/src/constants.ts +0 -9
  38. package/src/data-quality.test.ts +0 -415
  39. package/src/error-handling.test.ts +0 -504
  40. package/src/field-mappings.test.ts +0 -351
  41. package/src/field-mappings.ts +0 -1141
  42. package/src/field-name-matcher.test.ts +0 -292
  43. package/src/field-name-matcher.ts +0 -188
  44. package/src/index.d.ts.map +0 -1
  45. package/src/index.test.ts +0 -1218
  46. package/src/index.ts +0 -168
  47. package/src/integration.test.ts +0 -634
  48. package/src/jsf-config.d.ts.map +0 -1
  49. package/src/jsf-config.ts +0 -48
  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 -176
  54. package/src/performance.test.ts +0 -494
  55. package/src/plugin-integration.test.ts +0 -575
  56. package/src/post-process.test.ts +0 -151
  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 -216
  60. package/src/schema-enhancement.ts +0 -128
  61. package/src/steps/deterministic-seeds.steps.ts +0 -35
  62. package/src/steps/faker-plugin.steps.ts +0 -109
  63. package/src/test-utils.ts +0 -365
  64. package/src/validation.d.ts.map +0 -1
  65. package/src/validation.ts +0 -417
@@ -1,351 +0,0 @@
1
- import { describe, expect, it } from "vitest";
2
- import { ALL_FIELD_MAPPINGS } from "./field-mappings";
3
- import { findBestMapping } from "./field-name-matcher";
4
- import { generateFromSchema } from "./index";
5
-
6
- /**
7
- * Generate a value for a field by placing it inside an object schema
8
- * and calling generateFromSchema. Returns the generated value.
9
- */
10
- async function generateField(
11
- fieldName: string,
12
- schemaType: string,
13
- ): Promise<unknown> {
14
- const schema = {
15
- type: "object" as const,
16
- properties: {
17
- [fieldName]: { type: schemaType as "string" | "number" | "boolean" },
18
- },
19
- };
20
- const result = await generateFromSchema({ schema });
21
- return result[fieldName];
22
- }
23
-
24
- // ─── Matcher coverage: every mapping's first keyword resolves ───────
25
-
26
- describe("Field mapping matcher coverage", () => {
27
- for (const mapping of ALL_FIELD_MAPPINGS) {
28
- const keyword = mapping.keywords[0];
29
- const type = mapping.schemaType ?? "string";
30
-
31
- it(`"${keyword}" → ${mapping.fakerMethod}`, () => {
32
- const match = findBestMapping(keyword, {
33
- type: type as "string" | "number" | "boolean",
34
- });
35
- expect(match).toBeDefined();
36
- expect(match?.mapping.fakerMethod).toBe(mapping.fakerMethod);
37
- });
38
- }
39
- });
40
-
41
- // ─── Generation: every mapping produces a value of the right type ──
42
-
43
- describe("Field mapping generation", () => {
44
- // Group mappings by category for readable output
45
- const stringMappings = ALL_FIELD_MAPPINGS.filter(
46
- (m) => !m.schemaType || m.schemaType === "string",
47
- );
48
- const numberMappings = ALL_FIELD_MAPPINGS.filter(
49
- (m) => m.schemaType === "number",
50
- );
51
- const booleanMappings = ALL_FIELD_MAPPINGS.filter(
52
- (m) => m.schemaType === "boolean",
53
- );
54
-
55
- describe("string fields", () => {
56
- for (const mapping of stringMappings) {
57
- const keyword = mapping.keywords[0];
58
-
59
- it(`"${keyword}" generates a non-empty string`, async () => {
60
- const value = await generateField(keyword, "string");
61
- expect(typeof value).toBe("string");
62
- expect((value as string).length).toBeGreaterThan(0);
63
- });
64
- }
65
- });
66
-
67
- describe("number fields", () => {
68
- for (const mapping of numberMappings) {
69
- const keyword = mapping.keywords[0];
70
-
71
- it(`"${keyword}" generates a number`, async () => {
72
- const value = await generateField(keyword, "number");
73
- expect(typeof value).toBe("number");
74
- expect(Number.isFinite(value as number)).toBe(true);
75
- });
76
- }
77
- });
78
-
79
- describe("boolean fields", () => {
80
- for (const mapping of booleanMappings) {
81
- const keyword = mapping.keywords[0];
82
-
83
- it(`"${keyword}" generates a boolean`, async () => {
84
- const value = await generateField(keyword, "boolean");
85
- expect(typeof value).toBe("boolean");
86
- });
87
- }
88
- });
89
- });
90
-
91
- // ─── All keyword variants resolve to the same faker method ─────────
92
-
93
- describe("All keyword variants match", () => {
94
- for (const mapping of ALL_FIELD_MAPPINGS) {
95
- if (mapping.keywords.length <= 1) continue;
96
-
97
- describe(`${mapping.fakerMethod}`, () => {
98
- for (const keyword of mapping.keywords) {
99
- it(`"${keyword}" matches`, () => {
100
- const match = findBestMapping(keyword, {
101
- type: (mapping.schemaType ?? "string") as
102
- | "string"
103
- | "number"
104
- | "boolean",
105
- });
106
- expect(match).toBeDefined();
107
- expect(match?.mapping.fakerMethod).toBe(mapping.fakerMethod);
108
- });
109
- }
110
- });
111
- }
112
- });
113
-
114
- // ─── Category-specific quality checks ──────────────────────────────
115
-
116
- describe("Category quality checks", () => {
117
- async function samples(
118
- fieldName: string,
119
- type: string,
120
- count = 10,
121
- ): Promise<unknown[]> {
122
- const results: unknown[] = [];
123
- for (let i = 0; i < count; i++) {
124
- results.push(await generateField(fieldName, type));
125
- }
126
- return results;
127
- }
128
-
129
- describe("Identity & Auth", () => {
130
- it("email contains @ and .", async () => {
131
- for (const v of await samples("email", "string")) {
132
- expect(v).toMatch(/@.*\./);
133
- }
134
- });
135
-
136
- it("username is a non-empty string", async () => {
137
- for (const v of await samples("username", "string")) {
138
- expect(typeof v).toBe("string");
139
- expect((v as string).length).toBeGreaterThan(0);
140
- }
141
- });
142
- });
143
-
144
- describe("Person", () => {
145
- it("firstName starts with uppercase", async () => {
146
- for (const v of await samples("firstName", "string")) {
147
- expect(v).toMatch(/^[A-Z]/);
148
- }
149
- });
150
-
151
- it("lastName starts with uppercase", async () => {
152
- for (const v of await samples("lastName", "string")) {
153
- expect(v).toMatch(/^[A-Z]/);
154
- }
155
- });
156
-
157
- it("name (fullName) contains a space", async () => {
158
- for (const v of await samples("name", "string")) {
159
- expect(v).toContain(" ");
160
- }
161
- });
162
- });
163
-
164
- describe("Contact", () => {
165
- it("phone contains digits", async () => {
166
- for (const v of await samples("phone", "string")) {
167
- expect(v).toMatch(/\d/);
168
- }
169
- });
170
- });
171
-
172
- describe("Address", () => {
173
- it("city is a capitalized string", async () => {
174
- for (const v of await samples("city", "string")) {
175
- expect(v).toMatch(/^[A-Z]/);
176
- }
177
- });
178
-
179
- it("zipcode matches US format", async () => {
180
- for (const v of await samples("zipcode", "string")) {
181
- expect(v).toMatch(/^\d{5}/);
182
- }
183
- });
184
-
185
- it("latitude is a valid number", async () => {
186
- for (const v of await samples("latitude", "number")) {
187
- expect(typeof v).toBe("number");
188
- }
189
- });
190
-
191
- it("longitude is a valid number", async () => {
192
- for (const v of await samples("longitude", "number")) {
193
- expect(typeof v).toBe("number");
194
- }
195
- });
196
- });
197
-
198
- describe("Internet", () => {
199
- it("url starts with http", async () => {
200
- for (const v of await samples("url", "string")) {
201
- expect(v).toMatch(/^https?:\/\//);
202
- }
203
- });
204
-
205
- it("ip_address looks like an IP", async () => {
206
- for (const v of await samples("ip_address", "string")) {
207
- expect(v).toMatch(/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/);
208
- }
209
- });
210
- });
211
-
212
- describe("IDs", () => {
213
- it("uuid matches UUID pattern", async () => {
214
- for (const v of await samples("uuid", "string")) {
215
- expect(v).toMatch(
216
- /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i,
217
- );
218
- }
219
- });
220
-
221
- it("userId (suffix rule) generates UUID", async () => {
222
- for (const v of await samples("userId", "string")) {
223
- expect(v).toMatch(
224
- /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i,
225
- );
226
- }
227
- });
228
-
229
- it("order_id (suffix rule) generates UUID", async () => {
230
- for (const v of await samples("order_id", "string")) {
231
- expect(v).toMatch(
232
- /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i,
233
- );
234
- }
235
- });
236
- });
237
-
238
- describe("Text/Content", () => {
239
- it("description is a paragraph (> 20 chars)", async () => {
240
- for (const v of await samples("description", "string")) {
241
- expect((v as string).length).toBeGreaterThan(20);
242
- }
243
- });
244
-
245
- it("title is sentence-length", async () => {
246
- for (const v of await samples("title", "string")) {
247
- expect((v as string).length).toBeGreaterThan(5);
248
- }
249
- });
250
- });
251
-
252
- describe("Business", () => {
253
- it("company generates a non-empty string", async () => {
254
- for (const v of await samples("company", "string")) {
255
- expect((v as string).length).toBeGreaterThan(0);
256
- }
257
- });
258
- });
259
-
260
- describe("Date/Time", () => {
261
- it("createdAt generates parseable dates", async () => {
262
- for (const v of await samples("createdAt", "string")) {
263
- expect(Date.parse(v as string)).not.toBeNaN();
264
- }
265
- });
266
-
267
- it("updatedAt generates parseable dates", async () => {
268
- for (const v of await samples("updatedAt", "string")) {
269
- expect(Date.parse(v as string)).not.toBeNaN();
270
- }
271
- });
272
-
273
- it("expires_at generates parseable dates", async () => {
274
- for (const v of await samples("expires_at", "string")) {
275
- expect(Date.parse(v as string)).not.toBeNaN();
276
- }
277
- });
278
- });
279
-
280
- describe("Boolean weighting", () => {
281
- it("isActive is mostly true (~90%)", async () => {
282
- const values = (await samples("isActive", "boolean", 200)) as boolean[];
283
- const trueCount = values.filter(Boolean).length;
284
- // With p=0.9 and n=200, expect 160-200 trues
285
- expect(trueCount).toBeGreaterThan(140);
286
- });
287
-
288
- it("isDeleted is mostly false (~5% true)", async () => {
289
- const values = (await samples("isDeleted", "boolean", 200)) as boolean[];
290
- const trueCount = values.filter(Boolean).length;
291
- // With p=0.05 and n=200, expect 0-30 trues
292
- expect(trueCount).toBeLessThan(40);
293
- });
294
-
295
- it("isVerified is mostly true (~80%)", async () => {
296
- const values = (await samples("isVerified", "boolean", 200)) as boolean[];
297
- const trueCount = values.filter(Boolean).length;
298
- expect(trueCount).toBeGreaterThan(120);
299
- });
300
- });
301
-
302
- describe("Nullable post-processing", () => {
303
- it("schmockNullable fields are null ~5% of the time", async () => {
304
- const schema = {
305
- type: "object" as const,
306
- properties: {
307
- value: {
308
- type: "string" as const,
309
- schmockNullable: true,
310
- } as any,
311
- },
312
- };
313
-
314
- let nullCount = 0;
315
- const total = 500;
316
- for (let i = 0; i < total; i++) {
317
- const result = await generateFromSchema({ schema });
318
- if (result.value === null) nullCount++;
319
- }
320
-
321
- // With p=0.05 and n=500, expect 10-45 nulls (generous bounds)
322
- expect(nullCount).toBeGreaterThan(5);
323
- expect(nullCount).toBeLessThan(60);
324
- });
325
- });
326
-
327
- describe("Composition recursion", () => {
328
- it("enhances fields inside allOf branches", async () => {
329
- const schema = {
330
- type: "object" as const,
331
- properties: {
332
- data: {
333
- allOf: [
334
- {
335
- type: "object" as const,
336
- properties: {
337
- email: { type: "string" as const },
338
- city: { type: "string" as const },
339
- },
340
- },
341
- ],
342
- },
343
- },
344
- };
345
-
346
- const result = await generateFromSchema({ schema });
347
- expect(result.data.email).toMatch(/@/);
348
- expect(result.data.city).toMatch(/^[A-Z]/);
349
- });
350
- });
351
- });