Files
community-rule/tests/unit/createFlowValidation.test.ts
T
2026-04-06 16:37:15 -06:00

124 lines
3.1 KiB
TypeScript

import { describe, it, expect } from "vitest";
import {
assertPlainJsonValue,
DEFAULT_PLAIN_JSON_LIMITS,
} from "../../lib/server/validation/plainJson";
import {
createFlowStateSchema,
publishRuleBodySchema,
putDraftBodySchema,
} from "../../lib/server/validation/createFlowSchemas";
describe("assertPlainJsonValue", () => {
it("accepts plain JSON structures", () => {
expect(
assertPlainJsonValue(
{ a: [1, "x", { b: null }], c: true },
0,
DEFAULT_PLAIN_JSON_LIMITS,
),
).toBeNull();
});
it("rejects __proto__ keys", () => {
const obj = JSON.parse('{"__proto__": {"x": 1}}') as Record<
string,
unknown
>;
expect(assertPlainJsonValue(obj, 0, DEFAULT_PLAIN_JSON_LIMITS)).toBe(
"Unsafe object key",
);
});
it("rejects non-finite numbers", () => {
expect(assertPlainJsonValue(Number.NaN, 0, DEFAULT_PLAIN_JSON_LIMITS)).toBe(
"Invalid number value",
);
});
it("rejects excessive depth", () => {
let v: unknown = 1;
for (let i = 0; i < 50; i++) {
v = { x: v };
}
expect(assertPlainJsonValue(v, 0, DEFAULT_PLAIN_JSON_LIMITS)).toBe(
"Maximum nesting depth exceeded",
);
});
});
describe("createFlowStateSchema", () => {
it("accepts empty object", () => {
const r = createFlowStateSchema.safeParse({});
expect(r.success).toBe(true);
});
it("accepts known fields and passthrough keys", () => {
const r = createFlowStateSchema.safeParse({
title: "My rule",
currentStep: "cards",
customField: { nested: [1, 2] },
});
expect(r.success).toBe(true);
});
it("rejects invalid currentStep", () => {
const r = createFlowStateSchema.safeParse({ currentStep: "not-a-step" });
expect(r.success).toBe(false);
});
it("rejects title that is too long", () => {
const r = createFlowStateSchema.safeParse({ title: "x".repeat(600) });
expect(r.success).toBe(false);
});
});
describe("putDraftBodySchema", () => {
it("requires payload object", () => {
expect(putDraftBodySchema.safeParse({}).success).toBe(false);
expect(putDraftBodySchema.safeParse({ payload: {} }).success).toBe(true);
});
});
describe("publishRuleBodySchema", () => {
it("accepts minimal valid body", () => {
const r = publishRuleBodySchema.safeParse({
title: " Hello ",
document: { body: "text" },
});
expect(r.success).toBe(true);
if (r.success) {
expect(r.data.title).toBe("Hello");
expect(r.data.summary).toBeNull();
}
});
it("trims summary and maps empty to null", () => {
const r = publishRuleBodySchema.safeParse({
title: "T",
summary: " ",
document: {},
});
expect(r.success).toBe(true);
if (r.success) {
expect(r.data.summary).toBeNull();
}
});
it("rejects empty title", () => {
const r = publishRuleBodySchema.safeParse({
title: " ",
document: {},
});
expect(r.success).toBe(false);
});
it("rejects non-object document", () => {
const r = publishRuleBodySchema.safeParse({
title: "Ok",
document: "nope",
});
expect(r.success).toBe(false);
});
});