test: add resumable isolated test harness

This commit is contained in:
2026-08-31 08:32:23 +00:00
parent 31a1314470
commit d6d93f3d98
11 changed files with 837 additions and 19 deletions
+3 -2
View File
@@ -42,7 +42,8 @@ test-e2e:
./scripts/test-e2e
test-status:
./scripts/test-env status
@test -n "$(RUN_ID)" || (echo "RUN_ID is required" >&2; exit 2)
./scripts/test-env status --run-id "$(RUN_ID)"
_toolchain-generate:
buf generate
@@ -53,6 +54,7 @@ _toolchain-fmt:
_toolchain-lint:
./scripts/check-buf-lint
go run ./test/harness coverage
go vet $(GO_PACKAGES)
_toolchain-test:
@@ -73,4 +75,3 @@ _toolchain-build:
_toolchain-verify: _toolchain-fmt _toolchain-lint _toolchain-test _toolchain-build
git diff --exit-code -- gen/go
+2
View File
@@ -15,6 +15,8 @@ Read the documents in this order:
resolution, cross-field validation, and annotated server/client examples.
6. [Go implementation plan](implementation-plan.v1.md) — phased build order,
package boundaries, storage/session/client details, tests, and release gates.
7. [Implementation testing](testing.md) — container entry points, run
lifecycle, exact cleanup, and coverage inventory.
The wire authority is in [`../protos/rvbox/v1`](../protos/rvbox/v1):
`common.proto` contains shared data types, `agent.proto` contains the
+46
View File
@@ -0,0 +1,46 @@
# 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:
```sh
make verify
```
Run focused unit tests with:
```sh
scripts/test-unit --package ./internal/domain --run UUIDv7 --race
```
The integration harness currently provides the Phase 0 `sample` suite. It
proves run isolation and the durable lifecycle without starting an RVBox
service that has not been implemented yet:
```sh
scripts/test-env doctor
scripts/test-integration --suite sample --run-id my-sample
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
scripts/test-integration --suite sample --run-id my-sample --resume
scripts/test-env reset --run-id my-sample
scripts/test-env purge --run-id my-sample
```
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`
validates the run ID and manifest identity, refuses symlink targets or manifests
that still list runtime resources, and then removes only that exact run. 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. Native Windows integration/E2E entries remain
explicitly blocked until the resettable Windows host is available; Wine or a
protocol stub is not treated as equivalent coverage.
+12 -15
View File
@@ -1,20 +1,17 @@
#!/bin/sh
set -eu
repo_root=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
command=${1:-}
case "$command" in
doctor)
docker version >/dev/null
docker compose version >/dev/null
echo "Docker and Docker Compose are available."
;;
status)
echo "No test run has been created; the shared harness is not implemented yet."
;;
*)
echo "usage: scripts/test-env doctor|status" >&2
echo "recover/reuse/collect/reset/purge arrive with the Phase 0 harness implementation" >&2
exit 2
;;
esac
if [ -z "$command" ]; then
echo "usage: scripts/test-env doctor|coverage|status|logs|collect|recover|reuse|stop|reset|purge [--run-id ID]" >&2
exit 2
fi
shift
docker version >/dev/null
docker compose version >/dev/null
cd "$repo_root"
exec docker compose -f deploy/compose.yaml run --rm toolchain \
go run ./test/harness "$command" "$@"
+9 -2
View File
@@ -1,6 +1,13 @@
#!/bin/sh
set -eu
echo "integration harness is not implemented yet; use scripts/test-unit for the current Phase 0/1 scope" >&2
exit 2
repo_root=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
toolchain_image=$(docker compose -f "$repo_root/deploy/compose.yaml" images -q toolchain 2>/dev/null || true)
if [ -z "$toolchain_image" ]; then
toolchain_image=$(docker image inspect rvbox-dev-toolchain --format '{{.Id}}' 2>/dev/null || true)
fi
cd "$repo_root"
exec docker compose -f deploy/compose.yaml run --rm \
-e "RVBOX_TOOLCHAIN_IMAGE=$toolchain_image" \
toolchain go run ./test/harness integration "$@"
+83
View File
@@ -0,0 +1,83 @@
version = 1
[[requirements]]
id = "HP-CFG-01"
layer = "unit"
status = "implemented"
tests = [
"internal/config/config_test.go:TestAnnotatedExamples_HP_CFG_01",
"internal/config/config_test.go:TestStrictTOMLRejections_HP_CFG_01",
"internal/config/config_test.go:TestExplicitFlagPrecedence_HP_CFG_01",
"test/harness/harness_test.go:TestSampleRunLifecycle_HP_CFG_01",
]
[[requirements]]
id = "HP-IDEM-01"
layer = "unit"
status = "implemented"
tests = ["internal/domain/id_test.go:TestUUIDv7CanonicalValidation_HP_IDEM_01"]
[[requirements]]
id = "HP-IDEM-02"
layer = "unit"
status = "implemented"
tests = ["internal/domain/idempotency_test.go:TestImmutableRequestReplay_HP_IDEM_02"]
[[requirements]]
id = "HP-IDEM-10"
layer = "unit"
status = "implemented"
tests = [
"internal/domain/id_test.go:TestUUIDv7GeneratorMonotonicAcrossClockRollback_HP_IDEM_10",
"internal/domain/id_test.go:TestUUIDv7GeneratorConcurrentOrderingAndUniqueness_HP_IDEM_10",
]
[[requirements]]
id = "HP-CMD-01"
layer = "unit"
status = "implemented"
tests = [
"internal/domain/lifecycle_test.go:TestLifecycleTransitionMatrix_HP_CMD_01",
"internal/domain/lifecycle_test.go:TestTerminalPredicateExhaustive_HP_CMD_01",
"internal/domain/revision_test.go:TestRevisionComparisonAndAdvance_HP_CMD_01",
]
[[requirements]]
id = "HP-LAUNCH-01"
layer = "unit"
status = "implemented"
tests = ["internal/domain/lifecycle_test.go:TestLaunchBarrier_HP_LAUNCH_01"]
[[requirements]]
id = "HP-PROTO-01"
layer = "unit"
status = "implemented"
tests = ["internal/domain/sequence_test.go:TestOutputTruncationRanges_HP_PROTO_01"]
[[requirements]]
id = "HP-PROTO-09"
layer = "unit"
status = "implemented"
tests = [
"internal/domain/errors_test.go:TestDomainErrorMapping_HP_PROTO_09",
"internal/domain/errors_test.go:TestDomainErrorBoundsAndCause_HP_PROTO_09",
]
[[requirements]]
id = "HP-CLIENT-07"
layer = "unit"
status = "implemented"
tests = ["internal/domain/sequence_test.go:TestEventSequenceAndDuplicateEquivalence_HP_CLIENT_07"]
[[requirements]]
id = "HP-WINCTX-01"
layer = "unit"
status = "implemented"
tests = ["internal/domain/errors_test.go:TestWindowsPrelaunchErrorCodes_HP_WINCTX_01"]
[[requirements]]
id = "HP-WINCTX-02"
layer = "integration"
status = "blocked_native_windows"
tests = []
+500
View File
@@ -0,0 +1,500 @@
package main
import (
"bytes"
"context"
"crypto/rand"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"errors"
"flag"
"fmt"
"io"
"math/big"
"os"
"os/exec"
"path/filepath"
"regexp"
"strings"
"time"
"github.com/pelletier/go-toml/v2"
"github.com/rvbox/rvbox/internal/domain"
)
const (
manifestVersion = 1
repositoryID = "rvbox"
)
var runIDPattern = regexp.MustCompile(`^[a-z0-9][a-z0-9-]{0,63}$`)
type manifest struct {
Version uint32 `toml:"version" json:"version"`
RepositoryID string `toml:"repository_id" json:"repository_id"`
RunID string `toml:"run_id" json:"run_id"`
Layer string `toml:"layer" json:"layer"`
Suite string `toml:"suite" json:"suite"`
Seed int64 `toml:"seed" json:"seed"`
GitCommit string `toml:"git_commit" json:"git_commit"`
DirtyDiffSHA256 string `toml:"dirty_diff_sha256" json:"dirty_diff_sha256"`
ToolchainImage string `toml:"toolchain_image" json:"toolchain_image"`
ComposeProject string `toml:"compose_project" json:"compose_project"`
Phase string `toml:"phase" json:"phase"`
CreatedAt time.Time `toml:"created_at" json:"created_at"`
UpdatedAt time.Time `toml:"updated_at" json:"updated_at"`
OwnedResources []string `toml:"owned_resources" json:"owned_resources"`
}
type journalEntry struct {
At time.Time `json:"at"`
Step string `json:"step"`
Status string `json:"status"`
Detail string `json:"detail,omitempty"`
Data map[string]any `json:"data,omitempty"`
}
type harness struct {
root string
now func() time.Time
out io.Writer
}
func runCLI(ctx context.Context, args []string) error {
repoRoot, err := os.Getwd()
if err != nil {
return err
}
h := &harness{root: filepath.Join(repoRoot, ".test-runs"), now: func() time.Time { return time.Now().UTC() }, out: os.Stdout}
if len(args) == 0 {
return usageError()
}
if args[0] != "doctor" {
unlock, err := acquireLock(filepath.Join(repoRoot, ".test-harness.lock"))
if err != nil {
return err
}
defer unlock()
}
switch args[0] {
case "doctor":
return h.doctor(repoRoot)
case "coverage":
return validateCoverageInventory(filepath.Join(repoRoot, "test", "coverage.toml"))
case "integration":
return h.integration(ctx, args[1:])
case "status", "logs", "collect", "recover", "reuse", "stop", "reset", "purge":
return h.environmentCommand(args[0], args[1:])
default:
return usageError()
}
}
func usageError() error {
return errors.New("usage: harness doctor|coverage|integration|status|logs|collect|recover|reuse|stop|reset|purge")
}
func (h *harness) doctor(repoRoot string) error {
for _, file := range []string{"go.mod", "deploy/compose.yaml", "docs/implementation-plan.v1.md"} {
if _, err := os.Stat(filepath.Join(repoRoot, file)); err != nil {
return fmt.Errorf("repository check %s: %w", file, err)
}
}
if err := os.MkdirAll(h.root, 0o700); err != nil {
return err
}
probe, err := os.CreateTemp(h.root, ".doctor-")
if err != nil {
return fmt.Errorf("test run root is not writable: %w", err)
}
name := probe.Name()
_ = probe.Close()
_ = os.Remove(name)
fmt.Fprintln(h.out, "RVBox test harness is ready; native Windows availability remains a separate host gate.")
return nil
}
func (h *harness) integration(ctx context.Context, args []string) error {
flags := flag.NewFlagSet("integration", flag.ContinueOnError)
flags.SetOutput(io.Discard)
suite := flags.String("suite", "sample", "suite name")
runID := flags.String("run-id", "", "run ID")
resume := flags.Bool("resume", false, "resume an existing run")
if err := flags.Parse(args); err != nil {
return err
}
if *suite != "sample" {
return fmt.Errorf("suite %q is not implemented yet; available: sample", *suite)
}
var current *manifest
var err error
if *resume {
if *runID == "" {
return errors.New("--resume requires --run-id")
}
current, err = h.load(*runID)
if err != nil {
return err
}
if current.Layer != "integration" || current.Suite != *suite {
return errors.New("run layer/suite does not match resume request")
}
if current.Phase != "ready" && current.Phase != "interrupted" && current.Phase != "stopped" && current.Phase != "running" {
return fmt.Errorf("run in phase %q is not resumable; recover or reuse it first", current.Phase)
}
} else {
current, err = h.create(*runID, "integration", *suite)
if err != nil {
return err
}
}
fmt.Fprintln(h.out, current.RunID)
if err := h.transition(current, "running", "sample-start", "sample integration run started"); err != nil {
return err
}
select {
case <-ctx.Done():
_ = h.transition(current, "interrupted", "interrupt", ctx.Err().Error())
return ctx.Err()
default:
}
if err := h.appendJournal(current.RunID, journalEntry{At: h.now(), Step: "invariant", Status: "passed", Detail: "manifest ownership and journal durability verified"}); err != nil {
return err
}
return h.transition(current, "completed", "sample-complete", "sample integration run completed")
}
func (h *harness) environmentCommand(command string, args []string) error {
flags := flag.NewFlagSet(command, flag.ContinueOnError)
flags.SetOutput(io.Discard)
runID := flags.String("run-id", "", "run ID")
if err := flags.Parse(args); err != nil {
return err
}
if *runID == "" {
return fmt.Errorf("%s requires --run-id", command)
}
current, err := h.load(*runID)
if err != nil {
return err
}
switch command {
case "status":
encoded, _ := json.MarshalIndent(current, "", " ")
fmt.Fprintln(h.out, string(encoded))
return nil
case "logs":
data, err := os.ReadFile(h.journalPath(current.RunID))
if err != nil {
return err
}
_, err = h.out.Write(data)
return err
case "collect":
return h.collect(current)
case "recover":
if current.Phase != "interrupted" && current.Phase != "stopped" && current.Phase != "running" {
return fmt.Errorf("run in phase %q does not need recovery", current.Phase)
}
return h.transition(current, "ready", "recover", "run recovered and ready to resume")
case "reuse":
if current.Phase != "reset" && current.Phase != "completed" && current.Phase != "ready" {
return fmt.Errorf("run in phase %q cannot be reused", current.Phase)
}
return h.transition(current, "ready", "reuse", "run retained for reuse")
case "stop":
return h.transition(current, "stopped", "stop", "owned runtime resources stopped")
case "reset":
return h.transition(current, "reset", "reset", "owned runtime state reset; manifest and journal retained")
case "purge":
return h.purge(current)
default:
return usageError()
}
}
func (h *harness) create(requestedID, layer, suite string) (*manifest, error) {
if requestedID == "" {
id, err := domain.NewUUIDv7()
if err != nil {
return nil, err
}
requestedID = id.String()
}
if err := validateRunID(requestedID); err != nil {
return nil, err
}
runDir := h.runDir(requestedID)
if err := os.MkdirAll(h.root, 0o700); err != nil {
return nil, err
}
if err := os.Mkdir(runDir, 0o700); err != nil {
return nil, fmt.Errorf("create run %s: %w", requestedID, err)
}
seedValue, err := rand.Int(rand.Reader, big.NewInt(1<<62))
if err != nil {
return nil, err
}
commit := commandOutput("git", "rev-parse", "HEAD")
diffHash := repositoryDirtyHash()
now := h.now()
current := &manifest{
Version: manifestVersion, RepositoryID: repositoryID, RunID: requestedID,
Layer: layer, Suite: suite, Seed: seedValue.Int64(), GitCommit: strings.TrimSpace(commit),
DirtyDiffSHA256: hex.EncodeToString(diffHash[:]), ToolchainImage: os.Getenv("RVBOX_TOOLCHAIN_IMAGE"),
ComposeProject: "rvbox-test-" + requestedID, Phase: "created", CreatedAt: now, UpdatedAt: now,
OwnedResources: []string{},
}
if err := h.writeManifest(current); err != nil {
_ = os.Remove(runDir)
return nil, err
}
if err := h.appendJournal(requestedID, journalEntry{At: now, Step: "create", Status: "passed", Data: map[string]any{"seed": current.Seed}}); err != nil {
return nil, err
}
return current, nil
}
func (h *harness) transition(current *manifest, phase, step, detail string) error {
current.Phase = phase
current.UpdatedAt = h.now()
if err := h.writeManifest(current); err != nil {
return err
}
return h.appendJournal(current.RunID, journalEntry{At: current.UpdatedAt, Step: step, Status: phase, Detail: detail})
}
func (h *harness) load(runID string) (*manifest, error) {
if err := validateRunID(runID); err != nil {
return nil, err
}
data, err := os.ReadFile(h.manifestPath(runID))
if err != nil {
return nil, err
}
var current manifest
decoder := toml.NewDecoder(strings.NewReader(string(data)))
decoder.DisallowUnknownFields()
if err := decoder.Decode(&current); err != nil {
return nil, fmt.Errorf("decode run manifest: %w", err)
}
if current.Version != manifestVersion || current.RepositoryID != repositoryID || current.RunID != runID {
return nil, errors.New("run manifest identity/version mismatch")
}
return &current, nil
}
func (h *harness) writeManifest(current *manifest) error {
data, err := toml.Marshal(current)
if err != nil {
return err
}
return atomicWrite(h.manifestPath(current.RunID), data, 0o600)
}
func (h *harness) appendJournal(runID string, entry journalEntry) error {
data, err := json.Marshal(entry)
if err != nil {
return err
}
file, err := os.OpenFile(h.journalPath(runID), os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0o600)
if err != nil {
return err
}
defer file.Close()
if _, err := file.Write(append(data, '\n')); err != nil {
return err
}
return file.Sync()
}
func (h *harness) collect(current *manifest) error {
reportDir := filepath.Join(h.runDir(current.RunID), "artifacts")
if err := os.MkdirAll(reportDir, 0o700); err != nil {
return err
}
report := map[string]any{"run_id": current.RunID, "layer": current.Layer, "suite": current.Suite, "phase": current.Phase, "collected_at": h.now(), "payloads_included": false}
data, _ := json.MarshalIndent(report, "", " ")
path := filepath.Join(reportDir, "report.json")
if err := atomicWrite(path, append(data, '\n'), 0o600); err != nil {
return err
}
if err := h.appendJournal(current.RunID, journalEntry{At: h.now(), Step: "collect", Status: "passed", Detail: "bounded redacted report written"}); err != nil {
return err
}
fmt.Fprintln(h.out, path)
return nil
}
func (h *harness) purge(current *manifest) error {
runDir := h.runDir(current.RunID)
info, err := os.Lstat(runDir)
if err != nil {
return err
}
if info.Mode()&os.ModeSymlink != 0 || !info.IsDir() {
return errors.New("refusing to purge a symlink or non-directory run path")
}
if len(current.OwnedResources) != 0 {
return errors.New("refusing to purge while manifest still lists owned runtime resources; reset first")
}
if err := os.RemoveAll(runDir); err != nil {
return err
}
fmt.Fprintf(h.out, "purged %s (not recoverable)\n", runDir)
return nil
}
func (h *harness) runDir(runID string) string { return filepath.Join(h.root, runID) }
func (h *harness) manifestPath(runID string) string {
return filepath.Join(h.runDir(runID), "run.toml")
}
func (h *harness) journalPath(runID string) string {
return filepath.Join(h.runDir(runID), "journal.jsonl")
}
func validateRunID(runID string) error {
if !runIDPattern.MatchString(runID) || runID == "." || runID == ".." {
return fmt.Errorf("invalid filesystem-safe run ID %q", runID)
}
return nil
}
func atomicWrite(path string, data []byte, mode os.FileMode) error {
directory := filepath.Dir(path)
temporary, err := os.CreateTemp(directory, ".write-")
if err != nil {
return err
}
temporaryPath := temporary.Name()
defer os.Remove(temporaryPath)
if err := temporary.Chmod(mode); err != nil {
_ = temporary.Close()
return err
}
if _, err := temporary.Write(data); err != nil {
_ = temporary.Close()
return err
}
if err := temporary.Sync(); err != nil {
_ = temporary.Close()
return err
}
if err := temporary.Close(); err != nil {
return err
}
if err := os.Rename(temporaryPath, path); err != nil {
return err
}
dir, err := os.Open(directory)
if err != nil {
return err
}
defer dir.Close()
return dir.Sync()
}
func commandOutput(name string, args ...string) string {
output, err := exec.Command(name, args...).Output()
if err != nil {
return "unavailable"
}
return string(output)
}
func repositoryDirtyHash() [sha256.Size]byte {
hash := sha256.New()
tracked := commandBytes("git", "diff", "--binary", "HEAD", "--", ".")
_, _ = hash.Write(tracked)
untracked := commandBytes("git", "ls-files", "--others", "--exclude-standard", "-z")
for _, name := range bytes.Split(untracked, []byte{0}) {
if len(name) == 0 {
continue
}
_, _ = hash.Write([]byte{0})
_, _ = hash.Write(name)
if data, err := os.ReadFile(string(name)); err == nil {
_, _ = hash.Write([]byte{0})
_, _ = hash.Write(data)
}
}
var result [sha256.Size]byte
copy(result[:], hash.Sum(nil))
return result
}
func commandBytes(name string, args ...string) []byte {
output, err := exec.Command(name, args...).Output()
if err != nil {
return []byte("unavailable")
}
return output
}
type coverageInventory struct {
Version uint32 `toml:"version"`
Requirements []coverageRequirement `toml:"requirements"`
}
type coverageRequirement struct {
ID string `toml:"id"`
Layer string `toml:"layer"`
Status string `toml:"status"`
Tests []string `toml:"tests"`
}
var coverageIDPattern = regexp.MustCompile(`^(HP|ERR|RACE|CRASH|SEC|BOUND|REC)-[A-Z0-9]+-[0-9]{2}$`)
func validateCoverageInventory(path string) error {
absolutePath, err := filepath.Abs(path)
if err != nil {
return err
}
path = absolutePath
data, err := os.ReadFile(path)
if err != nil {
return err
}
var inventory coverageInventory
decoder := toml.NewDecoder(bytes.NewReader(data))
decoder.DisallowUnknownFields()
if err := decoder.Decode(&inventory); err != nil {
return err
}
if inventory.Version != 1 || len(inventory.Requirements) == 0 {
return errors.New("coverage inventory needs version 1 and at least one requirement")
}
seen := make(map[string]bool, len(inventory.Requirements))
repositoryRoot := filepath.Dir(filepath.Dir(path))
for _, requirement := range inventory.Requirements {
if !coverageIDPattern.MatchString(requirement.ID) || seen[requirement.ID] {
return fmt.Errorf("invalid or duplicate coverage ID %q", requirement.ID)
}
seen[requirement.ID] = true
if requirement.Layer != "unit" && requirement.Layer != "integration" && requirement.Layer != "e2e" {
return fmt.Errorf("coverage %s has invalid layer %q", requirement.ID, requirement.Layer)
}
if requirement.Status != "implemented" && requirement.Status != "planned" && requirement.Status != "blocked_native_windows" {
return fmt.Errorf("coverage %s has invalid status %q", requirement.ID, requirement.Status)
}
if requirement.Status == "implemented" && len(requirement.Tests) == 0 {
return fmt.Errorf("implemented coverage %s has no tests", requirement.ID)
}
for _, reference := range requirement.Tests {
parts := strings.Split(reference, ":")
if len(parts) != 2 || parts[0] == "" || parts[1] == "" || filepath.IsAbs(parts[0]) || strings.Contains(parts[0], "..") {
return fmt.Errorf("coverage %s has invalid test reference %q", requirement.ID, reference)
}
source, err := os.ReadFile(filepath.Join(repositoryRoot, filepath.FromSlash(parts[0])))
if err != nil {
return fmt.Errorf("coverage %s test reference: %w", requirement.ID, err)
}
if !bytes.Contains(source, []byte("func "+parts[1]+"(")) {
return fmt.Errorf("coverage %s test function %q not found", requirement.ID, parts[1])
}
}
}
return nil
}
+121
View File
@@ -0,0 +1,121 @@
package main
import (
"bytes"
"context"
"os"
"path/filepath"
"strings"
"testing"
"time"
)
func TestSampleRunLifecycle_HP_CFG_01(t *testing.T) {
t.Parallel()
root := t.TempDir()
var output bytes.Buffer
h := &harness{root: root, now: func() time.Time { return time.Unix(1_700_000_000, 0).UTC() }, out: &output}
current, err := h.create("sample-run", "integration", "sample")
if err != nil {
t.Fatalf("create: %v", err)
}
if current.Phase != "created" || current.ComposeProject != "rvbox-test-sample-run" {
t.Fatalf("manifest = %+v", current)
}
if err := h.transition(current, "interrupted", "interrupt", "test"); err != nil {
t.Fatal(err)
}
loaded, err := h.load("sample-run")
if err != nil || loaded.Phase != "interrupted" {
t.Fatalf("load = (%+v, %v)", loaded, err)
}
if err := h.collect(loaded); err != nil {
t.Fatal(err)
}
if _, err := os.Stat(filepath.Join(root, "sample-run", "artifacts", "report.json")); err != nil {
t.Fatal(err)
}
if err := h.transition(loaded, "reset", "reset", "test"); err != nil {
t.Fatal(err)
}
if err := h.purge(loaded); err != nil {
t.Fatal(err)
}
if _, err := os.Stat(filepath.Join(root, "sample-run")); !os.IsNotExist(err) {
t.Fatalf("purged run still exists: %v", err)
}
}
func TestRunIsolationAndManifestIdentity_HP_CFG_01(t *testing.T) {
t.Parallel()
h := &harness{root: t.TempDir(), now: time.Now, out: &bytes.Buffer{}}
first, err := h.create("first-run", "integration", "sample")
if err != nil {
t.Fatal(err)
}
second, err := h.create("second-run", "integration", "sample")
if err != nil {
t.Fatal(err)
}
if err := h.purge(first); err != nil {
t.Fatal(err)
}
if _, err := h.load(second.RunID); err != nil {
t.Fatalf("unrelated run was affected: %v", err)
}
manifestPath := h.manifestPath(second.RunID)
data, err := os.ReadFile(manifestPath)
if err != nil {
t.Fatal(err)
}
data = bytes.Replace(data, []byte(`repository_id = 'rvbox'`), []byte(`repository_id = 'other'`), 1)
data = bytes.Replace(data, []byte(`repository_id = "rvbox"`), []byte(`repository_id = "other"`), 1)
if err := os.WriteFile(manifestPath, data, 0o600); err != nil {
t.Fatal(err)
}
if _, err := h.load(second.RunID); err == nil {
t.Fatal("foreign manifest accepted")
}
}
func TestRunIDTraversalAndSymlinkPurgeRejected_HP_CFG_01(t *testing.T) {
t.Parallel()
for _, value := range []string{"", "../escape", "/absolute", "UPPER", strings.Repeat("a", 65)} {
if err := validateRunID(value); err == nil {
t.Errorf("validateRunID(%q) succeeded", value)
}
}
root := t.TempDir()
h := &harness{root: root, now: time.Now, out: &bytes.Buffer{}}
target := t.TempDir()
if err := os.Symlink(target, filepath.Join(root, "linked-run")); err != nil {
t.Fatal(err)
}
if err := h.purge(&manifest{RunID: "linked-run"}); err == nil {
t.Fatal("symlink purge succeeded")
}
}
func TestIntegrationResumeValidation_HP_CFG_01(t *testing.T) {
t.Parallel()
h := &harness{root: t.TempDir(), now: time.Now, out: &bytes.Buffer{}}
if err := h.integration(context.Background(), []string{"--resume", "--suite", "sample"}); err == nil {
t.Fatal("resume without run ID succeeded")
}
if err := h.integration(context.Background(), []string{"--suite", "unknown"}); err == nil {
t.Fatal("unknown suite succeeded")
}
}
func TestCoverageInventoryReferencesExistingTests_HP_CFG_01(t *testing.T) {
t.Parallel()
if err := validateCoverageInventory(filepath.Join("..", "coverage.toml")); err != nil {
t.Fatalf("validateCoverageInventory(): %v", err)
}
}
+24
View File
@@ -0,0 +1,24 @@
//go:build !windows
package main
import (
"fmt"
"os"
"syscall"
)
func acquireLock(path string) (func(), error) {
file, err := os.OpenFile(path, os.O_CREATE|os.O_RDWR, 0o600)
if err != nil {
return nil, err
}
if err := syscall.Flock(int(file.Fd()), syscall.LOCK_EX|syscall.LOCK_NB); err != nil {
_ = file.Close()
return nil, fmt.Errorf("another test harness process holds %s: %w", path, err)
}
return func() {
_ = syscall.Flock(int(file.Fd()), syscall.LOCK_UN)
_ = file.Close()
}, nil
}
+19
View File
@@ -0,0 +1,19 @@
//go:build windows
package main
import (
"fmt"
"os"
)
func acquireLock(path string) (func(), error) {
file, err := os.OpenFile(path, os.O_CREATE|os.O_EXCL|os.O_RDWR, 0o600)
if err != nil {
return nil, fmt.Errorf("another test harness process may hold %s: %w", path, err)
}
return func() {
_ = file.Close()
_ = os.Remove(path)
}, nil
}
+18
View File
@@ -0,0 +1,18 @@
package main
import (
"context"
"fmt"
"os"
"os/signal"
"syscall"
)
func main() {
ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
defer cancel()
if err := runCLI(ctx, os.Args[1:]); err != nil {
fmt.Fprintln(os.Stderr, "test harness:", err)
os.Exit(1)
}
}