fix: harden dashboard model validation

This commit is contained in:
vince 2026-06-18 17:43:55 +02:00
parent 7cfd5b4069
commit 74651a49de
4 changed files with 288 additions and 13 deletions

View file

@ -54,6 +54,77 @@ describe("dashboard model validation", () => {
}
});
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 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 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("exports JSON Schema for external tool contracts", () => {
expect(dashboardDocumentJsonSchema.$id).toContain("dashboard-document.v1");
expect(dashboardDocumentJsonSchema.properties).toHaveProperty("schemaVersion");