import { describe, it, expect, beforeEach, afterEach } from "vitest"; import { mkdtempSync, rmSync, existsSync, readFileSync } from "node:fs"; import { join } from "node:path"; import { tmpdir } from "node:os"; import { AgeBackend, generateAgeIdentity } from "../backends/age-backend.js"; describe("AgeBackend", () => { let tmp: string; let identityPath: string; let vaultPath: string; beforeEach(() => { tmp = mkdtempSync(join(tmpdir(), "vault-age-test-")); identityPath = join(tmp, "age.txt"); vaultPath = join(tmp, "vault.age.json"); }); afterEach(() => { rmSync(tmp, { recursive: true, force: true }); }); describe("generateAgeIdentity", () => { it("creates an identity file and returns a recipient", async () => { const recipient = await generateAgeIdentity(identityPath); expect(recipient).toMatch(/^age1/); expect(existsSync(identityPath)).toBe(true); const content = readFileSync(identityPath, "utf-8"); expect(content).toContain("AGE-SECRET-KEY-"); expect(content).toContain(`public key: ${recipient}`); }); }); describe("check", () => { it("reports unavailable when no identity exists", async () => { const backend = new AgeBackend({ identityPath, vaultPath }); const status = await backend.check(); expect(status.available).toBe(false); expect(status.error).toContain("No age identity"); }); it("reports available after identity generation", async () => { await generateAgeIdentity(identityPath); const backend = new AgeBackend({ identityPath, vaultPath }); const status = await backend.check(); expect(status.available).toBe(true); }); }); describe("CRUD operations", () => { let backend: AgeBackend; beforeEach(async () => { await generateAgeIdentity(identityPath); backend = new AgeBackend({ identityPath, vaultPath }); }); it("returns undefined for missing provider", async () => { expect(await backend.get("nonexistent")).toBeUndefined(); }); it("stores and retrieves an api_key entry", async () => { await backend.set("test", { type: "api_key", key: "sk-secret" }); const entry = await backend.get("test"); expect(entry).toEqual({ type: "api_key", key: "sk-secret" }); }); it("stores and retrieves an oauth entry", async () => { const oauth = { type: "oauth" as const, access: "acc-123", refresh: "ref-456", expires: 1700000000000, accountId: "acct-1", }; await backend.set("oauth-test", oauth); const entry = await backend.get("oauth-test"); expect(entry).toEqual(oauth); }); it("overwrites existing entries", async () => { await backend.set("test", { type: "api_key", key: "old" }); await backend.set("test", { type: "api_key", key: "new" }); const entry = await backend.get("test"); expect(entry).toEqual({ type: "api_key", key: "new" }); }); it("removes entries", async () => { await backend.set("test", { type: "api_key", key: "val" }); await backend.remove("test"); expect(await backend.get("test")).toBeUndefined(); }); it("lists stored provider IDs", async () => { await backend.set("a", { type: "api_key", key: "1" }); await backend.set("b", { type: "api_key", key: "2" }); const list = await backend.list(); expect(list).toContain("a"); expect(list).toContain("b"); expect(list).toHaveLength(2); }); it("vault file is not plaintext", async () => { await backend.set("secret", { type: "api_key", key: "do-not-leak" }); const raw = readFileSync(vaultPath, "utf-8"); expect(raw).not.toContain("do-not-leak"); expect(raw).not.toContain("secret"); }); }); describe("PI_VAULT_AGE_KEY env var", () => { it("resolves identity from env var", async () => { const recipient = await generateAgeIdentity(identityPath); const content = readFileSync(identityPath, "utf-8"); const secretKey = content .split("\n") .find((l) => l.startsWith("AGE-SECRET-KEY-")); // Create backend pointing to a non-existent identity file // but with the key in the env var const noFilePath = join(tmp, "no-such-file.txt"); const originalEnv = process.env.PI_VAULT_AGE_KEY; try { process.env.PI_VAULT_AGE_KEY = secretKey; const backend = new AgeBackend({ identityPath: noFilePath, vaultPath, recipients: [recipient], }); await backend.set("env-test", { type: "api_key", key: "from-env" }); const entry = await backend.get("env-test"); expect(entry).toEqual({ type: "api_key", key: "from-env" }); } finally { if (originalEnv === undefined) { delete process.env.PI_VAULT_AGE_KEY; } else { process.env.PI_VAULT_AGE_KEY = originalEnv; } } }); }); });