test(forge): cross-adapter ForgePort conformance suite (MF-6) #312
No reviewers
Labels
No labels
area:agents
area:dashboard
area:database
area:design
area:design-review
area:flows
area:infra
area:meta
area:security
area:sessions
area:webhook
area:workdir
security
type:bug
type:chore
type:meta
type:user-story
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
charles/claude-hooks!312
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "feat/297-mf6-forgeport-conformance"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
apps/server/src/infrastructure/forge/conformance.test.tsruns 37 shared scenarios × 3 adapters (ForgejoAdapter,GitHubAdapter,GitLabAdapter) + a dedicated divergence block — 112 tests, 379 expect() calls, all passing.apps/server/test/fixtures/forge/{forgejo,github,gitlab}/are the single source of truth for raw forge payloads; the shared test body asserts only domain shapes (ForgeIssue,ForgePullRequest,ForgeReview,ForgeIssueRef,ForgeWorkflowRun,ForgeDirEntry,ForgeComment).fetchis stubbed via a per-test URL router (one function per adapter) — no live forge traffic, no env assumptions.Coverage
Per the MF-6 acceptance criteria:
html_url→htmlUrl,{ name }label objects →string[], milestone object → title string, assignee user objects → username array,head.ref/sha→headRef/headSha, etc.).getIssue,getPullRequest,readFilereturnnullon 404 (no throws).updateAssignees,addLabels,removeLabel,patchIssue,createComment,requestReview,removeReviewRequest,addBlocker,writeFilereturntrue/false, never throw.addLabels([])returnsfalseon all three.#prefix across the port./dependencies+/blocks, GitLab Premium/issues/:iid/links, GitHub body-text parse ofDepends on #N/Blocks #N. GitLab Free-tier fallback exercised in divergence block."success"/"failure"on all three adapters.approved/changes_requested/commentflatten identically from Forgejo native reviews, GitHubreviews, GitLab approvals + thumbsdown emoji + notes.Divergences flagged (not fixed — guardrails honoured)
Surfaced with dedicated tests in a
Cross-adapter divergence — documented, enforcedblock so the drift is pinned rather than silently tolerated:getAggregateStatusstring union drift — Forgejo + GitHub return"pending"when CI is queued; GitLab routes throughtoPipelineStatusand returns"queued"instead. Consumers currently switch on a broader set. Suggested follow-up: normalise the GitLab adapter to emit"pending"for parity. I did not editgitlab-adapter.tsper the guardrails.getBlockedasymmetry on Free-tier GitLab — documented in the adapter already; the suite pins it with its own test (returns[]on Free, native on Premium). No action needed; behaviour matches the adapter's inline comment.Out of scope
just forge-livein a follow-up).github-adapter.test.tsalready covers the rate-limit retry path — extending that into the conformance suite would require adapter-specific plumbing that's out of scope for a cross-forge shape check).getAggregateStatusdrift — needs a separate issue.Test plan
bun test apps/server/src/infrastructure/forge/conformance.test.ts— 112 pass / 0 failbun x turbo run typecheck— clean on all four packagesbun x biome check apps/server/src/infrastructure/forge/conformance.test.ts apps/server/test/fixtures/forge/— cleanbun testunderapps/server/— pre-existing sweeper + foreman failures unchanged (unrelated; reproduced on a cleanmaintree)Closes #297
Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com
Adds `apps/server/src/infrastructure/forge/conformance.test.ts` — a shared battery of scenarios that every `ForgePort` implementation (ForgejoAdapter, GitHubAdapter, GitLabAdapter) must pass. Each scenario pulls from per-forge JSON fixtures under `apps/server/test/fixtures/forge/{forgejo,github,gitlab}/` and asserts the adapter returns the domain shape (`ForgeIssue`, `ForgePullRequest`, `ForgeReview`, `ForgeIssueRef`, `ForgeWorkflowRun`, …) rather than the forge-native payload. Coverage: - Shape mapping for getIssue / getPullRequest / listReviews / listComments / listWorkflowRuns / listRepoLabels / listAccessibleRepos / readFile / listDir. - Null contract on 404 (getIssue, getPullRequest, readFile). - Boolean contract on mutations (addLabels, removeLabel, updateAssignees, patchIssue, createComment, requestReview, removeReviewRequest, addBlocker, writeFile) — true on 2xx, false on 5xx. - Label name↔id uniformity (all three adapters accept names; Forgejo resolves internally). - Dependency parsing — Forgejo native, GitLab Premium issue_links, GitHub body-text "Depends on #N" / "Blocks #N". - CI aggregation — Forgejo/GitHub return "success|failure|pending", GitLab returns "queued" for pending (documented drift, asserted). - Review state mapping — APPROVED / CHANGES_REQUESTED / COMMENT flattened uniformly from each forge's native review shape (GitHub reviews, Forgejo native, GitLab approvals + thumbsdown emoji + notes). All HTTP calls go through a `fetch` stub; no live forge traffic. 112 tests total across the three adapters + a small divergence-scenarios block. Closes #297