sandboxtest

package
v0.2.0 Latest Latest
Warning

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

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

Documentation

Overview

Package sandboxtest is the contract suite every sandbox.Provider must pass (CLAUDE.md: backend variability lives behind an interface with one shared suite). It is test support; production code must never import it.

The suite asserts observable behavior only — what a tool would see — never a backend's internals. A new backend adds one test file that calls Run.

Index

Constants

View Source
const GateImageTag = "map-gate:sandboxtest"

GateImageTag is the tag BuildGateImage builds the repo's gate image under.

Variables

This section is empty.

Functions

func BuildGateImage added in v0.2.0

func BuildGateImage(t *testing.T) string

BuildGateImage builds the repo's gate image (docker build --target gate) once per test process and returns its tag. A build failure is a hard test failure — the repo convention for a missing daemon, not a skip.

func DockerDesktop added in v0.2.0

func DockerDesktop(t *testing.T) bool

DockerDesktop reports whether the daemon under test is Docker Desktop, which runs the engine inside its own VM. That — not GOOS — is what decides how a container addresses the test process. On macOS the two agree, which is why a compile-time check served; under WSL they part company, because the test binary is GOOS=linux while the daemon is still a Desktop VM, so the bridge gateway names a namespace this process is not in and nothing answers there. A daemon that cannot be asked is treated as sharing this namespace: that is the pre-existing behaviour, and the caller's own error is the better one.

func DockerHostAddr added in v0.2.0

func DockerHostAddr(t *testing.T) string

DockerHostAddr returns an address of the test host reachable from a container on Docker's default bridge network: host.docker.internal under Docker Desktop for Mac, this host's own routable address under Docker Desktop on Windows/WSL, and the bridge gateway IP when the daemon shares this process's network namespace. MAP_DOCKER_HOST_ADDR overrides all three, as MAP_K8S_HOST_ADDR does for the Kubernetes harness. Listeners meant to be reached this way must bind all interfaces, not loopback.

func Run

func Run(t *testing.T, newHarness func(t *testing.T) Harness)

Run exercises the sandbox.Provider contract. newHarness is called once per subtest so a backend can isolate its own fixtures.

Types

type GateFixture added in v0.2.0

type GateFixture struct {
	// Spec is the gate the provisioned sandbox pairs with.
	Spec *sandbox.GateSpec
	// AllowedAddr is the host:port — reachable from the gate container — of an
	// origin whose /echo endpoint reflects the Authorization header and body it
	// received. Its host is the single entry on the policy's allowed_hosts.
	AllowedAddr string
	// DeniedHost is a host the policy does not admit.
	DeniedHost string
	// Placeholder and Secret are the fixture's one env-var credential: the gate
	// substitutes Placeholder → Secret on plain-HTTP egress to AllowedAddr, and
	// must pass Placeholder through a CONNECT tunnel untouched.
	Placeholder string
	Secret      string
}

GateFixture is what a gate-declaring backend's Harness.Gate returns: the GateSpec its gated rows provision with, plus the egress targets and the one credential the fixture's stand-in controlplane serves for them.

type GateStub added in v0.2.0

type GateStub struct {
	// Addr is host:port reachable from a container on the default bridge.
	Addr        string
	Token       string
	Placeholder string
	Secret      string
}

GateStub is a stand-in controlplane and egress origin on one host listener: it serves the gate's config fetch (gateconfig.Path, bearer-authenticated with Token) and an /echo endpoint reflecting the Authorization header and body that egress delivered to the origin. The config's policy admits exactly the stub's own host and carries one header+body env-var credential.

func StartGateStub added in v0.2.0

func StartGateStub(t *testing.T) *GateStub

StartGateStub starts the stub addressed for a container on the default bridge, cleaned up with the test.

func StartGateStubAt added in v0.2.0

func StartGateStubAt(t *testing.T, hostAddr string) *GateStub

StartGateStubAt starts the stub with an explicit host address — the one the backend under test's containers can reach the test host at (the Docker bridge gateway, a kind network gateway, ...). The listener always binds 0.0.0.0; hostAddr only decides how the served config and the fixture name it.

func (*GateStub) Minter added in v0.2.0

func (s *GateStub) Minter() sandbox.GateTokenMinter

Minter returns a GateTokenMinter minting the stub's fixed token; Persist is a no-op — the stub itself is the token's verifier.

type Harness

type Harness struct {
	Provider          sandbox.Provider
	Image             string
	Gate              func(t *testing.T) GateFixture
	EnforcesPidsLimit bool
}

Harness is one backend under test. Image must name a Linux image carrying /bin/bash (the plan's image contract) and a POSIX userland — plus `tar`, which a backend that extracts a bulk write's archive inside the sandbox needs (the k8s backend does; the docker daemon extracts on the host, so that backend does not).

Gate, when non-nil, declares that the backend runs the per-session egress gate: it is called once per gated subtest and returns the fixture those rows provision with (a GateSpec plus the egress targets the fixture serves). A backend that does not yet run a gate leaves it nil — the gated rows are not registered, and the ungated limited-networking row keeps the fail-closed no-route expectation for every backend.

EnforcesPidsLimit declares that the backend's runtime can cap a single sandbox's process count (sandbox.Hardening.PidsLimit). Docker can; the Kubernetes Pod API cannot express a per-pod pids limit at all, so that backend leaves it false and the row is not registered — one of the two hardening dimensions the backends genuinely cannot share, both recorded in docs/DIVERGENCES.md.

The other one, EphemeralStorageBytes, is Kubernetes-only and gets no flag here, because no flag would gate anything: every row in this suite asserts what a tool *inside* the sandbox can observe, and a disk cap is enforced by the kubelet evicting the pod rather than by any cgroup file the sandbox could read. It is asserted at the provider level on both backends instead — the pod spec carries it, the Docker create payload provably does not — the way the memory cap is, since observing that one means being OOM-killed.

Jump to

Keyboard shortcuts

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