conformance

package
v1.0.0 Latest Latest
Warning

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

Go to latest
Published: Aug 30, 2026 License: MIT Imports: 21 Imported by: 0

Documentation

Overview

Package conformance runs the perk/v1 protocol conformance suite against one external plugin executable, outside Go's unit-test harness: fixture-driven protocol cases and generated transport cases, each in a fresh child spoken to as raw NDJSON-RPC on stdio. The runner never routes frames through the production Client — it must be able to send deliberately invalid frames — and never invokes build_target, open, or any session RPC, so a transport-only plugin (perk-redis without a Redis server) can be tested. Every child is terminated and reaped before the next case starts, and independent failures never stop later cases.

Index

Constants

View Source
const (
	CategorySpawn    = "spawn"
	CategoryProtocol = "protocol"
	CategoryBehavior = "behavior"
	CategoryTimeout  = "timeout"
	CategoryShutdown = "shutdown"
)

Stable failure categories for one conformance case.

View Source
const EvidenceSchema = "perk/v1/plugin-test-evidence.schema.json"

EvidenceSchema is the canonical identity of the evidence document schema (protocol/perk-v1/plugin-test-evidence.schema.json). The emitted evidence document names it so any consumer can validate the document against the exact schema that produced it.

View Source
const EvidenceVersion = 1

EvidenceVersion is the version of the evidence document shape this binary emits. A future change to the document shape bumps this version together with the schema asset.

Variables

This section is empty.

Functions

func ContractDigest

func ContractDigest(src source) (string, error)

ContractDigest returns the canonical SHA-256 of the whole perk/v1 contract asset set: schema.json, fixtures/manifest.json, and every fixture frame, hashed in sorted name order with explicit length framing — each asset contributes "<name-length>:<name>" followed by "<content-length>:<content>". Any drift in any asset, including a rename, reorder, or content edit, changes the digest; the framing makes concatenation boundaries unambiguous.

Types

type CapabilitiesIdentity

type CapabilitiesIdentity struct {
	Name    string `json:"name"`
	Display string `json:"display,omitempty"`
	Driver  string `json:"driver,omitempty"`
}

CapabilitiesIdentity is the declarative identity of a plugin's capability advertisement, captured from the initialize handshake — no backend required. The v1 protocol advertises no implementation version, so none is reported.

type Case

type Case struct {
	Name string
	Run  func(child *Child, until time.Time) error
}

Case is one named conformance case. Run receives the fresh child and the case deadline; a nil error is a pass. The child is always terminated and reaped by the engine afterwards.

type CaseError

type CaseError struct {
	Category string
	Message  string
}

CaseError is one case failure with its stable category and message. Messages are structural — raw protocol frames and request data are never reported.

func (*CaseError) Error

func (e *CaseError) Error() string

type Child

type Child struct {
	// contains filtered or unexported fields
}

Child is one plugin child under test, spoken to as raw NDJSON-RPC on stdio. Frames are never routed through the production Client, so the runner can send deliberately invalid frames; responses are parsed strictly — UTF-8, a single JSON object per LF-terminated frame within maxFrameBytes, jsonrpc "2.0", exactly one result or error, an unsigned numeric id, no duplicate or unknown responses, no stdout noise — and any violation terminates the child. stderr is drained concurrently with the same bounded intent as the host client (newest 64 KiB / 100 lines), so a chatty child can never block on a full stderr pipe or grow diagnostics without limit.

func (*Child) Close

func (c *Child) Close() error

Close shuts the child down and reaps it: stdin closes (EOF), the process gets up to killGrace to exit, then is killed. Idempotent; a child is never left running or unreaped. Close returns an error only when the child survives the kill and cannot be reaped.

func (*Child) CloseInput

func (c *Child) CloseInput() error

CloseInput closes the child's stdin (EOF) mid-case; the child is expected to terminate on its own.

func (*Child) ExitStatus

func (c *Child) ExitStatus() int

ExitStatus returns the child's exit code once reaped; -1 while running or signal-killed.

func (*Child) Expect

func (c *Child) Expect(until time.Time) (Frame, error)

Expect returns the next validated response frame, failing when the child terminates first or the deadline passes.

func (*Child) ExpectExit

func (c *Child) ExpectExit(until time.Time) error

ExpectExit requires the child to terminate (be reaped) within the bound, and fails when it answers after its expectations were complete.

func (*Child) ExpectQuiet

func (c *Child) ExpectQuiet(until time.Time, quiet time.Duration) error

ExpectQuiet requires silence after a case's expected exchanges: any response frame or child termination within quiet fails. This catches duplicate responses, fabricated responses, and premature exit.

func (*Child) ExpectSilent

func (c *Child) ExpectSilent(until time.Time, quiet time.Duration) error

ExpectSilent requires no response frame within quiet; child termination is expected (deliberately invalid input) and passes.

func (*Child) SendBatch

func (c *Child) SendBatch(frames ...[]byte) error

SendBatch delivers several request frames in one write, registering every id first so responses are expected regardless of arrival order.

func (*Child) SendFixture

func (c *Child) SendFixture(frame []byte) error

SendFixture delivers one canonical fixture frame, registering its unsigned numeric id when it carries one so its response is expected. Frames without a parseable id — notifications and deliberately invalid requests — are sent unanswered.

func (*Child) SendRaw

func (c *Child) SendRaw(frame []byte) error

SendRaw writes one raw frame without registering any response — deliberately invalid frames and notifications.

type Document

type Document struct {
	EvidenceSchema   string                `json:"evidence_schema"`
	EvidenceVersion  int                   `json:"evidence_version"`
	ProtocolVersion  int                   `json:"protocol_version"`
	HostVersion      string                `json:"host_version,omitempty"`
	ContractSHA256   string                `json:"contract_sha256,omitempty"`
	Entry            string                `json:"entry"`
	Path             string                `json:"path,omitempty"`
	ExecutableSHA256 string                `json:"executable_sha256,omitempty"`
	Capabilities     *CapabilitiesIdentity `json:"capabilities,omitempty"`
	Error            string                `json:"error,omitempty"` // suite-level failure (interrupt, setup, resolve, hash)
	Cases            []Result              `json:"cases,omitempty"`
	Passed           int                   `json:"passed"`
	Failed           int                   `json:"failed"`
	OK               bool                  `json:"ok"`
}

Document is the complete machine-readable outcome of one run: a self-contained release evidence document. It carries the stable evidence fields (schema identity and version, perk protocol version, host build version, canonical contract digest, executable digest and resolved path, capabilities identity) plus the per-case results and final counts. It never carries connection targets, form values, credentials, statements, or raw protocol frames, and it is deterministic except per-case durations and the path.

func NewDocument

func NewDocument(entry string) (Document, error)

NewDocument returns the base evidence document for one entry with every stable field the engine can fill: the evidence schema identity and version, the perk protocol version the host speaks, and the canonical contract digest. HostVersion is filled by the CLI from the host build version; Test fills the resolved path, executable digest, capabilities identity, and the run results. It fails only when the embedded contract assets cannot be read — no evidence document can be produced then.

type Engine

type Engine struct {

	// Timeout bounds one case end to end — its exchanges and its
	// shutdown; a case that exceeds it fails. Default 30 seconds.
	Timeout time.Duration
	// Quiet is the silence window required after a case's expected
	// exchanges (catching duplicate or fabricated responses and
	// premature exit) and the window a deliberately invalid input frame
	// must stay quiet for. Default 200ms.
	Quiet time.Duration
	// contains filtered or unexported fields
}

Engine runs the conformance suite against one executable. New builds it from the canonical embedded protocol assets; tests inject a fake source through NewFrom to exercise manifest drift.

func New

func New() (*Engine, error)

New builds the engine from the canonical embedded protocol assets.

func NewFrom

func NewFrom(src source) (*Engine, error)

NewFrom builds the engine from an injected fixture source. The manifest and every named fixture are loaded up front, and the metadata the cases rely on is required: missing files, missing entries, missing codes or methods, or a fixture whose method drifts from its manifest entry fail the run coherently here.

func (*Engine) Test

func (e *Engine) Test(ctx context.Context, entry, path string) Document

Test runs every case against one resolved executable path. Each case spawns a fresh child and terminates and reaps it before the next starts; independent failures never stop later cases. A canceled context stops the run between cases and fails it overall. The returned document carries the stable evidence fields: the evidence schema identity and version, the perk protocol version, the canonical contract digest (from the engine's asset source), the executable digest of the resolved path (when the file exists and is readable), and the capabilities identity captured from the first validated initialize reply. HostVersion is filled by the CLI.

type ErrorData

type ErrorData struct {
	Code    int             `json:"code"`
	Message string          `json:"message"`
	Data    json.RawMessage `json:"data"`
}

ErrorData is the JSON-RPC error object of one response.

type Frame

type Frame struct {
	ID     uint64
	HasID  bool
	Result json.RawMessage
	Error  *ErrorData
}

Frame is one validated response frame from the child. Exactly one of Result and Error is set; HasID reports whether the frame carried an unsigned numeric id.

type Result

type Result struct {
	Name     string        `json:"name"`
	OK       bool          `json:"ok"`
	Duration time.Duration `json:"duration"`
	Category string        `json:"category,omitempty"`
	Error    string        `json:"error,omitempty"`
	Stderr   []string      `json:"stderr,omitempty"`
}

Result is the outcome of one conformance case: a stable name, pass or fail, duration, the failure category and message, and the bounded sanitized stderr tail. It is also the JSON document shape of one case.

Jump to

Keyboard shortcuts

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