auths-cli 0.1.3

Command-line interface for Auths decentralized identity system
Documentation
use super::helpers::TestEnv;

/// Tests key rotation and its effect on commit verification.
///
/// NOTE: `emergency rotate-now` currently uses the legacy GitKel storage backend,
/// but `init` creates identities using the packed registry backend (`refs/auths/registry`).
/// This storage mismatch means rotation fails with "KEL not found for prefix".
/// When this is fixed, remove the early return and test the full rotation flow.
#[test]
fn test_key_rotation_supersedes_old_commit_verification() {
    let env = TestEnv::new();
    env.init_identity();

    // Commit A: signed with original key
    std::fs::write(env.repo_path.join("a.txt"), "commit A").unwrap();
    let add = env.git_cmd().args(["add", "a.txt"]).output().unwrap();
    assert!(add.status.success());
    let commit = env
        .git_cmd()
        .args(["commit", "-m", "commit A"])
        .output()
        .unwrap();
    assert!(
        commit.status.success(),
        "commit A failed: {}",
        String::from_utf8_lossy(&commit.stderr)
    );

    // Add the in-band Auths-Id / Auths-Device trailers. This amends commit A, so the
    // verifiable hash is captured *after* signing.
    let sign = env.cmd("auths").args(["sign", "HEAD"]).output().unwrap();
    assert!(
        sign.status.success(),
        "auths sign failed: {}",
        String::from_utf8_lossy(&sign.stderr)
    );
    let log_a = env.git_cmd().args(["rev-parse", "HEAD"]).output().unwrap();
    let commit_a_hash = String::from_utf8_lossy(&log_a.stdout).trim().to_string();

    // Verify commit A works (KEL-native — no allowlist).
    let verify_a = env
        .cmd("auths")
        .args(["verify", &commit_a_hash])
        .output()
        .unwrap();
    assert!(
        verify_a.status.success(),
        "commit A should verify, stderr: {}",
        String::from_utf8_lossy(&verify_a.stderr)
    );

    // Attempt rotation — may fail due to GitKel/registry storage mismatch
    let rotate = env
        .cmd("auths")
        .args([
            "emergency",
            "rotate-now",
            "--yes",
            "--current-alias",
            "main",
            "--next-alias",
            "main-rotated",
        ])
        .output()
        .unwrap();

    if !rotate.status.success() {
        let stderr = String::from_utf8_lossy(&rotate.stderr);
        // Known issues:
        // - rotate-now uses GitKel backend but init uses registry storage
        // - P-256 keys can't be rotated yet (rotation code assumes Ed25519)
        if stderr.contains("KEL not found")
            || stderr.contains("Unrecognized Ed25519")
            || stderr.contains("key decryption failed")
        {
            eprintln!(
                "Skipping post-rotation assertions: \
                 rotation not yet supported for current key type/backend"
            );
            return;
        }
        panic!("rotation failed unexpectedly: {}", stderr);
    }

    // If rotation succeeded: under current-key KEL verification, commit A was signed by
    // the now-superseded key, so it no longer verifies as current. Preserving old-key
    // commits across a rotation requires signing-time (anchored) verification — tracked
    // as #205.
    let verify_a_after = env
        .cmd("auths")
        .args(["verify", &commit_a_hash])
        .output()
        .unwrap();
    assert!(
        !verify_a_after.status.success(),
        "after rotation, an old-key commit is superseded under current-key verify (#205)"
    );
}