mirror of
https://github.com/Termix-SSH/Termix.git
synced 2026-10-29 06:35:24 +00:00
feat: ship host metrics as a first-party in-process plugin
This commit is contained in:
1 parent
a2479c19b0
commit
19b70430a1
145 files changed
+675
-349
No files matched your search
@@ -30,7 +30,7 @@ vi.mock("../../hosts/host-resolver.js", () => ({
|
||||
}));
|
||||
|
||||
const execCommand = vi.fn();
|
||||
vi.mock("../../hosts/metrics/widgets/common-utils.js", () => ({
|
||||
vi.mock("../../hosts/metrics-shared/common-utils.js", () => ({
|
||||
execCommand: (...args: unknown[]) => execCommand(...args),
|
||||
}));
|
||||
vi.mock("../../hosts/ssh-client-factory.js", () => ({
|
||||
|
||||
@@ -73,15 +73,15 @@ vi.mock("../../../hosts/ssh-connection-pool.js", () => ({
|
||||
) => fn({}),
|
||||
}));
|
||||
|
||||
vi.mock("../../../hosts/metrics/widgets/common-utils.js", () => ({
|
||||
vi.mock("../../../hosts/metrics-shared/common-utils.js", () => ({
|
||||
execCommand: vi.fn(async () => ({ stdout: "ok", stderr: "", code: 0 })),
|
||||
}));
|
||||
|
||||
vi.mock("../../../hosts/metrics/managers/platform.js", () => ({
|
||||
vi.mock("../../../hosts/metrics-shared/platform.js", () => ({
|
||||
detectPlatform: vi.fn(async () => ({ pkg: "apt", osPrettyName: "Debian" })),
|
||||
}));
|
||||
|
||||
vi.mock("../../../hosts/metrics/managers/exec-elevated.js", async () => {
|
||||
vi.mock("../../../hosts/metrics-shared/exec-elevated.js", async () => {
|
||||
class ElevationError extends Error {
|
||||
code: string;
|
||||
constructor(code: string, message: string) {
|
||||
@@ -95,11 +95,11 @@ vi.mock("../../../hosts/metrics/managers/exec-elevated.js", async () => {
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock("../../../hosts/metrics/managers/packages.js", () => ({
|
||||
vi.mock("../../../hosts/metrics-shared/package-commands.js", () => ({
|
||||
buildPackageActionCommand: vi.fn(() => "apt-get install -y foo"),
|
||||
}));
|
||||
|
||||
vi.mock("../../../hosts/metrics/managers/validation.js", () => ({
|
||||
vi.mock("../../../hosts/metrics-shared/validation.js", () => ({
|
||||
isValidPackageName: (v: unknown) => typeof v === "string" && v.length > 0,
|
||||
}));
|
||||
|
||||
@@ -320,7 +320,7 @@ describe("POST /:id/packages", () => {
|
||||
|
||||
it("surfaces an ElevationError as a per-host error, not a request failure", async () => {
|
||||
const { execElevated, ElevationError } =
|
||||
await import("../../../hosts/metrics/managers/exec-elevated.js");
|
||||
await import("../../../hosts/metrics-shared/exec-elevated.js");
|
||||
(execElevated as ReturnType<typeof vi.fn>).mockRejectedValue(
|
||||
new ElevationError("SUDO_REQUIRED", "sudo password required"),
|
||||
);
|
||||
@@ -347,7 +347,7 @@ describe("POST /:id/packages", () => {
|
||||
});
|
||||
|
||||
const { execElevated } =
|
||||
await import("../../../hosts/metrics/managers/exec-elevated.js");
|
||||
await import("../../../hosts/metrics-shared/exec-elevated.js");
|
||||
(execElevated as ReturnType<typeof vi.fn>).mockResolvedValue({
|
||||
code: 0,
|
||||
stdout: "done",
|
||||
|
||||
@@ -2,7 +2,7 @@ import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import type { Client } from "ssh2";
|
||||
|
||||
const execElevated = vi.fn();
|
||||
vi.mock("../../../hosts/metrics/managers/exec-elevated.js", () => ({
|
||||
vi.mock("../../../hosts/metrics-shared/exec-elevated.js", () => ({
|
||||
execElevated: (...args: unknown[]) => execElevated(...args),
|
||||
}));
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import { EventEmitter } from "node:events";
|
||||
import type { WebSocket } from "ws";
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import { SSHHostKeyVerifier } from "../../hosts/host-key-verifier.js";
|
||||
import { authFailureTracker } from "../../hosts/metrics/state.js";
|
||||
import { authFailureTracker } from "../../hosts/metrics-shared/state.js";
|
||||
|
||||
const { updateHostKey } = vi.hoisted(() => ({ updateHostKey: vi.fn() }));
|
||||
vi.mock("../../database/repositories/factory.js", () => ({
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@ import {
|
||||
toFixedNum,
|
||||
kibToGiB,
|
||||
encodePowerShellCommand,
|
||||
} from "../../../../hosts/metrics/widgets/common-utils.js";
|
||||
} from "../../../hosts/metrics-shared/common-utils.js";
|
||||
|
||||
describe("toFixedNum", () => {
|
||||
it("rounds to the requested digit count", () => {
|
||||
+2
-2
@@ -1,14 +1,14 @@
|
||||
import { describe, it, expect, vi, beforeEach } from "vitest";
|
||||
|
||||
const execCommand = vi.fn();
|
||||
vi.mock("../../../../hosts/metrics/widgets/common-utils.js", () => ({
|
||||
vi.mock("../../../hosts/metrics-shared/common-utils.js", () => ({
|
||||
execCommand: (...args: unknown[]) => execCommand(...args),
|
||||
}));
|
||||
|
||||
import {
|
||||
execElevated,
|
||||
ElevationError,
|
||||
} from "../../../../hosts/metrics/managers/exec-elevated.js";
|
||||
} from "../../../hosts/metrics-shared/exec-elevated.js";
|
||||
import type { Client } from "ssh2";
|
||||
|
||||
const fakeClient = {} as Client;
|
||||
@@ -0,0 +1,40 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import {
|
||||
isHostKeyVerificationError,
|
||||
statusAfterAuthentication,
|
||||
statusAfterReachabilityCheck,
|
||||
} from "../../../hosts/metrics-shared/host-status.js";
|
||||
|
||||
describe("isHostKeyVerificationError", () => {
|
||||
it.each([
|
||||
"Host denied (verification failed)",
|
||||
"Host key changed - please connect via Terminal to verify the new key",
|
||||
])("classifies %s", (message) => {
|
||||
expect(isHostKeyVerificationError(new Error(message))).toBe(true);
|
||||
});
|
||||
|
||||
it("does not classify ordinary authentication failures", () => {
|
||||
expect(isHostKeyVerificationError(new Error("Permission denied"))).toBe(
|
||||
false,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("host availability status", () => {
|
||||
it("does not call a TCP-reachable host online before authentication", () => {
|
||||
expect(statusAfterReachabilityCheck(true)).toBe("reachable");
|
||||
});
|
||||
|
||||
it("keeps a verified host online across later reachability checks", () => {
|
||||
expect(statusAfterReachabilityCheck(true, "online")).toBe("online");
|
||||
});
|
||||
|
||||
it("marks successful authentication online", () => {
|
||||
expect(statusAfterAuthentication(true, "reachable")).toBe("online");
|
||||
});
|
||||
|
||||
it("downgrades failed authentication without hiding reachability", () => {
|
||||
expect(statusAfterAuthentication(false, "online")).toBe("reachable");
|
||||
expect(statusAfterAuthentication(false, "offline")).toBe("offline");
|
||||
});
|
||||
});
|
||||
+1
-1
@@ -5,7 +5,7 @@ import {
|
||||
ConcurrentLimiter,
|
||||
HostPollCache,
|
||||
metricsConcurrencyFor,
|
||||
} from "../../../hosts/metrics/state.js";
|
||||
} from "../../../hosts/metrics-shared/state.js";
|
||||
|
||||
describe("auth failure tracking", () => {
|
||||
it("permanently pauses polling after a host key mismatch", () => {
|
||||
@@ -1,7 +1,7 @@
|
||||
import { describe, it, expect, vi, beforeEach } from "vitest";
|
||||
|
||||
const execCommand = vi.fn();
|
||||
vi.mock("../../../../hosts/metrics/widgets/common-utils.js", () => ({
|
||||
vi.mock("../../../../hosts/metrics-shared/common-utils.js", () => ({
|
||||
execCommand: (...args: unknown[]) => execCommand(...args),
|
||||
}));
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { describe, it, expect, vi, beforeEach } from "vitest";
|
||||
|
||||
const execCommand = vi.fn();
|
||||
vi.mock("../../../../hosts/metrics/widgets/common-utils.js", () => ({
|
||||
vi.mock("../../../../hosts/metrics-shared/common-utils.js", () => ({
|
||||
execCommand: (...args: unknown[]) => execCommand(...args),
|
||||
toFixedNum: (n: number | null | undefined, digits = 2) => {
|
||||
if (typeof n !== "number" || !Number.isFinite(n)) return null;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { describe, it, expect, vi, beforeEach } from "vitest";
|
||||
|
||||
const execCommand = vi.fn();
|
||||
vi.mock("../../../../hosts/metrics/widgets/common-utils.js", () => ({
|
||||
vi.mock("../../../../hosts/metrics-shared/common-utils.js", () => ({
|
||||
execCommand: (...args: unknown[]) => execCommand(...args),
|
||||
toFixedNum: (n: number | null | undefined, digits = 2) => {
|
||||
if (typeof n !== "number" || !Number.isFinite(n)) return null;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { describe, it, expect, vi, beforeEach } from "vitest";
|
||||
|
||||
const execCommand = vi.fn();
|
||||
vi.mock("../../../../hosts/metrics/widgets/common-utils.js", () => ({
|
||||
vi.mock("../../../../hosts/metrics-shared/common-utils.js", () => ({
|
||||
execCommand: (...args: unknown[]) => execCommand(...args),
|
||||
}));
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { describe, it, expect, vi, beforeEach } from "vitest";
|
||||
|
||||
const execCommand = vi.fn();
|
||||
vi.mock("../../../../hosts/metrics/widgets/common-utils.js", () => ({
|
||||
vi.mock("../../../../hosts/metrics-shared/common-utils.js", () => ({
|
||||
execCommand: (...args: unknown[]) => execCommand(...args),
|
||||
toFixedNum: (n: number | null | undefined, digits = 2) => {
|
||||
if (typeof n !== "number" || !Number.isFinite(n)) return null;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { describe, it, expect, vi, beforeEach } from "vitest";
|
||||
|
||||
const execCommand = vi.fn();
|
||||
vi.mock("../../../../hosts/metrics/widgets/common-utils.js", () => ({
|
||||
vi.mock("../../../../hosts/metrics-shared/common-utils.js", () => ({
|
||||
execCommand: (...args: unknown[]) => execCommand(...args),
|
||||
toFixedNum: (n: number | null | undefined, digits = 2) => {
|
||||
if (typeof n !== "number" || !Number.isFinite(n)) return null;
|
||||
|
||||
@@ -136,3 +136,53 @@ describe("bundled docker plugin", () => {
|
||||
expect(source).toMatch(/export async function deactivate/);
|
||||
});
|
||||
});
|
||||
|
||||
describe("bundled host-metrics plugin", () => {
|
||||
it("ships a directory with a manifest and both entry points", () => {
|
||||
const dir = path.join(getBundledPluginsDir(), "host-metrics");
|
||||
|
||||
expect(fs.existsSync(path.join(dir, "manifest.json"))).toBe(true);
|
||||
expect(fs.existsSync(path.join(dir, "backend", "index.mjs"))).toBe(true);
|
||||
expect(fs.existsSync(path.join(dir, "frontend", "index.mjs"))).toBe(true);
|
||||
});
|
||||
|
||||
it("has a manifest that passes the real validator", () => {
|
||||
const { manifest, errors } = parseManifest(
|
||||
readBundledManifest("host-metrics"),
|
||||
);
|
||||
|
||||
expect(errors).toEqual([]);
|
||||
expect(manifest?.id).toBe("host-metrics");
|
||||
expect(manifest?.category).toBe("Monitoring");
|
||||
});
|
||||
|
||||
it("declares the transport-owner capability and qualifies for the tier", () => {
|
||||
const { manifest } = parseManifest(readBundledManifest("host-metrics"));
|
||||
|
||||
expect(manifest?.permissions).toContain(TRANSPORT_OWNER_CAPABILITY);
|
||||
expect(isFirstParty("host-metrics")).toBe(true);
|
||||
expect(runsInProcess("host-metrics", manifest!.permissions)).toBe(true);
|
||||
});
|
||||
|
||||
it("contributes the host-metrics tab the shell registers", () => {
|
||||
const { manifest } = parseManifest(readBundledManifest("host-metrics"));
|
||||
const tab = manifest?.contributes?.tabs?.[0];
|
||||
|
||||
// The shell keys tab content off this id, so it has to stay "host-metrics".
|
||||
expect(tab?.id).toBe("host-metrics");
|
||||
expect(tab?.openFrom).toContain("host-context-menu");
|
||||
});
|
||||
|
||||
it("exports activate and deactivate from its backend entry", () => {
|
||||
const entry = path.join(
|
||||
getBundledPluginsDir(),
|
||||
"host-metrics",
|
||||
"backend",
|
||||
"index.mjs",
|
||||
);
|
||||
const source = fs.readFileSync(entry, "utf8");
|
||||
|
||||
expect(source).toMatch(/export async function activate/);
|
||||
expect(source).toMatch(/export async function deactivate/);
|
||||
});
|
||||
});
|
||||
@@ -137,7 +137,7 @@ describe("hosts publishers route through the bus", () => {
|
||||
|
||||
it("notifyAutomationStatus and notifyAutomationMetrics emit their topics", async () => {
|
||||
const { notifyAutomationStatus, notifyAutomationMetrics } =
|
||||
await import("../../hosts/metrics/automation-bridge.js");
|
||||
await import("../../../../plugins/host-metrics/backend/automation-bridge.js");
|
||||
|
||||
const status = vi.fn();
|
||||
const metrics = vi.fn();
|
||||
|
||||
@@ -39,16 +39,18 @@ function manifest(overrides: Record<string, unknown> = {}) {
|
||||
}
|
||||
|
||||
describe("first-party allowlist", () => {
|
||||
it("contains only ssh-terminal and docker", () => {
|
||||
it("contains only ssh-terminal, docker and host-metrics", () => {
|
||||
expect([...FIRST_PARTY_PLUGIN_IDS].sort()).toEqual([
|
||||
"docker",
|
||||
"host-metrics",
|
||||
"ssh-terminal",
|
||||
]);
|
||||
});
|
||||
|
||||
it("recognises ssh-terminal and docker and nothing else", () => {
|
||||
it("recognises ssh-terminal, docker and host-metrics and nothing else", () => {
|
||||
expect(isFirstParty("ssh-terminal")).toBe(true);
|
||||
expect(isFirstParty("docker")).toBe(true);
|
||||
expect(isFirstParty("host-metrics")).toBe(true);
|
||||
expect(isFirstParty("ssh-terminal-pro")).toBe(false);
|
||||
expect(isFirstParty("community-plugin")).toBe(false);
|
||||
expect(isFirstParty("")).toBe(false);
|
||||
@@ -61,11 +63,15 @@ describe("runsInProcess", () => {
|
||||
true,
|
||||
);
|
||||
expect(runsInProcess("docker", [TRANSPORT_OWNER_CAPABILITY])).toBe(true);
|
||||
expect(runsInProcess("host-metrics", [TRANSPORT_OWNER_CAPABILITY])).toBe(
|
||||
true,
|
||||
);
|
||||
|
||||
// On the list but not asking for it: stays in a worker, so the manifest
|
||||
// remains an honest description of what the plugin does.
|
||||
expect(runsInProcess("ssh-terminal", ["hosts.read"])).toBe(false);
|
||||
expect(runsInProcess("docker", ["hosts.read"])).toBe(false);
|
||||
expect(runsInProcess("host-metrics", ["hosts.read"])).toBe(false);
|
||||
|
||||
// Asking for it but not on the list.
|
||||
expect(
|
||||
@@ -121,6 +127,18 @@ describe("manifest gate on the reserved capability", () => {
|
||||
expect(parsed?.id).toBe("docker");
|
||||
});
|
||||
|
||||
it("allows it for host-metrics", () => {
|
||||
const { manifest: parsed, errors } = parseManifest(
|
||||
manifest({
|
||||
id: "host-metrics",
|
||||
permissions: [TRANSPORT_OWNER_CAPABILITY],
|
||||
}),
|
||||
);
|
||||
|
||||
expect(errors).toEqual([]);
|
||||
expect(parsed?.id).toBe("host-metrics");
|
||||
});
|
||||
|
||||
it("still accepts an ordinary manifest that never mentions it", () => {
|
||||
const { manifest: parsed, errors } = parseManifest(
|
||||
manifest({ permissions: ["hosts.read"] }),
|
||||
|
||||
+1
-1
@@ -35,7 +35,7 @@ vi.mock("../../../utils/discord-sender.js", () => ({
|
||||
}));
|
||||
|
||||
const alertEngineModule =
|
||||
await import("../../../hosts/metrics/alert-engine.js");
|
||||
await import("../../../../../plugins/host-metrics/backend/alert-engine.js");
|
||||
|
||||
const cpuRule = {
|
||||
id: 1,
|
||||
+1
-1
@@ -5,7 +5,7 @@ import {
|
||||
isTcpPingEnabled,
|
||||
parseStatusHostIds,
|
||||
tcpPingThroughJumpHost,
|
||||
} from "../../../hosts/metrics/helpers.js";
|
||||
} from "../../../../../plugins/host-metrics/backend/helpers.js";
|
||||
import { createConnectionLog } from "../../../hosts/connection-log.js";
|
||||
|
||||
describe("supportsMetrics", () => {
|
||||
+5
-2
@@ -5,11 +5,14 @@ import { beforeEach, expect, it, vi } from "vitest";
|
||||
|
||||
// Load the production route without starting metrics polling or SSH services.
|
||||
const source = readFileSync(
|
||||
new URL("../../../hosts/metrics/index.ts", import.meta.url),
|
||||
new URL(
|
||||
"../../../../../plugins/host-metrics/backend/routes.ts",
|
||||
import.meta.url,
|
||||
),
|
||||
"utf8",
|
||||
);
|
||||
const ast = ts.createSourceFile(
|
||||
"index.ts",
|
||||
"routes.ts",
|
||||
source,
|
||||
ts.ScriptTarget.Latest,
|
||||
true,
|
||||
+4
-1
@@ -11,7 +11,10 @@ import path from "path";
|
||||
describe("metrics service /internal/login-alert route order", () => {
|
||||
it("is registered before the global auth middleware", () => {
|
||||
const source = fs.readFileSync(
|
||||
path.resolve(__dirname, "../../../hosts/metrics/index.ts"),
|
||||
path.resolve(
|
||||
__dirname,
|
||||
"../../../../../plugins/host-metrics/backend/routes.ts",
|
||||
),
|
||||
"utf8",
|
||||
);
|
||||
|
||||
+13
-13
@@ -2,8 +2,8 @@ import { describe, it, expect } from "vitest";
|
||||
import {
|
||||
buildSudoCommand,
|
||||
shellSingleQuote,
|
||||
} from "../../../hosts/metrics/managers/exec-elevated.js";
|
||||
import { parsePlatformProbe } from "../../../hosts/metrics/managers/platform.js";
|
||||
} from "../../../../hosts/metrics-shared/exec-elevated.js";
|
||||
import { parsePlatformProbe } from "../../../../hosts/metrics-shared/platform.js";
|
||||
import {
|
||||
isValidSystemdUnit,
|
||||
isValidPid,
|
||||
@@ -15,53 +15,53 @@ import {
|
||||
isValidSignal,
|
||||
isValidServiceAction,
|
||||
isAllowedPath,
|
||||
} from "../../../hosts/metrics/managers/validation.js";
|
||||
} from "../../../../hosts/metrics-shared/validation.js";
|
||||
import {
|
||||
parseServiceList,
|
||||
buildServiceActionCommand,
|
||||
} from "../../../hosts/metrics/managers/services.js";
|
||||
} from "../../../../../../plugins/host-metrics/backend/managers/services.js";
|
||||
import {
|
||||
parseProcessList,
|
||||
buildKillCommand,
|
||||
} from "../../../hosts/metrics/managers/processes.js";
|
||||
} from "../../../../../../plugins/host-metrics/backend/managers/processes.js";
|
||||
import {
|
||||
parseDfMounts,
|
||||
parseTopMemory,
|
||||
} from "../../../hosts/metrics/managers/simple-reads.js";
|
||||
} from "../../../../../../plugins/host-metrics/backend/managers/simple-reads.js";
|
||||
import {
|
||||
parseCrontab,
|
||||
serializeCrontab,
|
||||
isValidCronSchedule,
|
||||
buildApplyCrontabCommand,
|
||||
} from "../../../hosts/metrics/managers/cron.js";
|
||||
} from "../../../../../../plugins/host-metrics/backend/managers/cron.js";
|
||||
import {
|
||||
buildPackageActionCommand,
|
||||
parseUpgradable,
|
||||
buildListUpgradableCommand,
|
||||
} from "../../../hosts/metrics/managers/packages.js";
|
||||
} from "../../../../hosts/metrics-shared/package-commands.js";
|
||||
import {
|
||||
buildIssueCommand,
|
||||
buildRenewCommand,
|
||||
buildRevokeCommand,
|
||||
isValidCertName,
|
||||
parseCertbotCertificates,
|
||||
} from "../../../hosts/metrics/managers/ssl.js";
|
||||
} from "../../../../../../plugins/host-metrics/backend/managers/ssl.js";
|
||||
import {
|
||||
buildIptablesRuleCommand,
|
||||
buildNftRuleCommand,
|
||||
} from "../../../hosts/metrics/managers/firewall.js";
|
||||
} from "../../../../../../plugins/host-metrics/backend/managers/firewall.js";
|
||||
import {
|
||||
parsePasswd,
|
||||
parseSudoers,
|
||||
} from "../../../hosts/metrics/managers/users.js";
|
||||
} from "../../../../../../plugins/host-metrics/backend/managers/users.js";
|
||||
import {
|
||||
buildHealthCheckCommand,
|
||||
parseHealthResult,
|
||||
} from "../../../hosts/metrics/managers/health.js";
|
||||
} from "../../../../../../plugins/host-metrics/backend/managers/health.js";
|
||||
import {
|
||||
buildTailCommand,
|
||||
clampLines,
|
||||
} from "../../../hosts/metrics/managers/logs.js";
|
||||
} from "../../../../../../plugins/host-metrics/backend/managers/logs.js";
|
||||
|
||||
describe("exec-elevated", () => {
|
||||
it("single-quotes and escapes for the shell", () => {
|
||||
+4
-1
@@ -13,7 +13,10 @@ import path from "path";
|
||||
describe("metrics service status polling", () => {
|
||||
it("never authenticates over SSH from pollHostStatus, on the first poll or any later one", () => {
|
||||
const source = fs.readFileSync(
|
||||
path.resolve(__dirname, "../../../hosts/metrics/index.ts"),
|
||||
path.resolve(
|
||||
__dirname,
|
||||
"../../../../../plugins/host-metrics/backend/routes.ts",
|
||||
),
|
||||
"utf8",
|
||||
);
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@ import { describe, it, expect } from "vitest";
|
||||
import {
|
||||
parseCpuLine,
|
||||
parseDarwinCpuUsageLine,
|
||||
} from "../../../../hosts/metrics/widgets/cpu-collector.js";
|
||||
} from "../../../../../../plugins/host-metrics/backend/widgets/cpu-collector.js";
|
||||
|
||||
describe("parseCpuLine", () => {
|
||||
it("parses a standard /proc/stat cpu line", () => {
|
||||
+1
-1
@@ -9,7 +9,7 @@ import {
|
||||
parseWindowsDiskJson,
|
||||
parseDarwinMountTypes,
|
||||
parseDarwinDfRows,
|
||||
} from "../../../../hosts/metrics/widgets/disk-collector.js";
|
||||
} from "../../../../../../plugins/host-metrics/backend/widgets/disk-collector.js";
|
||||
|
||||
describe("parseDfLines", () => {
|
||||
it("parses df -T -P output into rows", () => {
|
||||
+1
-1
@@ -3,7 +3,7 @@ import {
|
||||
parseNvidiaSmiGpus,
|
||||
parseNvidiaSmiOutput,
|
||||
parseNvidiaSmiProcesses,
|
||||
} from "../../../../hosts/metrics/widgets/gpu-collector.js";
|
||||
} from "../../../../../../plugins/host-metrics/backend/widgets/gpu-collector.js";
|
||||
|
||||
const RTX_UUID = "GPU-3b8f2c1a-5d4e-7f60-8a9b-0c1d2e3f4a5b";
|
||||
const H100_UUID = "GPU-9a8b7c6d-5e4f-3a2b-1c0d-e9f8a7b6c5d4";
|
||||
+1
-1
@@ -5,7 +5,7 @@ import {
|
||||
parseDarwinIfconfig,
|
||||
parseDarwinNetstat,
|
||||
parseWindowsAdapterJson,
|
||||
} from "../../../../hosts/metrics/widgets/network-collector.js";
|
||||
} from "../../../../../../plugins/host-metrics/backend/widgets/network-collector.js";
|
||||
|
||||
const PROC_NET = `Inter-| Receive | Transmit
|
||||
face |bytes packets errs drop fifo frame compressed multicast|bytes packets errs drop fifo colls carrier compressed
|
||||
+1
-1
@@ -2,7 +2,7 @@ import { describe, expect, it } from "vitest";
|
||||
import {
|
||||
parseSensorsOutput,
|
||||
parseSysfsThermalOutput,
|
||||
} from "../../../../hosts/metrics/widgets/temperature-collector.js";
|
||||
} from "../../../../../../plugins/host-metrics/backend/widgets/temperature-collector.js";
|
||||
|
||||
describe("temperature collectors", () => {
|
||||
it("parses sysfs thermal zone output", () => {
|
||||
Reference in new issue
Block a user