15 KiB
RVBox implementation test workflow
All Go, protobuf, and harness work runs in the pinned toolchain container. No host Go installation is used.
Run the normal pre-commit gate with:
make verify
Run focused unit tests with:
scripts/test-unit --package ./internal/domain --run UUIDv7 --race
Run one bounded hostile-input fuzz target in the same pinned container with:
scripts/test-fuzz --package ./internal/agentproto --name FuzzDecodeEnvelopeBounded_SEC_PROTO_01 --time 30s
scripts/test-fuzz --package ./internal/server/control --name FuzzDecodeJSONRPCRequestBounded_SEC_CTL_01 --time 30s
The fuzz command disables ordinary tests for that invocation and runs exactly one target. A crash stores Go's minimized reproducer in the affected package's fuzz corpus, so the normal test gate exercises it as a deterministic seed after it is reviewed and committed.
Build all supported binaries without installing make or Go on the host:
scripts/build build
scripts/build doctor reports the exact owned builder image, Go cache volumes,
and ignored bin/ artifact directory. To reclaim space, cleanup is dry-run by
default and never performs a global Docker prune:
scripts/build clean --all
scripts/build clean --all --execute --yes
scripts/build recover
The second command removes only bin/, rvbox-dev-toolchain:latest, and the
two named RVBox Go-cache volumes. recover rebuilds the pinned toolchain and
all binaries from source. It intentionally does not remove .test-runs/, which
may contain resumable environments; use the exact-run
scripts/test-env purge --run-id ID --execute --yes workflow for those.
For a native Windows run, create the immutable per-run test bundle with the pinned toolchain. Supply a test-specific config whose endpoint and CA path are valid for that run; the command refuses to replace an existing bundle.
scripts/windows/build-test-bundle \
--run-id windows-smoke \
--config .test-runs/windows-smoke/client.toml \
--ca .test-runs/windows-smoke/ca.pem
scripts/windows/test-host prepare --run-id windows-smoke
scripts/windows/test-host stage --run-id windows-smoke \
--bundle .test-runs/windows-smoke/windows-bundle
scripts/windows/test-host install --run-id windows-smoke
The bundle manifest records the source commit and SHA-256 of every bundled file. It is a test artifact, not a signed release package; release signing, version resources, and publication are Phase 8 gates.
The preferred complete native lane is now one command:
scripts/windows/native-test run --run-id windows-hierarchy
It builds a disposable fixture-tagged Windows binary in the pinned toolchain, starts the real Linux server and nginx TLS proxy in Docker Compose under test/linux-server on the current controller, and connects the Helium-hosted NAT guest to the current controller's explicit endpoint (x1.xcel.me by default). Helium hosts the VM only; it does not host any RVBox server containers. The self-signed server certificate is intentionally accepted by the v1 client without a test CA. It then drives the installed SCM service through the server's real Unix control socket and verifies every Windows execution context, ordered stdin close, TERM delivery, and a server-process restart while a command is running. The restart check preserves the server state volume, waits for a new reconciled WSS session, then proves that the same command can receive its terminal signal; it covers reconnect without treating the old session as valid. The tagged binary's controlled pre-launch failures are limited to the test fixture; a release binary rejects that switch.
Successful runs collect bounded artifacts, remove only their labeled Compose project, and restore the exact clean snapshot. A failed or --keep run stays recoverable:
scripts/windows/native-test recover --run-id windows-hierarchy
scripts/windows/native-test clean --run-id windows-hierarchy
scripts/windows/native-test clean --run-id windows-hierarchy --purge --yes
clean retains local artifacts by default. The explicit purge form removes only the exact local run root after the local stack is down and the VM snapshot has been restored.
The integration harness provides the Phase 0 sample suite, the incremental
Phase 2 store suite, Phase 3 server-session, and control and
client-agent protocol suites. The deterministic E2E lane provides smoke,
interactive, idempotency, reconnect, retention,
expiry-and-incidents, script, and recovery. Native Windows execution
contexts remain the separately leased scripts/windows/native-test lane.
The resumable E2E harness adds smoke, script, recovery, and all
scenarios. Each run writes its manifest and run ID before starting work.
The storage suite uses a real temporary SQLite database in WAL mode and a real
segment/audit filesystem. The session suite uses a real HTTP/WebSocket listener,
binary protobuf frames, SQLite fencing, and the race detector; neither mocks its
respective durability or wire boundary:
scripts/test-env doctor
scripts/test-integration --suite sample --run-id my-sample
scripts/test-integration --list
scripts/test-integration --suite all --run-id integration-all
scripts/test-integration --suite store --case '^TestStore' --run-id store-one-case
scripts/test-env status --run-id my-sample
scripts/test-env collect --run-id my-sample
scripts/test-env reset --run-id my-sample
scripts/test-env reuse --run-id my-sample --new-run-id my-sample-retry
scripts/test-integration --suite sample --run-id my-sample-retry --resume
scripts/test-env reset --run-id my-sample-retry
scripts/test-env purge --run-id my-sample --execute --yes
scripts/test-env purge --run-id my-sample-retry --execute --yes
scripts/test-integration --suite store --run-id store-smoke
scripts/test-env logs --run-id store-smoke
scripts/test-env collect --run-id store-smoke
scripts/test-env reset --run-id store-smoke
scripts/test-env purge --run-id store-smoke --execute --yes
scripts/test-integration --suite server-session --run-id session-smoke
scripts/test-env logs --run-id session-smoke
scripts/test-env collect --run-id session-smoke
scripts/test-env reset --run-id session-smoke
scripts/test-env purge --run-id session-smoke --execute --yes
scripts/test-e2e --scenario smoke --run-id e2e-smoke
scripts/test-e2e --list
scripts/test-e2e --scenario smoke --case '^Test' --run-id e2e-one-case
scripts/test-env status --run-id e2e-smoke
scripts/test-env recover --run-id e2e-smoke
scripts/test-e2e --scenario smoke --run-id e2e-smoke --resume
scripts/test-env reset --run-id e2e-smoke
scripts/test-env purge --run-id e2e-smoke --execute --yes
The client runtime unit lane also exercises a real child process through the
portable supervisor adapter. internal/client/agent/executor_test.go verifies
that command text is accepted once, output is journaled, lifecycle/terminal
events are durable, and a script cannot launch before its contiguous upload is
committed. The Windows build uses the same executor contract with the
platform-native adapter: a verified token is selected, the child is created
suspended, assigned to a kill-on-close Job, and held behind an authenticated
per-command launcher pipe. The daemon records launch_prepared, then the
durable launch_authorized transition sends the launcher's release frame; the
launcher resumes the shell only after that acknowledgement. The durable
launch_phase barrier is recovered as interrupted after a daemon restart, so
an uncertain release is never redispatched.
The Windows artifact is linked with the GUI subsystem (-H=windowsgui) so
service, launcher, and tray startup do not flash a console. Human-facing modes
still attach to a parent console explicitly when one exists.
Suite output is capped by the checked-in harness policy (10 MiB by default) and
stored as artifacts/suite.log. A failed run remains inspectable and can be
moved back to ready with recover, then resumed with the same run ID and
deterministic shuffle seed only while its source definitions and working tree
still match its manifest. reuse is intentionally different: after a reset or
completed run, it creates a fresh manifest/run ID with the same layer, suite,
and case selection, leaving the original evidence unchanged. reset refuses a
running run or one with recorded owned runtime resources, removes only its
ephemeral runtime, pki, and scratch directories, and retains the
manifest, journal, reports, and artifacts. Test-run cleanup never removes the
shared Go module or build-cache volumes.
Each run owns only .test-runs/<run-id> and resources explicitly recorded in
that run's versioned manifest. The journal is append-only and fsynced. purge
first prints the exact validated target and is dry-run by default. It requires
--execute --yes, refuses symlink targets or manifests that still list runtime
resources, and then removes only one completed/reset run (or eligible runs
selected by --all). gc --older-than DURATION uses the same explicit
execution confirmation. Purged artifacts are not recoverable. Dependency cache
volumes are never part of run cleanup.
test/coverage.toml is the incremental requirement-to-test inventory. The
make verify lint stage checks unique stable IDs and verifies every implemented
test reference against source. A resettable Windows smoke VM is now available.
The authoritative fixture record is
testing-vm.md: it lists the VM/host UUIDs, Windows build,
hardware and device profile, NAT and VRDE endpoints, snapshot UUIDs,
credential-file contract, and the required reset sequence. At the last check
the VM was powered off with baseline-clean-administrator selected. The guest address
10.0.2.15 is DHCP state only; use SSH plus VirtualBox Guest Control rather
than treating it as a stable endpoint. VRDE is enabled only on Helium loopback
at 127.0.0.1:3389 for diagnostics, while native Windows RDP is disabled in
the baseline.
Interactive browser access is a deliberately temporary recovery path only. See
test/rdp-access for the Docker-only,
self-signed HTTPS Guacamole lifecycle; it must be started only after the native
fixture controller has prepared and leased the VM, and stopped before reset.
The Linux production Compose asset has a separate, loopback-only smoke lane. It
uses a disposable self-signed key only under the ignored .test-runs tree,
starts the real non-root server and nginx services, probes TLS liveness and
readiness, and invokes rvc through the private socket. It leaves compact logs
and output for inspection, while removing its exact Compose project by default:
docker build -f deploy/Dockerfile.runtime -t rvbox-server:test .
scripts/test-production-compose run --run-id production-smoke
scripts/test-production-compose clean --run-id production-smoke --purge --yes
The canonical headless VirtualBox/Guest Control adapter is
scripts/windows/test-host. It is a POSIX controller script because the
fixture's VirtualBox host is Arch Linux and has no PowerShell runtime. The
controller connects to Helium over SSH; VBoxManage and the host-only password
file never need to exist on the Linux development controller. It takes the VM
identity, baseline snapshot, guest identity, and password-file only from host
environment variables, acquires an exclusive remote lease, and never writes
secrets to the repository, run manifest, or command line. Set
RVBOX_TEST_GUEST_PASSWORD_FILE to the mode-600 host-side file; the adapter
passes it only as VirtualBox --passwordfile.
The provisioned fixture's VM identity, fixed test-only account names, and
password-file path are safe defaults in that script and may be overridden for
another documented fixture. The fixed disposable-VM password remains only in
that mode-600 file; the controller never puts it on a command line, manifest,
log, or artifact.
The native lifecycle is status, prepare, stage, install, run,
collect, logs, stop, and reset. prepare verifies the VM and snapshot UUIDs,
restores the clean baseline, starts headless, waits for Guest Additions, and
proves that RVBoxClient is absent. stage copies a versioned non-secret test
bundle through a run-specific host directory to a run-specific guest directory.
install uses the fixture-only high-integrity automation principal to invoke
the real rvbox.exe --install-service path and proves completion through SCM.
run is for reconfiguration/restart scenarios after that first installation.
Neither action invokes the GUI-subsystem executable directly with the normal
Guest Control account. collect obtains only bounded/redacted artifacts.
logs is the narrow read-only service-startup/client-diagnostics action for a
retained prepared run. The service diagnostic file grants access to SYSTEM and
local Administrators only; it contains no command spool data. reset restores
the exact clean baseline and leaves the VM powered off. It
first permits a bounded ACPI shutdown; if that hangs, it force-powers off only
the exact leased disposable VM before snapshot restoration. That intentional
state loss is confined to the test isolation boundary.
This service-driven protocol is required because VirtualBox Guest Control
7.2.16 does not reliably complete a direct GUI-subsystem rvbox.exe run;
wrapping it in cmd.exe can leave the Guest Control wrapper waiting. Guest
Control is therefore limited to console-safe setup tools (sc.exe, whoami,
query, bounded file operations) and artifact collection. A native E2E run
also performs a bounded guest-to-nginx HTTPS/TCP readiness probe before it
starts the service. Wine and protocol stubs are not equivalent Windows
coverage.
For the hierarchy fixture only, run --fail-contexts ACTIVE_USER_ELEVATED[,ACTIVE_SYSTEM] restarts the separately tagged test service and forces the named token preparation step to fail before process creation. This proves the real daemon's fallback order without changing the wire protocol, normal client TOML, or release binary. The logoff action ends the sole active fixture session so the LocalService and no-user LocalSystem rows can be tested with the same running service.
The clean baseline intentionally contains no RVBox service, tray registration,
or RVBox state. Guest Control supplies rvboxtest with a filtered medium UAC
token, so it cannot safely perform the first machine-wide install. The fixture
therefore uses its separately enabled built-in Administrator account as a
test-only full-token automation principal. Its FilterAdministratorToken must
remain 0, preserving UAC for rvboxtest rather than disabling it machine-wide.
Its username and mode-600 host-side password-file are provided only as
RVBOX_TEST_PROVISIONER_USER and RVBOX_TEST_PROVISIONER_PASSWORD_FILE for
the install/machine-mutation actions. It is not an RVBox process, service,
broker, or Task Scheduler dependency, and it is never used to choose a command
execution context. The normal rvboxtest console session remains the subject
of active-user and elevation tests. See testing-vm.md for the
exact fixture contract.
The VM is the current Windows 10 v1 smoke lane. Native multi-session/ambiguous-session, Server Core, and older-build entries are explicitly deferred compatibility work until their own fixtures exist; they do not block the Windows 10 v1 baseline. Their pure selector tests remain mandatory.