Skip to content

test: Wave 24 - comprehensive tonic and JWT integration tests#43

Merged
EffortlessSteven merged 1 commit intomainfrom
wave-24/tonic-tests
Mar 1, 2026
Merged

test: Wave 24 - comprehensive tonic and JWT integration tests#43
EffortlessSteven merged 1 commit intomainfrom
wave-24/tonic-tests

Conversation

@EffortlessSteven
Copy link
Copy Markdown
Member

Adds 81 new integration tests: tonic adapter (44 tests covering server/client/mTLS TLS config, cert chains, key sizes, determinism, negative cases) and JWT adapter (37 tests covering all RSA/ECDSA/Ed25519/HMAC algorithms, cross-algorithm rejection, token round-trips).

Determinism impact: None
Policy impact: None

Adds integration test suites for tonic TLS configuration and
extended JWT token round-trip scenarios.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings March 1, 2026 19:10
@gemini-code-assist
Copy link
Copy Markdown

Warning

You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again!

@chatgpt-codex-connector
Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

@qodo-free-for-open-source-projects
Copy link
Copy Markdown

Review Summary by Qodo

Add comprehensive JWT and tonic adapter integration tests

🧪 Tests

Grey Divider

Walkthroughs

Description
• Adds 37 JWT adapter integration tests covering RSA, ECDSA, Ed25519, and HMAC algorithms
• Adds 44 tonic adapter integration tests for TLS/mTLS configuration and certificate chains
• Tests token round-trips with complex claims, algorithm mismatches, and cross-family rejection
• Tests deterministic key generation, various RSA key sizes, and negative scenarios
Diagram
flowchart LR
  JWT["JWT Adapter Tests<br/>37 tests"]
  RSA["RSA Algorithms<br/>RS256-512, PS256-512"]
  ECDSA["ECDSA Algorithms<br/>ES256, ES384"]
  ED["Ed25519<br/>EdDSA"]
  HMAC["HMAC Algorithms<br/>HS256-512"]
  CROSS["Cross-Family<br/>Rejection Tests"]
  STRUCT["Token Structure<br/>Validation"]
  
  TONIC["Tonic Adapter Tests<br/>44 tests"]
  SERVER["Server TLS<br/>Config"]
  CLIENT["Client TLS<br/>Config"]
  MTLS["mTLS<br/>Config"]
  CHAIN["Certificate Chain<br/>Handling"]
  RSA_SIZES["RSA Key Sizes<br/>2048-4096 bits"]
  DET["Determinism<br/>Tests"]
  NEG["Negative<br/>Cases"]
  
  JWT --> RSA
  JWT --> ECDSA
  JWT --> ED
  JWT --> HMAC
  JWT --> CROSS
  JWT --> STRUCT
  
  TONIC --> SERVER
  TONIC --> CLIENT
  TONIC --> MTLS
  TONIC --> CHAIN
  TONIC --> RSA_SIZES
  TONIC --> DET
  TONIC --> NEG
Loading

Grey Divider

File Changes

1. crates/uselesskey-jsonwebtoken/tests/jwt_extended.rs 🧪 Tests +690/-0

JWT adapter integration tests with all algorithms

• New file with 37 comprehensive JWT integration tests
• Tests all RSA algorithms (RS256, RS384, RS512, PS256, PS384, PS512) with round-trip verification
• Tests ECDSA (ES256, ES384), Ed25519 (EdDSA), and HMAC (HS256, HS384, HS512) algorithms
• Includes complex claims handling, algorithm mismatch detection, cross-key verification failures
• Tests token structure validation, expiration, malformed token rejection, and deterministic key
 generation
• Cross-algorithm family rejection tests for RSA/ECDSA, RSA/Ed25519, and ECDSA/Ed25519 combinations

crates/uselesskey-jsonwebtoken/tests/jwt_extended.rs


2. crates/uselesskey-tonic/tests/tonic_comprehensive.rs 🧪 Tests +564/-0

Tonic adapter TLS and certificate chain tests

• New file with 44 comprehensive tonic TLS integration tests
• Tests server TLS configuration with self-signed certificates and certificate chains
• Tests client TLS configuration with domain name handling and string/&str parameters
• Tests mTLS configuration for both server and client with separate and shared chains
• Tests certificate chain handling including PEM structure, root/leaf access, and DER outputs
• Tests various RSA key sizes (2048, 3072, 4096 bits) with tonic configuration
• Tests deterministic vs random factory modes, seed reproducibility, and label differentiation
• Negative test cases for mismatched chains, corrupt PEM data, and truncated DER outputs

crates/uselesskey-tonic/tests/tonic_comprehensive.rs


Grey Divider

Qodo Logo

@qodo-free-for-open-source-projects
Copy link
Copy Markdown

qodo-free-for-open-source-projects bot commented Mar 1, 2026

Code Review by Qodo

🐞 Bugs (1) 📘 Rule violations (1) 📎 Requirement gaps (0)

Grey Divider


Action required

1. assert_eq! on private PEM 📘 Rule violation ⛨ Security
Description
The new tonic integration tests compare private-key PEM strings using assert_eq!/assert_ne!,
which will print the full key material to test output on failure. This risks leaking secret-shaped
material via CI logs/debug output.
Code

crates/uselesskey-tonic/tests/tonic_comprehensive.rs[R364-369]

+        assert_eq!(chain_a.chain_pem(), chain_b.chain_pem());
+        assert_eq!(chain_a.root_cert_pem(), chain_b.root_cert_pem());
+        assert_eq!(
+            chain_a.leaf_private_key_pkcs8_pem(),
+            chain_b.leaf_private_key_pkcs8_pem()
+        );
Evidence
PR Compliance ID 2 forbids exposing key material in debug/log/test output; assert_eq!/assert_ne!
will include the compared values in the failure output, and these values are private-key PEMs
returned by *_private_key_*_pem().

CLAUDE.md
crates/uselesskey-tonic/tests/tonic_comprehensive.rs[364-369]
crates/uselesskey-tonic/tests/tonic_comprehensive.rs[382-385]
crates/uselesskey-tonic/tests/tonic_comprehensive.rs[443-447]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The tests use `assert_eq!` / `assert_ne!` to compare private key PEM strings (e.g., `leaf_private_key_pkcs8_pem()`, `private_key_pkcs8_pem()`). If these assertions fail, Rust will print the full PEM values in test output, leaking key material into CI logs.

## Issue Context
Compliance requires that debug/log/test output never exposes private keys, raw key bytes, or similar secret-shaped blobs.

## Fix Focus Areas
- crates/uselesskey-tonic/tests/tonic_comprehensive.rs[364-369]
- crates/uselesskey-tonic/tests/tonic_comprehensive.rs[382-385]
- crates/uselesskey-tonic/tests/tonic_comprehensive.rs[443-447]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

2. Tonic tests may be slow 🐞 Bug ➹ Performance
Description
The new tonic_comprehensive integration tests generate many distinct X.509 chains and include
multiple 4096-bit RSA scenarios. Since an X.509 chain generation creates three RSA keypairs
(root/intermediate/leaf) and caching keys include the label/spec, using many unique labels/specs
prevents reuse and can materially slow CI (especially with parallel test execution).
Code

crates/uselesskey-tonic/tests/tonic_comprehensive.rs[R51-58]

+    fn server_tls_with_custom_rsa_bits() {
+        let fx = fx();
+        let chain = fx.x509_chain(
+            "server-4096",
+            ChainSpec::new("grpc-4096.example.com").with_rsa_bits(4096),
+        );
+        let _server = chain.server_tls_config_tonic();
+    }
Evidence
The tonic suite repeatedly creates new chains with 4096-bit RSA (and other sizes), and each
x509_chain generation constructs 3 RSA keypairs. Factory caching is keyed by `(domain, label,
spec, variant)`, so each distinct label/spec combination triggers fresh work, increasing overall
test cost.

crates/uselesskey-tonic/tests/tonic_comprehensive.rs[50-58]
crates/uselesskey-tonic/tests/tonic_comprehensive.rs[165-174]
crates/uselesskey-tonic/tests/tonic_comprehensive.rs[324-345]
crates/uselesskey-x509/src/chain.rs[271-289]
crates/uselesskey-core/src/factory.rs[90-105]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The new tonic integration test suite generates many X.509 chains and includes multiple 4096-bit RSA cases. X509 chain creation generates 3 RSA keypairs per chain, and because caching is keyed by `(domain,label,spec,variant)`, many unique labels/specs trigger fresh work. This can slow CI significantly.

## Issue Context
This is test-only, but it can materially increase CPU/time in CI, especially when the Rust test harness runs tests in parallel.

## Fix Focus Areas
- crates/uselesskey-tonic/tests/tonic_comprehensive.rs[50-58]
- crates/uselesskey-tonic/tests/tonic_comprehensive.rs[165-174]
- crates/uselesskey-tonic/tests/tonic_comprehensive.rs[324-345]

## Suggested approach
- Consider consolidating 4096-bit coverage to a single shared fixture (same label/spec) reused across tests to benefit from `Factory` caching.
- Alternatively, mark the heaviest 4096-bit tests as `#[ignore]` or guard them behind an opt-in cargo feature (e.g., `slow-tests`) and run them in a separate CI job.
- Prefer deterministic factory reuse for expensive fixtures where randomness is not the intent.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

ⓘ The new review experience is currently in Beta. Learn more

Grey Divider

Qodo Logo

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 1, 2026

Warning

Rate limit exceeded

@EffortlessSteven has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 0 minutes and 22 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between bdfabd0 and bfdd934.

📒 Files selected for processing (2)
  • crates/uselesskey-jsonwebtoken/tests/jwt_extended.rs
  • crates/uselesskey-tonic/tests/tonic_comprehensive.rs
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch wave-24/tonic-tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Comment on lines +364 to +369
assert_eq!(chain_a.chain_pem(), chain_b.chain_pem());
assert_eq!(chain_a.root_cert_pem(), chain_b.root_cert_pem());
assert_eq!(
chain_a.leaf_private_key_pkcs8_pem(),
chain_b.leaf_private_key_pkcs8_pem()
);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

1. assert_eq! on private pem 📘 Rule violation ⛨ Security

The new tonic integration tests compare private-key PEM strings using assert_eq!/assert_ne!,
which will print the full key material to test output on failure. This risks leaking secret-shaped
material via CI logs/debug output.
Agent Prompt
## Issue description
The tests use `assert_eq!` / `assert_ne!` to compare private key PEM strings (e.g., `leaf_private_key_pkcs8_pem()`, `private_key_pkcs8_pem()`). If these assertions fail, Rust will print the full PEM values in test output, leaking key material into CI logs.

## Issue Context
Compliance requires that debug/log/test output never exposes private keys, raw key bytes, or similar secret-shaped blobs.

## Fix Focus Areas
- crates/uselesskey-tonic/tests/tonic_comprehensive.rs[364-369]
- crates/uselesskey-tonic/tests/tonic_comprehensive.rs[382-385]
- crates/uselesskey-tonic/tests/tonic_comprehensive.rs[443-447]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a large set of integration tests to exercise the TLS (tonic) and JWT adapters across supported configurations and algorithm families.

Changes:

  • Added comprehensive uselesskey-tonic integration tests for server/client TLS, mTLS, chain handling, RSA key sizes, determinism, and negative scenarios.
  • Added extended uselesskey-jsonwebtoken integration tests covering RSA/ECDSA/Ed25519/HMAC signing + verification, token structure checks, and cross-family rejection cases.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 5 comments.

File Description
crates/uselesskey-tonic/tests/tonic_comprehensive.rs New integration test suite covering TLS/mTLS config creation, X.509 chain material, determinism, and negative scenarios.
crates/uselesskey-jsonwebtoken/tests/jwt_extended.rs New integration test suite covering JWT round-trips and rejection behavior across RSA/ECDSA/Ed25519/HMAC algorithms.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +478 to +494
#[test]
fn self_signed_corrupt_cert_pem_still_builds_tonic_identity() {
let fx = fx();
let cert = fx.x509_self_signed("neg-corrupt", X509Spec::self_signed("localhost"));

// A valid cert should still produce an identity
let _identity = cert.identity_tonic();

// Corrupt PEM is available but tonic Identity::from_pem is infallible
// (it defers validation to the TLS handshake). Verify the corruption API exists.
let _corrupt = cert.corrupt_cert_pem(uselesskey_core::negative::CorruptPem::BadBase64);
assert_ne!(
cert.cert_pem(),
&_corrupt,
"Corrupt PEM should differ from original"
);
}
Copy link

Copilot AI Mar 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test name claims a corrupt cert PEM still builds a tonic identity, but the assertions only verify that a corruption API exists and produces a different PEM (it never attempts to construct an identity from the corrupted PEM). Either rename the test to reflect what it validates, or actually build a tonic identity from the corrupted PEM and assert construction succeeds.

Copilot uses AI. Check for mistakes.
Comment on lines +355 to +370
#[test]
fn deterministic_chain_produces_same_identity() {
let seed = Seed::from_env_value("tonic-det-test-v1").expect("seed");
let fx = Factory::deterministic(seed);

let chain_a = fx.x509_chain("det-id", ChainSpec::new("det.example.com"));
fx.clear_cache();
let chain_b = fx.x509_chain("det-id", ChainSpec::new("det.example.com"));

assert_eq!(chain_a.chain_pem(), chain_b.chain_pem());
assert_eq!(chain_a.root_cert_pem(), chain_b.root_cert_pem());
assert_eq!(
chain_a.leaf_private_key_pkcs8_pem(),
chain_b.leaf_private_key_pkcs8_pem()
);
}
Copy link

Copilot AI Mar 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test name says it produces the same identity, but it compares PEM/key material (and does not compare/build an identity object). Consider renaming to something like deterministic_chain_produces_same_cert_material (or similar) to match the assertions.

Copilot uses AI. Check for mistakes.
Comment on lines +314 to +333
#[test]
fn ecdsa_deterministic_keys_produce_same_tokens() {
let seed = Seed::from_env_value("jwt-ext-det-ec").unwrap();
let fx1 = Factory::deterministic(seed);
let fx2 = Factory::deterministic(seed);

let kp1 = fx1.ecdsa("jwt-det-ec", EcdsaSpec::es256());
let kp2 = fx2.ecdsa("jwt-det-ec", EcdsaSpec::es256());

// ECDSA signatures are non-deterministic (random k), so just verify cross-decode
let claims = Claims::standard();
let header = Header::new(Algorithm::ES256);
let token = encode(&header, &claims, &kp1.encoding_key()).unwrap();

let mut validation = Validation::new(Algorithm::ES256);
validation.set_audience(&["test-api"]);
let decoded = decode::<Claims>(&token, &kp2.decoding_key(), &validation).unwrap();

assert_eq!(decoded.claims, claims);
}
Copy link

Copilot AI Mar 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test name says it produces the same tokens, but it does not compare token strings—only that a token signed with kp1 can be verified with kp2. Either rename to reflect cross-verification (e.g., ecdsa_deterministic_keys_cross_decode), or add a token equality assertion if identical tokens are actually expected.

Copilot uses AI. Check for mistakes.
let kp1 = fx1.ecdsa("jwt-det-ec", EcdsaSpec::es256());
let kp2 = fx2.ecdsa("jwt-det-ec", EcdsaSpec::es256());

// ECDSA signatures are non-deterministic (random k), so just verify cross-decode
Copy link

Copilot AI Mar 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment states ECDSA signatures are non-deterministic due to random k, but many implementations use deterministic ECDSA (RFC 6979). Since the test behavior is about cross-verification (not determinism), consider rewording to avoid making a potentially incorrect blanket statement (e.g., explain that token equality is not asserted here and why).

Suggested change
// ECDSA signatures are non-deterministic (random k), so just verify cross-decode
// Some ECDSA implementations produce non-deterministic signatures, so instead of
// asserting token equality we just verify that a token signed with one keypair
// can be successfully decoded with the other keypair derived from the same seed.

Copilot uses AI. Check for mistakes.
Comment on lines +132 to +136
let token = encode(&header, &claims, &kp.encoding_key()).unwrap();

let mut validation = Validation::new(Algorithm::RS256);
validation.set_audience(&["test-api"]);
let decoded = decode::<Claims>(&token, &kp.decoding_key(), &validation).unwrap();
Copy link

Copilot AI Mar 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Several tests use bare .unwrap() for encode/decode, which can make failures harder to diagnose compared to sign_and_verify (which includes the algorithm in the panic message). Consider switching these unwraps to expect(...) / unwrap_or_else(...) with contextual messages to improve debuggability when a test fails.

Suggested change
let token = encode(&header, &claims, &kp.encoding_key()).unwrap();
let mut validation = Validation::new(Algorithm::RS256);
validation.set_audience(&["test-api"]);
let decoded = decode::<Claims>(&token, &kp.decoding_key(), &validation).unwrap();
let token = encode(&header, &claims, &kp.encoding_key())
.expect("failed to encode JWT with RS256 in rsa_complex_claims_round_trip");
let mut validation = Validation::new(Algorithm::RS256);
validation.set_audience(&["test-api"]);
let decoded = decode::<Claims>(&token, &kp.decoding_key(), &validation)
.expect("failed to decode JWT with RS256 in rsa_complex_claims_round_trip");

Copilot uses AI. Check for mistakes.
@EffortlessSteven EffortlessSteven merged commit b05228e into main Mar 1, 2026
8 checks passed
@EffortlessSteven EffortlessSteven deleted the wave-24/tonic-tests branch March 5, 2026 15:27
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