Files
claudemesh/apps/cli-v2/tests/unit/crypto-roundtrip.test.ts
Alejandro Gutiérrez d37516213a
Some checks failed
CI / Lint (push) Has been cancelled
CI / Typecheck (push) Has been cancelled
CI / Broker tests (Postgres) (push) Has been cancelled
CI / Docker build (linux/amd64) (push) Has been cancelled
chore(cli-v2): un-ignore CLI source tree for binary release workflow
The CLI source (242 files, ~14k lines) was gitignored during the
earlier cli→cli-v2 reorg so only the published npm package carried it.
That blocks the GitHub Actions release workflow (release-cli.yml),
which clones the repo fresh on each runner and needs the source to
compile binaries via `bun build --compile`.

Moves the gitignore from root-level to `apps/cli-v2/.gitignore` with
only the usual build artefacts excluded (node_modules, dist, .turbo,
.cache). Source is now in git at apps/cli-v2/src/.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 02:45:44 +01:00

85 lines
3.1 KiB
TypeScript

import { describe, it, expect } from "vitest";
import {
generateKeypair, sign, verify, encrypt, decrypt, boxSeal, boxOpen,
encryptDirect, decryptDirect, randomBytes, randomHex,
} from "~/services/crypto/facade.js";
describe("crypto", () => {
it("generates valid Ed25519 keypair", async () => {
const kp = await generateKeypair();
expect(kp.publicKey).toMatch(/^[0-9a-f]{64}$/);
expect(kp.secretKey).toMatch(/^[0-9a-f]{128}$/);
});
it("sign + verify round-trip", async () => {
const kp = await generateKeypair();
const msg = "hello world";
const sig = await sign(msg, kp.secretKey);
expect(await verify(msg, sig, kp.publicKey)).toBe(true);
expect(await verify("tampered", sig, kp.publicKey)).toBe(false);
});
it("file encrypt + decrypt round-trip", async () => {
const data = new TextEncoder().encode("secret document");
const encrypted = await encrypt(data);
expect(encrypted.ciphertext.length).toBeGreaterThan(0);
expect(encrypted.nonce).toBeTruthy();
const decrypted = await decrypt(encrypted.ciphertext, encrypted.nonce, encrypted.key);
expect(decrypted).not.toBeNull();
expect(new TextDecoder().decode(decrypted!)).toBe("secret document");
});
it("file encrypt + decrypt fails with wrong key", async () => {
const data = new TextEncoder().encode("secret");
const encrypted = await encrypt(data);
const wrongKey = await randomBytes(32);
const result = await decrypt(encrypted.ciphertext, encrypted.nonce, wrongKey);
expect(result).toBeNull();
});
it("boxSeal + boxOpen round-trip", async () => {
const kp = await generateKeypair();
const secret = await randomBytes(32);
const sealed = await boxSeal(secret, kp.publicKey);
expect(sealed).toBeTruthy();
const opened = await boxOpen(sealed, kp.publicKey, kp.secretKey);
expect(opened).not.toBeNull();
expect(Buffer.from(opened!).toString("hex")).toBe(Buffer.from(secret).toString("hex"));
});
it("crypto_box direct message round-trip", async () => {
const alice = await generateKeypair();
const bob = await generateKeypair();
const msg = "hello bob";
const envelope = await encryptDirect(msg, bob.publicKey, alice.secretKey);
expect(envelope.nonce).toBeTruthy();
expect(envelope.ciphertext).toBeTruthy();
const decrypted = await decryptDirect(envelope, alice.publicKey, bob.secretKey);
expect(decrypted).toBe("hello bob");
});
it("crypto_box decrypt fails with wrong keys", async () => {
const alice = await generateKeypair();
const bob = await generateKeypair();
const eve = await generateKeypair();
const envelope = await encryptDirect("secret", bob.publicKey, alice.secretKey);
const result = await decryptDirect(envelope, alice.publicKey, eve.secretKey);
expect(result).toBeNull();
});
it("randomBytes returns correct length", async () => {
const bytes = await randomBytes(16);
expect(bytes.length).toBe(16);
});
it("randomHex returns correct length", async () => {
const hex = await randomHex(8);
expect(hex).toMatch(/^[0-9a-f]{16}$/);
});
});