Skip to content

Commit eae4651

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

File tree

1 file changed

+170
-0
lines changed

1 file changed

+170
-0
lines changed

test-generator/prototype-safe.js

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

0 commit comments

Comments
 (0)