Files
homepage/src/__tests__/pages/api/widgets/glances.test.js
2026-04-01 08:00:20 -07:00

141 lines
4.9 KiB
JavaScript

import { beforeEach, describe, expect, it, vi } from "vitest";
import createMockRes from "test-utils/create-mock-res";
const { getPrivateWidgetOptions, httpProxy, logger } = vi.hoisted(() => ({
getPrivateWidgetOptions: vi.fn(),
httpProxy: vi.fn(),
logger: { error: vi.fn() },
}));
vi.mock("utils/config/widget-helpers", () => ({
getPrivateWidgetOptions,
}));
vi.mock("utils/proxy/http", () => ({
httpProxy,
}));
vi.mock("utils/logger", () => ({
default: () => logger,
}));
import handler from "pages/api/widgets/glances";
describe("pages/api/widgets/glances", () => {
beforeEach(() => {
vi.clearAllMocks();
});
it("returns 400 when the widget URL is missing", async () => {
getPrivateWidgetOptions.mockResolvedValueOnce({});
const req = { query: { index: "0" } };
const res = createMockRes();
await handler(req, res);
expect(res.statusCode).toBe(400);
expect(res.body.error).toBe("Missing Glances URL");
});
it("returns cpu/load/mem and includes optional endpoints when requested", async () => {
getPrivateWidgetOptions.mockResolvedValueOnce({ url: "http://glances", username: "u", password: "p" });
httpProxy
.mockResolvedValueOnce([200, null, Buffer.from(JSON.stringify({ total: 1 }))]) // cpu
.mockResolvedValueOnce([200, null, Buffer.from(JSON.stringify({ avg: 2 }))]) // load
.mockResolvedValueOnce([200, null, Buffer.from(JSON.stringify({ available: 3 }))]) // mem
.mockResolvedValueOnce([200, null, Buffer.from(JSON.stringify("1 days"))]) // uptime
.mockResolvedValueOnce([200, null, Buffer.from(JSON.stringify([{ label: "cpu_thermal", value: 50 }]))]) // sensors
.mockResolvedValueOnce([200, null, Buffer.from(JSON.stringify([{ mnt_point: "/", percent: 1 }]))]); // fs
const req = { query: { index: "0", uptime: "1", cputemp: "1", disk: "1", version: "4" } };
const res = createMockRes();
await handler(req, res);
expect(httpProxy).toHaveBeenCalledWith(
"http://glances/api/4/cpu",
expect.objectContaining({
method: "GET",
headers: expect.objectContaining({ Authorization: expect.any(String) }),
}),
);
expect(res.statusCode).toBe(200);
expect(res.body).toEqual({
cpu: { total: 1 },
load: { avg: 2 },
mem: { available: 3 },
uptime: "1 days",
sensors: [{ label: "cpu_thermal", value: 50 }],
fs: [{ mnt_point: "/", percent: 1 }],
});
});
it("does not call optional endpoints unless requested", async () => {
getPrivateWidgetOptions.mockResolvedValueOnce({ url: "http://glances" });
httpProxy
.mockResolvedValueOnce([200, null, Buffer.from(JSON.stringify({ total: 1 }))]) // cpu
.mockResolvedValueOnce([200, null, Buffer.from(JSON.stringify({ avg: 2 }))]) // load
.mockResolvedValueOnce([200, null, Buffer.from(JSON.stringify({ available: 3 }))]); // mem
const req = { query: { index: "0" } };
const res = createMockRes();
await handler(req, res);
expect(httpProxy).toHaveBeenCalledTimes(3);
expect(httpProxy.mock.calls[0][1].headers.Authorization).toBeUndefined();
expect(res.statusCode).toBe(200);
});
it("falls back to version 3 when version is invalid", async () => {
getPrivateWidgetOptions.mockResolvedValueOnce({ url: "http://glances" });
httpProxy
.mockResolvedValueOnce([200, null, Buffer.from(JSON.stringify({ total: 1 }))])
.mockResolvedValueOnce([200, null, Buffer.from(JSON.stringify({ avg: 2 }))])
.mockResolvedValueOnce([200, null, Buffer.from(JSON.stringify({ available: 3 }))]);
const req = { query: { index: "0", version: "3/../../secret-endpoint" } };
const res = createMockRes();
await handler(req, res);
expect(httpProxy).toHaveBeenCalledWith("http://glances/api/3/cpu", expect.any(Object));
expect(res.statusCode).toBe(200);
});
it("returns 400 when glances returns 401", async () => {
getPrivateWidgetOptions.mockResolvedValueOnce({ url: "http://glances" });
httpProxy.mockResolvedValueOnce([401, null, Buffer.from("nope")]);
const req = { query: { index: "0" } };
const res = createMockRes();
await handler(req, res);
expect(res.statusCode).toBe(400);
expect(res.body).toEqual(expect.objectContaining({ error: expect.stringContaining("Authorization failure") }));
});
it("returns 400 when glances returns a non-200 status for a downstream call", async () => {
getPrivateWidgetOptions.mockResolvedValueOnce({ url: "http://glances" });
httpProxy
.mockResolvedValueOnce([200, null, Buffer.from(JSON.stringify({ total: 1 }))]) // cpu
.mockResolvedValueOnce([500, null, Buffer.from("nope")]); // load
const req = { query: { index: "0" } };
const res = createMockRes();
await handler(req, res);
expect(res.statusCode).toBe(400);
expect(res.body).toEqual(expect.objectContaining({ error: expect.stringContaining("HTTP 500") }));
});
});