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 ¶
- func Run(t *testing.T, h Harness)
- func RunIssuesSuite(t *testing.T, h IssuesHarness)
- func RunLabelsSuite(t *testing.T, h LabelsHarness)
- func VersionProxy(baseURL, versionBody string) *httptest.Server
- type Harness
- type IssuesHarness
- type IssuesHarnessConfig
- type LabelsHarness
- type LabelsHarnessConfig
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func RunIssuesSuite ¶ added in v0.39.0
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/{digits} → GetResponse; 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 ¶ added in v0.38.0
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 VersionProxy ¶
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 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
// IssuesImplementedButUndeclared, when true, documents a deliberate
// implemented-but-undeclared state: the backend's concrete type carries
// the IssueManager methods but does not declare Capabilities().Issues
// because the live API cannot honor the interface's contract (Gitee:
// every current repo returns alphanumeric string issue numbers the
// int-typed interface can neither decode nor address). The capabilities
// consistency check tolerates exactly this documented state; the issues
// suite still auto-skips without an Issues config.
IssuesImplementedButUndeclared bool
}
Harness bundles the inputs needed to run the contract suite against a backend.
type IssuesHarness ¶ added in v0.39.0
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 ¶ added in v0.39.0
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
}
IssuesHarnessConfig carries the fixtures a backend's main Harness needs to auto-mount the issue-management suite via Harness.Issues.
type LabelsHarness ¶ added in v0.38.0
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 ¶ added in v0.38.1
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.