Skip to content

Commit 9c6023c

Browse files
committed
feat(4.5.2):enhance isDeepObjectMatch function for recursive object comparison; add comprehensive tests for deep object matching
1 parent 42d4bb4 commit 9c6023c

4 files changed

Lines changed: 179 additions & 5 deletions

File tree

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "bybit-api",
3-
"version": "4.5.1",
3+
"version": "4.5.2",
44
"description": "Complete & robust Node.js SDK for Bybit's REST APIs and WebSockets, with TypeScript & strong end to end tests.",
55
"main": "lib/index.js",
66
"types": "lib/index.d.ts",

src/util/websockets/WsStore.ts

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import {
1010
} from './WsStore.types';
1111

1212
/**
13-
* Simple comparison of two objects, only checks 1-level deep (nested objects won't match)
13+
* Simple comparison of two objects, recursive for nested objects
1414
*/
1515
export function isDeepObjectMatch(object1: unknown, object2: unknown): boolean {
1616
if (typeof object1 === 'string' && typeof object2 === 'string') {
@@ -21,11 +21,35 @@ export function isDeepObjectMatch(object1: unknown, object2: unknown): boolean {
2121
return false;
2222
}
2323

24+
if (object1 === null || object2 === null) {
25+
return object1 === object2;
26+
}
27+
28+
const keys1 = Object.keys(object1).sort();
29+
const keys2 = Object.keys(object2).sort();
30+
31+
if (keys1.length !== keys2.length) {
32+
return false;
33+
}
34+
35+
if (!keys1.every((val, i) => val === keys2[i])) {
36+
return false;
37+
}
38+
2439
for (const key in object1) {
2540
const value1 = (object1 as any)[key];
2641
const value2 = (object2 as any)[key];
2742

28-
if (value1 !== value2) {
43+
if (
44+
typeof value1 === 'object' &&
45+
typeof value2 === 'object' &&
46+
value1 !== null &&
47+
value2 !== null
48+
) {
49+
if (!isDeepObjectMatch(value1, value2)) {
50+
return false;
51+
}
52+
} else if (value1 !== value2) {
2953
return false;
3054
}
3155
}

test/websockets/wsStore.test.ts

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
import { isDeepObjectMatch } from '../../src/util/websockets/WsStore';
2+
3+
describe('WsStore', () => {
4+
describe('isDeepObjectMatch()', () => {
5+
it('should match two equal strings', () => {
6+
expect(
7+
isDeepObjectMatch('orderbook.50.BTCUSDT', 'orderbook.50.BTCUSDT'),
8+
).toBeTruthy();
9+
expect(
10+
isDeepObjectMatch('orderbook.50.BTCUSDT', 'orderbook.50.ETHUSDT'),
11+
).toBeFalsy();
12+
});
13+
14+
it('should match simple topic objects', () => {
15+
const topic1 = {
16+
topic: 'orderbook.50.BTCUSDT',
17+
};
18+
const topic2 = {
19+
topic: 'orderbook.50.BTCUSDT',
20+
};
21+
22+
expect(isDeepObjectMatch(topic1, topic2)).toBeTruthy();
23+
});
24+
25+
it('should match topic objects with payload, even if keys are differently ordered', () => {
26+
const topic1 = {
27+
topic: 'orderbook.50.BTCUSDT',
28+
payload: { symbol: 'BTCUSDT' },
29+
category: 'linear',
30+
};
31+
const topic2 = {
32+
category: 'linear',
33+
payload: { symbol: 'BTCUSDT' },
34+
topic: 'orderbook.50.BTCUSDT',
35+
};
36+
37+
expect(isDeepObjectMatch(topic1, topic2)).toBeTruthy();
38+
});
39+
40+
it('should match nested payload objects', () => {
41+
const topic1 = {
42+
topic: 'kline.5.BTCUSDT',
43+
payload: {
44+
symbol: 'BTCUSDT',
45+
interval: '5',
46+
},
47+
category: 'spot',
48+
};
49+
const topic2 = {
50+
topic: 'kline.5.BTCUSDT',
51+
payload: {
52+
symbol: 'BTCUSDT',
53+
interval: '5',
54+
},
55+
category: 'spot',
56+
};
57+
58+
expect(isDeepObjectMatch(topic1, topic2)).toBeTruthy();
59+
});
60+
61+
it('should NOT match topics with different category', () => {
62+
const topic1 = {
63+
topic: 'orderbook.50.BTCUSDT',
64+
category: 'linear',
65+
};
66+
const topic2 = {
67+
topic: 'orderbook.50.BTCUSDT',
68+
category: 'spot',
69+
};
70+
71+
expect(isDeepObjectMatch(topic1, topic2)).toBeFalsy();
72+
});
73+
74+
it('should NOT match topics with different payload values', () => {
75+
const topic1 = {
76+
topic: 'kline.5.BTCUSDT',
77+
payload: { symbol: 'BTCUSDT' },
78+
category: 'spot',
79+
};
80+
const topic2 = {
81+
topic: 'kline.5.BTCUSDT',
82+
payload: { symbol: 'ETHUSDT' },
83+
category: 'spot',
84+
};
85+
86+
expect(isDeepObjectMatch(topic1, topic2)).toBeFalsy();
87+
});
88+
89+
it('should NOT match topics with nested payload differences', () => {
90+
const topic1 = {
91+
topic: 'kline.5.BTCUSDT',
92+
payload: {
93+
symbol: 'BTCUSDT',
94+
interval: '5',
95+
},
96+
category: 'spot',
97+
};
98+
const topic2 = {
99+
topic: 'kline.5.BTCUSDT',
100+
payload: {
101+
symbol: 'BTCUSDT',
102+
interval: '15',
103+
},
104+
category: 'spot',
105+
};
106+
107+
expect(isDeepObjectMatch(topic1, topic2)).toBeFalsy();
108+
});
109+
110+
it('should NOT match asymmetric objects (missing category property)', () => {
111+
const topic1 = {
112+
topic: 'orderbook.50.BTCUSDT',
113+
category: 'linear',
114+
};
115+
const topic2 = {
116+
topic: 'orderbook.50.BTCUSDT',
117+
};
118+
119+
expect(isDeepObjectMatch(topic1, topic2)).toBeFalsy();
120+
});
121+
122+
it('should NOT match asymmetric objects (missing nested property)', () => {
123+
const topic1 = {
124+
topic: 'kline.5.BTCUSDT',
125+
payload: {
126+
symbol: 'BTCUSDT',
127+
interval: '5',
128+
},
129+
category: 'spot',
130+
};
131+
const topic2 = {
132+
topic: 'kline.5.BTCUSDT',
133+
payload: {
134+
symbol: 'BTCUSDT',
135+
},
136+
category: 'spot',
137+
};
138+
139+
expect(isDeepObjectMatch(topic1, topic2)).toBeFalsy();
140+
});
141+
142+
it('should NOT match string to object', () => {
143+
expect(
144+
isDeepObjectMatch('orderbook.50.BTCUSDT', {
145+
topic: 'orderbook.50.BTCUSDT',
146+
}),
147+
).toBeFalsy();
148+
});
149+
});
150+
});

0 commit comments

Comments
 (0)