[wip] cli capability parity
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

This commit is contained in:
2026-03-06 16:58:50 -06:00
parent 48b6ca6bd7
commit 87d830f952
94 changed files with 3694 additions and 734 deletions

View File

@@ -115,11 +115,33 @@ fn create_test_index(packs: &[(&str, &str)]) -> TempDir {
temp_dir
}
/// Create an isolated CLI command that never touches the user's real config.
///
/// Returns `(Command, TempDir)` — the `TempDir` must be kept alive for the
/// duration of the test so the config directory isn't deleted prematurely.
fn isolated_cmd() -> (Command, TempDir) {
let config_dir = TempDir::new().expect("Failed to create temp config dir");
// Write a minimal default config so the CLI doesn't try to create one
let attune_dir = config_dir.path().join("attune");
fs::create_dir_all(&attune_dir).expect("Failed to create attune config dir");
fs::write(
attune_dir.join("config.yaml"),
"profile: default\nformat: table\nprofiles:\n default:\n api_url: http://localhost:8080\n",
)
.expect("Failed to write test config");
let mut cmd = Command::cargo_bin("attune").unwrap();
cmd.env("XDG_CONFIG_HOME", config_dir.path())
.env("HOME", config_dir.path());
(cmd, config_dir)
}
#[test]
fn test_pack_checksum_directory() {
let pack_dir = create_test_pack("checksum-test", "1.0.0", &[]);
let mut cmd = Command::cargo_bin("attune").unwrap();
let (mut cmd, _config_dir) = isolated_cmd();
cmd.arg("--output")
.arg("table")
.arg("pack")
@@ -135,7 +157,7 @@ fn test_pack_checksum_directory() {
fn test_pack_checksum_json_output() {
let pack_dir = create_test_pack("checksum-json", "1.0.0", &[]);
let mut cmd = Command::cargo_bin("attune").unwrap();
let (mut cmd, _config_dir) = isolated_cmd();
cmd.arg("--output")
.arg("json")
.arg("pack")
@@ -153,7 +175,7 @@ fn test_pack_checksum_json_output() {
#[test]
fn test_pack_checksum_nonexistent_path() {
let mut cmd = Command::cargo_bin("attune").unwrap();
let (mut cmd, _config_dir) = isolated_cmd();
cmd.arg("pack").arg("checksum").arg("/nonexistent/path");
cmd.assert().failure().stderr(
@@ -165,7 +187,7 @@ fn test_pack_checksum_nonexistent_path() {
fn test_pack_index_entry_generates_valid_json() {
let pack_dir = create_test_pack("index-entry-test", "1.2.3", &[]);
let mut cmd = Command::cargo_bin("attune").unwrap();
let (mut cmd, _config_dir) = isolated_cmd();
cmd.arg("--output")
.arg("json")
.arg("pack")
@@ -199,7 +221,7 @@ fn test_pack_index_entry_generates_valid_json() {
fn test_pack_index_entry_with_archive_url() {
let pack_dir = create_test_pack("archive-test", "2.0.0", &[]);
let mut cmd = Command::cargo_bin("attune").unwrap();
let (mut cmd, _config_dir) = isolated_cmd();
cmd.arg("--output")
.arg("json")
.arg("pack")
@@ -227,7 +249,7 @@ fn test_pack_index_entry_missing_pack_yaml() {
let temp_dir = TempDir::new().unwrap();
fs::write(temp_dir.path().join("readme.txt"), "No pack.yaml here").unwrap();
let mut cmd = Command::cargo_bin("attune").unwrap();
let (mut cmd, _config_dir) = isolated_cmd();
cmd.arg("pack")
.arg("index-entry")
.arg(temp_dir.path().to_str().unwrap());
@@ -244,7 +266,7 @@ fn test_pack_index_update_adds_new_entry() {
let pack_dir = create_test_pack("new-pack", "1.0.0", &[]);
let mut cmd = Command::cargo_bin("attune").unwrap();
let (mut cmd, _config_dir) = isolated_cmd();
cmd.arg("pack")
.arg("index-update")
.arg("--index")
@@ -273,7 +295,7 @@ fn test_pack_index_update_prevents_duplicate_without_flag() {
let pack_dir = create_test_pack("existing-pack", "1.0.0", &[]);
let mut cmd = Command::cargo_bin("attune").unwrap();
let (mut cmd, _config_dir) = isolated_cmd();
cmd.arg("pack")
.arg("index-update")
.arg("--index")
@@ -294,7 +316,7 @@ fn test_pack_index_update_with_update_flag() {
let pack_dir = create_test_pack("existing-pack", "2.0.0", &[]);
let mut cmd = Command::cargo_bin("attune").unwrap();
let (mut cmd, _config_dir) = isolated_cmd();
cmd.arg("pack")
.arg("index-update")
.arg("--index")
@@ -327,7 +349,7 @@ fn test_pack_index_update_invalid_index_file() {
let pack_dir = create_test_pack("test-pack", "1.0.0", &[]);
let mut cmd = Command::cargo_bin("attune").unwrap();
let (mut cmd, _config_dir) = isolated_cmd();
cmd.arg("pack")
.arg("index-update")
.arg("--index")
@@ -345,8 +367,10 @@ fn test_pack_index_merge_combines_indexes() {
let output_dir = TempDir::new().unwrap();
let output_path = output_dir.path().join("merged.json");
let mut cmd = Command::cargo_bin("attune").unwrap();
cmd.arg("pack")
let (mut cmd, _config_dir) = isolated_cmd();
cmd.arg("--output")
.arg("table")
.arg("pack")
.arg("index-merge")
.arg("--file")
.arg(output_path.to_str().unwrap())
@@ -372,8 +396,10 @@ fn test_pack_index_merge_deduplicates() {
let output_dir = TempDir::new().unwrap();
let output_path = output_dir.path().join("merged.json");
let mut cmd = Command::cargo_bin("attune").unwrap();
cmd.arg("pack")
let (mut cmd, _config_dir) = isolated_cmd();
cmd.arg("--output")
.arg("table")
.arg("pack")
.arg("index-merge")
.arg("--file")
.arg(output_path.to_str().unwrap())
@@ -403,7 +429,7 @@ fn test_pack_index_merge_output_exists_without_force() {
let output_path = output_dir.path().join("merged.json");
fs::write(&output_path, "existing content").unwrap();
let mut cmd = Command::cargo_bin("attune").unwrap();
let (mut cmd, _config_dir) = isolated_cmd();
cmd.arg("pack")
.arg("index-merge")
.arg("--file")
@@ -423,7 +449,7 @@ fn test_pack_index_merge_with_force_flag() {
let output_path = output_dir.path().join("merged.json");
fs::write(&output_path, "existing content").unwrap();
let mut cmd = Command::cargo_bin("attune").unwrap();
let (mut cmd, _config_dir) = isolated_cmd();
cmd.arg("pack")
.arg("index-merge")
.arg("--file")
@@ -443,7 +469,7 @@ fn test_pack_index_merge_empty_input_list() {
let output_dir = TempDir::new().unwrap();
let output_path = output_dir.path().join("merged.json");
let mut cmd = Command::cargo_bin("attune").unwrap();
let (mut cmd, _config_dir) = isolated_cmd();
cmd.arg("pack")
.arg("index-merge")
.arg("--file")
@@ -459,8 +485,10 @@ fn test_pack_index_merge_missing_input_file() {
let output_dir = TempDir::new().unwrap();
let output_path = output_dir.path().join("merged.json");
let mut cmd = Command::cargo_bin("attune").unwrap();
cmd.arg("pack")
let (mut cmd, _config_dir) = isolated_cmd();
cmd.arg("--output")
.arg("table")
.arg("pack")
.arg("index-merge")
.arg("--file")
.arg(output_path.to_str().unwrap())
@@ -483,7 +511,7 @@ fn test_pack_commands_help() {
];
for args in commands {
let mut cmd = Command::cargo_bin("attune").unwrap();
let (mut cmd, _config_dir) = isolated_cmd();
for arg in &args {
cmd.arg(arg);
}

View File

@@ -20,7 +20,7 @@ async fn test_config_show_default() {
cmd.assert()
.success()
.stdout(predicate::str::contains("current_profile"))
.stdout(predicate::str::contains("profile"))
.stdout(predicate::str::contains("api_url"));
}
@@ -38,7 +38,7 @@ async fn test_config_show_json_output() {
cmd.assert()
.success()
.stdout(predicate::str::contains(r#""current_profile""#))
.stdout(predicate::str::contains(r#""profile""#))
.stdout(predicate::str::contains(r#""api_url""#));
}
@@ -56,7 +56,7 @@ async fn test_config_show_yaml_output() {
cmd.assert()
.success()
.stdout(predicate::str::contains("current_profile:"))
.stdout(predicate::str::contains("profile:"))
.stdout(predicate::str::contains("api_url:"));
}
@@ -118,7 +118,7 @@ async fn test_config_set_api_url() {
}
#[tokio::test]
async fn test_config_set_output_format() {
async fn test_config_set_format() {
let fixture = TestFixture::new().await;
fixture.write_default_config();
@@ -127,7 +127,7 @@ async fn test_config_set_output_format() {
.env("HOME", fixture.config_dir_path())
.arg("config")
.arg("set")
.arg("output_format")
.arg("format")
.arg("json");
cmd.assert()
@@ -137,7 +137,7 @@ async fn test_config_set_output_format() {
// 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("output_format: json"));
assert!(config_content.contains("format: json"));
}
#[tokio::test]
@@ -273,7 +273,7 @@ async fn test_profile_use_switch() {
// 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("current_profile: staging"));
assert!(config_content.contains("profile: staging"));
}
#[tokio::test]
@@ -384,7 +384,7 @@ async fn test_profile_override_with_flag() {
// 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("current_profile: default"));
assert!(config_content.contains("profile: default"));
}
#[tokio::test]
@@ -405,28 +405,35 @@ async fn test_profile_override_with_env_var() {
// 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("current_profile: default"));
assert!(config_content.contains("profile: default"));
}
#[tokio::test]
async fn test_profile_with_custom_output_format() {
async fn test_config_format_respected_by_commands() {
let fixture = TestFixture::new().await;
fixture.write_multi_profile_config();
// 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);
// Switch to production which has json output format
// 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("use")
.arg("production");
.arg("list");
cmd.assert().success();
// Verify the profile has custom output format
let config_content =
std::fs::read_to_string(&fixture.config_path).expect("Failed to read config");
assert!(config_content.contains("output_format: json"));
// JSON output contains curly braces
cmd.assert().success().stdout(predicate::str::contains("{"));
}
#[tokio::test]
@@ -443,7 +450,7 @@ async fn test_config_list_all_keys() {
cmd.assert()
.success()
.stdout(predicate::str::contains("api_url"))
.stdout(predicate::str::contains("output_format"))
.stdout(predicate::str::contains("format"))
.stdout(predicate::str::contains("auth_token"));
}