Skip to content

test: unit tests via httptest, concurrent stress, fork scenarios, benchmarks - #51

Merged
neverDefined merged 1 commit into
mainfrom
phase-3/test-depth
Apr 23, 2026
Merged

test: unit tests via httptest, concurrent stress, fork scenarios, benchmarks#51
neverDefined merged 1 commit into
mainfrom
phase-3/test-depth

Conversation

@neverDefined

@neverDefined neverDefined commented Apr 23, 2026

Copy link
Copy Markdown
Owner

Phase 3 PR F — fills the test-depth gaps.

Summary

Four new files plus subtests in the existing integration harness, adding +705 lines of test code.

`anvil_unit_test.go` — pure-Go, no live anvil (#47)

Runs without Foundry installed. Covers:

  • Builder validation — table-driven cases for `validate()`; `WithStartupTimeout` semantics (positive wins, zero/negative ignored); default propagation through `Build()`.
  • `resolveAnvilPath` — temp-dir fixtures for five scenarios: missing fallback (`ErrAnvilNotFound`), not-executable file (`ErrAnvilNotExecutable`), directory at expected path (`ErrAnvilNotExecutable`), executable fallback (success), `XDG_CONFIG_HOME` precedence over `HOME`. Skipped on Windows (POSIX-only check).
  • `retry` helper — success on first, success after N retries, attempts exhausted.
  • Error sentinels — `errors.Is` propagation through `fmt.Errorf("%w", ...)` for every `Err*` value.
  • RPC method shape via httptest — a recording JSON-RPC server asserts each RPC wrapper sends the right method and the right params. Covers all 17 mutating methods. Plus a ctx-cancel mid-call test against a slow server.

Contributors without Foundry can run the unit subset:

```
go test -run '^Test(AnvilBuilder|ResolveAnvilPath|Retry|SentinelErrors|RPC)' ./...
```

`anvil_test.go` — new integration subtests (#48)

  • `Test StartupTimeout exceeded` — builds with `WithStartupTimeout(1ms)`, expects `ErrStartupTimeout` from `Start()`.
  • `Test RPC ctx canceled` — cancels ctx, expects `context.Canceled` from an RPC call.
  • `Test Concurrent RPC calls` — 50 goroutines doing mixed `MineBlock` / `SetBalance` / `Metrics` reads for ~500ms; asserts metrics grew monotonically under concurrent load. Passes under `-race`.

`fork_test.go` — `//go:build fork` tag (#48)

Reads `ETH_RPC_URL` from env; skips cleanly if unset. CI default runs without the tag. Run locally:

```
ETH_RPC_URL=https://... go test -tags=fork -run Fork ./...
```

Covers `WithFork` at chain head and `WithForkBlockNumber` at a specific block.

`anvil_bench_test.go` — benchmarks (#48)

Four benchmarks backing the existing `make bench` target:

```
BenchmarkMineBlock-16 ~283 µs/op
BenchmarkSetBalance-16 ~195 µs/op
BenchmarkSnapshotRevertCycle-16 ~253 µs/op
BenchmarkResetState-16 ~380 µs/op
```

(Apple M4, localhost anvil 1.5.0.)

`.golangci.yml` — added `gosec` to `_test\.go` exclusions

Test fixtures legitimately create executable files (mode 0o755) which gosec G306 flags in production code. Not a concern in test-only code.

Test plan

  • `go build ./...` + `go build -tags=fork ./...` clean
  • `go vet ./...` clean
  • `golangci-lint run ./...` — 0 issues
  • `go test -race ./...` — all 24 subtests + 17 unit-RPC subtests + 3 new subtests green, ~24s
  • Benchmarks run clean with `-benchtime=2x`
  • CI matrix all four slots green + vuln + lint

Scope notes

  • No API changes.
  • No code changes in `anvil.go` or `helpers.go` — tests only.
  • `bloop` hints (`b.Loop()` for benchmarks) and `rangeint` hints (`for i := range N`) left as idiomatic-old-style to match existing codebase. Can sweep in a later cleanup.

Closes #47
Closes #48

What's left

🤖 Generated with Claude Code

…chmarks

Fills the test-depth gaps flagged in Phase 3. Four new files plus
subtests in the existing integration file.

anvil_unit_test.go — pure-Go tests, no live anvil required. Covers
builder validation, WithStartupTimeout semantics, resolveAnvilPath
with temp-dir fixtures (missing / not executable / directory /
executable / XDG vs HOME precedence), the retry helper's backoff and
exhaustion paths, errors.Is propagation for every sentinel, and RPC
method shape via an httptest JSON-RPC server that records method +
params and verifies ctx cancellation mid-call. Contributors without
Foundry can run the unit subset via
  go test -run '^Test(AnvilBuilder|ResolveAnvilPath|Retry|SentinelErrors|RPC)' ./...

anvil_test.go — three new subtests on the existing shared-anvil
harness: startup-timeout-exceeded expects ErrStartupTimeout, RPC with
cancelled ctx returns context.Canceled, and a 50-goroutine concurrent
stress test (~500ms burst of MineBlock / SetBalance / Metrics reads)
that exercises atomics under contention and validates no races under
-race.

fork_test.go — new file behind //go:build fork. Reads ETH_RPC_URL and
skips when unset. Covers WithFork at chain head and
WithForkBlockNumber at a specific block. CI default is tag-off so
these are skipped; run locally with
  ETH_RPC_URL=https://... go test -tags=fork -run Fork ./...

anvil_bench_test.go — BenchmarkMineBlock, BenchmarkSetBalance,
BenchmarkSnapshotRevertCycle, BenchmarkResetState. Sensible numbers
locally (~200–400 µs/op on Apple M4); backs the make bench target.

.golangci.yml — added gosec to the _test\.go exclusion list. The test
fixtures legitimately create executable files (mode 0o755) which
gosec G306 flags in production code but not in tests.

CONTRIBUTING.md — documents the unit-only and fork-mode test
invocations.

CHANGELOG.md — Tests section under [Unreleased].

Verified locally: go build / vet / lint clean, go test -race ./...
green in ~24s, benchmarks run clean.

Closes #47
Closes #48

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@neverDefined
neverDefined merged commit e4fdf01 into main Apr 23, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant