fetchtest

package
v0.16.1 Latest Latest
Warning

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

Go to latest
Published: Oct 31, 2026 License: Apache-2.0 Imports: 18 Imported by: 0

Documentation

Overview

Package fetchtest builds fetch fact records for tests. Import it from any test in the module that needs a FactRecord; it is a normal package rather than a _test one for the same reason internal/cli/testfakes is.

A test should say what is interesting about the record it needs and nothing else. Record covers the valid case and seals the record itself, so no test calls CanonicalHasher.SetContentHash by hand; Unsealed and Tampered cover the two ways a record can be invalid, so a test that exists to prove an invalid record is rejected keeps proving it. Reaching for Record where Unsealed or Tampered is meant leaves such a test green while it tests nothing — the builder cannot detect that, so choose deliberately.

The records produced here are byte-for-byte what the fetch pipeline persists: the defaults, the go.mod-only shape and the module-hash serialisation all follow domain.NewFactRecord rather than approximating it.

Index

Constants

View Source
const TamperDetail = "tampered after sealing"

TamperDetail is the verification detail Tampered writes after sealing. It is the mutated field because it is covered by the canonical hash yet is part of neither the store key nor any status assertion, so tampering is visible to the integrity check without disturbing how the record is looked up.

Variables

This section is empty.

Functions

func AssertComposesAcrossPipelineVersions

func AssertComposesAcrossPipelineVersions(t testing.TB, s ComposedStore)

AssertComposesAcrossPipelineVersions is the ports.FactRecordComposer contract test, and it pins both halves of the defect the capability was added for.

It seeds one coordinate with two measurements at two different FETCH pipeline versions — an older one whose checksum-database lookup answered, and a newer one whose lookup failed — and asserts three things:

  • the coordinate is found at all, though neither measurement sits at the pipeline version a reader would have guessed. A version-keyed read reports absence here, which is how a third of a real store became unextractable: the stage asked at the version it knew and the ledger answered "nothing", while the artefact sat in the blob store the whole time.
  • BOTH measurements reach the composer. Filtering before composing hides from the composer exactly the records it exists to rank.
  • the record served is the older, STRONGER one. A first-hit-wins fallback list returns whichever version it happened to name first, so a failed lookup appended after a good one becomes the answer — the defect domain.Compose's own doc says it was written to prevent. This is asserted against the served record's content hash, not against a version string.

Every implementation should call it, fakes included: a fake that answers a coordinate-only read by consulting one pipeline version lets an application-layer test go green on a read the real store answers differently.

func AssertRefusesUnsealed

func AssertRefusesUnsealed(t testing.TB, w RecordWriter)

AssertRefusesUnsealed is the ports.FactStore contract test for the one value the PutFetchRecord signature cannot exclude.

SealedRecord is meant to be self-evidencing: Seal and Rehydrate are the only ways to make one, so holding one proves the record was hashed. The exported struct leaves a gap — domain.SealedRecord{} compiles in any package and seals nothing — and an implementation that stores it appends an all-empty row that every later read treats as a genuine measurement of the empty module at the empty version.

It lives here so the rule has one definition rather than one per store. Every FactStore implementation should call it, fakes included: a fake that accepts an unsealed record lets an application-layer test go green on a write the real store rejects, which is the failure this whole guard exists to prevent.

It asserts the refusal only. Whether the refusal also left the store untouched needs a reader, so that leg stays with the implementation that has one.

func AssertRefusesZeroIdentity

func AssertRefusesZeroIdentity(t testing.TB, name string, op func() error)

AssertRefusesZeroIdentity asserts that op refuses the zero artefact identity with domain.ErrZeroIdentity.

It exists for the same reason AssertRefusesUnsealed and coordinatetest.AssertRefusesZeroCoordinate do: the rule needs one definition rather than one per store. Unexporting ArtefactIdentity's fields makes a hand-built identity impossible, but Go always permits the zero value, and the zero identity names no artefact at all — it would key a row on the empty hash on a write, and on a read it asks about nothing, to which absence is the wrong answer.

Every implementation that takes an identity should call it, fakes included: a fake that accepts the zero identity lets an application-layer test go green on a call the real store rejects.

It asserts the refusal only. Whether the refusal also left the store untouched needs a reader, so that leg stays with the implementation that has one.

func Blob

Blob is the blob identity of one artefact of a module version, for the tests that key a fake blob store directly rather than deriving the address from a record. A test that has a record should use ZipIdentity or GoModIdentity, which is the route production takes.

It goes through ports.NewBlobIdentity, so a fixture identity is one the constructor accepts, and it panics rather than taking a testing.TB for the reason H1 does — identities are built in table cases and package-level vars where no testing handle is in scope.

func ComposeCoordinate

func ComposeCoordinate(coord coordinate.ModuleCoordinate, records []domain.FactRecord) (domain.CompositeRecord, bool, error)

ComposeCoordinate is the composed read a fact-store fake owes, factored out so each fake answers it the way the real store does rather than approximating it. It selects the measurements describing coord — whatever pipeline version filed them — orders them as an append-only ledger would, and folds them exactly as the sqlite store's read does, divergence guard included. The bool is false when the ledger holds nothing about the coordinate.

Records are ordered by measurement time, then pipeline version, then content hash. A fake holds its records in a map, and map iteration order is random, so without a total order a fake would compose a differently-ordered slice on every run — which matters for a local coordinate, where Compose reads the ORDER as the sequence of observations rather than as competing claims.

func Composite

func Composite(t testing.TB, opts ...Option) domain.CompositeRecord

Composite returns the composed view of a single measurement — what a reader gets back from the store for an artefact measured once. It is what a test wants wherever production now hands out a CompositeRecord and the test's subject is the record's contents rather than the composition itself.

func GoModArtefact

func GoModArtefact(value string) domain.ArtefactIdentity

GoModArtefact is the artefact identity of a go.mod-only measurement whose go.mod has the h1 hash of value. See ZipArtefact.

func GoModBlob

func GoModBlob(t testing.TB, content string) ports.BlobIdentity

GoModBlob returns the blob identity a standalone go.mod holding content hashes to.

func GoModIdentity

func GoModIdentity(t testing.TB, r domain.FactRecord) ports.BlobIdentity

GoModIdentity returns the blob identity of the record's standalone go.mod, failing the test when the record carries no go.mod hash.

func H1

func H1(value string) domain.ModuleHash

H1 is the h1 module hash of value, the only algorithm current Go toolchains produce. It exists so callers pass a hash rather than a pre-formatted string and the ":" separator stays the domain's business.

It panics on a value the domain refuses, and takes no testing.TB in order to do so: hashes are built in table-driven cases and package-level vars where no testing handle is in scope, on the same terms as coordinatetest.MustNew. A panic here is a bad fixture, reported with a stack trace pointing at it.

func Hash

func Hash(algorithm, value string) domain.ModuleHash

Hash is the module hash of value under algorithm, for the tests whose subject is an artefact hashed by something other than h1 — a stdlib source tarball addressed by its SHA-256, say. It panics on a hash the domain refuses; see H1 for why it takes no testing.TB.

func ModuleZip

func ModuleZip(t testing.TB, path, version string, files map[string]string) ([]byte, ports.BlobIdentity)

ModuleZip returns the bytes of a module zip holding files, named relative to the module root, and the blob identity those bytes hash to. A blob store verifies what it serves, so a fixture it is to serve has to be one.

func Record

func Record(t testing.TB, opts ...Option) domain.FactRecord

Record returns a valid, sealed fact record: schema version and ecosystem are set, every option has been applied, and ContentHash covers the result. It is what a test wants whenever the record is a fixture rather than the subject.

func Respelt

func Respelt(t testing.TB, r domain.FactRecord) (fetchedAt, seal string)

Respelt returns the row a build spelling fetched_at as a trimmed fraction would have stored for r, a whole-second record: the stored text, and the seal taken over it. Its stored values hash to that seal, and this build, which spells the parsed time at full width, cannot reproduce it.

func Sealed

func Sealed(t testing.TB, opts ...Option) domain.SealedRecord

Sealed returns the same record as Record, wrapped in the SealedRecord the fact store accepts for writing. It is the counterpart the append-only ledger needs: PutFetchRecord takes only a sealed record, so a test that seeds a store goes through here rather than hand-rolling the seal.

It takes testing.TB, not *testing.T, so the walk fakes and the vulnerability benchmark — neither of which has a *testing.T — can seed records too.

func Tampered

func Tampered(t testing.TB, opts ...Option) domain.FactRecord

Tampered returns a sealed record whose body was altered afterwards, so its stored content hash no longer recomputes. Use it for tests that prove a mutated record is rejected; Record would return a self-consistent record and the test would pass while testing nothing.

func Unsealed

func Unsealed(t testing.TB, opts ...Option) domain.FactRecord

Unsealed returns a record with no content hash at all. Use it for tests that prove an unsealed record is rejected — Record would seal it and the test would pass without exercising the guard.

func ZipArtefact

func ZipArtefact(value string) domain.ArtefactIdentity

ZipArtefact is the artefact identity of a module zip with the h1 hash of value, and GoModArtefact the identity of a go.mod-only measurement. They are for the tests whose subject is an identity rather than the record carrying one; a test that has a record should derive the identity from it with domain.ArtefactIdentityOf, which is what production does.

Both go through domain.ParseArtefactIdentity, so a fixture identity is one the reader can read — a hand-built value object could claim a shape the parser rejects and no test would notice.

func ZipIdentity

func ZipIdentity(t testing.TB, r domain.FactRecord) ports.BlobIdentity

ZipIdentity returns the blob identity of the record's module zip, failing the test when the record describes no zip. Tests that seed a blob store must key the bytes by identity, because that is how production asks for them.

Types

type ComposedStore

type ComposedStore interface {
	PutFetchRecord(ctx context.Context, record domain.SealedRecord) error
	ComposeFetchRecord(ctx context.Context, coord coordinate.ModuleCoordinate) (domain.CompositeRecord, bool, error)
}

ComposedStore is the write half of ports.FactStore plus the coordinate-only read of ports.FactRecordComposer. The assertion below takes this rather than the full store so a fake that has no version-keyed read is still held to the composed one.

type Option

type Option func(*domain.FactRecord)

Option configures the record a builder produces. Options are applied in the order given, before the record is sealed.

func AcquisitionMode

func AcquisitionMode(m domain.AcquisitionMode) Option

AcquisitionMode sets the path the module's bytes arrived by, which is what makes the content handle resolvable.

func Content

func Content(handle string) Option

Content records where a measurement put the module zip, and gives the record a module hash to be addressed by when it has none.

The location is provenance only — production stores an artefact under the identity it measured, and a store either holds that identity or does not — so a record carrying a content location must also carry the zip hash that identifies it. Deriving one here from the handle keeps that invariant without every caller having to state a hash it does not care about; a caller that does care sets ModuleHash explicitly, and this leaves it alone.

func Coordinate

func Coordinate(c coordinate.ModuleCoordinate) Option

Coordinate sets the module path and version.

func Detail

func Detail(d string) Option

Detail sets the verification detail that explains the status.

func Digests

func Digests(d domain.ArtifactDigests) Option

Digests sets the raw artefact digests of the module zip.

func Ecosystem

func Ecosystem(e string) Option

Ecosystem overrides the ecosystem. It is for tests that prove a foreign ecosystem is rejected; kanonarion only ever writes domain.EcosystemGo.

func FetchedAt

func FetchedAt(at time.Time) Option

FetchedAt sets the measurement time, stored in UTC as the record does.

func GitReference

func GitReference(g domain.GitReference) Option

GitReference sets the VCS provenance the record was cross-verified against.

func GoMod

func GoMod(handle string) Option

GoMod records where a measurement put the standalone go.mod, and gives the record a go.mod hash to be addressed by when it has none. See Content.

func GoModHash

func GoModHash(h domain.ModuleHash) Option

GoModHash sets the h1 hash of the module's go.mod.

func GoModOnly

func GoModOnly(goModHandle string) Option

GoModOnly shapes the record as the go.mod-only acquisition path produces it: the go.mod is stored while the zip was never fetched, so ContentLocation is empty and no module hash was computed.

The absent module hash is written as ":" rather than "": domain.NewFactRecord stores ModuleHash.String(), which is Algorithm + ":" + Value, so a zero hash serialises as the bare separator. Records already in the store carry that value, and absence is tested with ModuleHash.IsZero(), so writing "" here would make the fixture diverge from both production and the persisted data.

func MeasurementKind

func MeasurementKind(k domain.MeasurementKind) Option

MeasurementKind sets what the measurement did — acquired or revalidated.

func Module

func Module(path, version string) Option

Module sets the module path and version from their parts.

func ModuleHash

func ModuleHash(h domain.ModuleHash) Option

ModuleHash sets the h1 hash of the module zip.

func Path

func Path(path string) Option

Path sets the module path, leaving the version as it is. It is for the tests whose subject is the path alone.

func PipelineVersion

func PipelineVersion(v string) Option

PipelineVersion sets the pipeline version the record is filed under.

func Retracted

func Retracted(retracted bool) Option

Retracted marks the module version as retracted by its author.

func SchemaVersion

func SchemaVersion(v string) Option

SchemaVersion overrides the schema version. It is for tests whose subject is the schema field itself; every other record should carry the current version the default supplies.

func Status

Status sets the verification status.

func SumDBCheck

func SumDBCheck(p domain.LegProvenance, source string) Option

SumDBCheck sets how the measurement came by its checksum-database leg, and source names the record it was inherited from (empty when rechecked).

func SumDBLookupFailed

func SumDBLookupFailed(failed bool) Option

SumDBLookupFailed marks the record's checksum-database lookup as having failed rather than answered, which is what makes it ineligible as a cache hit.

func VCSCheck

func VCSCheck(p domain.LegProvenance, source string) Option

VCSCheck sets how the measurement came by its VCS cross-verification leg, and source names the record it was inherited from (empty when rechecked). Leaving it unset is how a --skip-vcs run is represented: the leg is absent, which is a different claim from a check that ran and could not confirm.

func VCSURLBinding

func VCSURLBinding(b domain.VCSURLBinding) Option

VCSURLBinding sets which clone URL the VCS cross-verification leg put to the test: one derived from the module path, or one the untrusted proxy named. Leaving it unset is how a record written before this was measured is represented — the record does not say, which is not a negative finding.

func VersionTime

func VersionTime(at time.Time) Option

VersionTime sets the time the module source reported for the version. Leaving it unset is how a record whose source gave none, or one written before the field, is represented.

type RecordWriter

type RecordWriter interface {
	PutFetchRecord(ctx context.Context, record domain.SealedRecord) error
}

RecordWriter is the write half of ports.FactStore. The contract assertions below take this rather than the full interface so a fake that only writes — and the audit decorator, which is not a whole store either — can be checked without growing a read path it does not have.

Jump to

Keyboard shortcuts

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