import { beforeEach, describe, it } from "@std/testing/bdd"; import { createInMemoryFile, generateKeyPair, startMockFs, } from "../../../tests/fixtures/setup.ts"; import { armored, binary, createKeysFromFs, DEFAULT_KEY_DISCOVERY_RULES, } from "./create.ts"; import { assertEquals, assertRejects } from "@std/assert"; import { stub } from "@std/testing/mock"; startMockFs(); describe("createKeysFromFs", () => { let keyPair: Awaited>; beforeEach(async () => { keyPair = await generateKeyPair("Alice"); }); it("loads a single armored key file", async () => { const url = createInMemoryFile( new URL("file:///mock/alice.asc"), keyPair.privateKey.armor(), ); const keys = []; for await (const key of createKeysFromFs(url)) { keys.push(key); } assertEquals(keys.length, 1); }); it("loads a single binary key file", async () => { const binaryData = keyPair.privateKey.write(); const url = createInMemoryFile( new URL("file:///mock/alice.gpg"), binaryData as Uint8Array, ); const keys = []; for await (const key of createKeysFromFs(url)) { keys.push(key); } assertEquals(keys.length, 1); }); it("ignores unsupported file extensions", async () => { const url = createInMemoryFile( new URL("file:///mock/ignored.txt"), "This is not a key", ); const keys = []; for await (const key of createKeysFromFs(url)) { keys.push(key); } assertEquals(keys.length, 0); }); it("throws on overlapping discovery formats", async () => { const rules = { formats: { [armored]: new Set(["asc", "gpg"]), [binary]: new Set(["gpg"]), }, }; const url = new URL("file:///mock/bogus.gpg"); await assertRejects(() => createKeysFromFs(url, rules).next()); }); it("handles recursive directory traversal", async () => { const aliceURL = new URL("file:///mock/keys/alice.asc"); const bobURL = new URL("file:///mock/keys/sub/bob.asc"); createInMemoryFile(aliceURL, keyPair.privateKey.armor()); createInMemoryFile(bobURL, keyPair.privateKey.armor()); const mockedDirTree = { "file:///mock/keys/": [ { name: "alice.asc", isFile: true, isDirectory: false }, { name: "sub", isFile: false, isDirectory: true }, ], "file:///mock/keys/sub/": [ { name: "bob.asc", isFile: true, isDirectory: false }, ], }; stub(Deno, "stat", (url: URL | string) => { const href = new URL(url).href; return Promise.resolve({ isDirectory: href.endsWith("/") || href.includes("/sub"), isFile: href.endsWith(".asc"), isSymlink: false, } as Deno.FileInfo); }); stub(Deno, "readDir", async function* (url: URL | string) { const href = new URL(url).href; for ( const entry of mockedDirTree[href as keyof typeof mockedDirTree] ?? [] ) { yield entry as Deno.DirEntry; } }); const root = new URL("file:///mock/keys/"); const keys = []; for await ( const key of createKeysFromFs( root, { ...DEFAULT_KEY_DISCOVERY_RULES, recursive: true }, ) ) { keys.push(key); } assertEquals(keys.length, 2); }); });