Documentation
¶
Overview ¶
compat/embed.go — embeds the pre-built compatibility UI into the package.
The compat UI must be built before `go build ./cmd/compat`:
cd compat/ui && npm install && npm run build
The resulting compat/ui/dist/ tree is embedded here and exported as UIFS. The compat server serves it at GET /.
Package compat provides shared Go types for the NDJSON wire format emitted by all per-language test suite runners.
Every runner (Node.js, Python, Go, CLI, …) writes one JSON line per event to stdout. The Go runner in runner.go reads these lines, aggregates them, and builds a RunReport for display or further processing.
Package compat provides the runner that orchestrates per-language test suite subprocesses and aggregates their NDJSON output into a RunReport.
Each suite is an executable (or docker image) that writes NDJSON events to stdout. The runner starts each suite subprocess, reads its stdout line by line, and builds a live RunReport. Suite stderr is forwarded to the runner's own stderr as log lines.
Usage:
r := compat.NewRunner(cfg) report, err := r.Run(ctx)
Package compat — HTTP server for the compatibility test dashboard.
The server exposes three endpoints:
GET /events — Server-Sent Events stream of NDJSON test events.
New clients receive a full replay of the current run so
far, then live events as they arrive.
GET /results — The last completed RunReport as indented JSON.
GET / — Embedded static UI files.
Calls Broadcast(raw) for each NDJSON line from the test runner. Call FinishRun(report) once the whole run is done. Call ResetRun() to clear the replay buffer at the start of a new run.
Index ¶
- Variables
- func NewMCPServer(orch *Orchestrator, registryPath, workspaceRoot string, logger *slog.Logger) *intmcp.Server
- type EventType
- type GroupReport
- type Orchestrator
- func (o *Orchestrator) CancelTests(batchID, suite, group, test string, all bool) []QueueEntry
- func (o *Orchestrator) QueueState() []QueueEntry
- func (o *Orchestrator) RegisterSSEClient(ch chan []byte)
- func (o *Orchestrator) ReloadSuite(name string) error
- func (o *Orchestrator) Report() *RunReport
- func (o *Orchestrator) Results(suite, service, group, test, status string) []TestResultEvent
- func (o *Orchestrator) Shutdown()
- func (o *Orchestrator) Start() error
- func (o *Orchestrator) SubmitFailingTests(suiteFilter, serviceFilter string, statuses ...Status) (batchID string, queued []QueueEntry)
- func (o *Orchestrator) SubmitTests(suites []string, tests []TestRef) (batchID string, queued []QueueEntry, skippedDups int)
- func (o *Orchestrator) SuiteStates() []SuiteStatus
- func (o *Orchestrator) UnregisterSSEClient(ch chan []byte)
- type QueueEntry
- type QueuedBatch
- type RawEvent
- type RunConfig
- type RunEndEvent
- type RunFilter
- type RunReport
- type RunStartEvent
- type Runner
- type Server
- func (s *Server) Broadcast(raw []byte)
- func (s *Server) FinishRun(report *RunReport)
- func (s *Server) Handler() http.Handler
- func (s *Server) LoadResultsFile(path string) error
- func (s *Server) ResetRun(suites ...string)
- func (s *Server) SaveResultsFile(path string) error
- func (s *Server) SetOrchestrator(o *Orchestrator)
- func (s *Server) SetRunFunc(fn func(filter RunFilter) error)
- func (s *Server) SetRunning(v bool)
- type Status
- type StdinCommand
- type SuiteConfig
- type SuiteProcess
- type SuiteReport
- type SuiteState
- type SuiteStatus
- type TestRef
- type TestResultEvent
- type TestStartEvent
Constants ¶
This section is empty.
Variables ¶
var UIFS, _ = fs.Sub(rawUIFS, "ui/dist")
UIFS is the embedded compat UI sub-tree, rooted at the dist build output. Served at / by the compat HTTP server.
Functions ¶
func NewMCPServer ¶
func NewMCPServer(orch *Orchestrator, registryPath, workspaceRoot string, logger *slog.Logger) *intmcp.Server
NewMCPServer creates an MCP server that combines generic repo tools with compat-specific orchestration tools.
Types ¶
type EventType ¶
type EventType string
EventType identifies the kind of NDJSON event.
const ( EventRunStart EventType = "run_start" EventSuiteStarting EventType = "suite_starting" // emitted by the runner before a suite subprocess starts EventSuiteError EventType = "suite_error" // emitted when a suite subprocess fails to start or crashes EventTestStart EventType = "test_start" EventTestResult EventType = "test_result" EventRunEnd EventType = "run_end" )
type GroupReport ¶
type GroupReport struct {
Suite string
Service string
Name string
Tests []TestResultEvent
Passed int
Failed int
Skipped int
Unimplemented int
}
GroupReport is the aggregated result of one test group within a suite.
type Orchestrator ¶
type Orchestrator struct {
// Endpoint and Region are injected into suite subprocess environments.
Endpoint string
Region string
// OnIdle is called with an aggregated report each time the last
// outstanding batch completes. Interactive runs have no single end — the
// dashboard submits work whenever the user asks — so "everything queued
// has finished" is the point at which results are worth persisting.
// Optional; set before Start.
OnIdle func(*RunReport)
// contains filtered or unexported fields
}
Orchestrator manages all suite processes for interactive compat testing.
func NewOrchestrator ¶
func NewOrchestrator(ctx context.Context, configs []SuiteConfig, onEvent func([]byte), logger *slog.Logger) *Orchestrator
NewOrchestrator creates a new orchestrator for the given suite configs. onEvent is called with each raw NDJSON event line for SSE broadcast.
func (*Orchestrator) CancelTests ¶
func (o *Orchestrator) CancelTests(batchID, suite, group, test string, all bool) []QueueEntry
CancelTests cancels matching queued/running tests. Supports cancellation by batchID, suite+group+test, or all.
func (*Orchestrator) QueueState ¶
func (o *Orchestrator) QueueState() []QueueEntry
QueueState returns all queued/running items across all suites.
func (*Orchestrator) RegisterSSEClient ¶
func (o *Orchestrator) RegisterSSEClient(ch chan []byte)
RegisterSSEClient adds a channel that will receive copies of raw NDJSON event lines. Used by the MCP SSE endpoint.
func (*Orchestrator) ReloadSuite ¶
func (o *Orchestrator) ReloadSuite(name string) error
ReloadSuite restarts a specific suite process (hot-swap).
func (*Orchestrator) Report ¶
func (o *Orchestrator) Report() *RunReport
Report aggregates every result seen so far into a RunReport — the same shape the batch runner produces, so GET /results, the saved results file, and `--report` all work for dashboard-triggered runs too.
Results accumulate across batches, so this is the full picture, not just the most recent batch. Ordering is deterministic (suite, then group, then test) so a saved file does not churn between identical runs.
func (*Orchestrator) Results ¶
func (o *Orchestrator) Results(suite, service, group, test, status string) []TestResultEvent
Results returns the latest test results, optionally filtered. Pass empty strings to skip a filter dimension.
func (*Orchestrator) Shutdown ¶
func (o *Orchestrator) Shutdown()
Shutdown gracefully stops all suite processes.
func (*Orchestrator) Start ¶
func (o *Orchestrator) Start() error
Start spawns all suite processes, begins reading their stdout, and launches a watchdog goroutine that detects stalled suites.
func (*Orchestrator) SubmitFailingTests ¶
func (o *Orchestrator) SubmitFailingTests(suiteFilter, serviceFilter string, statuses ...Status) (batchID string, queued []QueueEntry)
SubmitFailingTests re-queues all tests whose last result matched one of the given statuses (e.g. "fail", "skip", "unimplemented"). If statuses is empty it defaults to StatusFail. Returns the queued entries so callers can relay them to clients.
func (*Orchestrator) SubmitTests ¶
func (o *Orchestrator) SubmitTests(suites []string, tests []TestRef) (batchID string, queued []QueueEntry, skippedDups int)
SubmitTests queues tests for execution across specified suites. If suites is nil/empty, submits to all suites. Returns batch ID, list of queued items, and count of skipped duplicates.
func (*Orchestrator) SuiteStates ¶
func (o *Orchestrator) SuiteStates() []SuiteStatus
SuiteStates returns the current state of all suites.
func (*Orchestrator) UnregisterSSEClient ¶
func (o *Orchestrator) UnregisterSSEClient(ch chan []byte)
UnregisterSSEClient removes a previously registered SSE channel.
type QueueEntry ¶
type QueueEntry struct {
BatchID string `json:"batch_id"`
Suite string `json:"suite"`
Group string `json:"group"`
Test string `json:"test,omitempty"`
State string `json:"state"` // "queued" or "running"
}
QueueEntry represents a single item in the queue (for API responses).
type QueuedBatch ¶
type QueuedBatch struct {
ID string `json:"batch_id"`
Tests []TestRef `json:"tests"`
CreatedAt time.Time `json:"created_at"`
}
QueuedBatch is a batch of tests waiting to be sent to a suite.
type RawEvent ¶
type RawEvent struct {
Event EventType `json:"event"`
}
RawEvent is used to peek at the "event" field before full unmarshalling.
type RunConfig ¶
type RunConfig struct {
// Endpoint is the Overcast base URL, e.g. "http://localhost:4566".
Endpoint string
// Region is the AWS region to advertise to suite clients.
Region string
// Suites lists which suites to run. An empty slice runs all registered suites.
Suites []string
// Service filters runs to a single AWS service (e.g. "s3"). Empty = all.
Service string
// Group filters runs to a single test group (e.g. "s3-crud"). Empty = all.
Group string
// Test filters runs to a single test within a group. Empty = all.
// Only meaningful when Group is also set.
Test string
// TestPairs restricts the run to specific (group, test) pairs.
// Format: ["groupName:testName", ...]. When set, Service/Group/Test filters
// are ignored — the pairs are the authoritative list.
TestPairs []string
// RunID is the unique identifier for this run, injected into all suite
// subprocesses as OVERCAST_COMPAT_RUN_ID. All test resources must be
// prefixed with this ID so the post-run orphan sweep can detect leaks.
// If empty, a random ID is generated in Run().
RunID string
// OnEvent is an optional callback invoked with each raw NDJSON event line
// as it is received from a suite subprocess. The byte slice is a stable
// copy and may be retained by the caller. Invoked from a single goroutine.
OnEvent func(raw []byte)
}
RunConfig controls how the runner executes suites.
type RunEndEvent ¶
type RunEndEvent struct {
Event EventType `json:"event"`
Suite string `json:"suite"`
Passed int `json:"passed"`
Failed int `json:"failed"`
Skipped int `json:"skipped"`
Unimplemented int `json:"unimplemented"`
DurationMS int64 `json:"duration_ms"`
}
RunEndEvent is the last line emitted by a suite runner.
type RunFilter ¶
type RunFilter struct {
Suite string `json:"suite,omitempty"`
Service string `json:"service,omitempty"`
Group string `json:"group,omitempty"`
Test string `json:"test,omitempty"`
// Statuses, when non-empty, restricts the run to tests whose result in the
// last run matched one of the given statuses (e.g. "fail", "skip",
// "unimplemented"). The server expands this to TestPairs before calling
// the run function.
Statuses []string `json:"statuses,omitempty"`
// TestPairs is set internally (not decoded from JSON) when Statuses is
// expanded. Format: ["groupName:testName", ...].
TestPairs []string `json:"-"`
}
RunFilter scopes a re-run to a subset of tests. All fields are optional; zero value means "run everything".
type RunReport ¶
type RunReport struct {
Endpoint string
StartedAt time.Time
FinishedAt time.Time
Suites []*SuiteReport
}
RunReport is the aggregated result of one or more suite runs. Built by runner.go from the streamed NDJSON events.
type RunStartEvent ¶
type RunStartEvent struct {
Event EventType `json:"event"`
Suite string `json:"suite"`
StartedAt time.Time `json:"started_at"`
Endpoint string `json:"endpoint"`
Version string `json:"version"`
TotalTests int `json:"total_tests,omitempty"`
}
RunStartEvent is the first line emitted by a suite runner.
type Runner ¶
type Runner struct {
// contains filtered or unexported fields
}
Runner orchestrates suite subprocesses.
func (*Runner) Run ¶
Run starts each configured suite subprocess in parallel, reads their NDJSON output, and returns an aggregated RunReport. Suites are independent OS processes with no shared state so concurrent execution is safe. OnEvent is called under a mutex so callers receive events from all suites on a single goroutine (same contract as the previous sequential Run).
type Server ¶
type Server struct {
// contains filtered or unexported fields
}
Server is the compatibility test HTTP server. Create via NewServer; all methods are safe for concurrent use.
func NewServer ¶
NewServer creates a Server backed by optional embedded UI files. Pass nil for uiFS to disable static file serving (useful in tests).
func (*Server) Broadcast ¶
Broadcast delivers a raw NDJSON event line to all connected SSE clients and appends it to the replay buffer for clients that connect later. Safe to call from any goroutine.
func (*Server) FinishRun ¶
FinishRun stores the completed RunReport for GET /results responses and broadcasts a run_complete event to all connected SSE clients.
When report only covers a subset of suites (partial re-run), FinishRun merges those results into the existing last report so that GET /results always returns the full picture across all suites, not just the ones that were just re-run.
func (*Server) LoadResultsFile ¶
LoadResultsFile reads a previously saved results file and pre-populates the GET /results response so the dashboard shows the last run immediately after a server restart, before any new run has been performed.
func (*Server) ResetRun ¶
ResetRun prepares for a new test run.
suites lists which suite names are about to be re-run. If empty, all suites are reset (full re-run). For a partial re-run (e.g. just "node-js-sdk"), pass only those suite names so the results for other suites are preserved in the replay buffer and remain visible in the UI while the new run proceeds.
ResetRun broadcasts a run_reset event to all live clients so the UI can mark the affected suites' data as stale while preserving the rest.
func (*Server) SaveResultsFile ¶
SaveResultsFile writes the last completed RunReport to path so it survives a server restart. The file is written atomically via a temp-file rename.
func (*Server) SetOrchestrator ¶
func (s *Server) SetOrchestrator(o *Orchestrator)
SetOrchestrator attaches the interactive-mode orchestrator to the server. When set, POST /run delegates to the orchestrator instead of the legacy runFn, and the new /suites, /queue, /cancel, /registry endpoints become available.
func (*Server) SetRunFunc ¶
SetRunFunc registers the function the server calls when POST /run is received. fn is invoked in a new goroutine. It must call ResetRun(), Broadcast(), and FinishRun() itself (the main run loop does this naturally). Only one run at a time is allowed; POST /run returns 409 if one is already in progress.
func (*Server) SetRunning ¶
SetRunning marks the server as running or idle. Called by the run function before and after a run so POST /run can enforce single-concurrency.
type Status ¶
type Status string
Status is the outcome of a single test.
const ( StatusPass Status = "pass" StatusFail Status = "fail" StatusSkip Status = "skip" // StatusUnimplemented indicates the endpoint returned HTTP 501. // The feature gap is known and expected; this is distinct from a real // failure (wrong response, assertion error, SDK crash). StatusUnimplemented Status = "unimplemented" // StatusNA indicates the AWS SDK client used by this suite does not yet // expose this operation. It is NOT an Overcast gap and NOT a suite // authoring gap — simply that the SDK library has no API for it yet. // NA results are excluded from all pass-rate calculations. StatusNA Status = "na" )
type StdinCommand ¶
type StdinCommand struct {
Command string `json:"command"`
BatchID string `json:"batch_id,omitempty"`
Tests []TestRef `json:"tests,omitempty"`
Group string `json:"group,omitempty"`
Test string `json:"test,omitempty"`
}
StdinCommand is a JSON command sent to a suite process via stdin.
type SuiteConfig ¶
type SuiteConfig struct {
// Name is the suite identifier, e.g. "node-js-sdk".
Name string
// Argv is the command + arguments to run.
// The first element is the executable; the rest are arguments.
// The executable is looked up on PATH.
Argv []string
// Env is additional environment variables (KEY=VALUE).
// OVERCAST_ENDPOINT and OVERCAST_DEFAULT_REGION are always injected by the runner.
Env []string
// Dir is the working directory for the subprocess.
// If empty, the runner's working directory is used.
Dir string
// Interactive indicates this suite supports the interactive NDJSON
// stdin/stdout protocol (building → ready → run commands).
// Suites without this flag are skipped by the orchestrator.
Interactive bool
}
SuiteConfig describes a single test suite subprocess.
func DefaultSuiteConfigs ¶
func DefaultSuiteConfigs(endpoint, region string) []SuiteConfig
DefaultSuiteConfigs returns the built-in suite configuration list with endpoint and region injected into each suite's environment. This is the public entry point for callers (e.g. cmd/compat interactive mode) that need to construct suite configs without creating a full Runner.
func FilterSuiteConfigs ¶
func FilterSuiteConfigs(all []SuiteConfig, names []string) []SuiteConfig
FilterSuiteConfigs filters a list of suite configs to only those whose Name appears in names. Used by cmd/compat to narrow the default configs by --suite.
type SuiteProcess ¶
type SuiteProcess struct {
Name string
Config SuiteConfig
State SuiteState
Cmd *exec.Cmd
Queue []QueuedBatch
ActiveBatch *QueuedBatch
RunningTest string // "group:test" currently executing
PendingBuffer []StdinCommand
LastEventAt time.Time // last time any stdout event was received
PingSentAt time.Time // last time a ping command was sent
CancelSentAt time.Time // last time a cancel command was sent for a stuck test
Interactive bool // true if the suite emitted a 'ready' event
// contains filtered or unexported fields
}
SuiteProcess manages a single long-lived suite runner process.
type SuiteReport ¶
type SuiteReport struct {
Suite string
Groups []*GroupReport
Passed int
Failed int
Skipped int
Unimplemented int
}
SuiteReport is the aggregated result of a single suite (e.g. node-js-sdk).
func (*SuiteReport) PassRate ¶
func (s *SuiteReport) PassRate() float64
PassRate returns the pass rate as a value in [0, 1]. Returns 0 for empty suites. Unimplemented tests are excluded from both numerator and denominator — they represent known gaps, not implementation quality.
func (*SuiteReport) Services ¶
func (s *SuiteReport) Services() []string
Services returns a deduplicated list of service names tested in this suite.
func (*SuiteReport) Total ¶
func (s *SuiteReport) Total() int
Total returns the total number of tests in this suite.
type SuiteState ¶
type SuiteState string
SuiteState represents the current lifecycle state of a suite process.
const ( SuiteBuilding SuiteState = "building" SuiteReady SuiteState = "ready" SuiteBusy SuiteState = "busy" SuiteError SuiteState = "error" SuiteStopped SuiteState = "stopped" )
type SuiteStatus ¶
type SuiteStatus struct {
Name string `json:"name"`
State SuiteState `json:"state"`
QueuedCount int `json:"queued_count"`
RunningTest string `json:"running_test,omitempty"`
}
SuiteStatus is the API response for suite state.
type TestRef ¶
type TestRef struct {
Group string `json:"group"`
Tests []string `json:"tests,omitempty"` // if nil, all tests in group
}
TestRef identifies a specific test or group of tests.
type TestResultEvent ¶
type TestResultEvent struct {
Event EventType `json:"event"`
Suite string `json:"suite"`
Service string `json:"service"`
Group string `json:"group"`
Test string `json:"test"`
// Op is the AWS API operation name used for documentation links.
// Empty string disables the doc link. When absent, Test is used.
Op string `json:"op,omitempty"`
Status Status `json:"status"`
DurationMS int64 `json:"duration_ms"`
Error string `json:"error,omitempty"`
}
TestResultEvent is emitted once per test, immediately after it completes.
type TestStartEvent ¶
type TestStartEvent struct {
Event EventType `json:"event"`
Suite string `json:"suite"`
Service string `json:"service"`
Group string `json:"group"`
Test string `json:"test"`
}
TestStartEvent is emitted once per test, immediately before it begins executing. Consumers use this to show a test as "running" while awaiting the result.