Files
Stirling-PDF/frontend/shared/policies/codec.test.ts
T
2026-07-01 13:42:26 +01:00

122 lines
4.2 KiB
TypeScript

import { describe, it, expect } from "vitest";
import { toWirePolicy, fromWirePolicy } from "@shared/policies/codec";
import type { PolicyDecodedState } from "@shared/policies/types";
const FULL_STATE: PolicyDecodedState = {
id: "pol_123",
name: "Security Policy",
enabled: true,
categoryId: "security",
sources: ["editor", "gdrive"],
scopeTypes: ["Contracts", "Invoices"],
reviewerEmail: "admin@example.com",
fieldValues: { auditTrail: true, frameworks: ["HIPAA"] },
runOn: "upload",
outputMode: "new_version",
outputName: "redacted",
outputNamePosition: "prefix",
maxRetries: 3,
retryDelayMinutes: 5,
steps: [
{
operation: "/api/v1/security/auto-redact",
parameters: { mode: "automatic" },
},
],
};
describe("toWirePolicy", () => {
it("sets trigger to null", () => {
expect(toWirePolicy(FULL_STATE).trigger).toBeNull();
});
it("sets output.type to inline", () => {
expect(toWirePolicy(FULL_STATE).output.type).toBe("inline");
});
it("packs metadata into output.options", () => {
const wire = toWirePolicy(FULL_STATE);
const opts = wire.output.options;
expect(opts.categoryId).toBe("security");
expect(opts.sources).toEqual(["editor", "gdrive"]);
expect(opts.runOn).toBe("upload");
expect(opts.mode).toBe("new_version");
expect(opts.position).toBe("prefix");
});
it("preserves steps at the top level", () => {
const wire = toWirePolicy(FULL_STATE);
expect(wire.steps).toEqual(FULL_STATE.steps);
});
});
describe("fromWirePolicy → round-trip", () => {
it("recovers all fields after encode→decode", () => {
const wire = toWirePolicy(FULL_STATE);
const decoded = fromWirePolicy(wire);
expect(decoded.id).toBe(FULL_STATE.id);
expect(decoded.categoryId).toBe(FULL_STATE.categoryId);
expect(decoded.sources).toEqual(FULL_STATE.sources);
expect(decoded.scopeTypes).toEqual(FULL_STATE.scopeTypes);
expect(decoded.reviewerEmail).toBe(FULL_STATE.reviewerEmail);
expect(decoded.fieldValues).toEqual(FULL_STATE.fieldValues);
expect(decoded.runOn).toBe(FULL_STATE.runOn);
expect(decoded.outputMode).toBe(FULL_STATE.outputMode);
expect(decoded.outputName).toBe(FULL_STATE.outputName);
expect(decoded.outputNamePosition).toBe(FULL_STATE.outputNamePosition);
expect(decoded.maxRetries).toBe(FULL_STATE.maxRetries);
expect(decoded.retryDelayMinutes).toBe(FULL_STATE.retryDelayMinutes);
expect(decoded.steps).toEqual(FULL_STATE.steps);
});
it("defaults runOn to upload when missing", () => {
const wire = toWirePolicy(FULL_STATE);
delete (wire.output.options as Record<string, unknown>).runOn;
expect(fromWirePolicy(wire).runOn).toBe("upload");
});
it("defaults outputMode to new_version when missing", () => {
const wire = toWirePolicy(FULL_STATE);
delete (wire.output.options as Record<string, unknown>).mode;
expect(fromWirePolicy(wire).outputMode).toBe("new_version");
});
it("preserves export runOn", () => {
const wire = toWirePolicy({ ...FULL_STATE, runOn: "export" });
expect(fromWirePolicy(wire).runOn).toBe("export");
});
it("preserves new_file outputMode", () => {
const wire = toWirePolicy({ ...FULL_STATE, outputMode: "new_file" });
expect(fromWirePolicy(wire).outputMode).toBe("new_file");
});
it("preserves all three outputNamePosition values", () => {
for (const pos of ["prefix", "suffix", "auto-number"] as const) {
const wire = toWirePolicy({ ...FULL_STATE, outputNamePosition: pos });
expect(fromWirePolicy(wire).outputNamePosition).toBe(pos);
}
});
it("handles empty options gracefully", () => {
const decoded = fromWirePolicy({
id: "x",
name: "X",
enabled: false,
trigger: null,
steps: [],
output: { type: "inline", options: {} },
});
expect(decoded.categoryId).toBe("");
expect(decoded.sources).toEqual([]);
expect(decoded.runOn).toBe("upload");
expect(decoded.outputMode).toBe("new_version");
});
it("defaults fieldValues to empty object when missing", () => {
const wire = toWirePolicy(FULL_STATE);
delete (wire.output.options as Record<string, unknown>).fieldValues;
expect(fromWirePolicy(wire).fieldValues).toEqual({});
});
});