-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathfuzzy.test.js
168 lines (147 loc) · 3.59 KB
/
fuzzy.test.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
const jsonPath = require("./");
const assert = require("node:assert");
const fp = require("lodash/fp");
const set = require("lodash/set");
const cloneDeep = require("lodash/cloneDeep");
const { test } = require("node:test");
const possibleObjectKeys = fp.pipe(
fp.range(0),
fp.map(() => Math.random().toString(36).slice(4)),
fp.uniq,
)(10);
const allTypes = [
"null",
"boolean",
"number",
"string",
"object",
"array",
"undefined",
];
test("fuzzy", () => {
for (let n = 0; n < 1000000; n++) {
doTest();
}
});
function doTest() {
const [object, path, value] = generateTestArgs();
const result = jsonPath.set(object, path, value);
const lodashRef = getLodashRef(object, path, value);
assert.deepStrictEqual(
result,
lodashRef,
getAssertMessage(object, path, value),
);
}
function getLodashRef(input, path, value) {
if (path === undefined) {
return JSON.parse(JSON.stringify(value));
}
const out = cloneDeep(input === null ? {} : input);
set(out, path, value);
return JSON.parse(JSON.stringify(out));
}
function generateTestArgs() {
const object = generateValue(7, ["object", "array", "null"]);
const path = generatePath();
const value =
path === undefined
? generateValue(7, ["object", "array", "null"])
: generateValue(
7,
allTypes.filter((x) => x !== "undefined"),
);
return [object, path, value];
}
/**
* @param {number} remainingDepth
*/
function generateValue(remainingDepth, allowedTypes = allTypes) {
const typeIndex = fp.random(0, allowedTypes.length - 1);
const type = allowedTypes[typeIndex];
switch (type) {
case "undefined":
return undefined;
case "null":
return null;
case "boolean":
return generateBooleanValue();
case "number":
return generateNumberValue();
case "string":
return generateStringValue();
case "object":
return generateObjectValue(remainingDepth);
case "array":
return generateArrayValue(remainingDepth);
}
}
function generateBooleanValue() {
return Math.random() > 0.5;
}
function generateNumberValue() {
return Math.random();
}
function generateStringValue() {
return Math.random().toString(36).slice(2);
}
/**
* @param {number} remainingDepth
*/
function generateObjectValue(remainingDepth) {
const keys = fp.random(0, 7);
const obj = {};
for (let i = 0; i < keys; i++) {
const nextDepth = remainingDepth - fp.random(1, 4);
if (nextDepth > 0) {
obj[generateKey()] = generateValue(nextDepth);
}
}
return obj;
}
/**
* @param {number} remainingDepth
*/
function generateArrayValue(remainingDepth) {
const length = fp.random(0, 7);
const arr = [];
for (let i = 0; i < length; i++) {
const nextDepth = remainingDepth - fp.random(1, 4);
if (nextDepth > 0) {
arr.push(generateValue(nextDepth));
}
}
return arr;
}
function generateKey() {
const keyIndex = fp.random(0, possibleObjectKeys.size - 1);
return possibleObjectKeys[keyIndex];
}
function generatePath() {
const depth = fp.random(0, 10);
if (depth === 0) {
return undefined;
}
const path = [];
for (let i = 0; i < depth; i++) {
const isObject = Math.random() > 0.5;
path.push(isObject ? generateKey() : fp.random(0, 10));
}
return path
.map((x, i, all) =>
typeof x === "number"
? `[${x}]`
: `${x}${i < all.length && typeof all[i + 1] === "string" ? "." : ""}`,
)
.join("");
}
function getAssertMessage(object, path, value) {
return `
== object ==
${JSON.stringify(object, null, 2)}
== path ==
${path}
== value ==
${JSON.stringify(value, null, 2)}
`;
}