Documentation
¶
Overview ¶
Package fakegithub is an in-process stand-in for the GitHub REST/GraphQL surface internal/host talks to, backed by REAL local git repositories.
It exists because the exec'd `a2a` binary could not be driven against a host at all: cmd/a2a wired a hardcoded API root, so every host-facing verb — submit, every lifecycle verb, every contract sub-verb, feedback submit — was unreachable from a script, and the wiring closures that assemble them (cmd/a2a/wire.go's runSubmit/runContract/runLifecycle/ runFeedback, resolveCredential, resolveTargetSpaceRef) were executed by no test at all. Point A2A_GITHUB_API at Server.URL and the whole chain runs for real: real config load, real credential resolution, real mirror clone, real `git push`, real PR open — with nothing leaving the machine.
It is a TEST DOUBLE, not a GitHub emulator: it implements exactly the calls internal/host makes. Merges are REAL git merges into a work clone, not ref moves — the funnel branches from the mirror's own possibly-stale HEAD, so a PR is routinely not a descendant of the current base, and a fast-forward stand-in would rewind the space instead of merging it.
Index ¶
Constants ¶
const ForkLogin = "consumer"
ForkLogin is the account EnsureFork forks as.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type PR ¶
type PR struct {
Number int
Head string // "<branch>" for a same-repo PR, "<owner>:<branch>" from a fork
Base string
Title string
Body string
State string // "open" | "merged"
Merged bool
// MergeCommitSHA is recorded ONCE, at merge time, from the base branch's
// tip immediately after this PR's own merge lands. It must not be
// re-resolved later: the base moves on with every subsequent merge onto
// it, and re-resolving at read time would hand a caller a LATER PR's
// merge commit under this PR's number.
MergeCommitSHA string
// HeadSHA is captured ONCE, at merge time, before any prune — the same
// discipline MergeCommitSHA already uses, for the same reason: real
// GitHub keeps reporting a merged PR's head commit forever, even after
// its branch is deleted (host.CheckStatus/prHeadSHA and
// internal/livee2e's WaitForRequiredCheck both read a MERGED PR's
// head.sha this way), so a fake that only resolved the live ref would
// invent an absence DeleteBranchOnMerge's own pruning would otherwise
// cause. pullPayload uses this as a fallback when the live ref no
// longer resolves; an OPEN PR is never frozen this way — its head
// genuinely moves as the branch is pushed to.
HeadSHA string
// AutoMergeArmed tracks whether enablePullRequestAutoMerge actually
// armed THIS PR — set inside handleGraphQL, on both the arming path and
// the AutoMergeAlreadyClean refusal path (which must leave it false: a
// refusal is "not armed", and getting that backwards would make the
// refusal untestable through host.ListOpenPRs' own AutoMergeArmed
// field). It drives the `auto_merge` field in pullPayload — GitHub
// reports that field as an object when armed and null otherwise, and
// internal/host.ListOpenPRs' AutoMergeArmed is exactly `auto_merge !=
// nil`, so an omitted/always-null field here would make every open PR
// look like auto-merge was never armed, regardless of the truth.
AutoMergeArmed bool
}
PR is one pull request the fake has seen.
type Server ¶
type Server struct {
// URL is the API root to hand the binary via A2A_GITHUB_API.
URL string
// OriginDir is the bare repository PRs merge into.
OriginDir string
// AutoMerge, when true (the default), fast-forwards the base branch to
// the PR's head as soon as auto-merge is enabled — the space's own CI
// gate is not modelled here, so "auto-merge armed" means "merged".
// Set false to leave PRs open (the pending-merge path).
AutoMerge bool
// CheckState/CheckConclusion are what CheckStatus reports.
CheckState string
CheckConclusion string
// CheckName is the check run's NAME — the field internal/host selects
// on. It defaults to the post-P33 COMPOUND context a migrated space
// really emits (`<caller-job> / <reusable-job>`), so the default rig
// exercises the shape the fleet is moving to; set it to the flat
// `a2a-validate` to stand in for an un-migrated space.
CheckName string
// ReviewApprovals are the logins whose latest review is an approval.
ReviewApprovals []string
// AllowAutoMerge is what GET /repos/{owner}/{name} reports as the repo's
// `allow_auto_merge` setting (host.GitHubHost.AutoMergeAllowed). Defaults
// to true — a correctly-configured space — so no existing test's
// behaviour changes; set false to drive the WAVE M2 doctor FAIL path.
AllowAutoMerge bool
// AllowMergeCommit/AllowSquashMerge/AllowRebaseMerge are what GET
// /repos/{owner}/{name} reports for the same three merge-method flags
// host.GitHubHost.MergePR reads to pick an explicit merge_method
// (WAVE M4). All default true — a freshly created repository's own
// default — so MergePR's default preference (merge commit) is what a
// test sees unless it narrows one of these.
AllowMergeCommit bool
AllowSquashMerge bool
AllowRebaseMerge bool
// AutoMergeAlreadyClean, when true, makes the enable-auto-merge mutation
// answer GitHub's REAL "clean status" refusal (WAVE M4) instead of
// arming/merging: `errors: [{"message": "... is in clean status"}]`,
// the exact wording internal/host's autoMergeRefusal/IsAutoMergeAlreadyClean
// match against. Reaching a merge from here therefore requires the
// caller to land the PR itself via the merge endpoint below — this flag
// is what makes that scenario reproducible over a real HTTP path rather
// than only inside internal/host's own httptest-server unit tests.
// False (default) preserves every existing test's behaviour.
AutoMergeAlreadyClean bool
// MergeableState is what a PR's list/get payload reports as
// `mergeable_state`. Defaults to "clean" (GitHub's steady-state value for
// a PR with no conflicts); set it to drive a different value in a test
// that exercises a caller's handling of, e.g., "dirty" or "blocked".
MergeableState string
// DeleteBranchOnMerge models GET /repos/{owner}/{name}'s own
// `delete_branch_on_merge` setting AND the pruning it causes. Defaults to
// true — the same reasoning ProtectionBody's own doc gives for mirroring
// production rather than being maximally strict (spec 36 §T6-a):
// internal/livee2e's RepoSettingsBody turns this on for every space this
// project operates, so a rig that modelled it off would model a
// configuration no space we run actually uses, and every scenario after
// a first merge would fail for a reason production does not have (a
// stale head branch defeating ProbeContractPublicationHeads' plan-digest
// check being the exact case that surfaced this). Set false when a test
// wants the un-pruned shape.
//
// When true, mergeByNumber deletes a SAME-REPO PR's head branch from
// OriginDir the instant its own merge lands — never a cross-fork PR's
// head (it lives in the fork; real GitHub does not touch a fork's own
// branch) and never the refs/fork/<branch> copy mergeByNumber keeps for
// SHA resolution.
//
// This models WHETHER a space prunes, never HOW LONG pruning takes. Real
// GitHub prunes ASYNCHRONOUSLY — a branch listing can differ between two
// reads that straddle the deletion, a timing fact spec 09 §3 names and
// that broke a live row once. This fake's deletion is synchronous (gone
// the instant the merge call returns); a scenario asserting on that async
// window is a provider fact and belongs in the live tier, not here.
DeleteBranchOnMerge bool
// contains filtered or unexported fields
}
Server is the fake host. Construct with New, point A2A_GITHUB_API at URL, and inspect the recorded calls afterwards.
func New ¶
New starts a fake host in front of originDir (a bare repository, e.g. spacefixture's own origin) and stops it when the test ends.
func (*Server) DenyPushes ¶
DenyPushes makes the origin refuse every push the way GitHub refuses a non-collaborator: a pre-receive hook that fails with GitHub's own wording, relayed by git as `remote: Permission to … denied to <login>`. This drives the REAL stderr classifier in internal/host, not a hand-written string.