docs: document expanded test coverage and the fake ElectrumX server
README's "Running tests" section now lists what each test area covers and how to measure coverage; CLAUDE.md/AGENTS.md point future work at extending the fake ElectrumX server instead of mocking ElectrumClient (it isn't an interface, by design).
This commit is contained in:
@@ -29,6 +29,7 @@ src/App/ shared Avalonia UI library (App, Views, ViewModels, Loc, Asset
|
||||
src/App.Desktop/ desktop head (WinExe): Program.cs, app.manifest, .ico -> runnable
|
||||
src/App.Android/ Android head (net10.0-android): MainApplication/MainActivity -> apk
|
||||
src/Cli/ CLI on the same Core tests/ xUnit
|
||||
docker/ reproducible release builds (build.sh + pinned Dockerfiles) -> dist/
|
||||
```
|
||||
|
||||
The Avalonia UI lives **once** in `src/App` (a library); the two heads only carry the
|
||||
@@ -43,11 +44,12 @@ by `MainWindow` on desktop and as the single-view root on Android.
|
||||
`export PATH="$HOME/.dotnet10:$PATH" DOTNET_ROOT="$HOME/.dotnet10"`.
|
||||
|
||||
- Build: `dotnet build`
|
||||
- Tests (headless, the primary verification layer): `dotnet test` -- single: `dotnet test --filter "FullyQualifiedName~TestName"`; property-based tests (CsCheck, `PropertyTests.cs`) run in the same command and take ~30 s
|
||||
- Tests (headless, the primary verification layer): `dotnet test` -- single: `dotnet test --filter "FullyQualifiedName~TestName"`; property-based tests (CsCheck, `PropertyTests.cs`) run in the same command and take ~30 s; coverage: `dotnet test tests/PalladiumWallet.Tests --collect:"XPlat Code Coverage"` (coverlet, Cobertura XML). Network/SPV code (`ElectrumClient`, `WalletSynchronizer`, `TransactionInspector`, TOFU pinning) is tested against the in-process fake ElectrumX server in `tests/PalladiumWallet.Tests/Net/FakeElectrumServer.cs` (loopback TCP + optional TLS, per-method handlers, call counters): extend that, don't mock the client -- it isn't an interface, by design.
|
||||
- GUI hot reload: `dotnet watch --project src/App.Desktop` (on WSL2/WSLg the window shows on the Windows desktop, no graphics dependencies to install)
|
||||
- CLI: `dotnet run --project src/Cli -- <command>` (no args -> usage)
|
||||
- Windows publish: `dotnet publish src/App.Desktop -r win-x64 -p:PublishSingleFile=true --self-contained`
|
||||
- Linux publish: `dotnet publish src/App.Desktop -r linux-x64 --self-contained` (then AppImage via PupNet Deploy)
|
||||
- **Release binaries (all 3 targets): `./docker/build.sh [windows|linux|android|all]`** -- reproducible builds in Docker (toolchain pinned in `docker/Dockerfile.*`, no SDK needed on host), artifacts in `dist/`, version taken from the App csproj. See `docker/README.md`. Gotchas already encoded there: single-file desktop publishes need `-p:IncludeNativeLibrariesForSelfExtract=true` (without it Avalonia's native libs -- Skia/HarfBuzz/ANGLE -- stay outside the exe, which then silently fails to start); the android workload dictates the SDK API level (error XA5207 -> bump `ANDROID_SDK_PLATFORM` in `Dockerfile.android`). Android release builds need a persistent signing keystore, generated once with `docker/keystore/generate-keystore.sh` (never committed -- see `docker/keystore/README.md`): without it every build gets a different random signature and users must uninstall the old app to receive an update instead of updating in place.
|
||||
- Manual Windows publish: `dotnet publish src/App.Desktop -r win-x64 -p:PublishSingleFile=true -p:IncludeNativeLibrariesForSelfExtract=true --self-contained`
|
||||
- Manual Linux publish: same with `-r linux-x64` (single-file binary; AppImage via PupNet Deploy is a future step, no pupnet.conf yet)
|
||||
|
||||
**Android (apk).** Needs the `android` workload (`dotnet workload install android`), a JDK
|
||||
(`JAVA_HOME`), and the Android SDK. To provision the SDK once:
|
||||
@@ -82,3 +84,4 @@ Note: a plain `dotnet build` at the solution level needs the Android SDK path fo
|
||||
|
||||
- **Cross-implementation tests:** compare addresses, txids, and PSBTs against a reference wallet (golden vectors). A different address or txid is a blocking bug.
|
||||
- **Security:** seed and private keys never in plaintext on disk/logs/network; every server response validated with Merkle + checkpoints; watch-only truly read-only.
|
||||
- **Releases:** whenever a new version tag is created (bumping `<Version>` in `src/App/PalladiumWallet.App.csproj`), update `CHANGELOG.md` with an entry for that version before/with the tag -- it's the technical record of what shipped, not optional bookkeeping.
|
||||
|
||||
@@ -44,7 +44,7 @@ by `MainWindow` on desktop and as the single-view root on Android.
|
||||
`export PATH="$HOME/.dotnet10:$PATH" DOTNET_ROOT="$HOME/.dotnet10"`.
|
||||
|
||||
- Build: `dotnet build`
|
||||
- Tests (headless, the primary verification layer): `dotnet test` — single: `dotnet test --filter "FullyQualifiedName~TestName"`; property-based tests (CsCheck, `PropertyTests.cs`) run in the same command and take ~30 s
|
||||
- Tests (headless, the primary verification layer): `dotnet test` — single: `dotnet test --filter "FullyQualifiedName~TestName"`; property-based tests (CsCheck, `PropertyTests.cs`) run in the same command and take ~30 s; coverage: `dotnet test tests/PalladiumWallet.Tests --collect:"XPlat Code Coverage"` (coverlet, Cobertura XML). Network/SPV code (`ElectrumClient`, `WalletSynchronizer`, `TransactionInspector`, TOFU pinning) is tested against the in-process fake ElectrumX server in `tests/PalladiumWallet.Tests/Net/FakeElectrumServer.cs` (loopback TCP + optional TLS, per-method handlers, call counters): extend that, don't mock the client — it isn't an interface, by design.
|
||||
- GUI hot reload: `dotnet watch --project src/App.Desktop` (on WSL2/WSLg the window shows on the Windows desktop, no graphics dependencies to install)
|
||||
- CLI: `dotnet run --project src/Cli -- <command>` (no args → usage)
|
||||
- **Release binaries (all 3 targets): `./docker/build.sh [windows|linux|android|all]`** — reproducible builds in Docker (toolchain pinned in `docker/Dockerfile.*`, no SDK needed on host), artifacts in `dist/`, version taken from the App csproj. See `docker/README.md`. Gotchas already encoded there: single-file desktop publishes need `-p:IncludeNativeLibrariesForSelfExtract=true` (without it Avalonia's native libs — Skia/HarfBuzz/ANGLE — stay outside the exe, which then silently fails to start); the android workload dictates the SDK API level (error XA5207 → bump `ANDROID_SDK_PLATFORM` in `Dockerfile.android`). Android release builds need a persistent signing keystore, generated once with `docker/keystore/generate-keystore.sh` (never committed — see `docker/keystore/README.md`): without it every build gets a different random signature and users must uninstall the old app to receive an update instead of updating in place.
|
||||
|
||||
@@ -139,9 +139,34 @@ Run only the tests in one project:
|
||||
dotnet test tests/PalladiumWallet.Tests
|
||||
```
|
||||
|
||||
Measure code coverage (the [coverlet](https://github.com/coverlet-coverage/coverlet) collector is already referenced; the report is a Cobertura XML under `TestResults/`):
|
||||
```bash
|
||||
dotnet test tests/PalladiumWallet.Tests --collect:"XPlat Code Coverage"
|
||||
```
|
||||
|
||||
> Cross-implementation tests compare addresses, txids and PSBTs against reference golden vectors: a different address or txid is a blocking bug.
|
||||
|
||||
The suite includes **property-based tests** ([CsCheck](https://github.com/AnthonyLloyd/CsCheck)) in `tests/PalladiumWallet.Tests/PropertyTests.cs`. These generate hundreds of random inputs per test and verify invariants that must hold universally — no crash on arbitrary strings, encrypt/decrypt roundtrip for any plaintext and password, every leaf in a randomly-built Merkle tree verifies against its root. They run automatically with `dotnet test` and take ~30 s.
|
||||
### What the suite covers
|
||||
|
||||
The tests mirror the `Core` layout (`tests/PalladiumWallet.Tests/<area>/`):
|
||||
|
||||
| Area | What is verified |
|
||||
|---|---|
|
||||
| `Chain/` | Network profiles (prefixes, ports, coin type 746, SLIP-132 headers), NBitcoin network registration |
|
||||
| `Crypto/` | BIP39 (official Trezor vectors, NFKD normalisation), BIP32/44/49/84/86 derivation against public golden vectors, SLIP-132 encode/decode, HD and imported-key accounts, watch-only isolation |
|
||||
| `Spv/` | Scripthash (vectors computed independently in Python), Merkle proofs (Bitcoin block 100000 + random trees), header parsing, and the full **`WalletSynchronizer`**: gap-limit scanning, UTXO/history reconstruction, unconfirmed/immature balances, busy-retry, disk-cache reuse — including the path where a lying server fails Merkle verification and the sync must abort |
|
||||
| `Net/` | JSON-RPC transport (pipelining, error mapping, notifications, disconnection, cancellation), typed protocol wrappers, peer discovery/persistence, TLS trust-on-first-use pinning end-to-end (pin, match, mismatch, reset), release-tag parsing |
|
||||
| `Storage/` | AES-GCM encryption (roundtrip, tampering, fresh salt/nonce), wallet document schema/versioning, atomic saves, single-instance lock, data paths |
|
||||
| `Wallet/` | Transaction building and signing (spendability rules, coinbase maturity, dust change, multi-UTXO selection, all standard destination types, watch-only PSBT flow, a golden txid for the PSBT signing path), transaction detail assembly (fees, mine/theirs attribution, RBF, coinbase), amount parsing/formatting |
|
||||
|
||||
Network-facing code is tested against an **in-process fake ElectrumX server**
|
||||
(`tests/PalladiumWallet.Tests/Net/FakeElectrumServer.cs`): a real loopback TCP
|
||||
socket speaking newline-delimited JSON-RPC (optionally TLS with a self-signed
|
||||
certificate), with per-method handlers and call counters. Client, synchroniser
|
||||
and inspector therefore exercise the same code paths used in production —
|
||||
framing, retries, TLS pinning included — without any external dependency.
|
||||
|
||||
The suite also includes **property-based tests** ([CsCheck](https://github.com/AnthonyLloyd/CsCheck)) in `tests/PalladiumWallet.Tests/PropertyTests.cs`. These generate hundreds of random inputs per test and verify invariants that must hold universally — no crash on arbitrary strings, encrypt/decrypt roundtrip for any plaintext and password, SLIP-132 key roundtrip for every script kind and network, every leaf in a randomly-built Merkle tree verifies against its root. They run automatically with `dotnet test` and take ~30 s.
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user