mirror of
https://github.com/Termix-SSH/Termix.git
synced 2026-10-29 22:55:24 +00:00
120 lines
3.6 KiB
TypeScript
120 lines
3.6 KiB
TypeScript
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
|
|
const statsApiMock = vi.hoisted(() => ({ get: vi.fn(), post: vi.fn() }));
|
|
const remoteStatsApiMock = vi.hoisted(() => ({ get: vi.fn() }));
|
|
const sshHostApiMock = vi.hoisted(() => ({ get: vi.fn() }));
|
|
const resolveConnectionOriginMock = vi.hoisted(() => vi.fn());
|
|
|
|
vi.mock("@/main-axios", () => ({
|
|
statsApi: statsApiMock,
|
|
sshHostApi: sshHostApiMock,
|
|
getRemoteStatsApi: () => remoteStatsApiMock,
|
|
isElectron: () => true,
|
|
handleApiError: vi.fn(),
|
|
}));
|
|
|
|
vi.mock("@/lib/hosts-request-cache", () => ({
|
|
getCachedServerStatuses: (loader: () => Promise<unknown>) => loader(),
|
|
}));
|
|
|
|
vi.mock("@/lib/connection-origin", () => ({
|
|
resolveConnectionOrigin: resolveConnectionOriginMock,
|
|
}));
|
|
|
|
import {
|
|
getAllServerStatuses,
|
|
getServerMetricsById,
|
|
sendMetricsHeartbeat,
|
|
} from "../../../../src/ui/api/host-metrics-status-api";
|
|
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
window.electronAPI = {
|
|
invoke: vi.fn(async (channel: string) =>
|
|
channel === "get-remote-sync-config"
|
|
? { serverUrl: "https://termix.example.test" }
|
|
: null,
|
|
),
|
|
} as unknown as NonNullable<typeof window.electronAPI>;
|
|
statsApiMock.get.mockResolvedValue({ data: { 1: { status: "online" } } });
|
|
remoteStatsApiMock.get.mockResolvedValue({
|
|
data: { 2: { status: "reachable" } },
|
|
});
|
|
});
|
|
|
|
describe("status check origin routing", () => {
|
|
it("only asks the embedded backend to poll local-origin hosts", async () => {
|
|
sshHostApiMock.get.mockResolvedValue({
|
|
data: [
|
|
{ id: 1, connectionOrigin: "local" },
|
|
{ id: 2, connectionOrigin: "remote" },
|
|
],
|
|
});
|
|
resolveConnectionOriginMock.mockImplementation(async (host) =>
|
|
host.connectionOrigin === "local" ? "local" : "remote",
|
|
);
|
|
|
|
await expect(getAllServerStatuses()).resolves.toEqual({
|
|
1: { status: "online" },
|
|
2: { status: "reachable" },
|
|
});
|
|
|
|
expect(statsApiMock.get).toHaveBeenCalledWith("/status", {
|
|
timeout: 2000,
|
|
params: { hostIds: "1" },
|
|
__silentRetry: true,
|
|
});
|
|
});
|
|
|
|
it("sends an empty allowlist when every host uses the remote server", async () => {
|
|
sshHostApiMock.get.mockResolvedValue({
|
|
data: [{ id: 2, connectionOrigin: "remote" }],
|
|
});
|
|
resolveConnectionOriginMock.mockResolvedValue("remote");
|
|
|
|
await getAllServerStatuses();
|
|
|
|
expect(statsApiMock.get).toHaveBeenCalledWith("/status", {
|
|
timeout: 2000,
|
|
params: { hostIds: "" },
|
|
__silentRetry: true,
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("metrics request coalescing", () => {
|
|
it("shares simultaneous reads for the same host", async () => {
|
|
statsApiMock.get.mockResolvedValueOnce({
|
|
status: 200,
|
|
data: { cpu: { percent: 12 } },
|
|
});
|
|
|
|
const [first, second] = await Promise.all([
|
|
getServerMetricsById(991),
|
|
getServerMetricsById(991),
|
|
]);
|
|
|
|
expect(first).toEqual({ cpu: { percent: 12 } });
|
|
expect(second).toBe(first);
|
|
expect(statsApiMock.get).toHaveBeenCalledOnce();
|
|
});
|
|
});
|
|
|
|
describe("metrics viewer heartbeat", () => {
|
|
it("reports a swept viewer session without throwing or logging", async () => {
|
|
statsApiMock.post.mockResolvedValueOnce({ status: 404 });
|
|
|
|
await expect(sendMetricsHeartbeat("expired-viewer")).resolves.toBe(false);
|
|
expect(statsApiMock.post).toHaveBeenCalledWith(
|
|
"/metrics/heartbeat",
|
|
{ viewerSessionId: "expired-viewer" },
|
|
{ validateStatus: expect.any(Function) },
|
|
);
|
|
});
|
|
|
|
it("keeps a live viewer session", async () => {
|
|
statsApiMock.post.mockResolvedValueOnce({ status: 200 });
|
|
await expect(sendMetricsHeartbeat("live-viewer")).resolves.toBe(true);
|
|
});
|
|
});
|