Some checks failed
CI / Rustfmt (push) Successful in 23s
CI / Cargo Audit & Deny (push) Successful in 30s
CI / Web Blocking Checks (push) Successful in 48s
CI / Security Blocking Checks (push) Successful in 8s
CI / Clippy (push) Failing after 1m55s
CI / Web Advisory Checks (push) Successful in 35s
CI / Security Advisory Checks (push) Successful in 37s
CI / Tests (push) Successful in 8m5s
530 lines
16 KiB
Rust
530 lines
16 KiB
Rust
//! Integration tests for CLI config and profile management commands
|
|
#![allow(deprecated)]
|
|
|
|
use assert_cmd::Command;
|
|
use predicates::prelude::*;
|
|
|
|
mod common;
|
|
use common::*;
|
|
|
|
#[tokio::test]
|
|
async fn test_config_show_default() {
|
|
let fixture = TestFixture::new().await;
|
|
fixture.write_default_config();
|
|
|
|
let mut cmd = Command::cargo_bin("attune").unwrap();
|
|
cmd.env("XDG_CONFIG_HOME", fixture.config_dir_path())
|
|
.env("HOME", fixture.config_dir_path())
|
|
.arg("config")
|
|
.arg("list");
|
|
|
|
cmd.assert()
|
|
.success()
|
|
.stdout(predicate::str::contains("profile"))
|
|
.stdout(predicate::str::contains("api_url"));
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_config_show_json_output() {
|
|
let fixture = TestFixture::new().await;
|
|
fixture.write_default_config();
|
|
|
|
let mut cmd = Command::cargo_bin("attune").unwrap();
|
|
cmd.env("XDG_CONFIG_HOME", fixture.config_dir_path())
|
|
.env("HOME", fixture.config_dir_path())
|
|
.arg("--json")
|
|
.arg("config")
|
|
.arg("list");
|
|
|
|
cmd.assert()
|
|
.success()
|
|
.stdout(predicate::str::contains(r#""profile""#))
|
|
.stdout(predicate::str::contains(r#""api_url""#));
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_config_show_yaml_output() {
|
|
let fixture = TestFixture::new().await;
|
|
fixture.write_default_config();
|
|
|
|
let mut cmd = Command::cargo_bin("attune").unwrap();
|
|
cmd.env("XDG_CONFIG_HOME", fixture.config_dir_path())
|
|
.env("HOME", fixture.config_dir_path())
|
|
.arg("--yaml")
|
|
.arg("config")
|
|
.arg("list");
|
|
|
|
cmd.assert()
|
|
.success()
|
|
.stdout(predicate::str::contains("profile:"))
|
|
.stdout(predicate::str::contains("api_url:"));
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_config_get_specific_key() {
|
|
let fixture = TestFixture::new().await;
|
|
fixture.write_default_config();
|
|
|
|
let mut cmd = Command::cargo_bin("attune").unwrap();
|
|
cmd.env("XDG_CONFIG_HOME", fixture.config_dir_path())
|
|
.env("HOME", fixture.config_dir_path())
|
|
.arg("config")
|
|
.arg("get")
|
|
.arg("api_url");
|
|
|
|
cmd.assert()
|
|
.success()
|
|
.stdout(predicate::str::contains(fixture.server_url()));
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_config_get_nonexistent_key() {
|
|
let fixture = TestFixture::new().await;
|
|
fixture.write_default_config();
|
|
|
|
let mut cmd = Command::cargo_bin("attune").unwrap();
|
|
cmd.env("XDG_CONFIG_HOME", fixture.config_dir_path())
|
|
.env("HOME", fixture.config_dir_path())
|
|
.arg("config")
|
|
.arg("get")
|
|
.arg("nonexistent_key");
|
|
|
|
cmd.assert()
|
|
.failure()
|
|
.stderr(predicate::str::contains("Error"));
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_config_set_api_url() {
|
|
let fixture = TestFixture::new().await;
|
|
fixture.write_default_config();
|
|
|
|
let mut cmd = Command::cargo_bin("attune").unwrap();
|
|
cmd.env("XDG_CONFIG_HOME", fixture.config_dir_path())
|
|
.env("HOME", fixture.config_dir_path())
|
|
.arg("config")
|
|
.arg("set")
|
|
.arg("api_url")
|
|
.arg("https://new-api.example.com");
|
|
|
|
cmd.assert()
|
|
.success()
|
|
.stdout(predicate::str::contains("Configuration updated"));
|
|
|
|
// Verify the change was persisted
|
|
let config_content =
|
|
std::fs::read_to_string(&fixture.config_path).expect("Failed to read config");
|
|
assert!(config_content.contains("https://new-api.example.com"));
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_config_set_format() {
|
|
let fixture = TestFixture::new().await;
|
|
fixture.write_default_config();
|
|
|
|
let mut cmd = Command::cargo_bin("attune").unwrap();
|
|
cmd.env("XDG_CONFIG_HOME", fixture.config_dir_path())
|
|
.env("HOME", fixture.config_dir_path())
|
|
.arg("config")
|
|
.arg("set")
|
|
.arg("format")
|
|
.arg("json");
|
|
|
|
cmd.assert()
|
|
.success()
|
|
.stdout(predicate::str::contains("Configuration updated"));
|
|
|
|
// Verify the change was persisted
|
|
let config_content =
|
|
std::fs::read_to_string(&fixture.config_path).expect("Failed to read config");
|
|
assert!(config_content.contains("format: json"));
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_profile_list() {
|
|
let fixture = TestFixture::new().await;
|
|
fixture.write_multi_profile_config();
|
|
|
|
let mut cmd = Command::cargo_bin("attune").unwrap();
|
|
cmd.env("XDG_CONFIG_HOME", fixture.config_dir_path())
|
|
.env("HOME", fixture.config_dir_path())
|
|
.arg("config")
|
|
.arg("profiles");
|
|
|
|
cmd.assert()
|
|
.success()
|
|
.stdout(predicate::str::contains("default"))
|
|
.stdout(predicate::str::contains("staging"))
|
|
.stdout(predicate::str::contains("production"));
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_profile_list_shows_current() {
|
|
let fixture = TestFixture::new().await;
|
|
fixture.write_multi_profile_config();
|
|
|
|
let mut cmd = Command::cargo_bin("attune").unwrap();
|
|
cmd.env("XDG_CONFIG_HOME", fixture.config_dir_path())
|
|
.env("HOME", fixture.config_dir_path())
|
|
.arg("config")
|
|
.arg("profiles");
|
|
|
|
cmd.assert()
|
|
.success()
|
|
.stdout(predicate::str::contains("*").or(predicate::str::contains("(active)")));
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_profile_show_specific() {
|
|
let fixture = TestFixture::new().await;
|
|
fixture.write_multi_profile_config();
|
|
|
|
let mut cmd = Command::cargo_bin("attune").unwrap();
|
|
cmd.env("XDG_CONFIG_HOME", fixture.config_dir_path())
|
|
.env("HOME", fixture.config_dir_path())
|
|
.arg("config")
|
|
.arg("show-profile")
|
|
.arg("staging");
|
|
|
|
cmd.assert()
|
|
.success()
|
|
.stdout(predicate::str::contains("staging.example.com"));
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_profile_show_nonexistent() {
|
|
let fixture = TestFixture::new().await;
|
|
fixture.write_multi_profile_config();
|
|
|
|
let mut cmd = Command::cargo_bin("attune").unwrap();
|
|
cmd.env("XDG_CONFIG_HOME", fixture.config_dir_path())
|
|
.env("HOME", fixture.config_dir_path())
|
|
.arg("config")
|
|
.arg("show-profile")
|
|
.arg("nonexistent");
|
|
|
|
cmd.assert()
|
|
.failure()
|
|
.stderr(predicate::str::contains("Error"));
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_profile_add_new() {
|
|
let fixture = TestFixture::new().await;
|
|
fixture.write_default_config();
|
|
|
|
let mut cmd = Command::cargo_bin("attune").unwrap();
|
|
cmd.env("XDG_CONFIG_HOME", fixture.config_dir_path())
|
|
.env("HOME", fixture.config_dir_path())
|
|
.arg("config")
|
|
.arg("add-profile")
|
|
.arg("testing")
|
|
.arg("--api-url")
|
|
.arg("https://test.example.com")
|
|
.arg("--description")
|
|
.arg("Testing environment");
|
|
|
|
cmd.assert()
|
|
.success()
|
|
.stdout(predicate::str::contains("Profile 'testing' added"));
|
|
|
|
// Verify the profile was added
|
|
let config_content =
|
|
std::fs::read_to_string(&fixture.config_path).expect("Failed to read config");
|
|
assert!(config_content.contains("testing:"));
|
|
assert!(config_content.contains("https://test.example.com"));
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_profile_add_without_description() {
|
|
let fixture = TestFixture::new().await;
|
|
fixture.write_default_config();
|
|
|
|
let mut cmd = Command::cargo_bin("attune").unwrap();
|
|
cmd.env("XDG_CONFIG_HOME", fixture.config_dir_path())
|
|
.env("HOME", fixture.config_dir_path())
|
|
.arg("config")
|
|
.arg("add-profile")
|
|
.arg("newprofile")
|
|
.arg("--api-url")
|
|
.arg("https://new.example.com");
|
|
|
|
cmd.assert()
|
|
.success()
|
|
.stdout(predicate::str::contains("Profile 'newprofile' added"));
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_profile_use_switch() {
|
|
let fixture = TestFixture::new().await;
|
|
fixture.write_multi_profile_config();
|
|
|
|
let mut cmd = Command::cargo_bin("attune").unwrap();
|
|
cmd.env("XDG_CONFIG_HOME", fixture.config_dir_path())
|
|
.env("HOME", fixture.config_dir_path())
|
|
.arg("config")
|
|
.arg("use")
|
|
.arg("staging");
|
|
|
|
cmd.assert()
|
|
.success()
|
|
.stdout(predicate::str::contains("Switched to profile 'staging'"));
|
|
|
|
// Verify the current profile was changed
|
|
let config_content =
|
|
std::fs::read_to_string(&fixture.config_path).expect("Failed to read config");
|
|
assert!(config_content.contains("profile: staging"));
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_profile_use_nonexistent() {
|
|
let fixture = TestFixture::new().await;
|
|
fixture.write_multi_profile_config();
|
|
|
|
let mut cmd = Command::cargo_bin("attune").unwrap();
|
|
cmd.env("XDG_CONFIG_HOME", fixture.config_dir_path())
|
|
.env("HOME", fixture.config_dir_path())
|
|
.arg("config")
|
|
.arg("use")
|
|
.arg("nonexistent");
|
|
|
|
cmd.assert()
|
|
.failure()
|
|
.stderr(predicate::str::contains("does not exist"));
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_profile_remove() {
|
|
let fixture = TestFixture::new().await;
|
|
fixture.write_multi_profile_config();
|
|
|
|
let mut cmd = Command::cargo_bin("attune").unwrap();
|
|
cmd.env("XDG_CONFIG_HOME", fixture.config_dir_path())
|
|
.env("HOME", fixture.config_dir_path())
|
|
.arg("config")
|
|
.arg("remove-profile")
|
|
.arg("staging");
|
|
|
|
cmd.assert()
|
|
.success()
|
|
.stdout(predicate::str::contains("Profile 'staging' removed"));
|
|
|
|
// Verify the profile was removed
|
|
let config_content =
|
|
std::fs::read_to_string(&fixture.config_path).expect("Failed to read config");
|
|
assert!(!config_content.contains("staging:"));
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_profile_remove_default_fails() {
|
|
let fixture = TestFixture::new().await;
|
|
fixture.write_default_config();
|
|
|
|
let mut cmd = Command::cargo_bin("attune").unwrap();
|
|
cmd.env("XDG_CONFIG_HOME", fixture.config_dir_path())
|
|
.env("HOME", fixture.config_dir_path())
|
|
.arg("config")
|
|
.arg("remove-profile")
|
|
.arg("default");
|
|
|
|
cmd.assert()
|
|
.failure()
|
|
.stderr(predicate::str::contains("Cannot remove"));
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_profile_remove_active_fails() {
|
|
let fixture = TestFixture::new().await;
|
|
fixture.write_multi_profile_config();
|
|
|
|
// Try to remove the currently active profile
|
|
let mut cmd = Command::cargo_bin("attune").unwrap();
|
|
cmd.env("XDG_CONFIG_HOME", fixture.config_dir_path())
|
|
.env("HOME", fixture.config_dir_path())
|
|
.arg("config")
|
|
.arg("remove-profile")
|
|
.arg("default");
|
|
|
|
cmd.assert()
|
|
.failure()
|
|
.stderr(predicate::str::contains("Cannot remove active profile"));
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_profile_remove_nonexistent() {
|
|
let fixture = TestFixture::new().await;
|
|
fixture.write_default_config();
|
|
|
|
let mut cmd = Command::cargo_bin("attune").unwrap();
|
|
cmd.env("XDG_CONFIG_HOME", fixture.config_dir_path())
|
|
.env("HOME", fixture.config_dir_path())
|
|
.arg("config")
|
|
.arg("remove-profile")
|
|
.arg("nonexistent");
|
|
|
|
cmd.assert().success(); // Removing non-existent profile might be a no-op
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_profile_override_with_flag() {
|
|
let fixture = TestFixture::new().await;
|
|
fixture.write_multi_profile_config();
|
|
|
|
// Use --profile flag to temporarily override
|
|
let mut cmd = Command::cargo_bin("attune").unwrap();
|
|
cmd.env("XDG_CONFIG_HOME", fixture.config_dir_path())
|
|
.env("HOME", fixture.config_dir_path())
|
|
.arg("--profile")
|
|
.arg("staging")
|
|
.arg("config")
|
|
.arg("list");
|
|
|
|
cmd.assert().success();
|
|
|
|
// Verify current profile wasn't changed in the config file
|
|
let config_content =
|
|
std::fs::read_to_string(&fixture.config_path).expect("Failed to read config");
|
|
assert!(config_content.contains("profile: default"));
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_profile_override_with_env_var() {
|
|
let fixture = TestFixture::new().await;
|
|
fixture.write_multi_profile_config();
|
|
|
|
// Use ATTUNE_PROFILE env var to temporarily override
|
|
let mut cmd = Command::cargo_bin("attune").unwrap();
|
|
cmd.env("XDG_CONFIG_HOME", fixture.config_dir_path())
|
|
.env("HOME", fixture.config_dir_path())
|
|
.env("ATTUNE_PROFILE", "production")
|
|
.arg("config")
|
|
.arg("list");
|
|
|
|
cmd.assert().success();
|
|
|
|
// Verify current profile wasn't changed in the config file
|
|
let config_content =
|
|
std::fs::read_to_string(&fixture.config_path).expect("Failed to read config");
|
|
assert!(config_content.contains("profile: default"));
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_config_format_respected_by_commands() {
|
|
let fixture = TestFixture::new().await;
|
|
// Write a config with format set to json
|
|
let config = format!(
|
|
r#"
|
|
profile: default
|
|
format: json
|
|
profiles:
|
|
default:
|
|
api_url: {}
|
|
description: Test server
|
|
"#,
|
|
fixture.server_url()
|
|
);
|
|
fixture.write_config(&config);
|
|
|
|
// Run config list without --json flag; should output JSON because config says so
|
|
let mut cmd = Command::cargo_bin("attune").unwrap();
|
|
cmd.env("XDG_CONFIG_HOME", fixture.config_dir_path())
|
|
.env("HOME", fixture.config_dir_path())
|
|
.arg("config")
|
|
.arg("list");
|
|
|
|
// JSON output contains curly braces
|
|
cmd.assert().success().stdout(predicate::str::contains("{"));
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_config_list_all_keys() {
|
|
let fixture = TestFixture::new().await;
|
|
fixture.write_authenticated_config("test_token", "test_refresh");
|
|
|
|
let mut cmd = Command::cargo_bin("attune").unwrap();
|
|
cmd.env("XDG_CONFIG_HOME", fixture.config_dir_path())
|
|
.env("HOME", fixture.config_dir_path())
|
|
.arg("config")
|
|
.arg("list");
|
|
|
|
cmd.assert()
|
|
.success()
|
|
.stdout(predicate::str::contains("api_url"))
|
|
.stdout(predicate::str::contains("format"))
|
|
.stdout(predicate::str::contains("auth_token"));
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_config_masks_sensitive_data() {
|
|
let fixture = TestFixture::new().await;
|
|
fixture.write_authenticated_config("secret_token_123", "secret_refresh_456");
|
|
|
|
let mut cmd = Command::cargo_bin("attune").unwrap();
|
|
cmd.env("XDG_CONFIG_HOME", fixture.config_dir_path())
|
|
.env("HOME", fixture.config_dir_path())
|
|
.arg("config")
|
|
.arg("get")
|
|
.arg("auth_token");
|
|
|
|
cmd.assert()
|
|
.success()
|
|
.stdout(predicate::str::contains("***"));
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_profile_add_duplicate_overwrites() {
|
|
let fixture = TestFixture::new().await;
|
|
fixture.write_multi_profile_config();
|
|
|
|
// Add a profile with the same name as existing one
|
|
let mut cmd = Command::cargo_bin("attune").unwrap();
|
|
cmd.env("XDG_CONFIG_HOME", fixture.config_dir_path())
|
|
.env("HOME", fixture.config_dir_path())
|
|
.arg("config")
|
|
.arg("add-profile")
|
|
.arg("staging")
|
|
.arg("--api-url")
|
|
.arg("https://new-staging.example.com");
|
|
|
|
cmd.assert().success();
|
|
|
|
// Verify the profile was updated
|
|
let config_content =
|
|
std::fs::read_to_string(&fixture.config_path).expect("Failed to read config");
|
|
assert!(config_content.contains("https://new-staging.example.com"));
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_profile_list_json_output() {
|
|
let fixture = TestFixture::new().await;
|
|
fixture.write_multi_profile_config();
|
|
|
|
let mut cmd = Command::cargo_bin("attune").unwrap();
|
|
cmd.env("XDG_CONFIG_HOME", fixture.config_dir_path())
|
|
.env("HOME", fixture.config_dir_path())
|
|
.arg("--json")
|
|
.arg("config")
|
|
.arg("profiles");
|
|
|
|
cmd.assert()
|
|
.success()
|
|
.stdout(predicate::str::contains(r#""default""#))
|
|
.stdout(predicate::str::contains(r#""staging""#));
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_config_path_display() {
|
|
let fixture = TestFixture::new().await;
|
|
fixture.write_default_config();
|
|
|
|
let mut cmd = Command::cargo_bin("attune").unwrap();
|
|
cmd.env("XDG_CONFIG_HOME", fixture.config_dir_path())
|
|
.env("HOME", fixture.config_dir_path())
|
|
.arg("config")
|
|
.arg("path");
|
|
|
|
cmd.assert()
|
|
.success()
|
|
.stdout(predicate::str::contains("config.yaml"));
|
|
}
|