use assert_cmd::Command;
use predicates::prelude::*;
use serde_json::json;
use std::fs;
use tempfile::tempdir;
fn tod_command() -> Command {
Command::cargo_bin("tod").expect("tod binary should build")
}
fn write_config_with_timezone(path: &std::path::Path, token: Option<&str>) {
let mut config = json!({
"path": path.to_string_lossy(),
"timezone": "UTC",
});
if let Some(token) = token {
config["token"] = json!(token);
}
fs::write(path, config.to_string()).expect("config should be written");
}
#[test]
fn auth_token_updates_config_with_existing_timezone() {
let dir = tempdir().expect("temp dir should be created");
let path = dir.path().join("tod.cfg");
write_config_with_timezone(&path, None);
tod_command()
.arg("--config")
.arg(&path)
.args(["auth", "token", "test-token-abc123"])
.assert()
.success()
.stdout(predicate::str::contains("✓ API token saved to"));
assert!(
path.exists(),
"config file should still exist after auth token"
);
}
#[test]
fn auth_token_output_includes_config_path() {
let dir = tempdir().expect("temp dir should be created");
let path = dir.path().join("tod.cfg");
write_config_with_timezone(&path, None);
tod_command()
.arg("--config")
.arg(&path)
.args(["auth", "token", "test-token-abc123"])
.assert()
.success()
.stdout(predicate::str::contains(path.to_string_lossy().as_ref()));
}
#[test]
fn auth_token_succeeds_with_existing_config() {
let dir = tempdir().expect("temp dir should be created");
let path = dir.path().join("tod.cfg");
write_config_with_timezone(&path, Some("initial-token"));
tod_command()
.arg("--config")
.arg(&path)
.args(["auth", "token", "updated-token-xyz"])
.assert()
.success()
.stdout(predicate::str::contains("✓ API token saved to"));
}
#[test]
fn auth_token_can_update_after_creating() {
let dir = tempdir().expect("temp dir should be created");
let path = dir.path().join("tod.cfg");
write_config_with_timezone(&path, Some("first-token"));
assert!(
path.exists(),
"config should exist before auth token update"
);
tod_command()
.arg("--config")
.arg(&path)
.args(["auth", "token", "second-token"])
.assert()
.success()
.stdout(predicate::str::contains("✓ API token saved to"));
assert!(path.exists(), "config should still exist after update");
}
#[test]
fn config_check_reports_valid_for_existing_config() {
let dir = tempdir().expect("temp dir should be created");
let path = dir.path().join("tod.cfg");
fs::write(&path, "{}").expect("config should be written");
tod_command()
.arg("--config")
.arg(&path)
.args(["config", "check"])
.assert()
.success()
.stdout(predicate::str::contains("is valid"));
}
#[test]
fn config_check_alias_reports_valid_for_existing_config() {
let dir = tempdir().expect("temp dir should be created");
let path = dir.path().join("tod.cfg");
fs::write(&path, "{}").expect("config should be written");
tod_command()
.arg("--config")
.arg(&path)
.args(["c", "check"])
.assert()
.success()
.stdout(predicate::str::contains("is valid"));
}
#[test]
fn config_about_works_without_config() {
tod_command()
.args(["config", "about"])
.assert()
.success()
.stdout(predicate::str::contains("APP:"))
.stdout(predicate::str::contains("VERSION:"));
}
#[test]
fn config_about_alias_works_without_config() {
tod_command()
.args(["config", "a"])
.assert()
.success()
.stdout(predicate::str::contains("APP:"));
}
#[test]
fn config_reset_force_deletes_existing_config() {
let dir = tempdir().expect("temp dir should be created");
let path = dir.path().join("tod.cfg");
fs::write(&path, "{}").expect("config should be written");
assert!(path.exists(), "config should exist before reset");
tod_command()
.arg("--config")
.arg(&path)
.args(["config", "reset", "--force"])
.assert()
.success()
.stdout(predicate::str::contains("deleted successfully"));
assert!(!path.exists(), "config should be gone after reset --force");
}
#[test]
fn config_reset_force_when_config_absent_reports_not_found() {
let dir = tempdir().expect("temp dir should be created");
let path = dir.path().join("tod.cfg");
assert!(!path.exists(), "config should not exist before test");
tod_command()
.arg("--config")
.arg(&path)
.args(["config", "reset", "--force"])
.assert()
.failure()
.stderr(predicate::str::contains("No config file found at"));
}
#[test]
fn config_reset_force_with_short_config_flag_deletes_manual_path() {
let dir = tempdir().expect("temp dir should be created");
let path = dir.path().join("manual-tod.cfg");
fs::write(&path, "{}").expect("config should be written");
let expected = format!("Config file at {} deleted successfully.", path.display());
tod_command()
.arg("-c")
.arg(&path)
.args(["config", "reset", "--force"])
.assert()
.success()
.stdout(predicate::str::contains(expected));
assert!(!path.exists(), "manual config path should be deleted");
}
#[test]
fn config_reset_force_with_short_config_flag_reports_missing_manual_path() {
let dir = tempdir().expect("temp dir should be created");
let path = dir.path().join("missing-manual-tod.cfg");
let expected = format!("No config file found at {}.", path.display());
tod_command()
.arg("-c")
.arg(&path)
.args(["config", "reset", "--force"])
.assert()
.failure()
.stderr(predicate::str::contains(expected));
}