import assert from 'node:assert/strict' import { it } from 'node:test' import { GitHubForge, compareUrl, parseGitHubPullUrl, parseGitHubRepo } from '../dist/index.js' const REPO = { owner: 'acme', repo: 'widget' } /** Build a FetchLike from an ordered list of {match, status, body} handlers. Each request consumes * the first handler whose `match(url, init)` is true. */ function fetcher(handlers) { const calls = [] const fetch = async (input, init) => { const url = String(input) calls.push({ url, method: init?.method ?? 'GET', body: init?.body }) const handler = handlers.find((h) => h.match(url, init)) if (!handler) throw new Error(`no handler for ${init?.method ?? 'GET'} ${url}`) const body = handler.body === undefined ? '' : JSON.stringify(handler.body) return new Response(body, { status: handler.status }) } return { fetch, calls } } // --- parseGitHubRepo -------------------------------------------------------- it('parseGitHubRepo handles https, ssh, scp, and .git suffixes', () => { assert.deepEqual(parseGitHubRepo('https://github.com/acme/widget.git'), REPO) assert.deepEqual(parseGitHubRepo('https://github.com/acme/widget'), REPO) assert.deepEqual(parseGitHubRepo('ssh://git@github.com/acme/widget.git'), REPO) assert.deepEqual(parseGitHubRepo('git@github.com:acme/widget.git'), REPO) }) it('parseGitHubRepo rejects non-github and pathless URLs', () => { assert.throws(() => parseGitHubRepo('https://gitlab.com/acme/widget.git'), /not a GitHub/) assert.throws(() => parseGitHubRepo('https://github.com/acme'), /not a GitHub/) assert.throws(() => parseGitHubRepo('not a url at all'), /not a GitHub/) }) it('compareUrl is a pure base..commit URL', () => { assert.equal(compareUrl(REPO, 'main', 'deadbeef'), 'https://github.com/acme/widget/compare/main...deadbeef') }) // --- getPullRequestState URL validation ------------------------------------- it('parseGitHubPullUrl accepts only canonical github.com/owner/repo/pull/', () => { assert.deepEqual(parseGitHubPullUrl('https://github.com/acme/widget/pull/42'), { repo: REPO, number: 42 }) assert.throws(() => parseGitHubPullUrl('https://gitlab.com/acme/widget/pull/42'), /canonical GitHub/) assert.throws(() => parseGitHubPullUrl('https://github.com/acme/widget/pull/abc'), /canonical/) assert.throws(() => parseGitHubPullUrl('http://github.com/acme/widget/pull/42'), /canonical GitHub/) assert.throws(() => parseGitHubPullUrl('https://github.com/acme/widget/pulls/42'), /canonical/) // Strictness (finding 11): userinfo, non-default port, query, and fragment are all rejected. assert.throws(() => parseGitHubPullUrl('https://user:pass@github.com/acme/widget/pull/42'), /canonical GitHub/) assert.throws(() => parseGitHubPullUrl('https://token@github.com/acme/widget/pull/42'), /canonical GitHub/) assert.throws(() => parseGitHubPullUrl('https://github.com:8443/acme/widget/pull/42'), /canonical GitHub/) assert.throws(() => parseGitHubPullUrl('https://github.com/acme/widget/pull/42?x=1'), /canonical GitHub/) assert.throws(() => parseGitHubPullUrl('https://github.com/acme/widget/pull/42#frag'), /canonical GitHub/) }) it('getPullRequestState parses the URL and builds the API call from the parsed parts', async () => { const { fetch, calls } = fetcher([ { match: (u) => u.includes('/repos/acme/widget/pulls/42'), status: 200, body: { number: 42, html_url: 'x', state: 'closed', merged: true, merged_at: '2026-03-01T00:00:00Z', head: { ref: 'b', sha: 'h' }, base: { ref: 'main' } } }, ]) const forge = new GitHubForge({ fetch, token: 't' }) const state = await forge.getPullRequestState('https://github.com/acme/widget/pull/42') assert.deepEqual(state, { state: 'merged', headRef: 'b', headRepoFullName: '', baseRef: 'main', mergedAt: '2026-03-01T00:00:00Z' }) // The token-bearing call targets the parsed api path, never the raw record URL. assert.ok(calls[0].url.includes('api.github.com/repos/acme/widget/pulls/42')) }) it('getPullRequestState rejects a non-github URL before making any request', async () => { const { fetch, calls } = fetcher([{ match: () => true, status: 200, body: {} }]) const forge = new GitHubForge({ fetch, token: 't' }) await assert.rejects(forge.getPullRequestState('https://evil.example/acme/widget/pull/1'), /canonical GitHub/) assert.equal(calls.length, 0) })