#![allow(clippy::unwrap_used, clippy::expect_used)]
#![cfg(unix)]
use super::*;
use crate::claude::force_link_profile_credentials;
use crate::profile::{
AppState, ClaudeCredentials, OAuthToken, Profile, claude_dir, save_app_state, save_profile,
};
use crate::testutil::HomeSandbox;
fn seed_active_linked() {
let mut p = Profile::new("active".to_string(), None, None);
p.credentials = Some(ClaudeCredentials {
claude_ai_oauth: Some(OAuthToken {
access_token: "stored-a".to_string(),
refresh_token: Some("stored-r".to_string()),
expires_at: None,
scopes: None,
subscription_type: None,
}),
});
save_profile(&p).expect("save profile");
force_link_profile_credentials("active").expect("link active");
let state = AppState {
active_profile: Some("active".into()),
profiles: vec!["active".into()],
..Default::default()
};
save_app_state(&state).expect("save state");
}
fn call_switch(name: &str) -> CallToolResult {
let server = ClauthServer::new();
let rt = tokio::runtime::Builder::new_current_thread()
.build()
.expect("runtime");
rt.block_on(async {
server
.switch(Parameters(SwitchArgs {
name: name.to_string(),
}))
.await
})
.expect("switch returns a tool result, never a transport error")
}
#[test]
fn unknown_target_is_rejected_without_stripping_live_creds() {
let _home = HomeSandbox::new();
seed_active_linked();
let live = claude_dir().expect("claude dir").join(".credentials.json");
assert!(
live.symlink_metadata().is_ok(),
"precondition: live credentials are linked",
);
let result = call_switch("ghost");
assert_eq!(
result.is_error,
Some(true),
"an unknown profile name must be a tool error",
);
assert!(
live.symlink_metadata().is_ok(),
"the live credentials symlink must survive a failed switch to an unknown name",
);
}