@schmock/faker 2.3.0 → 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.
- 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 +17 -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,301 +0,0 @@
|
|
|
1
|
-
import { describeFeature, loadFeature } from "@amiceli/vitest-cucumber";
|
|
2
|
-
import { expect } from "vitest";
|
|
3
|
-
import { generateFromSchema } from "../index";
|
|
4
|
-
import { isJSONSchema7 } from "../validation";
|
|
5
|
-
|
|
6
|
-
const feature = await loadFeature("../../features/faker-plugin.feature");
|
|
7
|
-
|
|
8
|
-
describeFeature(feature, ({ Scenario }) => {
|
|
9
|
-
let generated: unknown;
|
|
10
|
-
|
|
11
|
-
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
12
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
function isUnknownArray(value: unknown): value is unknown[] {
|
|
16
|
-
return Array.isArray(value);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
function parseSchema(
|
|
20
|
-
docString: string,
|
|
21
|
-
): Schmock.SchemaGenerationContext["schema"] {
|
|
22
|
-
const parsed: unknown = JSON.parse(docString);
|
|
23
|
-
if (!isJSONSchema7(parsed)) {
|
|
24
|
-
throw new Error("Expected feature DocString to contain a JSON Schema");
|
|
25
|
-
}
|
|
26
|
-
return parsed;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
Scenario(
|
|
30
|
-
"Generate object from simple schema",
|
|
31
|
-
({ Given, When, Then, And }) => {
|
|
32
|
-
let schema: Schmock.SchemaGenerationContext["schema"];
|
|
33
|
-
|
|
34
|
-
Given("I create a schema plugin with:", (_, docString: string) => {
|
|
35
|
-
schema = parseSchema(docString);
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
When("I generate data from the schema", async () => {
|
|
39
|
-
generated = await generateFromSchema({ schema });
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
Then(
|
|
43
|
-
"the generated data should have property {string} of type {string}",
|
|
44
|
-
(_, prop: string, type: string) => {
|
|
45
|
-
if (!isRecord(generated)) {
|
|
46
|
-
throw new Error("Expected generated data to be an object");
|
|
47
|
-
}
|
|
48
|
-
expect(generated).toHaveProperty(prop);
|
|
49
|
-
expect(typeof generated[prop]).toBe(type);
|
|
50
|
-
},
|
|
51
|
-
);
|
|
52
|
-
|
|
53
|
-
And(
|
|
54
|
-
"the generated data should have property {string} of type {string}",
|
|
55
|
-
(_, prop: string, type: string) => {
|
|
56
|
-
if (!isRecord(generated)) {
|
|
57
|
-
throw new Error("Expected generated data to be an object");
|
|
58
|
-
}
|
|
59
|
-
expect(generated).toHaveProperty(prop);
|
|
60
|
-
expect(typeof generated[prop]).toBe(type);
|
|
61
|
-
},
|
|
62
|
-
);
|
|
63
|
-
},
|
|
64
|
-
);
|
|
65
|
-
|
|
66
|
-
Scenario(
|
|
67
|
-
"Generate array of items with explicit count",
|
|
68
|
-
({ Given, When, Then }) => {
|
|
69
|
-
let schema: Schmock.SchemaGenerationContext["schema"];
|
|
70
|
-
let count: number;
|
|
71
|
-
|
|
72
|
-
Given(
|
|
73
|
-
"I create a schema plugin for array with count {int}:",
|
|
74
|
-
(_, cnt: number, docString: string) => {
|
|
75
|
-
schema = parseSchema(docString);
|
|
76
|
-
count = cnt;
|
|
77
|
-
},
|
|
78
|
-
);
|
|
79
|
-
|
|
80
|
-
When("I generate data from the schema", async () => {
|
|
81
|
-
generated = await generateFromSchema({ schema, count });
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
Then(
|
|
85
|
-
"the generated data should be an array of length {int}",
|
|
86
|
-
(_, length: number) => {
|
|
87
|
-
expect(Array.isArray(generated)).toBe(true);
|
|
88
|
-
expect(generated).toHaveLength(length);
|
|
89
|
-
},
|
|
90
|
-
);
|
|
91
|
-
},
|
|
92
|
-
);
|
|
93
|
-
|
|
94
|
-
Scenario(
|
|
95
|
-
"Template preserves string values for mixed templates",
|
|
96
|
-
({ Given, When, Then }) => {
|
|
97
|
-
let template: string;
|
|
98
|
-
let result: unknown;
|
|
99
|
-
|
|
100
|
-
Given(
|
|
101
|
-
"I create a schema plugin with template override {string}",
|
|
102
|
-
(_, tmpl: string) => {
|
|
103
|
-
template = tmpl;
|
|
104
|
-
},
|
|
105
|
-
);
|
|
106
|
-
|
|
107
|
-
When(
|
|
108
|
-
"I generate data with param {string} set to {string}",
|
|
109
|
-
async (_, paramName: string, paramValue: string) => {
|
|
110
|
-
const schema = {
|
|
111
|
-
type: "object" as const,
|
|
112
|
-
properties: {
|
|
113
|
-
value: { type: "string" as const },
|
|
114
|
-
},
|
|
115
|
-
};
|
|
116
|
-
result = await generateFromSchema({
|
|
117
|
-
schema,
|
|
118
|
-
overrides: { value: template },
|
|
119
|
-
params: { [paramName]: paramValue },
|
|
120
|
-
});
|
|
121
|
-
},
|
|
122
|
-
);
|
|
123
|
-
|
|
124
|
-
Then(
|
|
125
|
-
"the template result should be the string {string}",
|
|
126
|
-
(_, expected: string) => {
|
|
127
|
-
if (!isRecord(result)) {
|
|
128
|
-
throw new Error("Expected generated data to be an object");
|
|
129
|
-
}
|
|
130
|
-
expect(result.value).toBe(expected);
|
|
131
|
-
expect(typeof result.value).toBe("string");
|
|
132
|
-
},
|
|
133
|
-
);
|
|
134
|
-
},
|
|
135
|
-
);
|
|
136
|
-
|
|
137
|
-
Scenario(
|
|
138
|
-
"Generate tuple positions from their respective schemas",
|
|
139
|
-
({ Given, When, Then }) => {
|
|
140
|
-
let schema: Schmock.SchemaGenerationContext["schema"];
|
|
141
|
-
|
|
142
|
-
Given(
|
|
143
|
-
"I create a tuple schema with string, integer, and boolean positions",
|
|
144
|
-
() => {
|
|
145
|
-
schema = {
|
|
146
|
-
type: "array",
|
|
147
|
-
items: [
|
|
148
|
-
{ type: "string" },
|
|
149
|
-
{ type: "integer" },
|
|
150
|
-
{ type: "boolean" },
|
|
151
|
-
],
|
|
152
|
-
minItems: 3,
|
|
153
|
-
maxItems: 3,
|
|
154
|
-
};
|
|
155
|
-
},
|
|
156
|
-
);
|
|
157
|
-
|
|
158
|
-
When("I generate data from the schema", async () => {
|
|
159
|
-
generated = await generateFromSchema({ schema, seed: 42 });
|
|
160
|
-
});
|
|
161
|
-
|
|
162
|
-
Then(
|
|
163
|
-
"the generated tuple should contain string, number, and boolean values in order",
|
|
164
|
-
() => {
|
|
165
|
-
if (!isUnknownArray(generated)) {
|
|
166
|
-
throw new Error("Expected generated tuple data to be an array");
|
|
167
|
-
}
|
|
168
|
-
expect(generated).toHaveLength(3);
|
|
169
|
-
expect(typeof generated[0]).toBe("string");
|
|
170
|
-
expect(typeof generated[1]).toBe("number");
|
|
171
|
-
expect(typeof generated[2]).toBe("boolean");
|
|
172
|
-
},
|
|
173
|
-
);
|
|
174
|
-
},
|
|
175
|
-
);
|
|
176
|
-
|
|
177
|
-
Scenario(
|
|
178
|
-
"Generate a nested tuple through a schema reference",
|
|
179
|
-
({ Given, When, Then }) => {
|
|
180
|
-
let schema: Schmock.SchemaGenerationContext["schema"];
|
|
181
|
-
|
|
182
|
-
Given(
|
|
183
|
-
"I create an object schema whose payload references a tuple definition",
|
|
184
|
-
() => {
|
|
185
|
-
schema = {
|
|
186
|
-
type: "object",
|
|
187
|
-
properties: {
|
|
188
|
-
payload: { $ref: "#/definitions/payloadTuple" },
|
|
189
|
-
},
|
|
190
|
-
required: ["payload"],
|
|
191
|
-
definitions: {
|
|
192
|
-
payloadTuple: {
|
|
193
|
-
type: "array",
|
|
194
|
-
items: [
|
|
195
|
-
{ type: "string" },
|
|
196
|
-
{ type: "integer" },
|
|
197
|
-
{ type: "boolean" },
|
|
198
|
-
],
|
|
199
|
-
minItems: 3,
|
|
200
|
-
maxItems: 3,
|
|
201
|
-
additionalItems: false,
|
|
202
|
-
},
|
|
203
|
-
},
|
|
204
|
-
};
|
|
205
|
-
},
|
|
206
|
-
);
|
|
207
|
-
|
|
208
|
-
When("I generate data from the schema", async () => {
|
|
209
|
-
generated = await generateFromSchema({ schema, seed: 42 });
|
|
210
|
-
});
|
|
211
|
-
|
|
212
|
-
Then(
|
|
213
|
-
"the generated payload tuple should contain string, number, and boolean values in order",
|
|
214
|
-
() => {
|
|
215
|
-
if (!isRecord(generated) || !Array.isArray(generated.payload)) {
|
|
216
|
-
throw new Error("Expected generated payload to be a tuple");
|
|
217
|
-
}
|
|
218
|
-
expect(generated.payload).toHaveLength(3);
|
|
219
|
-
expect(typeof generated.payload[0]).toBe("string");
|
|
220
|
-
expect(typeof generated.payload[1]).toBe("number");
|
|
221
|
-
expect(typeof generated.payload[2]).toBe("boolean");
|
|
222
|
-
},
|
|
223
|
-
);
|
|
224
|
-
},
|
|
225
|
-
);
|
|
226
|
-
|
|
227
|
-
Scenario(
|
|
228
|
-
"Apply overrides to every generated array item",
|
|
229
|
-
({ Given, When, Then }) => {
|
|
230
|
-
let schema: Schmock.SchemaGenerationContext["schema"];
|
|
231
|
-
let count = 0;
|
|
232
|
-
let name = "";
|
|
233
|
-
|
|
234
|
-
Given(
|
|
235
|
-
"I create an object array schema with count {int} and override name {string}",
|
|
236
|
-
(_, requestedCount: number, overrideName: string) => {
|
|
237
|
-
count = requestedCount;
|
|
238
|
-
name = overrideName;
|
|
239
|
-
schema = {
|
|
240
|
-
type: "array",
|
|
241
|
-
items: {
|
|
242
|
-
type: "object",
|
|
243
|
-
properties: { name: { type: "string" } },
|
|
244
|
-
required: ["name"],
|
|
245
|
-
},
|
|
246
|
-
};
|
|
247
|
-
},
|
|
248
|
-
);
|
|
249
|
-
|
|
250
|
-
When("I generate data from the schema", async () => {
|
|
251
|
-
generated = await generateFromSchema({
|
|
252
|
-
schema,
|
|
253
|
-
count,
|
|
254
|
-
overrides: { name },
|
|
255
|
-
seed: 42,
|
|
256
|
-
});
|
|
257
|
-
});
|
|
258
|
-
|
|
259
|
-
Then(
|
|
260
|
-
"every generated array item should have name {string}",
|
|
261
|
-
(_, expectedName: string) => {
|
|
262
|
-
if (!isUnknownArray(generated)) {
|
|
263
|
-
throw new Error(
|
|
264
|
-
"Expected generated collection data to be an array",
|
|
265
|
-
);
|
|
266
|
-
}
|
|
267
|
-
for (const item of generated) {
|
|
268
|
-
expect(item).toMatchObject({ name: expectedName });
|
|
269
|
-
}
|
|
270
|
-
},
|
|
271
|
-
);
|
|
272
|
-
},
|
|
273
|
-
);
|
|
274
|
-
|
|
275
|
-
Scenario(
|
|
276
|
-
"Generate useful text for an unconstrained string",
|
|
277
|
-
({ Given, When, Then }) => {
|
|
278
|
-
let schema: Schmock.SchemaGenerationContext["schema"];
|
|
279
|
-
|
|
280
|
-
Given("I create a schema plugin with:", (_, docString: string) => {
|
|
281
|
-
schema = parseSchema(docString);
|
|
282
|
-
});
|
|
283
|
-
|
|
284
|
-
When("I generate data from the schema", async () => {
|
|
285
|
-
generated = await generateFromSchema({ schema, seed: 42 });
|
|
286
|
-
});
|
|
287
|
-
|
|
288
|
-
Then(
|
|
289
|
-
"the generated data should have a non-empty {string}",
|
|
290
|
-
(_, property: string) => {
|
|
291
|
-
if (!isRecord(generated)) {
|
|
292
|
-
throw new Error("Expected generated data to be an object");
|
|
293
|
-
}
|
|
294
|
-
const value = generated[property];
|
|
295
|
-
expect(typeof value).toBe("string");
|
|
296
|
-
expect(value).not.toBe("");
|
|
297
|
-
},
|
|
298
|
-
);
|
|
299
|
-
},
|
|
300
|
-
);
|
|
301
|
-
});
|
package/src/test-utils.ts
DELETED
|
@@ -1,385 +0,0 @@
|
|
|
1
|
-
import type { JSONSchema7 } from "json-schema";
|
|
2
|
-
import { expect } from "vitest";
|
|
3
|
-
import { generateFromSchema } from "./index";
|
|
4
|
-
|
|
5
|
-
interface FakerSchema extends JSONSchema7 {
|
|
6
|
-
faker?: string;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
10
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
// Schema Factory Functions
|
|
14
|
-
export const schemas = {
|
|
15
|
-
simple: {
|
|
16
|
-
string: (): JSONSchema7 => ({
|
|
17
|
-
type: "string",
|
|
18
|
-
}),
|
|
19
|
-
|
|
20
|
-
number: (): JSONSchema7 => ({
|
|
21
|
-
type: "number",
|
|
22
|
-
}),
|
|
23
|
-
|
|
24
|
-
object: (properties: Record<string, JSONSchema7> = {}): JSONSchema7 => ({
|
|
25
|
-
type: "object",
|
|
26
|
-
properties,
|
|
27
|
-
}),
|
|
28
|
-
|
|
29
|
-
array: (
|
|
30
|
-
items: JSONSchema7,
|
|
31
|
-
constraints?: { minItems?: number; maxItems?: number },
|
|
32
|
-
): JSONSchema7 => ({
|
|
33
|
-
type: "array",
|
|
34
|
-
items,
|
|
35
|
-
...constraints,
|
|
36
|
-
}),
|
|
37
|
-
},
|
|
38
|
-
|
|
39
|
-
withFaker: (type: JSONSchema7["type"], fakerMethod: string): FakerSchema => ({
|
|
40
|
-
type,
|
|
41
|
-
faker: fakerMethod,
|
|
42
|
-
}),
|
|
43
|
-
|
|
44
|
-
nested: {
|
|
45
|
-
deep: (
|
|
46
|
-
depth: number,
|
|
47
|
-
leafSchema: JSONSchema7 = schemas.simple.string(),
|
|
48
|
-
): JSONSchema7 => {
|
|
49
|
-
if (depth <= 0) return leafSchema;
|
|
50
|
-
return {
|
|
51
|
-
type: "object",
|
|
52
|
-
properties: {
|
|
53
|
-
nested: schemas.nested.deep(depth - 1, leafSchema),
|
|
54
|
-
},
|
|
55
|
-
};
|
|
56
|
-
},
|
|
57
|
-
|
|
58
|
-
wide: (
|
|
59
|
-
width: number,
|
|
60
|
-
propertySchema: JSONSchema7 = schemas.simple.string(),
|
|
61
|
-
): JSONSchema7 => ({
|
|
62
|
-
type: "object",
|
|
63
|
-
properties: Object.fromEntries(
|
|
64
|
-
Array.from({ length: width }, (_, i) => [`prop${i}`, propertySchema]),
|
|
65
|
-
),
|
|
66
|
-
}),
|
|
67
|
-
},
|
|
68
|
-
|
|
69
|
-
complex: {
|
|
70
|
-
user: (): JSONSchema7 => ({
|
|
71
|
-
type: "object",
|
|
72
|
-
properties: {
|
|
73
|
-
id: { type: "string", format: "uuid" },
|
|
74
|
-
email: { type: "string" },
|
|
75
|
-
firstName: { type: "string" },
|
|
76
|
-
lastName: { type: "string" },
|
|
77
|
-
createdAt: { type: "string" },
|
|
78
|
-
},
|
|
79
|
-
required: ["id", "email"],
|
|
80
|
-
}),
|
|
81
|
-
|
|
82
|
-
apiResponse: (): JSONSchema7 => ({
|
|
83
|
-
type: "object",
|
|
84
|
-
properties: {
|
|
85
|
-
success: { type: "boolean" },
|
|
86
|
-
data: {
|
|
87
|
-
type: "array",
|
|
88
|
-
items: schemas.complex.user(),
|
|
89
|
-
},
|
|
90
|
-
meta: {
|
|
91
|
-
type: "object",
|
|
92
|
-
properties: {
|
|
93
|
-
page: { type: "number" },
|
|
94
|
-
total: { type: "number" },
|
|
95
|
-
},
|
|
96
|
-
},
|
|
97
|
-
},
|
|
98
|
-
}),
|
|
99
|
-
},
|
|
100
|
-
};
|
|
101
|
-
|
|
102
|
-
// Validation Helpers
|
|
103
|
-
export const validators = {
|
|
104
|
-
// Check if a field was mapped to a faker method by comparing with unmapped behavior
|
|
105
|
-
isFieldMapped: async (
|
|
106
|
-
fieldName: string,
|
|
107
|
-
fieldType: JSONSchema7["type"] = "string",
|
|
108
|
-
): Promise<boolean> => {
|
|
109
|
-
const mappedSchema: JSONSchema7 = {
|
|
110
|
-
type: "object",
|
|
111
|
-
properties: {
|
|
112
|
-
[fieldName]: { type: fieldType },
|
|
113
|
-
},
|
|
114
|
-
};
|
|
115
|
-
|
|
116
|
-
const unmappedSchema: JSONSchema7 = {
|
|
117
|
-
type: "object",
|
|
118
|
-
properties: {
|
|
119
|
-
unmappedRandomField12345: { type: fieldType },
|
|
120
|
-
},
|
|
121
|
-
};
|
|
122
|
-
|
|
123
|
-
// Generate multiple samples to check for patterns
|
|
124
|
-
const mappedSamples: unknown[] = [];
|
|
125
|
-
for (let i = 0; i < 10; i++) {
|
|
126
|
-
const result = await generateFromSchema({
|
|
127
|
-
schema: mappedSchema,
|
|
128
|
-
});
|
|
129
|
-
if (!isRecord(result)) {
|
|
130
|
-
throw new Error(
|
|
131
|
-
"Expected mapped schema generation to return an object",
|
|
132
|
-
);
|
|
133
|
-
}
|
|
134
|
-
mappedSamples.push(result[fieldName]);
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
const unmappedSamples: unknown[] = [];
|
|
138
|
-
for (let i = 0; i < 10; i++) {
|
|
139
|
-
const result = await generateFromSchema({
|
|
140
|
-
schema: unmappedSchema,
|
|
141
|
-
});
|
|
142
|
-
if (!isRecord(result)) {
|
|
143
|
-
throw new Error(
|
|
144
|
-
"Expected unmapped schema generation to return an object",
|
|
145
|
-
);
|
|
146
|
-
}
|
|
147
|
-
unmappedSamples.push(result.unmappedRandomField12345);
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
// If field is mapped to a specific faker method, it should have different characteristics
|
|
151
|
-
// than the generic unmapped field
|
|
152
|
-
return (
|
|
153
|
-
analyzeDataCharacteristics(mappedSamples) !==
|
|
154
|
-
analyzeDataCharacteristics(unmappedSamples)
|
|
155
|
-
);
|
|
156
|
-
},
|
|
157
|
-
|
|
158
|
-
// Analyze uniqueness of generated data
|
|
159
|
-
uniquenessRatio: (samples: unknown[]): number => {
|
|
160
|
-
const unique = new Set(samples);
|
|
161
|
-
return unique.size / samples.length;
|
|
162
|
-
},
|
|
163
|
-
|
|
164
|
-
// Check if all samples match a basic pattern without being too specific
|
|
165
|
-
allMatch: <T>(samples: T[], validator: (sample: T) => boolean): boolean => {
|
|
166
|
-
return samples.every(validator);
|
|
167
|
-
},
|
|
168
|
-
|
|
169
|
-
// Check if data appears to be from a specific faker category
|
|
170
|
-
appearsToBeFromCategory: (
|
|
171
|
-
samples: string[],
|
|
172
|
-
category: "email" | "name" | "phone" | "address" | "uuid" | "date",
|
|
173
|
-
): boolean => {
|
|
174
|
-
switch (category) {
|
|
175
|
-
case "email":
|
|
176
|
-
return validators.allMatch(
|
|
177
|
-
samples,
|
|
178
|
-
(s) => typeof s === "string" && s.includes("@") && s.includes("."),
|
|
179
|
-
);
|
|
180
|
-
case "name":
|
|
181
|
-
return validators.allMatch(
|
|
182
|
-
samples,
|
|
183
|
-
(s) =>
|
|
184
|
-
typeof s === "string" &&
|
|
185
|
-
s.length > 1 &&
|
|
186
|
-
s.length < 50 &&
|
|
187
|
-
/^[A-Z]/.test(s),
|
|
188
|
-
);
|
|
189
|
-
case "phone":
|
|
190
|
-
return validators.allMatch(
|
|
191
|
-
samples,
|
|
192
|
-
(s) => typeof s === "string" && /\d/.test(s) && s.length > 10,
|
|
193
|
-
);
|
|
194
|
-
case "address":
|
|
195
|
-
return validators.allMatch(
|
|
196
|
-
samples,
|
|
197
|
-
(s) =>
|
|
198
|
-
typeof s === "string" &&
|
|
199
|
-
s.length > 10 &&
|
|
200
|
-
/\d/.test(s) &&
|
|
201
|
-
/[A-Z]/.test(s),
|
|
202
|
-
);
|
|
203
|
-
case "uuid":
|
|
204
|
-
return validators.allMatch(
|
|
205
|
-
samples,
|
|
206
|
-
(s) =>
|
|
207
|
-
typeof s === "string" &&
|
|
208
|
-
/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(
|
|
209
|
-
s,
|
|
210
|
-
),
|
|
211
|
-
);
|
|
212
|
-
case "date":
|
|
213
|
-
return validators.allMatch(
|
|
214
|
-
samples,
|
|
215
|
-
(s) => typeof s === "string" && !Number.isNaN(Date.parse(s)),
|
|
216
|
-
);
|
|
217
|
-
default:
|
|
218
|
-
return false;
|
|
219
|
-
}
|
|
220
|
-
},
|
|
221
|
-
};
|
|
222
|
-
|
|
223
|
-
// Performance Testing Utilities
|
|
224
|
-
export const performance = {
|
|
225
|
-
measure: async <T>(
|
|
226
|
-
fn: () => T | Promise<T>,
|
|
227
|
-
): Promise<{ result: T; duration: number }> => {
|
|
228
|
-
const start = Date.now();
|
|
229
|
-
const result = await fn();
|
|
230
|
-
const duration = Date.now() - start;
|
|
231
|
-
return { result, duration };
|
|
232
|
-
},
|
|
233
|
-
|
|
234
|
-
measureMemory: (fn: () => void): number => {
|
|
235
|
-
if (globalThis.gc) {
|
|
236
|
-
globalThis.gc();
|
|
237
|
-
}
|
|
238
|
-
const before = process.memoryUsage().heapUsed;
|
|
239
|
-
fn();
|
|
240
|
-
const after = process.memoryUsage().heapUsed;
|
|
241
|
-
return after - before;
|
|
242
|
-
},
|
|
243
|
-
|
|
244
|
-
benchmark: async (
|
|
245
|
-
_name: string,
|
|
246
|
-
fn: () => unknown | Promise<unknown>,
|
|
247
|
-
iterations = 100,
|
|
248
|
-
): Promise<{ mean: number; min: number; max: number }> => {
|
|
249
|
-
const times: number[] = [];
|
|
250
|
-
|
|
251
|
-
for (let i = 0; i < iterations; i++) {
|
|
252
|
-
const start = Date.now();
|
|
253
|
-
await fn();
|
|
254
|
-
const duration = Date.now() - start;
|
|
255
|
-
times.push(duration);
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
return {
|
|
259
|
-
mean: times.reduce((a, b) => a + b, 0) / times.length,
|
|
260
|
-
min: Math.min(...times),
|
|
261
|
-
max: Math.max(...times),
|
|
262
|
-
};
|
|
263
|
-
},
|
|
264
|
-
};
|
|
265
|
-
|
|
266
|
-
// Test Data Generators
|
|
267
|
-
export const generate = {
|
|
268
|
-
samples: async (
|
|
269
|
-
schema: JSONSchema7,
|
|
270
|
-
count = 10,
|
|
271
|
-
options?: Omit<Schmock.SchemaGenerationContext, "schema">,
|
|
272
|
-
): Promise<unknown[]> => {
|
|
273
|
-
const results: unknown[] = [];
|
|
274
|
-
for (let i = 0; i < count; i++) {
|
|
275
|
-
results.push(await generateFromSchema({ schema, ...options }));
|
|
276
|
-
}
|
|
277
|
-
return results;
|
|
278
|
-
},
|
|
279
|
-
|
|
280
|
-
withSeed: async (schema: JSONSchema7, seed?: number): Promise<unknown> => {
|
|
281
|
-
return await generateFromSchema({ schema, seed });
|
|
282
|
-
},
|
|
283
|
-
};
|
|
284
|
-
|
|
285
|
-
// Statistical Analysis
|
|
286
|
-
export const stats = {
|
|
287
|
-
distribution: (samples: unknown[]): Map<string, number> => {
|
|
288
|
-
const dist = new Map<string, number>();
|
|
289
|
-
for (const sample of samples) {
|
|
290
|
-
const key = JSON.stringify(sample) ?? "undefined";
|
|
291
|
-
dist.set(key, (dist.get(key) || 0) + 1);
|
|
292
|
-
}
|
|
293
|
-
return dist;
|
|
294
|
-
},
|
|
295
|
-
|
|
296
|
-
entropy: (samples: unknown[]): number => {
|
|
297
|
-
const dist = stats.distribution(samples);
|
|
298
|
-
const total = samples.length;
|
|
299
|
-
let entropy = 0;
|
|
300
|
-
|
|
301
|
-
for (const count of dist.values()) {
|
|
302
|
-
const p = count / total;
|
|
303
|
-
if (p > 0) {
|
|
304
|
-
entropy -= p * Math.log2(p);
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
return entropy;
|
|
309
|
-
},
|
|
310
|
-
};
|
|
311
|
-
|
|
312
|
-
// Schema Validation Test Helpers
|
|
313
|
-
export const schemaTests = {
|
|
314
|
-
expectValid: async (schema: JSONSchema7): Promise<void> => {
|
|
315
|
-
await expect(generateFromSchema({ schema })).resolves.not.toThrow();
|
|
316
|
-
},
|
|
317
|
-
|
|
318
|
-
expectInvalid: async (
|
|
319
|
-
schema: JSONSchema7,
|
|
320
|
-
errorMessage?: string | RegExp,
|
|
321
|
-
): Promise<void> => {
|
|
322
|
-
if (errorMessage) {
|
|
323
|
-
await expect(generateFromSchema({ schema })).rejects.toThrow(
|
|
324
|
-
errorMessage,
|
|
325
|
-
);
|
|
326
|
-
} else {
|
|
327
|
-
await expect(generateFromSchema({ schema })).rejects.toThrow();
|
|
328
|
-
}
|
|
329
|
-
},
|
|
330
|
-
|
|
331
|
-
expectSchemaError: async (
|
|
332
|
-
schema: JSONSchema7,
|
|
333
|
-
path: string,
|
|
334
|
-
issue?: string,
|
|
335
|
-
): Promise<void> => {
|
|
336
|
-
try {
|
|
337
|
-
await generateFromSchema({ schema });
|
|
338
|
-
throw new Error("Expected schema validation to fail");
|
|
339
|
-
} catch (error: unknown) {
|
|
340
|
-
expect(error).toBeInstanceOf(Error);
|
|
341
|
-
if (!(error instanceof Error)) {
|
|
342
|
-
throw new Error("Expected schema validation to throw an Error");
|
|
343
|
-
}
|
|
344
|
-
expect(error.name).toBe("SchemaValidationError");
|
|
345
|
-
// The schemaPath is in the context
|
|
346
|
-
if (
|
|
347
|
-
"context" in error &&
|
|
348
|
-
isRecord(error.context) &&
|
|
349
|
-
error.context.schemaPath
|
|
350
|
-
) {
|
|
351
|
-
expect(error.context.schemaPath).toBe(path);
|
|
352
|
-
}
|
|
353
|
-
if (issue) {
|
|
354
|
-
expect(error.message).toContain(issue);
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
},
|
|
358
|
-
};
|
|
359
|
-
|
|
360
|
-
// Helper to analyze data characteristics without hardcoding patterns
|
|
361
|
-
function analyzeDataCharacteristics(samples: unknown[]): string {
|
|
362
|
-
if (samples.length === 0) return "empty";
|
|
363
|
-
|
|
364
|
-
const first = samples[0];
|
|
365
|
-
const type = typeof first;
|
|
366
|
-
|
|
367
|
-
if (!samples.every((sample) => typeof sample === "string")) return type;
|
|
368
|
-
|
|
369
|
-
// Analyze string characteristics
|
|
370
|
-
const characteristics: string[] = [type];
|
|
371
|
-
|
|
372
|
-
// Check common patterns without being too specific
|
|
373
|
-
if (samples.every((s) => s.includes("@"))) characteristics.push("has-at");
|
|
374
|
-
if (samples.every((s) => /^\d+$/.test(s))) characteristics.push("numeric");
|
|
375
|
-
if (samples.every((s) => /^[0-9a-f-]+$/i.test(s)))
|
|
376
|
-
characteristics.push("hex-like");
|
|
377
|
-
if (samples.every((s) => s.length > 50)) characteristics.push("long");
|
|
378
|
-
if (samples.every((s) => s.length < 10)) characteristics.push("short");
|
|
379
|
-
if (validators.uniquenessRatio(samples) > 0.8)
|
|
380
|
-
characteristics.push("high-entropy");
|
|
381
|
-
if (validators.uniquenessRatio(samples) < 0.2)
|
|
382
|
-
characteristics.push("low-entropy");
|
|
383
|
-
|
|
384
|
-
return characteristics.join("-");
|
|
385
|
-
}
|
package/src/validation.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["validation.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAY/C,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,WAAW,CAElE;AAED;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,WAAW,EAAE,IAAI,SAAM,GAAG,IAAI,CAiJpE;AAwMD;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAmC7D"}
|