contracttest

package
v0.0.0-...-4f7fa88 Latest Latest
Warning

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

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

README

Client adapter contract tests

contracttest is the executable contract every client adapter has to satisfy, in-tree and out-of-tree. Capability lookup is clients.As[T], a runtime type assertion: a renamed method makes an adapter silently stop implementing an interface. These tests are what notice.

What to run

From install/integrationctl/agentplugins:

go test ./clients/contracttest ./clients/all

clients/all runs the harness against every shipped adapter. contracttest itself also has negative tests that a bad adapter fails.

Adding a client

Three production edits, then this harness:

  1. A row in domain.ClientDefinitions (identity and ClientTraits).
  2. A package clients/<id> with compile-time assertions for every capability the traits declare (var _ clients.Lifecycle = (*Adapter)(nil), and so on).
  3. A New() line in clients/all.

Generic packages (planner, providers, usecase, adapters/clientdetect) are not in that list. clients/internal/exampleclient is a test-only adapter that registers through clients.NewRegistry without those packages importing it.

What the harness checks

  • RunAdapter: stable id that domain actually defines.
  • RunHostDetector: surface ids are unique, repeated observations agree, and the adapter does not invent evidence the host never gave it.
  • Trait parity: a declared ClientTraits capability has a matching interface implementation on the adapter in clients/all.
  • Lifecycle, identity, projector, and plan-refiner suites cover the remaining segregated interfaces as those capabilities moved into adapters.

Documentation

Overview

Package contracttest holds the executable contract every client adapter has to satisfy, in-tree and out-of-tree alike. It is the counterweight to clients.As[T]: a capability is discovered by type assertion, so a renamed or misspelled method makes an adapter silently stop implementing an interface, and nothing but a test notices.

The harness grows with the refactor: RunAdapter covers identity today, and RunHostDetector, RunPlanRefiner and the rest arrive with the parts that move each capability into the adapters.

Each Run* function is a thin reporter over a pure violations function, so the harness can prove that it rejects a bad adapter instead of only that it accepts a good one.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func RunAdapter

func RunAdapter(t *testing.T, adapter clients.Adapter)

RunAdapter asserts the mandatory part of the contract: the adapter reports a stable id that domain actually defines.

func RunHostDetector

func RunHostDetector(t *testing.T, adapter clients.Adapter)

RunHostDetector asserts the detection half of the contract on each supported operating system, against a host whose probes answer "nothing is installed":

  • surface ids are non-empty and unique, and an undetected surface carries no evidence;
  • every selection surface is one of the reported surfaces;
  • repeated observations of the same host are identical, in content, in surface order and in the number of probe calls;
  • nothing is reported as present, because the host says nothing is.

The last one is how far this harness gets towards "the adapter observes the machine only through clients.Host". It catches the case that matters - an adapter that stats a real path or resolves a real binary behind the host's back reports evidence the host never gave it - but it is a necessary condition, not a proof: an ambient read whose result does not reach the Detection is invisible here.

The properties under test are structural. What each client reports for a real installation is frozen by the detector's golden files instead.

func RunLifecycle

func RunLifecycle(t *testing.T, adapter clients.Adapter)

RunLifecycle asserts the activation half of the contract when the adapter implements it. A client without Lifecycle is activated by the operator, and that absence is not a violation.

func RunPlanRefiner

func RunPlanRefiner(t *testing.T, adapter clients.Adapter)

RunPlanRefiner asserts the planning half of the contract. A refiner speaks last, on a plan the generic pipeline has already shaped, and it may only add to it:

  • the plan identity is the planner's: ClientID, Scope, PhysicalArtifactID, TargetRoot and ActivePath come back unchanged;
  • an unsupported plan is never promoted, because readiness is an upgrade over a usable plan and not a way to overrule the generic verdict;
  • refining an already refined plan changes nothing, because a caller that revisits a target refines the same plan again.

What each client adds is frozen by the planner's golden files instead.

func RunPlanRefinerWithHost

func RunPlanRefinerWithHost(t *testing.T, adapter clients.Adapter, host domain.OpenCodeHostAuthority)

RunPlanRefinerWithHost supplies caller-qualified fixture authority without deriving a native host or physical-root identity from synthetic locators.

func RunProjector

func RunProjector(t *testing.T, adapter clients.Adapter)

RunProjector asserts the staging half of the contract. A projector writes only inside the staging tree it was given, never claims the generic managed_package_directory object, and is deterministic: a second Project on a clean copy of the same tree returns the same objects.

func RunRegistryInspector

func RunRegistryInspector(t *testing.T, adapter clients.Adapter)

RunRegistryInspector asserts the identity-inspection half of the contract when the adapter implements it. A client without RegistryInspector is observed only through the generic prepared-root walk.

func RunTraitParity

func RunTraitParity(t *testing.T, registry *clients.Registry, requirements []CapabilityRequirement)

RunTraitParity fails for every registered adapter whose client declares a trait that the adapter does not implement. It closes the hole As[T] opens: a renamed method turns a capability off silently, and only a declaration checked against the implementation catches that.

An empty requirement set is a valid no-op so the harness can stay wired when a caller has nothing to declare yet. Parity is deliberately one-directional: declaring a trait requires the interface, while implementing an interface nobody declared yet is how a capability is normally introduced.

Types

type CapabilityRequirement

type CapabilityRequirement struct {
	// Name is how a failure names the trait, for example "LifecycleKind=native_config".
	Name       string
	Holds      func(definition domain.ClientDefinition) bool
	Implements func(adapter clients.Adapter) bool
}

CapabilityRequirement is one "declaring this trait promises implementing this interface" rule. Holds reads the declaration from the domain definition; Implements answers the same question about the adapter, normally with a type assertion such as:

func(a clients.Adapter) bool { _, ok := a.(clients.Lifecycle); return ok }

type OpenCodeV1Host

type OpenCodeV1Host struct{}

OpenCodeV1Host supplies explicit, pure qualified authority for direct adapter and usecase fixtures that bypass Engine.Prepare. It is not host probe evidence. Each Profile call returns a separately owned qualification-ledger snapshot.

func (OpenCodeV1Host) ConfigDialect

func (h OpenCodeV1Host) ConfigDialect() string

func (OpenCodeV1Host) Profile

func (OpenCodeV1Host) ValidateNative

func (h OpenCodeV1Host) ValidateNative(skills bool, transports []string) error

Jump to

Keyboard shortcuts

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