Enable connectivity diagnostics in Connect mode
This commit is contained in:
@@ -1,10 +1,14 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import test from 'node:test';
|
||||
import {
|
||||
createConnectivityDiagnosticsService,
|
||||
CURL_META_MARKER,
|
||||
} from '../../src/server/services/connectivityDiagnosticsService.js';
|
||||
|
||||
const server = fs.readFileSync(path.resolve(import.meta.dirname, '../../src/server/index.js'), 'utf8');
|
||||
|
||||
function response(body = '', overrides = {}) {
|
||||
return {
|
||||
exitCode: 0,
|
||||
@@ -48,6 +52,11 @@ test('connectivity diagnostics force separate direct and VPN paths', async () =>
|
||||
assert.ok(calls.some((args) => args.includes('--proxy') && args.includes('http://127.0.0.1:18080')));
|
||||
});
|
||||
|
||||
test('connectivity diagnostics endpoint is available in Connect and Gateway', () => {
|
||||
assert.match(server, /const localConnectivityDiagnostics = !remoteDataplane/);
|
||||
assert.doesNotMatch(server, /settings\.appMode !== 'gateway'[\s\S]{0,120}ENDPOINT_NOT_FOUND/);
|
||||
});
|
||||
|
||||
test('connectivity diagnostics reports a likely direct restriction without claiming its owner', async () => {
|
||||
const attempts = new Map();
|
||||
const execute = async (args) => {
|
||||
|
||||
@@ -30,9 +30,14 @@ test('client exposes one local proxy and routes local exceptions before the sele
|
||||
],
|
||||
});
|
||||
|
||||
assert.deepEqual(config.inbounds.map((inbound) => inbound.tag), ['mixed-in']);
|
||||
assert.deepEqual(config.inbounds.map((inbound) => inbound.tag), [
|
||||
'mixed-in',
|
||||
'diagnostics-vpn-in',
|
||||
]);
|
||||
assert.equal(config.inbounds[0].listen_port, 8082);
|
||||
assert.equal(config.inbounds[1].listen_port, 18080);
|
||||
assert.deepEqual(config.route.rules, [
|
||||
{ inbound: ['diagnostics-vpn-in'], outbound: 'test-vpn' },
|
||||
{ domain_suffix: ['ru'], outbound: 'direct' },
|
||||
{ domain: ['example.com'], outbound: 'direct' },
|
||||
{ inbound: ['mixed-in'], outbound: 'test-vpn' },
|
||||
@@ -48,9 +53,10 @@ test('client keeps its local proxy but routes directly when Harbor Gateway is ah
|
||||
});
|
||||
|
||||
assert.deepEqual(config.route.rules, [
|
||||
{ inbound: ['diagnostics-vpn-in'], outbound: 'test-vpn' },
|
||||
{ domain_suffix: ['ru'], outbound: 'direct' },
|
||||
{ inbound: ['mixed-in'], outbound: 'direct' },
|
||||
]);
|
||||
assert.equal(config.route.final, 'direct');
|
||||
assert.deepEqual(config.outbounds.map((outbound) => outbound.tag), ['direct', 'block']);
|
||||
assert.deepEqual(config.outbounds.map((outbound) => outbound.tag), ['test-vpn', 'direct', 'block']);
|
||||
});
|
||||
|
||||
@@ -433,9 +433,9 @@ setInterval(() => {}, 60_000);
|
||||
assert.equal(routed.state.route.localRulesPendingRestart, false);
|
||||
assert.deepEqual(routed.state.route.activeLocalRules, routed.state.route.localRules);
|
||||
assert.deepEqual(JSON.parse(fs.readFileSync(path.join(dir, 'sing-box-config.json'))).route.rules.slice(0, 3), [
|
||||
{ inbound: ['diagnostics-vpn-in'], outbound: testServerId },
|
||||
{ domain: ['example.com'], outbound: 'direct' },
|
||||
{ domain_suffix: ['example.org'], outbound: 'direct' },
|
||||
{ inbound: ['mixed-in'], outbound: testServerId },
|
||||
]);
|
||||
|
||||
await mutation('/api/singbox/stop');
|
||||
|
||||
@@ -42,8 +42,7 @@ test('Gateway device inventory uses the existing accessible responsive drawer',
|
||||
assert.match(panel, /const hasName = Boolean\(device\.alias \|\| device\.hostname\)/);
|
||||
assert.match(panel, /client-device-alias-trigger[\s\S]*client-device-name-separator[\s\S]*client-device-ip/);
|
||||
assert.match(panel, /onClick=\{\(\) => startEditing\(device\)\}/);
|
||||
assert.match(panel, /<span className="client-device-edit-wrap client-tooltip-anchor">/);
|
||||
assert.doesNotMatch(panel, /\{!hasName && <span className="client-device-edit-wrap/);
|
||||
assert.match(panel, /\{!hasName && <span className="client-device-edit-wrap client-tooltip-anchor">/);
|
||||
assert.match(panel, /pencilAnimationId === device\.id \? ' is-writing'/);
|
||||
assert.match(panel, /onAnimationEnd=\{\(\) => setPencilAnimationId/);
|
||||
assert.match(panel, /COPY_FEEDBACK_MS = 800/);
|
||||
@@ -117,7 +116,8 @@ test('Gateway device inventory uses the existing accessible responsive drawer',
|
||||
assert.match(styles, /\.client-device-traffic strong \{[\s\S]*font-size: 10px/);
|
||||
assert.match(styles, /\.client-device-name-separator \{[\s\S]*color: var\(--client-muted\)/);
|
||||
assert.match(styles, /\.client-device-ip\.is-copied \{[\s\S]*client-device-ip-copy 800ms/);
|
||||
assert.match(styles, /\.client-device-main \{[^}]*height: 34px[\s\S]*align-items: flex-end[\s\S]*padding: 11px 27px 0 0/);
|
||||
assert.match(styles, /\.client-device-main \{[^}]*height: 34px[\s\S]*align-items: flex-end[\s\S]*padding: 11px 0 0/);
|
||||
assert.match(styles, /\.client-device-main:has\(\.client-device-edit-wrap\) \{[\s\S]*padding-right: 27px/);
|
||||
assert.match(styles, /\.client-device-last-seen \{[^}]*height: 10px[\s\S]*align-items: center/);
|
||||
assert.match(styles, /\.client-device-traffic-value\.has-delta > \.is-total[\s\S]*translateY\(-0\.18em\)/);
|
||||
assert.match(styles, /\.client-device-last-seen\.is-online \{[\s\S]*color: var\(--client-accent\)/);
|
||||
@@ -129,6 +129,8 @@ test('Gateway device inventory uses the existing accessible responsive drawer',
|
||||
assert.match(styles, /\.client-device-edit-wrap \{[\s\S]*position: absolute;[\s\S]*right: 0;[\s\S]*bottom: 0/);
|
||||
assert.match(styles, /\.client-device-edit\.is-writing svg \{[\s\S]*client-device-pencil-write 620ms/);
|
||||
assert.match(styles, /@keyframes client-device-pencil-write[\s\S]*0%, 100%[\s\S]*translate\(1px, -1px\) rotate\(-5deg\)/);
|
||||
assert.match(styles, /\.client-device-traffic-plot \{[\s\S]*height: 100%;[\s\S]*min-height: 0;[\s\S]*overflow: hidden/);
|
||||
assert.match(styles, /\.client-device-traffic-plot svg \{[\s\S]*height: 100%;[\s\S]*overflow: hidden/);
|
||||
assert.match(styles, /\.client-devices-sort:hover \.client-devices-sort-icon[\s\S]*rotate\(360deg\)/);
|
||||
assert.match(styles, /\.client-devices-refresh-ring circle[\s\S]*client-devices-refresh-progress 15s/);
|
||||
assert.match(styles, /@media \(prefers-reduced-motion: reduce\)[\s\S]*\.client-device-policy svg[\s\S]*\.client-device-alias-trigger[\s\S]*\.client-device-ip[\s\S]*\.client-device-traffic-value > span[\s\S]*\.client-device-traffic-breakdown[\s\S]*\.client-device-traffic-lines[\s\S]*\.client-text-morph-value/);
|
||||
|
||||
@@ -91,6 +91,8 @@ test('secondary menus share one right rail and both drawers open from the right'
|
||||
assert.match(component, /client-instructions-toggle[\s\S]*client-local-rules-toggle/);
|
||||
assert.match(component, /client-diagnostics-toggle/);
|
||||
assert.match(component, /<ConnectivityDiagnosticsPanel/);
|
||||
assert.doesNotMatch(component, /\{isGateway && <button[\s\S]{0,120}diagnosticsToggleRef/);
|
||||
assert.match(component, /<ConnectivityDiagnosticsPanel[\s\S]*isGateway=\{isGateway\}/);
|
||||
assert.match(component, /Локальные правила недоступны: сейчас работают правила Gateway/);
|
||||
assert.match(rule('.client-secondary-menu'), /right:\s*max\(14px, env\(safe-area-inset-right\)\)/);
|
||||
assert.match(rule('.client-secondary-menu'), /display:\s*grid/);
|
||||
@@ -117,6 +119,7 @@ test('connectivity diagnostics render stable compact tables before the first run
|
||||
assert.equal((diagnostics.match(/<table className="client-diagnostics-table"/g) || []).length, 2);
|
||||
assert.match(diagnostics, /Добавить свой сервис/);
|
||||
assert.match(diagnostics, /client-diagnostics-refresh/);
|
||||
assert.match(diagnostics, /isGateway \? 'Gateway' : 'Connect'/);
|
||||
assert.doesNotMatch(diagnostics, /Проверить ещё раз|client-diagnostics-empty|client-diagnostics-run/);
|
||||
assert.match(diagnostics, /\{\(error \|\| result\) && <div className="client-diagnostics-feedback"/);
|
||||
assert.doesNotMatch(diagnostics, /PathDetails|client-diagnostics-details|Технические детали/);
|
||||
|
||||
Reference in New Issue
Block a user