Documentation
¶
Index ¶
- func RegisterTriggerAdapter(adapter TriggerAdapter)
- func RunAllYAMLTests(t *testing.T, dir string)
- func RunYAMLTests(t *testing.T, testFilePath string)
- func UnregisterTriggerAdapter(name string)
- type Assertion
- type Call
- type ExecOption
- type FixtureDef
- type Harness
- func (h *Harness) BaseURL() string
- func (h *Harness) DELETE(path string, opts ...RequestOption) *Result
- func (h *Harness) ExecutePipeline(name string, data map[string]any) *Result
- func (h *Harness) ExecutePipelineOpts(name string, data map[string]any, opts ...ExecOption) *Result
- func (h *Harness) FireEvent(topic string, data map[string]any) *Result
- func (h *Harness) FireSchedule(pipelineName string, params map[string]any) *Result
- func (h *Harness) GET(path string, opts ...RequestOption) *Result
- func (h *Harness) InjectTrigger(name, event string, data map[string]any) *Result
- func (h *Harness) POST(path, body string, opts ...RequestOption) *Result
- func (h *Harness) PUT(path, body string, opts ...RequestOption) *Result
- func (h *Harness) RecordStep(stepType string) *Recorder
- func (h *Harness) State() *StateStore
- func (h *Harness) WSDialer(path string) (*websocket.Conn, *http.Response, error)
- type MockConfig
- type MockModule
- type Option
- type Recorder
- type RequestOption
- type ResponseAssert
- type Result
- type SequenceStep
- type StateConfig
- type StateStore
- func (s *StateStore) Assert(store string, expected map[string]any) error
- func (s *StateStore) Get(store, key string) (any, bool)
- func (s *StateStore) GetAll(store string) map[string]any
- func (s *StateStore) LoadFixture(path string, store string) error
- func (s *StateStore) Seed(store string, data map[string]any)
- func (s *StateStore) Set(store, key string, value any)
- type StepContext
- type StepHandler
- type StepHandlerFunc
- type TestCase
- type TestFile
- type TriggerAdapter
- type TriggerAdapterFunc
- type TriggerDef
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func RegisterTriggerAdapter ¶
func RegisterTriggerAdapter(adapter TriggerAdapter)
RegisterTriggerAdapter registers a TriggerAdapter by name. Call this from plugin init() or TestMain to make the adapter available to all harnesses.
func RunAllYAMLTests ¶
RunAllYAMLTests discovers all *_test.yaml files in dir and runs them.
func RunYAMLTests ¶
RunYAMLTests parses a *_test.yaml file and runs each test case as a subtest. testFilePath may be absolute or relative to the working directory.
func UnregisterTriggerAdapter ¶
func UnregisterTriggerAdapter(name string)
UnregisterTriggerAdapter removes a previously registered adapter. Useful in test cleanup to avoid cross-test pollution.
Types ¶
type Assertion ¶
type Assertion struct {
// Step scopes this assertion to a specific step's output.
// If empty, the assertion applies to the overall pipeline output.
Step string `yaml:"step"`
// Output checks that all key/value pairs in this map appear in the output.
Output map[string]any `yaml:"output"`
// Executed checks whether the named step ran (true) or was skipped (false).
Executed *bool `yaml:"executed"`
// Response checks HTTP response fields (status, body).
Response *ResponseAssert `yaml:"response"`
// State checks per-store key/value pairs in the StateStore.
// Maps store_name → key → expected_value.
// Requires WithState() (or state config in YAML).
State map[string]map[string]any `yaml:"state"`
}
Assertion is one check applied to the test result.
type ExecOption ¶
type ExecOption func(*execConfig)
ExecOption configures a single pipeline execution (used by ExecutePipelineOpts).
func StopAfter ¶
func StopAfter(stepName string) ExecOption
StopAfter halts pipeline execution after the named step completes. Steps after the named step are not executed.
type FixtureDef ¶ added in v0.3.57
type FixtureDef struct {
// File is the path to the fixture file (JSON or YAML).
File string `yaml:"file"`
// Target is the store name to seed.
Target string `yaml:"target"`
}
FixtureDef loads a JSON or YAML file into a named state store.
type Harness ¶
type Harness struct {
// contains filtered or unexported fields
}
Harness is an in-process workflow engine for integration testing.
func (*Harness) BaseURL ¶
BaseURL returns the base URL of the test HTTP server (e.g. "http://127.0.0.1:PORT"). Panics via t.Fatal if WithServer() was not used.
func (*Harness) DELETE ¶
func (h *Harness) DELETE(path string, opts ...RequestOption) *Result
DELETE sends a DELETE request to the harness HTTP handler. Requires an http.router module in the config.
func (*Harness) ExecutePipeline ¶
ExecutePipeline runs a named pipeline with the given trigger data. StepResults in the returned Result is populated with per-step outputs.
func (*Harness) ExecutePipelineOpts ¶
ExecutePipelineOpts runs a named pipeline with optional per-execution options. It supports StopAfter to halt execution at a named step.
func (*Harness) FireEvent ¶
FireEvent simulates an eventbus trigger firing for the given topic. It finds all EventBusTrigger instances registered with the engine and invokes their subscriptions synchronously using the provided context, so the pipeline runs inline before FireEvent returns.
Unlike going through the real EventBus pub/sub channel, this bypasses the async dispatch goroutine, making it safe and deterministic for tests.
The engine is started lazily on the first call so that trigger subscriptions are configured. The YAML config must include an eventbus trigger for the topic.
func (*Harness) FireSchedule ¶
FireSchedule simulates a schedule trigger firing for the named pipeline. It calls TriggerWorkflow directly (bypassing cron timing) and returns the pipeline result. params is merged into the trigger data; pass nil for none.
func (*Harness) GET ¶
func (h *Harness) GET(path string, opts ...RequestOption) *Result
GET sends a GET request to the harness HTTP handler and returns the result. Requires an http.router module in the config.
func (*Harness) InjectTrigger ¶
InjectTrigger fires the named trigger adapter with the given event and data, returning the pipeline result. Fails the test if no adapter is registered with that name.
func (*Harness) POST ¶
func (h *Harness) POST(path, body string, opts ...RequestOption) *Result
POST sends a POST request with a body to the harness HTTP handler. Content-Type is set to application/json unless overridden via Header(). Requires an http.router module in the config.
func (*Harness) PUT ¶
func (h *Harness) PUT(path, body string, opts ...RequestOption) *Result
PUT sends a PUT request with a body to the harness HTTP handler. Content-Type is set to application/json unless overridden via Header(). Requires an http.router module in the config.
func (*Harness) RecordStep ¶
RecordStep registers a new Recorder for the given step type and returns it. It overrides any previously registered factory for stepType, including real implementations loaded by plugins. Unlike MockStep (used at construction via New), RecordStep can be called after the harness is created:
h := wftest.New(t, wftest.WithYAML(`...`))
rec := h.RecordStep("step.db_query")
h.ExecutePipeline("fetch", nil)
t.Logf("called %d times", rec.CallCount())
func (*Harness) State ¶ added in v0.3.57
func (h *Harness) State() *StateStore
State returns the in-memory StateStore (only non-nil when WithState() was used). Use it to seed and assert state around pipeline executions.
type MockConfig ¶
MockConfig holds YAML-level step mock declarations. Each entry maps a step type (e.g. "step.db_query") to a fixed output map.
type MockModule ¶
type MockModule struct {
// contains filtered or unexported fields
}
MockModule is a minimal modular.Module that exposes a service in the app's service registry under a given name. Use it so steps that look up a named dependency (e.g. a database connection) find this mock instead of a real module.
func NewMockModule ¶
func NewMockModule(name string, service any) *MockModule
NewMockModule creates a MockModule that registers service under name.
func (*MockModule) Init ¶
func (m *MockModule) Init(app modular.Application) error
Init registers the service in the application's service registry.
func (*MockModule) ProvidesServices ¶
func (m *MockModule) ProvidesServices() []modular.ServiceProvider
ProvidesServices advertises the service for dependency wiring.
func (*MockModule) RequiresServices ¶
func (m *MockModule) RequiresServices() []modular.ServiceDependency
RequiresServices returns nil — mock modules have no dependencies.
type Option ¶
type Option interface {
// contains filtered or unexported methods
}
Option configures a Harness when passed to New. All built-in option constructors (WithYAML, MockStep, etc.) implement this interface. *Recorder also implements Option so RecordStep can be passed directly to New without a separate MockStep call.
func MockStep ¶
func MockStep(stepType string, handler StepHandler) Option
MockStep registers a mock factory for stepType that calls handler on every execution. The mock is installed after built-in plugins load, so it overrides real implementations. Use Returns for fixed output or NewRecorder to capture calls.
func WithConfig ¶
WithConfig loads config from a YAML file path.
func WithMockModule ¶
func WithMockModule(mod *MockModule) Option
WithMockModule registers a fake module in the service registry so steps that look up a named dependency find this mock instead of a real module.
func WithPlugin ¶
func WithPlugin(p plugin.EnginePlugin) Option
WithPlugin loads an additional engine plugin into the harness before BuildFromConfig.
func WithServer ¶
func WithServer() Option
WithServer starts a real HTTP listener backed by the engine's HTTP router. After harness creation, use h.BaseURL() to get the server URL. The server is stopped automatically via t.Cleanup.
type Recorder ¶
type Recorder struct {
// contains filtered or unexported fields
}
Recorder is a StepHandler that records every call made to a mock step. Use NewRecorder to create one, then pass it to MockStep. Recorder also implements StepHandler and Option so a Recorder returned by RecordStep can be passed directly to New without a separate MockStep call.
func NewRecorder ¶
func NewRecorder() *Recorder
NewRecorder creates a Recorder that returns an empty output map by default.
func RecordStep ¶
RecordStep creates a Recorder bound to stepType and returns it. The returned *Recorder implements Option, so it can be passed directly to New:
rec := wftest.RecordStep("step.db_query")
h := wftest.New(t, wftest.WithYAML(`...`), rec)
h.ExecutePipeline("fetch", nil)
t.Logf("called %d times", rec.CallCount())
type RequestOption ¶
RequestOption configures an HTTP test request.
func Header ¶
func Header(key, value string) RequestOption
Header returns a RequestOption that sets a custom request header.
type ResponseAssert ¶
type ResponseAssert struct {
// Status is the expected HTTP status code (0 means "don't check").
Status int `yaml:"status"`
// Body is a substring expected in the response body.
Body string `yaml:"body"`
}
ResponseAssert checks HTTP response fields.
type Result ¶
type Result struct {
Output map[string]any // Final pipeline output
StepResults map[string]map[string]any // Per-step outputs
Error error
Duration time.Duration
// HTTP-specific (populated for HTTP triggers)
StatusCode int
Headers map[string]string
RawBody []byte
}
Result holds the outcome of a pipeline execution or HTTP request.
func (*Result) StepExecuted ¶
StepExecuted returns whether a step was executed.
func (*Result) StepOutput ¶
StepOutput returns the output map for a specific step.
type SequenceStep ¶ added in v0.3.57
type SequenceStep struct {
// Name is a label shown in test output.
Name string `yaml:"name"`
// Pipeline is a shorthand for a pipeline trigger name.
// If Trigger.Name is empty, Pipeline is used.
Pipeline string `yaml:"pipeline"`
// Trigger describes how to invoke this step.
Trigger TriggerDef `yaml:"trigger"`
// Assertions are checked after this step executes.
Assertions []Assertion `yaml:"assertions"`
}
SequenceStep is one step in a multi-step stateful test.
type StateConfig ¶ added in v0.3.57
type StateConfig struct {
// Fixtures are files to load into named stores.
Fixtures []FixtureDef `yaml:"fixtures"`
// Seed maps store_name → key → value for inline initial state.
Seed map[string]map[string]any `yaml:"seed"`
}
StateConfig describes state to set up before a test case runs.
type StateStore ¶ added in v0.3.57
type StateStore struct {
// contains filtered or unexported fields
}
StateStore provides in-memory state for test pipelines. It is safe for concurrent use.
func NewStateStore ¶ added in v0.3.57
func NewStateStore() *StateStore
NewStateStore creates an empty StateStore.
func (*StateStore) Assert ¶ added in v0.3.57
func (s *StateStore) Assert(store string, expected map[string]any) error
Assert checks that each key/value pair in expected matches the actual state in the named store. Comparison is done via JSON marshaling so numeric types are normalised. Returns nil on full match, or an error describing the first mismatch.
func (*StateStore) Get ¶ added in v0.3.57
func (s *StateStore) Get(store, key string) (any, bool)
Get retrieves a value from a named store.
func (*StateStore) GetAll ¶ added in v0.3.57
func (s *StateStore) GetAll(store string) map[string]any
GetAll returns a shallow copy of all entries in a named store. Returns nil if the store does not exist.
func (*StateStore) LoadFixture ¶ added in v0.3.57
func (s *StateStore) LoadFixture(path string, store string) error
LoadFixture loads state from a JSON or YAML file into a named store. The file must unmarshal to map[string]any.
func (*StateStore) Seed ¶ added in v0.3.57
func (s *StateStore) Seed(store string, data map[string]any)
Seed loads initial state into a named store. Existing keys are overwritten; other keys are preserved.
func (*StateStore) Set ¶ added in v0.3.57
func (s *StateStore) Set(store, key string, value any)
Set writes a value to a named store.
type StepContext ¶
type StepContext struct {
// Ctx is the context from the pipeline execution.
Ctx context.Context
// Config is the step's YAML config from BuildFromConfig.
Config map[string]any
// Input is the merged pipeline state (pc.Current) at execution time.
Input map[string]any
}
StepContext holds the full execution context passed to a mock step handler.
type StepHandler ¶
type StepHandler interface {
Handle(ctx context.Context, config, input map[string]any) (map[string]any, error)
}
StepHandler is invoked by a mock step during pipeline execution. config is the step's YAML config; input is the merged pipeline state (pc.Current).
func Returns ¶
func Returns(output map[string]any) StepHandler
Returns creates a StepHandler that always returns the given output map.
type StepHandlerFunc ¶
type StepHandlerFunc func(ctx context.Context, config, input map[string]any) (map[string]any, error)
StepHandlerFunc adapts a plain function to StepHandler.
type TestCase ¶
type TestCase struct {
// Description is an optional human-readable label shown in test output.
Description string `yaml:"description"`
// Trigger describes how to invoke the pipeline or HTTP endpoint.
// Mutually exclusive with Sequence.
Trigger TriggerDef `yaml:"trigger"`
// StopAfter halts pipeline execution after the named step completes.
StopAfter string `yaml:"stop_after"`
// Mocks overrides the file-level Mocks for this test case only.
Mocks *MockConfig `yaml:"mocks"`
// Assertions is an ordered list of checks applied to the result.
// Used with Trigger; ignored when Sequence is set.
Assertions []Assertion `yaml:"assertions"`
// State configures initial state to seed before the test runs.
State *StateConfig `yaml:"state"`
// Sequence replaces the single Trigger for multi-step stateful tests.
// Each step fires a trigger and may assert pipeline output and state.
Sequence []SequenceStep `yaml:"sequence"`
}
TestCase defines one test within a TestFile.
type TestFile ¶
type TestFile struct {
// Config is a file path to a workflow YAML config.
// Mutually exclusive with YAML.
Config string `yaml:"config"`
// YAML is an inline workflow YAML config string.
// Mutually exclusive with Config.
YAML string `yaml:"yaml"`
// Mocks are step-level mocks shared across all test cases.
// Per-test Mocks override these.
Mocks MockConfig `yaml:"mocks"`
// Tests maps test case names to their definitions.
Tests map[string]TestCase `yaml:"tests"`
}
TestFile is the top-level structure of a *_test.yaml file.
type TriggerAdapter ¶
type TriggerAdapter interface {
// Name returns the unique adapter identifier (e.g. "websocket", "grpc").
Name() string
// Inject simulates a trigger firing and returns the pipeline result.
Inject(ctx context.Context, h *Harness, event string, data map[string]any) (*Result, error)
}
TriggerAdapter allows external plugins to add test injection support for custom trigger types. Register an adapter with RegisterTriggerAdapter, then inject events via Harness.InjectTrigger.
type TriggerAdapterFunc ¶
type TriggerAdapterFunc struct {
AdapterName string
Fn func(ctx context.Context, h *Harness, event string, data map[string]any) (*Result, error)
}
TriggerAdapterFunc adapts a plain function to TriggerAdapter.
func (*TriggerAdapterFunc) Inject ¶
func (f *TriggerAdapterFunc) Inject(ctx context.Context, h *Harness, event string, data map[string]any) (*Result, error)
Inject calls the underlying function.
func (*TriggerAdapterFunc) Name ¶
func (f *TriggerAdapterFunc) Name() string
Name returns the adapter name.
type TriggerDef ¶
type TriggerDef struct {
// Type is the trigger kind: "pipeline", "http".
Type string `yaml:"type"`
// Name is the pipeline name (used when Type is "pipeline").
Name string `yaml:"name"`
// Data is the trigger input data (pipeline trigger data or HTTP body).
Data map[string]any `yaml:"data"`
// Method is the HTTP method (used when Type is "http").
Method string `yaml:"method"`
// Path is the HTTP path (used when Type is "http").
Path string `yaml:"path"`
// Headers are extra HTTP request headers.
Headers map[string]string `yaml:"headers"`
}
TriggerDef describes how to invoke the system under test.