/** * Showing a commit author as the identity the hosting account claims: an * atproto profile where the claim names a DID, and a label with a face where * it names something that holds no account. */ import { afterEach, beforeEach, describe, expect, it } from 'vitest'; import { describeAuthor, loadAuthors } from '../src/lib/authors.js'; import { account } from '../src/lib/git.js'; const OWNER = 'did:plc:owner'; const FRIEND = 'did:plc:friend'; const AVATAR_CID = 'bafkreiavatar'; /** * @param {Array>} idents * @param {Array>} profiles */ function serve(idents, profiles) { globalThis.fetch = async (/** @type {string|URL|Request} */ input) => { const url = String(input); if (url.includes('com.atproto.repo.getRecord')) { return Response.json({ uri: `at://${OWNER}/dev.pdsjs.git.identity/self`, value: { $type: 'dev.pdsjs.git.identity', idents, createdAt: '2026-01-01T00:00:00.000Z', }, }); } if (url.includes('app.bsky.actor.getProfiles')) { const wanted = new URL(url).searchParams.getAll('actors'); return Response.json({ profiles: profiles.filter((p) => wanted.includes(String(p.did))), }); } return Response.json({ error: 'NotFound' }, { status: 404 }); }; } describe('describing a commit author', () => { const original = globalThis.fetch; const originalLocation = globalThis.location; beforeEach(() => { account.did = OWNER; account.pds = 'https://pds.example'; account.handle = 'owner.example'; // The git service decision probes this page's own origin first; the // stubs below refuse that probe, so the account's PDS stays the service. globalThis.location = new URL('https://site.example/'); }); afterEach(() => { globalThis.fetch = original; globalThis.location = originalLocation; account.did = ''; account.pds = ''; account.handle = ''; }); it('takes the claims and faces the server resolves, where it serves them', async () => { /** @type {string[]} */ const asked = []; globalThis.fetch = async (/** @type {string|URL|Request} */ input) => { const url = String(input); asked.push(url); if (url.includes('community.lexicon.service.describe')) { return Response.json({ methods: [{ value: 'dev.pdsjs.git.getAuthors' }], }); } if (url.includes('dev.pdsjs.git.getAuthors')) { return Response.json({ owner: { did: OWNER, handle: 'owner.example', displayName: 'The Account', avatar: 'https://cdn/owner.jpg', }, claims: [ { name: '', email: 'friend@example.com', label: '', did: FRIEND, avatar: '', profile: { did: FRIEND, handle: 'friend.example', displayName: 'A Friend', avatar: 'https://cdn/friend.jpg', }, }, { name: '', email: 'noreply@anthropic.com', label: 'Claude', did: '', avatar: 'https://pds.example/xrpc/com.atproto.sync.getBlob?cid=x', profile: null, }, ], }); } return Response.json({ error: 'NotFound' }, { status: 404 }); }; await loadAuthors(); const friend = describeAuthor('Friend '); expect(friend.name).toBe('A Friend'); expect(friend.avatar).toBe('https://cdn/friend.jpg'); expect(friend.atproto).toBe(true); const tool = describeAuthor('Claude '); expect(tool.name).toBe('Claude'); expect(tool.avatar).toContain('getBlob?cid=x'); expect(asked.some((url) => url.includes('getProfiles'))).toBe(false); expect(asked.some((url) => url.includes('repo.getRecord'))).toBe(false); }); it('names a labelled identity itself, not the account holding the claim', async () => { // The account has a profile of its own, and the claim carries no DID. // Reading the account's profile here would show every commit the tool // co-wrote under the account's own name and face. serve( [ { email: 'noreply@anthropic.com', label: 'Claude', avatar: { $type: 'blob', ref: { $link: AVATAR_CID } }, }, ], [{ did: OWNER, handle: 'owner.example', displayName: 'The Account' }], ); await loadAuthors(); const author = describeAuthor('Claude '); expect(author.name).toBe('Claude'); expect(author.atproto).toBe(false); expect(author.handle).toBe(''); expect(author.avatar).toContain(AVATAR_CID); expect(author.avatar).toContain(encodeURIComponent(OWNER)); }); it('shows the profile of a claim that names a DID', async () => { serve( [{ email: 'friend@example.com', did: FRIEND }], [ { did: OWNER, handle: 'owner.example', displayName: 'The Account' }, { did: FRIEND, handle: 'friend.example', displayName: 'A Friend', avatar: 'https://cdn.example/friend.jpg', }, ], ); await loadAuthors(); const author = describeAuthor('Friend '); expect(author.name).toBe('A Friend'); expect(author.handle).toBe('friend.example'); expect(author.atproto).toBe(true); expect(author.did).toBe(FRIEND); }); it('carries the DID of a claimed account that publishes no profile', async () => { // The DID is what a face with no picture draws its pattern from, so // dropping it here gave one person an identicon beside their words and a // bare letter on their commits. serve( [{ email: 'friend@example.com', did: FRIEND }], [{ did: OWNER, handle: 'owner.example', displayName: 'The Account' }], ); await loadAuthors(); const author = describeAuthor('Friend '); expect(author.did).toBe(FRIEND); expect(author.atproto).toBe(true); // Nothing said what to call them, so the commit's own name stands. expect(author.name).toBe('Friend'); expect(author.avatar).toBe(''); }); it('reads a claim with neither DID nor label as the account itself', async () => { serve( [{ email: 'owner@example.com' }], [{ did: OWNER, handle: 'owner.example', displayName: 'The Account' }], ); await loadAuthors(); const author = describeAuthor('Owner '); expect(author.name).toBe('The Account'); expect(author.atproto).toBe(true); }); it('leaves an unclaimed author as the name the commit carries', async () => { serve([{ email: 'owner@example.com' }], []); await loadAuthors(); const author = describeAuthor('Stranger '); expect(author.name).toBe('Stranger'); expect(author.atproto).toBe(false); expect(author.avatar).toBe(''); }); });