Rename the entire workspace:
- Crate packages: quicnprotochat-{core,proto,server,client,gui,p2p,mobile} -> quicproquo-*
- Binary names: quicnprotochat -> qpq, quicnprotochat-server -> qpq-server,
quicnprotochat-gui -> qpq-gui
- Default files: *-state.bin -> qpq-state.bin, *-server.toml -> qpq-server.toml,
*.db -> qpq.db
- Environment variable prefix: QUICNPROTOCHAT_* -> QPQ_*
- App identifier: chat.quicnproto.gui -> chat.quicproquo.gui
- Proto package: quicnprotochat.bench -> quicproquo.bench
- All documentation, Docker, CI, and script references updated
HKDF domain-separation strings and P2P ALPN remain unchanged for
backward compatibility with existing encrypted state and wire protocol.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
7.0 KiB
Testing Strategy
This page describes the testing structure, conventions, and current coverage for
quicproquo. All tests run with cargo test --workspace and must pass before
any code is merged.
For the coding standards that tests must follow, see Coding Standards.
Test Organisation
Unit Tests
Unit tests live alongside the code they test, in #[cfg(test)] mod tests blocks
at the bottom of each source file. They test individual functions and types in
isolation.
quicproquo-core:
| Module | Tests | What they cover |
|---|---|---|
codec |
7 tests | Length-prefixed frame encoding/decoding, edge cases (empty payload, max size, partial frame, exact boundary) |
keypair |
3 tests | Ed25519 keypair generation, public key extraction, deterministic re-derivation |
group |
2 tests | Group round-trip (create + add + join + send + recv), group_id lifecycle |
hybrid_kem |
11 tests | Encapsulate/decapsulate round-trip, key generation, combiner correctness, wrong-key rejection, serialisation |
quicproquo-proto:
| Module | Tests | What they cover |
|---|---|---|
lib |
3 tests | Cap'n Proto builder/reader round-trip, canonical serialisation, schema validation |
Integration Tests
Integration tests live in crates/quicproquo-client/tests/ and test the
full client-server interaction. Each test spawns a server using tokio::spawn
within the same test binary, then runs client operations against it.
| File | Milestone | What it covers |
|---|---|---|
auth_service.rs |
M2 | KeyPackage upload via AS, KeyPackage fetch (single-use consume semantics), identity key validation |
mls_group.rs |
M3 | Full MLS round-trip: register state, create group, add member via Welcome, send encrypted message, receive and decrypt |
Test Pattern
All integration tests follow the same pattern:
#[tokio::test]
async fn test_something() {
// 1. Start server in background
let server_handle = tokio::spawn(async move {
server::run(config).await.unwrap();
});
// 2. Wait for server to be ready
tokio::time::sleep(Duration::from_millis(100)).await;
// 3. Run client operations
let result = client::do_something(server_addr).await;
// 4. Assert
assert!(result.is_ok());
// ...
// 5. Cleanup
server_handle.abort();
}
This pattern ensures tests are self-contained and do not require an external server process.
Running Tests
Full Workspace
cargo test --workspace
This runs all unit tests and integration tests across all four crates.
Single Crate
cargo test -p quicproquo-core
cargo test -p quicproquo-proto
cargo test -p quicproquo-server
cargo test -p quicproquo-client
Single Test
cargo test -p quicproquo-core -- codec::tests::test_round_trip
cargo test -p quicproquo-client --test mls_group
With Output
cargo test --workspace -- --nocapture
Current Results
All tests pass as of the M3 milestone on branch feat/m1-noise-transport.
Summary:
| Crate | Unit Tests | Integration Tests | Total |
|---|---|---|---|
quicproquo-core |
23 | -- | 23 |
quicproquo-proto |
3 | -- | 3 |
quicproquo-server |
0 | -- | 0 |
quicproquo-client |
0 | 5 | 5 |
| Total | 26 | 5 | 31 |
Test Conventions
Naming
Test functions use descriptive names that state what is being tested and the expected outcome:
#[test]
fn encode_decode_round_trip_preserves_payload() { ... }
#[test]
fn empty_payload_produces_length_zero_frame() { ... }
#[test]
fn fetch_consumes_keypackage_single_use() { ... }
Assertions
- Use
assert_eq!with both expected and actual values. - Use
assert!(result.is_ok(), "descriptive message: {result:?}")forResultchecks. - For crypto operations, assert on specific error variants, not just
is_err().
No External Dependencies
Tests must not depend on external services, network access, or filesystem state
outside the test's temporary directory. The tokio::spawn pattern for
client-server tests ensures everything runs in-process.
Determinism
Tests must be deterministic. If randomness is needed (e.g., key generation), the test must not depend on specific random values -- only on the properties of the output (correct length, successful round-trip, etc.).
Planned Testing Enhancements
The following testing improvements are planned for future milestones:
Fuzzing Targets (M5+)
Fuzz testing for parser and deserialisation code:
- Cap'n Proto message parser: Feed arbitrary bytes to the Cap'n Proto reader and verify it either parses correctly or returns a typed error (no panics, no undefined behaviour).
- MLS message handler: Feed arbitrary
MLSMessagebytes to theGroupMember::receive_messagepath. Tool:cargo-fuzzwithlibfuzzer.
Golden-Wire Fixtures (M5+)
Serialised test vectors for regression testing across versions:
- Capture the wire bytes of known-good Cap'n Proto messages (Envelope, Auth, Delivery structs) at the current version.
- Store as
.binfiles intests/fixtures/. - Each test deserialises the fixture and verifies the expected field values.
- When the wire format changes, fixtures are updated with a version bump.
This catches accidental wire-format regressions that would break client-server compatibility.
N-1 Compatibility Tests (M5+)
Test that a client built at version N can communicate with a server built at version N-1 (and vice versa):
- Build two versions of the binary (current and previous release).
- Run the older server with the newer client and verify all RPCs succeed.
- Run the newer server with the older client and verify graceful degradation (legacy mode works, new features return clean errors).
Criterion Benchmarks (M5)
Performance benchmarks using Criterion.rs:
- Key generation latency (Ed25519, X25519, ML-KEM-768).
- MLS encap/decap (KeyPackage generation, Welcome processing).
- Group-add latency scaling: 2, 10, 100, 1000 members.
- Cap'n Proto serialise/deserialise throughput.
Benchmarks run separately from tests (cargo bench) and are not part of the
CI gate, but are tracked for regression detection.
Docker-based E2E Tests (Phase 5)
End-to-end tests using testcontainers-rs (see
Future Research: Testcontainers-rs):
- Spin up server container from the Docker image.
- Run client operations from the test process against the containerised server.
- Verify real network boundaries, container startup, and multi-process interactions.
Cross-references
- Coding Standards -- quality requirements for test code
- Milestones -- which tests were added at each milestone
- Production Readiness WBS -- Phase 5 (E2E Harness and Security Tests)
- Future Research: Testcontainers-rs -- Docker-based testing