Skip to content

Commit 033a9f4

Browse files
committed
extended tests for ipinfo module
1 parent 2fbd668 commit 033a9f4

File tree

4 files changed

+198
-11
lines changed

4 files changed

+198
-11
lines changed

src/modules/ipinfo/IPInfoDB.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@ export class IPInfoDB extends FaucetModuleDB {
4545
) as {Json: string};
4646
if(!row)
4747
return null;
48-
4948
return JSON.parse(row.Json);
5049
}
5150

@@ -58,8 +57,7 @@ export class IPInfoDB extends FaucetModuleDB {
5857

5958
if(row) {
6059
await this.db.run("UPDATE IPInfoCache SET Json = ?, Timeout = ? WHERE IP = ?", [infoJson, timeout, ip.toLowerCase()]);
61-
}
62-
else {
60+
} else {
6361
await this.db.run("INSERT INTO IPInfoCache (IP, Json, Timeout) VALUES (?, ?, ?)", [ip.toLowerCase(), infoJson, timeout]);
6462
}
6563
}

src/modules/ipinfo/IPInfoModule.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ export class IPInfoModule extends BaseModule<IIPInfoConfig> {
2929
private ipInfoResolver: IPInfoResolver;
3030
private ipInfoMatchRestrictions: [pattern: string, restriction: number | IIPInfoRestrictionConfig][];
3131
private ipInfoMatchRestrictionsRefresh: number;
32+
private sessionRewardFactorCacheTimeout: number = 30;
3233

3334
protected override async startModule(): Promise<void> {
3435
this.ipInfoDb = await ServiceManager.GetService(FaucetDatabase).createModuleDb(IPInfoDB, this);
@@ -49,6 +50,7 @@ export class IPInfoModule extends BaseModule<IIPInfoConfig> {
4950

5051
protected override stopModule(): Promise<void> {
5152
this.ipInfoDb.dispose();
53+
this.ipInfoResolver.dispose();
5254
return Promise.resolve();
5355
}
5456

@@ -67,9 +69,9 @@ export class IPInfoModule extends BaseModule<IIPInfoConfig> {
6769
if(ipInfo.status !== "success" && this.moduleConfig.required)
6870
throw new FaucetError("INVALID_IPINFO", "Error while checking your IP: " + ipInfo.status);
6971
} catch(ex) {
72+
ServiceManager.GetService(FaucetProcess).emitLog(FaucetLogLevel.WARNING, "Error while fetching IP-Info for " + remoteIp + ": " + ex.toString());
7073
if(this.moduleConfig.required)
7174
throw new FaucetError("INVALID_IPINFO", "Error while checking your IP: " + ex.toString());
72-
ServiceManager.GetService(FaucetProcess).emitLog(FaucetLogLevel.WARNING, "Error while fetching IP-Info for " + remoteIp + ": " + ex.toString());
7375
}
7476
session.setSessionData("ipinfo.data", ipInfo);
7577

@@ -87,7 +89,7 @@ export class IPInfoModule extends BaseModule<IIPInfoConfig> {
8789
let refreshTime = session.getSessionModuleRef("ipinfo.restriction.time") || 0;
8890
let now = Math.floor((new Date()).getTime() / 1000);
8991
let sessionRestriction: IIPInfoRestriction;
90-
if(now - refreshTime > 30) {
92+
if(now - refreshTime > this.sessionRewardFactorCacheTimeout) {
9193
sessionRestriction = this.getSessionRestriction(session);
9294
session.setSessionModuleRef("ipinfo.restriction.time", Math.floor((new Date()).getTime() / 1000));
9395
session.setSessionModuleRef("ipinfo.restriction.data", sessionRestriction);

src/modules/ipinfo/IPInfoResolver.ts

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,20 @@ export class IPInfoResolver {
2424
private ipInfoDb: IPInfoDB;
2525
private ipInfoApi: string;
2626
private ipInfoCache: {[ip: string]: [number, Promise<IIPInfo>]} = {};
27+
private ipInfoCacheCleanupTimer: NodeJS.Timeout;
28+
private ipInfoCacheCleanupInterval: number = 20 * 1000;
29+
private ipInfoCacheCleanupTimeout: number = 6 * 60 * 60;
2730

2831
public constructor(ipInfoDb: IPInfoDB, api: string) {
2932
this.ipInfoDb = ipInfoDb;
3033
this.ipInfoApi = api;
31-
setInterval(() => {
34+
this.ipInfoCacheCleanupTimer = setInterval(() => {
3235
this.cleanIpInfoCache();
33-
}, 20 * 1000);
36+
}, this.ipInfoCacheCleanupInterval);
37+
}
38+
39+
public dispose() {
40+
clearInterval(this.ipInfoCacheCleanupTimer);
3441
}
3542

3643
public setApi(api: string) {
@@ -40,9 +47,9 @@ export class IPInfoResolver {
4047
public async getIpInfo(ipAddr: string): Promise<IIPInfo> {
4148
let cachedIpInfo = await this.ipInfoDb.getIPInfo(ipAddr);
4249
if(cachedIpInfo)
43-
return Promise.resolve(cachedIpInfo);
50+
return cachedIpInfo;
4451
if(this.ipInfoCache.hasOwnProperty(ipAddr))
45-
return this.ipInfoCache[ipAddr][1];
52+
return await this.ipInfoCache[ipAddr][1];
4653

4754
let ipApiUrl = this.ipInfoApi.replace(/{ip}/, ipAddr);
4855
let promise = FetchUtil.fetch(ipApiUrl)
@@ -88,7 +95,7 @@ export class IPInfoResolver {
8895
private cleanIpInfoCache() {
8996
let now = Math.floor((new Date()).getTime() / 1000);
9097
Object.keys(this.ipInfoCache).forEach((ipAddr) => {
91-
if(now - this.ipInfoCache[ipAddr][0] > 6 * 60 * 60) {
98+
if(now - this.ipInfoCache[ipAddr][0] > this.ipInfoCacheCleanupTimeout) {
9299
delete this.ipInfoCache[ipAddr];
93100
}
94101
});

tests/modules/IpInfoModule.spec.ts

Lines changed: 181 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,15 @@ import { bindTestStubs, unbindTestStubs, loadDefaultTestConfig, returnDelayedPro
1010
import { FetchUtil } from '../../src/utils/FetchUtil.js';
1111
import { ServiceManager } from '../../src/common/ServiceManager.js';
1212
import { FaucetDatabase } from '../../src/db/FaucetDatabase.js';
13-
import { ModuleManager } from '../../src/modules/ModuleManager.js';
13+
import { ModuleHookAction, ModuleManager } from '../../src/modules/ModuleManager.js';
1414
import { SessionManager } from '../../src/session/SessionManager.js';
1515
import { faucetConfig } from '../../src/config/FaucetConfig.js';
1616
import { FaucetError } from '../../src/common/FaucetError.js';
1717
import { IIPInfoConfig } from '../../src/modules/ipinfo/IPInfoConfig.js';
18+
import { FaucetSession } from '../../src/session/FaucetSession.js';
19+
import { IPInfoModule } from '../../src/modules/ipinfo/IPInfoModule.js';
20+
import { sleepPromise } from '../../src/utils/PromiseUtils.js';
21+
import { FaucetProcess } from '../../src/common/FaucetProcess.js';
1822

1923

2024
describe("Faucet module: ipinfo", () => {
@@ -138,6 +142,111 @@ describe("Faucet module: ipinfo", () => {
138142
expect(error?.getCode()).to.equal("INVALID_IPINFO", "unexpected error code");
139143
});
140144

145+
it("Start session from blocked IP", async () => {
146+
faucetConfig.modules["ipinfo"] = {
147+
enabled: true,
148+
apiUrl: "http://test-api-info-check.com/{ip}",
149+
cacheTime: 86400,
150+
required: true,
151+
restrictions: {
152+
hosting: 50,
153+
US: {
154+
reward: 1,
155+
blocked: true,
156+
},
157+
},
158+
restrictionsPattern: {},
159+
restrictionsFile: null,
160+
} as IIPInfoConfig;
161+
globalStubs["fetch"].returns(returnDelayedPromise(true, {
162+
json: () => Promise.resolve(testIPInfoResponse)
163+
}));
164+
await ServiceManager.GetService(ModuleManager).initialize();
165+
let sessionManager = ServiceManager.GetService(SessionManager);
166+
let error: FaucetError | null = null;
167+
try {
168+
await sessionManager.createSession("::ffff:8.8.8.8", {
169+
addr: "0x0000000000000000000000000000000000001337",
170+
});
171+
} catch(ex) {
172+
error = ex;
173+
}
174+
expect(error).to.not.equal(null, "no exception thrown");
175+
expect(error instanceof FaucetError).to.equal(true, "unexpected error type");
176+
expect(error?.getCode()).to.equal("IPINFO_RESTRICTION", "unexpected error code");
177+
});
178+
179+
it("check ipinfo caching (no double request for same IP)", async () => {
180+
faucetConfig.modules["ipinfo"] = {
181+
enabled: true,
182+
apiUrl: "http://test-api-info-check.com/{ip}",
183+
cacheTime: 86400,
184+
required: true,
185+
restrictions: {
186+
hosting: 100,
187+
proxy: 50,
188+
DE: 50,
189+
},
190+
restrictionsPattern: {},
191+
restrictionsFile: null,
192+
} as IIPInfoConfig;
193+
globalStubs["fetch"].returns(returnDelayedPromise(true, {
194+
json: () => Promise.resolve(testIPInfoResponse)
195+
}));
196+
await ServiceManager.GetService(ModuleManager).initialize();
197+
let sessionManager = ServiceManager.GetService(SessionManager);
198+
let testSession1 = await sessionManager.createSession("::ffff:8.8.8.8", {
199+
addr: "0x0000000000000000000000000000000000001337",
200+
});
201+
expect(testSession1.getSessionStatus()).to.equal("claimable", "unexpected session 1 status");
202+
expect(globalStubs["fetch"].callCount).to.equal(1, "unexpected fetch call count after session 2 start");
203+
await sleepPromise(50);
204+
let testSession2 = await sessionManager.createSession("::ffff:8.8.8.8", {
205+
addr: "0x0000000000000000000000000000000000001338",
206+
});
207+
expect(testSession2.getSessionStatus()).to.equal("claimable", "unexpected session 2 status");
208+
expect(globalStubs["fetch"].callCount).to.equal(1, "unexpected fetch call count after session 2 start");
209+
});
210+
211+
it("check ipinfo caching (cache timeout)", async () => {
212+
faucetConfig.modules["ipinfo"] = {
213+
enabled: true,
214+
apiUrl: "http://test-api-info-check.com/{ip}",
215+
cacheTime: 86400,
216+
required: true,
217+
restrictions: {
218+
hosting: 100,
219+
proxy: 50,
220+
DE: 50,
221+
},
222+
restrictionsPattern: {},
223+
restrictionsFile: null,
224+
} as IIPInfoConfig;
225+
globalStubs["fetch"].returns(returnDelayedPromise(true, {
226+
json: () => Promise.resolve(testIPInfoResponse)
227+
}));
228+
await ServiceManager.GetService(ModuleManager).initialize();
229+
let sessionManager = ServiceManager.GetService(SessionManager);
230+
let testSession1 = await sessionManager.createSession("::ffff:8.8.8.8", {
231+
addr: "0x0000000000000000000000000000000000001337",
232+
});
233+
expect(testSession1.getSessionStatus()).to.equal("claimable", "unexpected session 1 status");
234+
expect(globalStubs["fetch"].callCount).to.equal(1, "unexpected fetch call count after session 2 start");
235+
236+
let ipinfoModule = ServiceManager.GetService(ModuleManager).getModule<any>("ipinfo");
237+
ipinfoModule.ipInfoResolver.ipInfoCacheCleanupTimeout = 0;
238+
await ipinfoModule.ipInfoDb.cleanStore();
239+
ipinfoModule.ipInfoDb.now = () => { return Math.floor((new Date()).getTime() / 1000) + 86405; };
240+
await sleepPromise(1000);
241+
ipinfoModule.ipInfoResolver.cleanIpInfoCache();
242+
243+
let testSession2 = await sessionManager.createSession("::ffff:8.8.8.8", {
244+
addr: "0x0000000000000000000000000000000000001338",
245+
});
246+
expect(testSession2.getSessionStatus()).to.equal("claimable", "unexpected session 2 status");
247+
expect(globalStubs["fetch"].callCount).to.equal(2, "unexpected fetch call count after session 2 start");
248+
});
249+
141250
it("check ipinfo based restriction (no restriction)", async () => {
142251
faucetConfig.modules["ipinfo"] = {
143252
enabled: true,
@@ -279,4 +388,75 @@ describe("Faucet module: ipinfo", () => {
279388
expect(testSession.getDropAmount()).to.equal(50n, "unexpected drop amount");
280389
});
281390

391+
it("check refreshed restrictions on running session (block session)", async () => {
392+
faucetConfig.modules["ipinfo"] = {
393+
enabled: true,
394+
apiUrl: "http://test-api-info-check.com/{ip}",
395+
cacheTime: 86400,
396+
required: false,
397+
restrictions: {
398+
},
399+
restrictionsPattern: {},
400+
restrictionsFile: null,
401+
} as IIPInfoConfig;
402+
globalStubs["fetch"].returns(returnDelayedPromise(true, {
403+
json: () => Promise.resolve(testIPInfoResponse)
404+
}));
405+
await ServiceManager.GetService(ModuleManager).initialize();
406+
ServiceManager.GetService(ModuleManager).addActionHook(null, ModuleHookAction.SessionStart, 100, "test-task", (session: FaucetSession, userInput: any) => {
407+
session.addBlockingTask("test", "test1", 1);
408+
});
409+
ServiceManager.GetService(ModuleManager).getModule<any>("ipinfo").sessionRewardFactorCacheTimeout = 0;
410+
let sessionManager = ServiceManager.GetService(SessionManager);
411+
let testSession = await sessionManager.createSession("::ffff:8.8.8.8", {
412+
addr: "0x0000000000000000000000000000000000001337",
413+
});
414+
expect(testSession.getSessionStatus()).to.equal("running", "unexpected session status before restriction update");
415+
await testSession.addReward(50n);
416+
(faucetConfig.modules["ipinfo"] as any).restrictions["US"] = {
417+
blocked: true,
418+
};
419+
ServiceManager.GetService(FaucetProcess).emit("reload");
420+
await sleepPromise(1000);
421+
await testSession.addReward(10n);
422+
await sleepPromise(100);
423+
expect(testSession.getSessionStatus()).to.equal("claimable", "unexpected session status after restriction update");
424+
expect(testSession.getDropAmount()).to.equal(60n, "unexpected drop amount");
425+
});
426+
427+
it("check refreshed restrictions on running session (kill session)", async () => {
428+
faucetConfig.modules["ipinfo"] = {
429+
enabled: true,
430+
apiUrl: "http://test-api-info-check.com/{ip}",
431+
cacheTime: 86400,
432+
required: false,
433+
restrictions: {
434+
},
435+
restrictionsPattern: {},
436+
restrictionsFile: null,
437+
} as IIPInfoConfig;
438+
globalStubs["fetch"].returns(returnDelayedPromise(true, {
439+
json: () => Promise.resolve(testIPInfoResponse)
440+
}));
441+
await ServiceManager.GetService(ModuleManager).initialize();
442+
ServiceManager.GetService(ModuleManager).addActionHook(null, ModuleHookAction.SessionStart, 100, "test-task", (session: FaucetSession, userInput: any) => {
443+
session.addBlockingTask("test", "test1", 1);
444+
});
445+
ServiceManager.GetService(ModuleManager).getModule<any>("ipinfo").sessionRewardFactorCacheTimeout = 0;
446+
let sessionManager = ServiceManager.GetService(SessionManager);
447+
let testSession = await sessionManager.createSession("::ffff:8.8.8.8", {
448+
addr: "0x0000000000000000000000000000000000001337",
449+
});
450+
expect(testSession.getSessionStatus()).to.equal("running", "unexpected session status before restriction update");
451+
await testSession.addReward(50n);
452+
(faucetConfig.modules["ipinfo"] as any).restrictions["US"] = {
453+
blocked: "kill",
454+
message: "bye bye"
455+
};
456+
await sleepPromise(1000);
457+
await testSession.addReward(10n);
458+
await sleepPromise(100);
459+
expect(testSession.getSessionStatus()).to.equal("failed", "unexpected session status after restriction update");
460+
});
461+
282462
});

0 commit comments

Comments
 (0)