Skip to content

Commit d744844

Browse files
committed
test: cover syncService.getStoreData callback await semantics
Adds unit-style coverage for the getStoreData callback contract: - async success callback is awaited before getStoreData() resolves - a rejecting async callback is routed through onFail rather than becoming an unhandled promise rejection - synchronous callbacks continue to work unchanged Stubs Simulator.findAll so the suite runs in milliseconds without touching the test database.
1 parent 4a2813d commit d744844

1 file changed

Lines changed: 85 additions & 0 deletions

File tree

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
/**
2+
* Unit tests for syncService.getStoreData callback semantics.
3+
*
4+
* Pins the contract that the success callback is awaited before
5+
* getStoreData() resolves, and that a rejecting callback is routed
6+
* through the onFail handler rather than becoming an unhandled
7+
* promise rejection. Stubs Simulator.findAll to bypass the test
8+
* database and keep these tests in the millisecond range.
9+
*/
10+
11+
import { expect } from 'chai';
12+
import sinon from 'sinon';
13+
14+
// Import datalayer index first to resolve circular dependency in the
15+
// correct order, matching the pattern used by other unit-style specs.
16+
import '../../../src/datalayer/index.js';
17+
import { Simulator } from '../../../src/models/index.js';
18+
import syncService from '../../../src/datalayer/syncService.js';
19+
20+
describe('syncService.getStoreData - callback semantics', function () {
21+
this.timeout(10000);
22+
23+
const TEST_STORE_ID = 'abc123';
24+
// hex-encoded 'k' / 'v' so decodeDataLayerResponse produces predictable output
25+
const HEX_K = Buffer.from('k').toString('hex');
26+
const HEX_V = Buffer.from('v').toString('hex');
27+
28+
beforeEach(function () {
29+
sinon.stub(Simulator, 'findAll').resolves([
30+
{ key: `${TEST_STORE_ID}_${HEX_K}`, value: HEX_V },
31+
]);
32+
});
33+
34+
afterEach(function () {
35+
sinon.restore();
36+
});
37+
38+
it('awaits an async success callback before resolving', async function () {
39+
const events = [];
40+
41+
const callback = async () => {
42+
events.push('callback-start');
43+
await new Promise((resolve) => setTimeout(resolve, 25));
44+
events.push('callback-end');
45+
};
46+
47+
const onFail = sinon.spy();
48+
49+
await syncService.getStoreData(TEST_STORE_ID, callback, onFail);
50+
events.push('getStoreData-returned');
51+
52+
// Without `await callback(...)`, the order would be
53+
// ['callback-start', 'getStoreData-returned', 'callback-end'].
54+
expect(events).to.deep.equal([
55+
'callback-start',
56+
'callback-end',
57+
'getStoreData-returned',
58+
]);
59+
expect(onFail.called).to.be.false;
60+
});
61+
62+
it('routes a rejecting async callback through onFail', async function () {
63+
const onFail = sinon.spy();
64+
65+
const callback = async () => {
66+
throw new Error('callback failed');
67+
};
68+
69+
await syncService.getStoreData(TEST_STORE_ID, callback, onFail);
70+
71+
expect(onFail.calledOnce).to.be.true;
72+
expect(onFail.firstCall.args[0]).to.equal('callback failed');
73+
});
74+
75+
it('still works with a synchronous callback', async function () {
76+
const callback = sinon.spy();
77+
const onFail = sinon.spy();
78+
79+
await syncService.getStoreData(TEST_STORE_ID, callback, onFail);
80+
81+
expect(callback.calledOnce).to.be.true;
82+
expect(callback.firstCall.args[0]).to.deep.equal([{ key: 'k', value: 'v' }]);
83+
expect(onFail.called).to.be.false;
84+
});
85+
});

0 commit comments

Comments
 (0)