CH-J Server ManagerSerververwaltung über SSH
Menü
Veröffentlichte Quellen

CH-J Server Manager

Durchsuchen Sie Verzeichnisse und Dateien einer bestimmten Anwendungsausgabe.

Quellen als ZIP herunterladen
CH-J Proprietary Software License 1.14

Die Quellen werden unter der CH-J Proprietary Software License 1.14 bereitgestellt. Ihre Verfügbarkeit ändert die Lizenzbedingungen nicht und gewährt keine zusätzlichen Rechte.

7,0 KB · 114 ZeilenDatei herunterladen
1"use strict";
3const test = require("node:test");
4const assert = require("node:assert/strict");
5const { EventEmitter } = require("node:events");
6const { setImmediate: nextTurn } = require("node:timers/promises");
7const { LatencyMonitor, measurePing, parsePing } = require("../src/main/sessions/latencyMonitor");
8const { SessionManager } = require("../src/main/sessions/sessionManager");
10test("ICMP reply parser accepts platform/localized replies and never invents failed/upper-bound values", () => {
11 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"]) assert.equal(parsePing(reply), 24.1);
12 for (const reply of ["100% packet loss", "Request timed out.", "Reply: time<1ms", "time=0 ms"]) assert.equal(parsePing(reply), null);
13});
15test("ICMP uses fixed executable arguments without a shell on all client platforms and addresses", async () => {
16 for (const platform of ["win32", "darwin", "linux"]) {
17 for (const address of ["192.0.2.1", "2001:db8::1"]) {
18 let call;
19 assert.equal(await measurePing(address, { platform, run(executable, args, options, callback) {
20 call = { executable, args, options }; callback(null, "time=31 ms");
21 } }), 31);
22 assert.equal(call.args.at(-1), address); assert.equal(call.options.shell, false);
23 assert.equal(call.options.timeout, 5000); assert.ok(call.args.includes("1"));
24 }
25 }
26 assert.equal(await measurePing("host;touch /tmp/unsafe", { run() { assert.fail("Untrusted command executed"); } }), null);
27 assert.equal(await measurePing("192.0.2.1", { run(_exe, _args, _options, cb) { cb(new Error("Blocked")); } }), null);
28});
30test("monitor measures monotonic SSH duration, tolerates ICMP loss, refreshes without overlap and stops", async (t) => {
31 t.mock.timers.enable({ apis: ["setTimeout"] });
32 let now = 100, calls = 0, resolveRequest;
33 const values = [];
34 const monitor = new LatencyMonitor({ now: () => now, ping: async () => null, intervalMs: 12000 });
35 const stop = monitor.start({ address: "192.0.2.1" }, () => { calls++; return new Promise((resolve) => { resolveRequest = resolve; }); }, (v) => values.push(v));
36 await nextTurn();
37 t.mock.timers.tick(60000); await nextTurn(); assert.equal(calls, 1);
38 now = 131.25; resolveRequest(); await nextTurn();
39 assert.equal(values[0].sshRttMs, 31.25); assert.equal(values[0].pingMs, null);
40 t.mock.timers.tick(12000); await nextTurn(); assert.equal(calls, 2);
41 stop(); resolveRequest(); await nextTurn(); t.mock.timers.tick(60000); await nextTurn();
42 assert.equal(calls, 2); assert.equal(values.length, 1);
43});
45test("failed SSH request publishes unavailable without affecting another session", async () => {
46 const values = [], stops = [];
47 const monitor = new LatencyMonitor({ ping: async (address) => address === "192.0.2.1" ? 24 : null });
48 stops.push(monitor.start({ address: "192.0.2.1" }, async () => { throw new Error("Timeout"); }, (v) => values.push(["a", v])));
49 stops.push(monitor.start({ address: "192.0.2.2" }, async () => {}, (v) => values.push(["b", v])));
50 await nextTurn(); stops.forEach((stop) => stop());
51 assert.equal(values.find(([id]) => id === "a")[1].pingMs, 24);
52 assert.equal(values.find(([id]) => id === "a")[1].sshRttMs, null);
53 assert.equal(values.find(([id]) => id === "b")[1].pingMs, null);
54});
56test("connected sessions start/stop monitoring and reject stale results after reconnect", async () => {
57 const fingerprint = "ab".repeat(32), callbacks = [], stops = [];
58 const manager = new SessionManager({
59 profileService: { get: () => ({ id: "p", host: "192.0.2.1", port: 22, username: "user", authMethod: "password" }), getHostKey: () => fingerprint, markUsed() {} },
60 latencyMonitor: { start(record, request, publish) { callbacks.push({ record, request, publish }); const stop = { count: 0 }; stops.push(stop); return () => { stop.count++; }; } },
61 clientFactory() {
62 const client = new EventEmitter();
63 client.connect = () => setImmediate(() => client.emit("ready")); client.end = () => {};
64 client.shell = (_opts, cb) => { const stream = new EventEmitter(); stream.end = () => {}; cb(null, stream); };
65 client.exec = (command, cb) => {
66 assert.equal(command, "true"); const stream = new EventEmitter(); stream.stderr = new EventEmitter(); cb(null, stream); setImmediate(() => stream.emit("close", 0));
67 };
68 return client;
69 }
70 });
71 try {
72 await manager.connect({ sessionId: "s", profileId: "p", password: "secret" });
73 assert.equal(callbacks.length, 1); await callbacks[0].request();
74 callbacks[0].publish({ pingMs: 24, sshRttMs: 31 });
75 assert.equal(manager.list()[0].latency.sshRttMs, 31);
76 await manager.connect({ sessionId: "s", profileId: "p", password: "secret" });
77 assert.equal(stops[0].count, 1); assert.equal(manager.list()[0].latency.sshRttMs, null);
78 callbacks[0].publish({ pingMs: 999, sshRttMs: 999 });
79 assert.equal(manager.list()[0].latency.sshRttMs, null);
80 callbacks[1].publish({ pingMs: null, sshRttMs: 12 });
81 assert.equal(manager.list()[0].state, "connected");
82 } finally { await manager.disconnectAll(); }
83 assert.equal(stops[1].count, 1);
84});
86test("SSH request timeout closes the channel, releases pending work and accepts no late result", async (t) => {
87 t.mock.timers.enable({ apis: ["setTimeout"] });
88 let closed = 0;
89 const channel = new EventEmitter(); channel.stderr = new EventEmitter(); channel.close = () => { closed++; };
90 const record = { client: { exec(_cmd, cb) { cb(null, channel); } } };
91 const manager = new SessionManager({});
92 const result = assert.rejects(manager._execFixed(record, "true", 5000), { code: "REMOTE_COMMAND_TIMEOUT" });
93 t.mock.timers.tick(5000); await result;
94 assert.equal(closed, 1); assert.equal(record.pendingExec.size, 0);
95 channel.emit("close", 0);
96});
98test("terminal latency displays milliseconds, unavailable states and resets on disconnect", () => {
99 const fs = require("node:fs"), vm = require("node:vm");
100 const source = fs.readFileSync(require.resolve("../src/renderer/app.js"), "utf8");
101 const fn = source.slice(source.indexOf("function renderTerminalLatency()"), source.indexOf("async function connectTerminal"));
102 const element = {}, state = { terminalStateKind: "connected", terminalLatency: { pingMs: 24, sshRttMs: 31.25 } };
103 const { createI18n } = require("../src/renderer/i18n");
104 for (const language of ["cs", "de", "en"]) {
105 vm.runInNewContext(fn + "\nrenderTerminalLatency();", { state, $: () => element, t: createI18n(language).t });
106 assert.match(element.textContent, /24\.0 ms/); assert.match(element.textContent, /31\.3 ms/); assert.equal(element.hidden, false);
107 }
108 state.terminalLatency = { pingMs: null, sshRttMs: 0 };
109 vm.runInNewContext(fn + "\nrenderTerminalLatency();", { state, $: () => element, t: createI18n("en").t });
110 assert.match(element.textContent, /Unavailable/); assert.doesNotMatch(element.textContent, /0 ms/);
111 state.terminalStateKind = "disconnected";
112 vm.runInNewContext(fn + "\nrenderTerminalLatency();", { state, $: () => element, t: createI18n("en").t });
113 assert.equal(element.hidden, true);
114});

SHA-256: ae6d2c8dab2044f5634d2cd5473eceae224fc8246c3ffd39ed10c4d325480968

SHA-256 des Archivs: 5ac91caf4fa32a6fdb114f2430deed486fbe7489d5eea343d1f034169fafb5e0