coverage

package
v1.30.0 Latest Latest
Warning

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

Go to latest
Published: Aug 30, 2026 License: Apache-2.0 Imports: 6 Imported by: 0

Documentation

Overview

oasdiff's ultimate objective is to detect breaking changes, so that its users can prevent them. The diff layer reports every change to a spec, but falls short of that objective in two ways: it does not classify changes as breaking or not, and it reports them in the spec's own hierarchical, technical vocabulary rather than as human-readable descriptions. The checker closes both gaps with rules that detect kinds of changes, judge them, and report them in user-facing language.

The rules were defined gradually, each covering one more kind of change, which leaves the central question unanswerable: how well does oasdiff do its job? Which changes does it not detect? Auditing the rules against each other cannot answer it, because consistency cannot reveal what is absent altogether.

Package coverage computes the answer. Analyze measures the rules against every possible edit of an OpenAPI document (see checker/metaschema) and decides each edit: which checks cover it, or why none are expected. Each rule declares the edits it covers, and every edit that can change which payloads are valid must be covered by a check or waived with a reviewed reason. An edit that is neither is a gap; a waiver that accounts for nothing is stale; and an unchecked edit that a check could cover carries a suggested id for it. Completeness is thereby measured rather than assumed.

Analyze takes the rules to measure as an argument rather than calling checker.GetAllRules itself, so this package needs only the rule model and the enumeration, never the code that runs the checks.

Index

Constants

This section is empty.

Variables

View Source
var Waivers = []Waiver{

	{CategoryOpen, "components.securitySchemes.**", "scheme fields beyond type, flow URLs, and scopes (apiKey name and in, http scheme, bearerFormat, openIdConnectUrl) have no checks (tracked in #1175)"},
	{CategoryResolvedAtUsage, "components.**", "edits to component definitions surface as diffs at every referencing operation; only unused-component removal is reported directly (api-schemas-removed)"},
	{CategoryOpen, "webhooks.**", "webhooks are diffed (WebhooksDiff) but checkers only report webhook add/remove; changes inside a webhook's operations have no checks yet (tracked in #1160)"},
	{CategoryOpen, "paths.*.parameters.**", "path-level parameter additions are checked (new-request-*-default-parameter-to-existing-path); modifications and removals at path level have no checks yet (tracked in #1163)"},
	{CategoryOpen, "paths.*.*.callbacks.**", "callbacks are not checked (tracked in #1161)"},
	{CategoryOpen, "paths.*.*.requestBody.content.*.encoding.**", "multipart/form encoding metadata (contentType, per-part headers, style) has no checks (tracked in #1165)"},
	{CategoryOpen, "paths.*.*.responses.*.content.*.encoding.**", "encoding metadata has no checks (tracked in #1165)"},
	{CategoryOpen, "paths.*.*.parameters.*.content.**", "parameters serialized via a content map are not checked (only the schema form is) (tracked in #1166)"},
	{CategoryOpen, "paths.*.*.requestBody.content.*.itemSchema.**", "only itemSchema existence is checked (request-body-media-type-item-schema-added/removed); changes inside it are not (tracked in #1167)"},
	{CategoryOpen, "paths.*.*.responses.*.content.*.itemSchema.**", "only itemSchema existence is checked; changes inside it are not (tracked in #1167)"},
	{CategoryOpen, "paths.*.*.responses.*.headers.*.**", "response headers are checked for existence, required, and schema type/format/nullable only; serialization fields, the content form, and the remaining schema keywords are unchecked (tracked in #1162)"},
	{CategoryOpen, "paths.*.*.parameters.*.schema:set,unset", "a parameter schema appearing or disappearing is unchecked (the media-type analog has request-body-media-type-schema-added/removed) (tracked in #1054)"},
	{CategoryOpen, "paths.*.*.parameters.*.schema.**", "parameter schemas are checked for type/format, enum, bounds, pattern, nullable, default, and required/property membership; the remaining schema keywords are unchecked (tracked in #1054, #1155, #1156, #1157, #1159)"},
	{CategoryOpen, "paths.*.*.parameters.*.style", "parameter serialization style changes the wire format but is unchecked (tracked in #1164)"},
	{CategoryOpen, "paths.*.*.parameters.*.explode", "explode changes the wire format of array/object parameters but is unchecked (tracked in #1164)"},
	{CategoryOpen, "paths.*.*.parameters.*.allowReserved", "allowReserved changes accepted query characters but is unchecked (tracked in #1164)"},
	{CategoryCoveredAs, "**.discriminator.mapping.*:set,unset", "add/remove: a mapping entry appearing or disappearing is the entry add/remove, which is claimed"},
	{CategoryCoveredAs, "**.discriminator.propertyName:set,unset", "discriminator set/unset: propertyName is required inside discriminator, so its presence tracks the discriminator's"},
	{CategoryOpen, "**.schema.additionalProperties", "setting additionalProperties:false narrows accepted request objects (breaking) and is unchecked (tracked in #1054)"},
	{CategoryOpen, "**.schema.multipleOf", "response set (narrowing the output) and non-body contexts are unchecked; the breaking directions have rules (tracked in #1159)"},
	{CategoryOpen, "**.schema.uniqueItems", "response set (narrowing the output) and non-body contexts are unchecked; the breaking directions have rules (tracked in #1159)"},
	{CategoryOpen, "**.schema.maxProperties", "remaining directions and non-body contexts are unchecked; the breaking directions have rules (tracked in #1159)"},
	{CategoryOpen, "**.schema.minProperties", "remaining directions and non-body contexts are unchecked; the breaking directions have rules (tracked in #1159, #1171 for the set case)"},
	{CategoryOpen, "**.schema.items:set,unset", "an items subschema appearing on a request narrows accepted arrays (breaking) and is unchecked (tracked in #1054)"},
	{CategoryOpen, "**.schema.not", "a not subschema appearing on a request narrows the accepted set (breaking) and is unchecked (tracked in #1054)"},
	{CategoryOpen, "**.schema.maxItems", "remaining directions (request unset widens, response set/decrease narrow the server's output) and non-body contexts are unchecked; the breaking directions have rules (tracked in #1159)"},
	{CategoryOpen, "**.schema.maximum", "remaining directions (request unset widens, response set/decrease narrows the server's output) are unchecked; the breaking directions have rules (tracked in #1159)"},
	{CategoryOpen, "**.schema.minimum", "remaining directions are unchecked; the breaking directions have rules (tracked in #1159)"},
	{CategoryOpen, "**.schema.maxLength", "remaining directions are unchecked; the breaking directions have rules (tracked in #1159)"},
	{CategoryOpen, "**.schema.minLength", "remaining directions are unchecked; the breaking directions have rules (tracked in #1159)"},
	{CategoryOpen, "**.schema.minItems", "remaining directions are unchecked; the breaking directions have rules (tracked in #1159)"},
	{CategoryOpen, "**.schema.exclusiveMaximum", "remaining directions are unchecked; the breaking directions have rules (tracked in #1159)"},
	{CategoryOpen, "**.schema.exclusiveMinimum", "remaining directions are unchecked; the breaking directions have rules (tracked in #1159)"},
	{CategoryOpen, "**.schema.minContains:set,unset", "minContains presence changes are unchecked; increase/decrease have rules (tracked in #1159)"},
	{CategoryOpen, "**.schema.maxContains:set,unset", "maxContains presence changes are unchecked; increase/decrease have rules (tracked in #1159)"},
	{CategoryOpen, "**.schema.unevaluatedItems:change", "switching unevaluatedItems between boolean and schema form is unchecked; set/unset have rules (tracked in #1054)"},
	{CategoryOpen, "**.schema.unevaluatedProperties:change", "switching unevaluatedProperties between boolean and schema form is unchecked; set/unset have rules (tracked in #1054)"},
}

Waivers records why each wire-relevant edit with no rule is deliberately or knowingly uncovered. An uncovered edit no waiver matches (add a rule or a waiver) and a waiver matching no uncovered edit (remove the stale entry) both fail the build, so the list stays an honest, reviewed record.

Functions

This section is empty.

Types

type Edit

type Edit struct {
	Location string `json:"location" yaml:"location"`
	Action   string `json:"action" yaml:"action"`
	Polarity string `json:"polarity" yaml:"polarity"`
	Status   Status `json:"status" yaml:"status"`
	// Category refines a waived status: open (a missing check), or handled
	// elsewhere (resolved-at-usage, covered-as).
	Category WaiverCategory `json:"category,omitempty" yaml:"category,omitempty"`
	// Checks are the ids of the checks claiming the edit (covered only).
	Checks []string `json:"checks,omitempty" yaml:"checks,omitempty"`
	// Reason explains a waived or non-contract status.
	Reason string `json:"reason,omitempty" yaml:"reason,omitempty"`
	// SuggestedId is a derived candidate check id for an uncovered or waived
	// edit: a naming hint for the missing check, not a promise of one.
	SuggestedId string `json:"suggestedId,omitempty" yaml:"suggestedId,omitempty"`
}

Edit is one possible edit of an OpenAPI document with what the audit decided about it: its status, the checks that cover it, or the reason none are expected.

func Analyze

func Analyze(ruleset []rules.Rule) []Edit

Analyze maps every possible edit of an OpenAPI document to what the audit decides about it given these rules, sorted by location then action.

type Pattern

type Pattern struct {
	// Kind is "waiver" (what the checks miss today) or "non-contract" (a
	// fact about the object model).
	Kind string `json:"kind" yaml:"kind"`
	// Category refines a waiver: open, resolved-at-usage, or covered-as.
	Category WaiverCategory `json:"category,omitempty" yaml:"category,omitempty"`
	Pattern  string         `json:"pattern" yaml:"pattern"`
	// Edits is the number of edits the entry accounts for; attribution is
	// first-match, in table order.
	Edits  int    `json:"edits" yaml:"edits"`
	Reason string `json:"reason" yaml:"reason"`
}

Pattern is one waiver or non-contract entry with the number of edits it accounts for.

func Patterns

func Patterns() []Pattern

Patterns lists the waiver and non-contract entries with the number of edits each accounts for.

type Status

type Status string

Status is what the audit decided about one possible edit.

const (
	// Covered: at least one check claims the edit.
	Covered Status = "covered"
	// Uncovered: a wire-relevant edit with no check and no waiver;
	// the audit fails the build until it gains one or the other.
	Uncovered Status = "uncovered"
	// Waived: a wire-relevant edit with no check, accounted for by a
	// coverage waiver.
	Waived Status = "waived"
	// NonContract: the edit cannot change which payloads are valid
	// (an annotation, a specification extension, or a metaschema.NonContracts
	// entry), so no check is expected.
	NonContract Status = "non-contract"
)

type Waiver

type Waiver struct {
	Category WaiverCategory
	// Pattern is a location glob (see metaschema.MatchLocation), optionally
	// restricted to actions with ":action[,action...]"; without the suffix
	// it waives every action at the location.
	Pattern string
	Reason  string
}

Waiver explains one family of wire-relevant edits that no rule covers. Facts about the object model itself (fields outside the wire contract) live in metaschema.NonContracts; entries here describe what the checks happen to miss today, and go stale as checks are added.

type WaiverCategory

type WaiverCategory string

WaiverCategory says what kind of accounting a waiver is, which decides whether a missing check is implied: only open waivers describe checks that could exist.

const (
	// CategoryOpen: a candidate missing check, with its tracking issue.
	CategoryOpen WaiverCategory = "open"
	// CategoryResolvedAtUsage: component definitions are compared at their
	// referencing operations after $ref resolution, so the check belongs at
	// the usage sites, which have their own edits.
	CategoryResolvedAtUsage WaiverCategory = "resolved-at-usage"
	// CategoryCoveredAs: the same document edit is already reported under
	// another action at the same location.
	CategoryCoveredAs WaiverCategory = "covered-as"
)

Jump to

Keyboard shortcuts

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