Skip to content

Add tests to kill cargo-mutants survivors (72% → 100%)#35

Merged
sweis merged 2 commits intomainfrom
claude/cargo-mutants-analysis-HZ4wF
Mar 12, 2026
Merged

Add tests to kill cargo-mutants survivors (72% → 100%)#35
sweis merged 2 commits intomainfrom
claude/cargo-mutants-analysis-HZ4wF

Conversation

@sweis
Copy link
Copy Markdown
Owner

@sweis sweis commented Mar 12, 2026

Summary

Implements all recommendations from notes/cargo-mutants-analysis.md (PR #34). Adds 63 new unit tests and removes one dead function, raising the mutation score from 72.2% to 100% of fast-testable mutants.

Results

Metric Before After
Fast unit tests 95 158
Total unit tests 125 188
Mutants caught 203 264
Mutants missed 78 0
Mutation score 72.2% 100%

Verified with cargo mutants -j 2 in 8 minutes.

Changes by file

Deleted

  • Algorithm::signature_len() — dead code, never called anywhere

types.rs (+10 tests)

  • Algorithm::from_byte / to_byte exhaustive roundtrip (all 5 variants incl. Groth16)
  • KeyIdType::from_byte / to_byte exhaustive roundtrip
  • KeyIdentifier::key_id_type() all variants
  • is_zero(): JSON serialization omits zero not_before/issued_at; includes nonzero
  • Claims::validate duplicate-scope rejection

proto3.rs (+5 tests)

  • 10-byte varint overflow (final byte > 1 must reject, = 1 is u64::MAX)
  • Zero varint at nonzero offset (pos - start arithmetic)
  • Non-minimal varint at nonzero offset
  • to_u8 / read_u32 boundary rejection

serialize.rs (+18 tests)

  • Oversized payload/token/signature/proof rejection with specific error message pinning (so >== mutants don't slip through to downstream parse errors)
  • Exact-boundary acceptance: MAX_PAYLOAD_BYTES, MAX_SIGNATURE_BYTES, MAX_PROOF_BYTES, 255-byte subject/audience/scope
  • MAX_SIGNED_TOKEN_BYTES pinned to 6944 (catches arithmetic mutations)
  • Proof field (field 3) serialization roundtrip
  • FullKeyHash payload roundtrip + wrong-length rejection
  • Duplicate non-scope field rejection

keys.rs (+12 tests)

  • validate_public_key_size: wrong-length Ed25519 (31 B) and ML-DSA-44 (1000 B) keys
  • Groth16 algorithms rejected in VerifyingKey and extract_verifying_key with specific message pinning
  • Oversized secret_key / public_key DoS limits + boundary acceptance
  • Groth16 signing key serialization roundtrip + short-key rejection

sign.rs (+13 tests)

  • generate_hmac_key(): length is 32, two keys differ, not all-zeros
  • compute_full_key_hash / compute_sha256_full_key_hash: deterministic, different inputs → different outputs, known SHA-256 value for zero bytes
  • Claims::validate boundary via sign_hmac: overlong subject/audience/scope, too many scopes, exact-limit acceptance, not_before == expires_at
  • mldsa44_key_hash wrong-length rejection

verify.rs (+3 tests)

  • verify_key_match specifically returns KeyHashMismatch (was shadowed by signature check in all existing tests)
  • Ed25519 key hash mismatch returns KeyHashMismatch

poseidon.rs (+2 tests)

  • bytes_to_fr known values (little-endian 1 == Fr(1), zero == Fr(0))
  • poseidon_config parameters pinned (8/57/5/2/1)

.cargo/mutants.toml

  • Exclude sign_groth16/verify_groth16 function body replacements (12 mutants). These need ~2min SNARK trusted setup per test, covered by make test-snark. All their constituent helpers have fast unit tests.

Key test patterns

  • Error-message pinning: matches!(err, MalformedEncoding(m) if m.contains("specific message")) — ensures error-shadowing mutants (which reach a different error path) are caught.
  • Boundary pairs: every size-limit check has both an "oversized → rejects with specific error" test and an "exactly-at-limit → passes the size check" test, killing >>=== mutation cycles.
  • Known-value tests: hash functions pin specific input → specific output so constant-return mutants fail.
  • Test name hygiene: Groth16-adjacent tests use names like zk_symmetric instead of snark/groth16 so they run under the --skip snark --skip groth16 filters used during mutation testing.

claude added 2 commits March 12, 2026 01:37
Implements the recommendations from notes/cargo-mutants-analysis.md:

Dead code:
- Removed Algorithm::signature_len() — never called anywhere

New tests in keys.rs (12):
- validate_public_key_size: wrong-length Ed25519 and ML-DSA-44 public keys
- extract_verifying_key: Groth16 algorithms rejected (|| branches)
- deserialize_verifying_key: Groth16 algorithms rejected as symmetric
- Oversized secret_key (> MAX_SECRET_KEY_BYTES) and public_key (> MAX_PUBLIC_KEY_BYTES)
- Exact-boundary acceptance tests (at MAX_SECRET_KEY_BYTES / MAX_PUBLIC_KEY_BYTES)
- Groth16 signing key serialization roundtrip + short-key rejection

New tests in poseidon.rs (2):
- bytes_to_fr known-value test (little-endian 1 == Fr(1))
- poseidon_config parameter pinning (8/57/5/2/1)

New tests in proto3.rs (5):
- 10-byte varint overflow (final byte > 1 must reject)
- Valid zero varint at nonzero offset (pos - start correctness)
- Non-minimal varint at nonzero offset
- to_u8 / read_u32 boundary rejection

New tests in serialize.rs (16):
- Oversized payload / signed token / signature / proof rejection
- Exact-boundary acceptance (MAX_SIGNATURE_BYTES, MAX_PROOF_BYTES, 255-byte subject/audience/scope)
- MAX_SIGNED_TOKEN_BYTES arithmetic pinned to 6944
- Proof field roundtrip (serialize.rs:324-334 match arm)
- FullKeyHash payload roundtrip + wrong-length rejection
- Duplicate non-scope field rejection (canonical-order check)

New tests in sign.rs (13):
- generate_hmac_key length (32 bytes) + entropy (two keys differ)
- compute_full_key_hash / compute_sha256_full_key_hash deterministic + diff-inputs
- compute_sha256_full_key_hash known value (SHA-256 of zero bytes)
- Claims::validate boundary via sign_hmac: overlong subject/audience/scope,
  too many scopes, exact-255-byte acceptance, not_before == expires_at
- mldsa44_key_hash wrong-length rejection

New tests in types.rs (10):
- Algorithm / KeyIdType from_byte exhaustive (all arms incl. Groth16)
- Algorithm / KeyIdType to_byte roundtrip
- KeyIdentifier::key_id_type all variants
- is_zero: JSON serialization skips zero not_before/issued_at; includes nonzero
- Claims::validate duplicate-scope rejection

New tests in verify.rs (3):
- verify_key_match: specifically asserts KeyHashMismatch (not shadowed by sig check)
- Ed25519 key hash mismatch returns KeyHashMismatch specifically

Test names avoid 'snark'/'groth16' so they run under --skip filters.
Fast test suite: 95 → 156 unit tests (+64% increase).
Tightens 3 tests whose assertions were too loose to catch subtle mutants:

1. keys.rs:203/204 (|| → &&):
   test_rejects_zk_symmetric_verifying_key was asserting m.contains("symmetric"),
   but the fallback validate_public_key_size path ALSO returns a "symmetric"
   error. Now asserts the specific "cannot be a verifying key" message.

2. serialize.rs:70, serialize.rs:276 (> → == / >=):
   Oversized-input tests were only checking is_err(), but a zero-filled buffer
   that slips past a mutated size check still fails at parse time. Now asserts
   the specific "too large" error AND adds boundary tests proving exactly-MAX
   inputs pass the size check.

Config:
- .cargo/mutants.toml: exclude sign_groth16/verify_groth16 function body
  replacements (12 mutants). These need ~2min SNARK trusted setup per test,
  covered only by `make test-snark`. All their internal helpers are
  fast-tested individually.

Docs:
- notes/cargo-mutants-analysis.md: add post-fix summary (72.2% → 100%)
- CLAUDE.md: update test count (142 → 205)

Final mutation score: 100% of fast-testable mutants (264 caught / 264 viable).
@sweis sweis merged commit 073b1ae into main Mar 12, 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

Development

Successfully merging this pull request may close these issues.

2 participants