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

agenttest

package
v1.0.3 Latest Latest
Warning

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

Go to latest
Published: Oct 15, 2026 License: MIT Imports: 8 Imported by: 0

Documentation

Overview

Package agenttest supplies offline executables for runner and caller contract tests: agents, the container engine, the Docker Sandboxes CLI and a host MCP server.

Index

Constants

View Source
const CodexAppServerFakeEnv = "FAKE_CODEX_APP_SERVER"

CodexAppServerFakeEnv set to "1" makes the running test binary a fake `codex app-server` (RunCodexAppServer), the same way FAKE_CLAUDE makes it a fake `claude` in a caller's tests: the name carries no caller's Runner.EnvironmentPrefix, because the runner strips every inherited variable with that prefix from a session, so a flag in that namespace would never reach the fake. A test binary that wants to offer this fake, to its own tests or to a session a production Runner starts, checks this variable first in its TestMain and calls RunCodexAppServer when it is set, as this package's own tests do.

View Source
const OpenCodeNoCustomTools = `{"probe":"agentcore-opencode-custom-tools/1","roots":["/.opencode"],"tools":[],"errors":[]}`

OpenCodeNoCustomTools is what a fake opencode prints for the search for custom tools a held opencode turn runs first, the command it is started with BUN_BE_BUN=1 for: the search ran and found nothing to load.

Variables

This section is empty.

Functions

func CodexAppServer

func CodexAppServer(t *testing.T, script CodexAppServerScript) (bin, record string)

CodexAppServer arranges for the running test binary, re-executed, to be a fake `codex app-server` scripted by script, the way FAKE_CLAUDE and Runner.ClaudeBin = os.Args[0] do for claude in a caller's tests (see CONTRIBUTING.md): it sets CodexAppServerFakeEnv and the script's location for the rest of the calling test (t.Setenv, undone when it ends) and returns the test binary's own path, which the caller hands a Runner as CodexBin, or runs directly. Invoked with "app-server" as its first argument, the fake speaks the app-server JSON-RPC conversation over stdin and stdout, one message per line, and exits when stdin closes. Invoked with "mcp" as its first argument instead — a restricted turn's inventory, run apart from the app-server process — it prints script's MCPServers (or MCPServersRaw) and exits at once, reading nothing. Every line the app-server conversation receives, and every invocation's own argv, go to the record file whose path CodexAppServer also returns, which ReadCodexAppServerRecord reads back; it also counts how many times the fake started.

A test binary that wants to offer this fake must check CodexAppServerFakeEnv in its own TestMain and call RunCodexAppServer when it is set, as this package's own tests do; CodexAppServer only arranges the environment, since it has no way to install a TestMain in the caller's package.

func Docker

func Docker(t *testing.T, image, sessionVariable string) string

Docker writes a fake container engine. It answers `network`, `rm`, `image` and `build`, and runs what `run` is given on this machine, recording its arguments and environment in the directory sessionVariable names. A `run` with --entrypoint is the look an agent.NewContainer session takes into its image before any turn: its arguments are recorded in docker-probe.txt beside the script, and it prints image-vcs.txt from there, the VCS executables the image is to have ("f <path>" or "d <path>" a line; none without the file), or fails when a file named fail-probe is there.

Every subcommand but `run` and the probe is answered without consulting a real Docker: `network` always reports the one fixed gateway address, and `rm` always succeeds, whatever arguments they were given. A test that needs those to fail, or that needs the gateway address to vary, must use its own script instead. `run` itself is the one call this fake does not fake: it execs the command the caller gave it for real, after recording the arguments and environment it was started with.

func MCPServer

func MCPServer(t *testing.T, sessionVariable string) string

MCPServer writes a fake host MCP server: it records its environment, PID and arguments beside sessionVariable names, prints one fixed listening address and then idles. It always starts and never reports a port already in use or any other startup failure; a test of that behaviour must use a script of its own.

func RunCodexAppServer

func RunCodexAppServer() int

RunCodexAppServer runs a fake `codex app-server` on stdin and stdout, scripted by the CodexAppServerScript that CodexAppServer wrote under the directory named by codexAppServerDirEnv, appending its own argv and every line it receives there (record.jsonl). It returns the process's exit code: always 0, short of being unable to read back its own script or open its record file.

func Sbx

func Sbx(t *testing.T, sessionVariable string) string

Sbx writes a fake Docker Sandboxes CLI. `create` records its arguments in sbx-create.txt beside the script and fails when a file named fail-create is there, fails as sbx's start does after it has made the sandbox (a bare HTTP 500) when one named fail-start is, and fails the way sbx does when its primary workspace, the first one, is read-only, is not a directory, or has a parent at or inside a read-only workspace, where sbx's start writes the agent's instructions; `exec --interactive`, the session's command, records its arguments and the client's environment in the directory sessionVariable names (sbx-exec-args.txt, sbx-exec-env.txt) and runs the command after the sandbox name on this machine; `exec --workdir`, a probe the runner runs before the session's command, appends its arguments to sbx-probe.txt beside the script and runs its command the same way; `exec <name> dagger version` appends its arguments to sbx-dagger-probe.txt beside the script, one call per line, and prints the release in a file named dagger-version there the way `dagger version` does, or fails as a missing CLI when there is none; any other `exec`, a setup command, is appended to sbx-setup.txt beside the script, one argument per line, runs nothing and fails when a file named fail-setup is there; `policy` appends its arguments to sbx-policy.txt beside the script, one call per line, and fails when a file named fail-policy is there, `policy rm` also when one named fail-policy-rm is; `rm` records its arguments in sbx-rm.txt beside the script; `version` prints one, and fails when a file named fail-version is there. Every call but `exec` and `version` also appends its first two arguments to sbx-calls.txt beside the script, in the order they ran.

`rm` and `policy` beside `policy rm` always succeed unless their marker file is there: the fake does not check that a name passed to `rm` or a rule passed to `policy` matches a sandbox or rule that actually exists. A test of that check must read the recorded arguments itself.

func Script

func Script(t *testing.T, name, body string) string

Script writes a fake executable under the test's temporary directory.

Types

type CodexAppServerAction

type CodexAppServerAction struct {
	Notify  *CodexAppServerMessage `json:"notify,omitempty"`
	Request *CodexAppServerMessage `json:"request,omitempty"`
}

CodexAppServerAction is one step a fake `codex app-server` performs once it has answered `turn/start`, run in order. Exactly one of Notify and Request is set: Notify is sent as a notification of the fake's own; Request is sent as a request of the fake's own (any method, known to the real protocol or not), with an id the fake assigns, and the fake waits for the client's answer to it, which is recorded like any other line the fake receives, before moving on to the next action.

type CodexAppServerInterrupt

type CodexAppServerInterrupt struct {
	Status string `json:"status"`
}

CodexAppServerInterrupt scripts how a fake `codex app-server` answers `turn/interrupt`. A script with no CodexAppServerInterrupt ignores every `turn/interrupt` it is sent: it never answers it. Given one, every `turn/interrupt` is answered with an empty result, followed by a `turn/completed` notification carrying Status.

type CodexAppServerMessage

type CodexAppServerMessage struct {
	Method string `json:"method"`
	Params any    `json:"params,omitempty"`
}

CodexAppServerMessage is a notification's or a request's method and params, as CodexAppServerScript schedules the fake to send one.

type CodexAppServerPause

type CodexAppServerPause struct {
	ReadyFile, ReleaseFile string
}

CodexAppServerPause scripts a pause once turn/start is answered; see CodexAppServerScript.Pause.

type CodexAppServerRecord

type CodexAppServerRecord struct {
	Starts int
	Argv   [][]string
	Env    [][]string
	Lines  []string
}

CodexAppServerRecord is what a fake `codex app-server` wrote to its record file (see CodexAppServer): how many times it started, each start's argv and environment in the order the starts happened (Env[i] is Argv[i]'s invocation), and every line it received on stdin across every start, in the order it arrived.

func ReadCodexAppServerRecord

func ReadCodexAppServerRecord(t *testing.T, path string) CodexAppServerRecord

ReadCodexAppServerRecord reads back the record file at path, the second value CodexAppServer returned.

type CodexAppServerScript

type CodexAppServerScript struct {
	// ExitBefore names the step the fake exits at without answering: one
	// of "initialize", "config/read", "thread/start", "thread/resume" or
	// "turn/start" (whichever of thread/start and thread/resume the
	// client actually sends). Left empty, the fake answers every step it
	// reaches.
	ExitBefore string `json:"exitBefore,omitempty"`

	// InitializeResult is initialize's answer. Left nil, it answers with
	// an empty result.
	InitializeResult any `json:"initializeResult,omitempty"`

	// ConfigResult and ConfigError script config/read's answer; at most
	// one should be set. Neither set answers with an empty result.
	ConfigResult any            `json:"configResult,omitempty"`
	ConfigError  *CodexRPCError `json:"configError,omitempty"`

	// ThreadID and ThreadError script thread/start's answer, as
	// {"thread":{"id":ThreadID}} or the error, and thread/resume's too,
	// unless ResumeID or ResumeError is set.
	ThreadID    string         `json:"threadID,omitempty"`
	ThreadError *CodexRPCError `json:"threadError,omitempty"`
	// ResumeID and ResumeError, whichever is set, override ThreadID and
	// ThreadError for thread/resume only.
	ResumeID    string         `json:"resumeID,omitempty"`
	ResumeError *CodexRPCError `json:"resumeError,omitempty"`

	// TurnID is turn/start's answer, as {"turn":{"id":TurnID}}.
	TurnID string `json:"turnID,omitempty"`

	// Actions runs in order once turn/start is answered.
	Actions []CodexAppServerAction `json:"actions,omitempty"`

	// Interrupt scripts turn/interrupt; see CodexAppServerInterrupt.
	Interrupt *CodexAppServerInterrupt `json:"interrupt,omitempty"`

	// Pause, when set, makes the fake wait once turn/start is answered,
	// before running Actions: it creates PauseReady (so a test waiting on
	// it knows the turn is in progress and the command line it launched
	// with is already on disk) and then polls for PauseRelease before
	// going on. It exists for a test that needs to act while the turn is
	// still running — reading through a restricted turn's read server
	// before its turn ends and the server is closed.
	Pause *CodexAppServerPause `json:"pause,omitempty"`

	// MCPServers is the fake's answer to `codex mcp list --json`, the
	// inventory a restricted turn's command line runs (codexMCPInventory)
	// as a process of its own, apart from the app-server conversation
	// above: the configured servers' names, printed as the array `codex
	// mcp list` writes. Left nil, the fake prints an empty array.
	// MCPServersRaw, when set, is printed in its place, so a script can
	// answer with output the inventory cannot decode.
	MCPServers    []string `json:"mcpServers,omitempty"`
	MCPServersRaw string   `json:"mcpServersRaw,omitempty"`
}

CodexAppServerScript scripts a fake `codex app-server`'s conversation, as CodexAppServer hands it to the fake: see that function.

type CodexRPCError

type CodexRPCError struct {
	Code    int    `json:"code"`
	Message string `json:"message"`
}

CodexRPCError is a JSON-RPC error a scripted fake `codex app-server` answers a request with.

func ValidateCodexTurnInput

func ValidateCodexTurnInput(raw json.RawMessage) *CodexRPCError

ValidateCodexTurnInput checks raw, turn/start's "input" parameter, against the shape codexRPCRun (agent/codex_rpc.go) always sends: a non-empty JSON array of items, each an object with a "type" of "text" and a string "text". It returns nil when raw matches that shape, or a *CodexRPCError with code -32600 when it does not — in particular when raw is a bare string, the shape this validates against regressing to.

Directories

Path Synopsis
Package enforcertest is an agent.Enforcer that starts no process: a caller tests what it does around Prepare, Run and Release, and what its grants let a turn do, with a Go function playing the agent.
Package enforcertest is an agent.Enforcer that starts no process: a caller tests what it does around Prepare, Run and Release, and what its grants let a turn do, with a Go function playing the agent.

Jump to

Keyboard shortcuts

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