use assert_cmd::Command;
use predicates::prelude::*;
use serial_test::serial;
use std::fs;
use std::path::PathBuf;
use std::sync::Mutex;
static CONFIG_LOCK: Mutex<()> = Mutex::new(());
fn get_real_config_dir() -> PathBuf {
directories::ProjectDirs::from("com", "oauth-db", "oauth-db-cli")
.expect("Failed to get config dir")
.config_dir()
.to_path_buf()
}
fn backup_config() -> Option<String> {
let config_path = get_real_config_dir().join("config.toml");
if config_path.exists() {
fs::read_to_string(&config_path).ok()
} else {
None
}
}
fn restore_config(backup: Option<String>) {
let config_path = get_real_config_dir().join("config.toml");
if let Some(content) = backup {
fs::write(&config_path, content).ok();
} else {
fs::remove_file(&config_path).ok();
}
}
#[tokio::test]
#[serial(config)]
#[ignore] async fn test_login_success() {
let _lock = CONFIG_LOCK.lock().unwrap();
let backup = backup_config();
let mut cmd = Command::cargo_bin("oauth-db").unwrap();
cmd.arg("login")
.arg("--server")
.arg("http://localhost:28080")
.arg("--username")
.arg("testuser")
.arg("--password")
.arg("testpass")
.assert()
.success()
.stdout(predicates::str::contains("Logged in as testuser"));
let config_path = get_real_config_dir().join("config.toml");
let config_content = fs::read_to_string(config_path).unwrap();
assert!(config_content.contains("testuser"));
restore_config(backup);
}
#[tokio::test]
#[serial(config)]
async fn test_logout_success() {
let _lock = CONFIG_LOCK.lock().unwrap();
let backup = backup_config();
let config_dir = get_real_config_dir();
fs::create_dir_all(&config_dir).unwrap();
let config = r#"
[server]
url = "http://localhost:28080"
[output]
format = "table"
color = true
[log]
level = "info"
[[accounts]]
name = "testuser@localhost"
server = "http://localhost:28080"
username = "testuser"
token = "test_token_12345"
default = true
role = "user"
"#;
fs::write(config_dir.join("config.toml"), config).unwrap();
let mut cmd = Command::cargo_bin("oauth-db").unwrap();
cmd.arg("logout")
.assert()
.success()
.stdout(predicates::str::contains("Logged out from account"));
let config_content = fs::read_to_string(config_dir.join("config.toml")).unwrap_or_default();
assert!(!config_content.contains("testuser@localhost"));
restore_config(backup);
}
#[tokio::test]
#[serial(config)]
#[ignore] async fn test_whoami() {
let _lock = CONFIG_LOCK.lock().unwrap();
let backup = backup_config();
let config_dir = get_real_config_dir();
fs::create_dir_all(&config_dir).unwrap();
let config = r#"
[server]
url = "http://localhost:28080"
[output]
format = "table"
color = true
[log]
level = "info"
[[accounts]]
name = "testuser@localhost"
server = "http://localhost:28080"
username = "testuser"
token = "valid_token_here"
default = true
role = "user"
"#;
fs::write(config_dir.join("config.toml"), config).unwrap();
let mut cmd = Command::cargo_bin("oauth-db").unwrap();
cmd.arg("whoami")
.assert()
.success()
.stdout(predicates::str::contains("Account Name"))
.stdout(predicates::str::contains("testuser@localhost"));
restore_config(backup);
}
#[tokio::test]
#[serial(config)]
async fn test_whoami_not_logged_in() {
let _lock = CONFIG_LOCK.lock().unwrap();
let backup = backup_config();
let config_dir = get_real_config_dir();
fs::create_dir_all(&config_dir).unwrap();
let config = r#"
[server]
url = "http://localhost:28080"
[output]
format = "table"
color = true
[log]
level = "info"
"#;
fs::write(config_dir.join("config.toml"), config).unwrap();
let mut cmd = Command::cargo_bin("oauth-db").unwrap();
cmd.arg("whoami")
.assert()
.failure()
.stderr(predicates::str::contains("Not logged in"));
restore_config(backup);
}
#[tokio::test]
#[serial(config)]
async fn test_accounts_list() {
let _lock = CONFIG_LOCK.lock().unwrap();
let backup = backup_config();
let config_dir = get_real_config_dir();
fs::create_dir_all(&config_dir).unwrap();
let config = r#"
[server]
url = "http://localhost:28080"
[output]
format = "table"
color = true
[log]
level = "info"
[[accounts]]
name = "user1@localhost"
server = "http://localhost:28080"
username = "user1"
token = "token1"
default = true
role = "user"
[[accounts]]
name = "user2@localhost"
server = "http://localhost:28080"
username = "user2"
token = "token2"
default = false
role = "user"
"#;
fs::write(config_dir.join("config.toml"), config).unwrap();
let mut cmd = Command::cargo_bin("oauth-db").unwrap();
cmd.arg("accounts")
.arg("list")
.assert()
.success()
.stdout(predicates::str::contains("user1@localhost"))
.stdout(predicates::str::contains("user2@localhost"))
.stdout(predicates::str::contains("✓"));
restore_config(backup);
}
#[tokio::test]
#[serial(config)]
async fn test_accounts_list_empty() {
let _lock = CONFIG_LOCK.lock().unwrap();
let backup = backup_config();
let config_dir = get_real_config_dir();
fs::create_dir_all(&config_dir).unwrap();
let config = r#"
[server]
url = "http://localhost:28080"
[output]
format = "table"
color = true
[log]
level = "info"
"#;
fs::write(config_dir.join("config.toml"), config).unwrap();
let mut cmd = Command::cargo_bin("oauth-db").unwrap();
cmd.arg("accounts")
.arg("list")
.assert()
.success()
.stdout(predicates::str::contains("No accounts found"));
restore_config(backup);
}
#[tokio::test]
#[serial(config)]
async fn test_accounts_switch() {
let _lock = CONFIG_LOCK.lock().unwrap();
let backup = backup_config();
let config_dir = get_real_config_dir();
fs::create_dir_all(&config_dir).unwrap();
let config = r#"
[server]
url = "http://localhost:28080"
[output]
format = "table"
color = true
[log]
level = "info"
[[accounts]]
name = "user1@localhost"
server = "http://localhost:28080"
username = "user1"
token = "token1"
default = true
role = "user"
[[accounts]]
name = "user2@localhost"
server = "http://localhost:28080"
username = "user2"
token = "token2"
default = false
role = "user"
"#;
fs::write(config_dir.join("config.toml"), config).unwrap();
let mut cmd = Command::cargo_bin("oauth-db").unwrap();
cmd.arg("accounts")
.arg("switch")
.arg("user2@localhost")
.assert()
.success()
.stdout(predicates::str::contains("Switched to account: user2@localhost"));
let mut cmd = Command::cargo_bin("oauth-db").unwrap();
cmd.arg("accounts")
.arg("list")
.assert()
.success();
restore_config(backup);
}
#[tokio::test]
#[serial(config)]
async fn test_accounts_switch_not_found() {
let _lock = CONFIG_LOCK.lock().unwrap();
let backup = backup_config();
let config_dir = get_real_config_dir();
fs::create_dir_all(&config_dir).unwrap();
let config = r#"
[server]
url = "http://localhost:28080"
[output]
format = "table"
color = true
[log]
level = "info"
[[accounts]]
name = "user1@localhost"
server = "http://localhost:28080"
username = "user1"
token = "token1"
default = true
role = "user"
"#;
fs::write(config_dir.join("config.toml"), config).unwrap();
let mut cmd = Command::cargo_bin("oauth-db").unwrap();
cmd.arg("accounts")
.arg("switch")
.arg("nonexistent@localhost")
.assert()
.failure()
.stderr(predicates::str::contains("not found"));
restore_config(backup);
}
#[tokio::test]
#[serial(config)]
async fn test_accounts_remove() {
let _lock = CONFIG_LOCK.lock().unwrap();
let backup = backup_config();
let config_dir = get_real_config_dir();
fs::create_dir_all(&config_dir).unwrap();
let config = r#"
[server]
url = "http://localhost:28080"
[output]
format = "table"
color = true
[log]
level = "info"
[[accounts]]
name = "user1@localhost"
server = "http://localhost:28080"
username = "user1"
token = "token1"
default = true
role = "user"
[[accounts]]
name = "user2@localhost"
server = "http://localhost:28080"
username = "user2"
token = "token2"
default = false
role = "user"
"#;
fs::write(config_dir.join("config.toml"), config).unwrap();
let mut cmd = Command::cargo_bin("oauth-db").unwrap();
cmd.arg("accounts")
.arg("remove")
.arg("user2@localhost")
.assert()
.success()
.stdout(predicates::str::contains("Removed account: user2@localhost"));
let mut cmd = Command::cargo_bin("oauth-db").unwrap();
cmd.arg("accounts")
.arg("list")
.assert()
.success()
.stdout(predicates::str::contains("user1@localhost"))
.stdout(predicates::str::contains("user2@localhost").not());
restore_config(backup);
}
#[tokio::test]
#[serial(config)]
async fn test_accounts_remove_not_found() {
let _lock = CONFIG_LOCK.lock().unwrap();
let backup = backup_config();
let config_dir = get_real_config_dir();
fs::create_dir_all(&config_dir).unwrap();
let config = r#"
[server]
url = "http://localhost:28080"
[output]
format = "table"
color = true
[log]
level = "info"
[[accounts]]
name = "user1@localhost"
server = "http://localhost:28080"
username = "user1"
token = "token1"
default = true
role = "user"
"#;
fs::write(config_dir.join("config.toml"), config).unwrap();
let mut cmd = Command::cargo_bin("oauth-db").unwrap();
cmd.arg("accounts")
.arg("remove")
.arg("nonexistent@localhost")
.assert()
.failure()
.stderr(predicates::str::contains("not found"));
restore_config(backup);
}
#[tokio::test]
#[serial(config)]
#[ignore] async fn test_login_invalid_credentials() {
let _lock = CONFIG_LOCK.lock().unwrap();
let backup = backup_config();
let mut cmd = Command::cargo_bin("oauth-db").unwrap();
cmd.arg("login")
.arg("--server")
.arg("http://localhost:28080")
.arg("--username")
.arg("invalid_user")
.arg("--password")
.arg("wrong_password")
.assert()
.failure()
.stderr(predicates::str::contains("Login failed"));
restore_config(backup);
}
#[tokio::test]
#[serial(config)]
#[ignore] async fn test_login_custom_server() {
let _lock = CONFIG_LOCK.lock().unwrap();
let backup = backup_config();
let custom_server = "http://custom.example.com:8080";
let mut cmd = Command::cargo_bin("oauth-db").unwrap();
cmd.arg("login")
.arg("--server")
.arg(custom_server)
.arg("--username")
.arg("testuser")
.arg("--password")
.arg("testpass")
.assert()
.success()
.stdout(predicates::str::contains("Logged in as testuser"));
restore_config(backup);
}
#[tokio::test]
#[serial(config)]
async fn test_multiple_accounts_workflow() {
let _lock = CONFIG_LOCK.lock().unwrap();
let backup = backup_config();
let config_dir = get_real_config_dir();
fs::create_dir_all(&config_dir).unwrap();
let config = r#"
[server]
url = "http://localhost:28080"
[output]
format = "table"
color = true
[log]
level = "info"
[[accounts]]
name = "dev@localhost"
server = "http://localhost:28080"
username = "dev"
token = "dev_token"
default = true
role = "user"
[[accounts]]
name = "prod@localhost"
server = "http://localhost:28080"
username = "prod"
token = "prod_token"
default = false
role = "user"
[[accounts]]
name = "staging@localhost"
server = "http://localhost:28080"
username = "staging"
token = "staging_token"
default = false
role = "user"
"#;
fs::write(config_dir.join("config.toml"), config).unwrap();
let mut cmd = Command::cargo_bin("oauth-db").unwrap();
cmd.arg("accounts")
.arg("list")
.assert()
.success()
.stdout(predicates::str::contains("dev@localhost"))
.stdout(predicates::str::contains("prod@localhost"))
.stdout(predicates::str::contains("staging@localhost"));
let mut cmd = Command::cargo_bin("oauth-db").unwrap();
cmd.arg("accounts")
.arg("switch")
.arg("prod@localhost")
.assert()
.success();
let mut cmd = Command::cargo_bin("oauth-db").unwrap();
cmd.arg("accounts")
.arg("remove")
.arg("staging@localhost")
.assert()
.success();
let mut cmd = Command::cargo_bin("oauth-db").unwrap();
cmd.arg("accounts")
.arg("list")
.assert()
.success()
.stdout(predicates::str::contains("dev@localhost"))
.stdout(predicates::str::contains("prod@localhost"))
.stdout(predicates::str::contains("staging@localhost").not());
restore_config(backup);
}
#[tokio::test]
#[serial(config)]
async fn test_account_persistence() {
let _lock = CONFIG_LOCK.lock().unwrap();
let backup = backup_config();
let config_dir = get_real_config_dir();
fs::create_dir_all(&config_dir).unwrap();
let config = r#"
[server]
url = "http://localhost:28080"
[output]
format = "table"
color = true
[log]
level = "info"
[[accounts]]
name = "persistent@localhost"
server = "http://localhost:28080"
username = "persistent"
token = "persistent_token"
default = true
role = "user"
"#;
fs::write(config_dir.join("config.toml"), config).unwrap();
for _ in 0..3 {
let mut cmd = Command::cargo_bin("oauth-db").unwrap();
cmd.arg("accounts")
.arg("list")
.assert()
.success()
.stdout(predicates::str::contains("persistent@localhost"));
}
restore_config(backup);
}