CH-J Server Managerserver management over SSH
Menu
Published source

CH-J Server Manager

Browse directories and files for a specific application release.

Download source ZIP
CH-J Proprietary Software License 1.14

Source is provided under the CH-J Proprietary Software License 1.14. Its availability does not change the license terms or grant additional rights.

7,7 KB · 76 linesDownload file
1"use strict";
2const test = require("node:test");
3const assert = require("node:assert/strict");
4const fs = require("node:fs");
5const os = require("node:os");
6const path = require("node:path");
7const { parseTarget, normalizeOptions, stats } = require("../src/main/diagnostics/common");
8const { DiagnosticsHistory, scrub, exportReport } = require("../src/main/diagnostics/historyStore");
9const { DiagnosticsService, sameIp } = require("../src/main/diagnostics/diagnosticsService");
10const { once } = require("node:events");
11const fixtureReport = () => ({ id: "a".repeat(32), createdAt: new Date().toISOString(), status: "completed", options: normalizeOptions({ target: "https://localhost/?token=secret", tools: ["ping"] }), addresses: [{ address: "127.0.0.1", family: 4 }], results: { ping: [{ address: "127.0.0.1", status: "success", packets: [{ sequence: 1, rttMs: 1.25 }] }] }, progress: [] });
12test("diagnostics validates URLs, IDNA, literals and bounded options before any network activity", () => {
13 assert.equal(parseTarget("2001:db8::1").url, "https://[2001:db8::1]/");
14 assert.equal(parseTarget("[::1]").host, "::1");
15 assert.equal(parseTarget("https://münchen.de/a").host, "xn--mnchen-3ya.de");
16 assert.equal(parseTarget("host.test:8443/path").port, 8443);
17 for (const value of ["", "a b", "a\n--flag", "-bad.test", "https://user:password@host.test", "file:///tmp/a", "https://host.test\\evil", "; rm -rf /"]) assert.throws(() => parseTarget(value));
18 for (const options of [{ count: 0 }, { timeoutMs: 100000 }, { maxHops: 100 }, { ports: Array.from({ length: 17 }, (_, i) => i + 1).join(",") }, { ports: "22;bad" }, { selectedIp: "--help" }, { protocols: ["4"] }, { tools: ["shell"] }, { resolver: "custom", customDns: "evil.test" }]) assert.throws(() => normalizeOptions({ target: "localhost", ...options }));
19 assert.ok(sameIp("::1", "0:0:0:0:0:0:0:1")); assert.equal(sameIp("::1", "127.0.0.1"), false);
20});
21test("statistics exclude unknowns and use measured sequence for jitter", () => {
22 const result = stats([1, null, 5, undefined, NaN, 2, 4]);
23 assert.equal(result.average, 3); assert.equal(result.median, 3); assert.equal(result.p95, 5);
24 assert.equal(result.jitter, 3); assert.equal(result.stddev, Math.sqrt(2.5));
25 assert.equal(stats([]).average, null);
26});
27test("history round-trips private metadata, sanitizes secrets, exports CSV/TXT and rejects malformed imports", (t) => {
28 const root = fs.mkdtempSync(path.join(os.tmpdir(), "chj-diagnostics-")); t.after(() => fs.rmSync(root, { recursive: true, force: true }));
29 const history = new DiagnosticsHistory(root), report = fixtureReport();
30 report.options.target.input = "localhost/?token=othersecret";
31 report.results.http = [{ comparisons: [{ protocol: "1.1", address: "127.0.0.1", samples: [{ status: "success", headers: { "set-cookie": "session=secret", authorization: "secret" }, timings: { totalMs: 12 } }] }] }];
32 history.add(report); const content = fs.readFileSync(history.file, "utf8");
33 assert.ok(!content.includes("secret")); assert.ok(!content.includes("?token"));
34 // Windows uses ACLs and does not expose POSIX permission bits through stat.
35 if (process.platform !== "win32") assert.equal(fs.statSync(history.file).mode & 0o777, 0o600);
36 const loaded = new DiagnosticsHistory(root); assert.equal(loaded.list().length, 1);
37 const exported = exportReport(loaded.get(report.id), "json"); const imported = new DiagnosticsHistory(); assert.equal(imported.import(exported), report.id);
38 assert.match(exportReport(report, "csv"), /rttMs/); assert.match(exportReport(report, "txt"), /CH-J Server Diagnostics/);
39 assert.throws(() => imported.import("{")); assert.throws(() => imported.import(JSON.stringify({ schema: 1, report: { ...report, addresses: "bad" } })));
40 const malformed = structuredClone(report); malformed.results.http[0].comparisons[0].samples = "bad";
41 assert.throws(() => imported.import(JSON.stringify({ schema: 1, report: malformed })));
42 assert.throws(() => imported.import('{"schema":1,"report":{"__proto__":{}}}'));
43 let nested = {}; for (let i = 0; i < 30; i++) nested = { nested }; assert.throws(() => scrub(nested));
44 imported.remove(report.id); assert.deepEqual(imported.list(), []);
45});
46function fakeDns(addresses) { return { resolve: async () => addresses, run: async () => ({ status: "success", queries: [] }) }; }
47function completion(service) { return new Promise((resolve) => { const listener = (event) => { if (event.kind === "complete") { service.off("progress", listener); resolve(event.report); } }; service.on("progress", listener); }); }
48test("dual-stack continuous probes run concurrently, stop preserves packets and completion waits for cleanup", async () => {
49 const active = new Set(), cleaned = new Set();
50 const service = new DiagnosticsService({ dns: fakeDns([{ address: "127.0.0.1", family: 4 }, { address: "::1", family: 6 }]), adapters: {
51 ping: async (address, options, signal, progress) => {
52 active.add(address); progress({ kind: "ping-packet", address, sequence: 1, status: "success", sent: 1, received: 1, lossPercent: 0, rttMs: 0.5 });
53 await once(signal, "abort"); await new Promise((resolve) => setTimeout(resolve, address === "::1" ? 20 : 5)); cleaned.add(address); throw Object.assign(new Error("CANCELLED"), { code: "CANCELLED" });
54 }
55 } });
56 const done = completion(service); const { id } = service.start({ target: "localhost", tools: ["ping", "dns"], mode: "both", continuous: true });
57 assert.throws(() => service.start({ target: "localhost" }), { code: "DIAGNOSTICS_BUSY" });
58 await new Promise((resolve) => setTimeout(resolve, 30)); assert.equal(active.size, 2); service.cancel(id);
59 const report = await done; assert.equal(report.status, "cancelled"); assert.equal(cleaned.size, 2);
60 assert.equal(report.results.ping.length, 2); assert.equal(report.results.ping[0].packets.length, 1); assert.equal(report.results.ping[1].received, 1);
61 assert.equal(service.history.list().length, 1);
62});
63test("stopping a single tool lets other tools finish; offline and unavailable remain structured results", async () => {
64 const service = new DiagnosticsService({ dns: fakeDns([{ address: "127.0.0.1", family: 4 }]), adapters: { ping: async (_address, _options, signal) => { await once(signal, "abort"); throw Object.assign(new Error("CANCELLED"), { code: "CANCELLED" }); }, tcp: async () => ({ status: "warning", ports: [{ port: 443, status: "refused" }] }) } });
65 const done = completion(service); const { id } = service.start({ target: "localhost", tools: ["ping", "tcp"] });
66 await new Promise((r) => setTimeout(r, 15)); service.cancel(id, "ping"); const report = await done;
67 assert.equal(report.status, "completed"); assert.equal(report.results.ping[0].status, "cancelled"); assert.equal(report.results.tcp[0].ports[0].status, "refused");
68 const offline = new DiagnosticsService({ dns: { resolve: async () => { throw Object.assign(new Error("offline"), { code: "ENETUNREACH" }); }, run: async () => ({ status: "warning", queries: [] }) } });
69 const offDone = completion(offline); offline.start({ target: "offline.test", tools: ["ping", "dns"] }); const offReport = await offDone;
70 assert.equal(offReport.resolutionError.code, "ENETUNREACH"); assert.equal(offReport.results.ping[0].status, "not-tested");
71});
72test("specific IPv6 selections are canonicalized and unresolved addresses are rejected", async () => {
73 const service = new DiagnosticsService({ dns: fakeDns([{ address: "::1", family: 6 }, { address: "127.0.0.1", family: 4 }]) });
74 let done = completion(service); service.start({ target: "localhost", tools: ["dns"], selectedIp: "0:0:0:0:0:0:0:1" }); assert.equal((await done).addresses[0].family, 6);
75 done = completion(service); service.start({ target: "localhost", tools: ["dns"], selectedIp: "127.0.0.2" }); assert.equal((await done).error.code, "SELECTED_IP_NOT_RESOLVED");
76});

SHA-256: 3a3d3e3d097f580aeae0ff6db99034539f94a397a0b7562e2ad00299af195ef2

Archive SHA-256: 5ac91caf4fa32a6fdb114f2430deed486fbe7489d5eea343d1f034169fafb5e0