mirror of
https://github.com/gethomepage/homepage.git
synced 2026-04-05 09:41:21 -07:00
Chore: homepage tests (#6278)
This commit is contained in:
81
src/widgets/backrest/component.test.jsx
Normal file
81
src/widgets/backrest/component.test.jsx
Normal file
@@ -0,0 +1,81 @@
|
||||
// @vitest-environment jsdom
|
||||
|
||||
import { screen } from "@testing-library/react";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
import { renderWithProviders } from "test-utils/render-with-providers";
|
||||
|
||||
const { useWidgetAPI } = vi.hoisted(() => ({ useWidgetAPI: vi.fn() }));
|
||||
|
||||
vi.mock("utils/proxy/use-widget-api", () => ({
|
||||
default: useWidgetAPI,
|
||||
}));
|
||||
|
||||
import Component from "./component";
|
||||
|
||||
describe("widgets/backrest/component", () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it("defaults widget.fields and filters placeholders down to 4 blocks while loading", () => {
|
||||
useWidgetAPI.mockReturnValue({ data: undefined, error: undefined });
|
||||
|
||||
const service = { widget: { type: "backrest" } };
|
||||
const { container } = renderWithProviders(<Component service={service} />, { settings: { hideErrors: false } });
|
||||
|
||||
expect(service.widget.fields).toEqual([
|
||||
"num_success_latest",
|
||||
"num_failure_latest",
|
||||
"num_failure_30",
|
||||
"bytes_added_30",
|
||||
]);
|
||||
expect(container.querySelectorAll(".service-block")).toHaveLength(4);
|
||||
|
||||
expect(screen.getByText("backrest.num_success_latest")).toBeInTheDocument();
|
||||
expect(screen.getByText("backrest.num_failure_latest")).toBeInTheDocument();
|
||||
expect(screen.getByText("backrest.num_failure_30")).toBeInTheDocument();
|
||||
expect(screen.getByText("backrest.bytes_added_30")).toBeInTheDocument();
|
||||
expect(screen.queryByText("backrest.num_plans")).toBeNull();
|
||||
});
|
||||
|
||||
it("truncates widget.fields to 4", () => {
|
||||
useWidgetAPI.mockReturnValue({ data: undefined, error: undefined });
|
||||
|
||||
const service = {
|
||||
widget: { type: "backrest", fields: ["a", "b", "c", "d", "e"] },
|
||||
};
|
||||
|
||||
renderWithProviders(<Component service={service} />, { settings: { hideErrors: false } });
|
||||
|
||||
expect(service.widget.fields).toEqual(["a", "b", "c", "d"]);
|
||||
});
|
||||
|
||||
it("renders values and respects field filtering", () => {
|
||||
useWidgetAPI.mockReturnValue({
|
||||
data: {
|
||||
numPlans: 10,
|
||||
numSuccessLatest: 1,
|
||||
numFailureLatest: 2,
|
||||
numSuccess30Days: 3,
|
||||
numFailure30Days: 4,
|
||||
bytesAdded30Days: 500,
|
||||
},
|
||||
error: undefined,
|
||||
});
|
||||
|
||||
const { container } = renderWithProviders(<Component service={{ widget: { type: "backrest" } }} />, {
|
||||
settings: { hideErrors: false },
|
||||
});
|
||||
|
||||
// Default fields exclude num_plans and num_success_30
|
||||
expect(container.querySelectorAll(".service-block")).toHaveLength(4);
|
||||
expect(screen.queryByText("backrest.num_plans")).toBeNull();
|
||||
expect(screen.queryByText("backrest.num_success_30")).toBeNull();
|
||||
|
||||
expect(screen.getByText("1")).toBeInTheDocument();
|
||||
expect(screen.getByText("2")).toBeInTheDocument();
|
||||
expect(screen.getByText("4")).toBeInTheDocument();
|
||||
expect(screen.getByText("500")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
@@ -7,14 +7,14 @@ import widgets from "widgets/widgets";
|
||||
const proxyName = "backrestProxyHandler";
|
||||
const logger = createLogger(proxyName);
|
||||
|
||||
function sumField(plans, field) {
|
||||
export function sumField(plans, field) {
|
||||
return plans.reduce((sum, plan) => {
|
||||
const num = Number(plan[field]);
|
||||
return sum + (Number.isNaN(num) ? 0 : num);
|
||||
}, 0);
|
||||
}
|
||||
|
||||
function buildResponse(plans) {
|
||||
export function buildResponse(plans) {
|
||||
const numSuccess30Days = sumField(plans, "backupsSuccessLast30days");
|
||||
const numFailure30Days = sumField(plans, "backupsFailed30days");
|
||||
const bytesAdded30Days = sumField(plans, "bytesAddedLast30days");
|
||||
|
||||
212
src/widgets/backrest/proxy.test.js
Normal file
212
src/widgets/backrest/proxy.test.js
Normal file
@@ -0,0 +1,212 @@
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
import createMockRes from "test-utils/create-mock-res";
|
||||
|
||||
const { httpProxy, getServiceWidget, logger } = vi.hoisted(() => ({
|
||||
httpProxy: vi.fn(),
|
||||
getServiceWidget: vi.fn(),
|
||||
logger: {
|
||||
debug: vi.fn(),
|
||||
error: vi.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock("utils/logger", () => ({
|
||||
default: () => logger,
|
||||
}));
|
||||
|
||||
vi.mock("utils/config/service-helpers", () => ({
|
||||
default: getServiceWidget,
|
||||
}));
|
||||
|
||||
vi.mock("utils/proxy/http", () => ({
|
||||
httpProxy,
|
||||
}));
|
||||
|
||||
vi.mock("widgets/widgets", () => ({
|
||||
default: {
|
||||
backrest: {
|
||||
api: "{url}/v1.Backrest/{endpoint}",
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
import backrestProxyHandler, { buildResponse } from "./proxy";
|
||||
|
||||
describe("backrest proxy buildResponse", () => {
|
||||
it("aggregates plan metrics and latest status counts", () => {
|
||||
const plans = [
|
||||
{
|
||||
backupsSuccessLast30days: 3,
|
||||
backupsFailed30days: 1,
|
||||
bytesAddedLast30days: 1000,
|
||||
recentBackups: { status: ["STATUS_SUCCESS"] },
|
||||
},
|
||||
{
|
||||
backupsSuccessLast30days: 2,
|
||||
backupsFailed30days: 0,
|
||||
bytesAddedLast30days: 500,
|
||||
recentBackups: { status: ["STATUS_ERROR"] },
|
||||
},
|
||||
{
|
||||
backupsSuccessLast30days: "not-a-number",
|
||||
backupsFailed30days: 4,
|
||||
bytesAddedLast30days: 250,
|
||||
recentBackups: { status: [] },
|
||||
},
|
||||
];
|
||||
|
||||
expect(buildResponse(plans)).toEqual({
|
||||
numPlans: 3,
|
||||
numSuccess30Days: 5,
|
||||
numFailure30Days: 5,
|
||||
numSuccessLatest: 1,
|
||||
numFailureLatest: 1,
|
||||
bytesAdded30Days: 1750,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("widgets/backrest/proxy handler", () => {
|
||||
beforeEach(() => {
|
||||
httpProxy.mockReset();
|
||||
getServiceWidget.mockReset();
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it("returns 400 when the query is missing group or service", async () => {
|
||||
const req = { query: { service: "svc" } };
|
||||
const res = createMockRes();
|
||||
|
||||
await backrestProxyHandler(req, res);
|
||||
|
||||
expect(res.statusCode).toBe(400);
|
||||
expect(res.body).toEqual({ error: "Invalid proxy service type" });
|
||||
expect(getServiceWidget).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("returns 400 when the widget cannot be resolved", async () => {
|
||||
getServiceWidget.mockResolvedValue(null);
|
||||
|
||||
const req = { query: { group: "g", service: "svc", index: "0", endpoint: "GetSummaryDashboard" } };
|
||||
const res = createMockRes();
|
||||
|
||||
await backrestProxyHandler(req, res);
|
||||
|
||||
expect(res.statusCode).toBe(400);
|
||||
expect(res.body).toEqual({ error: "Invalid proxy service type" });
|
||||
});
|
||||
|
||||
it("calls the Backrest API with basic auth and returns the aggregated summary", async () => {
|
||||
getServiceWidget.mockResolvedValue({
|
||||
type: "backrest",
|
||||
url: "http://backrest/",
|
||||
username: "u",
|
||||
password: "p",
|
||||
});
|
||||
|
||||
httpProxy.mockResolvedValueOnce([
|
||||
200,
|
||||
"application/json",
|
||||
Buffer.from(
|
||||
JSON.stringify({
|
||||
planSummaries: [
|
||||
{
|
||||
backupsSuccessLast30days: 1,
|
||||
backupsFailed30days: 0,
|
||||
bytesAddedLast30days: 10,
|
||||
recentBackups: { status: [] },
|
||||
},
|
||||
{
|
||||
backupsSuccessLast30days: 0,
|
||||
backupsFailed30days: 1,
|
||||
bytesAddedLast30days: 5,
|
||||
recentBackups: { status: ["STATUS_ERROR"] },
|
||||
},
|
||||
],
|
||||
}),
|
||||
),
|
||||
]);
|
||||
|
||||
const req = { query: { group: "g", service: "svc", index: "0", endpoint: "GetSummaryDashboard" } };
|
||||
const res = createMockRes();
|
||||
|
||||
await backrestProxyHandler(req, res);
|
||||
|
||||
expect(httpProxy).toHaveBeenCalledTimes(1);
|
||||
expect(httpProxy.mock.calls[0][0].toString()).toBe("http://backrest/v1.Backrest/GetSummaryDashboard");
|
||||
expect(httpProxy.mock.calls[0][1]).toEqual(
|
||||
expect.objectContaining({
|
||||
method: "POST",
|
||||
body: "{}",
|
||||
headers: expect.objectContaining({
|
||||
"content-type": "application/json",
|
||||
Authorization: `Basic ${Buffer.from("u:p").toString("base64")}`,
|
||||
}),
|
||||
}),
|
||||
);
|
||||
|
||||
expect(res.headers["Content-Type"]).toBe("application/json");
|
||||
expect(res.statusCode).toBe(200);
|
||||
expect(res.body).toEqual({
|
||||
numPlans: 2,
|
||||
numSuccess30Days: 1,
|
||||
numFailure30Days: 1,
|
||||
numSuccessLatest: 0,
|
||||
numFailureLatest: 1,
|
||||
bytesAdded30Days: 15,
|
||||
});
|
||||
});
|
||||
|
||||
it("returns 500 when Backrest responds non-200", async () => {
|
||||
getServiceWidget.mockResolvedValue({ type: "backrest", url: "http://backrest" });
|
||||
httpProxy.mockResolvedValueOnce([401, "application/json", Buffer.from("nope")]);
|
||||
|
||||
const req = { query: { group: "g", service: "svc", index: "0", endpoint: "GetSummaryDashboard" } };
|
||||
const res = createMockRes();
|
||||
|
||||
await backrestProxyHandler(req, res);
|
||||
|
||||
expect(res.statusCode).toBe(500);
|
||||
expect(res.body).toEqual(
|
||||
expect.objectContaining({
|
||||
error: expect.objectContaining({
|
||||
message: "Error getting data from Backrest",
|
||||
data: expect.any(Buffer),
|
||||
}),
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("returns 500 when the plans payload is invalid", async () => {
|
||||
getServiceWidget.mockResolvedValue({ type: "backrest", url: "http://backrest" });
|
||||
httpProxy.mockResolvedValueOnce([200, "application/json", Buffer.from(JSON.stringify({ planSummaries: {} }))]);
|
||||
|
||||
const req = { query: { group: "g", service: "svc", index: "0", endpoint: "GetSummaryDashboard" } };
|
||||
const res = createMockRes();
|
||||
|
||||
await backrestProxyHandler(req, res);
|
||||
|
||||
expect(res.statusCode).toBe(500);
|
||||
expect(res.body).toEqual(
|
||||
expect.objectContaining({
|
||||
error: expect.objectContaining({
|
||||
message: "Invalid plans data",
|
||||
}),
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("returns 500 when httpProxy throws", async () => {
|
||||
getServiceWidget.mockResolvedValue({ type: "backrest", url: "http://backrest" });
|
||||
httpProxy.mockRejectedValueOnce(new Error("boom"));
|
||||
|
||||
const req = { query: { group: "g", service: "svc", index: "0", endpoint: "GetSummaryDashboard" } };
|
||||
const res = createMockRes();
|
||||
|
||||
await backrestProxyHandler(req, res);
|
||||
|
||||
expect(res.statusCode).toBe(500);
|
||||
expect(res.body).toEqual(expect.objectContaining({ error: "Backrest API Error", message: "boom" }));
|
||||
});
|
||||
});
|
||||
13
src/widgets/backrest/widget.test.js
Normal file
13
src/widgets/backrest/widget.test.js
Normal file
@@ -0,0 +1,13 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import { expectWidgetConfigShape } from "test-utils/widget-config";
|
||||
|
||||
import widget from "./widget";
|
||||
|
||||
describe("backrest widget config", () => {
|
||||
it("exports a valid widget config", () => {
|
||||
expectWidgetConfigShape(widget);
|
||||
expect(widget.api).toContain("/v1.Backrest/");
|
||||
expect(widget.mappings?.summary?.endpoint).toBe("GetSummaryDashboard");
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user