fix: address a GitHub repository by name, and an id on GitHub's id route - #109
Conversation
A handler that returns the same scripted body for every path lets a wrong route run the whole success path and look correct, so a route assertion made afterwards on a recorded URI is not load-bearing. RespondToPath makes the route a condition of getting the scripted response at all and answers anything else the way the host would. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TU2gC1LwTwyMin2XxYxg2m
…ute [patch]
ToRepositoryIdentifier preferred HostRepositoryId for every provider, which is
what Azure DevOps's documented {repositoryId} schema asks for but is wrong for
GitHub: its {repo} slot in /repos/{owner}/{repo} takes a repository name, and
the id-addressed form is the separate /repositories/{id} route. GitHubProvider
fills HostRepositoryId with GitHub's numeric id, so GetPullRequestsAsync
(GitRepository) and CreatePullRequest(GitRepository) issued
GET /repos/{owner}/{id}/pulls and answered 404 for a repository just
enumerated from the same provider - the overload IGitHostingProvider documents
as the one to prefer.
The preference is now per-provider, and the chosen form travels with the value
on GitRepositoryAddress, so a repository carrying only an id is addressed on
GitHub's id route through Octokit's long repositoryId overloads rather than by
dropping an id into the name slot. Azure DevOps has one slot for both forms and
keeps preferring the id.
The test that should have caught this asserted the id-addressed URL against a
handler that returned [] for every path; it now asserts the name-addressed one
through a path-scoped response, alongside the id-route and create-route cases.
Fixes #101
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TU2gC1LwTwyMin2XxYxg2m
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TU2gC1LwTwyMin2XxYxg2m
|
|
On SonarCloud's 10 New issues above — the gate passed, and all ten are
They surface now only because these lines are new; the analyzers are not enabled as build warnings, and both Debug and Release build with zero warnings. Adopting the suggestions on these few lines alone would leave two idioms in files that consistently use one, so the new tests stay matched to their surroundings. Happy to sweep any of the three repo-wide as its own change if that is wanted — that seems the right shape for it, rather than a partial conversion riding this fix. Generated by Claude Code |



Fixes #101
The bug
GitProvider.ToRepositoryIdentifierpreferredHostRepositoryIdoverNamefor every provider. That is what Azure DevOps's documented{repositoryId}schema asks for, but it is wrong for GitHub: the{repo}slot in/repos/{owner}/{repo}takes a repository name, and the id-addressed form is the separate/repositories/{id}route.GitHubProvider.ToGitRepositoryfillsHostRepositoryIdwith GitHub's numeric id, soissued
GET /repos/contoso/90000001/pulls→404→GitHostingNotFoundException, for a repository just enumerated from the same provider.CreatePullRequest(GitRepository)had the identical problem. Both are the overloadIGitHostingProviderdocuments as "the one to prefer when the caller has aGitRepositoryfromGetRepositoriesAsync", so the recommended call sequence was broken end to end on GitHub.The fix
GitProvider.PrefersHostRepositoryIdisprivate protected abstract. Azure DevOps answerstrue(its one{repositoryId}slot is documented as taking the id); GitHub answersfalse.GitRepositorycarries both. Which form was chosen now travels with the value on the internalGitRepositoryAddressrecord struct that both internal cores take, so a repository carrying only an id is still addressed correctly on GitHub — through Octokit'slong repositoryIdoverloads, which build the id-addressed route — rather than by dropping an id into the name slot. A bare string could not carry that distinction, and a provider cannot recover it from the value's shape, since a repository may legitimately be named a number.GitHostRepositoryIdGitHub cannot be addressed by (the type is unvalidated, because Azure DevOps's is a uuid and GitHub's a whole number) raisesArgumentExceptionbefore anything is sent, rather than aGitHostingExceptionwith no response to describe.No public API changed —
GitRepositoryAddressand the two core methods areinternal.Why the tests did not catch it
GitHubProviderTestsasserted the URL/repos/contoso/90000001/pullsagainst a fake handler that returned[]regardless of path, so the whole method ran its success path around a request GitHub would have refused.FakeHttpMessageHandler.RespondToPathmakes the route a condition of getting the scripted response, answering an unexpected path with404the way the host does. The listing test now asserts the name-addressed URL through it, joined by tests for the id-route and create-route cases.Verification
dotnet buildclean in Debug and Release, zero warnings, no suppressions added.dotnet test: 583 passed, 0 failed.CLAUDE.md), re-verifying the tree after each run:PrefersHostRepositoryId => trueon GitHub →AddressesARepositoryByNameWhenListingPullRequestsAsyncandAddressesARepositoryByNameWhenCreatingAPullRequestAsyncfail.false→AddressesARepositoryByItsHostIdOnTheIdRouteWhenNoNameIsKnownAsyncandReportsAHostRepositoryIdGitHubCannotBeAddressedByAsyncfail.🤖 Generated with Claude Code
https://claude.ai/code/session_01TU2gC1LwTwyMin2XxYxg2m
Generated by Claude Code