Testing¶
This document covers Shed's testing strategy, how to run each tier of tests, and the conventions used across the codebase.
Test Tiers¶
| Tier | Scope | Tool | Requirements | CI |
|---|---|---|---|---|
| Unit | Pure logic, file-shape, ordering invariants | go test |
Go toolchain | Yes |
| Integration suite (installed binary) | Live create-cycle via shed CLI against the brew/deb-installed shed-server |
pytest + subprocess (uv-managed) |
Reachable shed-server (local or remote); for FC: SSH to the host | No (locally + bare-metal release-validation) |
| Integration suite (dev binary, VZ) | Same, but targets a parallel dev shed-server running from the just-built source on the local Mac | make dev-server-up + make test-integration-dev |
All of the above + a ~/.shed/config.yaml entry for the dev server |
No |
| Integration suite (dev binary, FC) | Same, but targets a parallel dev shed-server on the FC remote (alongside the deb one on a different port) | make dev-server-up-fc + make test-integration-dev-fc |
SSH + sudo NOPASSWD on the remote + a ~/.shed/config.yaml entry for the FC dev server |
No |
| E2E (Firecracker, legacy) | Full VM lifecycle via API directly | go test -tags=e2e |
KVM, root, Firecracker assets | No |
The integration suite (PR #132) is the recommended path for live create-cycle verification on both backends. The Go-tagged FC e2e tests predate it and remain available for low-level API exercise.
Server-side changes require the dev-binary variant. The plain make test-integration runs whatever binary is currently installed on the host, not your source — so a server-side-only change can pass the installed-binary tier without ever exercising the new code path. See Validating server-side changes — parallel dev server below for the workflow.
Running Tests¶
Unit Tests¶
# All unit tests (skips linux-only tests on macOS)
make test
# Single package
go test ./internal/config/...
# With race detection
go test -race ./...
Firecracker unit tests (metadata, rootfs, networking) use the linux build tag and run automatically on Linux but are skipped on macOS:
Integration Suite (pytest)¶
Live create-cycle tests that drive a running shed-server via the shed CLI. The suite lives at tests/integration/ and is the recommended path for verifying that the host-side and guest-side pieces actually fit together — boot timing, SSE events, --repo clone, shed exec round-trip, etc.
Architecture: pytest + subprocess (+ Fabric reserved for remote-orchestration tasks like deploying a dev binary), managed with uv. This guide is the canonical reference for the suite's design and operation.
Running the suite¶
# One-time: install uv (or `brew install uv`)
# https://docs.astral.sh/uv/getting-started/installation/
# From repo root:
make test-integration
That target verifies uv is on PATH, runs uv sync into a managed venv (gitignored), and invokes pytest with -v. Each test is parameterized over ["vz", "fc"] and skips cleanly when its target backend isn't reachable from this host.
Per-backend requirements¶
VZ (Apple Silicon mac):
- A reachable shed-server, e.g.,
brew services start shed. shed -s my-server listsucceeds. The entry comes from~/.shed/config.yaml.- Server log at
/opt/homebrew/var/log/shed-server.log(Homebrew default). Override withSHED_VZ_LOG_PATHfor Intel-Mac (/usr/local/...) or custom install paths.
Firecracker (default: mini3 over SSH; override with SHED_FC_HOST):
- SSH access to the host with
BatchMode=yes(no interactive prompt). The defaultmini3is one example; any reachable Linux host with shed-server + KVM + Firecracker works. shed -s <host> listsucceeds (the entry exists in~/.shed/config.yamland the server responds).- Passwordless
sudo -n journalctl -u shed-serveron the remote, for the PhaseTimer log-line fetch. Two tests skip cleanly if it's unavailable (you still get the others). - The remote shed-server must be v0.5.4 or newer for the PhaseTimer-dependent assertions. PhaseTimer was added in PR #118 / v0.5.4; older servers cause those tests to skip with a clear reason while the rest of the suite still runs.
- For the parallel-dev variant (
make dev-server-up-fc/test-integration-dev-fc): the deb shed-server keeps running under systemd at/usr/local/bin/shed-server; the dev shed-server runs from/tmp/shed-server-devviasudo nohup(no systemd unit), with PID at/tmp/shed-server-dev.pidand log at/tmp/shed-server-dev.log. The SSH user needs passwordlesssudofor the operations the dev-server-* recipes drive (bash— the launcher wraps insudo bash -c '...'so the log redirect runs as root; plusinstall,rm,tail,cat,stat,kill).sudo -n truesucceeding from the SSH session is a reasonable smoke test.
Enabling FC tests on a fresh remote host (first-time setup)¶
The suite picks up FC tests automatically once the remote server emits PhaseTimer. To enable end-to-end FC verification on a new remote (mini3 is the default; substitute your host wherever it appears):
- Install shed-server v0.5.4 or newer. Every v0.5.4+ release publishes
shed-server_<version>_amd64.debandshed-server_<version>_arm64.debto GitHub Releases. On the remote host:
# Pick the arch matching the host (uname -m): x86_64 → amd64, aarch64 → arm64.
ARCH=$(dpkg --print-architecture)
# Fetch the latest release tag from GitHub (strips the leading `v`).
VERSION=$(curl -fsSL https://api.github.com/repos/charliek/shed/releases/latest \
| grep -m1 '"tag_name"' | sed -E 's/.*"v?([^"]+)".*/\1/')
curl -L -o /tmp/shed-server.deb \
"https://github.com/charliek/shed/releases/download/v${VERSION}/shed-server_${VERSION}_${ARCH}.deb"
sudo dpkg -i /tmp/shed-server.deb
sudo systemctl restart shed-server
systemctl is-active shed-server # active
/usr/local/bin/shed-server --version
- Verify passwordless sudo for journalctl (one-time per remote):
If this prompts for a password, the suite's PhaseTimer-dependent FC tests can't fetch logs. Either keep your sudo cache warm during the run or add a NOPASSWD rule for /usr/bin/journalctl (and /usr/bin/bash + /usr/bin/install + /usr/bin/cat + /usr/bin/rm + /usr/bin/kill + /usr/bin/tail + /usr/bin/stat if you intend to use make dev-server-up-fc).
- Add the entry to
~/.shed/config.yamlon the dev workstation (or point at an existing entry withSHED_FC_SERVER=<entry-name>), then run the suite:
FC tests now run live against the installed binary. To validate against your source tree instead, see Validating server-side changes below.
Environment overrides¶
| Variable | Default | Effect |
|---|---|---|
SHED_VZ_SERVER |
my-server |
~/.shed/config.yaml entry for the brew-installed VZ server. The dev-server-* and test-integration-dev Makefile targets use SHED_VZ_DEV_SERVER instead. |
SHED_VZ_LOG_PATH |
/opt/homebrew/var/log/shed-server.log |
Where the local VZ server writes its log file. Override for Intel-Mac Homebrew (/usr/local/...) or custom installs. Set automatically by make test-integration-dev to the dev server's log file. |
SHED_VZ_DEV_SERVER |
my-server-dev |
~/.shed/config.yaml entry for the parallel dev VZ server. Honored by the dev-server-* Makefile targets. |
SHED_FC_HOST |
mini3 |
SSH hostname for the FC server. Also honored by make dev-server-up-fc / dev-server-status-fc / test-integration-dev-fc. |
SHED_FC_SERVER |
same as $SHED_FC_HOST |
~/.shed/config.yaml entry name for the deb FC server. |
SHED_FC_DEV_SERVER |
$(SHED_FC_HOST)-dev |
~/.shed/config.yaml entry name for the parallel dev FC server. |
SHED_FC_LOG_PATH |
unset (uses journald) | Remote file path for fc_server fixture to read logs from. test-integration-dev-fc sets this to the dev server's log file so the existing tests find PhaseTimer lines (the dev server runs via sudo nohup, not systemd). |
RELEASE_BUILD_TOOLS_REF |
latest git tag matching v* |
shed-build-tools image ref injected into the dev binary so it uses release-shaped upper-template behavior. Pin to an older release if your source has drifted: RELEASE_BUILD_TOOLS_REF=ghcr.io/charliek/shed-build-tools:v0.5.7. |
Validating server-side changes — parallel dev server¶
make test-integration runs against whichever shed-server binary is currently installed on the host (brew on Mac, deb on Linux), not the source tree you're editing. A server-side-only change (orchestrator, lifecycle internals, backend handlers with no CLI-visible surface) can pass that suite without ever exercising its own code, because the installed binary is the OLD one.
The mechanism for closing this gap is a parallel dev shed-server that runs alongside the brew/deb one on a different port. The production server keeps running undisturbed; the dev server is what the suite targets when you run the dev-targeted Makefile chain.
VZ (local Mac):
# One-time setup per developer:
# Add a ~/.shed/config.yaml entry for the dev server (snippet at the
# end of this section), OR run `shed server add localhost --port 18080
# --name my-server-dev` after the first `make dev-server-up`.
# Per dev cycle:
make dev-server-up # launches dev shed-server on 18080/12222
make test-integration-dev # runs suite against dev server (auto-ups if needed)
# ... edit source ...
make build && make dev-server-restart
make test-integration-dev
make dev-server-down # when done
The dev server:
- Runs from
bin/shed-server(the just-built dev binary) vianohup— no launchd plist, no auto-restart, no survives-reboot. Crashes are visible (not silently recovered); re-up after a reboot is one command. - Listens on ports
18080(HTTP) +12222(SSH), separate from the brew server's8080+2222. Both servers run simultaneously without conflict. - Uses isolated state-dirs under
~/Library/Application Support/shed-dev/vz/— separateimages_dir,instance_dir,snapshots_dir,uppers_dir,socket_dir.shed image prunefrom the brew server never touches dev blobs, and vice versa. - Has
SHED_BUILD_TOOLS_REFset inline to the latest release tag, so the dev binary uses the release-shaped upper-template fast path (sub-100 ms rootfs phase) — the suite'stest_create_rootfs_template_presenttest passes against the dev server when the env var is wired correctly.
FC (remote Linux over SSH; default $SHED_FC_HOST=mini3):
# One-time setup per developer:
# shed server add mini3 --port 18080 --name mini3-dev # after first dev-server-up-fc
# (or manually add the entry to ~/.shed/config.yaml)
# Per dev cycle:
make dev-server-up-fc # launches dev shed-server on mini3:18080 via sudo nohup
make test-integration-dev-fc # runs suite against FC dev (auto-ups if needed)
# ... edit source ...
make build && make dev-server-restart-fc
make test-integration-dev-fc
make dev-server-down-fc # when done
The FC dev server runs via sudo nohup on the remote (needs root for FC's CAP_NET_ADMIN bridge/TAP operations). No systemd unit — same intentionally-ephemeral lifecycle as the Mac dev server (crashes visible, no survives-reboot). Listens on mini3:18080/12222 alongside the deb shed-server's mini3:8080/2222. Isolated state-dirs under /var/lib/shed-dev/firecracker/.
FC-specific isolation:
- Offset vsock_base_cid: 600 (deb default is 100) so the two servers' vsock CIDs don't collide. vsock allocation is in-memory per-server with no kernel check.
- Shared bridge + CIDR + tap_prefix. Kernel-level TAP existence check in internal/firecracker/network.go:FindAvailableTAPIndex coordinates cross-server. Known race window: two servers can both pick the same TAP index before either calls LinkAdd; the second call fails loudly with EEXIST. Diagnosable, not silent. For PR-validation workloads (one dev creating one shed at a time on the dev server while the deb server handles its own creates) this never fires; for production-style concurrent stress it would need a server-side retry-on-EEXIST fix.
A server-side PR should open with "make test-integration-dev: N/N pass against dev-build at commit <sha>" (or make test-integration-dev-fc for FC changes). That sentence is then true and meaningful — not a brew/deb-binary alibi.
~/.shed/config.yaml entry to add for the parallel-dev VZ server:
Validating pre-release: build-tools image changes¶
The parallel dev server uses SHED_BUILD_TOOLS_REF to pick which
shed-build-tools image mints the pre-formatted upper template ext4
on first VM create. By default that's the latest release tag
(resolved from git tag --list 'v*' at recipe time). To validate a
change to the build-tools/ tree before cutting a release:
# Setup
# -----
# 1. Build the local shed-build-tools image. Tags it as
# shed-build-tools:dev in the local Docker daemon.
make build-tools
# 2. Restart the dev server with the local image override.
# Works on either platform (Mac VZ or FC remote).
RELEASE_BUILD_TOOLS_REF=shed-build-tools:dev make dev-server-restart
# or:
RELEASE_BUILD_TOOLS_REF=shed-build-tools:dev make dev-server-restart-fc
# 3. Run the suite (or a focused subset). The dev shed-server will
# invoke shed-build-tools:dev for upper-template formatting.
make test-integration-dev # or test-integration-dev-fc
# Teardown
# --------
# 4. Restart WITHOUT the override so the dev server goes back to the
# latest-release tag for its next start (the env var is per-process,
# not persisted; this is just to leave a clean dev state).
make dev-server-restart # or dev-server-restart-fc
# 5. Optionally remove the local shed-build-tools:dev image to
# reclaim ~145 MB:
docker image rm shed-build-tools:dev
How to confirm the override took effect: after a shed create the
PhaseTimer line should show rootfs= sub-100 ms (fast template
clone), AND the server log should NOT contain a
[<shed-name>] upper template unavailable line for the created
shed. If you see rootfs= in the multi-second range, or the
"unavailable" log line, the override didn't take and the dev binary
fell back to in-guest mkfs.
For an FC remote validation you'll need shed-build-tools:dev
loaded into the REMOTE's Docker daemon (the build-tools image is
invoked by shed-server on the remote host, not by the dev
workstation). Either push shed-build-tools:dev to a registry the
remote can pull from, or docker save on the dev workstation +
docker load on the remote across the SSH boundary.
Validating pre-release: base image changes (vz/firecracker Dockerfiles)¶
Base images (shed-vz-base, shed-vz-extensions, shed-vz-full,
and the FC counterparts) are built by
scripts/build-vz-rootfs.sh / scripts/build-firecracker-rootfs.sh,
which write OCI blobs + a tag pointer into an OUTPUT_DIR. The
default OUTPUT_DIR is the BREW/deb server's images_dir
(~/Library/Application Support/shed/vz for Mac VZ;
/var/lib/shed/firecracker/images for Linux FC) — which means a
naive local build lands in the brew/deb server's blob store, NOT the
parallel dev server's.
To make the local-built image visible to the dev server, override
OUTPUT_DIR to point at the dev server's images_dir:
Mac VZ:
# Setup
# -----
# 1. Build the variant you changed. OUTPUT_DIR is the key override —
# it lands the blobs in the dev server's images_dir so `shed -s
# my-server-dev create --image base` picks them up.
OUTPUT_DIR="$HOME/Library/Application Support/shed-dev/vz" \
./scripts/build-vz-rootfs.sh --variant base
# If your change is in build-tools too, pass --build-tools-version
# dev so the local shed-build-tools:dev mints the rootfs erofs:
OUTPUT_DIR="$HOME/Library/Application Support/shed-dev/vz" \
./scripts/build-vz-rootfs.sh --variant base --build-tools-version dev
# Note on shed-extensions changes: the build-vz-rootfs.sh script
# does NOT pass --shed-ext-version through to the docker build
# (its `--shed-ext-version` flag is informational only, per
# scripts/build-vz-rootfs.sh:201-211). To validate a shed-extensions
# bump, edit `ARG SHED_EXT_VERSION` in `vz/Dockerfile` directly
# before running the script, or invoke `docker buildx build
# --build-arg SHED_EXT_VERSION=<tag> ...` manually.
# 2. Restart the dev server (it picks up the new blobs automatically
# — the OCI store is content-addressed so a `shed image ls` will
# show the new tag pointing at the new manifest digest).
make dev-server-restart
# 3. Run the suite, or just a focused test that uses the variant.
make test-integration-dev
# or for a focused single test:
cd tests/integration && \
SHED_VZ_SERVER=my-server-dev \
SHED_VZ_LOG_PATH="$HOME/.shed/dev/server.log" \
uv run pytest -v test_smoke.py::test_create_delete_lifecycle
# Teardown
# --------
# 4. Stop the dev server.
make dev-server-down
# 5. Optionally remove the locally-built blobs to reclaim disk (~250 MB
# to ~750 MB per variant). The shed-dev/vz tree is the dev server's
# images_dir; deleting it is safe because the brew server uses a
# DIFFERENT directory (~/Library/Application Support/shed/vz).
rm -rf "$HOME/Library/Application Support/shed-dev/vz/blobs"
rm -rf "$HOME/Library/Application Support/shed-dev/vz/tags"
# Or: `shed -s my-server-dev image rm <variant>` for one variant
# at a time + `shed -s my-server-dev image prune` to GC orphaned
# blobs (the dev server's prune is isolated from the brew server's).
FC remote:
The build script needs to run on Linux. The cleanest path is to build on the remote itself (or, if you have Docker buildx with cross-build set up, build on Mac and ship the OCI tarball). No one-command target for this yet; the manual sequence is:
# Setup
# -----
# 1. Build on the remote, writing blobs directly to the dev
# images_dir. `sudo env OUTPUT_DIR=...` (not `OUTPUT_DIR=... sudo`)
# is the load-bearing detail — sudo strips environment variables
# by default; `sudo env VAR=value ...` is the standard way to
# pass an env var through.
ssh $SHED_FC_HOST "cd /path/to/shed && \
sudo env OUTPUT_DIR=/var/lib/shed-dev/firecracker/images \
./scripts/build-firecracker-rootfs.sh --variant base"
# 2. Restart the dev FC server so it sees the new blobs.
make dev-server-restart-fc
# 3. Run the suite.
make test-integration-dev-fc
# Teardown
# --------
# 4. Stop the dev FC server.
make dev-server-down-fc
# 5. Optionally remove the locally-built blobs on the remote.
ssh $SHED_FC_HOST "sudo rm -rf \
/var/lib/shed-dev/firecracker/images/blobs \
/var/lib/shed-dev/firecracker/images/tags"
A make build-dev-image / build-dev-image-fc Makefile helper that
wraps these flows is worth adding if these workflows become
frequent — for now the explicit OUTPUT_DIR override is the
load-bearing piece to know.
How to confirm the override took effect: shed -s my-server-dev
image ls (Mac) or shed -s mini3-dev image ls (FC) will list the
locally-built image with its new manifest digest. If you don't see
your change, OUTPUT_DIR didn't land the blobs in the dev server's
images_dir.
Performance validation against the released version¶
For changes that touch the boot path, agent dial, healthPoll, upper-allocation, mount, image-resolution, or any other hot path: measure the impact on each platform the change affects, against the most recent release binary, before merging.
The split timing gate (test_create_agent_p50 + test_create_rootfs_template_present) is the floor — it fires on regressions around 500 ms or more (see tests/integration/fixtures/server.py:DEFAULT_AGENT_P50_MS for the per-backend ceiling; VZ has ~500 ms regression budget over its ~1550 ms median, FC has ~500 ms over its ~2400 ms median) — but a sub-threshold regression (or worse, a "no regression" that masks an actual gain that didn't materialise) won't trip it.
The workflow uses the parallel-dev pair on both sides of the comparison — no service interruption needed:
- Baseline against release.
make test-integration(it targets the brew/deb install). Record the agent_p50 and rootfs_ms (and total wall-clock) from the PhaseTimer line for each backend you're changing. - Compare against your branch.
make build && make dev-server-restart && make test-integration-dev(it targets the parallel dev server running your source). Same measurements. - Repeat on every affected backend. A change shipping for both VZ and FC needs both backends measured — Apple Silicon vfkit and Linux KVM Firecracker have different floors, and the same code can be faster on one and slower on the other.
- Record the measurements in the PR body. Hypothesised gains that don't show up are worth investigating before merge.
The v0.5.4 build-tools-ref regression (caught by a user noticing slow creates after brew upgrade, not by the suite) is the canonical example of "the binary built correctly but a config knob silently disabled the fast path." Measuring on the actual dev binary is what catches that class of bug before users do.
Adding a test¶
Tests live in test_smoke.py. Use the shed_server fixture (parameterized over ["vz", "fc"]) and test_shed_name (unique per-test name with autoteardown):
def test_my_thing(shed_server, test_shed_name):
shed_server.create(test_shed_name, image="base")
r = shed_server.exec(test_shed_name, ["uname", "-r"])
assert r.returncode == 0
assert "Linux" in r.stdout
For a backend-specific test, mark it explicitly:
(Markers are declared in pyproject.toml under [tool.pytest.ini_options].markers — keep them in sync.)
Suite layout¶
tests/integration/
pyproject.toml # uv-managed Python project + pytest config
uv.lock # committed for reproducibility (gitignore exception)
README.md # operator notes; same content as this section, in-tree
conftest.py # vz_server, fc_server, shed_server, test_shed_name
test_smoke.py # core MVP tests × 2 backends
test_lifecycle.py # create → stop → start → exec → delete
test_exec_shell.py # ssh shell semantics + shed exec direct-argv
test_timing_parser.py # PhaseTimer parser unit tests (no live server)
fixtures/
server.py # LocalServer + RemoteServer + ShedHandle
timing.py # PhaseTimer log-line parser
The core smoke tests in test_smoke.py:
| Test | What it asserts |
|---|---|
test_create_delete_lifecycle |
shed create succeeds; the shed appears in shed list; explicit shed delete removes it; absence verified. |
test_phase_timer_emitted |
Server log contains a timing: create … line with the expected phase keys. |
test_repo_clone_https |
--repo with an HTTPS URL clones the pinned octocat/Hello-World HEAD; git rev-parse HEAD in the guest matches. |
test_create_agent_p50 |
agent phase p50 (5 samples) stays under a per-backend ceiling. Skips cleanly when the VZ upper-template fast path was unavailable (in-guest mkfs cost would inflate agent_ms for a structural reason that's not a real regression). |
test_create_rootfs_template_present |
VZ-only: the host-side upper-template fast path is active (rootfs_ms ≤ 100 ms). Skips on FC (no host-side template path) and on VZ dev mode (where the fast path is unavailable by design). |
test_shed_exec_smoke |
shed exec <name> -- echo hello returns hello. |
test_extensions_image_smoke |
The extensions image carries the shed-extensions binaries at the documented paths with the executable bit. Skips when no extensions alias is configured. |
test_create_agent_p50 + test_create_rootfs_template_present are the dynamic perf-regression gates PR-time CI can't be (no /dev/kvm on GHA). The split replaced the single-gate test_plain_create_timing (renamed during PR #157) so the suite runs against either a brew/deb release binary or a make build dev binary without false-positive failures from the dev-build in-guest mkfs.ext4 fallback. See the module-level comment in tests/integration/test_smoke.py for the split rationale, and tests/integration/README.md for the workflow guide.
test_lifecycle.py adds a create → stop → start → exec → delete round-trip that catches StartShed-after-StopShed regressions the plain create/delete cycle can't see.
test_exec_shell.py (8 tests × 2 backends) exercises the SSH command channel: raw ssh shed-name 'cmd | pipe' gets the full shell via the server's bash -lc wrap, while shed exec name -- cmd ships argv literally without metacharacter expansion. Together they audit the shed exec semantics end-to-end.
Plus parser unit tests in test_timing_parser.py covering the PhaseTimer log shape (real captured lines, duplicate-phase summation, "no keys after err=" guard, convenience properties).
E2E (Firecracker, legacy)¶
Firecracker e2e tests exercise the full VM lifecycle: create, start, exec, stop, delete. They require KVM access, root privileges, and pre-built Firecracker assets.
# Build prerequisites
make build
sudo scripts/build-firecracker-rootfs.sh
scripts/download-firecracker.sh
# Run e2e tests
sudo go test -v -tags=e2e ./e2e/firecracker/...
Why Firecracker e2e can't run in CI
GitHub Actions runners do not support KVM (no nested virtualization). Firecracker requires /dev/kvm and root privileges to launch microVMs. These tests must be run manually on a bare-metal Linux host or a VM with nested virt enabled.
Build Tag Conventions¶
| Tag | Purpose | Example |
|---|---|---|
//go:build linux |
Code that uses Linux-only APIs (vsock, TAP, etc.) | internal/firecracker/*.go |
//go:build e2e |
Tests requiring KVM + Firecracker | e2e/firecracker/*_test.go |
The integration build tag was previously used for Docker backend tests and may still appear in older branches.
All internal/firecracker/ source and test files carry the linux build tag because they depend on Linux-specific syscalls (vsock, netlink).
Test Patterns¶
Table-Driven Tests¶
All test files use Go's standard table-driven pattern:
tests := []struct {
name string
input string
wantErr bool
}{
{"valid", "my-shed", false},
{"empty", "", true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := ValidateShedName(tt.input)
if (err != nil) != tt.wantErr {
t.Errorf("error = %v, wantErr %v", err, tt.wantErr)
}
})
}
Modify Pattern for Config Validation¶
Config validation tests use a modify func(*) pattern to test individual field changes against a known-good baseline:
Test Helpers¶
The internal/firecracker package provides shared helpers in testutil_test.go:
| Helper | Purpose |
|---|---|
mustTempDir(t, prefix) |
Creates a temp directory with automatic cleanup |
testMetadata(name) |
Returns a valid Metadata with sensible defaults |
testFirecrackerConfig(tmpDir) |
Returns a valid FirecrackerConfig for testing |
createTestInstance(t, dir, name) |
Creates a complete test instance on disk |
Conventions¶
- Place test files alongside the code they test (
foo.go/foo_test.go) - Use
t.Helper()in all test helper functions - Use
t.Cleanup()for resource teardown instead ofdeferwhere possible - Prefer
t.Fatalffor setup failures,t.Errorffor assertion failures - Use
os.MkdirTempwitht.Cleanupfor temporary directories