Skip to content

Commit afd279e

Browse files
committed
Add a generator for prototype-safe.json file
1 parent 74709a8 commit afd279e

File tree

1 file changed

+167
-0
lines changed

1 file changed

+167
-0
lines changed

test-generator/prototype-safe.js

Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
#!/usr/bin/env node
2+
3+
'use strict'
4+
5+
const makeNumber = name => `
6+
"schema": {
7+
"properties": { ${JSON.stringify(name)}: { "type": "number" } }
8+
},
9+
"tests": [
10+
{ "description": "Valid on numbers", "data": 0, "valid": true },
11+
{ "description": "Valid on arrays", "data": [], "valid": true },
12+
{ "description": "Valid if not present", "data": {}, "valid": true },
13+
{ "description": "Valid if correct", "data": { ${JSON.stringify(name)}: 10 }, "valid": true },
14+
{ "description": "Invalid if incorrect (string)", "data": { ${JSON.stringify(name)}: "foo" }, "valid": false },
15+
{ "description": "Invalid if incorrect (empty string)", "data": { ${JSON.stringify(name)}: "" }, "valid": false },
16+
{ "description": "Invalid if incorrect (boolean true)", "data": { ${JSON.stringify(name)}: true }, "valid": false },
17+
{ "description": "Invalid if incorrect (boolean false)", "data": { ${JSON.stringify(name)}: false }, "valid": false },
18+
{ "description": "Invalid if incorrect (array)", "data": { ${JSON.stringify(name)}: [42] }, "valid": false },
19+
{ "description": "Invalid if incorrect (empty array)", "data": { ${JSON.stringify(name)}: [] }, "valid": false },
20+
{ "description": "Invalid if incorrect (object)", "data": { ${JSON.stringify(name)}: {} }, "valid": false }
21+
]`.trim()
22+
const makeObject = name => `
23+
"schema": {
24+
"properties": { ${JSON.stringify(name)}: { "type": "object" } }
25+
},
26+
"tests": [
27+
{ "description": "Valid on numbers", "data": 0, "valid": true },
28+
{ "description": "Valid on arrays", "data": [], "valid": true },
29+
{ "description": "Valid if not present", "data": {}, "valid": true },
30+
{ "description": "Valid if correct", "data": { ${JSON.stringify(name)}: {} }, "valid": true },
31+
{ "description": "Invalid if incorrect (string)", "data": { ${JSON.stringify(name)}: "foo" }, "valid": false },
32+
{ "description": "Invalid if incorrect (empty string)", "data": { ${JSON.stringify(name)}: "" }, "valid": false },
33+
{ "description": "Invalid if incorrect (boolean true)", "data": { ${JSON.stringify(name)}: true }, "valid": false },
34+
{ "description": "Invalid if incorrect (boolean false)", "data": { ${JSON.stringify(name)}: false }, "valid": false },
35+
{ "description": "Invalid if incorrect (array)", "data": { ${JSON.stringify(name)}: [42] }, "valid": false },
36+
{ "description": "Invalid if incorrect (empty array)", "data": { ${JSON.stringify(name)}: [] }, "valid": false },
37+
{ "description": "Invalid if incorrect (zero number)", "data": { ${JSON.stringify(name)}: 0 }, "valid": false },
38+
{ "description": "Invalid if incorrect (number)", "data": { ${JSON.stringify(name)}: 42 }, "valid": false }
39+
]`.trim()
40+
const makeRequired = name => `
41+
"schema": {
42+
"required": [${JSON.stringify(name)}]
43+
},
44+
"tests": [
45+
{ "description": "Valid on numbers", "data": 0, "valid": true },
46+
{ "description": "Valid on arrays", "data": [], "valid": true },
47+
{ "description": "Invalid if not present", "data": {}, "valid": false },
48+
{ "description": "Valid if present (string)", "data": { ${JSON.stringify(name)}: "foo" }, "valid": true },
49+
{ "description": "Valid if present (empty string)", "data": { ${JSON.stringify(name)}: "" }, "valid": true },
50+
{ "description": "Valid if present (boolean true)", "data": { ${JSON.stringify(name)}: true }, "valid": true },
51+
{ "description": "Valid if present (boolean false)", "data": { ${JSON.stringify(name)}: false }, "valid": true },
52+
{ "description": "Valid if present (array)", "data": { ${JSON.stringify(name)}: [42] }, "valid": true },
53+
{ "description": "Valid if present (empty array)", "data": { ${JSON.stringify(name)}: [] }, "valid": true },
54+
{ "description": "Valid if present (object)", "data": { ${JSON.stringify(name)}: {} }, "valid": true },
55+
{ "description": "Valid if present (zero number)", "data": { ${JSON.stringify(name)}: 0 }, "valid": true },
56+
{ "description": "Valid if present (number)", "data": { ${JSON.stringify(name)}: 42 }, "valid": true }
57+
]`.trim()
58+
const makeDefault = name => `
59+
"schema": {
60+
"properties": { ${JSON.stringify(name)}: { "default": "foo" } }
61+
},
62+
"tests": [
63+
{ "description": "Valid on numbers", "data": 0, "valid": true },
64+
{ "description": "Valid on arrays", "data": [], "valid": true },
65+
{ "description": "Valid if not present", "data": {}, "valid": true },
66+
{ "description": "Valid if present (string)", "data": { ${JSON.stringify(name)}: "foo" }, "valid": true },
67+
{ "description": "Valid if present (empty string)", "data": { ${JSON.stringify(name)}: "" }, "valid": true },
68+
{ "description": "Valid if present (boolean true)", "data": { ${JSON.stringify(name)}: true }, "valid": true },
69+
{ "description": "Valid if present (boolean false)", "data": { ${JSON.stringify(name)}: false }, "valid": true },
70+
{ "description": "Valid if present (array)", "data": { ${JSON.stringify(name)}: [42] }, "valid": true },
71+
{ "description": "Valid if present (empty array)", "data": { ${JSON.stringify(name)}: [] }, "valid": true },
72+
{ "description": "Valid if present (object)", "data": { ${JSON.stringify(name)}: {} }, "valid": true },
73+
{ "description": "Valid if present (zero number)", "data": { ${JSON.stringify(name)}: 0 }, "valid": true },
74+
{ "description": "Valid if present (number)", "data": { ${JSON.stringify(name)}: 42 }, "valid": true }
75+
]`.trim()
76+
const makePropertyDefault = (name, parent) => `
77+
"schema": {
78+
"properties": { ${JSON.stringify(parent)}: { "properties": { ${JSON.stringify(name)}: { "default": "foo" } } } }
79+
},
80+
"tests": [
81+
{ "description": "Valid on numbers", "data": 0, "valid": true },
82+
{ "description": "Valid on arrays", "data": [], "valid": true },
83+
{ "description": "Valid if not present", "data": {}, "valid": true },
84+
{ "description": "Valid if present (string)", "data": { ${JSON.stringify(name)}: "foo" }, "valid": true },
85+
{ "description": "Valid if present (empty string)", "data": { ${JSON.stringify(name)}: "" }, "valid": true },
86+
{ "description": "Valid if present (boolean true)", "data": { ${JSON.stringify(name)}: true }, "valid": true },
87+
{ "description": "Valid if present (boolean false)", "data": { ${JSON.stringify(name)}: false }, "valid": true },
88+
{ "description": "Valid if present (array)", "data": { ${JSON.stringify(name)}: [42] }, "valid": true },
89+
{ "description": "Valid if present (empty array)", "data": { ${JSON.stringify(name)}: [] }, "valid": true },
90+
{ "description": "Valid if present (object)", "data": { ${JSON.stringify(name)}: {} }, "valid": true },
91+
{ "description": "Valid if present (zero number)", "data": { ${JSON.stringify(name)}: 0 }, "valid": true },
92+
{ "description": "Valid if present (number)", "data": { ${JSON.stringify(name)}: 42 }, "valid": true }
93+
]`.trim()
94+
const makeBlock = (name, comment) => `
95+
{
96+
"description": "Does not see elements non existing on the object: '${JSON.stringify(name).slice(1, -1)}' as number",
97+
"comment": ${JSON.stringify(comment)},
98+
${makeNumber(name)}
99+
},
100+
{
101+
"description": "Does not see elements non existing on the object: '${JSON.stringify(name).slice(1, -1)}' as object",
102+
"comment": ${JSON.stringify(comment)},
103+
${makeObject(name)}
104+
},
105+
{
106+
"description": "Does not see elements non existing on the object: '${JSON.stringify(name).slice(1, -1)}' via required",
107+
"comment": ${JSON.stringify(comment)},
108+
${makeRequired(name)}
109+
}`
110+
const makeBlockDefault = (name, proto) => `
111+
{
112+
"description": "Default value: '${JSON.stringify(name).slice(1, -1)}' as number",
113+
"comment": "Default should not affect passing validation in all cases",
114+
${makeDefault(name)}
115+
},
116+
{
117+
"description": "Default value on a '${JSON.stringify(proto).slice(1, -1)}' property: '${JSON.stringify(name).slice(1, -1)}' as object",
118+
"comment": "Default should not affect passing validation in all cases",
119+
${makePropertyDefault(name, proto)}
120+
},
121+
{
122+
"description": "Does not see inexisting elements on new objects: '${JSON.stringify(name).slice(1, -1)}' via required",
123+
"comment": "Validating a new object should not be affected by previous default",
124+
${makeRequired(name)}
125+
},
126+
{
127+
"description": "Does not see inexisting elements on new objects: '${JSON.stringify(name).slice(1, -1)}' via required",
128+
"comment": "Validating a new object should not be affected by previous default",
129+
${makeNumber(name)}
130+
},
131+
{
132+
"description": "Does not see inexisting elements on new objects: '${JSON.stringify(name).slice(1, -1)}' via required",
133+
"comment": "Validating a new object should not be affected by previous default",
134+
${makeObject(name)}
135+
}`
136+
const tests = `[${
137+
makeBlock('x', 'This is a baseline check to validate that other properties are treated the same and are not special compared to it')
138+
},${
139+
['length', 'toString', 'constructor', '__proto__'].map(name =>
140+
makeBlock(name, 'This is a common bug of some of the JS validators, this test detects it')
141+
).join(',')
142+
},${
143+
['__len', '__tostring', '__gc', '__index'].map(name =>
144+
makeBlock(name, 'Also test for Lua special name handling')
145+
).join(',')
146+
},${
147+
['foo', 'length', '__proto__'].map(name => makeBlockDefault(name, '__proto__')).join(',')
148+
},${
149+
makeBlockDefault('__index', '__index')
150+
}
151+
]`
152+
153+
// Ensure that everything is correct by checking against the validator
154+
/*
155+
const schemasafe = require('@exodus/schemasafe')
156+
for (const useDefaults of [false, true]) {
157+
for (const suite of JSON.parse(tests)) {
158+
const validate = schemasafe.validator(suite.schema, { useDefaults })
159+
for (const test of suite.tests) {
160+
if (validate(test.data) !== test.valid)
161+
throw new Error(`${suite.description} / ${test.description}: expected ${test.valid} (defaults: ${useDefaults})`)
162+
}
163+
}
164+
}
165+
*/
166+
167+
console.log(tests)

0 commit comments

Comments
 (0)