@schmock/schema 1.0.1 → 1.1.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.
@@ -0,0 +1,160 @@
1
+ import { describeFeature, loadFeature } from "@amiceli/vitest-cucumber";
2
+ import { expect } from "vitest";
3
+ import { generateFromSchema, schemaPlugin } from "../index";
4
+
5
+ const feature = await loadFeature("../../features/schema-plugin.feature");
6
+
7
+ describeFeature(feature, ({ Scenario }) => {
8
+ let generated: any;
9
+ let error: Error | null = null;
10
+
11
+ Scenario("Generate object from simple schema", ({ Given, When, Then, And }) => {
12
+ let schema: any;
13
+
14
+ Given("I create a schema plugin with:", (_, docString: string) => {
15
+ schema = JSON.parse(docString);
16
+ });
17
+
18
+ When("I generate data from the schema", () => {
19
+ generated = generateFromSchema({ schema });
20
+ });
21
+
22
+ Then("the generated data should have property {string} of type {string}", (_, prop: string, type: string) => {
23
+ expect(generated).toHaveProperty(prop);
24
+ expect(typeof generated[prop]).toBe(type);
25
+ });
26
+
27
+ And("the generated data should have property {string} of type {string}", (_, prop: string, type: string) => {
28
+ expect(generated).toHaveProperty(prop);
29
+ expect(typeof generated[prop]).toBe(type);
30
+ });
31
+ });
32
+
33
+ Scenario("Generate array of items with explicit count", ({ Given, When, Then }) => {
34
+ let schema: any;
35
+ let count: number;
36
+
37
+ Given("I create a schema plugin for array with count {int}:", (_, cnt: number, docString: string) => {
38
+ schema = JSON.parse(docString);
39
+ count = cnt;
40
+ });
41
+
42
+ When("I generate data from the schema", () => {
43
+ generated = generateFromSchema({ schema, count });
44
+ });
45
+
46
+ Then("the generated data should be an array of length {int}", (_, length: number) => {
47
+ expect(Array.isArray(generated)).toBe(true);
48
+ expect(generated).toHaveLength(length);
49
+ });
50
+ });
51
+
52
+ Scenario("Template preserves string values for mixed templates", ({ Given, When, Then }) => {
53
+ let template: string;
54
+ let result: any;
55
+
56
+ Given("I create a schema plugin with template override {string}", (_, tmpl: string) => {
57
+ template = tmpl;
58
+ });
59
+
60
+ When("I generate data with param {string} set to {string}", (_, paramName: string, paramValue: string) => {
61
+ const schema = {
62
+ type: "object" as const,
63
+ properties: {
64
+ value: { type: "string" as const },
65
+ },
66
+ };
67
+ result = generateFromSchema({
68
+ schema,
69
+ overrides: { value: template },
70
+ params: { [paramName]: paramValue },
71
+ });
72
+ });
73
+
74
+ Then("the template result should be the string {string}", (_, expected: string) => {
75
+ expect(result.value).toBe(expected);
76
+ expect(typeof result.value).toBe("string");
77
+ });
78
+ });
79
+
80
+ Scenario("Multiple schema plugin instances do not share state", ({ Given, When, Then }) => {
81
+ let plugin1: any;
82
+ let plugin2: any;
83
+ let data1: any;
84
+ let data2: any;
85
+
86
+ Given("I create two separate schema plugin instances", () => {
87
+ const schema = {
88
+ type: "object" as const,
89
+ properties: {
90
+ name: { type: "string" as const },
91
+ },
92
+ required: ["name"],
93
+ };
94
+ plugin1 = schemaPlugin({ schema });
95
+ plugin2 = schemaPlugin({ schema });
96
+ });
97
+
98
+ When("I generate data from both instances", async () => {
99
+ const ctx = {
100
+ path: "/test",
101
+ route: {},
102
+ method: "GET" as const,
103
+ params: {},
104
+ query: {},
105
+ headers: {},
106
+ state: new Map(),
107
+ };
108
+ const result1 = await plugin1.process(ctx);
109
+ const result2 = await plugin2.process(ctx);
110
+ data1 = result1.response;
111
+ data2 = result2.response;
112
+ });
113
+
114
+ Then("the data from each instance should be independently generated", () => {
115
+ expect(data1).toBeDefined();
116
+ expect(data2).toBeDefined();
117
+ expect(typeof data1.name).toBe("string");
118
+ expect(typeof data2.name).toBe("string");
119
+ });
120
+ });
121
+
122
+ Scenario("Invalid schema is rejected at plugin creation time", ({ Given, Then }) => {
123
+ Given("I attempt to create a schema plugin with invalid schema", () => {
124
+ error = null;
125
+ try {
126
+ schemaPlugin({ schema: {} as any });
127
+ } catch (e) {
128
+ error = e as Error;
129
+ }
130
+ });
131
+
132
+ Then("it should throw a SchemaValidationError", () => {
133
+ expect(error).not.toBeNull();
134
+ expect(error!.name).toBe("SchemaValidationError");
135
+ });
136
+ });
137
+
138
+ Scenario("Faker method validation checks actual method existence", ({ Given, Then }) => {
139
+ Given("I attempt to create a schema with faker method {string}", (_, fakerMethod: string) => {
140
+ error = null;
141
+ try {
142
+ const schema = {
143
+ type: "object" as const,
144
+ properties: {
145
+ field: { type: "string" as const, faker: fakerMethod },
146
+ },
147
+ };
148
+ schemaPlugin({ schema: schema as any });
149
+ } catch (e) {
150
+ error = e as Error;
151
+ }
152
+ });
153
+
154
+ Then("it should throw a SchemaValidationError with message {string}", (_, message: string) => {
155
+ expect(error).not.toBeNull();
156
+ expect(error!.name).toBe("SchemaValidationError");
157
+ expect(error!.message).toContain(message);
158
+ });
159
+ });
160
+ });
package/src/test-utils.ts CHANGED
@@ -2,6 +2,10 @@ import type { JSONSchema7 } from "json-schema";
2
2
  import { expect } from "vitest";
3
3
  import { generateFromSchema } from "./index";
4
4
 
5
+ interface FakerSchema extends JSONSchema7 {
6
+ faker?: string;
7
+ }
8
+
5
9
  // Schema Factory Functions
6
10
  export const schemas = {
7
11
  simple: {
@@ -28,11 +32,10 @@ export const schemas = {
28
32
  }),
29
33
  },
30
34
 
31
- withFaker: (type: JSONSchema7["type"], fakerMethod: string): JSONSchema7 =>
32
- ({
33
- type: type as any,
34
- faker: fakerMethod,
35
- }) as any,
35
+ withFaker: (type: JSONSchema7["type"], fakerMethod: string): FakerSchema => ({
36
+ type,
37
+ faker: fakerMethod,
38
+ }),
36
39
 
37
40
  nested: {
38
41
  deep: (
@@ -99,17 +102,17 @@ export const validators = {
99
102
  fieldName: string,
100
103
  fieldType: JSONSchema7["type"] = "string",
101
104
  ): Promise<boolean> => {
102
- const mappedSchema = {
103
- type: "object" as const,
105
+ const mappedSchema: JSONSchema7 = {
106
+ type: "object",
104
107
  properties: {
105
- [fieldName]: { type: fieldType as any },
108
+ [fieldName]: { type: fieldType },
106
109
  },
107
110
  };
108
111
 
109
- const unmappedSchema = {
110
- type: "object" as const,
112
+ const unmappedSchema: JSONSchema7 = {
113
+ type: "object",
111
114
  properties: {
112
- unmappedRandomField12345: { type: fieldType as any },
115
+ unmappedRandomField12345: { type: fieldType },
113
116
  },
114
117
  };
115
118
 
@@ -340,18 +343,3 @@ function analyzeDataCharacteristics(samples: any[]): string {
340
343
 
341
344
  return characteristics.join("-");
342
345
  }
343
-
344
- // Mock/Spy utilities for testing faker integration
345
- export const mocks = {
346
- trackFakerCalls: () => {
347
- const calls: string[] = [];
348
- // This would need actual implementation with faker.js internals
349
- // For now, it's a placeholder for the concept
350
- return {
351
- calls,
352
- reset: () => {
353
- calls.length = 0;
354
- },
355
- };
356
- },
357
- };