Documentation
¶
Overview ¶
Package requestinventory holds the committed record of what this server sends GitLab, and the one definition of which catalog actions it covers.
The artifact itself is written by cmd/gen_request_inventory out of the shards internal/testutil records, and read by cmd/audit_1to1's paths scope, which audits it. Both need the same answer to "which package was seen issuing nothing", and two answers to that question would be worse than none: the generator's summary and the auditor's gate would disagree about the number while both looked right.
What a row is, and what it is not ¶
One row per package, method and templated endpoint, carrying the union of the parameter names that package was seen to send it. A row is not keyed by action, because nothing on the wire names one: the httptest server answers on its own goroutine while the test goroutine that called the handler is out of reach, and the catalog's route is a closure over its handler, so neither the stack nor the catalog can hand back an action to attribute a request to.
The consequence runs through everything here. Coverage is per owning package, so "covered" means the package that owns this action issued some request, never that this action's request was seen. It is the strongest statement the recording can support, and saying more would be a guess dressed as a measurement.
Index ¶
Constants ¶
const ( // Path is where the committed artifact lives, relative to the repository // root. Path = "docs/development/request-inventory.json" // Note is the header the artifact carries, since JSON has no comments and // the file is long enough that a reader will meet a row before any prose // about it. Note = "Generated by `make gen-request-inventory`; do not edit. " + "One row per package, method and templated endpoint: what the unit suite was seen to send " + "through internal/testutil.NewTestClient, which is not the same as everything this server can send. " + "`query`, `body` and `variables` are the union of the names that package sent that endpoint, never one call's set. " + "`identifiers` counts the distinct raw values each placeholder was seen standing for, which is a lead about a hard-coded identifier and never a verdict. " + "A segment the shape rule could not recognize as an identifier is still a fixture value, " + "so one endpoint reached with two branch names is two rows: the rule is in internal/testutil/request_shape.go " + "and what a row is is in cmd/internal/requestinventory. " + "Recorded on Linux; a run on another platform may legitimately skip a test and record less." // KindREST and KindGraphQL are the two shapes a row comes in. KindREST = "rest" KindGraphQL = "graphql" )
const RootOwner = "tools"
RootOwner is what the catalog calls the orchestration package itself, which is ToolsDir rather than a directory under it: a spec group that declares no owner is given this one, and TestCollectedActionSpecs_DeclareCatalogOwnership admits it beside the domain names. Resolving it to the root package is what keeps an action owned by it from being classified as owned by nothing.
const ToolsDir = "internal/tools"
ToolsDir is where a domain package lives, and the prefix a recorded package name carries when the catalog knows it as an owner.
Variables ¶
This section is empty.
Functions ¶
func PackageDir ¶
PackageDir is where the package an owner names lives, which is ToolsDir itself for RootOwner and a directory under it for every domain.
func PackageName ¶ added in v3.1.0
PackageName is the owner spelled the way a recorded row spells its package, which is what joins an action to the requests its package was seen making. It is ToolsDir itself for RootOwner and a path under it for every domain, the same two cases PackageDir resolves against the filesystem.
func Packages ¶
Packages names the owners in a list, for a report that wants the packages without the actions under them.
func Render ¶
Render marshals rows as the committed artifact, with the trailing newline a text file in this repository ends with.
The marshal goes through cmdutil.Must because it cannot fail on a value built out of strings, and a caller handed that error could only print it and stop, which is what the panic already does.
Types ¶
type Action ¶
type Action struct {
ID string
Owner string
// ReadOnly is the action's own classification, not the route's: pagination
// is a question about a read, and a mutation answering with a list is
// answering about what it just changed.
ReadOnly bool
// Route carries the input and output types and their schemas. Its Handler
// is bound to no client here, since nothing in this package calls it.
Route toolutil.ActionRoute
}
Action is one catalog action as this dimension sees it: an identity, the package that owns it, which is the only handle the recording can be joined on, and the route the catalog registered it with.
The route is carried because the dimension asks two questions of an action and they need different halves of it. The coverage question below needs the owner alone. The pagination question needs what the action publishes and what it accepts, both of which live on the route as reflect types and schema maps that no source scan would have to reconstruct. Carrying it here is what keeps the catalog built once per run: the alternative is a second tools.BuildActionCatalog in the caller, which would also fork the decision this file makes once about which tier to build at.
type Coverage ¶
type Coverage struct {
Total int
Covered int
Silent int
Unmapped int
// Silent and Unmapped owners are sorted by package, and each one's actions
// are sorted too, so a report of them is stable between runs.
SilentOwners []Owner
UnmappedOwners []Owner
}
Coverage counts what the recording could and could not see of the catalog.
The three counts are disjoint and none of them is per action, which is the point of keeping them apart. Covered means the package owning the action issued some request, not that this action's request was seen; silent means the package issued none; unmapped means the action's owner names no package at all, neither the orchestration package nor a domain under it, so the recording could not have seen it either way and the fault is in the catalog's ownership metadata rather than in any test.
Silent is weaker than "never exercised" for a second reason beyond the coarse grain: a package may declare specs whose handlers live in other packages, and the request is then recorded under the package that made it while the count of silent actions blames the one that declared them. internal/tools/adminspecs was that case for 92 actions until each of them was given the domain package its route names as its owner. Reading the silent list as a work list still means reading it package by package, not action by action.
type Inventory ¶
Inventory is the committed artifact.
func Read ¶
Read loads the committed inventory from a repository root.
An artifact with no rows in it is an error rather than an empty inventory: every caller here answers a question of the form "which of these did the suite never issue", and an empty file answers all of them with "none of them", which is the loudest possible wrong answer.
type Owner ¶
Owner is one package the catalog names as owning actions, with the actions it owns, listed so a report can name the work rather than score it.
type Row ¶
type Row struct {
Package string `json:"package"`
Kind string `json:"kind"`
Method string `json:"method"`
Path string `json:"path"`
// Query, Body and Variables are the union of every name this package was
// seen sending this endpoint, never one call's set. Two calls that differ
// only in which optional filter they pass are the same endpoint, so the
// union answers which names were sent it and deliberately not which of
// them were sent together: a combination is a property of the tests, and
// reading one off this file would be reading a fixture as a contract.
Query []string `json:"query,omitempty"`
Body []string `json:"body,omitempty"`
Operation string `json:"operation,omitempty"`
Variables []string `json:"variables,omitempty"`
// Identifiers counts, per placeholder of Path, the distinct raw values the
// recorder templated into that position across every call this package
// made to this endpoint.
//
// It is the only thing in a row that is about the values rather than the
// names, and it is here because the templating that makes a row an
// endpoint is also what hides a whole class of defect: with one fixture
// value, a handler that builds /projects/1/... and one that builds
// /projects/<the caller's id>/... produce the same row. A count of one is
// therefore a lead and never a verdict, and most of them are innocent.
//
// Absent on a row recorded before this field existed, and on a path with
// no placeholder in it at all.
Identifiers map[string]int `json:"identifiers,omitempty"`
}
Row is one endpoint one package was seen to call.