harness

package
v0.6.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 3, 2026 License: MIT Imports: 25 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func ConfigureForCI

func ConfigureForCI(opts *Options)

ConfigureForCI adjusts options for CI environment

func FindRealBinary

func FindRealBinary(toolName string) (string, error)

FindRealBinary finds the path of a real ecosystem tool. It prioritizes an environment variable override before falling back to `grove dev current`.

func GetCIMetadata

func GetCIMetadata() map[string]string

GetCIMetadata returns CI-specific metadata

func IsCI

func IsCI() bool

IsCI returns true if running in any CI environment

func SetupCIEnvironment

func SetupCIEnvironment()

SetupCIEnvironment configures environment for CI

Types

type AssertionResult

type AssertionResult struct {
	Description string `json:"description"`
	Success     bool   `json:"success"`
	Error       string `json:"error,omitempty"`
}

AssertionResult holds the outcome of a single check within a step.

type CIProvider

type CIProvider string

CIProvider represents different CI environments

const (
	CIProviderUnknown       CIProvider = "unknown"
	CIProviderGitHubActions CIProvider = "github-actions"
	CIProviderJenkins       CIProvider = "jenkins"
	CIProviderCircleCI      CIProvider = "circleci"
	CIProviderGitLab        CIProvider = "gitlab"
)

func DetectCIProvider

func DetectCIProvider() CIProvider

DetectCIProvider detects the current CI environment

type ContainerInfo

type ContainerInfo struct {
	Image   string
	Created string
	Names   string
}

ContainerInfo holds simplified container information

func GetContainerSnapshot

func GetContainerSnapshot(filter string) ([]ContainerInfo, error)

GetContainerSnapshot returns a current snapshot of containers

type ContainerMonitor

type ContainerMonitor struct {
	// contains filtered or unexported fields
}

ContainerMonitor monitors Docker containers during test execution

func NewContainerMonitor

func NewContainerMonitor(filter string, interval time.Duration) *ContainerMonitor

NewContainerMonitor creates a new container monitor

func (*ContainerMonitor) Start

func (m *ContainerMonitor) Start(updateFunc func(containers []ContainerInfo))

Start begins monitoring containers

func (*ContainerMonitor) Stop

func (m *ContainerMonitor) Stop()

Stop stops monitoring containers

type Context

type Context struct {
	// Core paths
	RootDir     string // Root temporary directory for the scenario
	ProjectRoot string // Root of the project being tested
	GroveBinary string // Path to the grove binary under test
	TestID      string // Unique ID for this test run

	UseRealDeps map[string]bool // Map of dependencies to use real binaries for
	// contains filtered or unexported fields
}

Context carries state through a scenario execution

func (*Context) AddAssertion

func (c *Context) AddAssertion(description string, err error)

AddAssertion logs a new assertion result for the current step.

func (*Context) Bin

func (c *Context) Bin(args ...string) *command.Command

Bin creates a new command using the project binary under test. This is a convenience wrapper around Command() that automatically uses the binary path from grove.yml (stored in c.GroveBinary).

Example:

cmd := ctx.Bin("plan", "init", "my-plan")  // instead of ctx.Command(flowBinary, "plan", "init", "my-plan")

func (*Context) CacheDir

func (c *Context) CacheDir() string

CacheDir returns the path to the sandboxed XDG_CACHE_HOME directory.

func (*Context) Check

func (c *Context) Check(description string, err error) error

Check performs a hard assertion. It logs the result and returns an error immediately if the assertion fails, stopping the current step.

func (*Context) Command

func (c *Context) Command(program string, args ...string) *command.Command

Command creates a new command with the test's mock-aware PATH.

func (*Context) CommandExecutor

func (c *Context) CommandExecutor() corecommand.Executor

CommandExecutor creates a new TestExecutor configured with the current context. This is the preferred way to get a command executor within a test step, as it encapsulates all necessary test environment setup.

func (*Context) ConfigDir

func (c *Context) ConfigDir() string

ConfigDir returns the path to the sandboxed XDG_CONFIG_HOME directory.

func (*Context) DataDir

func (c *Context) DataDir() string

DataDir returns the path to the sandboxed XDG_DATA_HOME directory.

func (*Context) Dir

func (c *Context) Dir(name string) string

Dir retrieves a previously created named directory

func (*Context) Get

func (c *Context) Get(key string) interface{}

Get retrieves a stored value

func (*Context) GetBool

func (c *Context) GetBool(key string) bool

GetBool retrieves a stored bool value

func (*Context) GetInt

func (c *Context) GetInt(key string) int

GetInt retrieves a stored int value

func (*Context) GetString

func (c *Context) GetString(key string) string

GetString retrieves a stored string value

func (*Context) GetStringSlice

func (c *Context) GetStringSlice(key string) []string

GetStringSlice retrieves a stored string slice value

func (*Context) HasKey

func (c *Context) HasKey(key string) bool

HasKey checks if a key exists in the context

func (*Context) HomeDir

func (c *Context) HomeDir() string

HomeDir returns the path to the sandboxed home directory for the test.

func (*Context) Keys

func (c *Context) Keys() []string

Keys returns all stored keys

func (*Context) NewDir

func (c *Context) NewDir(name string) string

NewDir creates and tracks a named directory within the test

func (*Context) Set

func (c *Context) Set(key string, value interface{})

Set stores a value for inter-step communication

func (*Context) ShowCommandOutput

func (c *Context) ShowCommandOutput(command, stdout, stderr string)

ShowCommandOutput displays command output if UI is available and in verbose mode

func (*Context) StartHeadless

func (c *Context) StartHeadless(model tea.Model) *teatest.HeadlessSession

StartHeadless launches a BubbleTea model in a headless, non-tmux test runner. This is ideal for testing model logic and view output without the overhead of a full TUI session.

func (*Context) StartTUI

func (c *Context) StartTUI(binaryPath string, args []string, opts ...tui.StartOption) (*tui.Session, error)

StartTUI launches a TUI application in a new, isolated tmux session. It returns a Session handle for interaction and ensures the session is cleaned up automatically at the end of the scenario.

func (*Context) StateDir

func (c *Context) StateDir() string

StateDir returns the path to the sandboxed XDG_STATE_HOME directory.

func (*Context) Verify

func (c *Context) Verify(fn func(v *verify.Collector)) error

Verify provides a scope for soft assertions. It collects all failures within the provided function and returns a single aggregated error at the end. All checks, pass or fail, are logged for detailed reporting.

type Harness

type Harness struct {
	// contains filtered or unexported fields
}

Harness runs scenarios

func New

func New(opts Options) *Harness

New creates a new test harness

func (*Harness) Run

func (h *Harness) Run(ctx context.Context, scenario *Scenario) (*Result, error)

Run executes a scenario and returns the result

func (*Harness) RunAll

func (h *Harness) RunAll(ctx context.Context, scenarios []*Scenario) ([]*Result, error)

RunAll executes multiple scenarios in sequence

type Mock

type Mock struct {
	// The name of the command to be replaced (e.g., "git", "llm").
	CommandName string
	// Optional: The path to a binary to use as the mock.
	// If empty, tend will look for a binary named "mock-{CommandName}"
	// in a conventional location like `./bin/mock-{CommandName}` or
	// `./tests/mocks/bin/mock-{CommandName}`.
	BinaryPath string
}

Mock defines a command to be mocked during a test scenario using a compiled binary.

type Options

type Options struct {
	Interactive     bool          // Enable interactive mode
	Verbose         bool          // Enable verbose output
	VeryVerbose     bool          // Enable very verbose output (includes command details)
	NoCleanup       bool          // Keep temp dirs for debugging
	ContinueOnError bool          // Continue batch execution on error
	Timeout         time.Duration // Global timeout for scenarios
	GroveBinary     string        // Path to Grove binary (optional)
	RootDir         string        // Root directory for tests (optional)
	MonitorDocker   bool          // Show live Docker container updates
	DockerFilter    string        // Filter for Docker containers (e.g., "name=grove")
	TmuxSplit       bool          // Split tmux window and cd to test directory
	Nvim            bool          // Start nvim in the new tmux split
	UseRealDeps     []string      // List of dependencies to use real binaries for
	// TestRootDir specifies a pre-existing directory to use for the test run.
	// If empty, a new temporary directory will be created.
	TestRootDir string
	// TmuxSocket specifies a custom tmux socket for debug-session mode.
	TmuxSocket string
	// TmuxEditorTarget specifies the tmux target for the editor window in debug-session mode.
	TmuxEditorTarget string
	// RunSetup runs the setup phase then pauses (or switches to interactive if no setup).
	RunSetup bool
	// RunSteps specifies which test steps to auto-run at startup before pausing.
	// Format: "1,2,3" runs test steps 1-3 then pauses at step 4
	RunSteps string
	// RecordTUIDir specifies the directory to save TUI session recordings for failed tests.
	RecordTUIDir string
}

Options for harness execution

type Result

type Result struct {
	ScenarioName string
	Success      bool
	FailedStep   string
	Error        error
	Duration     time.Duration
	StartTime    time.Time
	EndTime      time.Time
	StepResults  []StepResult // Added for detailed reporting
}

Result represents the outcome of a scenario run

type Scenario

type Scenario struct {
	Name         string
	Description  string
	Tags         []string
	Setup        []Step
	Steps        []Step
	Teardown     []Step
	LocalOnly    bool // Skips in CI by default
	ExplicitOnly bool // Skips in "run all"
	File         string
	Line         int
}

Scenario is a collection of steps defining a test

func NewScenario

func NewScenario(name, description string, tags []string, steps []Step) *Scenario

NewScenario creates a new scenario and captures its definition location.

Using this constructor enables the debug editor feature (--debug flag) to open at the scenario definition when tests start. If you use struct literals instead (&harness.Scenario{...}), the editor will fall back to opening at the first step's location (assuming steps use harness.NewStep).

Example:

scenario := harness.NewScenario(
    "my-test",
    "Tests something important",
    []string{"integration"},
    []harness.Step{
        harness.NewStep("First step", func(ctx *harness.Context) error {
            // ...
        }),
    },
)

Source location capture is automatic via runtime.Caller and is used by: - Debug mode editor navigation (--debug flag) - Future debugging and reporting features

func NewScenarioWithOptions

func NewScenarioWithOptions(name, description string, tags []string, steps []Step, localOnly, explicitOnly bool) *Scenario

NewScenarioWithOptions creates a new scenario with explicit control over all fields. See NewScenario for documentation on source location capture and debug features.

func (*Scenario) WithSetup

func (s *Scenario) WithSetup(steps ...Step) *Scenario

WithSetup adds setup steps to the scenario.

func (*Scenario) WithTeardown

func (s *Scenario) WithTeardown(steps ...Step) *Scenario

WithTeardown adds teardown steps to the scenario.

type ScenarioError

type ScenarioError struct {
	ScenarioName string
	Err          error
}

ScenarioError indicates a scenario-level failure

func (*ScenarioError) Error

func (e *ScenarioError) Error() string

func (*ScenarioError) Unwrap

func (e *ScenarioError) Unwrap() error

type Step

type Step struct {
	Name        string
	Description string
	Func        func(ctx *Context) error
	File        string
	Line        int
}

Step represents a single action in a scenario

func ConditionalStep

func ConditionalStep(name string, condition func(*Context) bool, fn StepFunc) Step

ConditionalStep creates a step that only runs if a condition is met

func DelayStep

func DelayStep(name string, duration time.Duration) Step

DelayStep creates a step that waits for a duration

func NewStep

func NewStep(name string, fn StepFunc) Step

NewStep creates a new step with the given name and function.

This constructor automatically captures the source location where the step is defined, which enables the debug editor feature (--debug flag) to navigate to each step as the test progresses.

Using NewStep is recommended over Step{} literals for better debugging support.

func RetryStep

func RetryStep(name string, maxAttempts int, delay time.Duration, fn StepFunc) Step

RetryStep creates a step that retries on failure

func SequentialSteps

func SequentialSteps(name string, steps ...Step) Step

SequentialSteps creates a step that runs multiple sub-steps in sequence

func SetupMocks

func SetupMocks(mocks ...Mock) Step

SetupMocks is a harness.Step that prepares a sandboxed `bin` directory with the specified mocks, making them available on the PATH for subsequent steps. For complex, stateful, or dynamic mocks, it is recommended to use compiled Go binaries rather than inline scripts.

func SetupMocksWithSubprocessSupport

func SetupMocksWithSubprocessSupport(mocks ...Mock) Step

SetupMocksWithSubprocessSupport is like SetupMocks but also creates a wrapper shell script that forces PATH for subprocess commands. This is useful when the code under test spawns subprocesses via `sh -c` or similar, which may not inherit the modified PATH correctly.

type StepError

type StepError struct {
	StepName string
	Err      error
}

StepError wraps an error with step context

func (*StepError) Error

func (e *StepError) Error() string

func (*StepError) Unwrap

func (e *StepError) Unwrap() error

type StepFunc

type StepFunc func(ctx *Context) error

StepFunc is a convenience type for step functions

type StepResult

type StepResult struct {
	Name       string
	Success    bool
	Error      error
	StartTime  time.Time
	EndTime    time.Time
	Duration   time.Duration
	Assertions []*AssertionResult
}

StepResult represents the outcome of a single step

type TestExecutor

type TestExecutor struct {
	// contains filtered or unexported fields
}

TestExecutor creates commands with a test-aware environment, including a modified PATH for mock binaries and sandboxed home directories.

func NewTestExecutor

func NewTestExecutor(ctx *Context) *TestExecutor

NewTestExecutor creates a new TestExecutor.

func (*TestExecutor) Command

func (e *TestExecutor) Command(name string, args ...string) *exec.Cmd

Command creates a new exec.Cmd instance with the test environment applied.

func (*TestExecutor) CommandContext

func (e *TestExecutor) CommandContext(ctx context.Context, name string, args ...string) *exec.Cmd

CommandContext creates a new context-aware exec.Cmd with the test environment.

type UI

type UI struct {
	// contains filtered or unexported fields
}

UI handles all user interface output (basic implementation)

func NewUI

func NewUI(interactive, verbose, veryVerbose bool) *UI

NewUI creates a new UI instance

func (*UI) Cleanup

func (ui *UI) Cleanup()

Cleanup displays cleanup message

func (*UI) CommandOutput

func (ui *UI) CommandOutput(command, stdout, stderr string)

CommandOutput displays command output in verbose mode, mimicking terminal experience

func (*UI) DisableMonitoring

func (ui *UI) DisableMonitoring()

DisableMonitoring stops container monitoring

func (*UI) EnableMonitoring

func (ui *UI) EnableMonitoring(filter string)

EnableMonitoring starts container monitoring

func (*UI) Error

func (ui *UI) Error(title string, err error)

Error displays an error message

func (*UI) Info

func (ui *UI) Info(title, message string)

Info displays an info message

func (*UI) PhaseStart

func (ui *UI) PhaseStart(name string)

PhaseStart displays the start of a test phase (e.g., Setup, Test, Teardown).

func (*UI) RenderTUICapture

func (ui *UI) RenderTUICapture(content string)

RenderTUICapture displays the captured content of a TUI session

func (*UI) ScenarioFailed

func (ui *UI) ScenarioFailed(name string, err error)

ScenarioFailed displays scenario failure

func (*UI) ScenarioStart

func (ui *UI) ScenarioStart(name, description string)

ScenarioStart displays the start of a scenario

func (*UI) ScenarioSuccess

func (ui *UI) ScenarioSuccess(name string, duration time.Duration)

ScenarioSuccess displays scenario completion

func (*UI) SetTestID

func (ui *UI) SetTestID(testID string)

SetTestID sets the test ID for container filtering

func (*UI) ShowDockerStatus

func (ui *UI) ShowDockerStatus()

ShowDockerStatus displays current Docker container status

func (*UI) StepFailed

func (ui *UI) StepFailed(stepResult StepResult)

StepFailed displays step failure

func (*UI) StepStart

func (ui *UI) StepStart(current, total int, name string)

StepStart displays the start of a step

func (*UI) StepSuccess

func (ui *UI) StepSuccess(stepResult StepResult)

StepSuccess displays step completion

func (*UI) WaitForUser

func (ui *UI) WaitForUser() string

WaitForUser prompts the user to continue Returns a string indicating the user's choice: "continue", "quit", or "attach"

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL