Files
UHD_Media_Tool/apps/web/app/components/dashboard/home-runtime.test.ts
T

138 lines
4.5 KiB
TypeScript

import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import type { Dashboard, SystemHealth, SystemResources } from "@umt/contracts";
import { createElement } from "react";
import { renderToStaticMarkup } from "react-dom/server";
import { MemoryRouter } from "react-router";
import { describe, expect, it } from "vitest";
import { qk } from "~/lib/query-keys.ts";
import { HomeRuntime } from "./home-runtime.tsx";
// 首页运行状态简版卡:整体健康、CPU、内存、磁盘,以及下载器 / 站点 / 媒体库目录摘要,点进去是运行状态页。
const health = (over: Partial<SystemHealth> = {}): SystemHealth =>
({
level: "warn",
checkedAt: new Date().toISOString(),
api: { version: "t", startedAt: new Date().toISOString(), uptimeSec: 10 },
processes: [],
retiredProcesses: [],
sse: { connections: 0 },
db: { level: "ok", latencyMs: 1, sizeBytes: 1, connections: null, error: null },
disks: [
{
scope: "library_root",
id: "r1",
name: "电影",
path: "/media/movies",
level: "warn",
totalBytes: 1000,
usedBytes: 870,
availableBytes: 130,
usedRatio: 0.87,
error: null,
},
],
diskThresholds: { warnPercent: 85, criticalPercent: 90 },
mounts: [
{
kind: "library_root",
id: "r1",
name: "电影",
path: "/m",
status: "online",
reason: null,
sentinelChecked: true,
},
{
kind: "library_root",
id: "r2",
name: "剧集",
path: "/t",
status: "offline",
reason: "root_missing",
sentinelChecked: true,
},
],
engine: {
level: "ok",
pending: 0,
running: 0,
oldestPendingAgeSec: null,
finished24h: 0,
failed24h: 0,
failureRate24h: null,
},
schedules: { level: "ok", total: 0, enabled: 0, running: 0, failing: [], failedRuns24h: 0 },
storage: { level: "ok", alerts: [] },
...over,
}) as SystemHealth;
const resources: SystemResources = {
at: new Date().toISOString(),
intervalSec: 5,
bucketSec: 5,
cpu: { scope: "cgroup", percent: 42.4, cores: 4, hostPercent: 30, load: [1, 1, 1] },
memory: {
scope: "cgroup",
usedBytes: 900,
limitBytes: 1000,
percent: 92,
hostTotalBytes: 2000,
hostAvailableBytes: 800,
swapUsedBytes: 0,
},
network: { scope: "cgroup", rxBps: 1, txBps: 1, rxTotalBytes: 1, txTotalBytes: 1 },
diskIo: { scope: "cgroup", readBps: 1, writeBps: 1 },
hostUptimeSec: 100,
samples: [],
};
function render(h: SystemHealth, downloaders: Dashboard["sources"]["downloaders"]) {
const client = new QueryClient();
client.setQueryData(qk.sub("system", "health"), h);
client.setQueryData(qk.sub("system", "resources", 30), resources);
client.setQueryData([...qk.dashboard, "sites-accounts"], {
items: [
{ id: "s1", enabled: true, accountStatus: "ok" },
{ id: "s2", enabled: true, accountStatus: "expired" },
{ id: "s3", enabled: false, accountStatus: "expired" },
],
nextCursor: null,
});
return renderToStaticMarkup(
createElement(
QueryClientProvider,
{ client },
createElement(MemoryRouter, null, createElement(HomeRuntime, { downloaders })),
),
);
}
describe("首页运行状态卡", () => {
it("整体健康、CPU / 内存、磁盘、下载器 / 站点 / 媒体库目录摘要,链到运行状态页", () => {
const html = render(health(), [
{ id: "d1", name: "qb", enabled: true, health: "healthy", lastSeenAt: null },
{ id: "d2", name: "qb2", enabled: true, health: "offline", lastSeenAt: null },
] as Dashboard["sources"]["downloaders"]);
expect(html).toContain("运行状态");
expect(html).toContain('href="/console/status"');
expect(html).toContain("需要关注");
expect(html).toContain("42%");
// 内存 ≥ 90% 标红
expect(html).toMatch(/text-destructive[^"]*">92%/);
expect(html).toContain("电影");
expect(html).toContain("87%");
// 摘要:正常数 / 总数(停用的不算)
expect(html).toMatch(/下载器<span[^>]*>1\/2/);
expect(html).toMatch(/站点<span[^>]*>1\/2/);
expect(html).toMatch(/媒体库目录<span[^>]*>1\/2/);
});
it("全部正常时写「全部正常」,没有配置的项不列", () => {
const html = render(health({ level: "ok", mounts: [] }), []);
expect(html).toContain("全部正常");
expect(html).not.toContain("媒体库目录<");
expect(html).not.toContain("下载器<span");
});
});