import { describe, it, expect, beforeEach, afterEach, vi } from "vitest"; import { mkdtemp, rm } from "node:fs/promises"; import { execFile } from "node:child_process"; import { promisify } from "node:util"; import { tmpdir } from "node:os"; import { join } from "node:path"; import { AssetRepository } from "../src/gatekeeper/repository.js"; import { Gatekeeper } from "../src/gatekeeper/gatekeeper.js"; import { GitCommitter } from "../src/autoext/git-committer.js"; import { AutoExtensionService } from "../src/autoext/auto-extension.js"; import { buildProposeAssetTool } from "../src/autoext/tool.js"; import { NullAlertSender } from "../src/alerts/sender.js"; import { createLogger } from "../src/audit/log.js"; import type { ToolSpec } from "../src/agent/types.js"; const exec = promisify(execFile); const log = createLogger("silent"); describe("gatekeeper autorise un outil sanctionné", () => { it("propose_asset (privilégié + sanctioned) est autorisé sans review", () => { const repo = new AssetRepository(":memory:"); const gk = new Gatekeeper(repo, log); const spec: ToolSpec = { name: "chlova.propose_asset", description: "", parameters: {}, server: "chlova", readOnly: false, riskTier: "privileged", sanctioned: true, }; expect(gk.authorizeTool(spec).allowed).toBe(true); repo.close(); }); }); describe("propose_asset tool", () => { let root: string; let repo: AssetRepository; beforeEach(async () => { root = await mkdtemp(join(tmpdir(), "chlova-tool-")); await exec("git", ["init", "-q"], { cwd: root }); await exec("git", ["-c", "user.name=t", "-c", "user.email=t@t", "commit", "--allow-empty", "-q", "-m", "init"], { cwd: root }); repo = new AssetRepository(":memory:"); }); afterEach(async () => { repo.close(); await rm(root, { recursive: true, force: true }); }); it("exécute la proposition et renvoie un résumé", async () => { const svc = new AutoExtensionService(repo, new GitCommitter(root), new NullAlertSender(log), log, root); const tool = buildProposeAssetTool(svc); expect(tool.spec.sanctioned).toBe(true); const out = await tool.execute({ type: "tool", name: "Ping Host", version: "1.0.0", riskTier: "reversible", summary: "ping", content: "{}", }); expect(out).toContain("need-review"); expect(repo.listByStatus("provisoire")).toHaveLength(1); }); });