fluid-core/conformance/suite/harness.go
tegwick 55363905bc
Some checks failed
ci / build (push) Has been cancelled
Add the conformance suite, echo fixture and integration guide
Completes FLUID-WP-0007. The seven minimal-conformance requirements and
the mechanically checkable architectural invariants are asserted as
tests rather than claimed in a README, because a conformance claim
nobody re-checks is one that quietly stops being true. Only the
checkable subset of the invariants is asserted; pretending a test can
settle the rest would be worse than leaving them to review.

TestFirstVerticalSlice runs all eleven steps of Blueprint 50 with no
human steps: two revisions, explicit routing, telemetry, a cohort
dimension, detected pressure, a hypothesis, a candidate, a 90/10
experiment, fitness comparison, promotion, and a complete audit trail.
Requests per completed task fall from 5.65 to 1.00 against a 1.20
target. A companion test runs the loop twice and requires the same
verdict, since a loop whose conclusion depended on run order would be
measuring the harness rather than the interface.

The failure-containment matrix covers Blueprint 34 directly: the data
plane keeps serving with the evidence store closed, with telemetry
wedged against a sink that never returns, after a failed build, after an
experiment rollback, and with the adaptive concurrency limit saturated.

Fixes a real bug the suite exposed. Drain closed the emitter outright,
so every request after the first flush emitted into a dead emitter and
was silently lost -- the kind of fault that makes a later measurement
quietly wrong rather than loudly broken. Emitter.Flush now waits for
delivery without stopping it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014KmVxhJ35tCo7rE7UnLwWu

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 1116572@bnt-lap001
Assistant-Session: 8ba9bb93-a72a-4883-b189-2499cce5c400
2026-09-04 08:21:49 +02:00

310 lines
9.7 KiB
Go

// Package suite is the FLUID conformance harness.
//
// It asserts the seven minimal-conformance requirements of
// FluidAPIStandards.md section 36 and the mechanically checkable subset of the
// sixteen architectural invariants in ArchitectureBlueprint.md section 55.
//
// Everything here runs with no external services and no human steps. A
// conformance claim that needed someone to run through it by hand would be a
// claim nobody re-checks after the first time.
package suite
import (
"context"
"crypto/ed25519"
"fmt"
"net/http"
"net/http/httptest"
"path/filepath"
"testing"
"time"
_ "modernc.org/sqlite"
echo "github.com/tegwick/fluid-core/examples/echo-interface"
"github.com/tegwick/fluid-core/internal/contract"
"github.com/tegwick/fluid-core/internal/evidence"
"github.com/tegwick/fluid-core/internal/intent"
"github.com/tegwick/fluid-core/internal/observation"
"github.com/tegwick/fluid-core/internal/policy"
"github.com/tegwick/fluid-core/internal/publish"
"github.com/tegwick/fluid-core/internal/runtime"
"github.com/tegwick/fluid-core/internal/science"
"github.com/tegwick/fluid-core/internal/signing"
"github.com/tegwick/fluid-core/internal/validate"
)
// Interface is the fixture's interface identifier.
const Interface contract.InterfaceID = "echo-interface"
// IntentDocument is the fixture's governing intent.
const IntentDocument = `# Interface Evolution Intent — echo-interface
**Current operational authority mode:**
FLUID-4
## Mission
Serve entries to consumers, and learn from how they ask for them.
`
// Operator is the fixture's human actor.
var Operator = contract.Actor{Type: contract.ActorTypeHuman, ID: "conformance"}
// Harness is a fully wired FLUID deployment over the echo fixture.
type Harness struct {
T *testing.T
Store *evidence.SQLStore
Intents *intent.Store
Registry *runtime.Registry
Gateway *runtime.Gateway
Emitter *runtime.Emitter
Ingest *observation.Ingest
Pipeline *publish.Pipeline
Validator *validate.OpenAPIValidator
Signer *signing.Signer
PublicKey ed25519.PublicKey
Hypotheses *science.HypothesisStore
Experiments *science.ExperimentController
adapters map[contract.RevisionID]*httptest.Server
}
// New builds a harness with both fixture revisions published and R-1 stable.
func New(t *testing.T) *Harness {
t.Helper()
ctx := context.Background()
store, err := evidence.OpenSQLite(ctx, filepath.Join(t.TempDir(), "conformance.db"))
if err != nil {
t.Fatalf("open evidence store: %v", err)
}
t.Cleanup(func() { _ = store.Close() })
intents := intent.New(store, Interface)
if _, err := intents.Put(ctx, "IEI-1", IntentDocument); err != nil {
t.Fatalf("record intent: %v", err)
}
if err := intents.SetActive(ctx, "IEI-1"); err != nil {
t.Fatalf("activate intent: %v", err)
}
signer, pub, err := signing.GenerateKey("conformance-key")
if err != nil {
t.Fatal(err)
}
limits := policy.DefaultLimits()
// The fixture's intent grants FLUID-4, and the loop under test promotes a
// contract adaptation, so the fixture permits that class explicitly.
limits.AllowedAdaptationClasses = append(limits.AllowedAdaptationClasses,
contract.AdaptationClassContract)
limits.RequiredMode = intent.ModeExperimental
gate := policy.NewGate(limits)
pipeline, err := publish.New(publish.Options{
Gate: gate, Signer: signer, Store: store, Intents: intents,
})
if err != nil {
t.Fatal(err)
}
salt := []byte("conformance-salt-of-sufficient-length")
ingest, err := observation.NewIngest(store, Interface, observation.DefaultRedactionPolicy(salt))
if err != nil {
t.Fatal(err)
}
h := &Harness{
T: t, Store: store, Intents: intents, Pipeline: pipeline,
Validator: validate.NewOpenAPIValidator(), Signer: signer, PublicKey: pub,
Ingest: ingest,
adapters: map[contract.RevisionID]*httptest.Server{},
}
// A verifying registry: the router must accept only signed descriptors.
h.Registry = runtime.NewVerifiedRegistry(Interface,
signing.NewVerifier(map[string]ed25519.PublicKey{"conformance-key": pub}))
h.adapters["R-1"] = httptest.NewServer(echo.NewR1())
h.adapters["R-2"] = httptest.NewServer(echo.NewR2())
t.Cleanup(func() {
for _, s := range h.adapters {
s.Close()
}
})
h.Emitter = runtime.NewEmitter(ingest, runtime.EmitterOptions{Buffer: 8192, Workers: 2})
gw, err := runtime.NewGateway(runtime.GatewayOptions{
Interface: Interface,
Registry: h.Registry,
Resolver: runtime.NewResolver(h.Registry, true),
Connector: runtime.NewConnector(),
Emitter: h.Emitter,
Cohorts: observation.NewCohortEngine("agents", observation.DefaultRedactionPolicy(salt)),
Response: runtime.ResponsePolicy{FeedbackPath: "/v1/feedback"},
Validator: h.Validator,
})
if err != nil {
t.Fatal(err)
}
h.Gateway = gw
h.Hypotheses = science.NewHypothesisStore(store, Interface)
h.Experiments = science.NewExperimentController(store, h.Hypotheses, Interface)
h.PublishRevision("R-1", "examples/echo-interface/r1.openapi.yaml", nil)
h.PublishRevision("R-2", "examples/echo-interface/r2.openapi.yaml",
[]contract.AdaptationClass{contract.AdaptationClassContract})
h.InstallPolicy(1, "R-1", nil)
return h
}
// PublishRevision takes a revision through the full pipeline and registers it.
func (h *Harness) PublishRevision(id contract.RevisionID, contractPath string, classes []contract.AdaptationClass) contract.Revision {
h.T.Helper()
ctx := context.Background()
raw := readRepoFile(h.T, contractPath)
digest := signing.Digest(raw)
if err := h.Validator.Register(digest, raw); err != nil {
h.T.Fatalf("register contract for %s: %v", id, err)
}
pc := contract.RevisionPolicyPolicyCheckPassed
descriptor := contract.Revision{
SchemaVersion: "0.1",
ID: id,
Interface: Interface,
State: contract.RevisionStateStable,
Contract: contract.RevisionContract{
Type: contract.RevisionContractTypeOpenapi,
Digest: digest,
Source: contractPath,
},
Runtime: contract.RevisionRuntime{Upstream: h.adapters[id].URL},
Intent: contract.RevisionIntent{Version: "IEI-1"},
Policy: contract.RevisionPolicy{
Compatibility: contract.RevisionPolicyCompatibilityAdditive,
SecurityCheck: contract.RevisionPolicySecurityCheckPassed,
PolicyCheck: &pc,
},
}
if classes == nil {
classes = []contract.AdaptationClass{contract.AdaptationClassPresentation}
}
verified, report, err := h.Pipeline.Run(ctx, publish.NewCandidate(descriptor, Operator),
publish.PromotionRequest{
AdaptationClasses: classes,
ComplexityDelta: 0.2,
RequestedTrafficShare: 0.2,
Approved: true,
ApprovedBy: &Operator,
})
if err != nil {
h.T.Fatalf("publish %s: %v (%+v)", id, err, report.Stages)
}
if err := h.Pipeline.Publish(ctx, verified); err != nil {
h.T.Fatalf("record %s: %v", id, err)
}
if err := h.Registry.PutRevision(verified.Descriptor()); err != nil {
h.T.Fatalf("register %s: %v", id, err)
}
return verified.Descriptor()
}
// InstallPolicy loads a routing policy into the registry.
func (h *Harness) InstallPolicy(generation int64, defaultRevision contract.RevisionID, rules []contract.RoutingPolicyRulesItem) {
h.T.Helper()
if rules == nil {
rules = []contract.RoutingPolicyRulesItem{}
}
if err := h.Registry.PutPolicy(contract.RoutingPolicy{
SchemaVersion: "0.1",
Interface: Interface,
Generation: generation,
DefaultRevision: defaultRevision,
Rules: rules,
}); err != nil {
h.T.Fatalf("install policy generation %d: %v", generation, err)
}
}
// Call issues a request through the gateway as a named consumer.
func (h *Harness) Call(consumer, method, target string) *httptest.ResponseRecorder {
h.T.Helper()
req := httptest.NewRequest(method, target, nil)
req.Header.Set("X-FLUID-Consumer", consumer)
rec := httptest.NewRecorder()
h.Gateway.ServeHTTP(rec, req)
return rec
}
// CallPinned issues a request pinned to a specific revision.
func (h *Harness) CallPinned(consumer string, rev contract.RevisionID, method, target string) *httptest.ResponseRecorder {
h.T.Helper()
req := httptest.NewRequest(method, target, nil)
req.Header.Set("X-FLUID-Consumer", consumer)
req.Header.Set("X-FLUID-Revision", string(rev))
rec := httptest.NewRecorder()
h.Gateway.ServeHTTP(rec, req)
return rec
}
// Drain flushes buffered telemetry so assertions see every event.
//
// It flushes rather than closes: the harness keeps serving after a drain, and
// closing here would mean every later request emitted into a dead emitter.
func (h *Harness) Drain() {
if !h.Emitter.Flush(5 * time.Second) {
h.T.Fatal("telemetry did not reach the store within 5s")
}
}
// Telemetry returns everything recorded for the interface.
func (h *Harness) Telemetry() []contract.FluidTelemetry {
h.T.Helper()
rows, err := h.Store.Telemetry(context.Background(),
evidence.TelemetryFilter{InterfaceID: Interface})
if err != nil {
h.T.Fatal(err)
}
return rows
}
// Events returns audit events for an entity.
func (h *Harness) Events(entityID string) []contract.FluidEvent {
h.T.Helper()
events, err := h.Store.Events(context.Background(), evidence.EventFilter{EntityID: entityID})
if err != nil {
h.T.Fatal(err)
}
return events
}
// AdapterURL returns a fixture adapter's address.
func (h *Harness) AdapterURL(rev contract.RevisionID) string {
return h.adapters[rev].URL
}
// StopAdapter kills a fixture adapter, for failure-containment tests.
func (h *Harness) StopAdapter(rev contract.RevisionID) {
h.adapters[rev].Close()
}
// mustStatus fails unless the response carries the expected status.
func mustStatus(t *testing.T, rec *httptest.ResponseRecorder, want int, context string) {
t.Helper()
if rec.Code != want {
t.Fatalf("%s: status %d, want %d; body %s", context, rec.Code, want, rec.Body.String())
}
}
var _ = fmt.Sprintf
var _ = http.MethodGet