import { describe, expect, it } from "vitest"; import { DASHBOARD_SCHEMA_VERSION, dashboardDocumentJsonSchema, validateDashboardDocument, } from "."; import { dimensionLabDashboardFixture, genericDashboardFixture, } from "./fixtures"; describe("dashboard model validation", () => { it("accepts the generic dashboard fixture", () => { const result = validateDashboardDocument(genericDashboardFixture); expect(result.valid).toBe(true); if (result.valid) { expect(result.data.schemaVersion).toBe(DASHBOARD_SCHEMA_VERSION); } }); it("accepts the Dimension Lab dashboard fixture", () => { const result = validateDashboardDocument(dimensionLabDashboardFixture); expect(result.valid).toBe(true); }); it("rejects documents with an unsupported schema version", () => { const invalid = { ...genericDashboardFixture, schemaVersion: "dashboard.v0", }; const result = validateDashboardDocument(invalid); expect(result.valid).toBe(false); if (!result.valid) { expect(result.errors.join(" ")).toContain("must be equal to constant"); expect(result.details.length).toBeGreaterThan(0); } }); it("returns actionable field paths for invalid documents", () => { const invalid = JSON.parse(JSON.stringify(genericDashboardFixture)); delete invalid.metadata.title; invalid.telemetry[0].severity = "fine"; const result = validateDashboardDocument(invalid); expect(result.valid).toBe(false); if (!result.valid) { expect(result.errors.some((error) => error.includes("/metadata"))).toBe(true); expect(result.errors.some((error) => error.includes("/telemetry/0/severity"))).toBe(true); } }); it("includes offending additional property names in errors", () => { const invalid = JSON.parse(JSON.stringify(genericDashboardFixture)); invalid.metadata.unexpected = true; const result = validateDashboardDocument(invalid); expect(result.valid).toBe(false); if (!result.valid) { expect(result.errors.join(" ")).toContain("unexpected"); } }); it("rejects non-finite numbers that cannot roundtrip through JSON", () => { const invalid = JSON.parse(JSON.stringify(genericDashboardFixture)); invalid.telemetry[0].value.value = Number.NaN; const result = validateDashboardDocument(invalid); expect(result.valid).toBe(false); if (!result.valid) { expect(result.errors.join(" ")).toContain("finite JSON number"); } }); it("rejects dangling layout references", () => { const invalid = JSON.parse(JSON.stringify(genericDashboardFixture)); invalid.layout.telemetry.push("missing-card"); const result = validateDashboardDocument(invalid); expect(result.valid).toBe(false); if (!result.valid) { expect(result.errors.join(" ")).toContain("missing-card"); expect(result.errors.join(" ")).toContain("must reference an existing item"); } }); it("rejects duplicate IDs within collections", () => { const invalid = JSON.parse(JSON.stringify(genericDashboardFixture)); invalid.telemetry[1].id = invalid.telemetry[0].id; const result = validateDashboardDocument(invalid); expect(result.valid).toBe(false); if (!result.valid) { expect(result.errors.join(" ")).toContain("must be unique"); expect(result.errors.join(" ")).toContain(invalid.telemetry[0].id); } }); it("rejects duplicate status strip item IDs", () => { const invalid = JSON.parse(JSON.stringify(genericDashboardFixture)); invalid.statusStrips[0].items.push({ ...invalid.statusStrips[0].items[0], }); const result = validateDashboardDocument(invalid); expect(result.valid).toBe(false); if (!result.valid) { expect(result.errors.join(" ")).toContain("must be unique"); expect(result.errors.join(" ")).toContain(invalid.statusStrips[0].items[0].id); } }); it("rejects string values for numeric metric kinds", () => { const invalid = JSON.parse(JSON.stringify(genericDashboardFixture)); invalid.telemetry[0].value.value = "not a percent"; const result = validateDashboardDocument(invalid); expect(result.valid).toBe(false); }); it("rejects percent values outside 0 to 100", () => { const invalid = JSON.parse(JSON.stringify(genericDashboardFixture)); invalid.telemetry[0].value.value = 150; const result = validateDashboardDocument(invalid); expect(result.valid).toBe(false); }); it("rejects negative values for nonnegative metric kinds", () => { const invalid = JSON.parse(JSON.stringify(genericDashboardFixture)); invalid.telemetry[0].value = { kind: "latency", value: -20 }; const result = validateDashboardDocument(invalid); expect(result.valid).toBe(false); }); it("rejects contradictory warning and danger thresholds", () => { const invalid = JSON.parse(JSON.stringify(genericDashboardFixture)); invalid.telemetry[0].thresholds = { warning: 90, danger: 80 }; const result = validateDashboardDocument(invalid); expect(result.valid).toBe(false); if (!result.valid) { expect(result.errors.join(" ")).toContain("warning threshold"); } }); it("rejects percent thresholds above 100", () => { const invalid = JSON.parse(JSON.stringify(genericDashboardFixture)); invalid.telemetry[0].thresholds = { warning: 99, danger: 999 }; const result = validateDashboardDocument(invalid); expect(result.valid).toBe(false); if (!result.valid) { expect(result.errors.join(" ")).toContain("percent thresholds"); } }); it("rejects thresholds on text metric values", () => { const invalid = JSON.parse(JSON.stringify(genericDashboardFixture)); invalid.telemetry[0].value = { kind: "text", value: "available" }; invalid.telemetry[0].thresholds = { warning: 10 }; const result = validateDashboardDocument(invalid); expect(result.valid).toBe(false); if (!result.valid) { expect(result.errors.join(" ")).toContain("text metric values"); } }); it("rejects undefined properties because they are not JSON values", () => { const invalid = JSON.parse(JSON.stringify(genericDashboardFixture)); invalid.serviceGroups[0].services[0].link = undefined; const result = validateDashboardDocument(invalid); expect(result.valid).toBe(false); if (!result.valid) { expect(result.errors.join(" ")).toContain("must be omitted instead of undefined"); } }); it("exports JSON Schema for external tool contracts", () => { expect(dashboardDocumentJsonSchema.$id).toContain("dashboard-document.v1"); expect(dashboardDocumentJsonSchema.properties).toHaveProperty("schemaVersion"); expect(dashboardDocumentJsonSchema.properties).toHaveProperty("telemetry"); expect(dashboardDocumentJsonSchema.properties).toHaveProperty("serviceGroups"); }); });