|
7 | 7 | PYTHAGORA_TESTS_DIR,
|
8 | 8 | PYTHAGORA_DELIMITER
|
9 | 9 | } = require("../const/common");
|
| 10 | +const path = require('path'); |
10 | 11 | let mongodb;
|
11 | 12 | // this is needed so that "mongodb" is not required before mongo patches are applied
|
12 | 13 | const ObjectId = class {
|
@@ -93,43 +94,47 @@ function stringToDate(str) {
|
93 | 94 | function isJSONObject(value) {
|
94 | 95 | return Object.prototype.toString.call(value) === "[object Object]";
|
95 | 96 | }
|
96 |
| - |
97 | 97 | function compareJson(a, b, strict) {
|
98 |
| - // TODO make more complex by running tests right after capture |
99 | 98 | if (a === b) return true;
|
100 |
| - else if (typeof a !== typeof b) return false; |
101 |
| - else if (!a || !b) return false; |
102 |
| - else if (Array.isArray(a) && Array.isArray(b)) { |
| 99 | + if (typeof a !== typeof b) return false; |
| 100 | + if ((!a && a !== 0) || (!b && b !== 0)) return false; |
| 101 | + |
| 102 | + if (Array.isArray(a) && Array.isArray(b)) { |
103 | 103 | if (a.length !== b.length) return false;
|
104 |
| - // TODO optimize because this is O(n^2) |
105 |
| - return a.reduce((acc, item, index) => acc && !!b.find(b2 => compareJson(item, b2, strict)), true); |
106 |
| - } else if (typeof a === 'string' && typeof b === 'string') { |
107 |
| - return objectIdAsStringRegex.test(a) && objectIdAsStringRegex.test(b) && !strict ? true : a === b; |
| 104 | + return a.every((itemA) => b.some((itemB) => compareJson(itemA, itemB, strict))) && |
| 105 | + b.every((itemB) => a.some((itemA) => compareJson(itemA, itemB, strict))); |
108 | 106 | }
|
109 |
| - let ignoreKeys = ['stacktrace']; |
110 |
| - // let ignoreIfKeyContains = ['token']; |
111 |
| - let aProps = Object.getOwnPropertyNames(a); |
112 |
| - let bProps = Object.getOwnPropertyNames(b); |
113 |
| - if (aProps.length !== bProps.length) { |
114 |
| - return false; |
| 107 | + |
| 108 | + if (typeof a === 'string' && typeof b === 'string') { |
| 109 | + return objectIdAsStringRegex.test(a) && objectIdAsStringRegex.test(b) && !strict ? true : a === b; |
115 | 110 | }
|
116 |
| - for (let i = 0; i < aProps.length; i++) { |
117 |
| - let propName = aProps[i]; |
118 |
| - if ( |
119 |
| - a[propName] !== b[propName] && |
120 |
| - (!isDate(a[propName]) && !isDate(a[propName])) && |
121 |
| - !ignoreKeys.includes(propName) && |
122 |
| - !(isObjectId(a[propName]) && isObjectId(b[propName]))// && |
123 |
| - // !ignoreIfKeyContains.some(function(v) { return propName.indexOf(v) >= 0; }) |
124 |
| - ) { |
| 111 | + |
| 112 | + if (typeof a === 'object' && typeof b === 'object') { |
| 113 | + let ignoreKeys = ['stacktrace']; |
| 114 | + let aProps = Object.getOwnPropertyNames(a); |
| 115 | + let bProps = Object.getOwnPropertyNames(b); |
| 116 | + |
| 117 | + if (aProps.length !== bProps.length) { |
| 118 | + return false; |
| 119 | + } |
| 120 | + |
| 121 | + for (let i = 0; i < aProps.length; i++) { |
| 122 | + let propName = aProps[i]; |
| 123 | + |
| 124 | + if (ignoreKeys.includes(propName)) continue; |
| 125 | + if (isObjectId(a[propName]) && isObjectId(b[propName])) continue; |
| 126 | + |
| 127 | + // Perform deep comparison first |
125 | 128 | if (typeof a[propName] === 'object') {
|
126 | 129 | if (!compareJson(a[propName], b[propName], strict))
|
127 | 130 | return false;
|
128 |
| - } else |
129 |
| - return false; |
| 131 | + } else if (a[propName] !== b[propName]) return false; // If a[propName] and b[propName] are not objects, use !== for comparison |
130 | 132 | }
|
| 133 | + return true; |
131 | 134 | }
|
132 |
| - return true; |
| 135 | + |
| 136 | + // If a and b are neither arrays nor objects, use !== for comparison |
| 137 | + return a !== b ? false : true; |
133 | 138 | }
|
134 | 139 |
|
135 | 140 | function compareJsonDetailed(a, b, strict) {
|
@@ -220,17 +225,20 @@ function stringToRegExp(str) {
|
220 | 225 | const getCircularReplacer = () => {
|
221 | 226 | const seen = new WeakSet();
|
222 | 227 | return (key, value) => {
|
223 |
| - value = _.clone(value); |
224 |
| - if (isLegacyObjectId(value)) value = (new ObjectId(Buffer.from(value.id.data))).toString(); |
225 |
| - else if (value instanceof RegExp) value = `RegExp("${value.toString()}")`; |
226 |
| - else if (Array.isArray(value) && value.find(v => isLegacyObjectId(v))) { |
227 |
| - value = value.map(v => isLegacyObjectId(v) ? (new ObjectId(Buffer.from(v.id.data))).toString() : v); |
228 |
| - } if (typeof value === "object" && value !== null) { |
| 228 | + if (typeof value === "object" && value !== null) { |
229 | 229 | if (seen.has(value)) {
|
230 | 230 | return;
|
231 | 231 | }
|
232 | 232 | seen.add(value);
|
233 | 233 | }
|
| 234 | + |
| 235 | + // Handle the specific types after checking for circular references |
| 236 | + if (isLegacyObjectId(value)) value = (new ObjectId(Buffer.from(value.id.data))).toString(); |
| 237 | + else if (value instanceof RegExp) value = `RegExp("${value.toString()}")`; |
| 238 | + else if (Array.isArray(value) && value.find(v => isLegacyObjectId(v))) { |
| 239 | + value = value.map(v => isLegacyObjectId(v) ? (new ObjectId(Buffer.from(v.id.data))).toString() : v); |
| 240 | + } |
| 241 | + |
234 | 242 | return value;
|
235 | 243 | };
|
236 | 244 | };
|
@@ -329,7 +337,12 @@ function insertVariablesInText(text, variables) {
|
329 | 337 | }
|
330 | 338 |
|
331 | 339 | function updateMetadata(newMetadata) {
|
332 |
| - fs.writeFileSync(`./${PYTHAGORA_METADATA_DIR}/${METADATA_FILENAME}`, JSON.stringify(newMetadata, getCircularReplacer(), 2)); |
| 340 | + const dirPath = `./${PYTHAGORA_METADATA_DIR}`; |
| 341 | + const filePath = path.join(dirPath, METADATA_FILENAME); |
| 342 | + if (!fs.existsSync(dirPath)) { |
| 343 | + fs.mkdirSync(dirPath, { recursive: true }); |
| 344 | + } |
| 345 | + fs.writeFileSync(filePath, JSON.stringify(newMetadata, getCircularReplacer(), 2)); |
333 | 346 | }
|
334 | 347 |
|
335 | 348 | function comparePaths(path1, path2) {
|
|
0 commit comments