rusty-pwgen 0.1.0

Generate pronounceable or random passwords from the OS CSPRNG — a Rust port of Theodore Ts'o's `pwgen` with strict-compat mode, deterministic `-H` reproducible mode (SHA256 + ChaCha20), and a typed library API.
Documentation
//! US3 (Flag-Soup Combinations, P2) integration tests.
//!
//! Parameterized matrix over `(capitalize ∈ {on, off}) × (numerals ∈ {on, off})
//! × (symbols ∈ {on, off}) × (ambiguous_filter ∈ {on, off})` for SC-007.

mod common;

use common::rusty_pwgen_cmd;

/// Run rusty-pwgen with `flags` (e.g., `["-c", "-n", "-y"]`) at length 16 count 50,
/// returning the captured stdout bytes (newlines included).
fn run_secure(flags: &[&str]) -> Vec<u8> {
    let mut cmd = rusty_pwgen_cmd();
    cmd.arg("-s");
    for f in flags {
        cmd.arg(f);
    }
    cmd.arg("16").arg("50");
    cmd.assert().success().get_output().stdout.clone()
}

#[test]
fn matrix_caps_on_numerals_on_no_symbols() {
    // Default secure mode → alphanumeric.
    let bytes = run_secure(&[]);
    for &b in &bytes {
        if b.is_ascii_whitespace() {
            continue;
        }
        assert!(b.is_ascii_alphanumeric(), "unexpected byte {:?}", b as char);
    }
}

#[test]
fn matrix_caps_off_numerals_on() {
    // -A: no uppercase.
    let bytes = run_secure(&["-A"]);
    for &b in &bytes {
        if b.is_ascii_whitespace() {
            continue;
        }
        assert!(
            !b.is_ascii_uppercase(),
            "uppercase '{}' should be absent with -A",
            b as char
        );
    }
}

#[test]
fn matrix_numerals_off() {
    // -0: no digits.
    let bytes = run_secure(&["-0"]);
    for &b in &bytes {
        if b.is_ascii_whitespace() {
            continue;
        }
        assert!(
            !b.is_ascii_digit(),
            "digit '{}' should be absent with -0",
            b as char
        );
    }
}

#[test]
fn matrix_symbols_on_includes_symbol_char() {
    // -y: symbols allowed. Statistically near-certain in 800 bytes.
    let bytes = run_secure(&["-y"]);
    let has_symbol = bytes
        .iter()
        .any(|&b| !b.is_ascii_alphanumeric() && b.is_ascii_graphic());
    assert!(has_symbol, "expected ≥1 symbol char with -y in 50×16 bytes");
}

#[test]
fn matrix_ambiguous_filter_drops_l1_oi0() {
    let bytes = run_secure(&["-B"]);
    for &b in &bytes {
        if b.is_ascii_whitespace() {
            continue;
        }
        assert!(
            !b"l1O0I".contains(&b),
            "ambiguous '{}' should be absent with -B",
            b as char
        );
    }
}

#[test]
fn matrix_combined_caps_on_numerals_off_symbols_on_ambiguous_on() {
    // -A doesn't apply here since we want caps ON: use `-y -B` with default caps + digits.
    // Combined: -n + -y + -B → has uppercase, has symbols, no ambiguous chars,
    // no `0` digit (filtered as ambiguous).
    let bytes = run_secure(&["-y", "-B"]);
    let has_upper = bytes.iter().any(|b| b.is_ascii_uppercase());
    let has_symbol = bytes
        .iter()
        .any(|&b| !b.is_ascii_alphanumeric() && b.is_ascii_graphic());
    assert!(has_upper);
    assert!(has_symbol);
    for &b in &bytes {
        if !b.is_ascii_whitespace() {
            assert!(!b"l1O0I".contains(&b));
        }
    }
}

#[test]
fn matrix_all_filters_caps_off_numerals_off_no_vowels() {
    // -A -0 -v → no caps, no digits, no vowels. -v implies -s.
    let bytes = run_secure(&["-A", "-0", "-v"]);
    for &b in &bytes {
        if b.is_ascii_whitespace() {
            continue;
        }
        assert!(
            !b.is_ascii_uppercase(),
            "uppercase '{}' should be absent",
            b as char
        );
        assert!(
            !b.is_ascii_digit(),
            "digit '{}' should be absent",
            b as char
        );
        assert!(
            !b"aeiouAEIOU".contains(&b),
            "vowel '{}' should be absent",
            b as char
        );
    }
}