Skip to content

chore: drop direct rand dep, add BDD corruption & tempfile tests#19

Merged
EffortlessSteven merged 5 commits into
mainfrom
chore/drop-rand-dep-bdd-tests
Feb 21, 2026
Merged

chore: drop direct rand dep, add BDD corruption & tempfile tests#19
EffortlessSteven merged 5 commits into
mainfrom
chore/drop-rand-dep-bdd-tests

Conversation

@EffortlessSteven

Copy link
Copy Markdown
Member

Summary

  • Drop rand from workspace dependencies. uselesskey-core only used rand for rand::rngs::OsRng; since rand_core 0.6 provides OsRng directly (gated on its std feature), the rand crate is no longer needed as a direct dependency. This eliminates a brittle coupling that caused build failures during the rand 0.10 upgrade attempt.
  • Add 604 lines of BDD scenarios covering deterministic corruption stability (RSA, ECDSA, Ed25519), X.509 wrong-key-usage edge cases, DER tempfile output verification, and chain/CRL tempfile scenarios.

Test plan

  • cargo check --workspace --all-features — compiles
  • cargo check -p uselesskey-core --no-default-features — no-std path compiles
  • cargo xtask feature-matrix — all 18 feature combos pass
  • cargo xtask test — all unit/integration tests pass
  • cargo xtask bdd — all 230 scenarios (1113 steps) pass
  • cargo xtask clippy — no warnings
  • cargo xtask dep-guard — no duplicate rand_core

`uselesskey-core` only used `rand` for `rand::rngs::OsRng`. Since
`rand_core 0.6` provides `OsRng` directly (gated on its `std` feature),
we can remove the `rand` crate entirely from workspace dependencies.

This eliminates a brittle coupling that caused build failures during the
`rand 0.10` upgrade attempt — downstream crates pulling `rand 0.10`
would conflict with our pinned `rand 0.8`.
…9 edge cases

- Deterministic corruption stability tests for RSA, ECDSA, Ed25519
- X.509 wrong-key-usage scenarios (sign-with-encipherment, etc.)
- DER tempfile output verification across key types
- Chain and CRL tempfile scenarios
Copilot AI review requested due to automatic review settings February 20, 2026 17:03
@coderabbitai

coderabbitai Bot commented Feb 20, 2026

Copy link
Copy Markdown

Summary by CodeRabbit

Release Notes

  • Tests

    • Expanded test coverage for deterministic cryptographic artifact corruption across RSA, Ed25519, ECDSA, and X.509 certificates
    • Added certificate chain and CRL tempfile output verification scenarios
    • Enhanced certificate validity period and key usage validation tests
  • Chores

    • Updated random number generation dependency from rand to rand_core

Walkthrough

This PR removes the direct rand dependency in favor of rand_core, updates RNG imports, and introduces extensive deterministic-corruption and tempfile-focused BDD tests across RSA, Ed25519, ECDSA, chain, and X.509 flows.

Changes

Cohort / File(s) Summary
Workspace & core feature
Cargo.toml, crates/uselesskey-core/Cargo.toml
Removed workspace rand dependency; switched default std feature to rand_core/std.
Factory RNG imports
crates/uselesskey-core/src/factory.rs
Replaced rand::rngs::OsRng with rand_core::OsRng and imported rand_core::RngCore under std.
BDD feature files
crates/uselesskey-bdd/features/ecdsa.feature, crates/uselesskey-bdd/features/ed25519.feature, crates/uselesskey-bdd/features/rsa.feature, crates/uselesskey-bdd/features/x509.feature, crates/uselesskey-bdd/features/chain.feature
Added deterministic PEM/DER corruption scenarios (pairwise stability checks) for RSA/Ed25519/ECDSA/X.509 and new tempfile/chain/CRL write-and-verify scenarios.
BDD test implementation
crates/uselesskey-bdd/tests/bdd.rs
Expanded UselessWorld with deterministic artifact fields and many new step implementations for deterministic corruption, tempfile writing, path/content assertions, CRL parsing checks, and wrong-key-usage handling.
X.509 tests & behavior checks
crates/uselesskey-x509/src/cert.rs, crates/uselesskey-x509/src/chain.rs, crates/uselesskey-x509/src/chain_negative.rs, crates/uselesskey-x509/src/chain_spec.rs, crates/uselesskey-x509/src/negative.rs, crates/uselesskey-x509/src/spec.rs, crates/uselesskey-x509/src/util.rs
Adjusted assertions to length checks, added deterministic-corruption unit tests, validity-period and EKU/CA checks, stable_bytes sensitivity tests, and CRL timestamp sanity checks; mostly test additions and stricter expectations.
Mutation testing config
.cargo/mutants.toml
Added exclusion to prevent mutator that would replace KeyUsage::leaf() with Default::default (infinite recursion guard).

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰 I nibbled at rand and found a core so neat,
Swapped seeds and rngs and made the tests repeat.
Corruptions deterministic, tempfiles in a line,
I hop, I check, the artifacts align! ✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the two main changes: removing the rand dependency and adding BDD corruption & tempfile tests.
Description check ✅ Passed The description is directly related to the changeset, providing a clear summary of the rand dependency removal, test plan verification, and comprehensive BDD scenario additions.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch chore/drop-rand-dep-bdd-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.

@gemini-code-assist

Copy link
Copy Markdown

Summary of Changes

Hello @EffortlessSteven, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request refines the project's dependency structure by removing an unnecessary direct rand dependency, leveraging rand_core directly for OsRng. Concurrently, it significantly enhances the test suite by introducing extensive BDD scenarios. These new tests cover critical aspects such as deterministic corruption stability for cryptographic keys, handling of X.509 certificate edge cases, and robust verification of temporary file outputs for certificates and certificate revocation lists, thereby improving the overall reliability and maintainability of the codebase.

Highlights

  • Dependency Management: The direct dependency on the rand crate has been removed from uselesskey-core. The OsRng functionality is now directly provided by rand_core 0.6 when its std feature is enabled, streamlining dependencies and resolving potential build issues.
  • BDD Test Expansion - Corruption Stability: New BDD scenarios have been added to verify the deterministic corruption stability for RSA, ECDSA, and Ed25519 keys in both PEM and DER formats. These tests ensure that repeated deterministic corruption with the same variant yields identical results and that the corrupted artifacts fail to parse as valid keys.
  • BDD Test Expansion - X.509 Edge Cases: New BDD scenarios cover X.509 certificate wrong-key-usage edge cases, ensuring that certificates with intentionally incorrect key usages are still parseable and correctly marked, while their specifications reflect the intended disabled key usage.
  • BDD Test Expansion - Tempfile Output Verification: Additional BDD scenarios have been introduced to verify the correct writing and content of various temporary files, including X.509 certificate DER, certificate chain PEMs (chain, full chain, root), and Certificate Revocation List (CRL) PEM and DER formats.
Changelog
  • Cargo.lock
    • Removed the rand 0.8.5 entry from the workspace dependencies.
  • Cargo.toml
    • Removed the direct rand dependency.
    • Updated the std feature for uselesskey-core to enable the std feature of rand_core instead of depending on rand.
  • crates/uselesskey-bdd/features/chain.feature
    • Added a scenario to test certificate chain writing chain, full chain, and root tempfiles.
    • Added a scenario to test revoked chain writing CRL tempfiles.
  • crates/uselesskey-bdd/features/ecdsa.feature
    • Added a scenario for deterministic ECDSA PEM corruption stability.
    • Added a scenario for deterministic ECDSA DER corruption stability.
  • crates/uselesskey-bdd/features/ed25519.feature
    • Added a scenario for deterministic Ed25519 PEM corruption stability.
    • Added a scenario for deterministic Ed25519 DER corruption stability.
  • crates/uselesskey-bdd/features/rsa.feature
    • Added a scenario for deterministic RSA PEM corruption stability.
    • Added a scenario for deterministic RSA DER corruption stability.
  • crates/uselesskey-bdd/features/x509.feature
    • Added a scenario for wrong-key-usage X.509 certificate parsing and CA marking.
    • Added a scenario for deterministic X.509 PEM corruption stability.
    • Added a scenario for deterministic X.509 DER corruption stability.
    • Added a scenario for X.509 certificate DER writing to a tempfile.
  • crates/uselesskey-bdd/tests/bdd.rs
    • Added new fields to UselessWorld for storing deterministic text and binary artifacts (deterministic_text_1, deterministic_text_2, deterministic_bytes_1, deterministic_bytes_2).
    • Added new fields to UselessWorld for storing X.509 wrong-key-usage certificate and various new tempfile types (e.g., x509_wrong_key_usage, x509_cert_der_tempfile, x509_chain_pem_tempfile, x509_full_chain_tempfile, x509_root_cert_tempfile, x509_crl_pem_tempfile, x509_crl_der_tempfile).
    • Implemented when steps for deterministically corrupting RSA, Ed25519, ECDSA, and X.509 PEM/DER artifacts.
    • Implemented when steps for getting the wrong-key-usage variant of an X.509 certificate.
    • Implemented when steps for writing X.509 certificate DER, chain PEM, full chain PEM, root certificate PEM, revoked chain CRL PEM, and revoked chain CRL DER to tempfiles.
    • Implemented then steps to assert the identity of deterministic text and binary artifacts.
    • Implemented then steps to assert that deterministically corrupted RSA, Ed25519, ECDSA, and X.509 PEM/DER artifacts fail to parse.
    • Implemented then steps to assert properties of the wrong-key-usage X.509 certificate (parseability, CA marking, keyCertSign status, label).
    • Implemented then steps to assert tempfile paths and content for X.509 certificate DER, chain PEM, full chain PEM, root certificate PEM, and CRL PEM/DER.
  • crates/uselesskey-core/Cargo.toml
    • Removed the rand dependency from the [dependencies] section.
    • Added rand_core/std to the std feature list.
  • crates/uselesskey-core/src/factory.rs
    • Updated the OsRng import to use rand_core::OsRng instead of rand::rngs::OsRng.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review

This pull request is a nice improvement. It correctly removes the direct rand dependency in favor of rand_core, which simplifies the dependency tree and avoids potential version conflicts. The addition of over 600 lines of BDD scenarios significantly enhances test coverage for deterministic corruption, tempfile outputs, and various X.509 edge cases. The new tests are thorough and well-structured. My review includes a couple of suggestions to reduce boilerplate and improve the maintainability of the newly added test code.

Comment on lines +343 to +365
#[when(regex = r#"^I deterministically corrupt the RSA PKCS8 PEM with variant "([^"]+)"$"#)]
fn det_corrupt_rsa_pem(world: &mut UselessWorld, variant: String) {
let rsa = world.rsa.as_ref().expect("rsa not set");
world.deterministic_text_1 = Some(rsa.private_key_pkcs8_pem_corrupt_deterministic(&variant));
}

#[when(regex = r#"^I deterministically corrupt the RSA PKCS8 PEM with variant "([^"]+)" again$"#)]
fn det_corrupt_rsa_pem_again(world: &mut UselessWorld, variant: String) {
let rsa = world.rsa.as_ref().expect("rsa not set");
world.deterministic_text_2 = Some(rsa.private_key_pkcs8_pem_corrupt_deterministic(&variant));
}

#[when(regex = r#"^I deterministically corrupt the RSA PKCS8 DER with variant "([^"]+)"$"#)]
fn det_corrupt_rsa_der(world: &mut UselessWorld, variant: String) {
let rsa = world.rsa.as_ref().expect("rsa not set");
world.deterministic_bytes_1 = Some(rsa.private_key_pkcs8_der_corrupt_deterministic(&variant));
}

#[when(regex = r#"^I deterministically corrupt the RSA PKCS8 DER with variant "([^"]+)" again$"#)]
fn det_corrupt_rsa_der_again(world: &mut UselessWorld, variant: String) {
let rsa = world.rsa.as_ref().expect("rsa not set");
world.deterministic_bytes_2 = Some(rsa.private_key_pkcs8_der_corrupt_deterministic(&variant));
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

There's significant boilerplate in the when steps for deterministic corruption. This pattern of getting a key, calling a corruption method, and storing the result is repeated for RSA, Ed25519, ECDSA, and X.509, for both PEM and DER formats, and for both the initial and ... again steps. This results in many nearly identical functions.

To reduce this duplication and improve maintainability, you could use a macro to generate these step definitions. For example:

macro_rules! define_det_corrupt_steps {
    (
        $fn_name:ident, $fn_name_again:ident,
        $regex_key_name:literal, $key_field:ident,
        $dest_field1:ident, $dest_field2:ident,
        $method:ident
    ) => {
        #[when(regex = concat!(r#"^I deterministically corrupt the "#, $regex_key_name, r#" with variant "([^"]+)"$"#))]
        fn $fn_name(world: &mut UselessWorld, variant: String) {
            let key = world.$key_field.as_ref().expect(concat!(stringify!($key_field), " not set"));
            world.$dest_field1 = Some(key.$method(&variant));
        }

        #[when(regex = concat!(r#"^I deterministically corrupt the "#, $regex_key_name, r#" with variant "([^"]+)" again$"#))]
        fn $fn_name_again(world: &mut UselessWorld, variant: String) {
            let key = world.$key_field.as_ref().expect(concat!(stringify!($key_field), " not set"));
            world.$dest_field2 = Some(key.$method(&variant));
        }
    };
}

You could then replace these functions with macro calls, like so:

define_det_corrupt_steps!(
    det_corrupt_rsa_pem,
    det_corrupt_rsa_pem_again,
    "RSA PKCS8 PEM",
    rsa,
    deterministic_text_1,
    deterministic_text_2,
    private_key_pkcs8_pem_corrupt_deterministic
);

define_det_corrupt_steps!(
    det_corrupt_rsa_der,
    det_corrupt_rsa_der_again,
    "RSA PKCS8 DER",
    rsa,
    deterministic_bytes_1,
    deterministic_bytes_2,
    private_key_pkcs8_der_corrupt_deterministic
);

Comment on lines +1876 to +1887
#[then(regex = r#"^the X\.509 DER tempfile path should end with "([^"]+)"$"#)]
fn x509_cert_der_tempfile_path_ends_with(world: &mut UselessWorld, suffix: String) {
let tf = world
.x509_cert_der_tempfile
.as_ref()
.expect("x509_cert_der_tempfile not set");
let path = tf.path().to_string_lossy().to_string();
assert!(
path.ends_with(&suffix),
"expected path to end with '{suffix}', got '{path}'"
);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

The new then steps for verifying tempfile paths and contents introduce a lot of repetitive code. For example, x509_cert_der_tempfile_path_ends_with, chain_tempfile_path_ends_with, and their content-checking counterparts like x509_tempfile_matches_cert_der all share the same structure.

To make this more maintainable, you could extract this common logic into helper functions. For example:

// Helper for checking path suffixes
fn assert_tempfile_path_ends_with(
    temp_artifact: Option<&uselesskey_core::sink::TempArtifact>,
    name: &str,
    suffix: &str,
) {
    let tf = temp_artifact.unwrap_or_else(|| panic!("{} not set", name));
    let path = tf.path().to_string_lossy();
    assert!(
        path.ends_with(suffix),
        "expected path to end with '{suffix}', got '{path}'"
    );
}

// Using the helper
#[then(regex = r#"^the X\.509 DER tempfile path should end with "([^\"]+)"$"#)]
fn x509_cert_der_tempfile_path_ends_with(world: &mut UselessWorld, suffix: String) {
    assert_tempfile_path_ends_with(
        world.x509_cert_der_tempfile.as_ref(),
        "x509_cert_der_tempfile",
        &suffix,
    );
}

A similar helper could be created for verifying file contents. This would significantly reduce the boilerplate across the test suite.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This pull request removes the direct rand dependency from the workspace in favor of using rand_core::OsRng directly, which has been available since rand_core 0.6 with the std feature. The PR also adds comprehensive BDD test scenarios (604 lines) covering deterministic corruption stability for RSA, ECDSA, Ed25519, and X.509 artifacts, as well as tempfile output verification for X.509 certificates, chains, and CRLs.

Changes:

  • Removed direct rand workspace dependency and replaced rand::rngs::OsRng import with rand_core::OsRng in uselesskey-core
  • Added BDD test scenarios for deterministic corruption methods across RSA, ECDSA, Ed25519, and X.509 certificate types
  • Extended BDD test coverage for X.509 wrong-key-usage variant and additional tempfile outputs (DER format, chain variants, CRL files)

Reviewed changes

Copilot reviewed 9 out of 10 changed files in this pull request and generated no comments.

Show a summary per file
File Description
Cargo.toml Removed rand = "0.8.5" from workspace dependencies and updated comment to reflect rand_core 0.6
Cargo.lock Removed rand 0.8.5 from uselesskey-core dependencies
crates/uselesskey-core/Cargo.toml Removed optional rand dependency and added rand_core/std to the std feature
crates/uselesskey-core/src/factory.rs Changed import from rand::rngs::OsRng to rand_core::OsRng
crates/uselesskey-bdd/tests/bdd.rs Added 8 new fields to UselessWorld struct and 45+ new test step functions for deterministic corruption and tempfile scenarios
crates/uselesskey-bdd/features/rsa.feature Added 2 scenarios for deterministic RSA corruption stability testing
crates/uselesskey-bdd/features/ed25519.feature Added 2 scenarios for deterministic Ed25519 corruption stability testing
crates/uselesskey-bdd/features/ecdsa.feature Added 2 scenarios for deterministic ECDSA corruption stability testing
crates/uselesskey-bdd/features/x509.feature Added 3 scenarios for wrong-key-usage variant, deterministic X.509 corruption, and DER tempfile output
crates/uselesskey-bdd/features/chain.feature Added 2 scenarios for additional chain tempfile outputs and CRL tempfile verification

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@crates/uselesskey-bdd/features/x509.feature`:
- Around line 135-140: The scenario "deterministic X.509 PEM corruption with
variant is stable" is missing the parse-failure assertion; add a final step
mirroring other key tests such as RSA/ECDSA/Ed25519: append "And the
deterministic X.509 PEM artifact should fail to parse" to the scenario so the
corrupted PEM is asserted unparsable (match the existing DER test pattern and
naming).

In `@crates/uselesskey-bdd/tests/bdd.rs`:
- Around line 61-64: The four fields deterministic_text_1, deterministic_text_2,
deterministic_bytes_1 and deterministic_bytes_2 on UselessWorld are shared by
RSA/Ed25519/ECDSA/X509 deterministic corruption steps and can be silently
overwritten if a scenario mixes key types; refactor by introducing a nested
struct (e.g., DeterministicArtifacts) keyed or namespaced per key type and
replace the flat fields on UselessWorld with a map or per-key-type fields (or
alternately add a clear doc comment on UselessWorld stating the
single-key-type-per-scenario invariant) so tests either isolate artifacts by key
type or clearly document the constraint.
- Around line 343-365: Multiple near-identical step functions
(det_corrupt_rsa_pem, det_corrupt_rsa_pem_again, det_corrupt_rsa_der,
det_corrupt_rsa_der_again and their counterparts for other key types) repeat
boilerplate; replace them with a single macro (e.g., det_corrupt_steps!) that
takes the world field name (like rsa, ed25519, ecdsa, x509), the PEM/DER
corruption method names (private_key_pkcs8_pem_corrupt_deterministic,
private_key_pkcs8_der_corrupt_deterministic or equivalents), the four regex
literals, and four function identifiers, and expands to the four #[when(...)]
functions that assert world.<key_field>.as_ref().expect("<key_field> not set")
and assign to world.deterministic_text_1 / deterministic_text_2 or
world.deterministic_bytes_1 / deterministic_bytes_2 as appropriate; then remove
the duplicated explicit functions and invoke det_corrupt_steps! for each key
type (refer to the existing function names and world fields to wire arguments).

Comment thread crates/uselesskey-bdd/features/x509.feature
Comment on lines +61 to +64
deterministic_text_1: Option<String>,
deterministic_text_2: Option<String>,
deterministic_bytes_1: Option<Vec<u8>>,
deterministic_bytes_2: Option<Vec<u8>>,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Shared deterministic_text/bytes slots across key types — safe but fragile.

These four fields are written by RSA, Ed25519, ECDSA, and X.509 deterministic corruption steps alike. Since cucumber resets UselessWorld per scenario this works, but a future scenario that mixes key types in a single flow would silently overwrite artifacts. Consider namespacing (e.g., a nested DeterministicArtifacts struct) or documenting the single-key-type-per-scenario invariant so future contributors don't hit a subtle test bug.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/uselesskey-bdd/tests/bdd.rs` around lines 61 - 64, The four fields
deterministic_text_1, deterministic_text_2, deterministic_bytes_1 and
deterministic_bytes_2 on UselessWorld are shared by RSA/Ed25519/ECDSA/X509
deterministic corruption steps and can be silently overwritten if a scenario
mixes key types; refactor by introducing a nested struct (e.g.,
DeterministicArtifacts) keyed or namespaced per key type and replace the flat
fields on UselessWorld with a map or per-key-type fields (or alternately add a
clear doc comment on UselessWorld stating the single-key-type-per-scenario
invariant) so tests either isolate artifacts by key type or clearly document the
constraint.

Comment on lines +343 to +365
#[when(regex = r#"^I deterministically corrupt the RSA PKCS8 PEM with variant "([^"]+)"$"#)]
fn det_corrupt_rsa_pem(world: &mut UselessWorld, variant: String) {
let rsa = world.rsa.as_ref().expect("rsa not set");
world.deterministic_text_1 = Some(rsa.private_key_pkcs8_pem_corrupt_deterministic(&variant));
}

#[when(regex = r#"^I deterministically corrupt the RSA PKCS8 PEM with variant "([^"]+)" again$"#)]
fn det_corrupt_rsa_pem_again(world: &mut UselessWorld, variant: String) {
let rsa = world.rsa.as_ref().expect("rsa not set");
world.deterministic_text_2 = Some(rsa.private_key_pkcs8_pem_corrupt_deterministic(&variant));
}

#[when(regex = r#"^I deterministically corrupt the RSA PKCS8 DER with variant "([^"]+)"$"#)]
fn det_corrupt_rsa_der(world: &mut UselessWorld, variant: String) {
let rsa = world.rsa.as_ref().expect("rsa not set");
world.deterministic_bytes_1 = Some(rsa.private_key_pkcs8_der_corrupt_deterministic(&variant));
}

#[when(regex = r#"^I deterministically corrupt the RSA PKCS8 DER with variant "([^"]+)" again$"#)]
fn det_corrupt_rsa_der_again(world: &mut UselessWorld, variant: String) {
let rsa = world.rsa.as_ref().expect("rsa not set");
world.deterministic_bytes_2 = Some(rsa.private_key_pkcs8_der_corrupt_deterministic(&variant));
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Repeated boilerplate across four key types for deterministic corruption steps.

Each key type (RSA, Ed25519, ECDSA, X.509) repeats the same four-function pattern (PEM, PEM-again, DER, DER-again) differing only in which world field is read and which corruption method is called. That's 16 functions with near-identical bodies. A helper macro or generic step could cut this to a single implementation.

Example sketch:

macro_rules! det_corrupt_steps {
    ($key_field:ident, $pem_method:ident, $der_method:ident,
     $pem_re:literal, $pem_again_re:literal,
     $der_re:literal, $der_again_re:literal,
     $fn_pem:ident, $fn_pem_again:ident,
     $fn_der:ident, $fn_der_again:ident) => {
        #[when(regex = $pem_re)]
        fn $fn_pem(world: &mut UselessWorld, variant: String) {
            let key = world.$key_field.as_ref().expect(concat!(stringify!($key_field), " not set"));
            world.deterministic_text_1 = Some(key.$pem_method(&variant));
        }
        // ... analogous for the other three
    };
}

Also applies to: 817-847, 1185-1207, 1571-1601

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/uselesskey-bdd/tests/bdd.rs` around lines 343 - 365, Multiple
near-identical step functions (det_corrupt_rsa_pem, det_corrupt_rsa_pem_again,
det_corrupt_rsa_der, det_corrupt_rsa_der_again and their counterparts for other
key types) repeat boilerplate; replace them with a single macro (e.g.,
det_corrupt_steps!) that takes the world field name (like rsa, ed25519, ecdsa,
x509), the PEM/DER corruption method names
(private_key_pkcs8_pem_corrupt_deterministic,
private_key_pkcs8_der_corrupt_deterministic or equivalents), the four regex
literals, and four function identifiers, and expands to the four #[when(...)]
functions that assert world.<key_field>.as_ref().expect("<key_field> not set")
and assign to world.deterministic_text_1 / deterministic_text_2 or
world.deterministic_bytes_1 / deterministic_bytes_2 as appropriate; then remove
the duplicated explicit functions and invoke det_corrupt_steps! for each key
type (refer to the existing function names and world fields to wire arguments).

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@crates/uselesskey-x509/src/cert.rs`:
- Around line 463-464: The assertion assert_ne!(pem_a, "xyzzy") is a no-op
because corrupt_cert_pem_deterministic will never produce that literal; replace
it with a meaningful check that the corrupted PEM (pem_a) fails to parse: after
generating pem_a with corrupt_cert_pem_deterministic, attempt to parse it using
the project's PEM/X.509 parse routine (e.g., the function/method used elsewhere
to parse certs like X509::from_pem or parse_cert_pem) and assert that parsing
returns an error (or Err variant) rather than Ok; keep the existing
assert!(!pem_a.is_empty()) and remove the trivial assert_ne! line.

---

Duplicate comments:
In `@crates/uselesskey-bdd/features/x509.feature`:
- Around line 135-149: The deterministic X.509 corruption feature already
includes parse-failure assertions for both PEM and DER cases, so no code changes
are needed; confirm the two scenarios "deterministic X.509 PEM corruption with
variant is stable" and "deterministic X.509 DER corruption with variant is
stable" keep the lines asserting the deterministic artifacts are identical and
the lines asserting the PEM/DER artifact should fail to parse, and then
approve/merge the change as-is.

In `@crates/uselesskey-bdd/tests/bdd.rs`:
- Around line 61-64: The four shared fields deterministic_text_1,
deterministic_text_2, deterministic_bytes_1, deterministic_bytes_2 in
UselessWorld can be clobbered if a scenario mixes key types; change the storage
to be keyed by key type (e.g., replace those four Option fields with a
HashMap<KeyType, (Option<String>, Option<String>, Option<Vec<u8>>,
Option<Vec<u8>>) or separate per-key-type fields like
rsa_deterministic_text_1/2, ed25519_deterministic_text_1/2, etc.), and update
all uses in the deterministic corruption steps to read/write via the new keyed
names or map entries (search for uses of deterministic_text_1,
deterministic_text_2, deterministic_bytes_1, deterministic_bytes_2 to update).
- Around line 343-365: The four nearly identical step functions
(det_corrupt_rsa_pem, det_corrupt_rsa_pem_again, det_corrupt_rsa_der,
det_corrupt_rsa_der_again) should be collapsed into a macro or helper to remove
boilerplate; create a macro that accepts the world key accessor (e.g.,
world.rsa), the two target world fields (deterministic_text_1/2 or
deterministic_bytes_1/2), and the method names to call on the key
(private_key_pkcs8_pem_corrupt_deterministic and
private_key_pkcs8_der_corrupt_deterministic), and generate the paired step
handlers (normal and "again") for both PEM and DER variants; update the RSA,
Ed25519, ECDSA and X.509 blocks to use that macro so the four duplicated
patterns are defined once and reference the correct world fields and key
methods.

Comment on lines +463 to +464
assert!(!pem_a.is_empty());
assert_ne!(pem_a, "xyzzy");

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

assert_ne!(pem_a, "xyzzy") is a trivially-true no-op assertion.

corrupt_cert_pem_deterministic will never return the literal string "xyzzy", so this line never exercises any code path. Consider replacing it with a meaningful assertion such as verifying the corrupted PEM fails to parse.

🔧 Proposed replacement
- assert_ne!(pem_a, "xyzzy");
+ // Corrupted PEM should fail to parse
+ use x509_parser::prelude::*;
+ let pem_bytes = pem_a.as_bytes();
+ let result = x509_parser::pem::parse_x509_pem(pem_bytes);
+ assert!(result.is_err() || result.map(|(_, p)| X509Certificate::from_der(p.contents.as_slice()).is_err()).unwrap_or(true),
+     "deterministically corrupted PEM should fail to parse as a valid certificate");
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
assert!(!pem_a.is_empty());
assert_ne!(pem_a, "xyzzy");
assert!(!pem_a.is_empty());
// Corrupted PEM should fail to parse
use x509_parser::prelude::*;
let pem_bytes = pem_a.as_bytes();
let result = x509_parser::pem::parse_x509_pem(pem_bytes);
assert!(result.is_err() || result.map(|(_, p)| X509Certificate::from_der(p.contents.as_slice()).is_err()).unwrap_or(true),
"deterministically corrupted PEM should fail to parse as a valid certificate");
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/uselesskey-x509/src/cert.rs` around lines 463 - 464, The assertion
assert_ne!(pem_a, "xyzzy") is a no-op because corrupt_cert_pem_deterministic
will never produce that literal; replace it with a meaningful check that the
corrupted PEM (pem_a) fails to parse: after generating pem_a with
corrupt_cert_pem_deterministic, attempt to parse it using the project's
PEM/X.509 parse routine (e.g., the function/method used elsewhere to parse certs
like X509::from_pem or parse_cert_pem) and assert that parsing returns an error
(or Err variant) rather than Ok; keep the existing assert!(!pem_a.is_empty())
and remove the trivial assert_ne! line.

@EffortlessSteven EffortlessSteven merged commit 676e643 into main Feb 21, 2026
3 of 4 checks passed
@EffortlessSteven EffortlessSteven deleted the chore/drop-rand-dep-bdd-tests branch February 21, 2026 11:08
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