@schmock/faker 1.13.0 → 2.0.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.
@@ -0,0 +1,216 @@
1
+ import type { JSONSchema7 } from "json-schema";
2
+ import { describe, expect, it } from "vitest";
3
+ import { enhanceSchemaWithSmartMapping } from "./schema-enhancement";
4
+
5
+ describe("enhanceSchemaWithSmartMapping", () => {
6
+ it("does NOT mutate the original schema", () => {
7
+ const original: JSONSchema7 = {
8
+ type: "object",
9
+ properties: {
10
+ email: { type: "string" },
11
+ firstName: { type: "string" },
12
+ createdAt: { type: "string" },
13
+ },
14
+ };
15
+
16
+ // Deep snapshot before enhancement
17
+ const snapshot = JSON.parse(JSON.stringify(original));
18
+
19
+ enhanceSchemaWithSmartMapping(original);
20
+
21
+ // Original should be unchanged
22
+ expect(original).toEqual(snapshot);
23
+ });
24
+
25
+ it("enhancement of object with properties adds faker methods to appropriate fields", () => {
26
+ const schema: JSONSchema7 = {
27
+ type: "object",
28
+ properties: {
29
+ email: { type: "string" },
30
+ firstName: { type: "string" },
31
+ lastName: { type: "string" },
32
+ phone: { type: "string" },
33
+ uuid: { type: "string" },
34
+ },
35
+ };
36
+
37
+ const enhanced = enhanceSchemaWithSmartMapping(schema) as Record<
38
+ string,
39
+ unknown
40
+ >;
41
+ const props = enhanced.properties as Record<
42
+ string,
43
+ JSONSchema7 & { faker?: string }
44
+ >;
45
+
46
+ // Well-known fields should receive faker methods
47
+ expect(props.email.faker).toBeDefined();
48
+ expect(props.firstName.faker).toBeDefined();
49
+ expect(props.lastName.faker).toBeDefined();
50
+ expect(props.phone.faker).toBeDefined();
51
+ expect(props.uuid.faker).toBeDefined();
52
+
53
+ // Verify they are plausible faker method strings (namespace.method format)
54
+ for (const field of ["email", "firstName", "lastName", "phone", "uuid"]) {
55
+ const fakerMethod = props[field].faker as string;
56
+ expect(fakerMethod).toContain(".");
57
+ expect(fakerMethod.split(".").length).toBeGreaterThanOrEqual(2);
58
+ }
59
+ });
60
+
61
+ it("enhancement recurses into allOf branches", () => {
62
+ const schema: JSONSchema7 = {
63
+ allOf: [
64
+ {
65
+ type: "object",
66
+ properties: {
67
+ email: { type: "string" },
68
+ },
69
+ },
70
+ {
71
+ type: "object",
72
+ properties: {
73
+ phone: { type: "string" },
74
+ },
75
+ },
76
+ ],
77
+ };
78
+
79
+ const enhanced = enhanceSchemaWithSmartMapping(schema);
80
+ const allOf = enhanced.allOf as Array<
81
+ JSONSchema7 & { properties: Record<string, { faker?: string }> }
82
+ >;
83
+
84
+ expect(allOf).toHaveLength(2);
85
+ expect(allOf[0].properties.email.faker).toBeDefined();
86
+ expect(allOf[1].properties.phone.faker).toBeDefined();
87
+ });
88
+
89
+ it("enhancement recurses into anyOf branches", () => {
90
+ const schema: JSONSchema7 = {
91
+ anyOf: [
92
+ {
93
+ type: "object",
94
+ properties: {
95
+ email: { type: "string" },
96
+ },
97
+ },
98
+ ],
99
+ };
100
+
101
+ const enhanced = enhanceSchemaWithSmartMapping(schema);
102
+ const anyOf = enhanced.anyOf as Array<
103
+ JSONSchema7 & { properties: Record<string, { faker?: string }> }
104
+ >;
105
+
106
+ expect(anyOf).toHaveLength(1);
107
+ expect(anyOf[0].properties.email.faker).toBeDefined();
108
+ });
109
+
110
+ it("enhancement recurses into oneOf branches", () => {
111
+ const schema: JSONSchema7 = {
112
+ oneOf: [
113
+ {
114
+ type: "object",
115
+ properties: {
116
+ firstName: { type: "string" },
117
+ },
118
+ },
119
+ ],
120
+ };
121
+
122
+ const enhanced = enhanceSchemaWithSmartMapping(schema);
123
+ const oneOf = enhanced.oneOf as Array<
124
+ JSONSchema7 & { properties: Record<string, { faker?: string }> }
125
+ >;
126
+
127
+ expect(oneOf).toHaveLength(1);
128
+ expect(oneOf[0].properties.firstName.faker).toBeDefined();
129
+ });
130
+
131
+ it("enhancement recurses into array items", () => {
132
+ const schema: JSONSchema7 = {
133
+ type: "array",
134
+ items: {
135
+ type: "object",
136
+ properties: {
137
+ email: { type: "string" },
138
+ createdAt: { type: "string" },
139
+ },
140
+ },
141
+ };
142
+
143
+ const enhanced = enhanceSchemaWithSmartMapping(schema);
144
+ const items = enhanced.items as JSONSchema7 & {
145
+ properties: Record<string, { faker?: string }>;
146
+ };
147
+
148
+ expect(items.properties.email.faker).toBeDefined();
149
+ expect(items.properties.createdAt.faker).toBeDefined();
150
+ });
151
+
152
+ it("fields with existing faker property are not overridden", () => {
153
+ const schema: JSONSchema7 = {
154
+ type: "object",
155
+ properties: {
156
+ email: {
157
+ type: "string",
158
+ faker: "lorem.word",
159
+ } as JSONSchema7 & { faker: string },
160
+ },
161
+ };
162
+
163
+ const enhanced = enhanceSchemaWithSmartMapping(schema);
164
+ const props = enhanced.properties as Record<
165
+ string,
166
+ JSONSchema7 & { faker?: string }
167
+ >;
168
+
169
+ // Should keep the explicit faker method, not replace with email generator
170
+ expect(props.email.faker).toBe("lorem.word");
171
+ });
172
+
173
+ it("handles schema without properties gracefully", () => {
174
+ const schema: JSONSchema7 = { type: "string" };
175
+ const enhanced = enhanceSchemaWithSmartMapping(schema);
176
+ expect(enhanced).toEqual({ type: "string" });
177
+ });
178
+
179
+ it("handles null/undefined schema gracefully", () => {
180
+ expect(enhanceSchemaWithSmartMapping(null as unknown as JSONSchema7)).toBe(
181
+ null,
182
+ );
183
+ expect(
184
+ enhanceSchemaWithSmartMapping(undefined as unknown as JSONSchema7),
185
+ ).toBe(undefined);
186
+ });
187
+
188
+ it("enhancement recurses into array items when items is a tuple (array)", () => {
189
+ const schema: JSONSchema7 = {
190
+ type: "array",
191
+ items: [
192
+ {
193
+ type: "object",
194
+ properties: {
195
+ email: { type: "string" },
196
+ },
197
+ },
198
+ {
199
+ type: "object",
200
+ properties: {
201
+ phone: { type: "string" },
202
+ },
203
+ },
204
+ ],
205
+ };
206
+
207
+ const enhanced = enhanceSchemaWithSmartMapping(schema);
208
+ const items = enhanced.items as Array<
209
+ JSONSchema7 & { properties: Record<string, { faker?: string }> }
210
+ >;
211
+
212
+ expect(Array.isArray(items)).toBe(true);
213
+ expect(items[0].properties.email.faker).toBeDefined();
214
+ expect(items[1].properties.phone.faker).toBeDefined();
215
+ });
216
+ });
@@ -32,25 +32,4 @@ describeFeature(feature, ({ Scenario }) => {
32
32
  });
33
33
  });
34
34
 
35
- Scenario(
36
- "Different seeds produce different output",
37
- ({ Given, And, When, Then }) => {
38
- Given("a schema plugin with seed 42", () => {
39
- // setup done during generation
40
- });
41
-
42
- And("a schema plugin with seed 99", () => {
43
- // setup done during generation
44
- });
45
-
46
- When("I generate data from each", async () => {
47
- result1 = await generateFromSchema({ schema: testSchema, seed: 42 });
48
- result2 = await generateFromSchema({ schema: testSchema, seed: 99 });
49
- });
50
-
51
- Then("the outputs are different", () => {
52
- expect(result1).not.toEqual(result2);
53
- });
54
- },
55
- );
56
35
  });
@@ -1,12 +1,11 @@
1
1
  import { describeFeature, loadFeature } from "@amiceli/vitest-cucumber";
2
2
  import { expect } from "vitest";
3
- import { fakerPlugin, generateFromSchema } from "../index";
3
+ import { generateFromSchema } from "../index";
4
4
 
5
5
  const feature = await loadFeature("../../features/faker-plugin.feature");
6
6
 
7
7
  describeFeature(feature, ({ Scenario }) => {
8
8
  let generated: any;
9
- let error: Error | null = null;
10
9
 
11
10
  Scenario(
12
11
  "Generate object from simple schema",
@@ -107,102 +106,4 @@ describeFeature(feature, ({ Scenario }) => {
107
106
  },
108
107
  );
109
108
 
110
- Scenario(
111
- "Multiple schema plugin instances do not share state",
112
- ({ Given, When, Then }) => {
113
- let plugin1: any;
114
- let plugin2: any;
115
- let data1: any;
116
- let data2: any;
117
-
118
- Given("I create two separate schema plugin instances", () => {
119
- const schema = {
120
- type: "object" as const,
121
- properties: {
122
- name: { type: "string" as const },
123
- },
124
- required: ["name"],
125
- };
126
- plugin1 = fakerPlugin({ schema });
127
- plugin2 = fakerPlugin({ schema });
128
- });
129
-
130
- When("I generate data from both instances", async () => {
131
- const ctx = {
132
- path: "/test",
133
- route: {},
134
- method: "GET" as const,
135
- params: {},
136
- query: {},
137
- headers: {},
138
- state: new Map(),
139
- };
140
- const result1 = await plugin1.process(ctx);
141
- const result2 = await plugin2.process(ctx);
142
- data1 = result1.response;
143
- data2 = result2.response;
144
- });
145
-
146
- Then(
147
- "the data from each instance should be independently generated",
148
- () => {
149
- expect(data1).toBeDefined();
150
- expect(data2).toBeDefined();
151
- expect(typeof data1.name).toBe("string");
152
- expect(typeof data2.name).toBe("string");
153
- },
154
- );
155
- },
156
- );
157
-
158
- Scenario(
159
- "Invalid schema is rejected at plugin creation time",
160
- ({ Given, Then }) => {
161
- Given("I attempt to create a schema plugin with invalid schema", () => {
162
- error = null;
163
- try {
164
- fakerPlugin({ schema: {} as any });
165
- } catch (e) {
166
- error = e as Error;
167
- }
168
- });
169
-
170
- Then("it should throw a SchemaValidationError", () => {
171
- expect(error).not.toBeNull();
172
- expect(error?.name).toBe("SchemaValidationError");
173
- });
174
- },
175
- );
176
-
177
- Scenario(
178
- "Faker method validation checks actual method existence",
179
- ({ Given, Then }) => {
180
- Given(
181
- "I attempt to create a schema with faker method {string}",
182
- (_, fakerMethod: string) => {
183
- error = null;
184
- try {
185
- const schema = {
186
- type: "object" as const,
187
- properties: {
188
- field: { type: "string" as const, faker: fakerMethod },
189
- },
190
- };
191
- fakerPlugin({ schema: schema as any });
192
- } catch (e) {
193
- error = e as Error;
194
- }
195
- },
196
- );
197
-
198
- Then(
199
- "it should throw a SchemaValidationError with message {string}",
200
- (_, message: string) => {
201
- expect(error).not.toBeNull();
202
- expect(error?.name).toBe("SchemaValidationError");
203
- expect(error?.message).toContain(message);
204
- },
205
- );
206
- },
207
- );
208
109
  });
package/src/test-utils.ts CHANGED
@@ -119,13 +119,17 @@ export const validators = {
119
119
  // Generate multiple samples to check for patterns
120
120
  const mappedSamples: any[] = [];
121
121
  for (let i = 0; i < 10; i++) {
122
- const result = await generateFromSchema({ schema: mappedSchema });
122
+ const result = (await generateFromSchema({
123
+ schema: mappedSchema,
124
+ })) as Record<string, unknown>;
123
125
  mappedSamples.push(result[fieldName]);
124
126
  }
125
127
 
126
128
  const unmappedSamples: any[] = [];
127
129
  for (let i = 0; i < 10; i++) {
128
- const result = await generateFromSchema({ schema: unmappedSchema });
130
+ const result = (await generateFromSchema({
131
+ schema: unmappedSchema,
132
+ })) as Record<string, unknown>;
129
133
  unmappedSamples.push(result.unmappedRandomField12345);
130
134
  }
131
135
 
@@ -254,7 +258,7 @@ export const generate = {
254
258
  ): Promise<T[]> => {
255
259
  const results: T[] = [];
256
260
  for (let i = 0; i < count; i++) {
257
- results.push(await generateFromSchema({ schema, ...options }));
261
+ results.push((await generateFromSchema({ schema, ...options })) as T);
258
262
  }
259
263
  return results;
260
264
  },
package/src/validation.ts CHANGED
@@ -392,10 +392,10 @@ export function validateFakerMethod(fakerMethod: string): void {
392
392
  validationFaker = createFakerInstance();
393
393
  }
394
394
  const faker = validationFaker;
395
- let current: any = faker;
395
+ let current: unknown = faker;
396
396
  for (const part of parts) {
397
397
  if (current && typeof current === "object" && part in current) {
398
- current = current[part];
398
+ current = (current as Record<string, unknown>)[part];
399
399
  } else {
400
400
  throw new SchemaValidationError(
401
401
  "$.faker",