tighten validation, add tests
This commit is contained in:
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;
|
||||
`);
|
||||
};
|
||||
@@ -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();
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user