Documentation
¶
Overview ¶
Package engine is the core of the ApiQube testing platform.
The engine parses declarative test manifests, builds dependency graphs from template references, resolves templates, executes tests through protocol plugins, and emits events to frontends (CLI, desktop, web, SDK).
Engine is stateless after construction — a single Engine instance is safe for concurrent Run() calls. Each Run() creates an isolated execution context with its own event handler, signals, and data flow store.
Basic Usage ¶
eng := engine.New()
results, err := eng.Run(ctx, engine.FromPaths("./tests/"))
if err != nil {
return err
}
fmt.Printf("%d passed, %d failed\n", results.Passed, results.Failed)
With Event Handler ¶
type myHandler struct{}
func (myHandler) Handle(event engine.Event) {
switch e := event.(type) {
case engine.TestCompleted:
fmt.Printf("%s: %s\n", e.Name, e.Status)
}
}
eng := engine.New()
results, err := eng.Run(ctx,
engine.FromPaths("./tests/"),
engine.WithHandler(myHandler{}),
)
Input Sources ¶
Tests can come from multiple sources:
engine.FromPaths("./tests/") // files and directories
engine.FromBytes(yamlBytes) // raw YAML bytes
engine.FromReader(os.Stdin) // any io.Reader
Concurrent Runs ¶
A single Engine can handle multiple concurrent Run() calls safely:
eng := engine.New()
go eng.Run(ctx1, engine.FromPaths("./suite-a/"))
go eng.Run(ctx2, engine.FromPaths("./suite-b/"))
Each run has its own isolated EventHandler, Signals channel, and data flow.
Index ¶
- Constants
- Variables
- func Subscribe[T Event](d *Dispatcher, fn func(T))
- func SubscribePluginEvent(d *Dispatcher, name string, fn func(PluginEvent))
- func SubscribePluginTyped[T any](d *Dispatcher, name string, fn func(T))
- type AssertionResult
- type CheckOption
- type ConfigLoaded
- type Dispatcher
- type Engine
- func (e *Engine) Check(ctx context.Context, input Input, opts ...CheckOption) ([]ValidationError, error)
- func (e *Engine) Close() error
- func (e *Engine) EventSchema(fullyQualifiedName string) *EventSpec
- func (e *Engine) Plugins() []PluginSchema
- func (e *Engine) Run(ctx context.Context, input Input, opts ...RunOption) (*Results, error)
- type Event
- type EventHandler
- type EventSpec
- type FieldSpec
- type FieldType
- type GraphResolved
- type Input
- type NopHandler
- type Option
- type PluginEvent
- type PluginLoaded
- type PluginSchema
- type Progress
- type Protocol
- type RequestData
- type ResponseData
- type Results
- type RunCompleted
- type RunOption
- type RunStarted
- type Severity
- type Signal
- type TemplateError
- type TestCompleted
- type TestResult
- type TestStarted
- type TestStatus
- type ValidationError
- type WaveCompleted
- type WaveResult
- type WaveStarted
Constants ¶
const ( ProtocolHTTP = wire.ProtocolHTTP ProtocolHTTPS = wire.ProtocolHTTPS ProtocolGRPC = wire.ProtocolGRPC ProtocolGRPCS = wire.ProtocolGRPCS ProtocolWS = wire.ProtocolWS ProtocolWSS = wire.ProtocolWSS ProtocolGraphQL = wire.ProtocolGraphQL ProtocolSQL = wire.ProtocolSQL ProtocolKafka = wire.ProtocolKafka ProtocolAMQP = wire.ProtocolAMQP ProtocolRedis = wire.ProtocolRedis )
const ( SeverityError = wire.SeverityError SeverityWarning = wire.SeverityWarning )
const ( FieldString = wire.FieldString FieldNumber = wire.FieldNumber FieldBool = wire.FieldBool FieldObject = wire.FieldObject FieldArray = wire.FieldArray FieldMap = wire.FieldMap FieldAny = wire.FieldAny )
const ( StatusPassed = wire.StatusPassed StatusFailed = wire.StatusFailed StatusSkipped = wire.StatusSkipped StatusErrored = wire.StatusErrored )
const ( SignalPause = wire.SignalPause SignalResume = wire.SignalResume SignalSkipTest = wire.SignalSkipTest )
Variables ¶
var ErrNoInput = errors.New("engine: no input source provided")
ErrNoInput is returned when Run or Check is called without a valid Input source.
Functions ¶
func Subscribe ¶
func Subscribe[T Event](d *Dispatcher, fn func(T))
Subscribe registers a handler for a core engine event type. T must be one of the sealed Event types declared in this package (RunStarted, TestCompleted, WaveCompleted, Progress, ...).
Multiple handlers can subscribe to the same event type — all of them will be invoked in registration order when the event is dispatched.
Example:
d := engine.NewDispatcher()
engine.Subscribe(d, func(e engine.TestCompleted) {
fmt.Println(e.Name, e.Status)
})
func SubscribePluginEvent ¶
func SubscribePluginEvent(d *Dispatcher, name string, fn func(PluginEvent))
SubscribePluginEvent registers a raw handler for a plugin event identified by its fully-qualified name ("<plugin>.<kind>", e.g. "grpc.StreamError").
Use this when you want the full PluginEvent including its Plugin, Kind, and raw Data map — for example, to route events dynamically, log them generically, or handle events whose shape you do not want to model as a Go struct.
Example:
engine.SubscribePluginEvent(d, "grpc.StreamError", func(e engine.PluginEvent) {
log.Printf("%s on stream %v", e.Kind, e.Data["stream_id"])
})
func SubscribePluginTyped ¶
func SubscribePluginTyped[T any](d *Dispatcher, name string, fn func(T))
SubscribePluginTyped registers a typed handler for a plugin event. The consumer defines a Go struct matching the fields declared in the plugin's EventSpec schema; Dispatcher decodes PluginEvent.Data into that struct via JSON round-trip and invokes the handler with the typed value.
Decoding errors are silently dropped — the handler simply does not fire. If you need strict error handling, use SubscribePluginEvent and decode manually, or query the plugin schema via Engine.Plugins() at startup to validate your struct against the declared fields.
Example:
type StreamMessage struct {
StreamID string `json:"stream_id"`
Count int `json:"count"`
Bytes int64 `json:"bytes"`
}
engine.SubscribePluginTyped(d, "grpc.StreamMessageReceived",
func(e StreamMessage) {
metrics.Record(e.StreamID, e.Count)
})
Types ¶
type AssertionResult ¶
type AssertionResult struct {
Expression string `json:"expression"`
Passed bool `json:"passed"`
Expected any `json:"expected,omitempty"`
Actual any `json:"actual,omitempty"`
Message string `json:"message,omitempty"`
}
AssertionResult captures the outcome of a single assertion check.
type CheckOption ¶
type CheckOption func(*checkConfig)
CheckOption configures a single Check() invocation. Check validates manifests without executing them, so it accepts a smaller set of options than Run — only input source, plugins, and config.
func WithCheckConfigPath ¶
func WithCheckConfigPath(path string) CheckOption
WithCheckConfigPath loads a specific .qube.yaml for validation context.
func WithCheckPlugins ¶
func WithCheckPlugins(dir string) CheckOption
WithCheckPlugins sets the plugin directory for this Check, so plugin-specific fields in manifests can be validated.
type ConfigLoaded ¶
type ConfigLoaded struct {
Path string `json:"path"`
Targets []string `json:"targets,omitempty"`
PluginCount int `json:"plugin_count"`
}
ConfigLoaded is emitted after the .qube.yaml config is parsed.
func (ConfigLoaded) Type ¶
func (ConfigLoaded) Type() string
type Dispatcher ¶
type Dispatcher struct {
// contains filtered or unexported fields
}
Dispatcher routes events to handlers registered by type or by plugin event name. It implements EventHandler, so you can pass it to WithHandler() just like any other handler — frontends that need typed subscriptions use Dispatcher, simpler frontends can implement EventHandler directly with a type switch.
Three subscription styles coexist:
- Subscribe[T] — typed handler for a core engine event (T must be Event)
- SubscribePluginEvent — raw handler for a plugin event by fully-qualified name
- SubscribePluginTyped[T] — decodes a plugin event's Data into a user-defined T
Dispatcher is safe for concurrent use — subscriptions and dispatches can happen from any goroutine.
func NewDispatcher ¶
func NewDispatcher() *Dispatcher
NewDispatcher creates an empty Dispatcher with no registered handlers.
func (*Dispatcher) Handle ¶
func (d *Dispatcher) Handle(event Event)
Handle implements EventHandler. It dispatches the event to all registered handlers matching either its Go type or (for plugin events) its fully-qualified name.
type Engine ¶
type Engine struct {
// contains filtered or unexported fields
}
Engine is the core of the ApiQube testing platform.
Engine is stateless after construction — safe for concurrent Run() calls. Configuration set via Options applies to all runs. Per-run configuration (event handler, signals, env overrides) is passed via RunOptions.
func New ¶
New creates a new Engine with the given Options. Defaults: parallel=true, maxConcurrent=GOMAXPROCS, failFast=false.
func (*Engine) Check ¶
func (e *Engine) Check(ctx context.Context, input Input, opts ...CheckOption) ([]ValidationError, error)
Check validates manifests from the given Input source without executing them.
Returns a list of validation errors (parse errors, unresolved alias references, dependency cycles). An empty slice means everything validated.
func (*Engine) Close ¶
Close releases plugin resources held by the engine. Safe to call multiple times. The engine cannot be used after Close.
func (*Engine) EventSchema ¶
EventSchema looks up the schema for a single plugin event by its fully-qualified name ("<plugin>.<kind>").
Returns nil if no loaded plugin declares an event with that name, or if no plugins have been loaded yet.
func (*Engine) Plugins ¶
func (e *Engine) Plugins() []PluginSchema
Plugins returns the metadata of every plugin currently loaded by this engine.
Frontends use this to introspect plugin capabilities at startup: the list of supported protocols, the host capabilities each plugin requires, the available manifest fields, and the catalog of events a plugin can emit.
Returns an empty slice if no plugins have been loaded yet (i.e. before the first Run or Check).
func (*Engine) Run ¶
Run executes tests from the given Input source.
Input is mandatory. Additional RunOptions configure the event handler, signals, environment, etc. Run returns a non-nil *Results in all paths; the error is non-nil only on engine-level failures (parse error, dependency cycle, plugin load failure, ctx cancelled). Individual test failures are reported within Results.
type Event ¶
type Event interface {
Type() string
// contains filtered or unexported methods
}
Event is a sealed interface — only types in this package can implement it.
type EventHandler ¶
type EventHandler interface {
Handle(event Event)
}
EventHandler receives events from the engine during execution. Frontends implement this with a type switch to handle events they care about.
type FieldSpec ¶
FieldSpec describes one field in a plugin contract — used both for manifest fields (what the plugin reads from test cases) and event fields (what the plugin writes into events it emits).
type GraphResolved ¶
type GraphResolved struct {
TotalWaves int `json:"total_waves"`
Dependencies int `json:"dependencies"`
SaveRequirements int `json:"save_requirements"`
Warnings []string `json:"warnings,omitempty"`
}
GraphResolved is emitted after the dependency graph is built, before execution starts.
func (GraphResolved) Type ¶
func (GraphResolved) Type() string
type Input ¶
type Input interface {
// contains filtered or unexported methods
}
Input is a source of test manifests for Run and Check. Create instances via FromPaths, FromBytes, or FromReader.
Input is a sealed interface — only this package can implement it.
func FromBytes ¶
FromBytes loads test manifests from raw YAML bytes. Useful for web UIs where tests are authored in-memory.
func FromPaths ¶
FromPaths loads test manifests from the given file or directory paths. Directories are walked recursively for .yaml and .yml files.
func FromReader ¶
FromReader loads test manifests from any io.Reader. Useful for piped input or streaming sources.
type Option ¶
type Option func(*Engine)
Option configures the Engine (set once, reused across runs).
func WithFailFast ¶
func WithMaxConcurrent ¶
func WithParallel ¶
func WithPluginDir ¶
type PluginEvent ¶
type PluginEvent struct {
Plugin string `json:"plugin"`
Kind string `json:"kind"`
Data map[string]any `json:"data,omitempty"`
}
PluginEvent is the universal wrapper for events emitted by plugins.
Plugins (WASM or built-in) declare their events via PluginInfo.Events at load time. When a plugin emits an event, the engine builds a PluginEvent with the plugin name, event kind, and typed payload data.
Frontends subscribe via Dispatcher using SubscribePluginEvent (raw) or SubscribePluginTyped (decoded into a user-defined Go struct).
func (PluginEvent) FullName ¶
func (e PluginEvent) FullName() string
FullName returns the fully-qualified event name "<plugin>.<kind>".
func (PluginEvent) Type ¶
func (PluginEvent) Type() string
type PluginLoaded ¶
type PluginLoaded struct {
Name string `json:"name"`
Version string `json:"version"`
Protocols []string `json:"protocols"`
}
PluginLoaded is emitted when a plugin is successfully loaded.
func (PluginLoaded) Type ¶
func (PluginLoaded) Type() string
type PluginSchema ¶
type PluginSchema = wire.PluginSchema
PluginSchema is the subset of PluginInfo exposed to consumers of the engine. Frontends use it to introspect loaded plugins — their protocols, the host capabilities they require, the manifest fields they read, and the events they may emit.
type Progress ¶
type Progress struct {
Completed int `json:"completed"`
Total int `json:"total"`
Wave int `json:"wave"`
}
Progress is emitted periodically to report execution progress.
type Protocol ¶
Protocol identifies a target protocol, derived from the target URL scheme or declared explicitly by a plugin.
Engine treats Protocol as an open set — plugins may register new protocols at load time. The constants below cover common cases but are not exhaustive.
type RequestData ¶
type RequestData struct {
Method string `json:"method,omitempty"`
URL string `json:"url,omitempty"`
Headers map[string]string `json:"headers,omitempty"`
Body any `json:"body,omitempty"`
Metadata map[string]any `json:"metadata,omitempty"`
}
RequestData captures what was sent to the target during a test. Protocol-specific fields go in Metadata.
type ResponseData ¶
type ResponseData struct {
Status any `json:"status"`
Headers map[string]string `json:"headers,omitempty"`
Body any `json:"body,omitempty"`
Duration time.Duration `json:"duration"`
Metadata map[string]any `json:"metadata,omitempty"`
}
ResponseData captures what the target returned. Status is any because HTTP uses int, gRPC uses string, etc.
type Results ¶
type Results struct {
Total int `json:"total"`
Passed int `json:"passed"`
Failed int `json:"failed"`
Skipped int `json:"skipped"`
Errored int `json:"errored"`
Duration time.Duration `json:"duration"`
Tests []TestResult `json:"tests"`
Waves []WaveResult `json:"waves"`
}
Results holds the aggregated outcome of a Run(). Frontends use this as the final snapshot after execution.
type RunCompleted ¶
type RunCompleted struct {
Total int `json:"total"`
Passed int `json:"passed"`
Failed int `json:"failed"`
Skipped int `json:"skipped"`
Errored int `json:"errored"`
Duration time.Duration `json:"duration"`
}
RunCompleted is emitted once when all waves have finished.
func (RunCompleted) Type ¶
func (RunCompleted) Type() string
type RunOption ¶
type RunOption func(*runConfig)
RunOption configures a single Run() invocation. RunOptions are isolated per-execution — safe to use with concurrent Run() calls.
func WithConfigPath ¶
WithConfigPath loads a specific .qube.yaml config file for this Run, overriding any engine-level default.
func WithEnv ¶
WithEnv overrides environment variables for this Run. Values are accessible in templates via {{ env.KEY }}.
func WithHandler ¶
func WithHandler(h EventHandler) RunOption
WithHandler sets the EventHandler for this Run. If not set, events are discarded via NopHandler.
func WithPlugins ¶
WithPlugins sets the plugin directory for this Run, overriding any engine-level default. Useful for multi-tenant cloud deployments.
func WithSignals ¶
WithSignals provides a channel for sending control signals (pause, resume, skip) to the engine during execution.
type RunStarted ¶
type RunStarted struct {
Files []string `json:"files"`
TotalTests int `json:"total_tests"`
TotalWaves int `json:"total_waves"`
}
RunStarted is emitted once when a Run() begins, after parsing and graph building.
func (RunStarted) Type ¶
func (RunStarted) Type() string
type TemplateError ¶
type TemplateError struct {
File string `json:"file"`
Test string `json:"test"`
Expression string `json:"expression"`
Message string `json:"message"`
}
TemplateError is emitted when a template reference cannot be resolved.
func (TemplateError) Type ¶
func (TemplateError) Type() string
type TestCompleted ¶
type TestCompleted struct {
TestResult
}
TestCompleted is emitted after a single test case completes.
func (TestCompleted) Type ¶
func (TestCompleted) Type() string
type TestResult ¶
type TestResult struct {
Name string `json:"name"`
File string `json:"file"`
Protocol Protocol `json:"protocol"`
Target string `json:"target,omitempty"`
Status TestStatus `json:"status"`
Duration time.Duration `json:"duration"`
Request *RequestData `json:"request,omitempty"`
Response *ResponseData `json:"response,omitempty"`
Assertions []AssertionResult `json:"assertions,omitempty"`
Error string `json:"error,omitempty"`
}
TestResult is the complete record of a single test case execution. The TestCompleted event embeds this type, so any field added here is automatically available on the event.
type TestStarted ¶
type TestStarted struct {
Name string `json:"name"`
File string `json:"file"`
Protocol Protocol `json:"protocol"`
Target string `json:"target"`
}
TestStarted is emitted before a single test case runs.
func (TestStarted) Type ¶
func (TestStarted) Type() string
type TestStatus ¶
type TestStatus = wire.TestStatus
TestStatus represents the outcome of a test case.
type ValidationError ¶
type ValidationError = wire.ValidationError
ValidationError describes a problem found during manifest validation.
type WaveCompleted ¶
type WaveCompleted struct {
WaveResult
}
WaveCompleted is emitted after a parallel wave has finished.
func (WaveCompleted) Type ¶
func (WaveCompleted) Type() string
type WaveResult ¶
type WaveResult struct {
Index int `json:"index"`
Parallel bool `json:"parallel"`
Tests []TestResult `json:"tests"`
Duration time.Duration `json:"duration"`
Passed int `json:"passed"`
Failed int `json:"failed"`
Skipped int `json:"skipped"`
}
WaveResult is the complete record of a single execution wave. The WaveCompleted event embeds this type.
type WaveStarted ¶
type WaveStarted struct {
Index int `json:"index"`
TestNames []string `json:"test_names"`
Parallel bool `json:"parallel"`
}
WaveStarted is emitted before a parallel wave begins executing.
func (WaveStarted) Type ¶
func (WaveStarted) Type() string
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
internal
|
|
|
assertion
Package assertion evaluates expect expressions against test responses.
|
Package assertion evaluates expect expressions against test responses. |
|
dataflow
Package dataflow manages data passing between test cases at runtime.
|
Package dataflow manages data passing between test cases at runtime. |
|
graph
Package graph builds and analyzes the dependency graph between test cases.
|
Package graph builds and analyzes the dependency graph between test cases. |
|
parser
Package parser loads and normalizes test manifests from YAML sources.
|
Package parser loads and normalizes test manifests from YAML sources. |
|
plugin/capabilities
Package capabilities provides the host-side functions WASM plugins import to interact with the outside world.
|
Package capabilities provides the host-side functions WASM plugins import to interact with the outside world. |
|
runner
Package runner executes parsed test plans by coordinating all engine subsystems.
|
Package runner executes parsed test plans by coordinating all engine subsystems. |
|
template
Package template implements the ApiQube template DSL.
|
Package template implements the ApiQube template DSL. |
|
wire
Package wire holds the value types shared between the public engine package and internal subsystems (plugin, runner).
|
Package wire holds the value types shared between the public engine package and internal subsystems (plugin, runner). |