import assert from 'node:assert/strict' import { it } from 'node:test' import { ForgeRegistry, GitHubForge, TangledForge, urlOnlyForgeAdapters } from '../dist/index.js' const GITHUB = 'https://github.com/acme/widget.git' const TANGLED = 'https://tangled.org/@alice/widget' const tangled = (options = {}) => new TangledForge({ reader: { async listForeignRecords() { return { records: [] } }, async getForeignRecord() { throw new Error('not used') }, }, resolveHandle: async (handle) => `did:plc:${handle}`, ...options, }) it('routes a project to the adapter that speaks for its remote, and to nothing else', () => { const github = new GitHubForge({ fetch, token: 't' }) const registry = new ForgeRegistry([github, tangled()]) assert.equal(registry.for(GITHUB), github) assert.equal(registry.for('git@github.com:acme/widget.git'), github) assert.equal(registry.for(TANGLED)?.kind, 'tangled') assert.equal(registry.for('git@tangled.org:alice/widget')?.kind, 'tangled') // A host neither adapter claims, and a value that is not a URL at all: no opinion, never a throw. assert.equal(registry.for('https://gitlab.com/acme/widget.git'), undefined) assert.equal(registry.for('not a url'), undefined) }) it('two forges coexist in one registry: neither claims the other’s remote', () => { const registry = new ForgeRegistry([new GitHubForge({ fetch, token: 't' }), tangled()]) assert.equal(registry.for(GITHUB)?.kind, 'github') assert.equal(registry.for(TANGLED)?.kind, 'tangled') assert.equal(registry.ofKind('tangled')?.kind, 'tangled') assert.equal(registry.size, 2) }) it('a self-hosted tangled host is routed once it is configured, and not before', () => { const configured = tangled({ hosts: ['git.example.org'] }) assert.equal(new ForgeRegistry([configured]).for('https://git.example.org/@bob/thing')?.kind, 'tangled') // The default host list is not additive: an adapter configured for a self-hosted knot only. assert.equal(new ForgeRegistry([configured]).for(TANGLED), undefined) }) it('the GitHub adapter answers the two URL questions the dispatcher and the socket ask', async () => { const github = new GitHubForge({ fetch, token: 't' }) assert.equal(await github.projectIdentity(GITHUB), 'acme/widget') assert.equal(await github.pullBelongsToProject('https://github.com/acme/widget/pull/7', GITHUB), true) assert.equal(await github.pullBelongsToProject('https://github.com/other/widget/pull/7', GITHUB), false) await assert.rejects(() => github.pullBelongsToProject('https://github.com/acme/widget/pulls/7', GITHUB), /canonical/) assert.equal(github.ownsPullUrl('https://github.com/acme/widget/pull/7'), true) assert.equal(github.ownsPullUrl('at://did:plc:a/sh.tangled.repo.pull/3k'), false) assert.equal(github.compareUrl(GITHUB, 'main', 'deadbeef'), 'https://github.com/acme/widget/compare/main...deadbeef') }) it('a GitHub adapter with no token still reasons about URLs, but refuses to observe', async () => { const [urlOnly] = urlOnlyForgeAdapters() assert.equal(urlOnly.canObserve, false) assert.equal(await urlOnly.pullBelongsToProject('https://github.com/acme/widget/pull/7', GITHUB), true) // Never an unauthenticated call: on a private repository that would 404 and read as "no such // pull request", which the reuse guard would take as an answer. await assert.rejects(() => urlOnly.getPullRequestState('https://github.com/acme/widget/pull/7'), /unavailable/) assert.match(urlOnly.implementationBlockedReason() ?? '', /GitHub authentication is unavailable/) }) it('a tangled adapter reports why an implementation turn cannot run yet', () => { // No loaded agent identity to author the pull record. assert.match(tangled().implementationBlockedReason() ?? '', /no agent identity/) // An identity, but no push key configured. const actor = () => ({ did: 'did:plc:agent', createForeign: async () => ({ uri: '', cid: '' }), putForeign: async () => ({ uri: '', cid: '' }), uploadBlob: async () => ({}) }) assert.match(tangled({ actor }).implementationBlockedReason() ?? '', /push key/) assert.equal(tangled({ actor, push: async () => undefined }).implementationBlockedReason(), undefined) })