Skip to content

Commit 7ff1f57

Browse files
committed
Update
1 parent 6c20c72 commit 7ff1f57

File tree

3 files changed

+174
-0
lines changed

3 files changed

+174
-0
lines changed

example/lookup-watch.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
const geoIp = require('../index.js');
2+
3+
geoIp.startWatchingDataUpdate(err => {
4+
if (err) {
5+
console.error('[geoip-lite2] GeoIP reload failed:', err);
6+
return;
7+
}
8+
console.log('[geoip-lite2] Reloaded GeoIP database');
9+
});
10+
11+
const ipv4 = '79.186.130.100';
12+
console.log(ipv4, geoIp.lookup(ipv4));
13+
14+
const ipv6 = '2a01:11bf:4222:900a:99ae:285f:7432:8f8e';
15+
console.log(ipv6, geoIp.lookup(ipv6));

test/fsWatcher.test.js

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
const { afterEach, beforeEach, describe, expect, it, jest: jestGlobals } = require('@jest/globals');
2+
const { join } = require('node:path');
3+
4+
describe('fsWatcher', () => {
5+
beforeEach(() => {
6+
jestGlobals.resetModules();
7+
jestGlobals.useFakeTimers();
8+
});
9+
10+
afterEach(() => {
11+
jestGlobals.useRealTimers();
12+
jestGlobals.restoreAllMocks();
13+
});
14+
15+
it('should debounce watch events and emit the latest change details', () => {
16+
let onWatchEvent;
17+
const watcher = {
18+
close: jestGlobals.fn(),
19+
on: jestGlobals.fn().mockReturnThis(),
20+
};
21+
const watch = jestGlobals.fn((_directory, callback) => {
22+
onWatchEvent = callback;
23+
return watcher;
24+
});
25+
26+
jestGlobals.doMock('node:fs', () => ({ watch }));
27+
const fsWatcher = require('../scripts/fsWatcher.js');
28+
const callback = jestGlobals.fn();
29+
30+
fsWatcher.makeFsWatchFilter('debounceWatcher', '/data', 200, callback);
31+
32+
onWatchEvent('rename', 'geoip-city.dat');
33+
onWatchEvent('change', 'geoip-country.dat');
34+
35+
jestGlobals.advanceTimersByTime(199);
36+
expect(callback).not.toHaveBeenCalled();
37+
38+
jestGlobals.advanceTimersByTime(1);
39+
expect(callback).toHaveBeenCalledTimes(1);
40+
expect(callback).toHaveBeenCalledWith({
41+
event: 'change',
42+
file: 'geoip-country.dat',
43+
path: join('/data', 'geoip-country.dat'),
44+
});
45+
});
46+
47+
it('should filter events to requested files', () => {
48+
let onWatchEvent;
49+
const watcher = {
50+
close: jestGlobals.fn(),
51+
on: jestGlobals.fn().mockReturnThis(),
52+
};
53+
const watch = jestGlobals.fn((_directory, callback) => {
54+
onWatchEvent = callback;
55+
return watcher;
56+
});
57+
58+
jestGlobals.doMock('node:fs', () => ({ watch }));
59+
const fsWatcher = require('../scripts/fsWatcher.js');
60+
const callback = jestGlobals.fn();
61+
62+
fsWatcher.makeFsWatchFilter('fileFilterWatcher', '/data', ['geoip-city.dat'], 100, callback);
63+
64+
onWatchEvent('rename', 'geoip-country.dat');
65+
jestGlobals.advanceTimersByTime(100);
66+
expect(callback).not.toHaveBeenCalled();
67+
68+
onWatchEvent('rename', 'geoip-city.dat');
69+
jestGlobals.advanceTimersByTime(100);
70+
expect(callback).toHaveBeenCalledTimes(1);
71+
});
72+
73+
it('should close previous watcher when re-registering the same watcher name', () => {
74+
let firstHandler;
75+
let secondHandler;
76+
const firstWatcher = {
77+
close: jestGlobals.fn(),
78+
on: jestGlobals.fn().mockReturnThis(),
79+
};
80+
const secondWatcher = {
81+
close: jestGlobals.fn(),
82+
on: jestGlobals.fn().mockReturnThis(),
83+
};
84+
let call = 0;
85+
const watch = jestGlobals.fn((_directory, callback) => {
86+
call++;
87+
if (call === 1) {
88+
firstHandler = callback;
89+
return firstWatcher;
90+
}
91+
secondHandler = callback;
92+
return secondWatcher;
93+
});
94+
95+
jestGlobals.doMock('node:fs', () => ({ watch }));
96+
const fsWatcher = require('../scripts/fsWatcher.js');
97+
const callback = jestGlobals.fn();
98+
99+
fsWatcher.makeFsWatchFilter('sameNameWatcher', '/data', 100, callback);
100+
fsWatcher.makeFsWatchFilter('sameNameWatcher', '/data', 100, callback);
101+
102+
expect(firstWatcher.close).toHaveBeenCalledTimes(1);
103+
expect(secondWatcher.close).not.toHaveBeenCalled();
104+
expect(typeof firstHandler).toBe('function');
105+
expect(typeof secondHandler).toBe('function');
106+
});
107+
});

test/watcher.integration.test.js

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
const { afterEach, describe, expect, it, jest: jestGlobals } = require('@jest/globals');
2+
3+
describe('Watcher integration', () => {
4+
afterEach(() => {
5+
jestGlobals.restoreAllMocks();
6+
jestGlobals.resetModules();
7+
});
8+
9+
it('should log detected file changes and trigger async reload', done => {
10+
let watchCallback;
11+
const makeFsWatchFilter = jestGlobals.fn((_name, _directory, _files, _delay, callback) => {
12+
watchCallback = callback;
13+
});
14+
15+
jestGlobals.doMock('../scripts/fsWatcher.js', () => ({
16+
makeFsWatchFilter,
17+
stopWatching: jestGlobals.fn(),
18+
}));
19+
20+
const logSpy = jestGlobals.spyOn(console, 'log').mockImplementation(() => undefined);
21+
const geoIp = require('../index.js');
22+
23+
geoIp.startWatchingDataUpdate(err => {
24+
try {
25+
expect(err).toBeFalsy();
26+
expect(makeFsWatchFilter).toHaveBeenCalledWith(
27+
'dataWatcher',
28+
expect.any(String),
29+
expect.arrayContaining([
30+
'geoip-city.dat',
31+
'geoip-city6.dat',
32+
'geoip-city-names.dat',
33+
'geoip-country.dat',
34+
'geoip-country6.dat',
35+
]),
36+
60 * 1000,
37+
expect.any(Function)
38+
);
39+
expect(logSpy).toHaveBeenCalledWith('[geoip-lite2] Detected change in "geoip-city.dat". Reloading data...');
40+
done();
41+
} catch (assertErr) {
42+
done(assertErr);
43+
}
44+
});
45+
46+
watchCallback({
47+
event: 'rename',
48+
file: 'geoip-city.dat',
49+
path: '/tmp/geoip-city.dat',
50+
});
51+
});
52+
});

0 commit comments

Comments
 (0)