Phase 1 - Code Quality: - Rename script/script.rs to script/types.rs (module inception fix) - Apply Clippy lint fixes (is_none_or, is_some_and, char patterns, etc.) - Implement FromStr for CatalogFile and Category - Add filtered_targets() and filtered_repos() helpers to Config Phase 2 - Validation & Error Handling: - Add validate_repo_identifier() for GitHub shorthand validation - Fix first-run setup to fail gracefully if no targets configured - Improve import to collect failures and only save on success - Add AssetInstallResult for detailed install failure tracking - Fix lockfile timestamp documentation (Unix epoch, not RFC 3339) - Add comprehensive RevType heuristics documentation - Add checkout warning when local modifications will be discarded Phase 3 - Test Coverage: - Add tempfile, assert_cmd, predicates dev dependencies - Add security tests (symlink boundaries, copy mode) - Add git operations tests (init, head_commit, RevType parsing) - Add lockfile tests (roundtrip, lock/get operations) - Add CLI integration tests (help, validation, duplicates) - Add config validation tests for new helper methods All 48 tests pass, clippy clean, release build verified.
200 lines
5.2 KiB
Rust
200 lines
5.2 KiB
Rust
//! CLI integration tests for empeve
|
|
//!
|
|
//! These tests exercise the binary's command-line interface to ensure
|
|
//! proper user-facing behavior.
|
|
|
|
use assert_cmd::Command;
|
|
use predicates::prelude::*;
|
|
use tempfile::TempDir;
|
|
|
|
/// Get a command instance for empeve
|
|
#[allow(deprecated)]
|
|
fn empeve() -> Command {
|
|
Command::cargo_bin("empeve").unwrap()
|
|
}
|
|
|
|
#[test]
|
|
fn test_cli_help() {
|
|
empeve()
|
|
.arg("--help")
|
|
.assert()
|
|
.success()
|
|
.stdout(predicate::str::contains("Plugin manager for mpv"));
|
|
}
|
|
|
|
#[test]
|
|
fn test_cli_version() {
|
|
empeve()
|
|
.arg("--version")
|
|
.assert()
|
|
.success()
|
|
.stdout(predicate::str::contains("empeve"));
|
|
}
|
|
|
|
#[test]
|
|
fn test_cli_help_add() {
|
|
empeve()
|
|
.args(["add", "--help"])
|
|
.assert()
|
|
.success()
|
|
.stdout(predicate::str::contains("Add a repository"));
|
|
}
|
|
|
|
#[test]
|
|
fn test_cli_help_install() {
|
|
empeve()
|
|
.args(["install", "--help"])
|
|
.assert()
|
|
.success()
|
|
.stdout(predicate::str::contains("Install all configured repositories"));
|
|
}
|
|
|
|
#[test]
|
|
fn test_cli_help_update() {
|
|
empeve()
|
|
.args(["update", "--help"])
|
|
.assert()
|
|
.success()
|
|
.stdout(predicate::str::contains("Update all repositories"));
|
|
}
|
|
|
|
/// Test that the add command rejects invalid repo formats
|
|
#[test]
|
|
fn test_cli_add_invalid_repo_format() {
|
|
let temp = TempDir::new().unwrap();
|
|
let config_dir = temp.path().join(".config").join("empeve");
|
|
std::fs::create_dir_all(&config_dir).unwrap();
|
|
|
|
// Create minimal config
|
|
let config_path = config_dir.join("config.toml");
|
|
std::fs::write(&config_path, "[settings]\n").unwrap();
|
|
|
|
empeve()
|
|
.env("XDG_CONFIG_HOME", temp.path().join(".config"))
|
|
.args(["add", "invalid_repo_no_slash"])
|
|
.assert()
|
|
.failure()
|
|
.stderr(predicate::str::contains("invalid repository format"));
|
|
}
|
|
|
|
/// Test that the add command accepts valid GitHub shorthand
|
|
#[test]
|
|
fn test_cli_add_valid_repo_format() {
|
|
let temp = TempDir::new().unwrap();
|
|
let config_dir = temp.path().join(".config").join("empeve");
|
|
std::fs::create_dir_all(&config_dir).unwrap();
|
|
|
|
// Create minimal config
|
|
let config_path = config_dir.join("config.toml");
|
|
std::fs::write(&config_path, "[settings]\n").unwrap();
|
|
|
|
empeve()
|
|
.env("XDG_CONFIG_HOME", temp.path().join(".config"))
|
|
.args(["add", "user/repo"])
|
|
.assert()
|
|
.success()
|
|
.stdout(predicate::str::contains("Added"));
|
|
|
|
// Verify config was updated
|
|
let config_content = std::fs::read_to_string(&config_path).unwrap();
|
|
assert!(config_content.contains("user/repo"));
|
|
}
|
|
|
|
/// Test that adding a duplicate repo fails
|
|
#[test]
|
|
fn test_cli_add_duplicate_fails() {
|
|
let temp = TempDir::new().unwrap();
|
|
let config_dir = temp.path().join(".config").join("empeve");
|
|
std::fs::create_dir_all(&config_dir).unwrap();
|
|
|
|
// Create config with existing repo
|
|
let config_path = config_dir.join("config.toml");
|
|
std::fs::write(
|
|
&config_path,
|
|
r#"[settings]
|
|
|
|
[[repos]]
|
|
repo = "existing/repo"
|
|
"#,
|
|
)
|
|
.unwrap();
|
|
|
|
empeve()
|
|
.env("XDG_CONFIG_HOME", temp.path().join(".config"))
|
|
.args(["add", "existing/repo"])
|
|
.assert()
|
|
.failure()
|
|
.stderr(predicate::str::contains("already exists"));
|
|
}
|
|
|
|
/// Test that removing a non-existent repo fails
|
|
#[test]
|
|
fn test_cli_remove_nonexistent_fails() {
|
|
let temp = TempDir::new().unwrap();
|
|
let config_dir = temp.path().join(".config").join("empeve");
|
|
std::fs::create_dir_all(&config_dir).unwrap();
|
|
|
|
// Create empty config
|
|
let config_path = config_dir.join("config.toml");
|
|
std::fs::write(&config_path, "[settings]\n").unwrap();
|
|
|
|
empeve()
|
|
.env("XDG_CONFIG_HOME", temp.path().join(".config"))
|
|
.args(["remove", "nonexistent/repo"])
|
|
.assert()
|
|
.failure()
|
|
.stderr(predicate::str::contains("not found"));
|
|
}
|
|
|
|
/// Test status command with empty config
|
|
#[test]
|
|
fn test_cli_status_empty_config() {
|
|
let temp = TempDir::new().unwrap();
|
|
let config_dir = temp.path().join(".config").join("empeve");
|
|
std::fs::create_dir_all(&config_dir).unwrap();
|
|
|
|
// Create empty config
|
|
let config_path = config_dir.join("config.toml");
|
|
std::fs::write(&config_path, "[settings]\n").unwrap();
|
|
|
|
empeve()
|
|
.env("XDG_CONFIG_HOME", temp.path().join(".config"))
|
|
.arg("status")
|
|
.assert()
|
|
.success()
|
|
.stdout(predicate::str::contains("No repositories"));
|
|
}
|
|
|
|
/// Test that --target flag is recognized
|
|
#[test]
|
|
fn test_cli_target_flag() {
|
|
let temp = TempDir::new().unwrap();
|
|
let config_dir = temp.path().join(".config").join("empeve");
|
|
std::fs::create_dir_all(&config_dir).unwrap();
|
|
|
|
// Create config with target
|
|
let config_path = config_dir.join("config.toml");
|
|
let mpv_path = temp.path().join("mpv");
|
|
std::fs::create_dir_all(&mpv_path).unwrap();
|
|
|
|
std::fs::write(
|
|
&config_path,
|
|
format!(
|
|
r#"[settings]
|
|
|
|
[[targets]]
|
|
name = "mpv"
|
|
path = "{}"
|
|
"#,
|
|
mpv_path.display()
|
|
),
|
|
)
|
|
.unwrap();
|
|
|
|
empeve()
|
|
.env("XDG_CONFIG_HOME", temp.path().join(".config"))
|
|
.args(["--target", "mpv", "list"])
|
|
.assert()
|
|
.success();
|
|
}
|