use tempfile::TempDir;
use super::cli_runner::
{
run_cs_with_env, assert_exit, stdout, stderr,
write_credentials, write_account, write_account_renewal_json,
write_account_profile_json,
live_active_token, require_live_api,
write_account_with_token,
FAR_FUTURE_MS,
};
fn mtime_ms( path : &std::path::Path ) -> u64
{
path.metadata().ok()
.and_then( | m | m.modified().ok() )
.and_then( | t | t.duration_since( std::time::UNIX_EPOCH ).ok() )
.map_or( 0, | d | u64::try_from( d.as_millis() ).unwrap_or( 0 ) )
}
#[ test ]
fn save_bv1_resave_same_credentials_idempotent()
{
let dir = TempDir::new().unwrap();
let home = dir.path().to_str().unwrap();
write_credentials( dir.path(), "max", "default", FAR_FUTURE_MS );
let out1 = run_cs_with_env(
&[ ".account.save", "name::alice@acme.com" ],
&[ ( "HOME", home ) ],
);
assert_exit( &out1, 0 );
let cred_store = dir.path().join( ".persistent" ).join( "claude" ).join( "credential" );
let cred_file = cred_store.join( "alice@acme.com.credentials.json" );
let content_v1 = std::fs::read_to_string( &cred_file ).unwrap();
let out2 = run_cs_with_env(
&[ ".account.save", "name::alice@acme.com" ],
&[ ( "HOME", home ) ],
);
assert_exit( &out2, 0 );
let content_v2 = std::fs::read_to_string( &cred_file ).unwrap();
assert_eq!(
content_v1, content_v2,
"re-saving same credentials must produce identical stored content",
);
}
#[ test ]
fn save_bv2_transitions_absent_to_saved()
{
let dir = TempDir::new().unwrap();
let home = dir.path().to_str().unwrap();
write_credentials( dir.path(), "max", "default", FAR_FUTURE_MS );
let cred_store = dir.path().join( ".persistent" ).join( "claude" ).join( "credential" );
let cred_file = cred_store.join( "alice@acme.com.credentials.json" );
let meta_file = cred_store.join( "alice@acme.com.json" );
assert!( !cred_file.exists(), "precondition: account must not exist before save" );
let out = run_cs_with_env(
&[ ".account.save", "name::alice@acme.com" ],
&[ ( "HOME", home ) ],
);
assert_exit( &out, 0 );
assert!( cred_file.exists(), "credentials file must exist after save" );
assert!( meta_file.exists(), "metadata .json file must exist after save" );
let out_text = stdout( &out );
assert!(
out_text.contains( "alice@acme.com" ),
"stdout must mention account name: {out_text}",
);
}
#[ test ]
fn save_bv3_without_credentials_exits_2()
{
let dir = TempDir::new().unwrap();
let home = dir.path().to_str().unwrap();
let out = run_cs_with_env(
&[ ".account.save", "name::alice@acme.com" ],
&[ ( "HOME", home ) ],
);
assert_exit( &out, 2 );
}
#[ test ]
fn use_bv1_reactivate_already_active_idempotent()
{
let dir = TempDir::new().unwrap();
let home = dir.path().to_str().unwrap();
write_credentials( dir.path(), "max", "default", FAR_FUTURE_MS );
write_account( dir.path(), "alice@acme.com", "max", "default", FAR_FUTURE_MS, true );
let out = run_cs_with_env(
&[ ".account.use", "name::alice@acme.com" ],
&[ ( "HOME", home ) ],
);
assert_exit( &out, 0 );
let creds_path = dir.path().join( ".claude" ).join( ".credentials.json" );
let content = std::fs::read_to_string( &creds_path ).unwrap();
assert!(
content.contains( "\"max\"" ),
"credentials must retain alice's subscription type after re-activating already-active account: {content}",
);
}
#[ test ]
fn use_bv2_transitions_saved_to_active()
{
let dir = TempDir::new().unwrap();
let home = dir.path().to_str().unwrap();
write_credentials( dir.path(), "max", "default", FAR_FUTURE_MS );
write_account( dir.path(), "alice@acme.com", "pro", "default", FAR_FUTURE_MS, false );
write_account( dir.path(), "bob@acme.com", "max", "default", FAR_FUTURE_MS, true );
let out = run_cs_with_env(
&[ ".account.use", "name::alice@acme.com" ],
&[ ( "HOME", home ) ],
);
assert_exit( &out, 0 );
let creds = std::fs::read_to_string(
dir.path().join( ".claude" ).join( ".credentials.json" ),
).unwrap();
assert!(
creds.contains( "\"pro\"" ),
"after switch to alice (pro), credentials must contain her subscriptionType: {creds}",
);
let bob_file = dir.path()
.join( ".persistent" ).join( "claude" ).join( "credential" )
.join( "bob@acme.com.credentials.json" );
assert!( bob_file.exists(), "bob's stored credentials must remain after alice is activated" );
}
#[ test ]
fn use_bv3_nonexistent_account_exits_2()
{
let dir = TempDir::new().unwrap();
let home = dir.path().to_str().unwrap();
write_credentials( dir.path(), "max", "default", FAR_FUTURE_MS );
write_account( dir.path(), "alice@acme.com", "max", "default", FAR_FUTURE_MS, true );
let out = run_cs_with_env(
&[ ".account.use", "name::nonexistent@acme.com" ],
&[ ( "HOME", home ) ],
);
assert_exit( &out, 2 );
}
#[ test ]
fn delete_bv1_second_delete_exits_2_non_idempotent()
{
let dir = TempDir::new().unwrap();
let home = dir.path().to_str().unwrap();
write_credentials( dir.path(), "max", "default", FAR_FUTURE_MS );
write_account( dir.path(), "alice@acme.com", "max", "default", FAR_FUTURE_MS, false );
let out1 = run_cs_with_env(
&[ ".account.delete", "name::alice@acme.com" ],
&[ ( "HOME", home ) ],
);
assert_exit( &out1, 0 );
let out2 = run_cs_with_env(
&[ ".account.delete", "name::alice@acme.com" ],
&[ ( "HOME", home ) ],
);
assert_exit( &out2, 2 );
}
#[ test ]
fn delete_bv2_transitions_saved_to_absent()
{
let dir = TempDir::new().unwrap();
let home = dir.path().to_str().unwrap();
write_credentials( dir.path(), "max", "default", FAR_FUTURE_MS );
write_account( dir.path(), "alice@acme.com", "max", "default", FAR_FUTURE_MS, false );
let cred_store = dir.path().join( ".persistent" ).join( "claude" ).join( "credential" );
let cred_file = cred_store.join( "alice@acme.com.credentials.json" );
assert!( cred_file.exists(), "precondition: account file must exist before delete" );
let out = run_cs_with_env(
&[ ".account.delete", "name::alice@acme.com" ],
&[ ( "HOME", home ) ],
);
assert_exit( &out, 0 );
assert!( !cred_file.exists(), "credentials file must be removed after delete" );
let live_creds = dir.path().join( ".claude" ).join( ".credentials.json" );
assert!( live_creds.exists(), "live ~/.claude/.credentials.json must remain after delete of non-active account" );
}
#[ test ]
fn delete_bv3_nonexistent_account_exits_2()
{
let dir = TempDir::new().unwrap();
let home = dir.path().to_str().unwrap();
write_credentials( dir.path(), "max", "default", FAR_FUTURE_MS );
write_account( dir.path(), "alice@acme.com", "max", "default", FAR_FUTURE_MS, false );
let out = run_cs_with_env(
&[ ".account.delete", "name::nobody@acme.com" ],
&[ ( "HOME", home ) ],
);
assert_exit( &out, 2 );
}
#[ test ]
fn limits_bv1_lim_it_repeated_calls_no_side_effects()
{
let Some( token ) = live_active_token() else { return };
if !require_live_api( "limits_bv1_lim_it" ) { return; }
let dir = TempDir::new().unwrap();
let home = dir.path().to_str().unwrap();
write_account_with_token( dir.path(), "alice@acme.com", &token, true );
write_credentials( dir.path(), "max", "default", FAR_FUTURE_MS );
let cred_file = dir.path()
.join( ".persistent" ).join( "claude" ).join( "credential" )
.join( "alice@acme.com.credentials.json" );
let mtime_before = mtime_ms( &cred_file );
let out1 = run_cs_with_env(
&[ ".account.limits", "name::alice@acme.com" ],
&[ ( "HOME", home ) ],
);
assert_exit( &out1, 0 );
let out2 = run_cs_with_env(
&[ ".account.limits", "name::alice@acme.com" ],
&[ ( "HOME", home ) ],
);
assert_exit( &out2, 0 );
let mtime_after = mtime_ms( &cred_file );
assert_eq!(
mtime_before, mtime_after,
"limits command must not modify stored credential files",
);
}
#[ test ]
fn limits_bv2_lim_it_read_is_non_mutating()
{
let Some( token ) = live_active_token() else { return };
if !require_live_api( "limits_bv2_lim_it" ) { return; }
let dir = TempDir::new().unwrap();
let home = dir.path().to_str().unwrap();
write_account_with_token( dir.path(), "alice@acme.com", &token, true );
write_credentials( dir.path(), "max", "default", FAR_FUTURE_MS );
let cred_file = dir.path()
.join( ".persistent" ).join( "claude" ).join( "credential" )
.join( "alice@acme.com.credentials.json" );
let meta_file = dir.path()
.join( ".persistent" ).join( "claude" ).join( "credential" )
.join( "alice@acme.com.json" );
let mtime_cred_before = mtime_ms( &cred_file );
let out = run_cs_with_env(
&[ ".account.limits", "name::alice@acme.com" ],
&[ ( "HOME", home ) ],
);
assert_exit( &out, 0 );
assert_eq!(
mtime_cred_before, mtime_ms( &cred_file ),
"limits must not modify alice@acme.com.credentials.json",
);
assert!(
!meta_file.exists() || mtime_ms( &meta_file ) == mtime_ms( &meta_file ),
"limits must not write alice@acme.com.json",
);
}
#[ test ]
fn limits_bv3_without_accessible_account_exits_2()
{
let dir = TempDir::new().unwrap();
let home = dir.path().to_str().unwrap();
let out = run_cs_with_env(
&[ ".account.limits" ],
&[ ( "HOME", home ) ],
);
assert_exit( &out, 2 );
}
#[ test ]
fn relogin_bv1_lim_it_non_idempotent_oauth_flow()
{
let Some( _token ) = live_active_token() else { return };
}
#[ test ]
fn relogin_bv2_lim_it_updates_in_place_state_preserved()
{
let Some( _token ) = live_active_token() else { return };
}
#[ test ]
fn relogin_bv3_absent_account_exits_1()
{
let dir = TempDir::new().unwrap();
let home = dir.path().to_str().unwrap();
write_credentials( dir.path(), "max", "default", FAR_FUTURE_MS );
let out = run_cs_with_env(
&[ ".account.relogin", "name::nobody@acme.com" ],
&[ ( "HOME", home ) ],
);
let code = out.status.code().unwrap_or( -1 );
assert!(
code != 0,
"relogin on absent account must exit non-zero; got 0\nstdout: {}\nstderr: {}",
stdout( &out ), stderr( &out ),
);
}
#[ test ]
fn rotate_bv1_non_idempotent_outcome_changes_with_expiry()
{
let dir = TempDir::new().unwrap();
let home = dir.path().to_str().unwrap();
write_account( dir.path(), "alice@acme.com", "max", "default", FAR_FUTURE_MS, false );
write_account( dir.path(), "bob@acme.com", "max", "default", FAR_FUTURE_MS, true );
let out = run_cs_with_env( &[ ".account.rotate" ], &[ ( "HOME", home ) ] );
assert_exit( &out, 1 );
}
#[ test ]
fn rotate_bv2_activates_highest_expiry_inactive_account()
{
let dir = TempDir::new().unwrap();
let home = dir.path().to_str().unwrap();
let out = run_cs_with_env( &[ ".account.rotate" ], &[ ( "HOME", home ) ] );
assert_exit( &out, 1 );
let combined = format!( "{}{}", stdout( &out ), stderr( &out ) );
assert!(
combined.contains( ".usage rotate" ),
"deprecated `.account.rotate` must reference `.usage rotate`, got: {combined}",
);
}
#[ test ]
fn rotate_bv3_no_inactive_accounts_exits_2()
{
let dir = TempDir::new().unwrap();
let home = dir.path().to_str().unwrap();
write_account( dir.path(), "alice@acme.com", "max", "default", FAR_FUTURE_MS, true );
let out = run_cs_with_env( &[ ".account.rotate" ], &[ ( "HOME", home ) ] );
assert_exit( &out, 1 );
}
#[ test ]
fn renewal_bv1_resetting_same_timestamp_idempotent()
{
let dir = TempDir::new().unwrap();
let home = dir.path().to_str().unwrap();
write_credentials( dir.path(), "max", "default", FAR_FUTURE_MS );
write_account( dir.path(), "alice@acme.com", "max", "default", FAR_FUTURE_MS, false );
write_account_renewal_json( dir.path(), "alice@acme.com", "2026-07-01T00:00:00Z" );
write_account_profile_json( dir.path(), "alice@acme.com", None, Some( "work" ) );
let out1 = run_cs_with_env(
&[ ".account.renewal", "name::alice@acme.com", "at::2026-07-01T00:00:00Z" ],
&[ ( "HOME", home ) ],
);
assert_exit( &out1, 0 );
let meta_file = dir.path()
.join( ".persistent" ).join( "claude" ).join( "credential" )
.join( "alice@acme.com.json" );
let content_v1 = std::fs::read_to_string( &meta_file ).unwrap();
let out2 = run_cs_with_env(
&[ ".account.renewal", "name::alice@acme.com", "at::2026-07-01T00:00:00Z" ],
&[ ( "HOME", home ) ],
);
assert_exit( &out2, 0 );
let content_v2 = std::fs::read_to_string( &meta_file ).unwrap();
let v1_parsed : serde_json::Value = serde_json::from_str( &content_v1 ).unwrap();
let v2_parsed : serde_json::Value = serde_json::from_str( &content_v2 ).unwrap();
assert_eq!(
v1_parsed.get( "_renewal_at" ), v2_parsed.get( "_renewal_at" ),
"_renewal_at must be unchanged after re-setting the same timestamp",
);
assert_eq!(
v2_parsed.get( "role" ).and_then( | v | v.as_str() ),
Some( "work" ),
"role field must be preserved after idempotent renewal set",
);
}
#[ test ]
fn renewal_bv2_writes_renewal_at_preserving_other_fields()
{
let dir = TempDir::new().unwrap();
let home = dir.path().to_str().unwrap();
write_credentials( dir.path(), "max", "default", FAR_FUTURE_MS );
write_account( dir.path(), "alice@acme.com", "max", "default", FAR_FUTURE_MS, false );
write_account_profile_json( dir.path(), "alice@acme.com", Some( "laptop" ), Some( "work" ) );
let meta_file = dir.path()
.join( ".persistent" ).join( "claude" ).join( "credential" )
.join( "alice@acme.com.json" );
let before : serde_json::Value = serde_json::from_str(
&std::fs::read_to_string( &meta_file ).unwrap(),
).unwrap();
assert!(
before.get( "_renewal_at" ).is_none(),
"precondition: _renewal_at must be absent before renewal",
);
let out = run_cs_with_env(
&[ ".account.renewal", "name::alice@acme.com", "at::2026-07-01T00:00:00Z" ],
&[ ( "HOME", home ) ],
);
assert_exit( &out, 0 );
let after : serde_json::Value = serde_json::from_str(
&std::fs::read_to_string( &meta_file ).unwrap(),
).unwrap();
assert_eq!(
after.get( "_renewal_at" ).and_then( | v | v.as_str() ),
Some( "2026-07-01T00:00:00Z" ),
"_renewal_at must be set after renewal",
);
assert_eq!(
after.get( "role" ).and_then( | v | v.as_str() ), Some( "work" ),
"role field must be preserved after renewal",
);
assert_eq!(
after.get( "host" ).and_then( | v | v.as_str() ), Some( "laptop" ),
"host field must be preserved after renewal",
);
}
#[ test ]
fn renewal_bv3_without_operation_param_exits_1()
{
let dir = TempDir::new().unwrap();
let home = dir.path().to_str().unwrap();
write_credentials( dir.path(), "max", "default", FAR_FUTURE_MS );
write_account( dir.path(), "alice@acme.com", "max", "default", FAR_FUTURE_MS, false );
let out = run_cs_with_env(
&[ ".account.renewal", "name::alice@acme.com" ],
&[ ( "HOME", home ) ],
);
assert_exit( &out, 1 );
}
#[ test ]
fn inspect_bv1_lim_it_repeated_calls_no_side_effects()
{
let Some( token ) = live_active_token() else { return };
if !require_live_api( "inspect_bv1_lim_it" ) { return; }
let dir = TempDir::new().unwrap();
let home = dir.path().to_str().unwrap();
write_account_with_token( dir.path(), "alice@acme.com", &token, true );
write_credentials( dir.path(), "max", "default", FAR_FUTURE_MS );
let cred_file = dir.path()
.join( ".persistent" ).join( "claude" ).join( "credential" )
.join( "alice@acme.com.credentials.json" );
let mtime_before = mtime_ms( &cred_file );
let out1 = run_cs_with_env(
&[ ".account.inspect", "name::alice@acme.com" ],
&[ ( "HOME", home ) ],
);
assert_exit( &out1, 0 );
let out2 = run_cs_with_env(
&[ ".account.inspect", "name::alice@acme.com" ],
&[ ( "HOME", home ) ],
);
assert_exit( &out2, 0 );
let mtime_after = mtime_ms( &cred_file );
assert_eq!(
mtime_before, mtime_after,
"inspect must not modify stored credential files",
);
}
#[ test ]
fn inspect_bv2_lim_it_read_is_non_mutating()
{
let Some( token ) = live_active_token() else { return };
if !require_live_api( "inspect_bv2_lim_it" ) { return; }
let dir = TempDir::new().unwrap();
let home = dir.path().to_str().unwrap();
write_account_with_token( dir.path(), "alice@acme.com", &token, true );
write_account_renewal_json( dir.path(), "alice@acme.com", "2026-07-01T00:00:00Z" );
write_credentials( dir.path(), "max", "default", FAR_FUTURE_MS );
let meta_file = dir.path()
.join( ".persistent" ).join( "claude" ).join( "credential" )
.join( "alice@acme.com.json" );
let cred_file = dir.path()
.join( ".persistent" ).join( "claude" ).join( "credential" )
.join( "alice@acme.com.credentials.json" );
let mtime_meta_before = mtime_ms( &meta_file );
let mtime_cred_before = mtime_ms( &cred_file );
let out = run_cs_with_env(
&[ ".account.inspect", "name::alice@acme.com" ],
&[ ( "HOME", home ) ],
);
assert_exit( &out, 0 );
assert_eq!(
mtime_meta_before, mtime_ms( &meta_file ),
"inspect must not modify alice@acme.com.json",
);
assert_eq!(
mtime_cred_before, mtime_ms( &cred_file ),
"inspect must not modify alice@acme.com.credentials.json",
);
}
#[ test ]
fn inspect_bv3_without_credentials_exits_2()
{
let dir = TempDir::new().unwrap();
let home = dir.path().to_str().unwrap();
let out = run_cs_with_env(
&[ ".account.inspect" ],
&[ ( "HOME", home ) ],
);
assert_exit( &out, 2 );
}
#[ test ]
fn assign_bv1_reassigning_same_account_idempotent()
{
let dir = TempDir::new().unwrap();
let home = dir.path().to_str().unwrap();
write_credentials( dir.path(), "max", "default", FAR_FUTURE_MS );
write_account( dir.path(), "alice@acme.com", "max", "default", FAR_FUTURE_MS, true );
let out1 = run_cs_with_env(
&[ ".accounts", "assign::1", "name::alice@acme.com" ],
&[ ( "HOME", home ), ( "USER", "testuser" ), ( "HOSTNAME", "testmachine" ) ],
);
assert_exit( &out1, 0 );
let marker_path = dir.path()
.join( ".persistent" ).join( "claude" ).join( "credential" )
.join( "_active_testmachine_testuser" );
let marker_content = std::fs::read_to_string( &marker_path ).unwrap();
let out2 = run_cs_with_env(
&[ ".accounts", "assign::1", "name::alice@acme.com" ],
&[ ( "HOME", home ), ( "USER", "testuser" ), ( "HOSTNAME", "testmachine" ) ],
);
assert_exit( &out2, 0 );
let marker_after = std::fs::read_to_string( &marker_path ).unwrap();
assert_eq!(
marker_content, marker_after,
"active marker must be unchanged after idempotent re-assign",
);
}
#[ test ]
fn assign_bv2_writes_marker_without_touching_credential_files()
{
let dir = TempDir::new().unwrap();
let home = dir.path().to_str().unwrap();
write_credentials( dir.path(), "max", "default", FAR_FUTURE_MS );
write_account( dir.path(), "alice@acme.com", "max", "default", FAR_FUTURE_MS, false );
let cred_file = dir.path()
.join( ".persistent" ).join( "claude" ).join( "credential" )
.join( "alice@acme.com.credentials.json" );
let live_creds = dir.path().join( ".claude" ).join( ".credentials.json" );
let mtime_cred_before = mtime_ms( &cred_file );
let mtime_live_before = mtime_ms( &live_creds );
let out = run_cs_with_env(
&[ ".accounts", "assign::1", "name::alice@acme.com" ],
&[ ( "HOME", home ), ( "USER", "testuser" ), ( "HOSTNAME", "testmachine" ) ],
);
assert_exit( &out, 0 );
let marker_path = dir.path()
.join( ".persistent" ).join( "claude" ).join( "credential" )
.join( "_active_testmachine_testuser" );
assert!( marker_path.exists(), "_active_testmachine_testuser marker must be created" );
assert_eq!(
std::fs::read_to_string( &marker_path ).unwrap().trim(),
"alice@acme.com",
);
assert_eq!(
mtime_cred_before, mtime_ms( &cred_file ),
"alice@acme.com.credentials.json must not be modified by assign",
);
assert_eq!(
mtime_live_before, mtime_ms( &live_creds ),
"~/.claude/.credentials.json must not be modified by assign",
);
}
#[ test ]
fn assign_bv3_nonexistent_account_exits_2()
{
let dir = TempDir::new().unwrap();
let home = dir.path().to_str().unwrap();
write_credentials( dir.path(), "max", "default", FAR_FUTURE_MS );
let out = run_cs_with_env(
&[ ".accounts", "assign::1", "name::nobody@acme.com" ],
&[ ( "HOME", home ), ( "USER", "testuser" ), ( "HOSTNAME", "testmachine" ) ],
);
assert_exit( &out, 2 );
}
#[ test ]
fn status_bv1_token_status_twice_same_classification()
{
let dir = TempDir::new().unwrap();
let home = dir.path().to_str().unwrap();
write_credentials( dir.path(), "max", "default", FAR_FUTURE_MS );
let out1 = run_cs_with_env( &[ ".token.status" ], &[ ( "HOME", home ) ] );
assert_exit( &out1, 0 );
let out2 = run_cs_with_env( &[ ".token.status" ], &[ ( "HOME", home ) ] );
assert_exit( &out2, 0 );
let first1 = stdout( &out1 ).lines().next().unwrap_or( "" ).to_string();
let first2 = stdout( &out2 ).lines().next().unwrap_or( "" ).to_string();
assert_eq!( first1, first2, "classification must be identical across two calls" );
}
#[ test ]
fn status_bv2_token_status_non_mutating()
{
let dir = TempDir::new().unwrap();
let home = dir.path().to_str().unwrap();
write_credentials( dir.path(), "max", "default", FAR_FUTURE_MS );
let creds_path = dir.path().join( ".claude" ).join( ".credentials.json" );
let mtime_before = mtime_ms( &creds_path );
let out = run_cs_with_env( &[ ".token.status" ], &[ ( "HOME", home ) ] );
assert_exit( &out, 0 );
assert_eq!(
mtime_before, mtime_ms( &creds_path ),
".token.status must not modify ~/.claude/.credentials.json",
);
}
#[ test ]
fn status_bv3_token_status_absent_creds_exits_2()
{
let dir = TempDir::new().unwrap();
let home = dir.path().to_str().unwrap();
let out = run_cs_with_env( &[ ".token.status" ], &[ ( "HOME", home ) ] );
assert_exit( &out, 2 );
}
#[ test ]
fn status_bv4_credentials_status_twice_same_output()
{
let dir = TempDir::new().unwrap();
let home = dir.path().to_str().unwrap();
write_credentials( dir.path(), "max", "default", FAR_FUTURE_MS );
write_account( dir.path(), "alice@acme.com", "max", "default", FAR_FUTURE_MS, true );
let out1 = run_cs_with_env( &[ ".credentials.status" ], &[ ( "HOME", home ) ] );
assert_exit( &out1, 0 );
let out2 = run_cs_with_env( &[ ".credentials.status" ], &[ ( "HOME", home ) ] );
assert_exit( &out2, 0 );
assert_eq!(
stdout( &out1 ), stdout( &out2 ),
".credentials.status must produce identical output on repeated calls",
);
}