Help us shape the future of Go! Take the 2026 Go Developer Survey

daemontest

package
v0.2.4 Latest Latest
Warning

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

Go to latest
Published: Aug 9, 2026 License: Apache-2.0 Imports: 9 Imported by: 0

Documentation

Overview

Package daemontest boots a real rysh daemon for tests.

Almost every test in this tree exercises a handler by calling it directly, which is fast and right for logic but blind to a whole class of defect: the path from `rysh <verb>` through NATS into a live WorkspaceActor and back. Design 021's exit-status work found five handlers that reported success when they should have failed, and NOT ONE of them was catchable that way — they were paths where a named target does not exist, or where a handler delegates to a `handleCLI*`, prints its error and drops it. They were found by a human running ~50 commands against a daemon by hand, which is not a regression test.

This package is that sweep, automated.

Isolation

A test daemon must never touch the sessions a developer is actually using. Three things guarantee that, and all three matter:

  • Every session gets its own temp directory holding its own rysh.config.yaml. rysh state is project-local and derived from the config file's location, so the session registry, the KV buckets and the JetStream data all land inside that directory and vanish with it.
  • The NATS port is 0, i.e. kernel-assigned. Two test daemons — or a test daemon and a developer's — can never collide on a fixed port.
  • Session names carry the pid and a counter, so a crashed run cannot leave a record that a later run adopts.

Cost

The package runs in about 40 seconds: ~8s to build the binary once, ~12s booting daemons, and ~14s of CLI round-trips at roughly 150ms each. That is in line with internal/actors and is the price of testing the real path, so it runs in the default `go test ./...` rather than hiding behind a build tag — a suite nobody runs catches nothing. `go test -short` skips it for quick local iteration.

Booting a daemon costs a second or two, so the default is one shared session per test binary (Shared), reused by every test that only needs to ASK the daemon things. A test that mutates the workspace in a way others would notice takes its own with Fresh.

What this does NOT cover

The daemon is a separate process, so `go test -race` instruments the test binary and not the daemon. A data race inside the WorkspaceActor is invisible here — that is what the in-process tests in internal/actors are for. These tests check behaviour across the wire, not memory safety behind it.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Binary

func Binary(t *testing.T) string

Binary builds cmd/rysh once per test process and returns the path.

It builds rather than reusing an installed rysh on purpose: the point is to test THIS tree, and a stale binary on PATH would quietly test something else.

func Main

func Main(m *testing.M) int

Main runs a package's tests and then stops the shared session, if one was booted. A package that uses Shared must call it:

func TestMain(m *testing.M) { os.Exit(daemontest.Main(m)) }

Without it the shared daemon outlives the test binary. Nothing would break — its name and port are unique and its state is in a temp directory — but a stray daemon per test run is exactly the kind of litter that makes people stop running a suite.

Types

type Session

type Session struct {
	// Name is the session name, unique to this test process.
	Name string
	// Dir is the temp directory holding the session's config and all its state.
	Dir string
	// Config is the absolute path of the session's rysh.config.yaml.
	Config string
	// Bin is the rysh binary under test.
	Bin string
}

Session is a running rysh daemon and the CLI binary that drives it.

func Fresh

func Fresh(t *testing.T) *Session

Fresh boots a session used only by this test and stops it when the test ends. Use it when the test mutates the workspace in a way another test would see.

func Shared

func Shared(t *testing.T) *Session

Shared returns the session shared by every test in this binary, booting it on first use. Use it for anything that only reads.

func (*Session) CLI

func (s *Session) CLI(t *testing.T, args ...string) (string, int)

CLI runs an arbitrary rysh subcommand against this session's config, for tests that drive a verb other than exec/script.

func (*Session) Exec

func (s *Session) Exec(t *testing.T, command string) (string, int)

Exec runs a "##" command and returns its output and exit code.

func (*Session) MustFail

func (s *Session) MustFail(t *testing.T, command string) string

MustFail asserts a command exits non-zero, and returns its output.

func (*Session) MustSucceed

func (s *Session) MustSucceed(t *testing.T, command string) string

MustSucceed asserts a command exits 0, and returns its output.

func (*Session) Script

func (s *Session) Script(t *testing.T, src string, args ...string) (string, int)

Script writes src to a .rysh file and runs it against this session, returning the combined output and exit code.

Jump to

Keyboard shortcuts

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