Skip to content

Commit 7a12ad2

Browse files
UTAPI-116: Unit tests for flexiblePromisify
1 parent 2bcfee3 commit 7a12ad2

File tree

1 file changed

+274
-0
lines changed

1 file changed

+274
-0
lines changed
Lines changed: 274 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,274 @@
1+
const assert = require('assert');
2+
const { scheduler } = require('timers/promises');
3+
const sinon = require('sinon');
4+
const flexiblePromisify = require('../../../../libV2/utils/flexiblePromisify');
5+
6+
describe('flexiblePromisify', () => {
7+
describe('Custom promisified functions', () => {
8+
it('should return custom promisified version when available', () => {
9+
const customPromisified = () => Promise.resolve('custom result');
10+
const originalFn = () => {};
11+
originalFn[Symbol.for('nodejs.util.promisify.custom')] = customPromisified;
12+
13+
const result = flexiblePromisify(originalFn);
14+
15+
assert.strictEqual(result, customPromisified);
16+
});
17+
18+
it('should use custom promisified function even if original has callback signature', () => {
19+
const customPromisified = () => Promise.resolve('custom result');
20+
const originalFn = callback => callback(null, 'original result');
21+
originalFn[Symbol.for('nodejs.util.promisify.custom')] = customPromisified;
22+
23+
const result = flexiblePromisify(originalFn);
24+
25+
assert.strictEqual(result, customPromisified);
26+
});
27+
});
28+
29+
describe('Promise-returning functions', () => {
30+
it('should handle functions that return resolved promises', async () => {
31+
const originalFn = () => Promise.resolve('promise result');
32+
const promisified = flexiblePromisify(originalFn);
33+
34+
const result = await promisified();
35+
36+
assert.strictEqual(result, 'promise result');
37+
});
38+
39+
it('should handle functions that return rejected promises', async () => {
40+
const error = new Error('promise error');
41+
const originalFn = () => Promise.reject(error);
42+
const promisified = flexiblePromisify(originalFn);
43+
44+
await assert.rejects(promisified(), error);
45+
});
46+
47+
it('should handle functions with arguments that return promises', async () => {
48+
const originalFn = (a, b) => Promise.resolve(a + b);
49+
const promisified = flexiblePromisify(originalFn);
50+
51+
const result = await promisified(5, 3);
52+
53+
assert.strictEqual(result, 8);
54+
});
55+
56+
it('should preserve this context for promise-returning functions', async () => {
57+
const obj = {
58+
value: 42,
59+
fn() {
60+
return Promise.resolve(this.value);
61+
}
62+
};
63+
const promisified = flexiblePromisify(obj.fn);
64+
65+
const result = await promisified.call(obj);
66+
67+
assert.strictEqual(result, 42);
68+
});
69+
70+
it('should ignore asynchronous callback when function returns a promise', async () => {
71+
const callbackSpy = sinon.spy();
72+
const originalFn = callback => {
73+
// Even though we have a callback parameter, we return a promise
74+
// The callback should be ignored
75+
setTimeout(() => {
76+
callbackSpy();
77+
callback(null, 'callback result');
78+
}, 10);
79+
return Promise.resolve('promise result');
80+
};
81+
const promisified = flexiblePromisify(originalFn);
82+
83+
const result = await promisified();
84+
85+
assert.strictEqual(result, 'promise result');
86+
// Give callback time to potentially execute
87+
await scheduler.wait(20);
88+
assert.strictEqual(callbackSpy.callCount, 1); // Callback still executes but is ignored
89+
});
90+
91+
it('should use synchronous callback result over returned promise', async () => {
92+
const originalFn = callback => {
93+
callback(null, 'callback result');
94+
return Promise.resolve('promise result');
95+
};
96+
const promisified = flexiblePromisify(originalFn);
97+
98+
const result = await promisified();
99+
100+
assert.strictEqual(result, 'callback result');
101+
});
102+
103+
it('should handle functions that return thenable objects', async () => {
104+
const thenable = {
105+
then: resolve => {
106+
setTimeout(() => resolve('thenable result'), 10);
107+
}
108+
};
109+
const originalFn = () => thenable;
110+
const promisified = flexiblePromisify(originalFn);
111+
112+
const result = await promisified();
113+
114+
assert.strictEqual(result, 'thenable result');
115+
});
116+
117+
it('should handle functions that return rejected thenable objects', async () => {
118+
const error = new Error('thenable error');
119+
const thenable = {
120+
then: (resolve, reject) => {
121+
setTimeout(() => reject(error), 10);
122+
}
123+
};
124+
const originalFn = () => thenable;
125+
const promisified = flexiblePromisify(originalFn);
126+
127+
await assert.rejects(promisified(), error);
128+
});
129+
});
130+
131+
describe('Callback-style functions', () => {
132+
it('should handle successful callback functions', async () => {
133+
const originalFn = callback => {
134+
setTimeout(() => callback(null, 'callback result'), 10);
135+
};
136+
const promisified = flexiblePromisify(originalFn);
137+
138+
const result = await promisified();
139+
140+
assert.strictEqual(result, 'callback result');
141+
});
142+
143+
it('should handle callback functions with errors', async () => {
144+
const error = new Error('callback error');
145+
const originalFn = callback => {
146+
setTimeout(() => callback(error), 10);
147+
};
148+
const promisified = flexiblePromisify(originalFn);
149+
150+
await assert.rejects(promisified(), error);
151+
});
152+
153+
it('should handle callback functions with arguments', async () => {
154+
const originalFn = (a, b, callback) => {
155+
setTimeout(() => callback(null, a * b), 10);
156+
};
157+
const promisified = flexiblePromisify(originalFn);
158+
159+
const result = await promisified(4, 5);
160+
161+
assert.strictEqual(result, 20);
162+
});
163+
164+
it('should preserve this context for callback functions', async () => {
165+
const obj = {
166+
multiplier: 3,
167+
fn(value, callback) {
168+
setTimeout(() => callback(null, value * this.multiplier), 10);
169+
}
170+
};
171+
const promisified = flexiblePromisify(obj.fn);
172+
173+
const result = await promisified.call(obj, 7);
174+
175+
assert.strictEqual(result, 21);
176+
});
177+
178+
it('should handle synchronous callback functions', async () => {
179+
const originalFn = (value, callback) => {
180+
callback(null, value.toUpperCase());
181+
};
182+
const promisified = flexiblePromisify(originalFn);
183+
184+
const result = await promisified('hello');
185+
186+
assert.strictEqual(result, 'HELLO');
187+
});
188+
189+
it('should handle synchronous callback functions with errors', async () => {
190+
const error = new Error('sync error');
191+
const originalFn = callback => {
192+
callback(error);
193+
};
194+
const promisified = flexiblePromisify(originalFn);
195+
196+
await assert.rejects(promisified(), error);
197+
});
198+
199+
it('should handle functions that return non-thenable objects', async () => {
200+
const originalFn = callback => {
201+
callback(null, 'success');
202+
return { notAPromise: true };
203+
};
204+
const promisified = flexiblePromisify(originalFn);
205+
206+
const result = await promisified();
207+
208+
assert.strictEqual(result, 'success');
209+
});
210+
211+
it('should handle functions that throw synchronously', async () => {
212+
const error = new Error('sync throw');
213+
const originalFn = () => {
214+
throw error;
215+
};
216+
const promisified = flexiblePromisify(originalFn);
217+
218+
await assert.rejects(promisified(), error);
219+
});
220+
221+
it('should handle callback functions that call callback multiple times', async () => {
222+
const callbackSpy = sinon.spy();
223+
const originalFn = callback => {
224+
callbackSpy();
225+
callback(null, 'first call');
226+
setTimeout(() => {
227+
callbackSpy();
228+
callback(null, 'second call');
229+
}, 10);
230+
};
231+
const promisified = flexiblePromisify(originalFn);
232+
233+
const result = await promisified();
234+
235+
assert.strictEqual(result, 'first call');
236+
// Wait to see if second callback was called
237+
await scheduler.wait(20);
238+
assert.strictEqual(callbackSpy.callCount, 2);
239+
});
240+
});
241+
242+
describe('Integration with Node.js util.promisify', () => {
243+
it('should work similarly to util.promisify for callback functions', async () => {
244+
const { promisify } = require('util');
245+
const originalFn = (value, callback) => {
246+
setTimeout(() => callback(null, value * 2), 10);
247+
};
248+
249+
const utilPromisified = promisify(originalFn);
250+
const flexiblePromisified = flexiblePromisify(originalFn);
251+
252+
const [utilResult, flexibleResult] = await Promise.all([
253+
utilPromisified(5),
254+
flexiblePromisified(5)
255+
]);
256+
257+
assert.strictEqual(utilResult, flexibleResult);
258+
assert.strictEqual(utilResult, 10);
259+
});
260+
261+
it('should respect custom promisify symbol like util.promisify', () => {
262+
const { promisify } = require('util');
263+
const customPromisified = () => Promise.resolve('custom');
264+
const originalFn = () => {};
265+
originalFn[Symbol.for('nodejs.util.promisify.custom')] = customPromisified;
266+
267+
const utilResult = promisify(originalFn);
268+
const flexibleResult = flexiblePromisify(originalFn);
269+
270+
assert.strictEqual(utilResult, customPromisified);
271+
assert.strictEqual(flexibleResult, customPromisified);
272+
});
273+
});
274+
});

0 commit comments

Comments
 (0)