mirror of
https://github.com/jason5ng32/MyIP.git
synced 2026-11-04 08:55:47 +00:00
The geolocation fallback used to rewrite the stored preference on every successful fallback and disable failing sources for the session, so a transient error changed what the user had picked and struck sources out of the Preferences picker. Now ipDBs.enabled is derived purely from the /api/configs flags the moment they arrive (replacing the 4s timer in Preferences.vue), runtime failures just walk to the next source with a one-time toast, and the stored preference is only rewritten when it points at a source that is no longer configured — migrated to the nearest available one, also announced via toast, in all four locales. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
120 lines
4.1 KiB
JavaScript
120 lines
4.1 KiB
JavaScript
import assert from 'node:assert/strict';
|
|
import { describe, it } from 'node:test';
|
|
|
|
import {
|
|
IP_DATABASES,
|
|
createInitialIpDBs,
|
|
buildDbUrl,
|
|
applyConfigAvailability,
|
|
nearestEnabledId,
|
|
} from '../frontend/data/ip-databases.js';
|
|
|
|
describe('IP_DATABASES', () => {
|
|
it('has 7 unique sequential ids', () => {
|
|
assert.equal(IP_DATABASES.length, 7);
|
|
const ids = IP_DATABASES.map((d) => d.id);
|
|
assert.deepEqual(ids, [0, 1, 2, 3, 4, 5, 6]);
|
|
});
|
|
|
|
it('every entry has url + text + enabled fields', () => {
|
|
for (const db of IP_DATABASES) {
|
|
assert.ok(db.text);
|
|
assert.ok(db.url.startsWith('/api/'));
|
|
assert.equal(typeof db.enabled, 'boolean');
|
|
}
|
|
});
|
|
});
|
|
|
|
describe('createInitialIpDBs()', () => {
|
|
it('returns fresh array and fresh entries', () => {
|
|
const a = createInitialIpDBs();
|
|
const b = createInitialIpDBs();
|
|
assert.notEqual(a, b);
|
|
assert.notEqual(a[0], b[0]);
|
|
a[0].enabled = false;
|
|
assert.equal(b[0].enabled, true, 'mutating one copy must not affect the other');
|
|
assert.equal(IP_DATABASES[0].enabled, true, 'must not mutate the static definition');
|
|
});
|
|
});
|
|
|
|
describe('buildDbUrl()', () => {
|
|
const db = { id: 0, url: '/api/x?ip={{ip}}&lang={{lang}}' };
|
|
|
|
it('substitutes {{ip}} and {{lang}}', () => {
|
|
assert.equal(buildDbUrl(db, '1.1.1.1', 'zh'), '/api/x?ip=1.1.1.1&lang=zh');
|
|
});
|
|
|
|
it('defaults lang to "en" when falsy', () => {
|
|
assert.equal(buildDbUrl(db, '1.1.1.1', undefined), '/api/x?ip=1.1.1.1&lang=en');
|
|
assert.equal(buildDbUrl(db, '1.1.1.1', ''), '/api/x?ip=1.1.1.1&lang=en');
|
|
assert.equal(buildDbUrl(db, '1.1.1.1', null), '/api/x?ip=1.1.1.1&lang=en');
|
|
});
|
|
|
|
it('handles urls without {{lang}} placeholder', () => {
|
|
const noLangDb = { url: '/api/y?ip={{ip}}' };
|
|
assert.equal(buildDbUrl(noLangDb, '8.8.8.8', 'zh'), '/api/y?ip=8.8.8.8');
|
|
});
|
|
|
|
it('returns null for missing db', () => {
|
|
assert.equal(buildDbUrl(null, '1.1.1.1', 'en'), null);
|
|
assert.equal(buildDbUrl(undefined, '1.1.1.1', 'en'), null);
|
|
assert.equal(buildDbUrl({}, '1.1.1.1', 'en'), null);
|
|
});
|
|
});
|
|
|
|
describe('applyConfigAvailability()', () => {
|
|
// Shape of a real /api/configs payload (booleans only).
|
|
const configs = { ipChecking: true, ipInfo: false, ipapiis: false, ip2location: true };
|
|
|
|
it('follows the configKey flag for keyed sources', () => {
|
|
const out = applyConfigAvailability(createInitialIpDBs(), configs);
|
|
const byId = Object.fromEntries(out.map((db) => [db.id, db.enabled]));
|
|
assert.equal(byId[0], true); // ipChecking: true
|
|
assert.equal(byId[1], false); // ipInfo: false
|
|
assert.equal(byId[3], false); // ipapiis: false
|
|
assert.equal(byId[4], true); // ip2location: true
|
|
});
|
|
|
|
it('keeps key-free sources always enabled', () => {
|
|
const allOff = applyConfigAvailability(createInitialIpDBs(),
|
|
{ ipChecking: false, ipInfo: false, ipapiis: false, ip2location: false });
|
|
for (const id of [2, 5, 6]) {
|
|
assert.equal(allOff.find((db) => db.id === id).enabled, true, `id ${id} must stay enabled`);
|
|
}
|
|
});
|
|
|
|
it('returns fresh entries without mutating the input', () => {
|
|
const input = createInitialIpDBs();
|
|
const out = applyConfigAvailability(input, configs);
|
|
assert.notEqual(out, input);
|
|
assert.notEqual(out[1], input[1]);
|
|
assert.equal(input[1].enabled, true, 'input must be untouched');
|
|
});
|
|
});
|
|
|
|
describe('nearestEnabledId()', () => {
|
|
const dbs = (enabledIds) =>
|
|
createInitialIpDBs().map((db) => ({ ...db, enabled: enabledIds.includes(db.id) }));
|
|
|
|
it('keeps the preference when it is still available', () => {
|
|
assert.equal(nearestEnabledId(3, dbs([0, 3, 6])), 3);
|
|
});
|
|
|
|
it('walks forward to the next available source', () => {
|
|
assert.equal(nearestEnabledId(1, dbs([0, 4, 5])), 4);
|
|
});
|
|
|
|
it('wraps around past the end of the list', () => {
|
|
assert.equal(nearestEnabledId(6, dbs([1])), 1);
|
|
});
|
|
|
|
it('starts from the head for unknown ids', () => {
|
|
assert.equal(nearestEnabledId(42, dbs([2, 5])), 2);
|
|
});
|
|
|
|
it('leaves the preference unchanged when nothing is enabled', () => {
|
|
assert.equal(nearestEnabledId(3, dbs([])), 3);
|
|
assert.equal(nearestEnabledId(0, []), 0);
|
|
});
|
|
});
|