Skip to content

Commit 9c98c93

Browse files
authored
feat: adds additional test coverage to multiple files (#3920)
Signed-off-by: Simeon Nakov <[email protected]>
1 parent 0261cb5 commit 9c98c93

File tree

11 files changed

+1517
-124
lines changed

11 files changed

+1517
-124
lines changed

packages/relay/src/lib/services/ethService/ethFilterService/FilterService.ts

Lines changed: 148 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,49 @@ export class FilterService implements IFilterService {
6262
this.supportedTypes = [constants.FILTER.TYPE.LOG, constants.FILTER.TYPE.NEW_BLOCK];
6363
}
6464

65+
/**
66+
* Generates cache key for filter ID
67+
* @param filterId
68+
* @private
69+
*/
70+
private getCacheKey(filterId: string): string {
71+
return `${constants.CACHE_KEY.FILTERID}_${filterId}`;
72+
}
73+
74+
/**
75+
* Updates filter cache with new data
76+
* @param filterId
77+
* @param type
78+
* @param params
79+
* @param lastQueried
80+
* @param method
81+
* @param requestDetails
82+
* @private
83+
*/
84+
private async updateFilterCache(
85+
filterId: string,
86+
type: string,
87+
params: any,
88+
lastQueried: number | null,
89+
method: string,
90+
requestDetails: RequestDetails,
91+
): Promise<void> {
92+
const cacheKey = this.getCacheKey(filterId);
93+
await this.cacheService.set(cacheKey, { type, params, lastQueried }, method, requestDetails, constants.FILTER.TTL);
94+
}
95+
96+
/**
97+
* Retrieves filter from cache
98+
* @param filterId
99+
* @param method
100+
* @param requestDetails
101+
* @private
102+
*/
103+
private async getFilterFromCache(filterId: string, method: string, requestDetails: RequestDetails) {
104+
const cacheKey = this.getCacheKey(filterId);
105+
return await this.cacheService.getAsync(cacheKey, method, requestDetails);
106+
}
107+
65108
/**
66109
* Creates a new filter with the specified type and parameters
67110
* @param type
@@ -70,20 +113,12 @@ export class FilterService implements IFilterService {
70113
*/
71114
async createFilter(type: string, params: any, requestDetails: RequestDetails): Promise<string> {
72115
const filterId = generateRandomHex();
73-
const cacheKey = `${constants.CACHE_KEY.FILTERID}_${filterId}`;
74-
await this.cacheService.set(
75-
cacheKey,
76-
{
77-
type,
78-
params,
79-
lastQueried: null,
80-
},
81-
this.ethNewFilter,
82-
requestDetails,
83-
constants.FILTER.TTL,
84-
);
116+
await this.updateFilterCache(filterId, type, params, null, this.ethNewFilter, requestDetails);
117+
85118
if (this.logger.isLevelEnabled('trace')) {
86-
this.logger.trace(`${requestDetails.formattedRequestId} created filter with TYPE=${type}, params: ${params}`);
119+
this.logger.trace(
120+
`${requestDetails.formattedRequestId} created filter with TYPE=${type}, params: ${JSON.stringify(params)}`,
121+
);
87122
}
88123
return filterId;
89124
}
@@ -130,27 +165,24 @@ export class FilterService implements IFilterService {
130165
}
131166

132167
async newBlockFilter(requestDetails: RequestDetails): Promise<string> {
133-
try {
134-
FilterService.requireFiltersEnabled();
135-
return await this.createFilter(
136-
constants.FILTER.TYPE.NEW_BLOCK,
137-
{
138-
blockAtCreation: await this.common.getLatestBlockNumber(requestDetails),
139-
},
140-
requestDetails,
141-
);
142-
} catch (e) {
143-
throw this.common.genericErrorHandler(e);
144-
}
168+
FilterService.requireFiltersEnabled();
169+
170+
return await this.createFilter(
171+
constants.FILTER.TYPE.NEW_BLOCK,
172+
{
173+
blockAtCreation: await this.common.getLatestBlockNumber(requestDetails),
174+
},
175+
requestDetails,
176+
);
145177
}
146178

147179
public async uninstallFilter(filterId: string, requestDetails: RequestDetails): Promise<boolean> {
148180
FilterService.requireFiltersEnabled();
149181

150-
const cacheKey = `${constants.CACHE_KEY.FILTERID}_${filterId}`;
151-
const filter = await this.cacheService.getAsync(cacheKey, this.ethUninstallFilter, requestDetails);
182+
const filter = await this.getFilterFromCache(filterId, this.ethUninstallFilter, requestDetails);
152183

153184
if (filter) {
185+
const cacheKey = this.getCacheKey(filterId);
154186
await this.cacheService.delete(cacheKey, this.ethUninstallFilter, requestDetails);
155187
return true;
156188
}
@@ -165,9 +197,8 @@ export class FilterService implements IFilterService {
165197
public async getFilterLogs(filterId: string, requestDetails: RequestDetails): Promise<Log[]> {
166198
FilterService.requireFiltersEnabled();
167199

168-
const cacheKey = `${constants.CACHE_KEY.FILTERID}_${filterId}`;
169-
const filter = await this.cacheService.getAsync(cacheKey, this.ethGetFilterLogs, requestDetails);
170-
if (filter?.type != constants.FILTER.TYPE.LOG) {
200+
const filter = await this.getFilterFromCache(filterId, this.ethGetFilterLogs, requestDetails);
201+
if (filter?.type !== constants.FILTER.TYPE.LOG) {
171202
throw predefined.FILTER_NOT_FOUND;
172203
}
173204

@@ -181,82 +212,114 @@ export class FilterService implements IFilterService {
181212
);
182213

183214
// update filter to refresh TTL
184-
await this.cacheService.set(
185-
cacheKey,
186-
{
187-
type: filter.type,
188-
params: filter.params,
189-
lastQueried: filter.lastQueried,
190-
},
215+
await this.updateFilterCache(
216+
filterId,
217+
filter.type,
218+
filter.params,
219+
filter.lastQueried,
191220
this.ethGetFilterChanges,
192221
requestDetails,
193-
constants.FILTER.TTL,
194222
);
195223

196224
return logs;
197225
}
198226

227+
/**
228+
* Handles log filter changes
229+
* @param filter
230+
* @param requestDetails
231+
* @private
232+
*/
233+
private async handleLogFilterChanges(
234+
filter: any,
235+
requestDetails: RequestDetails,
236+
): Promise<{ result: Log[]; latestBlockNumber: number }> {
237+
const result = await this.common.getLogs(
238+
null,
239+
filter?.lastQueried || filter?.params.fromBlock,
240+
filter?.params.toBlock,
241+
filter?.params.address,
242+
filter?.params.topics,
243+
requestDetails,
244+
);
245+
246+
// get the latest block number and add 1 to exclude current results from the next response because
247+
// the mirror node query executes "gte" not "gt"
248+
const latestBlockNumber =
249+
Number(
250+
result.length ? result[result.length - 1].blockNumber : await this.common.getLatestBlockNumber(requestDetails),
251+
) + 1;
252+
253+
return { result, latestBlockNumber };
254+
}
255+
256+
/**
257+
* Handles new block filter changes
258+
* @param filter
259+
* @param requestDetails
260+
* @private
261+
*/
262+
private async handleNewBlockFilterChanges(
263+
filter: any,
264+
requestDetails: RequestDetails,
265+
): Promise<{ result: string[]; latestBlockNumber: number }> {
266+
const blockResponse = await this.mirrorNodeClient.getBlocks(
267+
requestDetails,
268+
[`gt:${filter.lastQueried || filter.params.blockAtCreation}`],
269+
undefined,
270+
{
271+
order: 'asc',
272+
},
273+
);
274+
275+
const latestBlockNumber = Number(
276+
blockResponse?.blocks?.length
277+
? blockResponse.blocks[blockResponse.blocks.length - 1].number
278+
: await this.common.getLatestBlockNumber(requestDetails),
279+
);
280+
281+
const result = blockResponse?.blocks?.map((r) => r.hash) || [];
282+
283+
return { result, latestBlockNumber };
284+
}
285+
199286
public async getFilterChanges(filterId: string, requestDetails: RequestDetails): Promise<string[] | Log[]> {
200287
FilterService.requireFiltersEnabled();
201288

202-
const cacheKey = `${constants.CACHE_KEY.FILTERID}_${filterId}`;
203-
const filter = await this.cacheService.getAsync(cacheKey, this.ethGetFilterChanges, requestDetails);
289+
const filter = await this.getFilterFromCache(filterId, this.ethGetFilterChanges, requestDetails);
204290

205291
if (!filter) {
206292
throw predefined.FILTER_NOT_FOUND;
207293
}
208294

209-
let result, latestBlockNumber;
210-
if (filter.type === constants.FILTER.TYPE.LOG) {
211-
result = await this.common.getLogs(
212-
null,
213-
filter?.lastQueried || filter?.params.fromBlock,
214-
filter?.params.toBlock,
215-
filter?.params.address,
216-
filter?.params.topics,
217-
requestDetails,
218-
);
295+
let result: string[] | Log[];
296+
let latestBlockNumber: number;
219297

220-
// get the latest block number and add 1 to exclude current results from the next response because
221-
// the mirror node query executes "gte" not "gt"
222-
latestBlockNumber =
223-
Number(
224-
result.length
225-
? result[result.length - 1].blockNumber
226-
: await this.common.getLatestBlockNumber(requestDetails),
227-
) + 1;
228-
} else if (filter.type === constants.FILTER.TYPE.NEW_BLOCK) {
229-
result = await this.mirrorNodeClient.getBlocks(
230-
requestDetails,
231-
[`gt:${filter.lastQueried || filter.params.blockAtCreation}`],
232-
undefined,
233-
{
234-
order: 'asc',
235-
},
236-
);
237-
238-
latestBlockNumber = Number(
239-
result?.blocks?.length
240-
? result.blocks[result.blocks.length - 1].number
241-
: await this.common.getLatestBlockNumber(requestDetails),
242-
);
243-
244-
result = result?.blocks?.map((r) => r.hash) || [];
245-
} else if (this.supportedTypes.indexOf(filter.type) === -1) {
246-
throw predefined.UNSUPPORTED_METHOD;
298+
switch (filter.type) {
299+
case constants.FILTER.TYPE.LOG: {
300+
const logResult = await this.handleLogFilterChanges(filter, requestDetails);
301+
result = logResult.result;
302+
latestBlockNumber = logResult.latestBlockNumber;
303+
break;
304+
}
305+
case constants.FILTER.TYPE.NEW_BLOCK: {
306+
const blockResult = await this.handleNewBlockFilterChanges(filter, requestDetails);
307+
result = blockResult.result;
308+
latestBlockNumber = blockResult.latestBlockNumber;
309+
break;
310+
}
311+
default:
312+
throw predefined.UNSUPPORTED_METHOD;
247313
}
248314

249315
// update filter to refresh TTL and set lastQueried block number
250-
await this.cacheService.set(
251-
cacheKey,
252-
{
253-
type: filter.type,
254-
params: filter.params,
255-
lastQueried: latestBlockNumber,
256-
},
316+
await this.updateFilterCache(
317+
filterId,
318+
filter.type,
319+
filter.params,
320+
latestBlockNumber,
257321
this.ethGetFilterChanges,
258322
requestDetails,
259-
constants.FILTER.TTL,
260323
);
261324

262325
return result;

packages/relay/tests/helpers.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { expect } from 'chai';
77
import crypto from 'crypto';
88
import { ethers } from 'ethers';
99
import { Logger } from 'pino';
10+
import * as sinon from 'sinon';
1011
import { v4 as uuid } from 'uuid';
1112

1213
import { ConfigServiceTestHelper } from '../../config-service/tests/configServiceTestHelper';
@@ -38,6 +39,23 @@ const expectUnsupportedMethod = (result) => {
3839
expect(result.message).to.be.equal('Unsupported JSON-RPC method');
3940
};
4041

42+
export const createMockRedisClient = (options: { connectRejects?: boolean; evalRejects?: boolean } = {}) => {
43+
const { connectRejects = false, evalRejects = false } = options;
44+
45+
const connectStub = connectRejects
46+
? sinon.stub().rejects(new Error('Redis connection failed'))
47+
: sinon.stub().resolves();
48+
49+
const evalStub = evalRejects ? sinon.stub().rejects(new Error('Redis operation failed')) : sinon.stub();
50+
51+
return {
52+
connect: connectStub,
53+
on: sinon.stub(),
54+
eval: evalStub,
55+
quit: sinon.stub(),
56+
};
57+
};
58+
4159
const expectedError = () => {
4260
expect(true).to.eq(false);
4361
};

0 commit comments

Comments
 (0)