@k03mad/ip2geo 3.0.0 β 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -2
- package/app/cli.js +22 -23
- package/app/helpers/colors.js +0 -1
- package/app/index.js +1 -1
- package/app/lib/ip2geo.js +12 -3
- package/package.json +4 -4
- package/tests/helpers/consts.js +33 -0
- package/tests/opts-assigned-all.js +33 -0
- package/tests/opts-assigned-cache-map.js +43 -0
- package/tests/opts-assigned-ipv6.js +12 -32
- package/tests/opts-assigned-multi-requests.js +33 -0
- package/tests/opts-assigned-no-data.js +33 -0
- package/tests/opts-assigned-subfolder.js +17 -38
- package/tests/opts-default.js +24 -50
- package/tests/shared/fs.js +18 -8
- package/tests/opts-assigned.js +0 -54
package/README.md
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
```bash
|
|
9
9
|
npm i @k03mad/ip2geo -g
|
|
10
|
+
ip2geo --help
|
|
10
11
|
|
|
11
12
|
ip2geo 1.1.1.1
|
|
12
13
|
# {
|
|
@@ -21,8 +22,8 @@ ip2geo 1.1.1.1
|
|
|
21
22
|
# ispDomain: 'cloudflare.com'
|
|
22
23
|
# }
|
|
23
24
|
|
|
24
|
-
ip2geo 1.1.1.1 --json
|
|
25
|
-
# {"ip":"1.1.1.1","emoji":"πΊπΈ","country":"United States","countryA2":"US","region":"District of Columbia","city":"Washington","org":"APNIC and Cloudflare DNS Resolver project","isp":"Cloudflare, Inc.","ispDomain":"cloudflare.com"}
|
|
25
|
+
ip2geo 1.1.1.1 8.8.8.8 --json
|
|
26
|
+
# [{"ip":"1.1.1.1","emoji":"πΊπΈ","country":"United States","countryA2":"US","region":"District of Columbia","city":"Washington","org":"APNIC and Cloudflare DNS Resolver project","isp":"Cloudflare, Inc.","ispDomain":"cloudflare.com"},{"ip":"8.8.8.8","emoji":"πΊπΈ","country":"United States","countryA2":"US","region":"California","city":"Mountain View","org":"Google LLC","isp":"Google LLC","ispDomain":"google.com"}]
|
|
26
27
|
```
|
|
27
28
|
|
|
28
29
|
## API
|
|
@@ -40,6 +41,8 @@ const info = await ip2geo('1.1.1.1', {
|
|
|
40
41
|
cacheFileName: 'ips.log',
|
|
41
42
|
cacheFileSeparator: ';;',
|
|
42
43
|
cacheFileNewline: '\n',
|
|
44
|
+
cacheMap: new Map(),
|
|
45
|
+
rps: 5, // useful in Promise.all with big IPs array
|
|
43
46
|
});
|
|
44
47
|
|
|
45
48
|
// info {
|
package/app/cli.js
CHANGED
|
@@ -1,40 +1,39 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
import {log
|
|
3
|
+
import {log} from '@k03mad/simple-log';
|
|
4
4
|
|
|
5
|
-
import {codeText,
|
|
5
|
+
import {codeText, nameText} from './helpers/colors.js';
|
|
6
6
|
|
|
7
7
|
import {ip2geo} from './index.js';
|
|
8
8
|
|
|
9
|
-
const
|
|
9
|
+
const args = process.argv.slice(2);
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
if (args.length === 0) {
|
|
11
|
+
if (args.includes('-h') || args.includes('--help')) {
|
|
14
12
|
const prefix = codeText('$');
|
|
15
13
|
const name = nameText('ip2geo');
|
|
14
|
+
const json = codeText('// json output for parse');
|
|
15
|
+
const multi = codeText('// multi IPs');
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
errorText('IP(s) should be passed as args'),
|
|
17
|
+
log([
|
|
19
18
|
'',
|
|
19
|
+
`${prefix} ${name}`,
|
|
20
20
|
`${prefix} ${name} 1.1.1.1`,
|
|
21
|
-
`${prefix} ${name} 1.1.1.1
|
|
21
|
+
`${prefix} ${name} 1.1.1.1 -j ${json}`,
|
|
22
|
+
`${prefix} ${name} 1.1.1.1 --json ${json}`,
|
|
23
|
+
`${prefix} ${name} 1.1.1.1 8.8.8.8 ${multi}`,
|
|
24
|
+
`${prefix} ${name} 1.1.1.1,8.8.8.8 ${multi}`,
|
|
25
|
+
'',
|
|
22
26
|
]);
|
|
23
27
|
}
|
|
24
28
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
json = true;
|
|
29
|
-
args = args.filter(elem => elem !== jsonParam);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
await Promise.all(args.map(async arg => {
|
|
33
|
-
const output = await ip2geo(arg);
|
|
29
|
+
const output = await Promise.all(args
|
|
30
|
+
.filter(arg => !arg.startsWith('-'))
|
|
31
|
+
.map(arg => Promise.all(arg.split(',').map(elem => ip2geo(elem)))));
|
|
34
32
|
|
|
35
|
-
|
|
36
|
-
return log(JSON.stringify(output));
|
|
37
|
-
}
|
|
33
|
+
const flatten = output.flat();
|
|
38
34
|
|
|
39
|
-
|
|
40
|
-
|
|
35
|
+
if (args.includes('--json') || args.includes('-j')) {
|
|
36
|
+
log(JSON.stringify(flatten.length > 1 ? flatten : flatten[0]));
|
|
37
|
+
} else {
|
|
38
|
+
log(flatten);
|
|
39
|
+
}
|
package/app/helpers/colors.js
CHANGED
package/app/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export
|
|
1
|
+
export * from './lib/ip2geo.js';
|
package/app/lib/ip2geo.js
CHANGED
|
@@ -26,8 +26,9 @@ const DEFAULT_CACHE_FILE_DIR = path.join(os.tmpdir(), '.ip2geo');
|
|
|
26
26
|
const DEFAULT_CACHE_FILE_NAME = 'ips.log';
|
|
27
27
|
const DEFAULT_CACHE_FILE_SEPARATOR = ';;';
|
|
28
28
|
const DEFAULT_CACHE_FILE_NEWLINE = '\n';
|
|
29
|
+
const DEFAULT_RPS = 5;
|
|
29
30
|
|
|
30
|
-
const
|
|
31
|
+
export const cacheStorage = new Map();
|
|
31
32
|
|
|
32
33
|
const outputKeys = [
|
|
33
34
|
'ip',
|
|
@@ -110,13 +111,17 @@ const writeToFsCache = async (ip, data, cacheDir, cacheFileName, cacheFileSepara
|
|
|
110
111
|
* @param {string} [opts.cacheFileName]
|
|
111
112
|
* @param {string} [opts.cacheFileSeparator]
|
|
112
113
|
* @param {string} [opts.cacheFileNewline]
|
|
114
|
+
* @param {Map} [opts.cacheMap]
|
|
115
|
+
* @param {number} opts.rps
|
|
113
116
|
* @returns {Promise<GeoIpOutput>}
|
|
114
117
|
*/
|
|
115
|
-
export
|
|
118
|
+
export const ip2geo = async (ip = '', {
|
|
116
119
|
cacheDir = DEFAULT_CACHE_FILE_DIR,
|
|
117
120
|
cacheFileName = DEFAULT_CACHE_FILE_NAME,
|
|
118
121
|
cacheFileSeparator = DEFAULT_CACHE_FILE_SEPARATOR,
|
|
119
122
|
cacheFileNewline = DEFAULT_CACHE_FILE_NEWLINE,
|
|
123
|
+
cacheMap = cacheStorage,
|
|
124
|
+
rps = DEFAULT_RPS,
|
|
120
125
|
} = {}) => {
|
|
121
126
|
if (ip) {
|
|
122
127
|
const ipData = cacheMap.get(ip);
|
|
@@ -145,7 +150,11 @@ export default async (ip = '', {
|
|
|
145
150
|
}
|
|
146
151
|
}
|
|
147
152
|
|
|
148
|
-
const {body} = await request(API + ip);
|
|
153
|
+
const {body} = await request(API + ip, {}, {rps});
|
|
154
|
+
|
|
155
|
+
if (!body?.ip) {
|
|
156
|
+
throw new Error(`API error:\n${body}`);
|
|
157
|
+
}
|
|
149
158
|
|
|
150
159
|
const usedData = [
|
|
151
160
|
body.ip,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@k03mad/ip2geo",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.0.0",
|
|
4
4
|
"description": "GeoIP library",
|
|
5
5
|
"maintainers": [
|
|
6
6
|
"Kirill Molchanov <k03.mad@gmail.com"
|
|
@@ -19,13 +19,13 @@
|
|
|
19
19
|
"node": ">=20"
|
|
20
20
|
},
|
|
21
21
|
"dependencies": {
|
|
22
|
-
"@k03mad/request": "5.
|
|
23
|
-
"@k03mad/simple-log": "2.
|
|
22
|
+
"@k03mad/request": "5.5.0",
|
|
23
|
+
"@k03mad/simple-log": "2.1.0",
|
|
24
24
|
"chalk": "5.3.0",
|
|
25
25
|
"debug": "4.3.4"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
|
-
"@k03mad/eslint-config": "19.
|
|
28
|
+
"@k03mad/eslint-config": "19.4.0",
|
|
29
29
|
"eslint": "8.56.0",
|
|
30
30
|
"husky": "8.0.3",
|
|
31
31
|
"mocha": "10.2.0"
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import os from 'node:os';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
|
|
4
|
+
export const DEFAULT_OPTS = {
|
|
5
|
+
cacheDir: path.join(os.tmpdir(), '.ip2geo'),
|
|
6
|
+
cacheFileName: 'ips.log',
|
|
7
|
+
cacheFileSeparator: ';;',
|
|
8
|
+
cacheFileNewline: '\n',
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export const REQUEST_IPV4 = {
|
|
12
|
+
ip: '8.8.8.8',
|
|
13
|
+
emoji: 'πΊπΈ',
|
|
14
|
+
country: 'United States',
|
|
15
|
+
countryA2: 'US',
|
|
16
|
+
region: 'California',
|
|
17
|
+
city: 'Mountain View',
|
|
18
|
+
org: 'Google LLC',
|
|
19
|
+
isp: 'Google LLC',
|
|
20
|
+
ispDomain: 'google.com',
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export const REQUEST_IPV6 = {
|
|
24
|
+
ip: '2a00:dd80:40:100::',
|
|
25
|
+
emoji: 'π³π±',
|
|
26
|
+
country: 'Netherlands',
|
|
27
|
+
countryA2: 'NL',
|
|
28
|
+
region: 'North Holland',
|
|
29
|
+
city: 'Amsterdam',
|
|
30
|
+
org: '',
|
|
31
|
+
isp: 'NetActuate Inc',
|
|
32
|
+
ispDomain: '',
|
|
33
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
|
|
3
|
+
import {describe, it} from 'mocha';
|
|
4
|
+
|
|
5
|
+
import {ip2geo} from '../app/index.js';
|
|
6
|
+
|
|
7
|
+
import {REQUEST_IPV4} from './helpers/consts.js';
|
|
8
|
+
import {getCurrentFilename, getTestFolder} from './helpers/path.js';
|
|
9
|
+
import {checkCacheFile, removeCacheFolder} from './shared/fs.js';
|
|
10
|
+
|
|
11
|
+
const testName = getCurrentFilename(import.meta.url);
|
|
12
|
+
|
|
13
|
+
describe(testName, () => {
|
|
14
|
+
const opts = {
|
|
15
|
+
cacheDir: getTestFolder(testName),
|
|
16
|
+
cacheFileName: 'ips.md',
|
|
17
|
+
cacheFileSeparator: '-_-',
|
|
18
|
+
cacheFileNewline: '%%%',
|
|
19
|
+
cacheMap: new Map(),
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
it('should remove fs cache dir if exist', () => removeCacheFolder(opts.cacheDir));
|
|
23
|
+
|
|
24
|
+
it(`should return correct response for IP: "${REQUEST_IPV4.ip}"`, async () => {
|
|
25
|
+
const data = await ip2geo(REQUEST_IPV4.ip, opts);
|
|
26
|
+
assert.deepEqual(data, REQUEST_IPV4);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('should have cache file', () => checkCacheFile({
|
|
30
|
+
...opts,
|
|
31
|
+
response: REQUEST_IPV4,
|
|
32
|
+
}));
|
|
33
|
+
});
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
|
|
3
|
+
import {describe, it} from 'mocha';
|
|
4
|
+
|
|
5
|
+
import {ip2geo} from '../app/index.js';
|
|
6
|
+
|
|
7
|
+
import {REQUEST_IPV4, REQUEST_IPV6} from './helpers/consts.js';
|
|
8
|
+
import {getCurrentFilename, getTestFolder} from './helpers/path.js';
|
|
9
|
+
import {removeCacheFolder} from './shared/fs.js';
|
|
10
|
+
|
|
11
|
+
const testName = getCurrentFilename(import.meta.url);
|
|
12
|
+
|
|
13
|
+
describe(testName, () => {
|
|
14
|
+
const cacheMap = new Map();
|
|
15
|
+
|
|
16
|
+
const opts = {
|
|
17
|
+
cacheDir: getTestFolder(testName),
|
|
18
|
+
cacheMap,
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
it('should remove fs cache dir if exist', () => removeCacheFolder(opts.cacheDir));
|
|
22
|
+
|
|
23
|
+
it(`should return correct response for IP: "${REQUEST_IPV4.ip}"`, async () => {
|
|
24
|
+
const data = await ip2geo(REQUEST_IPV4.ip, opts);
|
|
25
|
+
assert.deepEqual(data, REQUEST_IPV4);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('should have 1 correct cache entry', () => {
|
|
29
|
+
assert.equal(cacheMap.size, 1);
|
|
30
|
+
assert.deepEqual(cacheMap.get(REQUEST_IPV4.ip), REQUEST_IPV4);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it(`should return correct response for IP: "${REQUEST_IPV6.ip}"`, async () => {
|
|
34
|
+
const data = await ip2geo(REQUEST_IPV6.ip, opts);
|
|
35
|
+
assert.deepEqual(data, REQUEST_IPV6);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('should have 2 correct cache entries', () => {
|
|
39
|
+
assert.equal(cacheMap.size, 2);
|
|
40
|
+
assert.deepEqual(cacheMap.get(REQUEST_IPV4.ip), REQUEST_IPV4);
|
|
41
|
+
assert.deepEqual(cacheMap.get(REQUEST_IPV6.ip), REQUEST_IPV6);
|
|
42
|
+
});
|
|
43
|
+
});
|
|
@@ -4,48 +4,28 @@ import {describe, it} from 'mocha';
|
|
|
4
4
|
|
|
5
5
|
import {ip2geo} from '../app/index.js';
|
|
6
6
|
|
|
7
|
+
import {REQUEST_IPV6} from './helpers/consts.js';
|
|
7
8
|
import {getCurrentFilename, getTestFolder} from './helpers/path.js';
|
|
8
9
|
import {checkCacheFile, removeCacheFolder} from './shared/fs.js';
|
|
9
10
|
|
|
10
11
|
const testName = getCurrentFilename(import.meta.url);
|
|
11
12
|
|
|
12
13
|
describe(testName, () => {
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
const CACHE_FILE_NEWLINE = '\n';
|
|
17
|
-
|
|
18
|
-
const REQUEST_IP = '2a00:dd80:40:100::';
|
|
19
|
-
|
|
20
|
-
const cacheFile = `${REQUEST_IP.split(/\.|:/)[0]}_${CACHE_FILE_NAME}`;
|
|
21
|
-
|
|
22
|
-
const response = {
|
|
23
|
-
ip: REQUEST_IP,
|
|
24
|
-
emoji: 'π³π±',
|
|
25
|
-
country: 'Netherlands',
|
|
26
|
-
countryA2: 'NL',
|
|
27
|
-
region: 'North Holland',
|
|
28
|
-
city: 'Amsterdam',
|
|
29
|
-
org: '',
|
|
30
|
-
isp: 'NetActuate Inc',
|
|
31
|
-
ispDomain: '',
|
|
14
|
+
const opts = {
|
|
15
|
+
cacheDir: getTestFolder(testName),
|
|
16
|
+
cacheMap: new Map(),
|
|
32
17
|
};
|
|
33
18
|
|
|
34
|
-
it('should remove fs cache dir if exist', () => removeCacheFolder(
|
|
19
|
+
it('should remove fs cache dir if exist', () => removeCacheFolder(opts.cacheDir));
|
|
35
20
|
|
|
36
|
-
it(`should return correct response for IP: "${
|
|
37
|
-
const data = await ip2geo(
|
|
38
|
-
cacheDir: CACHE_FILE_DIR,
|
|
39
|
-
});
|
|
21
|
+
it(`should return correct response for IP: "${REQUEST_IPV6.ip}"`, async () => {
|
|
22
|
+
const data = await ip2geo(REQUEST_IPV6.ip, opts);
|
|
40
23
|
|
|
41
|
-
assert.deepEqual(data,
|
|
24
|
+
assert.deepEqual(data, REQUEST_IPV6);
|
|
42
25
|
});
|
|
43
26
|
|
|
44
|
-
it('should have cache file', () => checkCacheFile(
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
CACHE_FILE_NEWLINE,
|
|
49
|
-
response,
|
|
50
|
-
));
|
|
27
|
+
it('should have cache file', () => checkCacheFile({
|
|
28
|
+
...opts,
|
|
29
|
+
response: REQUEST_IPV6,
|
|
30
|
+
}));
|
|
51
31
|
});
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
|
|
3
|
+
import {describe, it} from 'mocha';
|
|
4
|
+
|
|
5
|
+
import {ip2geo} from '../app/index.js';
|
|
6
|
+
|
|
7
|
+
import {REQUEST_IPV4} from './helpers/consts.js';
|
|
8
|
+
import {getCurrentFilename, getTestFolder} from './helpers/path.js';
|
|
9
|
+
import {checkCacheFile, removeCacheFolder} from './shared/fs.js';
|
|
10
|
+
|
|
11
|
+
const testName = getCurrentFilename(import.meta.url);
|
|
12
|
+
|
|
13
|
+
describe(testName, () => {
|
|
14
|
+
const opts = {
|
|
15
|
+
cacheDir: getTestFolder(testName),
|
|
16
|
+
cacheMap: new Map(),
|
|
17
|
+
requestsCount: 5,
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
it('should remove fs cache dir if exist', () => removeCacheFolder(opts.cacheDir));
|
|
21
|
+
|
|
22
|
+
Array.from({length: opts.requestsCount}).forEach(() => {
|
|
23
|
+
it(`should return correct response for IP: "${REQUEST_IPV4.ip}"`, async () => {
|
|
24
|
+
const data = await ip2geo(REQUEST_IPV4.ip, opts);
|
|
25
|
+
assert.deepEqual(data, REQUEST_IPV4);
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('should have cache file', () => checkCacheFile({
|
|
30
|
+
...opts,
|
|
31
|
+
response: REQUEST_IPV4,
|
|
32
|
+
}));
|
|
33
|
+
});
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
|
|
3
|
+
import {describe, it} from 'mocha';
|
|
4
|
+
|
|
5
|
+
import {ip2geo} from '../app/index.js';
|
|
6
|
+
|
|
7
|
+
import {REQUEST_IPV4} from './helpers/consts.js';
|
|
8
|
+
import {getCurrentFilename, getTestFolder} from './helpers/path.js';
|
|
9
|
+
import {removeCacheFolder} from './shared/fs.js';
|
|
10
|
+
|
|
11
|
+
const testName = getCurrentFilename(import.meta.url);
|
|
12
|
+
|
|
13
|
+
describe(testName, () => {
|
|
14
|
+
const response = {ip: '10.10.10.10'};
|
|
15
|
+
|
|
16
|
+
Object.keys(REQUEST_IPV4).forEach(elem => {
|
|
17
|
+
if (elem !== 'ip') {
|
|
18
|
+
response[elem] = undefined;
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
const opts = {
|
|
23
|
+
cacheDir: getTestFolder(testName),
|
|
24
|
+
cacheMap: new Map(),
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
it('should remove fs cache dir if exist', () => removeCacheFolder(opts.cacheDir));
|
|
28
|
+
|
|
29
|
+
it(`should return empty response for IP: "${response.ip}"`, async () => {
|
|
30
|
+
const data = await ip2geo(response.ip, opts);
|
|
31
|
+
assert.deepEqual(data, response);
|
|
32
|
+
});
|
|
33
|
+
});
|
|
@@ -5,54 +5,33 @@ import {describe, it} from 'mocha';
|
|
|
5
5
|
|
|
6
6
|
import {ip2geo} from '../app/index.js';
|
|
7
7
|
|
|
8
|
+
import {REQUEST_IPV4} from './helpers/consts.js';
|
|
8
9
|
import {getCurrentFilename, getTestFolder} from './helpers/path.js';
|
|
9
10
|
import {checkCacheFile, removeCacheFolder} from './shared/fs.js';
|
|
10
11
|
|
|
11
12
|
const testName = getCurrentFilename(import.meta.url);
|
|
12
|
-
const SUBFOLDERS = 5;
|
|
13
13
|
|
|
14
14
|
describe(testName, () => {
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
const SUBFOLDERS = 5;
|
|
16
|
+
|
|
17
|
+
const opts = {
|
|
18
|
+
cacheDir: getTestFolder(
|
|
19
|
+
path.join(
|
|
20
|
+
...Array.from({length: SUBFOLDERS}, () => testName),
|
|
21
|
+
),
|
|
18
22
|
),
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
const CACHE_FILE_NAME = 'ips.log';
|
|
22
|
-
const CACHE_FILE_SEPARATOR = ';;';
|
|
23
|
-
const CACHE_FILE_NEWLINE = '\n';
|
|
24
|
-
|
|
25
|
-
const REQUEST_IP = '9.9.9.9';
|
|
26
|
-
|
|
27
|
-
const cacheFile = `${REQUEST_IP.split(/\.|:/)[0]}_${CACHE_FILE_NAME}`;
|
|
28
|
-
|
|
29
|
-
const response = {
|
|
30
|
-
ip: REQUEST_IP,
|
|
31
|
-
emoji: 'π¨π',
|
|
32
|
-
country: 'Switzerland',
|
|
33
|
-
countryA2: 'CH',
|
|
34
|
-
region: 'Zurich',
|
|
35
|
-
city: 'ZΓΌrich',
|
|
36
|
-
org: 'Quad9',
|
|
37
|
-
isp: 'Quad9',
|
|
38
|
-
ispDomain: 'quad9.net',
|
|
23
|
+
cacheMap: new Map(),
|
|
39
24
|
};
|
|
40
25
|
|
|
41
|
-
it('should remove fs cache dir if exist', () => removeCacheFolder(
|
|
42
|
-
|
|
43
|
-
it(`should return correct response for IP: "${REQUEST_IP}"`, async () => {
|
|
44
|
-
const data = await ip2geo(REQUEST_IP, {
|
|
45
|
-
cacheDir: CACHE_FILE_DIR,
|
|
46
|
-
});
|
|
26
|
+
it('should remove fs cache dir if exist', () => removeCacheFolder(opts.cacheDir));
|
|
47
27
|
|
|
48
|
-
|
|
28
|
+
it(`should return correct response for IP: "${REQUEST_IPV4.ip}"`, async () => {
|
|
29
|
+
const data = await ip2geo(REQUEST_IPV4.ip, opts);
|
|
30
|
+
assert.deepEqual(data, REQUEST_IPV4);
|
|
49
31
|
});
|
|
50
32
|
|
|
51
|
-
it('should have cache file', () => checkCacheFile(
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
CACHE_FILE_NEWLINE,
|
|
56
|
-
response,
|
|
57
|
-
));
|
|
33
|
+
it('should have cache file', () => checkCacheFile({
|
|
34
|
+
...opts,
|
|
35
|
+
response: REQUEST_IPV4,
|
|
36
|
+
}));
|
|
58
37
|
});
|
package/tests/opts-default.js
CHANGED
|
@@ -1,63 +1,32 @@
|
|
|
1
1
|
import assert from 'node:assert/strict';
|
|
2
|
-
import os from 'node:os';
|
|
3
|
-
import path from 'node:path';
|
|
4
2
|
|
|
5
3
|
import {describe, it} from 'mocha';
|
|
6
4
|
|
|
7
|
-
import {ip2geo} from '../app/index.js';
|
|
5
|
+
import {cacheStorage, ip2geo} from '../app/index.js';
|
|
8
6
|
|
|
7
|
+
import {REQUEST_IPV4} from './helpers/consts.js';
|
|
8
|
+
import {getCurrentFilename} from './helpers/path.js';
|
|
9
9
|
import {checkCacheFile, removeCacheFolder} from './shared/fs.js';
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
const CACHE_FILE_DIR = path.join(os.tmpdir(), '.ip2geo');
|
|
13
|
-
const CACHE_FILE_NAME = 'ips.log';
|
|
14
|
-
const CACHE_FILE_SEPARATOR = ';;';
|
|
15
|
-
const CACHE_FILE_NEWLINE = '\n';
|
|
11
|
+
const testName = getCurrentFilename(import.meta.url);
|
|
16
12
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
const cacheFile = `${REQUEST_IP.split(/\.|:/)[0]}_${CACHE_FILE_NAME}`;
|
|
20
|
-
|
|
21
|
-
const response = {
|
|
22
|
-
ip: REQUEST_IP,
|
|
23
|
-
emoji: 'πΊπΈ',
|
|
24
|
-
country: 'United States',
|
|
25
|
-
countryA2: 'US',
|
|
26
|
-
region: 'District of Columbia',
|
|
27
|
-
city: 'Washington',
|
|
28
|
-
org: 'APNIC and Cloudflare DNS Resolver project',
|
|
29
|
-
isp: 'Cloudflare, Inc.',
|
|
30
|
-
ispDomain: 'cloudflare.com',
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
const outputKeys = [
|
|
34
|
-
'ip',
|
|
35
|
-
'emoji',
|
|
36
|
-
'country',
|
|
37
|
-
'countryA2',
|
|
38
|
-
'region',
|
|
39
|
-
'city',
|
|
40
|
-
'org',
|
|
41
|
-
'isp',
|
|
42
|
-
'ispDomain',
|
|
43
|
-
];
|
|
44
|
-
|
|
45
|
-
removeCacheFolder(CACHE_FILE_DIR);
|
|
13
|
+
describe(testName, () => {
|
|
14
|
+
it('should remove fs cache dir if exist', () => removeCacheFolder());
|
|
46
15
|
|
|
47
16
|
describe('with ip arg', () => {
|
|
48
|
-
it(`should return correct response for IP: "${
|
|
49
|
-
const data = await ip2geo(
|
|
50
|
-
|
|
51
|
-
assert.deepEqual(data, response);
|
|
17
|
+
it(`should return correct response for IP: "${REQUEST_IPV4.ip}"`, async () => {
|
|
18
|
+
const data = await ip2geo(REQUEST_IPV4.ip);
|
|
19
|
+
assert.deepEqual(data, REQUEST_IPV4);
|
|
52
20
|
});
|
|
53
21
|
|
|
54
|
-
it('should have cache file', () => checkCacheFile(
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
22
|
+
it('should have cache file', () => checkCacheFile({
|
|
23
|
+
response: REQUEST_IPV4,
|
|
24
|
+
}));
|
|
25
|
+
|
|
26
|
+
it('should have 1 correct cache entry', () => {
|
|
27
|
+
assert.equal(cacheStorage.size, 1);
|
|
28
|
+
assert.deepEqual(cacheStorage.get(REQUEST_IPV4.ip), REQUEST_IPV4);
|
|
29
|
+
});
|
|
61
30
|
});
|
|
62
31
|
|
|
63
32
|
describe('without ip arg', () => {
|
|
@@ -67,14 +36,19 @@ describe('opts-default', () => {
|
|
|
67
36
|
data = await ip2geo();
|
|
68
37
|
});
|
|
69
38
|
|
|
70
|
-
|
|
39
|
+
Object.keys(REQUEST_IPV4).forEach(key => {
|
|
71
40
|
it(`should have "${key}" in request response`, () => {
|
|
72
41
|
assert.ok(data[key]);
|
|
73
42
|
});
|
|
74
43
|
});
|
|
75
44
|
|
|
76
45
|
it('should not have extra keys in request response', () => {
|
|
77
|
-
assert.deepEqual(Object.keys(data),
|
|
46
|
+
assert.deepEqual(Object.keys(data), Object.keys(REQUEST_IPV4));
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('should have 2 cache entries', () => {
|
|
50
|
+
assert.equal(cacheStorage.size, 2);
|
|
51
|
+
assert.deepEqual(cacheStorage.get(REQUEST_IPV4.ip), REQUEST_IPV4);
|
|
78
52
|
});
|
|
79
53
|
});
|
|
80
54
|
});
|
package/tests/shared/fs.js
CHANGED
|
@@ -2,10 +2,12 @@ import assert from 'node:assert/strict';
|
|
|
2
2
|
import fs from 'node:fs/promises';
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
|
|
5
|
+
import {DEFAULT_OPTS} from '../helpers/consts.js';
|
|
6
|
+
|
|
5
7
|
/**
|
|
6
8
|
* @param {string} cacheDir
|
|
7
9
|
*/
|
|
8
|
-
export const removeCacheFolder = async cacheDir => {
|
|
10
|
+
export const removeCacheFolder = async (cacheDir = DEFAULT_OPTS.cacheDir) => {
|
|
9
11
|
try {
|
|
10
12
|
await fs.rm(cacheDir, {recursive: true, force: true});
|
|
11
13
|
} catch (err) {
|
|
@@ -16,14 +18,22 @@ export const removeCacheFolder = async cacheDir => {
|
|
|
16
18
|
};
|
|
17
19
|
|
|
18
20
|
/**
|
|
19
|
-
* @param {
|
|
20
|
-
* @param {string}
|
|
21
|
-
* @param {string}
|
|
22
|
-
* @param {string}
|
|
23
|
-
* @param {
|
|
21
|
+
* @param {object} opts
|
|
22
|
+
* @param {string} opts.cacheDir
|
|
23
|
+
* @param {string} opts.cacheFileName
|
|
24
|
+
* @param {string} opts.cacheFileSeparator
|
|
25
|
+
* @param {string} opts.cacheFileNewline
|
|
26
|
+
* @param {object} opts.response
|
|
24
27
|
*/
|
|
25
|
-
export const checkCacheFile = async (
|
|
26
|
-
|
|
28
|
+
export const checkCacheFile = async ({
|
|
29
|
+
cacheDir = DEFAULT_OPTS.cacheDir,
|
|
30
|
+
cacheFileName = DEFAULT_OPTS.cacheFileName,
|
|
31
|
+
cacheFileSeparator = DEFAULT_OPTS.cacheFileSeparator,
|
|
32
|
+
cacheFileNewline = DEFAULT_OPTS.cacheFileNewline,
|
|
33
|
+
response,
|
|
34
|
+
}) => {
|
|
35
|
+
const cacheFile = `${response.ip.split(/\.|:/)[0]}_${cacheFileName}`;
|
|
36
|
+
const data = await fs.readFile(path.join(cacheDir, cacheFile), {encoding: 'utf8'});
|
|
27
37
|
|
|
28
38
|
assert.equal(data, Object.values(response).join(cacheFileSeparator) + cacheFileNewline);
|
|
29
39
|
};
|
package/tests/opts-assigned.js
DELETED
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
import assert from 'node:assert/strict';
|
|
2
|
-
|
|
3
|
-
import {describe, it} from 'mocha';
|
|
4
|
-
|
|
5
|
-
import {ip2geo} from '../app/index.js';
|
|
6
|
-
|
|
7
|
-
import {getCurrentFilename, getTestFolder} from './helpers/path.js';
|
|
8
|
-
import {checkCacheFile, removeCacheFolder} from './shared/fs.js';
|
|
9
|
-
|
|
10
|
-
const testName = getCurrentFilename(import.meta.url);
|
|
11
|
-
|
|
12
|
-
describe(testName, () => {
|
|
13
|
-
const CACHE_FILE_DIR = getTestFolder(testName);
|
|
14
|
-
const CACHE_FILE_NAME = 'ips.md';
|
|
15
|
-
const CACHE_FILE_SEPARATOR = '-_-';
|
|
16
|
-
const CACHE_FILE_NEWLINE = '%%%';
|
|
17
|
-
|
|
18
|
-
const REQUEST_IP = '8.8.8.8';
|
|
19
|
-
|
|
20
|
-
const cacheFile = `${REQUEST_IP.split(/\.|:/)[0]}_${CACHE_FILE_NAME}`;
|
|
21
|
-
|
|
22
|
-
const response = {
|
|
23
|
-
ip: REQUEST_IP,
|
|
24
|
-
emoji: 'πΊπΈ',
|
|
25
|
-
country: 'United States',
|
|
26
|
-
countryA2: 'US',
|
|
27
|
-
region: 'California',
|
|
28
|
-
city: 'Mountain View',
|
|
29
|
-
org: 'Google LLC',
|
|
30
|
-
isp: 'Google LLC',
|
|
31
|
-
ispDomain: 'google.com',
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
it('should remove fs cache dir if exist', () => removeCacheFolder(CACHE_FILE_DIR));
|
|
35
|
-
|
|
36
|
-
it(`should return correct response for IP: "${REQUEST_IP}"`, async () => {
|
|
37
|
-
const data = await ip2geo(REQUEST_IP, {
|
|
38
|
-
cacheDir: CACHE_FILE_DIR,
|
|
39
|
-
cacheFileName: CACHE_FILE_NAME,
|
|
40
|
-
cacheFileSeparator: CACHE_FILE_SEPARATOR,
|
|
41
|
-
cacheFileNewline: CACHE_FILE_NEWLINE,
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
assert.deepEqual(data, response);
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
it('should have cache file', () => checkCacheFile(
|
|
48
|
-
CACHE_FILE_DIR,
|
|
49
|
-
cacheFile,
|
|
50
|
-
CACHE_FILE_SEPARATOR,
|
|
51
|
-
CACHE_FILE_NEWLINE,
|
|
52
|
-
response,
|
|
53
|
-
));
|
|
54
|
-
});
|