Skip to content

Commit 0134931

Browse files
committed
chore: update compare DB script data sorting logics
1 parent f2c0a05 commit 0134931

File tree

2 files changed

+248
-44
lines changed

2 files changed

+248
-44
lines changed

.scripts/compare-database.js

Lines changed: 65 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,48 @@ const omit = (object, ...keys) =>
88
const omitArray = (arrayOfObjects, ...keys) =>
99
arrayOfObjects.map((value) => omit(value, ...keys));
1010

11+
// Exported utility functions for testing
12+
export const autoCompare = (a, b) => {
13+
if (typeof a !== typeof b) {
14+
return (typeof a).localeCompare(typeof b);
15+
}
16+
17+
if (typeof a === 'object' && a !== null && b !== null) {
18+
const aKeys = Object.keys(a).sort();
19+
const bKeys = Object.keys(b).sort();
20+
21+
for (let i = 0; i < Math.min(aKeys.length, bKeys.length); i++) {
22+
if (aKeys[i] !== bKeys[i]) {
23+
return aKeys[i].localeCompare(bKeys[i]);
24+
}
25+
const comparison = autoCompare(a[aKeys[i]], b[bKeys[i]]);
26+
if (comparison !== 0) {
27+
return comparison;
28+
}
29+
}
30+
31+
return aKeys.length - bKeys.length;
32+
}
33+
34+
return String(a).localeCompare(String(b));
35+
};
36+
37+
export const deepSort = (obj) => {
38+
if (Array.isArray(obj)) {
39+
return obj.map(deepSort).sort(autoCompare);
40+
}
41+
42+
if (obj && typeof obj === 'object') {
43+
const sorted = {};
44+
Object.keys(obj).sort().forEach(key => {
45+
sorted[key] = deepSort(obj[key]);
46+
});
47+
return sorted;
48+
}
49+
50+
return obj;
51+
};
52+
1153
const schemas = ['cloud', 'public'];
1254
const schemasArray = `(${schemas.map((schema) => `'${schema}'`).join(', ')})`;
1355

@@ -204,53 +246,18 @@ const queryDatabaseManifest = async (database) => {
204246
};
205247
};
206248

207-
const [, , database1, database2] = process.argv;
208-
209-
console.log('Compare database manifest between', database1, 'and', database2);
210-
211-
const manifests = [
212-
await queryDatabaseManifest(database1),
213-
await queryDatabaseManifest(database2),
214-
];
215-
216-
tryCompare(...manifests);
217-
218-
const autoCompare = (a, b) => {
219-
if (typeof a !== typeof b) {
220-
return (typeof a).localeCompare(typeof b);
221-
}
222-
223-
if (typeof a === 'object' && a !== null && b !== null) {
224-
const aKeys = Object.keys(a).sort();
225-
const bKeys = Object.keys(b).sort();
226-
227-
for (let i = 0; i < Math.min(aKeys.length, bKeys.length); i++) {
228-
if (aKeys[i] !== bKeys[i]) {
229-
return aKeys[i].localeCompare(bKeys[i]);
230-
}
231-
const comparison = autoCompare(a[aKeys[i]], b[bKeys[i]]);
232-
if (comparison !== 0) {
233-
return comparison;
234-
}
235-
}
236-
237-
return aKeys.length - bKeys.length;
238-
}
239-
240-
return String(a).localeCompare(String(b));
241-
};
242-
243249
const buildSortByKeys = (keys) => (a, b) => {
244250
const found = keys.find((key) => a[key] !== b[key]);
245251
return found ? autoCompare(a[found], b[found]) : 0;
246252
};
247253

248-
const queryDatabaseData = async (database) => {
254+
const queryDatabaseData = async (database, manifests) => {
249255
const pool = new pg.Pool({
250256
database,
251257
user: 'postgres',
252258
password: 'postgres',
253259
});
260+
254261
const result = await Promise.all(
255262
manifests[0].tables.map(async ({ table_schema, table_name }) => {
256263
const { rows } = await pool.query(
@@ -262,7 +269,7 @@ const queryDatabaseData = async (database) => {
262269
const data = omitArray(rows, 'value');
263270
return [
264271
table_name,
265-
data.sort(buildSortByKeys(Object.keys(data[0] ?? {}))),
272+
data.map(deepSort).sort(buildSortByKeys(Object.keys(data[0] ?? {}))),
266273
];
267274
}
268275

@@ -281,16 +288,30 @@ const queryDatabaseData = async (database) => {
281288
'add_on_sku_id'
282289
);
283290

284-
return [table_name, data.sort(buildSortByKeys(Object.keys(data[0] ?? {})))];
291+
return [table_name, data.map(deepSort).sort(buildSortByKeys(Object.keys(data[0] ?? {})))];
285292
})
286293
);
287294

288295
return Object.fromEntries(result);
289296
};
290297

291-
console.log('Compare database data between', database1, 'and', database2);
298+
// Only run the main logic when this file is executed directly
299+
if (import.meta.url === `file://${process.argv[1]}`) {
300+
const [, , database1, database2] = process.argv;
301+
302+
console.log('Compare database manifest between', database1, 'and', database2);
303+
304+
const manifests = [
305+
await queryDatabaseManifest(database1),
306+
await queryDatabaseManifest(database2),
307+
];
292308

293-
tryCompare(
294-
await queryDatabaseData(database1),
295-
await queryDatabaseData(database2)
296-
);
309+
tryCompare(...manifests);
310+
311+
console.log('Compare database data between', database1, 'and', database2);
312+
313+
tryCompare(
314+
await queryDatabaseData(database1, manifests),
315+
await queryDatabaseData(database2, manifests)
316+
);
317+
}

.scripts/compare-database.test.js

Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
/**
2+
* Test file for deepSort and autoCompare functions from compare-database.js
3+
*
4+
* Usage: node .scripts/compare-database.test.js
5+
*
6+
* This file tests the deep sorting functionality that ensures consistent
7+
* ordering of arrays and objects in database comparison operations.
8+
*/
9+
10+
import assert from 'node:assert';
11+
import { autoCompare, deepSort } from './compare-database.js';
12+
13+
// Test helper function
14+
const runTest = (testName, testFn) => {
15+
try {
16+
testFn();
17+
console.log(`✅ ${testName}`);
18+
} catch (error) {
19+
console.error(`❌ ${testName}: ${error.message}`);
20+
process.exit(1);
21+
}
22+
};
23+
24+
// Test cases for autoCompare function
25+
runTest('autoCompare - different types', () => {
26+
assert.strictEqual(autoCompare('string', 123) > 0, true); // string > number
27+
assert.strictEqual(autoCompare(123, 'string') < 0, true); // number < string
28+
});
29+
30+
runTest('autoCompare - same primitive types', () => {
31+
assert.strictEqual(autoCompare('apple', 'banana') < 0, true);
32+
assert.strictEqual(autoCompare('banana', 'apple') > 0, true);
33+
assert.strictEqual(autoCompare('apple', 'apple'), 0);
34+
assert.strictEqual(autoCompare(1, 2) < 0, true);
35+
assert.strictEqual(autoCompare(2, 1) > 0, true);
36+
assert.strictEqual(autoCompare(1, 1), 0);
37+
});
38+
39+
runTest('autoCompare - objects', () => {
40+
const obj1 = { a: 1, b: 2 };
41+
const obj2 = { a: 1, b: 2 };
42+
const obj3 = { a: 1, b: 3 };
43+
44+
assert.strictEqual(autoCompare(obj1, obj2), 0);
45+
assert.strictEqual(autoCompare(obj1, obj3) < 0, true);
46+
assert.strictEqual(autoCompare(obj3, obj1) > 0, true);
47+
});
48+
49+
// Test cases for deepSort function
50+
runTest('deepSort - primitive values', () => {
51+
assert.strictEqual(deepSort('hello'), 'hello');
52+
assert.strictEqual(deepSort(123), 123);
53+
assert.strictEqual(deepSort(null), null);
54+
assert.strictEqual(deepSort(undefined), undefined);
55+
});
56+
57+
runTest('deepSort - simple arrays', () => {
58+
const input = [3, 1, 2];
59+
const expected = [1, 2, 3];
60+
const result = deepSort(input);
61+
62+
assert.deepStrictEqual(result, expected);
63+
});
64+
65+
runTest('deepSort - string arrays', () => {
66+
const input = ['banana', 'apple', 'cherry'];
67+
const expected = ['apple', 'banana', 'cherry'];
68+
const result = deepSort(input);
69+
70+
assert.deepStrictEqual(result, expected);
71+
});
72+
73+
runTest('deepSort - nested arrays', () => {
74+
const input = [[3, 1], [2, 4], [1, 2]];
75+
const expected = [[1, 2], [1, 3], [2, 4]];
76+
const result = deepSort(input);
77+
78+
assert.deepStrictEqual(result, expected);
79+
});
80+
81+
runTest('deepSort - simple objects', () => {
82+
const input = { c: 3, a: 1, b: 2 };
83+
const expected = { a: 1, b: 2, c: 3 };
84+
const result = deepSort(input);
85+
86+
assert.deepStrictEqual(result, expected);
87+
});
88+
89+
runTest('deepSort - objects with arrays', () => {
90+
const input = {
91+
name: 'test',
92+
items: [3, 1, 2],
93+
tags: ['beta', 'alpha']
94+
};
95+
const expected = {
96+
items: [1, 2, 3],
97+
name: 'test',
98+
tags: ['alpha', 'beta']
99+
};
100+
const result = deepSort(input);
101+
102+
assert.deepStrictEqual(result, expected);
103+
});
104+
105+
runTest('deepSort - arrays with objects', () => {
106+
const input = [
107+
{ name: 'Bob', age: 25 },
108+
{ name: 'Alice', age: 30 },
109+
{ name: 'Charlie', age: 20 }
110+
];
111+
// Objects are sorted by their keys and values in lexicographic order
112+
// First by 'age' key, then by 'name' key
113+
const expected = [
114+
{ age: 20, name: 'Charlie' },
115+
{ age: 25, name: 'Bob' },
116+
{ age: 30, name: 'Alice' }
117+
];
118+
const result = deepSort(input);
119+
120+
assert.deepStrictEqual(result, expected);
121+
});
122+
123+
runTest('deepSort - complex nested structure', () => {
124+
const input = {
125+
users: [
126+
{
127+
name: 'Bob',
128+
roles: ['admin', 'user'],
129+
metadata: { created: '2023-01-01', active: true }
130+
},
131+
{
132+
name: 'Alice',
133+
roles: ['user', 'editor'],
134+
metadata: { created: '2023-01-02', active: false }
135+
}
136+
],
137+
config: {
138+
version: '1.0',
139+
features: ['auth', 'logging', 'api']
140+
}
141+
};
142+
143+
const expected = {
144+
config: {
145+
features: ['api', 'auth', 'logging'],
146+
version: '1.0'
147+
},
148+
users: [
149+
{
150+
metadata: { active: false, created: '2023-01-02' },
151+
name: 'Alice',
152+
roles: ['editor', 'user']
153+
},
154+
{
155+
metadata: { active: true, created: '2023-01-01' },
156+
name: 'Bob',
157+
roles: ['admin', 'user']
158+
}
159+
]
160+
};
161+
162+
const result = deepSort(input);
163+
assert.deepStrictEqual(result, expected);
164+
});
165+
166+
runTest('deepSort - empty structures', () => {
167+
assert.deepStrictEqual(deepSort([]), []);
168+
assert.deepStrictEqual(deepSort({}), {});
169+
// Empty array comes before empty object in autoCompare logic
170+
assert.deepStrictEqual(deepSort([[], {}]), [[], {}]);
171+
});
172+
173+
runTest('deepSort - mixed types in array', () => {
174+
const input = [{ b: 2 }, 'string', 1, { a: 1 }];
175+
// Type order in autoCompare: number < object < string
176+
// Objects are sorted by their content
177+
const expected = [1, { a: 1 }, { b: 2 }, 'string'];
178+
const result = deepSort(input);
179+
180+
assert.deepStrictEqual(result, expected);
181+
});
182+
183+
console.log('\n🎉 All tests passed!');

0 commit comments

Comments
 (0)