import { describe, it, expect, beforeEach, afterEach } from "vitest"; import { AssetRepository } from "../src/gatekeeper/repository.js"; import { ReviewService, runExpiryOnce } from "../src/gatekeeper/review.js"; import { handleReviewCommand, isCommand } from "../src/surfaces/commands.js"; import { createAsset, PROVISIONAL_TTL_MS } from "../src/gatekeeper/assets.js"; import { createLogger } from "../src/audit/log.js"; const log = createLogger("silent"); let repo: AssetRepository; let review: ReviewService; beforeEach(() => { repo = new AssetRepository(":memory:"); review = new ReviewService(repo, log); }); afterEach(() => repo.close()); describe("ReviewService", () => { it("approuve un asset bloqué", () => { repo.create(createAsset({ id: "p", type: "stack-portainer", version: "1.0.0", riskTier: "privileged" })); expect(review.approve("p").status).toBe("approuvé"); expect(repo.get("p")?.status).toBe("approuvé"); }); it("refuse un asset", () => { repo.create(createAsset({ id: "p", type: "tool", version: "1.0.0", riskTier: "privileged" })); expect(review.refuse("p").status).toBe("refusé"); }); it("lève sur asset inconnu", () => { expect(() => review.approve("nope")).toThrow(/inconnu/); }); it("listPending renvoie bloqués + provisoires", () => { repo.create(createAsset({ id: "b", type: "tool", version: "1.0.0", riskTier: "privileged" })); repo.create(createAsset({ id: "r", type: "tool", version: "1.0.0", riskTier: "reversible" })); expect(review.listPending().map((a) => a.id).sort()).toEqual(["b", "r"]); }); }); describe("runExpiryOnce", () => { it("bascule les provisoires échus", () => { const t0 = Date.now() - PROVISIONAL_TTL_MS - 1000; repo.create(createAsset({ id: "old", type: "tool", version: "1.0.0", riskTier: "reversible", now: t0 })); expect(runExpiryOnce(repo, log)).toEqual(["old"]); expect(repo.get("old")?.status).toBe("bloqué"); }); }); describe("commandes de review", () => { it("isCommand détecte le préfixe /", () => { expect(isCommand("/pending")).toBe(true); expect(isCommand("bonjour")).toBe(false); }); it("/pending liste ou indique vide", () => { expect(handleReviewCommand(review, "/pending")).toMatch(/Aucun/); repo.create(createAsset({ id: "p", type: "tool", version: "1.0.0", riskTier: "privileged" })); expect(handleReviewCommand(review, "/pending")).toContain("p"); }); it("/approve approuve", () => { repo.create(createAsset({ id: "p", type: "tool", version: "1.0.0", riskTier: "privileged" })); expect(handleReviewCommand(review, "/approve p")).toMatch(/approuvé/); expect(repo.get("p")?.status).toBe("approuvé"); }); it("/approve sans id renvoie l'usage", () => { expect(handleReviewCommand(review, "/approve")).toMatch(/Usage/); }); it("commande inconnue renvoie l'aide", () => { expect(handleReviewCommand(review, "/wat")).toMatch(/inconnue/); }); });