Help us shape the future of Go! Take the 2026 Go Developer Survey

contracttest

package
v0.99.9 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 30, 2026 License: MIT Imports: 25 Imported by: 0

Documentation

Overview

Package contracttest provides a reusable test harness for verifying that platform backends satisfy the behavioral contracts defined by the provider.Provider interface.

Each backend's test suite imports this package and calls Run with a backend-specific Harness. This ensures that list/pagination, error classification, retry behavior, webhook validation, and context cancellation are consistent across every platform the SDK supports.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func ParseSigned added in v0.77.0

func ParseSigned(t testing.TB, p provider.Provider, req *http.Request, secret string) *provider.NormalizedEvent

ParseSigned signs req with the platform's registered webhook validator and secret, then parses the event through p.ParseWebhookEvent. Backend unit tests use it to exercise payload mapping without hand-rolling per-platform signature code — and since v0.77.0 an empty secret is rejected by every validator, so unsigned fixture requests no longer pass validation.

func Run

func Run(t *testing.T, h Harness)

Run executes the full contract suite against h. Each subtest is independent.

func RunBranchProtectionsSuite

func RunBranchProtectionsSuite(t *testing.T, h BranchProtectionsHarness)

RunBranchProtectionsSuite tests branch-protection CRUD operations.

func RunCollaboratorsSuite

func RunCollaboratorsSuite(t *testing.T, h CollaboratorsHarness)

RunCollaboratorsSuite tests collaborator list operations.

func RunDeployKeysSuite

func RunDeployKeysSuite(t *testing.T, h DeployKeysHarness)

RunDeployKeysSuite tests deploy-key CRUD operations.

func RunIssuesSuite

func RunIssuesSuite(t *testing.T, h IssuesHarness)

RunIssuesSuite executes the issue-management contract suite. The mock routes by method and path shape so platform-specific paths don't matter: GET ending /issues/{alphanumeric} → GetResponse (issue numbers are not always numeric — Gitee's are alphanumeric); GET containing comments|notes → CommentsResponse; GET containing labels (not issues) → LabelsResponse; other GET → ListResponse; POST → 201 + MutateResponse (a POST to a labels path — adding labels to an issue — returns LabelsResponse, since GitHub-shaped APIs answer with the label array); PATCH/PUT → MutateResponse; DELETE → 204. Requests are recorded for wire assertions.

func RunLabelsSuite

func RunLabelsSuite(t *testing.T, h LabelsHarness)

RunLabelsSuite executes the label-management contract suite. The mock server routes by HTTP method so platform-specific paths don't matter: GET returns ListResponse, POST returns 201 + MutateResponse, PATCH/PUT return 200 + MutateResponse, DELETE returns 204. The mock also records every request (method, path+query, decoded body) so the subtests can assert the wire behavior behind each operation: pagination parameters on list, the verb and body on create/update/delete, and that update does not clobber fields the caller left nil.

func RunMilestonesSuite

func RunMilestonesSuite(t *testing.T, h MilestonesHarness)

RunMilestonesSuite executes the milestone-management contract suite. The mock server is dedicated to milestone paths (route by "milestones" in the path so platform-specific prefixes don't matter) and routes by HTTP method: GET → ListResponse (array); POST → 201 + MutateResponse; PATCH/PUT → MutateResponse (GitLab updates with PUT, the rest with PATCH); DELETE → 204. Requests are recorded for wire assertions.

func RunNotesSuite added in v0.99.8

func RunNotesSuite(t *testing.T, h NotesHarness)

RunNotesSuite tests CR-comment update and full listing. The mock routes by method only: GET serves the notes array (page-aware so AllPages walkers stop), PUT returns the mutated note — both platforms' note-edit verbs are PUT.

func RunNotificationsSuite

func RunNotificationsSuite(t *testing.T, h NotificationsHarness)

RunNotificationsSuite executes the notification contract suite.

func RunReactionsSuite

func RunReactionsSuite(t *testing.T, h ReactionsHarness)

RunReactionsSuite executes the reaction contract suite.

func RunReleaseSuite

func RunReleaseSuite(t *testing.T, h ReleaseHarness)

RunReleaseSuite executes the release-management contract suite. The mock routes by method and path shape so platform-specific paths don't matter: GET ending at a releases collection (…/releases) returns a one-element array wrapping ByTagResponse (list-shaped); every other GET returns the ByTagResponse object — this covers both the native by-tag fetches (…/releases/tags/{tag} on GitHub/Gitea/Forgejo/Gitee/GitCode, …/releases/{tag} on GitLab/TencentCode) and the tag→id resolution fetches ID-addressed platforms make before update/delete, which go through the same single-release endpoints; PATCH/PUT return UpdateResponse; DELETE returns 200 + ByTagResponse — GitLab's delete responds with the deleted release object and its SDK decodes it, while the body-agnostic platforms ignore it (the shared transport skips decoding for nil results). Requests are recorded for wire assertions.

func RunRepoStatsSuite

func RunRepoStatsSuite(t *testing.T, h RepoStatsHarness)

RunRepoStatsSuite tests repo-stats read operations. Each method is tested independently — backends that don't support a method will return an error, which the suite accepts as long as the type assertion works.

func RunReviewsSuite

func RunReviewsSuite(t *testing.T, h ReviewsHarness)

RunReviewsSuite executes the review-management contract suite. The mock routes by method and path shape so platform-specific paths don't matter: GET ending /reviews/{digits} or /notes/{digits} → GetResponse; other GET → ListResponse; POST → 201 + MutateResponse; PUT/PATCH → MutateResponse (GitHub's dismiss is a PUT to a .../dismissals sub-resource); DELETE → 204. Requests are recorded for wire assertions.

func RunSearchSuite

func RunSearchSuite(t *testing.T, h SearchHarness)

RunSearchSuite executes the search contract suite. The mock server is dedicated to search requests and routes pragmatically, because the platforms disagree on both paths and encoding:

  • GitLab sends every scope to the same path (/search) with a scope query param, so a scope param wins when present: projects -> repos, issues -> issues, users -> users.
  • Otherwise the path is matched: "repositories" (GitHub/Gitee/GitCode /search/repositories) or "projects" -> repos; "issues" (including Gitea/Forgejo's /repos/issues/search) -> issues; "users" -> users.
  • Anything else falls back to the repos fixture, which covers Gitea/Forgejo's bare /repos/search path.

Subtests: each search parses its fixture (repos by full_name, issues by title + string number, users by login) and the query keyword reaches the wire under some query parameter; the repo-scoped subtest additionally verifies SearchIssuesOptions.Repo takes a wire route that reflects the repo (path-embedded or query-carried, per platform).

func RunUsersSuite

func RunUsersSuite(t *testing.T, h UsersHarness)

RunUsersSuite tests user lookup operations.

func RunWebhookConfigSuite added in v0.99.9

func RunWebhookConfigSuite(t *testing.T, h WebhookConfigHarness)

RunWebhookConfigSuite tests single-webhook get/update.

func RunWebhookCorpus added in v0.72.0

func RunWebhookCorpus(t *testing.T, platform provider.Platform, parser WebhookParser, dir string)

RunWebhookCorpus runs the golden-file webhook corpus under dir: every <event>.json fixture (raw POST body + required headers) is parsed by parser and the normalized event compared byte-for-byte against <event>.golden.json with the volatile fields (ID, Timestamp, RawPayload) cleared. The corpus is the cross-platform uniformity net: every backend must map its platform's wire events onto the same canonical vocabulary (cr./push/tag./branch./issue./comment.) with the same payload fields populated.

Fixture format:

{
  "headers": {"X-Gitea-Event": "issues"},   // per-platform event headers
  "body": { ...raw webhook payload... }
}

The driver signs requests with the platform's registered validator and corpusSecret, so signature validation runs end to end.

func VersionProxy

func VersionProxy(baseURL, versionBody string) *httptest.Server

VersionProxy returns a test server that responds to /api/v1/version with versionBody and reverse-proxies every other path to baseURL. Gitea/Forgejo SDKs require the version endpoint at client init; this wrapper lets the contract suite target those backends with a plain mock server.

Types

type BranchProtectionsHarness

type BranchProtectionsHarness struct {
	Name           string
	Platform       provider.Platform
	NewProvider    func(t *testing.T, cfg provider.Config) provider.Provider
	ListResponse   string
	MutateResponse string
}

BranchProtectionsHarness bundles inputs for the branch-protection suite.

type BranchProtectionsHarnessConfig

type BranchProtectionsHarnessConfig struct {
	ListResponse   string
	MutateResponse string
}

BranchProtectionsHarnessConfig carries fixtures for the branch-protection suite. All fields are required.

type CollaboratorsHarness

type CollaboratorsHarness struct {
	Name               string
	Platform           provider.Platform
	NewProvider        func(t *testing.T, cfg provider.Config) provider.Provider
	ListResponse       string
	UserLookupResponse string
}

CollaboratorsHarness bundles inputs for the collaborator suite.

type CollaboratorsHarnessConfig

type CollaboratorsHarnessConfig struct {
	ListResponse string
	// UserLookupResponse, when set, is served for the username→ID lookup
	// GET (…/users/{username}) that platforms with ID-addressed member
	// APIs (GitLab, TencentCode) perform before add/remove mutations.
	// Empty means the platform addresses collaborators by username
	// directly and makes no lookup.
	UserLookupResponse string
}

CollaboratorsHarnessConfig carries fixtures for the collaborator suite.

type CommitStatusHarnessConfig

type CommitStatusHarnessConfig struct {
	ListResponse  string
	EmptyResponse string
}

CommitStatusHarnessConfig mounts the commit-status suite. The suite is self-driving: it records requests, invokes CreateCommitStatus and ListCommitStatuses, and asserts exactly one status request per operation reached the wire.

ListResponse is the platform-shaped JSON fixture served for the ListCommitStatuses probe; it must decode into at least one status with a non-empty State under the backend's converter. It is required whenever the platform declares the CommitStatuses capability.

EmptyResponse is the platform-shaped JSON body served on page ≥ 2 to terminate the pagination walk. It defaults to Harness.EmptyListResponse; set it when the status endpoint's empty shape differs from the platform's usual empty-list shape (e.g. Gitee's wrapped check-run object).

type DeployKeysHarness

type DeployKeysHarness struct {
	Name           string
	Platform       provider.Platform
	NewProvider    func(t *testing.T, cfg provider.Config) provider.Provider
	ListResponse   string
	CreateResponse string
}

DeployKeysHarness bundles inputs for the deploy-key suite.

type DeployKeysHarnessConfig

type DeployKeysHarnessConfig struct {
	ListResponse   string
	CreateResponse string
}

DeployKeysHarnessConfig carries fixtures for the deploy-key suite.

type Harness

type Harness struct {
	// Name is the human-readable platform identifier (e.g. "GitHub").
	Name string
	// Platform is the provider.Platform constant for this backend.
	Platform provider.Platform
	// NewProvider builds a provider.Provider from the given config. The harness
	// fills in BaseURL (and, for the retry subtest, RetryConfig) before calling,
	// so the function should forward any supplied RetryConfig/Hooks rather than
	// discarding them.
	NewProvider func(t *testing.T, cfg provider.Config) provider.Provider
	// EmptyListResponse is the JSON body the mock returns for empty lists.
	EmptyListResponse string
	// NonEmptyListResponse is the JSON body the mock returns for a non-empty
	// list, with at least one item that maps to a valid repo.
	NonEmptyListResponse string
	// Labels, when non-nil, auto-mounts the label-management suite inside
	// Run. Run enforces both directions: a platform declaring
	// Capabilities().Labels must provide this config, and a config must not
	// be provided by a platform that does not declare the capability.
	Labels *LabelsHarnessConfig
	// Issues, when non-nil, auto-mounts the issue-management suite inside
	// Run, with the same bidirectional drift checks as Labels.
	Issues *IssuesHarnessConfig
	// Reviews, when non-nil, auto-mounts the review-management suite inside
	// Run, with the same bidirectional drift checks as Labels and Issues.
	Reviews *ReviewsHarnessConfig
	// Milestones, when non-nil, auto-mounts the milestone-management suite
	// inside Run, with the same bidirectional drift checks as Labels,
	// Issues, and Reviews.
	Milestones *MilestonesHarnessConfig
	// Search, when non-nil, auto-mounts the search suite inside Run, with
	// the same bidirectional drift checks as Labels, Issues, Reviews, and
	// Milestones.
	Search *SearchHarnessConfig
	// CommitStatus, when non-nil, auto-mounts the commit-status suite inside
	// Run, with the same bidirectional drift checks as Labels, Issues,
	// Reviews, Milestones, and Search.
	CommitStatus *CommitStatusHarnessConfig
	// Notifications, when non-nil, auto-mounts the notification suite inside
	// Run, with the same bidirectional drift checks.
	Notifications *NotificationsHarnessConfig
	// Reactions, when non-nil, auto-mounts the reaction suite inside
	// Run, with the same bidirectional drift checks.
	Reactions *ReactionsHarnessConfig
	// BranchProtections, when non-nil, auto-mounts the branch-protection
	// suite inside Run, with the same bidirectional drift checks.
	BranchProtections *BranchProtectionsHarnessConfig
	// Collaborators, when non-nil, auto-mounts the collaborator suite.
	Collaborators *CollaboratorsHarnessConfig
	// WebhookConfig, when non-nil, auto-mounts the single-webhook
	// get/update suite (WebhookConfigManager — all seven backends).
	WebhookConfig *WebhookConfigHarnessConfig
	// Notes, when non-nil, auto-mounts the CR-note (merge-request note)
	// read/edit suite — GitLab-family platforms where MR notes live in a
	// separate iid namespace (GitHub-family: covered by IssueManager, no
	// config expected).
	Notes *NotesHarnessConfig
	// DeployKeys, when non-nil, auto-mounts the deploy-key suite.
	DeployKeys *DeployKeysHarnessConfig
	// RepoStats, when non-nil, auto-mounts the repo-stats suite.
	RepoStats *RepoStatsHarnessConfig
	// Users, when non-nil, auto-mounts the user-manager suite.
	Users *UsersHarnessConfig
	// Releases auto-mounts the release-management suite inside Run. Unlike
	// the fields above, ReleaseManager is a core interface composed into
	// provider.Provider (every backend implements it), so there is no
	// capability-declaration drift to check — the config is mandatory and
	// the suite fails the run when it is missing.
	Releases *ReleasesHarnessConfig
}

Harness bundles the inputs needed to run the contract suite against a backend.

type IssuesHarness

type IssuesHarness struct {
	Name        string
	Platform    provider.Platform
	NewProvider func(t *testing.T, cfg provider.Config) provider.Provider
	IssuesHarnessConfig
}

IssuesHarness is the full harness RunIssuesSuite consumes; auto-mounting builds it from the enclosing Harness plus IssuesHarnessConfig.

type IssuesHarnessConfig

type IssuesHarnessConfig struct {
	// ListResponse is the JSON array for issue-list GETs. First item: number
	// 1, title "bug", state "open", milestone {number:1, title:"v1"}.
	ListResponse string
	// GetResponse is the JSON object for single-issue GETs (number 1, title
	// "bug", state "open", milestone {number:1, title:"v1"}).
	GetResponse string
	// MutateResponse is the JSON object for POST/PATCH/PUT (same shape as
	// GetResponse).
	MutateResponse string
	// CommentsResponse is the JSON array for issue-comment GETs. First item:
	// id 1, body "a comment".
	CommentsResponse string
	// LabelsResponse is the JSON array for repository-label GETs. First
	// item: id 1, name "bug", color "#4cc917".
	LabelsResponse string
	// CreateIssueAssigneesByID declares that the backend resolves issue
	// assignee usernames through a users lookup (GitLab's /users?username=
	// exact-match filter, Tencent 工蜂's /users/{username} endpoint) and
	// writes numeric assignee IDs. When set, the suite asserts the
	// lookup-then-write wire shape on CreateIssue; when unset, the create
	// subtest exercises the plain title-only wire (the default).
	CreateIssueAssigneesByID bool
	// UpdateCommentViaIssue declares that the backend addresses an issue
	// comment edit through the issue (GitLab, Tencent 工蜂 note APIs): the
	// suite additionally asserts the recorded edit request hits
	// issues/1/notes/1, so a swap of the number and commentID plumbing
	// fails instead of passing on the body assertion alone.
	UpdateCommentViaIssue bool
}

IssuesHarnessConfig carries the fixtures a backend's main Harness needs to auto-mount the issue-management suite via Harness.Issues.

type LabelsHarness

type LabelsHarness struct {
	// Name is the human-readable platform identifier (e.g. "GitHub").
	Name string
	// Platform is the provider.Platform constant for this backend.
	Platform provider.Platform
	// NewProvider builds a provider.Provider; the harness fills in BaseURL.
	// It must construct the provider the same way the platform's main
	// harness does (including any VersionProxy wrapping for Gitea/Forgejo).
	NewProvider func(t *testing.T, cfg provider.Config) provider.Provider
	// ListResponse is the JSON array the mock returns for GET requests
	// (label listings and name→ID resolution lookups). Its first item must
	// have name "bug" and color "#4cc917" so the suite can assert color
	// normalization.
	ListResponse string
	// MutateResponse is the JSON object the mock returns for POST/PATCH
	// requests. It must have name "bug" and color "#4cc917".
	MutateResponse string
	// IgnoresListPagination declares that the backend's list endpoint does
	// not accept pagination parameters, so the suite skips its wire-level
	// page/per-page assertion. Currently only GitCode sets this: its SDK's
	// ListIssueLabels exposes no page/page-size parameters, so ListLabels
	// silently drops ListLabelsOptions on the wire. That is a known contract
	// gap, not something the other five backends share.
	IgnoresListPagination bool
}

LabelsHarness bundles the inputs needed to run the label-management contract suite against a backend that implements provider.LabelManager.

type LabelsHarnessConfig

type LabelsHarnessConfig struct {
	// ListResponse is the JSON array the mock returns for GET requests. Its
	// first item must have name "bug" and color "#4cc917".
	ListResponse string
	// MutateResponse is the JSON object the mock returns for POST/PATCH/PUT
	// requests. It must have name "bug" and color "#4cc917".
	MutateResponse string
	// IgnoresListPagination opts the backend out of the wire-level
	// pagination assertion (see LabelsHarness.IgnoresListPagination).
	IgnoresListPagination bool
}

LabelsHarnessConfig carries the fixtures a backend's main Harness needs to auto-mount the label-management suite via Harness.Labels. Name, Platform, and NewProvider are reused from the enclosing Harness.

type MilestonesHarness

type MilestonesHarness struct {
	Name        string
	Platform    provider.Platform
	NewProvider func(t *testing.T, cfg provider.Config) provider.Provider
	MilestonesHarnessConfig
}

MilestonesHarness is the full harness RunMilestonesSuite consumes; auto-mounting builds it from the enclosing Harness plus MilestonesHarnessConfig.

type MilestonesHarnessConfig

type MilestonesHarnessConfig struct {
	// ListResponse is the JSON array for milestone-list GETs. First item:
	// the platform's addressing identifier ("number" on GitHub and Gitee,
	// "id" on GitLab, Gitea, Forgejo, GitCode, and Tencent Code) rendering
	// as the string "1", title "v1", state "open" (GitLab's wire state
	// "active" is also accepted — the backend normalizes it).
	ListResponse string
	// MutateResponse is the JSON object for POST/PATCH/PUT (same shape as
	// the first list item).
	MutateResponse string
}

MilestonesHarnessConfig carries the fixtures a backend's main Harness needs to auto-mount the milestone-management suite via Harness.Milestones.

type NotesHarness added in v0.99.8

type NotesHarness struct {
	Name           string
	Platform       provider.Platform
	NewProvider    func(t *testing.T, cfg provider.Config) provider.Provider
	ListResponse   string
	MutateResponse string
}

NotesHarness bundles inputs for the CR-note suite.

type NotesHarnessConfig added in v0.99.8

type NotesHarnessConfig struct {
	// ListResponse is the JSON array served for the notes-list GET (and
	// the empty terminal page beyond page 1). Required shape: at least
	// one note with id/body/author.username.
	ListResponse string
	// MutateResponse is the JSON object returned by the note-edit PUT.
	MutateResponse string
}

NotesHarnessConfig carries the fixtures a backend needs to auto-mount the CR-note (merge-request note) read/edit suite via Harness.Notes.

NoteManager is an optional capability interface with GitLab-family semantics: MR notes live in their own iid namespace, so the IssueManager comment tools 404 for merge requests there. GitHub-family platforms (an issue IS the PR) are covered by IssueManager and are expected NOT to provide this config.

type NotificationsHarness

type NotificationsHarness struct {
	Name         string
	Platform     provider.Platform
	NewProvider  func(t *testing.T, cfg provider.Config) provider.Provider
	ListResponse string
}

NotificationsHarness bundles the inputs needed to run the notification contract suite against a backend that implements provider.NotificationManager.

type NotificationsHarnessConfig

type NotificationsHarnessConfig struct {
	// ListResponse is the JSON array the mock returns for notification list
	// requests. It must contain at least one notification with unread=true.
	ListResponse string
}

NotificationsHarnessConfig carries the fixtures a backend's main Harness needs to auto-mount the notification suite via Harness.Notifications.

type ReactionsHarness

type ReactionsHarness struct {
	Name           string
	Platform       provider.Platform
	NewProvider    func(t *testing.T, cfg provider.Config) provider.Provider
	ListResponse   string
	CreateResponse string
}

ReactionsHarness bundles the inputs needed to run the reaction contract suite against a backend that implements provider.ReactionManager.

type ReactionsHarnessConfig

type ReactionsHarnessConfig struct {
	// ListResponse is the JSON array the mock returns for reaction list
	// requests. It must contain at least one reaction with content "+1".
	ListResponse string
	// CreateResponse is the JSON object the mock returns for reaction create
	// requests.
	CreateResponse string
}

ReactionsHarnessConfig carries the fixtures a backend's main Harness needs to auto-mount the reaction suite via Harness.Reactions.

type ReleaseHarness

type ReleaseHarness struct {
	Name        string
	Platform    provider.Platform
	NewProvider func(t *testing.T, cfg provider.Config) provider.Provider
	ReleasesHarnessConfig
}

ReleaseHarness is the full harness RunReleaseSuite consumes; auto-mounting builds it from the enclosing Harness plus ReleasesHarnessConfig.

type ReleasesHarnessConfig

type ReleasesHarnessConfig struct {
	// ByTagResponse is the JSON object for single-release fetches. Required
	// shape: id 1, tag_name "v1.0.0", name "v1.0.0", body "release notes"
	// (the suite asserts the parsed TagName).
	ByTagResponse string
	// UpdateResponse is the JSON object for PATCH/PUT release updates (same
	// shape as ByTagResponse).
	UpdateResponse string
	// UpdateSendsDescriptionOnly declares that the platform's update
	// endpoint cannot carry a release name (TencentCode's update surface
	// only accepts a description): the update wire subtest then asserts the
	// "description" key instead of "name".
	UpdateSendsDescriptionOnly bool
}

ReleasesHarnessConfig carries the fixtures a backend's main Harness needs to auto-mount the release-management suite via Harness.Releases.

Unlike Labels/Issues/Reviews, ReleaseManager is a core interface composed into provider.Provider and implemented by every backend, so there is no capability-declaration drift to enforce: the config is mandatory for all seven platforms and testReleaseSuite fails the run when it is missing.

type RepoStatsHarness

type RepoStatsHarness struct {
	Name                 string
	Platform             provider.Platform
	NewProvider          func(t *testing.T, cfg provider.Config) provider.Provider
	ForksResponse        string
	StargazersResponse   string
	ContributorsResponse string
}

RepoStatsHarness bundles inputs for the repo-stats suite.

type RepoStatsHarnessConfig

type RepoStatsHarnessConfig struct {
	ForksResponse        string
	StargazersResponse   string
	ContributorsResponse string
}

RepoStatsHarnessConfig carries fixtures for the repo-stats suite.

type ReviewsHarness

type ReviewsHarness struct {
	Name        string
	Platform    provider.Platform
	NewProvider func(t *testing.T, cfg provider.Config) provider.Provider
	ReviewsHarnessConfig
	// IgnoresRequestReviewers declares that the backend's RequestReviewers is
	// a registered ignore: the platform's reviewer API needs inputs the SDK
	// surface cannot supply (GitLab's reviewer_ids want user IDs, but the SDK
	// addresses reviewers by username and exposes no Users API to resolve
	// them), so the method documents the ignore and performs no request. The
	// wire subtest then only asserts a silent, error-free no-op — anything
	// reaching the network would mean the registration drifted.
	IgnoresRequestReviewers bool
	// IgnoresDismissal declares that the backend's DismissReview is a
	// registered stub: the platform's review surface has no dismissal
	// endpoint at all (Tencent 工蜂 review notes expose no dismiss verb), so
	// the method documents the gap and returns a provider error wrapping
	// provider.ErrNotImplemented without touching the wire. The subtest then
	// asserts exactly that registration — a successful call or any recorded
	// request would mean the stub drifted.
	IgnoresDismissal bool
	// ListStateIsCommented declares that the backend's review reads carry no
	// verdict state: the SDK model behind ListReviews/GetReview has no state
	// field (Tencent 工蜂 review notes — the verdict travels only on the
	// create/update writes as reviewer_state and never comes back), so every
	// read review normalizes to provider.ReviewStateCommented, a registered
	// limitation. The List subtest then asserts that commented normalization
	// instead of the default approved one.
	ListStateIsCommented bool
}

ReviewsHarness is the full harness RunReviewsSuite consumes; auto-mounting builds it from the enclosing Harness plus ReviewsHarnessConfig.

type ReviewsHarnessConfig

type ReviewsHarnessConfig struct {
	// ListResponse is the JSON array for review-list GETs. First item: id 1,
	// user login "dev", state "APPROVED" (the suite asserts normalization to
	// provider.ReviewStateApproved).
	ListResponse string
	// GetResponse is the JSON object for single-review GETs (id 1, user
	// "dev", state "APPROVED").
	GetResponse string
	// MutateResponse is the JSON object for POST/PUT/PATCH (same shape as
	// GetResponse).
	MutateResponse string
	// CreateEvent is the wire value the platform's create must carry under
	// the "event" key for the suite's APPROVE-verdict create. The suite
	// calls CreateReview with Event "APPROVE": GitHub and GitCode forward
	// the option verbatim ("APPROVE"), Gitea/Forgejo translate it to their
	// SDK's review state ("APPROVED"). Empty means the platform's create
	// carries no verdict on the wire (GitLab's note-based create) and the
	// event-key assertion is skipped — the body assertion still runs.
	CreateEvent string
	// IgnoresRequestReviewers opts the backend out of the RequestReviewers
	// wire assertion (see ReviewsHarness.IgnoresRequestReviewers).
	IgnoresRequestReviewers bool
	// RequestReviewersByID declares that the backend resolves reviewer
	// usernames to numeric user IDs through a /users lookup before writing
	// them (GitLab resolves usernames to IDs first): the wire subtest then
	// asserts the lookup GET and an ID-carrying update body instead of the
	// username-carrying create (see assertRequestReviewersByIDWire).
	RequestReviewersByID bool
	// IgnoresDismissal opts the backend out of the DismissReview verb
	// assertion (see ReviewsHarness.IgnoresDismissal). It is for platforms
	// whose ONLY dismissal gap is a registered stub: the platform exposes no
	// dismiss surface at all, so DismissReview returns a wrapped
	// provider.ErrNotImplemented and the subtest asserts that registration
	// instead of a state-changing verb.
	IgnoresDismissal bool
	// ListStateIsCommented declares that the platform's review reads cannot
	// carry a verdict state (see ReviewsHarness.ListStateIsCommented).
	ListStateIsCommented bool
}

ReviewsHarnessConfig carries the fixtures a backend's main Harness needs to auto-mount the review-management suite via Harness.Reviews.

type SearchHarness

type SearchHarness struct {
	Name        string
	Platform    provider.Platform
	NewProvider func(t *testing.T, cfg provider.Config) provider.Provider
	SearchHarnessConfig
}

SearchHarness is the full harness RunSearchSuite consumes; auto-mounting builds it from the enclosing Harness plus SearchHarnessConfig.

type SearchHarnessConfig

type SearchHarnessConfig struct {
	// ReposResponse is the JSON body for repository-search GETs, in the
	// platform's wire shape (GitHub wraps items in {"total_count":..,
	// "items":[..]}, Gitea/Forgejo in {"data":[..]}, the rest are bare
	// arrays).
	ReposResponse string
	// ReposTotalCount is the server-side total the repos fixture reports
	// (GitHub's envelope carries total_count; the suite then asserts
	// SearchRepos returns exactly this total). Zero means the platform
	// reports no server-side total — the backend returns nil for total and
	// the suite keeps the weaker total == nil || *total >= len(results)
	// assertion.
	ReposTotalCount int
	// IssuesResponse is the JSON body for issue-search GETs, same per-
	// platform wrapping rules as ReposResponse.
	IssuesResponse string
	// UsersResponse is the JSON body for user-search GETs, same per-
	// platform wrapping rules as ReposResponse.
	UsersResponse string
}

SearchHarnessConfig carries the fixtures a backend's main Harness needs to auto-mount the search suite via Harness.Search. Every fixture's first item must render as: repos — full_name "owner/repo"; issues — number "1" (the platform's wire encoding of the identifier, parsed to the string "1"), title "found"; users — login "dev".

type UsersHarness

type UsersHarness struct {
	Name            string
	Platform        provider.Platform
	NewProvider     func(t *testing.T, cfg provider.Config) provider.Provider
	GetUserResponse string
}

UsersHarness bundles inputs for the user-manager suite.

type UsersHarnessConfig

type UsersHarnessConfig struct {
	// GetUserResponse is the JSON the mock returns for user lookup.
	GetUserResponse string
}

UsersHarnessConfig carries fixtures for the user-manager suite.

type WebhookConfigHarness added in v0.99.9

type WebhookConfigHarness struct {
	Name        string
	Platform    provider.Platform
	NewProvider func(t *testing.T, cfg provider.Config) provider.Provider
	GetResponse string
}

WebhookConfigHarness bundles inputs for the webhook-config suite.

type WebhookConfigHarnessConfig added in v0.99.9

type WebhookConfigHarnessConfig struct {
	// GetResponse is one webhook in the platform's native wire shape. It is
	// served for both the GET (get + family read-back) and the update call,
	// so converters must be able to decode it — e.g. GitHub/GitCode
	// `{"id":42,"url":"…","events":["push"]}`, GitLab/Gitee/TencentCode
	// boolean-flag bodies, Gitea-family `{"id":42,"config":{"url":"…"}, …}`.
	GetResponse string
}

WebhookConfigHarnessConfig carries fixtures for the webhook-config suite (single-webhook get/update on top of the core WebhookManager).

type WebhookParser added in v0.72.0

type WebhookParser interface {
	ParseWebhookEvent(r *http.Request, secret string) (*provider.NormalizedEvent, error)
}

WebhookParser is the surface RunWebhookCorpus exercises. Every backend's Provider implements it via WebhookManager; the narrow interface keeps the driver usable with zero-value backend structs (the parse path only needs Platform()).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL