use super::*;
#[test]
fn login_config_defaults_to_bare_tui() {
assert!(LoginConfig::default().args.is_empty());
}
fn settled_session(id: &str, status: LoginStatus, age: chrono::Duration) -> Arc<Session> {
Arc::new(Session {
id: id.to_string(),
provider: SubscriptionProvider::Claude,
url: "https://claude.ai/oauth/authorize".to_string(),
user_code: None,
deadline: Utc::now() + chrono::Duration::seconds(900),
state: Mutex::new(SessionState {
status,
expires_at: None,
error: None,
pty: None,
claude_login: None,
auth_task: None,
settled_at: Some(Utc::now() - age),
}),
})
}
#[test]
fn finished_sessions_are_evicted_once_their_result_has_been_retained() {
let manager = LoginManager::new(LoginConfig::default());
let fresh = settled_session("fresh", LoginStatus::Authorized, chrono::Duration::zero());
let stale = settled_session(
"stale",
LoginStatus::Failed,
terminal_retention() + chrono::Duration::seconds(1),
);
{
let mut sessions = manager.lock_sessions();
sessions.insert("fresh".to_string(), fresh);
sessions.insert("stale".to_string(), stale);
}
manager.sweep();
assert!(manager.status("fresh").is_some());
assert!(manager.status("stale").is_none());
}
#[test]
fn an_expired_session_becomes_evictable() {
let manager = LoginManager::new(LoginConfig::default());
let session = Arc::new(Session {
id: "gone".to_string(),
provider: SubscriptionProvider::Claude,
url: String::new(),
user_code: None,
deadline: Utc::now() - chrono::Duration::seconds(1),
state: Mutex::new(SessionState {
status: LoginStatus::AwaitingCode,
expires_at: None,
error: None,
pty: None,
claude_login: None,
auth_task: None,
settled_at: None,
}),
});
manager
.lock_sessions()
.insert("gone".to_string(), Arc::clone(&session));
manager.sweep();
assert_eq!(session.view().status, LoginStatus::Expired);
let settled = session
.state
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner)
.settled_at;
assert!(settled.is_some());
}
#[test]
fn a_failure_excerpt_carries_neither_the_token_nor_the_pasted_code() {
let code = "authcode-9f3a2b7c";
let transcript =
format!("Paste code: {code}\nrejected\nleftover token sk-ant-oat01-SECRETVALUE0001\n");
let text = excerpt(&transcript, code, 400);
assert!(!text.contains("SECRETVALUE0001"), "{text}");
assert!(!text.contains(code), "{text}");
assert!(text.contains("rejected"), "{text}");
}
#[test]
fn write_credential_is_readable_by_the_oauth_reader() {
let dir = tempfile::tempdir().unwrap();
write_credential(dir.path(), "sk-ant-oat01-testtoken").unwrap();
let provider = crate::oauth::OAuthProvider::new(dir.path().to_str().unwrap());
assert_eq!(provider.get_token().unwrap(), "sk-ant-oat01-testtoken");
}
#[test]
fn ensure_writable_dir_creates_missing_directories() {
let dir = tempfile::tempdir().unwrap();
let nested = dir.path().join("a/b/claude");
ensure_writable_dir(&nested).unwrap();
assert!(nested.is_dir());
}
#[test]
fn disabled_manager_refuses_to_begin() {
let manager = LoginManager::new(LoginConfig {
enabled: false,
..LoginConfig::default()
});
let result = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.unwrap()
.block_on(manager.begin());
assert!(matches!(result, Err(LoginError::Disabled)));
}