Публикуван източник
Изтегляне на източник ZIP
CH-J Server Manager
Преглед на директории и файлове за конкретна версия на приложението.
CH-J / Server Manager
377 Файлове
core-20261019T111616Z-160a00bfcddc
Добавено: · Построена:
1
"use strict";3
const test = require("node:test");4
const assert = require("node:assert/strict");5
const { EventEmitter } = require("node:events");6
const { setImmediate: nextTurn } = require("node:timers/promises");7
const { LatencyMonitor, measurePing, parsePing } = require("../src/main/sessions/latencyMonitor");8
const { SessionManager } = require("../src/main/sessions/sessionManager");10
test("ICMP reply parser reads every reply line on macOS, Linux and localized Windows, never statistics lines", () => {11
const { parsePingReplies } = require("../src/main/sessions/latencyMonitor");12
for (const reply of ["64 bytes from 192.0.2.1: icmp_seq=1 ttl=64 time=24.1 ms", "Antwort von 192.0.2.1: Bytes=32 Zeit=24,1ms TTL=64", "16 bytes from 2001:db8::1, icmp_seq=0 hlim=64 time=24.1 ms"]) assert.equal(parsePing(reply), 24.1);13
for (const reply of ["100% packet loss", "Request timed out.", "Minimum = 5ms, Maximum = 6ms, Average = 5ms", "Reply from 192.0.2.1: bytes=32 time<1ms TTL=128"]) assert.equal(parsePing(reply), null);14
const windowsCzech = "Odpověď od 192.0.2.1: bajty=32 čas=5ms TTL=128\r\nOdpověď od 192.0.2.1: bajty=32 čas=7ms TTL=128\r\nVypršel časový limit žádosti.\r\n Minimum = 5ms, Maximum = 7ms, Průměr = 6ms";15
assert.deepEqual(parsePingReplies(windowsCzech).map((reply) => reply.ms), [5, 7]);16
assert.deepEqual(parsePingReplies("Reply from 192.0.2.1: bytes=32 time<1ms TTL=128"), [{ ms: 1, lessThan: true }]);17
});19
test("ICMP runs 3 probes with fixed arguments and no shell; reports median, loss and concrete failure reasons", async () => {20
for (const platform of ["win32", "darwin", "linux"]) {21
for (const address of ["192.0.2.1", "2001:db8::1"]) {22
let call;23
const result = await measurePing(address, { platform, run(executable, args, options, callback) {24
call = { executable, args, options };25
callback(null, "64 bytes: ttl=64 time=31 ms\n64 bytes: ttl=64 time=40 ms\n64 bytes: ttl=64 time=20 ms\n");26
} });27
assert.equal(result.ms, 31); assert.equal(result.loss, 0); assert.equal(result.status, "ok"); assert.equal(result.family, address.includes(":") ? 6 : 4);28
assert.equal(call.args.at(-1), address); assert.equal(call.options.shell, false); assert.ok(call.args.includes("3"));29
if (platform === "darwin") assert.equal(call.executable, address.includes(":") ? "/sbin/ping6" : "/sbin/ping");30
}31
}32
const partial = await measurePing("192.0.2.1", { platform: "linux", run(_e, _a, _o, cb) { cb(Object.assign(new Error("exit 1"), { code: 1 }), "64 bytes: ttl=64 time=10 ms\n"); } });33
assert.equal(partial.ms, 10); assert.ok(Math.abs(partial.loss - 2 / 3) < 1e-9);34
const untrusted = await measurePing("host;touch /tmp/unsafe", { run() { assert.fail("Untrusted command executed"); } });35
assert.equal(untrusted.ms, null); assert.equal(untrusted.status, "error");36
const cases = [37
[Object.assign(new Error("exit 2"), { code: 2 }), "", "timeout"],38
[Object.assign(new Error("spawn ping ENOENT"), { code: "ENOENT" }), "", "unsupported"],39
[Object.assign(new Error("exit 68"), { code: 68 }), "ping: sendto: Operation not permitted", "denied"],40
[Object.assign(new Error("exit 1"), { code: 1 }), "Reply from 192.0.2.254: Destination host unreachable.", "unreachable"]41
];42
for (const [error, output, status] of cases) {43
const result = await measurePing("192.0.2.1", { platform: "darwin", run(_e, _a, _o, cb) { cb(error, output, ""); } });44
assert.equal(result.status, status); assert.equal(result.ms, null); assert.ok(result.reason.length > 10);45
}46
});48
test("monitor takes several monotonic SSH samples, keeps ICMP independent, never overlaps and stops", async (t) => {49
t.mock.timers.enable({ apis: ["setTimeout"] });50
let now = 100, calls = 0;51
const values = [];52
const durations = [31.25, 10, 20];53
const monitor = new LatencyMonitor({ now: () => now, ping: async () => ({ ms: null, status: "timeout", reason: "blocked" }), intervalMs: 10000 });54
const stop = monitor.start({ address: "192.0.2.1" }, async () => { now += durations[calls % 3]; calls++; }, (v) => values.push(v));55
for (let index = 0; index < 5; index += 1) await nextTurn();56
assert.equal(calls, 3);57
assert.equal(values[0].sshRttMs, 20, "median of three samples"); assert.equal(values[0].sshRttMethod, "command");58
assert.equal(values[0].pingMs, null); assert.equal(values[0].pingStatus, "timeout");59
t.mock.timers.tick(10000); for (let index = 0; index < 5; index += 1) await nextTurn();60
assert.equal(calls, 6);61
stop(); t.mock.timers.tick(60000); await nextTurn();62
assert.equal(calls, 6); assert.equal(values.length, 2);63
});65
test("transport measurements are used directly and a failed SSH round trip does not hide ICMP", async () => {66
const values = [], stops = [];67
const monitor = new LatencyMonitor({ ping: async (address) => address === "192.0.2.1" ? { ms: 24, status: "ok", loss: 0, family: 4 } : { ms: null, status: "timeout" } });68
stops.push(monitor.start({ address: "192.0.2.1" }, async () => { throw new Error("Timeout"); }, (v) => values.push(["a", v])));69
stops.push(monitor.start({ address: "192.0.2.2" }, async () => ({ ms: 3.5, method: "transport" }), (v) => values.push(["b", v])));70
for (let index = 0; index < 6; index += 1) await nextTurn();71
stops.forEach((stop) => stop());72
const a = values.find(([id]) => id === "a")[1]; const b = values.find(([id]) => id === "b")[1];73
assert.equal(a.pingMs, 24); assert.equal(a.sshRttMs, null); assert.match(a.sshRttError, /Timeout/);74
assert.equal(b.pingMs, null); assert.equal(b.sshRttMs, 3.5); assert.equal(b.sshRttMethod, "transport");75
});77
test("SSH round trip uses the transport keepalive request and falls back to a shell-neutral no-op", async () => {78
const manager = new SessionManager({});79
let pings = 0;80
const client = { _callbacks: [], _protocol: { ping() { pings++; setTimeout(() => client._callbacks.shift()(true), 5); } } };81
const result = await manager._sshRoundTrip({ client });82
assert.equal(result.method, "transport"); assert.ok(result.ms >= 0); assert.equal(pings, 1);83
let command;84
const channel = new EventEmitter(); channel.stderr = new EventEmitter();85
const fallback = manager._sshRoundTrip({ client: { exec(cmd, cb) { command = cmd; cb(null, channel); setImmediate(() => channel.emit("close", 0)); } } });86
assert.equal((await fallback).method, "command"); assert.equal(command, "exit 0", "valid in sh, cmd.exe and PowerShell");87
});89
test("connected sessions start/stop monitoring and reject stale results after reconnect", async () => {90
const fingerprint = "ab".repeat(32), callbacks = [], stops = [];91
const manager = new SessionManager({92
profileService: { get: () => ({ id: "p", host: "192.0.2.1", port: 22, username: "user", authMethod: "password" }), getHostKey: () => fingerprint, markUsed() {} },93
latencyMonitor: { start(record, request, publish) { callbacks.push({ record, request, publish }); const stop = { count: 0 }; stops.push(stop); return () => { stop.count++; }; } },94
clientFactory() {95
const client = new EventEmitter();96
client.connect = () => setImmediate(() => client.emit("ready")); client.end = () => {};97
client.shell = (_opts, cb) => { const stream = new EventEmitter(); stream.end = () => {}; cb(null, stream); };98
client.exec = (command, cb) => {99
assert.equal(command, "exit 0"); const stream = new EventEmitter(); stream.stderr = new EventEmitter(); cb(null, stream); setImmediate(() => stream.emit("close", 0));100
};101
return client;102
}103
});104
try {105
await manager.connect({ sessionId: "s", profileId: "p", password: "secret" });106
assert.equal(callbacks.length, 1); await callbacks[0].request();107
callbacks[0].publish({ pingMs: 24, sshRttMs: 31 });108
assert.equal(manager.list()[0].latency.sshRttMs, 31);109
await manager.connect({ sessionId: "s", profileId: "p", password: "secret" });110
assert.equal(stops[0].count, 1); assert.equal(manager.list()[0].latency.sshRttMs, null);111
callbacks[0].publish({ pingMs: 999, sshRttMs: 999 });112
assert.equal(manager.list()[0].latency.sshRttMs, null);113
callbacks[1].publish({ pingMs: null, sshRttMs: 12 });114
assert.equal(manager.list()[0].state, "connected");115
} finally { await manager.disconnectAll(); }116
assert.equal(stops[1].count, 1);117
});119
test("SSH request timeout closes the channel, releases pending work and accepts no late result", async (t) => {120
t.mock.timers.enable({ apis: ["setTimeout"] });121
let closed = 0;122
const channel = new EventEmitter(); channel.stderr = new EventEmitter(); channel.close = () => { closed++; };123
const record = { client: { exec(_cmd, cb) { cb(null, channel); } } };124
const manager = new SessionManager({});125
const result = assert.rejects(manager._execFixed(record, "true", 5000), { code: "REMOTE_COMMAND_TIMEOUT" });126
t.mock.timers.tick(5000); await result;127
assert.equal(closed, 1); assert.equal(record.pendingExec.size, 0);128
channel.emit("close", 0);129
});131
test("terminal latency displays milliseconds, concrete unavailable reasons and resets on disconnect", () => {132
const fs = require("node:fs"), vm = require("node:vm");133
const source = fs.readFileSync(require.resolve("../src/renderer/app.js"), "utf8");134
const fn = source.slice(source.indexOf("function renderTerminalLatency()"), source.indexOf("async function connectTerminal"));135
const element = {}, state = { terminalStateKind: "connected", terminalLatency: { pingMs: 24, pingStatus: "ok", pingLoss: 1 / 3, pingFamily: 6, sshRttMs: 31.25, sshRttMethod: "transport", measuredAt: "x" } };136
const { createI18n } = require("../src/renderer/i18n");137
for (const language of ["cs", "de", "en"]) {138
const i18n = createI18n(language);139
vm.runInNewContext(fn + "\nrenderTerminalLatency();", { state, $: () => element, t: (key, values) => i18n.t(key, values) });140
assert.match(element.textContent, /24\.0 ms IPv6/); assert.match(element.textContent, /33 %/); assert.match(element.textContent, /31\.3 ms/); assert.equal(element.hidden, false);141
}142
const en = createI18n("en");143
state.terminalLatency = { pingMs: null, pingStatus: "timeout", sshRttMs: 4.2, measuredAt: "x" };144
vm.runInNewContext(fn + "\nrenderTerminalLatency();", { state, $: () => element, t: (key, values) => en.t(key, values) });145
assert.match(element.textContent, /firewall may block ICMP/); assert.match(element.textContent, /4\.2 ms/, "SSH RTT stays visible when ICMP is blocked");146
state.terminalLatency = null;147
vm.runInNewContext(fn + "\nrenderTerminalLatency();", { state, $: () => element, t: (key, values) => en.t(key, values) });148
assert.match(element.textContent, /measuring/);149
state.terminalStateKind = "disconnected";150
vm.runInNewContext(fn + "\nrenderTerminalLatency();", { state, $: () => element, t: (key, values) => en.t(key, values) });151
assert.equal(element.hidden, true);152
});SHA-256
024443c13a757f6845e4bc95fa0d725b5525c2b0b9cd583608fc0298a8cc98a2CH-J Proprietary Software License 1.14 · Източникът е предоставен под CH-J Proprietary Software License 1.14. Неговата наличност не променя лицензионните условия или не предоставя допълнителни права.
Архив SHA-256
a9605c0870a223b6a8542bb0705edd51d8b04f3069582653417a8397fabcc09a