Skip to content

Commit 76e998a

Browse files
committed
Fix tests
1 parent e58cbf4 commit 76e998a

File tree

4 files changed

+76
-45
lines changed

4 files changed

+76
-45
lines changed

src/update-feeds/dapi-data-registry.ts

+9-4
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ export const getDapiDataRegistry = (address: string, provider: ethers.providers.
99
export const verifyMulticallResponse = (
1010
response: Awaited<ReturnType<DapiDataRegistry['callStatic']['tryMulticall']>>
1111
) => {
12-
const [successes, returndata] = response;
12+
const { successes, returndata } = response;
1313

1414
if (!successes.every(Boolean)) throw new Error('One of the multicalls failed');
1515
return returndata;
@@ -19,7 +19,6 @@ export const decodeDapisCountResponse = (dapiDataRegistry: DapiDataRegistry, dap
1919
const dapisCount = dapiDataRegistry.interface.decodeFunctionResult('dapisCount', dapisCountReturndata)[0] as Awaited<
2020
ReturnType<DapiDataRegistry['dapisCount']>
2121
>;
22-
2322
return dapisCount.toNumber();
2423
};
2524

@@ -36,10 +35,16 @@ export const decodeReadDapiWithIndexResponse = (
3635

3736
// Ethers responses are returned as a combination of array and object. When such object is logged, only the array part
3837
// is logged. To make the logs more readable, we convert the object part to a plain object.
38+
const { deviationReference, deviationThresholdInPercentage, heartbeatInterval } = updateParameters;
39+
const { value, timestamp } = dataFeedValue;
3940
return {
4041
dapiName,
41-
updateParameters,
42-
dataFeedValue,
42+
updateParameters: {
43+
deviationReference,
44+
deviationThresholdInPercentage,
45+
heartbeatInterval,
46+
},
47+
dataFeedValue: { value, timestamp },
4348
dataFeed,
4449
signedApiUrls,
4550
};

src/update-feeds/update-feeds.test.ts

+39-27
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import { ethers } from 'ethers';
22

3-
import { generateMockDapiDataRegistry, generateReadDapisResponse } from '../../test/fixtures/dapi-data-registry';
3+
import {
4+
generateMockDapiDataRegistry,
5+
generateReadDapiWithIndexResponse,
6+
} from '../../test/fixtures/dapi-data-registry';
47
import { allowPartial } from '../../test/utils';
58
import type { DapiDataRegistry } from '../../typechain-types';
69
import type { Chain } from '../config/schema';
@@ -38,7 +41,7 @@ describe(startUpdateFeedLoops.name, () => {
3841

3942
// Expect the intervals to be called with the correct stagger time.
4043
expect(setInterval).toHaveBeenCalledTimes(2);
41-
expect(intervalCalls[1]! - intervalCalls[0]!).toBeGreaterThanOrEqual(40); // Reserving 10s as the buffer for computing stagger time.
44+
expect(intervalCalls[1]! - intervalCalls[0]!).toBeGreaterThanOrEqual(40); // Reserving 10ms as the buffer for computing stagger time.
4245

4346
// Expect the logs to be called with the correct context.
4447
expect(logger.debug).toHaveBeenCalledTimes(3);
@@ -92,21 +95,21 @@ describe(startUpdateFeedLoops.name, () => {
9295

9396
// Expect the logs to be called with the correct context.
9497
expect(logger.debug).toHaveBeenCalledTimes(4);
95-
expect(logger.debug).toHaveBeenCalledWith('Starting update loops for chain', {
98+
expect(logger.debug).toHaveBeenNthCalledWith(1, 'Starting update loops for chain', {
9699
chainId: '123',
97100
staggerTime: 100,
98101
providerNames: ['first-provider'],
99102
});
100-
expect(logger.debug).toHaveBeenCalledWith('Starting update loops for chain', {
103+
expect(logger.debug).toHaveBeenNthCalledWith(2, 'Starting update feed loop', {
104+
chainId: '123',
105+
providerName: 'first-provider',
106+
});
107+
expect(logger.debug).toHaveBeenNthCalledWith(3, 'Starting update loops for chain', {
101108
chainId: '456',
102109
staggerTime: 100,
103110
providerNames: ['another-provider'],
104111
});
105-
expect(logger.debug).toHaveBeenCalledWith('Starting update feed loop', {
106-
chainId: '123',
107-
providerName: 'first-provider',
108-
});
109-
expect(logger.debug).toHaveBeenCalledWith('Starting update feed loop', {
112+
expect(logger.debug).toHaveBeenNthCalledWith(4, 'Starting update feed loop', {
110113
chainId: '456',
111114
providerName: 'another-provider',
112115
});
@@ -119,7 +122,7 @@ describe(runUpdateFeed.name, () => {
119122
jest
120123
.spyOn(dapiDataRegistryModule, 'getDapiDataRegistry')
121124
.mockReturnValue(dapiDataRegistry as unknown as DapiDataRegistry);
122-
dapiDataRegistry.readDapis.mockRejectedValueOnce(new Error('provider-error'));
125+
dapiDataRegistry.callStatic.tryMulticall.mockRejectedValueOnce(new Error('provider-error'));
123126
jest.spyOn(logger, 'error');
124127

125128
await runUpdateFeed(
@@ -145,16 +148,19 @@ describe(runUpdateFeed.name, () => {
145148

146149
it('fetches other batches in a staggered way and logs errors', async () => {
147150
// Prepare the mocked contract so it returns three batches (of size 1) of dAPIs and the second batch fails to load.
148-
const firstBatch = generateReadDapisResponse();
149-
const thirdBatch = generateReadDapisResponse();
151+
const firstBatch = generateReadDapiWithIndexResponse();
152+
const thirdBatch = generateReadDapiWithIndexResponse();
150153
const dapiDataRegistry = generateMockDapiDataRegistry();
151154
jest
152155
.spyOn(dapiDataRegistryModule, 'getDapiDataRegistry')
153156
.mockReturnValue(dapiDataRegistry as unknown as DapiDataRegistry);
154-
dapiDataRegistry.readDapis.mockResolvedValueOnce(firstBatch);
155-
dapiDataRegistry.readDapis.mockRejectedValueOnce(new Error('provider-error'));
156-
dapiDataRegistry.readDapis.mockResolvedValueOnce(thirdBatch);
157-
dapiDataRegistry.dapisCount.mockResolvedValueOnce(ethers.BigNumber.from(3));
157+
dapiDataRegistry.interface.decodeFunctionResult.mockImplementation((_fn, value) => value);
158+
dapiDataRegistry.callStatic.tryMulticall.mockResolvedValueOnce({
159+
successes: [true, true],
160+
returndata: [[ethers.BigNumber.from(3)], firstBatch],
161+
});
162+
dapiDataRegistry.callStatic.tryMulticall.mockResolvedValueOnce({ successes: [false], returndata: [] });
163+
dapiDataRegistry.callStatic.tryMulticall.mockResolvedValueOnce({ successes: [true], returndata: [thirdBatch] });
158164
const sleepCalls = [] as number[];
159165
const originalSleep = utilsModule.sleep;
160166
jest.spyOn(utilsModule, 'sleep').mockImplementation(async (ms) => {
@@ -179,36 +185,42 @@ describe(runUpdateFeed.name, () => {
179185

180186
// Expect the contract to fetch the batches to be called with the correct stagger time.
181187
expect(utilsModule.sleep).toHaveBeenCalledTimes(3);
182-
expect(sleepCalls[0]).toBeGreaterThanOrEqual(40); // Reserving 10s as the buffer for computing stagger time.
188+
expect(sleepCalls[0]).toBeGreaterThanOrEqual(40); // Reserving 10ms as the buffer for computing stagger time.
183189
expect(sleepCalls[1]).toBeGreaterThanOrEqual(0);
184190
expect(sleepCalls[2]).toBe(49.999_999_999_999_99); // Stagger time is actually 150 / 3 = 50, but there is an rounding error.
185191

186192
// Expect the logs to be called with the correct context.
187193
expect(logger.error).toHaveBeenCalledTimes(1);
188-
expect(logger.error).toHaveBeenCalledWith('Failed to get active dAPIs batch', new Error('provider-error'), {
189-
chainId: '123',
190-
providerName: 'provider-name',
191-
});
192-
expect(logger.debug).toHaveBeenCalledTimes(4);
193-
expect(logger.debug).toHaveBeenCalledWith('Fetching first batch of dAPIs batches', {
194+
expect(logger.error).toHaveBeenCalledWith(
195+
'Failed to get active dAPIs batch',
196+
new Error('One of the multicalls failed'),
197+
{
198+
chainId: '123',
199+
providerName: 'provider-name',
200+
}
201+
);
202+
expect(logger.debug).toHaveBeenCalledTimes(6);
203+
expect(logger.debug).toHaveBeenNthCalledWith(1, 'Fetching first batch of dAPIs batches', {
194204
chainId: '123',
195205
providerName: 'provider-name',
196206
});
197-
expect(logger.debug).toHaveBeenCalledWith('Fetching batches of active dAPIs', {
207+
expect(logger.debug).toHaveBeenNthCalledWith(2, 'Processing batch of active dAPIs', expect.anything());
208+
expect(logger.debug).toHaveBeenNthCalledWith(3, 'Fetching batches of active dAPIs', {
198209
batchesCount: 3,
199-
staggerTime: 49.999_999_999_999_99,
200210
chainId: '123',
201211
providerName: 'provider-name',
212+
staggerTime: 49.999_999_999_999_99,
202213
});
203-
expect(logger.debug).toHaveBeenCalledWith('Fetching batch of active dAPIs', {
214+
expect(logger.debug).toHaveBeenNthCalledWith(4, 'Fetching batch of active dAPIs', {
204215
batchIndex: 1,
205216
chainId: '123',
206217
providerName: 'provider-name',
207218
});
208-
expect(logger.debug).toHaveBeenCalledWith('Fetching batch of active dAPIs', {
219+
expect(logger.debug).toHaveBeenNthCalledWith(5, 'Fetching batch of active dAPIs', {
209220
batchIndex: 2,
210221
chainId: '123',
211222
providerName: 'provider-name',
212223
});
224+
expect(logger.debug).toHaveBeenNthCalledWith(6, 'Processing batch of active dAPIs', expect.anything());
213225
});
214226
});

test/fixtures/dapi-data-registry.ts

+27-13
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,35 @@
1+
import { ethers } from 'ethers';
2+
3+
import type { ReadDapiWithIndexResponse } from '../../src/update-feeds/dapi-data-registry';
14
import type { DapiDataRegistry } from '../../typechain-types';
5+
import type { DeepPartial } from '../utils';
26

3-
export const generateReadDapisResponse = () => [
4-
{
5-
totalCount: 1,
6-
dapiNames: ['MOCK_FEED'],
7-
dataFeedIds: ['0xebba8507d616ed80766292d200a3598fdba656d9938cecc392765d4a284a69a4'],
8-
updateParameters: [{ deviationThresholdInPercentage: 0.5, deviationReference: 0.5, heartbeatInterval: 100 }],
9-
// NOTE: We will need to decode this from the contract, because it will store the template IDs as encoded bytes.
10-
dataFeedTemplateIds: [['0xcc35bd1800c06c12856a87311dd95bfcbb3add875844021d59a929d79f3c99bd']],
11-
signedApiUrls: [['http://localhost:8080']],
12-
airnodeAddresses: ['0xbF3137b0a7574563a23a8fC8badC6537F98197CC'],
7+
export const generateReadDapiWithIndexResponse = (): ReadDapiWithIndexResponse => ({
8+
dapiName: 'MOCK_FEED',
9+
updateParameters: {
10+
deviationThresholdInPercentage: ethers.BigNumber.from(0.5 * 1e8),
11+
deviationReference: ethers.BigNumber.from(0.5 * 1e8),
12+
heartbeatInterval: 100,
13+
},
14+
dataFeedValue: {
15+
value: ethers.BigNumber.from(123 * 1e6),
16+
timestamp: 1_629_811_200,
1317
},
14-
];
18+
dataFeed: '0xebba8507d616ed80766292d200a3598fdba656d9938cecc392765d4a284a69a4',
19+
signedApiUrls: ['http://localhost:8080'],
20+
});
1521

1622
export const generateMockDapiDataRegistry = () => {
1723
return {
18-
readDapis: jest.fn(),
24+
interface: {
25+
encodeFunctionData: jest.fn(),
26+
decodeFunctionResult: jest.fn(),
27+
},
28+
callStatic: {
29+
tryMulticall: jest.fn(),
30+
},
31+
tryMulticall: jest.fn(),
32+
readDapiWithIndex: jest.fn(),
1933
dapisCount: jest.fn(),
20-
} satisfies Partial<DapiDataRegistry>;
34+
} satisfies DeepPartial<DapiDataRegistry>;
2135
};

test/utils.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ export const signData = async (signer: ethers.Signer, templateId: string, timest
99

1010
export const generateRandomBytes32 = () => ethers.utils.hexlify(ethers.utils.randomBytes(32));
1111

12-
type DeepPartial<T> = T extends object
12+
export type DeepPartial<T> = T extends object
1313
? {
1414
[P in keyof T]?: DeepPartial<T[P]>;
1515
}

0 commit comments

Comments
 (0)