import { describe, expect, it } from 'vitest'; import { RepoCommit, verifyCommit } from '../src/commit.js'; import { fromHex, referenceCommit, vectors } from './helpers/vectors.js'; const { records } = vectors.commit; const secret = fromHex(vectors.commit.secretHex); const mac = fromHex(vectors.commit.macHex); const params = { space: vectors.commit.space, repo: vectors.commit.repo, sharedSecret: secret, mac, }; describe('RepoCommit', () => { it('reproduces the reference commit from the same records', async () => { const commit = await RepoCommit.fromRecords(records).toCommit({ space: vectors.commit.space, repo: vectors.commit.repo, rev: vectors.commit.rev, }); expect(commit).toEqual(referenceCommit()); }); it('matches() compares against a commit', async () => { expect( await RepoCommit.fromRecords(records).matches(referenceCommit()), ).toBe(true); expect(await new RepoCommit().matches(referenceCommit())).toBe(false); }); it('fromState round-trips', async () => { const a = RepoCommit.fromRecords(records); const b = RepoCommit.fromState(a.state()); expect(await b.matches(referenceCommit())).toBe(true); }); it('fromState treats null as an empty repo', () => { expect(RepoCommit.fromState(null).setHash.isEmpty()).toBe(true); }); it('applyOp handles create, update, and delete', async () => { const [a, b] = records; const repo = new RepoCommit(); repo.applyOp({ ...a, prev: null }); repo.applyOp({ ...b, prev: null }); expect(await repo.matches(referenceCommit())).toBe(true); repo.applyOp({ ...a, cid: b.cid, prev: a.cid }); expect(await repo.matches(referenceCommit())).toBe(false); repo.applyOps([ { ...a, cid: a.cid, prev: b.cid }, { ...b, cid: null, prev: b.cid }, ]); expect(await repo.matches(referenceCommit())).toBe(false); repo.applyOp({ ...b, prev: null }); expect(await repo.matches(referenceCommit())).toBe(true); }); }); describe('verifyCommit', () => { it('accepts the reference commit under its MAC', async () => { expect(await verifyCommit(referenceCommit(), params)).toBe(true); }); it('rejects a wrong version', async () => { expect(await verifyCommit({ ...referenceCommit(), ver: 2 }, params)).toBe( false, ); }); it('rejects a commit for another repo or space', async () => { expect( await verifyCommit(referenceCommit(), { ...params, repo: 'did:plc:other', }), ).toBe(false); expect( await verifyCommit(referenceCommit(), { ...params, space: 'at://did:plc:aaaa/space/com.example.type/key2', }), ).toBe(false); }); it('rejects tampered data, rev or MAC', async () => { const data = referenceCommit().data.slice(); data[0] ^= 1; expect(await verifyCommit({ ...referenceCommit(), data }, params)).toBe( false, ); expect( await verifyCommit( { ...referenceCommit(), rev: '3lkabcdefgh2y' }, params, ), ).toBe(false); const bad = mac.slice(); bad[31] ^= 1; expect(await verifyCommit(referenceCommit(), { ...params, mac: bad })).toBe( false, ); }); it('rejects the right MAC under another secret', async () => { expect( await verifyCommit(referenceCommit(), { ...params, sharedSecret: new Uint8Array(32), }), ).toBe(false); }); });