@schmock/schema 1.0.1 → 1.0.2
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/dist/index.d.ts.map +1 -1
- package/dist/index.js +136 -140
- package/dist/steps/schema-plugin.steps.d.ts +2 -0
- package/dist/steps/schema-plugin.steps.d.ts.map +1 -0
- package/dist/steps/schema-plugin.steps.js +139 -0
- package/package.json +11 -5
- package/src/data-quality.test.ts +1 -1
- package/src/error-handling.test.ts +2 -3
- package/src/index.test.ts +4 -5
- package/src/index.ts +35 -61
- package/src/plugin-integration.test.ts +1 -1
- package/src/steps/schema-plugin.steps.ts +160 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema-plugin.steps.d.ts","sourceRoot":"","sources":["../../src/steps/schema-plugin.steps.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { describeFeature, loadFeature } from "@amiceli/vitest-cucumber";
|
|
2
|
+
import { expect } from "vitest";
|
|
3
|
+
import { generateFromSchema, schemaPlugin } from "../index";
|
|
4
|
+
const feature = await loadFeature("../../features/schema-plugin.feature");
|
|
5
|
+
describeFeature(feature, ({ Scenario }) => {
|
|
6
|
+
let generated;
|
|
7
|
+
let error = null;
|
|
8
|
+
Scenario("Generate object from simple schema", ({ Given, When, Then, And }) => {
|
|
9
|
+
let schema;
|
|
10
|
+
Given("I create a schema plugin with:", (_, docString) => {
|
|
11
|
+
schema = JSON.parse(docString);
|
|
12
|
+
});
|
|
13
|
+
When("I generate data from the schema", () => {
|
|
14
|
+
generated = generateFromSchema({ schema });
|
|
15
|
+
});
|
|
16
|
+
Then("the generated data should have property {string} of type {string}", (_, prop, type) => {
|
|
17
|
+
expect(generated).toHaveProperty(prop);
|
|
18
|
+
expect(typeof generated[prop]).toBe(type);
|
|
19
|
+
});
|
|
20
|
+
And("the generated data should have property {string} of type {string}", (_, prop, type) => {
|
|
21
|
+
expect(generated).toHaveProperty(prop);
|
|
22
|
+
expect(typeof generated[prop]).toBe(type);
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
Scenario("Generate array of items with explicit count", ({ Given, When, Then }) => {
|
|
26
|
+
let schema;
|
|
27
|
+
let count;
|
|
28
|
+
Given("I create a schema plugin for array with count {int}:", (_, cnt, docString) => {
|
|
29
|
+
schema = JSON.parse(docString);
|
|
30
|
+
count = cnt;
|
|
31
|
+
});
|
|
32
|
+
When("I generate data from the schema", () => {
|
|
33
|
+
generated = generateFromSchema({ schema, count });
|
|
34
|
+
});
|
|
35
|
+
Then("the generated data should be an array of length {int}", (_, length) => {
|
|
36
|
+
expect(Array.isArray(generated)).toBe(true);
|
|
37
|
+
expect(generated).toHaveLength(length);
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
Scenario("Template preserves string values for mixed templates", ({ Given, When, Then }) => {
|
|
41
|
+
let template;
|
|
42
|
+
let result;
|
|
43
|
+
Given("I create a schema plugin with template override {string}", (_, tmpl) => {
|
|
44
|
+
template = tmpl;
|
|
45
|
+
});
|
|
46
|
+
When("I generate data with param {string} set to {string}", (_, paramName, paramValue) => {
|
|
47
|
+
const schema = {
|
|
48
|
+
type: "object",
|
|
49
|
+
properties: {
|
|
50
|
+
value: { type: "string" },
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
result = generateFromSchema({
|
|
54
|
+
schema,
|
|
55
|
+
overrides: { value: template },
|
|
56
|
+
params: { [paramName]: paramValue },
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
Then("the template result should be the string {string}", (_, expected) => {
|
|
60
|
+
expect(result.value).toBe(expected);
|
|
61
|
+
expect(typeof result.value).toBe("string");
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
Scenario("Multiple schema plugin instances do not share state", ({ Given, When, Then }) => {
|
|
65
|
+
let plugin1;
|
|
66
|
+
let plugin2;
|
|
67
|
+
let data1;
|
|
68
|
+
let data2;
|
|
69
|
+
Given("I create two separate schema plugin instances", () => {
|
|
70
|
+
const schema = {
|
|
71
|
+
type: "object",
|
|
72
|
+
properties: {
|
|
73
|
+
name: { type: "string" },
|
|
74
|
+
},
|
|
75
|
+
required: ["name"],
|
|
76
|
+
};
|
|
77
|
+
plugin1 = schemaPlugin({ schema });
|
|
78
|
+
plugin2 = schemaPlugin({ schema });
|
|
79
|
+
});
|
|
80
|
+
When("I generate data from both instances", async () => {
|
|
81
|
+
const ctx = {
|
|
82
|
+
path: "/test",
|
|
83
|
+
route: {},
|
|
84
|
+
method: "GET",
|
|
85
|
+
params: {},
|
|
86
|
+
query: {},
|
|
87
|
+
headers: {},
|
|
88
|
+
state: new Map(),
|
|
89
|
+
};
|
|
90
|
+
const result1 = await plugin1.process(ctx);
|
|
91
|
+
const result2 = await plugin2.process(ctx);
|
|
92
|
+
data1 = result1.response;
|
|
93
|
+
data2 = result2.response;
|
|
94
|
+
});
|
|
95
|
+
Then("the data from each instance should be independently generated", () => {
|
|
96
|
+
expect(data1).toBeDefined();
|
|
97
|
+
expect(data2).toBeDefined();
|
|
98
|
+
expect(typeof data1.name).toBe("string");
|
|
99
|
+
expect(typeof data2.name).toBe("string");
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
Scenario("Invalid schema is rejected at plugin creation time", ({ Given, Then }) => {
|
|
103
|
+
Given("I attempt to create a schema plugin with invalid schema", () => {
|
|
104
|
+
error = null;
|
|
105
|
+
try {
|
|
106
|
+
schemaPlugin({ schema: {} });
|
|
107
|
+
}
|
|
108
|
+
catch (e) {
|
|
109
|
+
error = e;
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
Then("it should throw a SchemaValidationError", () => {
|
|
113
|
+
expect(error).not.toBeNull();
|
|
114
|
+
expect(error.constructor.name).toBe("SchemaValidationError");
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
Scenario("Faker method validation checks actual method existence", ({ Given, Then }) => {
|
|
118
|
+
Given("I attempt to create a schema with faker method {string}", (_, fakerMethod) => {
|
|
119
|
+
error = null;
|
|
120
|
+
try {
|
|
121
|
+
const schema = {
|
|
122
|
+
type: "object",
|
|
123
|
+
properties: {
|
|
124
|
+
field: { type: "string", faker: fakerMethod },
|
|
125
|
+
},
|
|
126
|
+
};
|
|
127
|
+
schemaPlugin({ schema: schema });
|
|
128
|
+
}
|
|
129
|
+
catch (e) {
|
|
130
|
+
error = e;
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
Then("it should throw a SchemaValidationError with message {string}", (_, message) => {
|
|
134
|
+
expect(error).not.toBeNull();
|
|
135
|
+
expect(error.constructor.name).toBe("SchemaValidationError");
|
|
136
|
+
expect(error.message).toContain(message);
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
});
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@schmock/schema",
|
|
3
3
|
"description": "JSON Schema-based automatic data generation for Schmock",
|
|
4
|
-
"version": "1.0.
|
|
4
|
+
"version": "1.0.2",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
7
7
|
"types": "./dist/index.d.ts",
|
|
@@ -23,17 +23,23 @@
|
|
|
23
23
|
"build:types": "tsc -p tsconfig.json",
|
|
24
24
|
"test": "vitest",
|
|
25
25
|
"test:watch": "vitest --watch",
|
|
26
|
+
"test:bdd": "vitest run --config vitest.config.bdd.ts",
|
|
26
27
|
"lint": "biome check src/*.ts",
|
|
27
|
-
"lint:fix": "biome check --write --unsafe src/*.ts"
|
|
28
|
+
"lint:fix": "biome check --write --unsafe src/*.ts",
|
|
29
|
+
"check:publish": "publint && attw --pack --ignore-rules cjs-resolves-to-esm"
|
|
28
30
|
},
|
|
29
31
|
"license": "MIT",
|
|
30
32
|
"dependencies": {
|
|
31
33
|
"json-schema-faker": "^0.5.6",
|
|
32
|
-
"@faker-js/faker": "^10.
|
|
34
|
+
"@faker-js/faker": "^10.2.0"
|
|
35
|
+
},
|
|
36
|
+
"peerDependencies": {
|
|
37
|
+
"@schmock/core": "^1.0.0"
|
|
33
38
|
},
|
|
34
39
|
"devDependencies": {
|
|
40
|
+
"@amiceli/vitest-cucumber": "^6.2.0",
|
|
35
41
|
"@types/json-schema": "^7.0.15",
|
|
36
|
-
"@types/node": "^
|
|
42
|
+
"@types/node": "^25.1.0",
|
|
37
43
|
"vitest": "^4.0.15"
|
|
38
44
|
}
|
|
39
|
-
}
|
|
45
|
+
}
|
package/src/data-quality.test.ts
CHANGED
|
@@ -299,7 +299,7 @@ describe("Data Quality and Statistical Properties", () => {
|
|
|
299
299
|
// Street addresses should have numbers and street names
|
|
300
300
|
expect(sample.street).toMatch(/\d/);
|
|
301
301
|
expect(sample.street).toMatch(/[A-Z]/);
|
|
302
|
-
expect(sample.street.length).toBeGreaterThanOrEqual(
|
|
302
|
+
expect(sample.street.length).toBeGreaterThanOrEqual(5); // Allow short addresses like "9 Ave."
|
|
303
303
|
|
|
304
304
|
// Cities should be properly formatted
|
|
305
305
|
expect(sample.city).toMatch(/^[A-Z]/);
|
|
@@ -84,9 +84,8 @@ describe("Schema Error Handling", () => {
|
|
|
84
84
|
});
|
|
85
85
|
expect.fail("Should have thrown");
|
|
86
86
|
} catch (error: any) {
|
|
87
|
-
expect(error.message).toContain("
|
|
88
|
-
expect(error.message).toContain("badnamespace");
|
|
89
|
-
expect(error.message).toContain("Valid namespaces include");
|
|
87
|
+
expect(error.message).toContain("Invalid faker method");
|
|
88
|
+
expect(error.message).toContain("badnamespace.method");
|
|
90
89
|
}
|
|
91
90
|
});
|
|
92
91
|
|
package/src/index.test.ts
CHANGED
|
@@ -404,7 +404,7 @@ describe("Schema Generator", () => {
|
|
|
404
404
|
field: schemas.withFaker("string", "invalidnamespace.method"),
|
|
405
405
|
});
|
|
406
406
|
|
|
407
|
-
schemaTests.expectInvalid(schema, /
|
|
407
|
+
schemaTests.expectInvalid(schema, /Invalid faker method/);
|
|
408
408
|
});
|
|
409
409
|
|
|
410
410
|
it("handles all common field mapping categories", () => {
|
|
@@ -765,7 +765,7 @@ describe("Schema Generator", () => {
|
|
|
765
765
|
});
|
|
766
766
|
|
|
767
767
|
expect(plugin).toHaveProperty("name", "schema");
|
|
768
|
-
expect(plugin).toHaveProperty("version", "1.0.
|
|
768
|
+
expect(plugin).toHaveProperty("version", "1.0.1");
|
|
769
769
|
expect(plugin).toHaveProperty("process");
|
|
770
770
|
expect(typeof plugin.process).toBe("function");
|
|
771
771
|
});
|
|
@@ -905,9 +905,8 @@ describe("Schema Generator", () => {
|
|
|
905
905
|
});
|
|
906
906
|
expect.fail("Should have thrown");
|
|
907
907
|
} catch (error: any) {
|
|
908
|
-
expect(error.message).toContain("
|
|
909
|
-
expect(error.
|
|
910
|
-
expect(error.context?.schemaPath).toContain("field");
|
|
908
|
+
expect(error.message).toContain("Invalid faker method");
|
|
909
|
+
expect(error.message).toContain("invalid.namespace.method");
|
|
911
910
|
}
|
|
912
911
|
});
|
|
913
912
|
|
package/src/index.ts
CHANGED
|
@@ -17,18 +17,23 @@ function createFakerInstance() {
|
|
|
17
17
|
return new Faker({ locale: [en] });
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
jsf.
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
20
|
+
let jsfConfigured = false;
|
|
21
|
+
|
|
22
|
+
function getJsf() {
|
|
23
|
+
if (!jsfConfigured) {
|
|
24
|
+
jsf.extend("faker", () => createFakerInstance());
|
|
25
|
+
jsf.option({
|
|
26
|
+
requiredOnly: false,
|
|
27
|
+
alwaysFakeOptionals: true,
|
|
28
|
+
useDefaultValue: true,
|
|
29
|
+
ignoreMissingRefs: true,
|
|
30
|
+
failOnInvalidTypes: false,
|
|
31
|
+
failOnInvalidFormat: false,
|
|
32
|
+
});
|
|
33
|
+
jsfConfigured = true;
|
|
34
|
+
}
|
|
35
|
+
return jsf;
|
|
36
|
+
}
|
|
32
37
|
|
|
33
38
|
// Resource limits for safety
|
|
34
39
|
const MAX_ARRAY_SIZE = 10000;
|
|
@@ -58,7 +63,7 @@ export function schemaPlugin(options: SchemaPluginOptions): Plugin {
|
|
|
58
63
|
|
|
59
64
|
return {
|
|
60
65
|
name: "schema",
|
|
61
|
-
version: "1.0.
|
|
66
|
+
version: "1.0.1",
|
|
62
67
|
|
|
63
68
|
process(context: PluginContext, response?: any) {
|
|
64
69
|
// If response already exists, pass it through
|
|
@@ -130,7 +135,7 @@ export function generateFromSchema(options: SchemaGenerationContext): any {
|
|
|
130
135
|
|
|
131
136
|
generated = [];
|
|
132
137
|
for (let i = 0; i < itemCount; i++) {
|
|
133
|
-
let item =
|
|
138
|
+
let item = getJsf().generate(
|
|
134
139
|
enhanceSchemaWithSmartMapping(itemSchema as JSONSchema7),
|
|
135
140
|
);
|
|
136
141
|
item = applyOverrides(item, overrides, params, state, query);
|
|
@@ -139,7 +144,7 @@ export function generateFromSchema(options: SchemaGenerationContext): any {
|
|
|
139
144
|
} else {
|
|
140
145
|
// Handle object schemas
|
|
141
146
|
const enhancedSchema = enhanceSchemaWithSmartMapping(schema);
|
|
142
|
-
generated =
|
|
147
|
+
generated = getJsf().generate(enhancedSchema);
|
|
143
148
|
generated = applyOverrides(generated, overrides, params, state, query);
|
|
144
149
|
}
|
|
145
150
|
|
|
@@ -686,16 +691,6 @@ function processTemplate(
|
|
|
686
691
|
},
|
|
687
692
|
);
|
|
688
693
|
|
|
689
|
-
// Try to convert to number if it's a numeric string
|
|
690
|
-
if (typeof processed === "string") {
|
|
691
|
-
if (/^\d+$/.test(processed)) {
|
|
692
|
-
return Number.parseInt(processed, 10);
|
|
693
|
-
}
|
|
694
|
-
if (/^\d+\.\d+$/.test(processed)) {
|
|
695
|
-
return Number.parseFloat(processed);
|
|
696
|
-
}
|
|
697
|
-
}
|
|
698
|
-
|
|
699
694
|
return processed;
|
|
700
695
|
}
|
|
701
696
|
|
|
@@ -706,31 +701,6 @@ function processTemplate(
|
|
|
706
701
|
* @throws {SchemaValidationError} When faker method format or namespace is invalid
|
|
707
702
|
*/
|
|
708
703
|
function validateFakerMethod(fakerMethod: string): void {
|
|
709
|
-
// List of known faker namespaces and common methods
|
|
710
|
-
const validFakerNamespaces = [
|
|
711
|
-
"person",
|
|
712
|
-
"internet",
|
|
713
|
-
"phone",
|
|
714
|
-
"location",
|
|
715
|
-
"string",
|
|
716
|
-
"date",
|
|
717
|
-
"company",
|
|
718
|
-
"commerce",
|
|
719
|
-
"color",
|
|
720
|
-
"database",
|
|
721
|
-
"finance",
|
|
722
|
-
"git",
|
|
723
|
-
"hacker",
|
|
724
|
-
"helpers",
|
|
725
|
-
"image",
|
|
726
|
-
"lorem",
|
|
727
|
-
"music",
|
|
728
|
-
"number",
|
|
729
|
-
"science",
|
|
730
|
-
"vehicle",
|
|
731
|
-
"word",
|
|
732
|
-
];
|
|
733
|
-
|
|
734
704
|
// Check if faker method follows valid format (namespace.method)
|
|
735
705
|
const parts = fakerMethod.split(".");
|
|
736
706
|
if (parts.length < 2) {
|
|
@@ -741,20 +711,24 @@ function validateFakerMethod(fakerMethod: string): void {
|
|
|
741
711
|
);
|
|
742
712
|
}
|
|
743
713
|
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
714
|
+
// Validate by resolving the method path on a real faker instance
|
|
715
|
+
const faker = createFakerInstance();
|
|
716
|
+
let current: any = faker;
|
|
717
|
+
for (const part of parts) {
|
|
718
|
+
if (current && typeof current === "object" && part in current) {
|
|
719
|
+
current = current[part];
|
|
720
|
+
} else {
|
|
721
|
+
throw new SchemaValidationError(
|
|
722
|
+
"$.faker",
|
|
723
|
+
`Invalid faker method: "${fakerMethod}"`,
|
|
724
|
+
"Check faker.js documentation for valid methods",
|
|
725
|
+
);
|
|
726
|
+
}
|
|
751
727
|
}
|
|
752
|
-
|
|
753
|
-
// Check for obviously invalid method names
|
|
754
|
-
if (fakerMethod.includes("nonexistent") || fakerMethod.includes("invalid")) {
|
|
728
|
+
if (typeof current !== "function") {
|
|
755
729
|
throw new SchemaValidationError(
|
|
756
730
|
"$.faker",
|
|
757
|
-
`Invalid faker method: "${fakerMethod}"`,
|
|
731
|
+
`Invalid faker method: "${fakerMethod}" is not a function`,
|
|
758
732
|
"Check faker.js documentation for valid methods",
|
|
759
733
|
);
|
|
760
734
|
}
|
|
@@ -11,7 +11,7 @@ describe("Schema Plugin Integration", () => {
|
|
|
11
11
|
});
|
|
12
12
|
expect(validPlugin).toBeDefined();
|
|
13
13
|
expect(validPlugin.name).toBe("schema");
|
|
14
|
-
expect(validPlugin.version).toBe("1.0.
|
|
14
|
+
expect(validPlugin.version).toBe("1.0.1");
|
|
15
15
|
|
|
16
16
|
// Invalid schema should throw immediately
|
|
17
17
|
expect(() => {
|
|
@@ -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!.constructor.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!.constructor.name).toBe("SchemaValidationError");
|
|
157
|
+
expect(error!.message).toContain(message);
|
|
158
|
+
});
|
|
159
|
+
});
|
|
160
|
+
});
|