This project uses ExUnit. Tests are mandatory for every code change — see AGENTS.md. Add or update tests in the same PR as any lib/ change; do not rely on manual verification alone.
# Lint (CI runs this)
mix lint
# Single file (preferred while developing)
mix test test/network/rpc_http_test.exs
# Isolated tests without anvil / full app boot
DIODE_MINIMAL_TEST=1 mix test --no-start test/network/rpc_http_test.exs
# TURN unit tests only
mix test.turn
# Full suite (starts local anvil chain — slower)
mix testCI currently runs mix lint. Tests are still required in every PR so regressions are caught locally and can be enabled in CI later.
- Every changed module under
lib/has matching coverage undertest/(new file or updated assertions). - Run
mix lintand the narrowestmix test path/to/your_test.exsthat covers your change. - For bug fixes, confirm the new test fails on the base branch before your fix.
| Path | Purpose |
|---|---|
test/test_helper.exs |
ExUnit config, TestHelper, anvil chain startup |
test/helpers/ |
Shared test modules compiled via elixirc_paths(:test) |
test/network/ |
RPC, WebSocket, Edge, and HTTP tests |
test/diode/turn/ |
TURN server unit tests (mix test.turn) |
Mirror lib/ module paths when adding new tests.
Call the plug with Plug.Test — no need to bind a port:
defmodule Network.RpcHttpTest do
use ExUnit.Case, async: true
import Plug.Conn
import Plug.Test
@opts Network.RpcHttp.init([])
test "GET /api renders without error" do
conn = :get |> conn("/api") |> Network.RpcHttp.call(@opts)
assert conn.status == 200
assert String.contains?(conn.resp_body, "dio_version")
end
endFull example: test/network/rpc_http_test.exs (covers server notification docs that omit example_request).
- Reproduce the failure on
main(crash, wrong response, missing field). - Add a test that asserts the broken symptom.
- Confirm the test fails before your fix and passes after.
- Keep the test focused — one scenario per test when possible.
When changing Globals.cache/3, refresh, or per-key storage:
- Assert cache key shape (e.g.
{Module, chain_id}not a single:allkey). - Cover first-access initialization and that a second read hits the cache.
- Cover refresh/invalidation: updated values for cached keys, uncached keys unchanged.
Example: test/remote_chain/chain_list_test.exs.
When maps omit keys (e.g. notification docs without example_request), use Access syntax in templates (doc[:field]) and test both the data shape and the rendered output.
@tag :requires_wireguard— excluded unlessWIREGUARD_ENABLED=1DIODE_MINIMAL_TEST=1— skips anvil startup intest_helper.exsfor fast isolated tests
Multi-node and chain-dependent tests use TestHelper (reset/0, start_clones/1, wallets, anvil). See existing files under test/network/ for WebSocket ticket billing and Edge e2e patterns.
For WireGuard-specific manual and automated steps, see TEST_EXECUTION_GUIDE.md.