Add network identity diagnostics
This commit is contained in:
@@ -49,6 +49,7 @@ test('connectivity diagnostics force separate direct and VPN paths', async () =>
|
||||
assert.deepEqual(result.vpn.ipv4.addresses, ['203.0.113.20']);
|
||||
assert.equal(result.direct.ipv6, '2001:db8::10');
|
||||
assert.equal(result.vpn.ipv6, '2001:db8::20');
|
||||
assert.equal(result.direct.network.error, 'invalid network response');
|
||||
assert.equal(result.assessment.summary, 'available');
|
||||
assert.ok(calls.some((args) => args.includes('--noproxy') && args.includes('*')));
|
||||
assert.ok(calls.some((args) => args.includes('--proxy') && args.includes('http://127.0.0.1:18080')));
|
||||
@@ -242,6 +243,48 @@ test('a targeted IP row uses three samples and keeps the majority address', asyn
|
||||
assert.equal(result.vpn.ipv4.sources[0].latencyMs, 250);
|
||||
});
|
||||
|
||||
test('a targeted network row reports provider, ASN and location through both forced paths', async () => {
|
||||
const attempts = { direct: 0, vpn: 0 };
|
||||
const calls = [];
|
||||
const execute = async (args) => {
|
||||
calls.push(args);
|
||||
const route = args.includes('--proxy') ? 'vpn' : 'direct';
|
||||
attempts[route] += 1;
|
||||
if (route === 'direct' && attempts.direct === 1) return response('{invalid');
|
||||
return response(JSON.stringify(route === 'vpn' ? {
|
||||
success: true,
|
||||
ip: '203.0.113.20',
|
||||
city: 'Amsterdam',
|
||||
country_code: 'NL',
|
||||
connection: { asn: 9009, isp: 'M247 Europe' },
|
||||
} : {
|
||||
success: true,
|
||||
ip: '198.51.100.10',
|
||||
city: 'Moscow',
|
||||
country_code: 'RU',
|
||||
connection: { asn: 12389, isp: 'Rostelecom' },
|
||||
}));
|
||||
};
|
||||
const result = await createConnectivityDiagnosticsService({ proxyPort: 18080, execute })
|
||||
.run({ vpnAvailable: true, target: 'network' });
|
||||
|
||||
assert.deepEqual(attempts, { direct: 3, vpn: 3 });
|
||||
assert.deepEqual(result.direct.network, {
|
||||
address: '198.51.100.10',
|
||||
asn: 'AS12389',
|
||||
provider: 'Rostelecom',
|
||||
city: 'Moscow',
|
||||
country: 'RU',
|
||||
attempts: 3,
|
||||
error: null,
|
||||
});
|
||||
assert.equal(result.vpn.network.asn, 'AS9009');
|
||||
assert.equal(result.vpn.network.provider, 'M247 Europe');
|
||||
assert.ok(calls.every((args) => args.at(-1) === 'https://ipwho.is/' && args.includes('--ipv4')));
|
||||
assert.ok(calls.some((args) => args.includes('--noproxy') && args.includes('*')));
|
||||
assert.ok(calls.some((args) => args.includes('--proxy') && args.includes('http://127.0.0.1:18080')));
|
||||
});
|
||||
|
||||
test('a targeted service row averages three measurements and ignores one transient failure', async () => {
|
||||
const attempts = { direct: 0, vpn: 0 };
|
||||
const execute = async (args) => {
|
||||
|
||||
@@ -20,6 +20,13 @@ const pathResult = {
|
||||
ipv4: { addresses: ['198.51.100.10'], sources: [ip] },
|
||||
ipv6: null,
|
||||
ipv6Source: null,
|
||||
network: {
|
||||
address: '198.51.100.10',
|
||||
asn: 'AS12389',
|
||||
provider: 'Rostelecom',
|
||||
city: 'Moscow',
|
||||
country: 'RU',
|
||||
},
|
||||
sites: [site],
|
||||
};
|
||||
const valid = {
|
||||
@@ -62,6 +69,7 @@ test('unknown target and legacy-full results pass one identity-preserving parser
|
||||
{ ...valid, direct: { ...valid.direct, available: 'yes' } },
|
||||
{ ...valid, direct: { ...valid.direct, ipv4: { addresses: [], sources: [{}] } } },
|
||||
{ ...valid, direct: { ...valid.direct, ipv6: 6 } },
|
||||
{ ...valid, direct: { ...valid.direct, network: { ...valid.direct.network, asn: 12389 } } },
|
||||
{ ...valid, direct: { ...valid.direct, sites: [{ ...site, id: '' }] } },
|
||||
{ ...valid, direct: { ...valid.direct, sites: [{ ...site, status: 'blocked' }] } },
|
||||
{ ...valid, direct: { ...valid.direct, sites: [{ ...site, latencyMs: -1 }] } },
|
||||
@@ -75,7 +83,7 @@ test('unknown target and legacy-full results pass one identity-preserving parser
|
||||
});
|
||||
|
||||
test('serial probes, storage and editor behavior stay panel-owned', () => {
|
||||
assert.match(panel, /const targets = \[[\s\S]*CONNECTIVITY_IP_SOURCES\.map[\s\S]*sites\.map/);
|
||||
assert.match(panel, /const targets = \[[\s\S]*CONNECTIVITY_NETWORK_SOURCE\.id[\s\S]*CONNECTIVITY_IP_SOURCES\.map[\s\S]*sites\.map/);
|
||||
assert.match(panel, /for \(const target of targets\) \{[\s\S]*setActiveTarget\(target\)[\s\S]*await runConnectivityDiagnostics\(customServices, target\)[\s\S]*if \(legacyFullResult\) break/);
|
||||
assert.match(panel, /CUSTOM_SERVICES_KEY = 'harbor-diagnostic-services'/);
|
||||
assert.match(panel, /HIDDEN_SERVICES_KEY = 'harbor-hidden-diagnostic-services'/);
|
||||
@@ -85,3 +93,10 @@ test('serial probes, storage and editor behavior stay panel-owned', () => {
|
||||
assert.match(panel, /retryable: Boolean\(Reflect\.get\(value, 'retryable'\)\)/);
|
||||
assert.match(panel, /requestDetails\(error\)[\s\S]*requestError\.retryable/);
|
||||
});
|
||||
|
||||
test('network identity is one stable compact row for Direct and VPN', () => {
|
||||
assert.match(panel, /data-diagnostic-target=\{CONNECTIVITY_NETWORK_SOURCE\.id\}[\s\S]*<NetworkCell[\s\S]*route="Напрямую, сеть"[\s\S]*<NetworkCell[\s\S]*route="VPN, сеть"/);
|
||||
assert.match(panel, /path\?\.network\?\.asn[\s\S]*path\?\.network\?\.provider[\s\S]*path\?\.network\?\.city[\s\S]*path\?\.network\?\.country/);
|
||||
assert.match(panel, /<Status value=\{status\} route=\{route\} \/><br \/>[\s\S]*client-diagnostics-status is-muted[\s\S]* /);
|
||||
assert.doesNotMatch(panel, /traceroute|tracepath/);
|
||||
});
|
||||
|
||||
@@ -56,7 +56,7 @@ const acceptedLedger = {
|
||||
ruleDeclarationSequences: '9dacb5d7407717ae041cabf6e8e75af7cfccfb7b5e4c387880468db409e93fbd',
|
||||
selectors: '8456f64c608e0411bbc56c0c955d25383391f019fa5af0648a72d40564cfee1a',
|
||||
variableReferences: '42661286e6150115a367068908f79c79951e24fb3fcf8775c2b083bb2d520e38',
|
||||
witnesses: 'd992876195d2c68c00d687c5a174e5657e3480ad8496251afe52d20c9571e82f',
|
||||
witnesses: '7f44af307fd67fc88e274c1dcc6a21a5e064a8be3895d80fd640715e1af42419',
|
||||
},
|
||||
};
|
||||
|
||||
@@ -209,7 +209,7 @@ test('client typography uses the shared semantic scale outside the token owner',
|
||||
|
||||
test('accepted stylesheet has pinned declaration, selector, keyframe, variable, and cascade ledgers', () => {
|
||||
const witnesses = readStyleWitnesses(root);
|
||||
assert.equal(witnesses.length, 737);
|
||||
assert.equal(witnesses.length, 757);
|
||||
assert.equal(witnesses.filter((witness) => witness.unknown || witness.ancestorUnknown).length, 0);
|
||||
const ledger = createStyleLedger(readStyleSource(root), { witnesses });
|
||||
assert.deepEqual(ledger.counts, acceptedLedger.counts);
|
||||
|
||||
Reference in New Issue
Block a user