@schmock/schema 1.0.0 → 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 +2 -1
- 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 +38 -65
- 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
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
/// <reference path="../../../types/schmock.d.ts" />
|
|
2
|
-
|
|
3
1
|
import { en, Faker } from "@faker-js/faker";
|
|
2
|
+
import type { Plugin, PluginContext } from "@schmock/core";
|
|
4
3
|
import {
|
|
5
4
|
ResourceLimitError,
|
|
6
5
|
SchemaGenerationError,
|
|
@@ -18,18 +17,23 @@ function createFakerInstance() {
|
|
|
18
17
|
return new Faker({ locale: [en] });
|
|
19
18
|
}
|
|
20
19
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
jsf.
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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
|
+
}
|
|
33
37
|
|
|
34
38
|
// Resource limits for safety
|
|
35
39
|
const MAX_ARRAY_SIZE = 10000;
|
|
@@ -53,15 +57,15 @@ interface SchemaPluginOptions {
|
|
|
53
57
|
overrides?: Record<string, any>;
|
|
54
58
|
}
|
|
55
59
|
|
|
56
|
-
export function schemaPlugin(options: SchemaPluginOptions):
|
|
60
|
+
export function schemaPlugin(options: SchemaPluginOptions): Plugin {
|
|
57
61
|
// Validate schema immediately when plugin is created
|
|
58
62
|
validateSchema(options.schema);
|
|
59
63
|
|
|
60
64
|
return {
|
|
61
65
|
name: "schema",
|
|
62
|
-
version: "1.0.
|
|
66
|
+
version: "1.0.1",
|
|
63
67
|
|
|
64
|
-
process(context:
|
|
68
|
+
process(context: PluginContext, response?: any) {
|
|
65
69
|
// If response already exists, pass it through
|
|
66
70
|
if (response !== undefined && response !== null) {
|
|
67
71
|
return { context, response };
|
|
@@ -131,7 +135,7 @@ export function generateFromSchema(options: SchemaGenerationContext): any {
|
|
|
131
135
|
|
|
132
136
|
generated = [];
|
|
133
137
|
for (let i = 0; i < itemCount; i++) {
|
|
134
|
-
let item =
|
|
138
|
+
let item = getJsf().generate(
|
|
135
139
|
enhanceSchemaWithSmartMapping(itemSchema as JSONSchema7),
|
|
136
140
|
);
|
|
137
141
|
item = applyOverrides(item, overrides, params, state, query);
|
|
@@ -140,7 +144,7 @@ export function generateFromSchema(options: SchemaGenerationContext): any {
|
|
|
140
144
|
} else {
|
|
141
145
|
// Handle object schemas
|
|
142
146
|
const enhancedSchema = enhanceSchemaWithSmartMapping(schema);
|
|
143
|
-
generated =
|
|
147
|
+
generated = getJsf().generate(enhancedSchema);
|
|
144
148
|
generated = applyOverrides(generated, overrides, params, state, query);
|
|
145
149
|
}
|
|
146
150
|
|
|
@@ -687,16 +691,6 @@ function processTemplate(
|
|
|
687
691
|
},
|
|
688
692
|
);
|
|
689
693
|
|
|
690
|
-
// Try to convert to number if it's a numeric string
|
|
691
|
-
if (typeof processed === "string") {
|
|
692
|
-
if (/^\d+$/.test(processed)) {
|
|
693
|
-
return Number.parseInt(processed, 10);
|
|
694
|
-
}
|
|
695
|
-
if (/^\d+\.\d+$/.test(processed)) {
|
|
696
|
-
return Number.parseFloat(processed);
|
|
697
|
-
}
|
|
698
|
-
}
|
|
699
|
-
|
|
700
694
|
return processed;
|
|
701
695
|
}
|
|
702
696
|
|
|
@@ -707,31 +701,6 @@ function processTemplate(
|
|
|
707
701
|
* @throws {SchemaValidationError} When faker method format or namespace is invalid
|
|
708
702
|
*/
|
|
709
703
|
function validateFakerMethod(fakerMethod: string): void {
|
|
710
|
-
// List of known faker namespaces and common methods
|
|
711
|
-
const validFakerNamespaces = [
|
|
712
|
-
"person",
|
|
713
|
-
"internet",
|
|
714
|
-
"phone",
|
|
715
|
-
"location",
|
|
716
|
-
"string",
|
|
717
|
-
"date",
|
|
718
|
-
"company",
|
|
719
|
-
"commerce",
|
|
720
|
-
"color",
|
|
721
|
-
"database",
|
|
722
|
-
"finance",
|
|
723
|
-
"git",
|
|
724
|
-
"hacker",
|
|
725
|
-
"helpers",
|
|
726
|
-
"image",
|
|
727
|
-
"lorem",
|
|
728
|
-
"music",
|
|
729
|
-
"number",
|
|
730
|
-
"science",
|
|
731
|
-
"vehicle",
|
|
732
|
-
"word",
|
|
733
|
-
];
|
|
734
|
-
|
|
735
704
|
// Check if faker method follows valid format (namespace.method)
|
|
736
705
|
const parts = fakerMethod.split(".");
|
|
737
706
|
if (parts.length < 2) {
|
|
@@ -742,20 +711,24 @@ function validateFakerMethod(fakerMethod: string): void {
|
|
|
742
711
|
);
|
|
743
712
|
}
|
|
744
713
|
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
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
|
+
}
|
|
752
727
|
}
|
|
753
|
-
|
|
754
|
-
// Check for obviously invalid method names
|
|
755
|
-
if (fakerMethod.includes("nonexistent") || fakerMethod.includes("invalid")) {
|
|
728
|
+
if (typeof current !== "function") {
|
|
756
729
|
throw new SchemaValidationError(
|
|
757
730
|
"$.faker",
|
|
758
|
-
`Invalid faker method: "${fakerMethod}"`,
|
|
731
|
+
`Invalid faker method: "${fakerMethod}" is not a function`,
|
|
759
732
|
"Check faker.js documentation for valid methods",
|
|
760
733
|
);
|
|
761
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
|
+
});
|