Introduce stable server IDs for subscription state
All checks were successful
Build and Deploy Gateway / build-and-push (push) Successful in 14s
Build and Deploy Gateway / deploy (push) Successful in 7s

This commit is contained in:
2026-07-12 11:59:22 +03:00
parent 005c7a101b
commit 267afc5c7e
16 changed files with 323 additions and 145 deletions

View File

@@ -6,26 +6,51 @@ import {
selectRefreshedServer,
} from '../../src/server/subscription.js';
test('subscription server tags are trimmed for selection', () => {
const { servers } = parseSubscriptionBody(JSON.stringify({
outbounds: [{ type: 'vless', tag: 'de-frankfurt ', server: 'de.example', server_port: 443 }],
}));
assert.equal(servers[0].tag, 'de-frankfurt');
const parse = (outbounds) => parseSubscriptionBody(JSON.stringify({ outbounds }));
const outbound = (tag, server, server_port = 443) => ({
type: 'vless',
tag,
server,
server_port,
});
test('refreshed subscription keeps the selected server when its tag still exists', () => {
const servers = [{ tag: 'de' }, { tag: 'nl' }];
test('duplicate labels remain independently addressable by stable ID', () => {
const { config, servers } = parse([
outbound('Amsterdam', 'nl-1.example'),
outbound('Amsterdam', 'nl-2.example'),
]);
assert.equal(selectRefreshedServer('nl', servers), 'nl');
assert.equal(servers.length, 2);
assert.equal(servers[0].label, 'Amsterdam');
assert.equal(servers[1].label, 'Amsterdam');
assert.notEqual(servers[0].id, servers[1].id);
assert.deepEqual(config.outbounds.map((item) => item.tag), servers.map((server) => server.id));
});
test('refreshed subscription selects the first server when the old tag disappeared', () => {
const servers = [{ tag: 'de' }, { tag: 'nl' }];
test('provider reorder does not change server IDs or selection', () => {
const before = parse([outbound('DE', 'de.example'), outbound('NL', 'nl.example')]).servers;
const after = parse([outbound('NL', 'nl.example'), outbound('DE', 'de.example')]).servers;
const selectedId = before[1].id;
assert.equal(selectRefreshedServer('old-name', servers), 'de');
assert.deepEqual(
new Map(after.map((server) => [server.host, server.id])),
new Map(before.map((server) => [server.host, server.id])),
);
assert.equal(selectRefreshedServer(selectedId, before, after), selectedId);
});
test('refreshed subscription does not select a server before the first user choice', () => {
assert.equal(selectRefreshedServer('', [{ tag: 'de' }]), '');
test('cosmetic rename keeps selection for the same endpoint', () => {
const before = parse([outbound('Old name', 'nl.example')]).servers;
const after = parse([outbound('New name', 'nl.example')]).servers;
assert.equal(after[0].id, before[0].id);
assert.equal(selectRefreshedServer(before[0].id, before, after), before[0].id);
});
test('removed selected server requires an explicit new choice', () => {
const before = parse([outbound('DE', 'de.example'), outbound('NL', 'nl.example')]).servers;
const after = parse([outbound('DE', 'de.example')]).servers;
assert.equal(selectRefreshedServer(before[1].id, before, after), '');
assert.equal(selectRefreshedServer('', before, after), '');
});