import { describe, expect, it } from "vitest"; import { requirePermission, canPromote, type PromotionEvidence, acceptResearchResult, taskIngress, } from "./controls"; // ── requirePermission ─────────────────────────────────────────────────────── describe("requirePermission", () => { it("throws Unauthenticated when principal is null", () => { expect(() => requirePermission(null, "read")).toThrow("Unauthenticated"); }); it("throws Unauthenticated when subject is empty string", () => { const principal = { subject: "", permissions: ["read"] } as const; expect(() => requirePermission(principal, "write")).toThrow("Unauthenticated"); }); it("throws Unauthenticated when subject is whitespace only", () => { const principal = { subject: " ", permissions: ["read"] } as const; expect(() => requirePermission(principal, "write")).toThrow("Unauthenticated"); }); it("throws Forbidden when requested permission is empty", () => { const principal = { subject: "user@example.com", permissions: ["read"] } as const; expect(() => requirePermission(principal, "")).toThrow("Forbidden"); }); it("throws Forbidden when requested permission is whitespace", () => { const principal = { subject: "user@example.com", permissions: ["read"] } as const; expect(() => requirePermission(principal, " ")).toThrow("Forbidden"); }); it("throws Forbidden when permission is absent", () => { const principal = { subject: "user@example.com", permissions: ["read"] } as const; expect(() => requirePermission(principal, "write")).toThrow("Forbidden"); }); it("returns undefined when principal has the permission", () => { const principal = { subject: "user@example.com", permissions: ["read", "write"] } as const; expect(requirePermission(principal, "read")).toBeUndefined(); }); }); // ── canPromote ────────────────────────────────────────────────────────────── describe("canPromote", () => { const validDigest = "sha256:" + "a".repeat(64); it("returns true when all conditions are met", () => { const evidence: PromotionEvidence = { testedDigest: validDigest, proposedDigest: validDigest, approvedDigest: validDigest, checksPassed: true, }; expect(canPromote(evidence)).toBe(true); }); it("returns false when checksPassed is false", () => { const evidence: PromotionEvidence = { testedDigest: validDigest, proposedDigest: validDigest, approvedDigest: validDigest, checksPassed: false, }; expect(canPromote(evidence)).toBe(false); }); it("returns false when proposedDigest does not match testedDigest", () => { const evidence: PromotionEvidence = { testedDigest: validDigest, proposedDigest: "sha256:" + "b".repeat(64), approvedDigest: validDigest, checksPassed: true, }; expect(canPromote(evidence)).toBe(false); }); it("returns false when approvedDigest does not match testedDigest", () => { const evidence: PromotionEvidence = { testedDigest: validDigest, proposedDigest: validDigest, approvedDigest: "sha256:" + "b".repeat(64), checksPassed: true, }; expect(canPromote(evidence)).toBe(false); }); it("returns false when testedDigest is malformed", () => { const evidence: PromotionEvidence = { testedDigest: "sha512:" + "a".repeat(64), proposedDigest: "sha512:" + "a".repeat(64), approvedDigest: "sha512:" + "a".repeat(64), checksPassed: true, }; expect(canPromote(evidence)).toBe(false); }); }); // ── acceptResearchResult ──────────────────────────────────────────────────── describe("acceptResearchResult", () => { const permitted = new Set(["https://docs.example.org/guide"]); const rawValid = { taskId: "task-1", summary: "A summary", sources: [{ url: "https://docs.example.org/guide", title: "Guide" }], }; it("returns a fresh object when input is valid", () => { const result = acceptResearchResult(rawValid, "task-1", permitted); expect(result).toEqual({ taskId: "task-1", summary: "A summary", sources: [{ url: "https://docs.example.org/guide", title: "Guide" }], }); }); it("throws for wrong taskId", () => { expect(() => acceptResearchResult(rawValid, "task-2", permitted)).toThrow("Invalid research result"); }); it("throws when raw is null", () => { expect(() => acceptResearchResult(null as unknown, "task-1", permitted)).toThrow("Invalid research result"); }); it("throws when raw is not an object", () => { expect(() => acceptResearchResult("string" as unknown, "task-1", permitted)).toThrow("Invalid research result"); }); it("throws when raw is an array", () => { expect(() => acceptResearchResult([] as unknown, "task-1", permitted)).toThrow("Invalid research result"); }); it("throws when raw has an unknown key", () => { const bad = { ...rawValid, measuredCoverage: 100 }; expect(() => acceptResearchResult(bad as unknown, "task-1", permitted)).toThrow("Invalid research result"); }); it("throws when sources array is empty", () => { const bad = { ...rawValid, sources: [] }; expect(() => acceptResearchResult(bad as unknown, "task-1", permitted)).toThrow("Invalid research result"); }); it("throws when sources array has too many entries", () => { const sources = Array.from({ length: 11 }, (_, index) => ({ url: `https://docs.example.org/guide-${index}`, title: "Source", })); const admitted = new Set(sources.map((source) => source.url)); const bad = { ...rawValid, sources }; expect(() => acceptResearchResult(bad, "task-1", admitted)) .toThrow("Invalid research result"); }); it("throws for source with unknown url", () => { const bad = { ...rawValid, sources: [{ url: "https://other.example.org/x", title: "X" }] }; expect(() => acceptResearchResult(bad as unknown, "task-1", permitted)).toThrow("Invalid research result"); }); it("throws for source with HTTP url", () => { const bad = { ...rawValid, sources: [{ url: "http://docs.example.org/guide", title: "Guide" }] }; expect(() => acceptResearchResult(bad as unknown, "task-1", new Set(["http://docs.example.org/guide"]))).toThrow("Invalid research result"); }); it("throws for source with credentialed URL", () => { const bad = { ...rawValid, sources: [{ url: "https://user:pass@docs.example.org/guide", title: "Guide" }] }; expect(() => acceptResearchResult(bad as unknown, "task-1", new Set(["https://user:pass@docs.example.org/guide"]))).toThrow("Invalid research result"); }); it("throws when summary is blank", () => { const bad = { ...rawValid, summary: "" }; expect(() => acceptResearchResult(bad, "task-1", permitted)).toThrow("Invalid research result"); }); it("throws when summary exceeds 2000 characters", () => { const bad = { ...rawValid, summary: "a".repeat(2001) }; expect(() => acceptResearchResult(bad, "task-1", permitted)).toThrow("Invalid research result"); }); it("throws when source title is blank", () => { const bad = { ...rawValid, sources: [{ url: "https://docs.example.org/guide", title: "" }] }; expect(() => acceptResearchResult(bad as unknown, "task-1", permitted)).toThrow("Invalid research result"); }); it("throws when title exceeds 200 characters", () => { const bad = { ...rawValid, sources: [{ url: "https://docs.example.org/guide", title: "a".repeat(201) }] }; expect(() => acceptResearchResult(bad as unknown, "task-1", permitted)).toThrow("Invalid research result"); }); it("throws for duplicate source URLs", () => { const bad = { ...rawValid, sources: [ { url: "https://docs.example.org/guide", title: "A" }, { url: "https://docs.example.org/guide", title: "B" }, ], }; expect(() => acceptResearchResult(bad, "task-1", permitted)).toThrow("Invalid research result"); }); it("does not mutate the input object", () => { const snapshot = JSON.stringify(rawValid); acceptResearchResult(rawValid, "task-1", permitted); expect(JSON.stringify(rawValid)).toBe(snapshot); }); it("returns independent sources on a second call", () => { const r1 = acceptResearchResult(rawValid, "task-1", permitted); const r2 = acceptResearchResult(rawValid, "task-1", permitted); r1.sources![0].title = "changed"; expect(r2.sources![0].title).toBe("Guide"); }); }); // ── taskIngress ───────────────────────────────────────────────────────────── describe("taskIngress", () => { it("throws when both security group IDs are the same", () => { expect(() => taskIngress("sg-aaaaaaaaaaaaaaaaa", "sg-aaaaaaaaaaaaaaaaa"), ).toThrow("Invalid security group"); }); it("accepts distinct 17-character security group IDs", () => { expect(taskIngress("sg-aaaaaaaaaaaaaaaaa", "sg-bbbbbbbbbbbbbbbbb").Properties) .toMatchObject({ SourceSecurityGroupId: "sg-aaaaaaaaaaaaaaaaa" }); }); it("throws when loadBalancerSecurityGroupId is invalid", () => { expect(() => taskIngress("invalid", "sg-bbbbbbbb"), ).toThrow("Invalid security group"); }); it("throws when taskSecurityGroupId is invalid", () => { expect(() => taskIngress("sg-aaaaaaaaaaaaaaaaa", "invalid"), ).toThrow("Invalid security group"); }); it("returns a CloudFormation SecurityGroupIngress resource for valid distinct IDs", () => { const res = taskIngress("sg-aaaaaaaa", "sg-bbbbbbbb"); expect(res).toEqual({ Type: "AWS::EC2::SecurityGroupIngress", Properties: { GroupId: "sg-bbbbbbbb", SourceSecurityGroupId: "sg-aaaaaaaa", IpProtocol: "tcp", FromPort: 443, ToPort: 443, }, }); }); it("does not include CidrIp, CidrIpv6, or SourcePrefixListId", () => { const res = taskIngress("sg-aaaaaaaa", "sg-bbbbbbbb"); const { Type: _, ...props } = res as typeof res & { Properties: Record }; const keys = Object.keys(props.Properties); expect(keys).not.toContain("CidrIp"); expect(keys).not.toContain("CidrIpv6"); expect(keys).not.toContain("SourcePrefixListId"); }); });