tighten validation, add tests

This commit is contained in:
Alex committed 2026-05-11 11:28:39 -07:00
1 parent 78fdc548bb
commit 6b63091d89
3 files changed
+111 -20

No files matched your search

@@ -1,15 +0,0 @@
import type { Pool } from "pg";
export const addStrategyToMonitors = async (pool: Pool) => {
await pool.query(`
ALTER TABLE monitors
ADD COLUMN IF NOT EXISTS strategy TEXT DEFAULT NULL;
`);
};
export const dropStrategyFromMonitors = async (pool: Pool) => {
await pool.query(`
ALTER TABLE monitors
DROP COLUMN IF EXISTS strategy;
`);
};
+18 -5
View File
@@ -1,7 +1,7 @@
import { z } from "zod";
import { booleanCoercion, dnsHostnameRegex, dnsServerValidation } from "./shared.js";
import { GeoContinents } from "@/types/geoCheck.js";
import { DefaultPageSpeedStrategy, DnsRecordTypes, MonitorMatchMethods, MonitorStatuses, MonitorTypes, PageSpeedStrategies } from "@/types/monitor.js";
import { DnsRecordTypes, MonitorMatchMethods, MonitorStatuses, MonitorTypes, PageSpeedStrategies } from "@/types/monitor.js";
export const getMonitorByIdParamValidation = z.object({
monitorId: z.string().min(1, "Monitor ID is required"),
@@ -49,6 +49,16 @@ const refineDnsHostname = (body: { type?: string; url?: string }, ctx: z.Refinem
}
};
const refineStrategyType = (body: { type?: string; strategy?: string }, ctx: z.RefinementCtx) => {
if (body.strategy !== undefined && body.type !== undefined && body.type !== "pagespeed") {
ctx.addIssue({
code: "custom",
path: ["strategy"],
message: "Strategy is only valid for pagespeed monitors",
});
}
};
export const createMonitorBodyValidation = z
.object({
_id: z.string().optional(),
@@ -83,7 +93,8 @@ export const createMonitorBodyValidation = z
dnsServer: dnsServerValidation.optional(),
dnsRecordType: z.enum(DnsRecordTypes).optional(),
})
.superRefine(refineDnsHostname);
.superRefine(refineDnsHostname)
.superRefine(refineStrategyType);
export const editMonitorBodyValidation = z
.object({
@@ -117,7 +128,8 @@ export const editMonitorBodyValidation = z
dnsServer: dnsServerValidation.optional(),
dnsRecordType: z.enum(DnsRecordTypes).optional(),
})
.superRefine(refineDnsHostname);
.superRefine(refineDnsHostname)
.superRefine(refineStrategyType);
export const pauseMonitorParamValidation = z.object({
monitorId: z.string().min(1, "Monitor ID is required"),
@@ -175,7 +187,7 @@ const importedMonitorSchema = z
selectedDisks: z.array(z.string()).default([]),
gameId: z.union([z.string(), z.literal("")]).optional(),
grpcServiceName: z.union([z.string(), z.literal("")]).default(""),
strategy: z.enum(PageSpeedStrategies).default(DefaultPageSpeedStrategy),
strategy: z.enum(PageSpeedStrategies).optional(),
group: z.union([z.string().max(50).trim(), z.null()]).default(null),
geoCheckEnabled: z.boolean().default(false),
geoCheckLocations: z.array(z.enum(GeoContinents)).default([]),
@@ -185,7 +197,8 @@ const importedMonitorSchema = z
createdAt: z.string().optional(),
updatedAt: z.string().optional(),
})
.superRefine(refineDnsHostname);
.superRefine(refineDnsHostname)
.superRefine(refineStrategyType);
export const importMonitorsBodyValidation = z.object({
monitors: z.array(importedMonitorSchema).min(1, "At least one monitor is required"),
@@ -129,3 +129,96 @@ describe("monitorValidation — DNS fields", () => {
});
});
});
describe("monitorValidation — strategy gating", () => {
describe("createMonitorBodyValidation", () => {
it("accepts strategy on pagespeed monitors", () => {
const parsed = createMonitorBodyValidation.parse({
name: "PS check",
type: "pagespeed",
url: "https://example.com",
strategy: "mobile",
});
expect(parsed.strategy).toBe("mobile");
});
it("accepts pagespeed monitors without a strategy", () => {
const parsed = createMonitorBodyValidation.parse({
name: "PS check",
type: "pagespeed",
url: "https://example.com",
});
expect(parsed.strategy).toBeUndefined();
});
it("rejects strategy on non-pagespeed monitor types", () => {
for (const type of ["http", "ping", "docker", "port", "game", "grpc", "websocket", "dns", "hardware"] as const) {
expect(() =>
createMonitorBodyValidation.parse({
name: "wrong-type check",
type,
url: type === "dns" ? "example.com" : "https://example.com",
strategy: "desktop",
})
).toThrow();
}
});
it("allows non-pagespeed monitors without a strategy", () => {
const parsed = createMonitorBodyValidation.parse({
name: "HTTP check",
type: "http",
url: "https://example.com",
});
expect(parsed.strategy).toBeUndefined();
});
});
describe("editMonitorBodyValidation", () => {
it("rejects strategy when type is explicitly non-pagespeed", () => {
expect(() =>
editMonitorBodyValidation.parse({
type: "http",
strategy: "mobile",
})
).toThrow();
});
it("allows strategy when type is omitted (partial edit)", () => {
const parsed = editMonitorBodyValidation.parse({
strategy: "mobile",
});
expect(parsed.strategy).toBe("mobile");
});
});
describe("importMonitorsBodyValidation", () => {
it("does not inject a default strategy on imported HTTP monitors", () => {
const parsed = importMonitorsBodyValidation.parse({
monitors: [
{
name: "Imported HTTP",
type: "http",
url: "https://example.com",
},
],
});
expect(parsed.monitors[0].strategy).toBeUndefined();
});
it("rejects strategy on imported non-pagespeed monitors", () => {
expect(() =>
importMonitorsBodyValidation.parse({
monitors: [
{
name: "Imported HTTP",
type: "http",
url: "https://example.com",
strategy: "desktop",
},
],
})
).toThrow();
});
});
});