Implement Monlet MVP stack and UI updates
This commit is contained in:
133
web/tests/mock-server.mjs
Normal file
133
web/tests/mock-server.mjs
Normal file
@@ -0,0 +1,133 @@
|
||||
import { createServer } from "node:http";
|
||||
|
||||
const PORT = Number(process.env.MOCK_PORT ?? 8765);
|
||||
|
||||
const now = new Date().toISOString();
|
||||
|
||||
const fixtures = {
|
||||
"/api/v1/health": { status: "ok" },
|
||||
"/api/v1/agents": {
|
||||
items: [
|
||||
{
|
||||
agent_id: "agent-1",
|
||||
hostname: "host-1",
|
||||
status: "alive",
|
||||
last_seen_at: now,
|
||||
features: { push: true, metrics: false },
|
||||
labels: { env: "dev", monlet_agent_version: "0.1.0" },
|
||||
},
|
||||
{
|
||||
agent_id: "agent-2",
|
||||
hostname: "host-2",
|
||||
status: "stale",
|
||||
last_seen_at: now,
|
||||
features: { push: true, metrics: false },
|
||||
labels: {},
|
||||
},
|
||||
],
|
||||
next_cursor: null,
|
||||
},
|
||||
"/api/v1/agents/agent-1": {
|
||||
agent_id: "agent-1",
|
||||
hostname: "host-1",
|
||||
status: "alive",
|
||||
last_seen_at: now,
|
||||
features: { push: true, metrics: false },
|
||||
labels: { env: "dev", monlet_agent_version: "0.1.0" },
|
||||
},
|
||||
"/api/v1/checks": {
|
||||
items: [
|
||||
{
|
||||
agent_id: "agent-1",
|
||||
check_id: "disk",
|
||||
status: "ok",
|
||||
exit_code: 0,
|
||||
last_observed_at: now,
|
||||
incident_key: "agent-1:disk",
|
||||
},
|
||||
{
|
||||
agent_id: "agent-1",
|
||||
check_id: "load",
|
||||
status: "warning",
|
||||
exit_code: 1,
|
||||
last_observed_at: now,
|
||||
incident_key: "agent-1:load",
|
||||
},
|
||||
],
|
||||
next_cursor: null,
|
||||
},
|
||||
"/api/v1/incidents": {
|
||||
items: [
|
||||
{
|
||||
id: "00000000-0000-0000-0000-000000000001",
|
||||
incident_key: "agent-1:load",
|
||||
state: "open",
|
||||
severity: "warning",
|
||||
agent_id: "agent-1",
|
||||
check_id: "load",
|
||||
opened_at: now,
|
||||
summary: "load high",
|
||||
},
|
||||
],
|
||||
next_cursor: null,
|
||||
},
|
||||
"/api/v1/events/query": {
|
||||
items: [
|
||||
{
|
||||
event_id: "00000000-0000-7000-8000-000000000001",
|
||||
agent_id: "agent-1",
|
||||
check_id: "disk",
|
||||
observed_at: now,
|
||||
received_at: now,
|
||||
status: "ok",
|
||||
exit_code: 0,
|
||||
duration_ms: 12,
|
||||
},
|
||||
],
|
||||
next_cursor: null,
|
||||
},
|
||||
"/api/v1/notifiers/outbox": {
|
||||
items: [
|
||||
{
|
||||
id: "00000000-0000-0000-0000-000000000002",
|
||||
notifier: "debug",
|
||||
state: "sent",
|
||||
incident_id: "00000000-0000-0000-0000-000000000001",
|
||||
event_type: "firing",
|
||||
attempts: 1,
|
||||
next_attempt_at: null,
|
||||
last_error: null,
|
||||
updated_at: now,
|
||||
},
|
||||
],
|
||||
next_cursor: null,
|
||||
},
|
||||
};
|
||||
|
||||
const server = createServer((req, res) => {
|
||||
const url = new URL(req.url ?? "/", `http://x`);
|
||||
let body = fixtures[url.pathname];
|
||||
if (url.pathname === "/api/v1/checks" && body) {
|
||||
const agentId = url.searchParams.get("agent_id");
|
||||
if (agentId) {
|
||||
body = {
|
||||
...body,
|
||||
items: body.items.filter((item) => item.agent_id === agentId),
|
||||
next_cursor: null,
|
||||
};
|
||||
}
|
||||
}
|
||||
res.setHeader("Content-Type", "application/json");
|
||||
if (!body) {
|
||||
res.statusCode = 404;
|
||||
res.end(
|
||||
JSON.stringify({ error: { code: "not_found", message: "not in fixtures", request_id: "mock" } }),
|
||||
);
|
||||
return;
|
||||
}
|
||||
res.end(JSON.stringify(body));
|
||||
});
|
||||
|
||||
server.listen(PORT, "127.0.0.1", () => {
|
||||
console.log(`mock server on ${PORT}`);
|
||||
});
|
||||
63
web/tests/smoke.spec.ts
Normal file
63
web/tests/smoke.spec.ts
Normal file
@@ -0,0 +1,63 @@
|
||||
import { expect, test } from "@playwright/test";
|
||||
|
||||
test("overview renders tallies", async ({ page }) => {
|
||||
await page.goto("/");
|
||||
await expect(page.getByRole("heading", { name: "Overview" })).toBeVisible();
|
||||
await expect(page.locator("body")).toContainText("Agents");
|
||||
await expect(page.locator("body")).toContainText("Checks");
|
||||
});
|
||||
|
||||
test("agents list links to detail", async ({ page }) => {
|
||||
await page.goto("/agents?sort=status&dir=desc");
|
||||
await expect(page).toHaveURL(/\/agents$/);
|
||||
await page.goto("/agents");
|
||||
await expect(page.getByRole("heading", { name: "Agents" })).toBeVisible();
|
||||
await expect(page.locator("thead")).not.toContainText("hostname");
|
||||
await expect(page.locator("thead")).toContainText("features");
|
||||
await expect(page.locator("thead")).not.toContainText("asc");
|
||||
await expect(page.locator("thead")).not.toContainText("desc");
|
||||
await expect(page.locator("thead")).toContainText("↓");
|
||||
await expect(page.locator("body")).toContainText("checks 2");
|
||||
await expect(page.locator("body")).toContainText("warn 1");
|
||||
await page.getByPlaceholder("search agent").fill("host-1");
|
||||
await expect(page).toHaveURL(/\/agents$/);
|
||||
await expect(page.locator("tbody")).toContainText("host-1");
|
||||
await expect(page.locator("tbody")).not.toContainText("host-2");
|
||||
await page.getByRole("button", { name: "host" }).click();
|
||||
await expect(page).toHaveURL(/\/agents$/);
|
||||
await page.getByRole("button", { name: "clear" }).click();
|
||||
await page.getByRole("link", { name: "agent-1" }).first().click();
|
||||
await expect(page).toHaveURL(/\/agents\/agent-1$/);
|
||||
await expect(page.locator("body")).toContainText("hostname");
|
||||
await expect(page.getByRole("link", { name: "Checks" }).last()).toBeVisible();
|
||||
await expect(page.locator("tbody").first().locator("tr").first()).toContainText("warning");
|
||||
await expect(page.locator("thead").first()).not.toContainText("incident_key");
|
||||
await page.getByRole("link", { name: "check_id" }).click();
|
||||
await expect(page).toHaveURL(/sort=check_id/);
|
||||
await page.getByRole("link", { name: "Events" }).last().click();
|
||||
await expect(page).toHaveURL(/tab=events.*sort=check_id|sort=check_id.*tab=events/);
|
||||
await expect(page.locator("thead").first()).not.toContainText("exit");
|
||||
await page.getByRole("link", { name: "Checks" }).last().click();
|
||||
await expect(page).toHaveURL(/tab=checks.*sort=check_id|sort=check_id.*tab=checks/);
|
||||
});
|
||||
|
||||
test("checks page lists rows", async ({ page }) => {
|
||||
await page.goto("/checks");
|
||||
await expect(page.locator("body")).toContainText("disk");
|
||||
await expect(page.locator("body")).toContainText("load");
|
||||
});
|
||||
|
||||
test("incidents filter renders", async ({ page }) => {
|
||||
await page.goto("/incidents");
|
||||
await expect(page.locator("body")).toContainText("load high");
|
||||
});
|
||||
|
||||
test("events page", async ({ page }) => {
|
||||
await page.goto("/events");
|
||||
await expect(page.getByRole("heading", { name: "Events" })).toBeVisible();
|
||||
});
|
||||
|
||||
test("outbox page", async ({ page }) => {
|
||||
await page.goto("/outbox");
|
||||
await expect(page.locator("body")).toContainText("debug");
|
||||
});
|
||||
Reference in New Issue
Block a user