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
- func ContractDigest(src source) (string, error)
- type CapabilitiesIdentity
- type Case
- type CaseError
- type Child
- func (c *Child) Close() error
- func (c *Child) CloseInput() error
- func (c *Child) ExitStatus() int
- func (c *Child) Expect(until time.Time) (Frame, error)
- func (c *Child) ExpectExit(until time.Time) error
- func (c *Child) ExpectQuiet(until time.Time, quiet time.Duration) error
- func (c *Child) ExpectSilent(until time.Time, quiet time.Duration) error
- func (c *Child) SendBatch(frames ...[]byte) error
- func (c *Child) SendFixture(frame []byte) error
- func (c *Child) SendRaw(frame []byte) error
- type Document
- type Engine
- type ErrorData
- type Frame
- type Result
Constants ¶
const ( CategorySpawn = "spawn" CategoryProtocol = "protocol" CategoryBehavior = "behavior" CategoryTimeout = "timeout" CategoryShutdown = "shutdown" )
Stable failure categories for one conformance case.
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.
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 ¶
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 ¶
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 ¶
CaseError is one case failure with its stable category and message. Messages are structural — raw protocol frames and request data are never reported.
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 ¶
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 ¶
CloseInput closes the child's stdin (EOF) mid-case; the child is expected to terminate on its own.
func (*Child) ExitStatus ¶
ExitStatus returns the child's exit code once reaped; -1 while running or signal-killed.
func (*Child) Expect ¶
Expect returns the next validated response frame, failing when the child terminates first or the deadline passes.
func (*Child) ExpectExit ¶
ExpectExit requires the child to terminate (be reaped) within the bound, and fails when it answers after its expectations were complete.
func (*Child) ExpectQuiet ¶
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 ¶
ExpectSilent requires no response frame within quiet; child termination is expected (deliberately invalid input) and passes.
func (*Child) SendBatch ¶
SendBatch delivers several request frames in one write, registering every id first so responses are expected regardless of arrival order.
func (*Child) SendFixture ¶
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.
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 ¶
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 NewFrom ¶
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 ¶
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.