|
| 1 | +import { describe, test, expect } from "bun:test"; |
| 2 | +import fs from "node:fs"; |
| 3 | +import os from "node:os"; |
| 4 | +import path from "node:path"; |
| 5 | + |
| 6 | +import { loadOrCreateCommitSigner } from "./commit-signer.js"; |
| 7 | + |
| 8 | +const KEY_FILE = path.join("keys", "commit-ed25519.json"); |
| 9 | + |
| 10 | +function tempDir(): string { |
| 11 | + return fs.mkdtempSync(path.join(os.tmpdir(), "commit-signer-")); |
| 12 | +} |
| 13 | + |
| 14 | +function keyPath(dir: string): string { |
| 15 | + return path.join(dir, KEY_FILE); |
| 16 | +} |
| 17 | + |
| 18 | +function publicKeyInFile(dir: string): string { |
| 19 | + const parsed = JSON.parse(fs.readFileSync(keyPath(dir), "utf8")) as { publicKey: string }; |
| 20 | + return parsed.publicKey; |
| 21 | +} |
| 22 | + |
| 23 | +describe("loadOrCreateCommitSigner", () => { |
| 24 | + test("first call creates a signed-able signer and a 0600 key file", async () => { |
| 25 | + const dir = tempDir(); |
| 26 | + const signer = await loadOrCreateCommitSigner(dir); |
| 27 | + const signature = await signer("payload"); |
| 28 | + expect(typeof signature).toBe("string"); |
| 29 | + expect(signature.length).toBeGreaterThan(0); |
| 30 | + |
| 31 | + const st = fs.statSync(keyPath(dir)); |
| 32 | + expect(st.mode & 0o777).toBe(0o600); |
| 33 | + }); |
| 34 | + |
| 35 | + test("second call reloads the same key (same publicKey bytes in the file)", async () => { |
| 36 | + const dir = tempDir(); |
| 37 | + await loadOrCreateCommitSigner(dir); |
| 38 | + const firstPublic = publicKeyInFile(dir); |
| 39 | + await loadOrCreateCommitSigner(dir); |
| 40 | + expect(publicKeyInFile(dir)).toBe(firstPublic); |
| 41 | + }); |
| 42 | + |
| 43 | + test("concurrent first-time create: both succeed, only one key file, both signers work", async () => { |
| 44 | + const dir = tempDir(); |
| 45 | + const [a, b] = await Promise.all([ |
| 46 | + loadOrCreateCommitSigner(dir), |
| 47 | + loadOrCreateCommitSigner(dir), |
| 48 | + ]); |
| 49 | + const sigA = await a("a"); |
| 50 | + const sigB = await b("b"); |
| 51 | + expect(typeof sigA).toBe("string"); |
| 52 | + expect(typeof sigB).toBe("string"); |
| 53 | + expect(sigA.length).toBeGreaterThan(0); |
| 54 | + expect(sigB.length).toBeGreaterThan(0); |
| 55 | + expect(fs.readdirSync(path.join(dir, "keys"))).toEqual(["commit-ed25519.json"]); |
| 56 | + }); |
| 57 | + |
| 58 | + test("corrupt JSON throws Invalid commit signing key", async () => { |
| 59 | + const dir = tempDir(); |
| 60 | + fs.mkdirSync(path.join(dir, "keys")); |
| 61 | + fs.writeFileSync(keyPath(dir), "{not-json"); |
| 62 | + await expect(loadOrCreateCommitSigner(dir)).rejects.toThrow( |
| 63 | + `Invalid commit signing key at ${keyPath(dir)}`, |
| 64 | + ); |
| 65 | + }); |
| 66 | + |
| 67 | + test("invalid arktype shape throws Invalid commit signing key", async () => { |
| 68 | + const dir = tempDir(); |
| 69 | + fs.mkdirSync(path.join(dir, "keys")); |
| 70 | + fs.writeFileSync(keyPath(dir), JSON.stringify({ privateKey: 1 })); |
| 71 | + await expect(loadOrCreateCommitSigner(dir)).rejects.toThrow( |
| 72 | + `Invalid commit signing key at ${keyPath(dir)}`, |
| 73 | + ); |
| 74 | + }); |
| 75 | +}); |
0 commit comments