Refactor VPN proxy client implementation
This commit is contained in:
@@ -5,9 +5,11 @@ import test from 'node:test';
|
||||
import {
|
||||
createConnectivityDiagnosticsService,
|
||||
CURL_META_MARKER,
|
||||
} from '../../src/server/services/connectivityDiagnosticsService.js';
|
||||
} from '../../dist/server/services/connectivityDiagnosticsService.js';
|
||||
import { createConnectivityDiagnosticsUseCase } from '../../dist/server/features/diagnostics/index.js';
|
||||
import { createConnectivityDiagnosticsRoute } from '../../dist/server/http/routes/connectivityDiagnosticsRoute.js';
|
||||
|
||||
const server = fs.readFileSync(path.resolve(import.meta.dirname, '../../src/server/index.js'), 'utf8');
|
||||
const server = fs.readFileSync(path.resolve(import.meta.dirname, '../../src/server/index.ts'), 'utf8');
|
||||
|
||||
function response(body = '', overrides = {}) {
|
||||
return {
|
||||
@@ -52,10 +54,154 @@ 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', () => {
|
||||
test('connectivity diagnostics endpoint is available in Connect and Gateway through one owner', () => {
|
||||
assert.match(server, /const localConnectivityDiagnostics = !remoteDataplane/);
|
||||
assert.doesNotMatch(server, /settings\.appMode !== 'gateway'[\s\S]{0,120}ENDPOINT_NOT_FOUND/);
|
||||
assert.match(server, /runConnectivityDiagnostics\(services, target\)/);
|
||||
assert.match(server, /createConnectivityDiagnosticsUseCase\(\{/);
|
||||
assert.match(server, /createConnectivityDiagnosticsRoute\(\{/);
|
||||
assert.match(server, /connectivityDiagnosticsRoute\.handle\(req, res\)/);
|
||||
assert.doesNotMatch(server, /['"]\/api\/diagnostics\/connectivity['"]/);
|
||||
assert.doesNotMatch(server, /appliedServerId\s*\|\|\s*state\.selectedServerId/);
|
||||
});
|
||||
|
||||
test('connectivity use case captures applied server before probes and preserves result fields', async () => {
|
||||
const events = [];
|
||||
let state = {
|
||||
appliedServerId: 'applied',
|
||||
selectedServerId: 'selected',
|
||||
servers: [
|
||||
{ id: 'applied', label: 'Applied before probe' },
|
||||
{ id: 'selected', label: 'Selected' },
|
||||
],
|
||||
};
|
||||
let releaseProbe;
|
||||
const probe = new Promise((resolve) => { releaseProbe = resolve; });
|
||||
const sourceResult = {
|
||||
checkedAt: 'now',
|
||||
direct: { available: true },
|
||||
vpn: { available: true, server: { id: 'stale', label: 'Stale' }, detail: 7 },
|
||||
assessment: { summary: 'available' },
|
||||
};
|
||||
const useCase = createConnectivityDiagnosticsUseCase({
|
||||
readState: () => {
|
||||
events.push('state');
|
||||
return state;
|
||||
},
|
||||
runDiagnostics: async (services, target) => {
|
||||
events.push(['probe', services, target]);
|
||||
await probe;
|
||||
return sourceResult;
|
||||
},
|
||||
});
|
||||
const resultPromise = useCase.run({ raw: true }, 42);
|
||||
state.servers[0].label = 'Changed during probe';
|
||||
releaseProbe();
|
||||
const result = await resultPromise;
|
||||
|
||||
assert.deepEqual(events, ['state', ['probe', { raw: true }, 42]]);
|
||||
assert.deepEqual(result, {
|
||||
...sourceResult,
|
||||
vpn: {
|
||||
...sourceResult.vpn,
|
||||
server: { id: 'applied', label: 'Applied before probe' },
|
||||
},
|
||||
});
|
||||
assert.deepEqual(sourceResult.vpn.server, { id: 'stale', label: 'Stale' });
|
||||
});
|
||||
|
||||
test('connectivity use case keeps applied priority, selected fallback and error identity', async () => {
|
||||
const result = { vpn: { available: false }, marker: true };
|
||||
const selected = createConnectivityDiagnosticsUseCase({
|
||||
readState: () => ({
|
||||
appliedServerId: '',
|
||||
selectedServerId: 'selected',
|
||||
servers: [{ id: 'selected', label: 'Selected' }],
|
||||
}),
|
||||
runDiagnostics: async () => result,
|
||||
});
|
||||
assert.deepEqual((await selected.run(null, null)).vpn.server, {
|
||||
id: 'selected',
|
||||
label: 'Selected',
|
||||
});
|
||||
|
||||
const missingApplied = createConnectivityDiagnosticsUseCase({
|
||||
readState: () => ({
|
||||
appliedServerId: 'missing',
|
||||
selectedServerId: 'selected',
|
||||
servers: [{ id: 'selected', label: 'Selected' }],
|
||||
}),
|
||||
runDiagnostics: async () => result,
|
||||
});
|
||||
assert.equal((await missingApplied.run([], null)).vpn.server, null);
|
||||
|
||||
const stateError = new Error('state failed');
|
||||
let probes = 0;
|
||||
const brokenState = createConnectivityDiagnosticsUseCase({
|
||||
readState: () => { throw stateError; },
|
||||
runDiagnostics: async () => { probes += 1; return result; },
|
||||
});
|
||||
await assert.rejects(brokenState.run([], null), (error) => error === stateError);
|
||||
assert.equal(probes, 0);
|
||||
|
||||
const probeError = new Error('probe failed');
|
||||
const brokenProbe = createConnectivityDiagnosticsUseCase({
|
||||
readState: () => ({ servers: [] }),
|
||||
runDiagnostics: async () => { throw probeError; },
|
||||
});
|
||||
await assert.rejects(brokenProbe.run([], null), (error) => error === probeError);
|
||||
});
|
||||
|
||||
function routeResponse() {
|
||||
return {
|
||||
writeHead(status, headers) {
|
||||
this.status = status;
|
||||
this.headers = headers;
|
||||
},
|
||||
end(payload) {
|
||||
this.payload = JSON.parse(payload);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
test('connectivity route preserves exact URL, defaults and raw response', async () => {
|
||||
const calls = [];
|
||||
let body = {};
|
||||
let bodyReads = 0;
|
||||
const route = createConnectivityDiagnosticsRoute({
|
||||
diagnostics: {
|
||||
run: async (...args) => {
|
||||
calls.push(args);
|
||||
return { checkedAt: 'now', vpn: { server: null } };
|
||||
},
|
||||
},
|
||||
readBody: async () => {
|
||||
bodyReads += 1;
|
||||
return body;
|
||||
},
|
||||
});
|
||||
const res = routeResponse();
|
||||
assert.equal(await route.handle({
|
||||
method: 'POST',
|
||||
url: '/api/diagnostics/connectivity',
|
||||
}, res), true);
|
||||
assert.deepEqual(calls, [[[], null]]);
|
||||
assert.equal(res.status, 200);
|
||||
assert.equal(res.headers['content-type'], 'application/json; charset=utf-8');
|
||||
assert.deepEqual(res.payload, { checkedAt: 'now', vpn: { server: null } });
|
||||
|
||||
body = { services: null, target: 17 };
|
||||
await route.handle({ method: 'POST', url: '/api/diagnostics/connectivity' }, routeResponse());
|
||||
assert.deepEqual(calls.at(-1), [null, 17]);
|
||||
|
||||
for (const [method, url] of [
|
||||
['GET', '/api/diagnostics/connectivity'],
|
||||
['POST', '/api/diagnostics/connectivity?target=all'],
|
||||
['POST', '/api/diagnostics/other'],
|
||||
]) {
|
||||
assert.equal(await route.handle({ method, url }, routeResponse()), false);
|
||||
}
|
||||
assert.equal(bodyReads, 2);
|
||||
});
|
||||
|
||||
test('a targeted IP row uses three samples and keeps the majority address', async () => {
|
||||
|
||||
Reference in New Issue
Block a user