use anyhow::Result;
use std::fs;
mod common;
mod fixtures;
use common::TestProject;
#[test]
fn test_pattern_based_installation() -> Result<()> {
ccpm::test_utils::init_test_logging(None);
let project = TestProject::new()?;
let test_repo = project.create_source_repo("test-repo")?;
test_repo.add_resource(
"agents/ai",
"assistant",
"# AI Assistant\n\nAI assistant agent",
)?;
test_repo.add_resource(
"agents/ai",
"analyzer",
"# AI Analyzer\n\nAI analyzer agent",
)?;
test_repo.add_resource(
"agents/ai",
"generator",
"# AI Generator\n\nAI generator agent",
)?;
test_repo.add_resource("agents", "reviewer", "# Reviewer\n\nCode reviewer agent")?;
test_repo.add_resource(
"agents",
"review-helper",
"# Review Helper\n\nReview helper agent",
)?;
test_repo.add_resource("agents", "debugger", "# Debugger\n\nDebugger agent")?;
test_repo.add_resource("agents", "tester", "# Tester\n\nTester agent")?;
test_repo.commit_all("Add multiple agent files")?;
test_repo.tag_version("v1.0.0")?;
let repo_url = test_repo.bare_file_url(project.sources_path())?;
let manifest_content = format!(
r#"
[sources]
test-repo = "{}"
[agents]
# Install all AI agents
ai-agents = {{ source = "test-repo", path = "agents/ai/*.md", version = "v1.0.0" }}
# Install all review-related agents
review-agents = {{ source = "test-repo", path = "agents/review*.md", version = "v1.0.0" }}
# Install all agents recursively
all-agents = {{ source = "test-repo", path = "agents/**/*.md", version = "v1.0.0" }}
"#,
repo_url
);
project.write_manifest(&manifest_content)?;
let output = project.run_ccpm(&["install"])?;
assert!(output.success);
let ai_agents_dir = project.project_path().join(".claude/agents");
assert!(
ai_agents_dir.join("assistant.md").exists(),
"AI assistant not installed"
);
assert!(
ai_agents_dir.join("analyzer.md").exists(),
"AI analyzer not installed"
);
assert!(
ai_agents_dir.join("generator.md").exists(),
"AI generator not installed"
);
assert!(
ai_agents_dir.join("reviewer.md").exists(),
"Reviewer not installed"
);
assert!(
ai_agents_dir.join("review-helper.md").exists(),
"Review helper not installed"
);
let lockfile_path = project.project_path().join("ccpm.lock");
assert!(lockfile_path.exists(), "Lockfile not created");
let lockfile_content = fs::read_to_string(&lockfile_path)?;
assert!(
lockfile_content.contains("assistant"),
"Assistant not in lockfile"
);
assert!(
lockfile_content.contains("analyzer"),
"Analyzer not in lockfile"
);
assert!(
lockfile_content.contains("generator"),
"Generator not in lockfile"
);
assert!(
lockfile_content.contains("reviewer"),
"Reviewer not in lockfile"
);
assert!(
lockfile_content.contains("review-helper"),
"Review helper not in lockfile"
);
Ok(())
}
#[test]
fn test_pattern_with_custom_target() -> Result<()> {
ccpm::test_utils::init_test_logging(None);
let project = TestProject::new()?;
let test_repo = project.create_source_repo("test-repo")?;
test_repo.add_resource("snippets", "util1", "# Utility 1")?;
test_repo.add_resource("snippets", "util2", "# Utility 2")?;
test_repo.add_resource("snippets", "helper", "# Helper")?;
test_repo.commit_all("Add snippets")?;
test_repo.tag_version("v1.0.0")?;
let repo_url = test_repo.bare_file_url(project.sources_path())?;
let manifest_content = format!(
r#"
[sources]
test-repo = "{}"
[snippets]
utilities = {{ source = "test-repo", path = "snippets/util*.md", version = "v1.0.0", target = "tools/utilities" }}
"#,
repo_url
);
project.write_manifest(&manifest_content)?;
let output = project.run_ccpm(&["install"])?;
assert!(output.success);
let custom_dir = project.project_path().join(".claude/tools/utilities");
assert!(
custom_dir.join("util1.md").exists(),
"util1 not installed to custom path"
);
assert!(
custom_dir.join("util2.md").exists(),
"util2 not installed to custom path"
);
assert!(
!custom_dir.join("helper.md").exists(),
"helper should not be installed"
);
Ok(())
}
#[test]
fn test_pattern_with_versions() -> Result<()> {
ccpm::test_utils::init_test_logging(None);
let project = TestProject::new()?;
let test_repo = project.create_source_repo("test-repo")?;
test_repo.add_resource("agents", "agent1", "# Agent 1 v1.0.0")?;
test_repo.add_resource("agents", "agent2", "# Agent 2 v1.0.0")?;
test_repo.commit_all("Add agents v1.0.0")?;
test_repo.tag_version("v1.0.0")?;
let repo_url = test_repo.bare_file_url(project.sources_path())?;
let manifest_content = format!(
r#"
[sources]
test-repo = "{}"
[agents]
v1-agents = {{ source = "test-repo", path = "agents/*.md", version = "v1.0.0" }}
"#,
repo_url
);
project.write_manifest(&manifest_content)?;
let output = project.run_ccpm(&["install"])?;
assert!(output.success);
let agent1_path = project.project_path().join(".claude/agents/agent1.md");
let agent2_path = project.project_path().join(".claude/agents/agent2.md");
let agent3_path = project.project_path().join(".claude/agents/agent3.md");
assert!(agent1_path.exists(), "Agent 1 not installed");
assert!(agent2_path.exists(), "Agent 2 not installed");
assert!(!agent3_path.exists(), "Agent 3 should not exist in v1.0.0");
let agent1_content = fs::read_to_string(&agent1_path)?;
assert!(
agent1_content.contains("v1.0.0"),
"Agent 1 should be v1.0.0"
);
assert!(
!agent1_content.contains("Updated"),
"Agent 1 should not be updated version"
);
Ok(())
}
#[test]
fn test_local_pattern_dependencies() -> Result<()> {
ccpm::test_utils::init_test_logging(None);
let project = TestProject::new()?;
let resources_dir = project.sources_path().join("local_resources");
let agents_dir = resources_dir.join("agents");
fs::create_dir_all(&agents_dir)?;
fs::write(agents_dir.join("local1.md"), "# Local Agent 1")?;
fs::write(agents_dir.join("local2.md"), "# Local Agent 2")?;
fs::write(agents_dir.join("local3.md"), "# Local Agent 3")?;
let manifest_content = format!(
r#"
[agents]
local-agents = {{ path = "{}/agents/local*.md" }}
"#,
resources_dir.display()
);
project.write_manifest(&manifest_content)?;
let output = project.run_ccpm(&["install"])?;
if output.success {
let agents_installed = project.project_path().join(".claude/agents");
println!(
"Checking for installed local agents in: {:?}",
agents_installed
);
assert!(
agents_installed.join("local1.md").exists()
|| agents_installed.join("local2.md").exists()
|| agents_installed.join("local3.md").exists(),
"At least one local agent should be installed"
);
} else {
println!("Local pattern installation not yet supported");
}
Ok(())
}
#[test]
fn test_invalid_pattern_error() -> Result<()> {
ccpm::test_utils::init_test_logging(None);
let project = TestProject::new()?;
let manifest_content = r#"
[sources]
test-repo = "https://github.com/example/repo.git"
[agents]
unsafe = { source = "test-repo", path = "../../../etc/*.conf", version = "latest" }
"#;
project.write_manifest(manifest_content)?;
let output = project.run_ccpm(&["validate"])?;
assert!(!output.success);
Ok(())
}
#[test]
#[ignore = "Performance test - run manually"]
fn test_pattern_performance() -> Result<()> {
ccpm::test_utils::init_test_logging(None);
let project = TestProject::new()?;
let test_repo = project.create_source_repo("test-repo")?;
for i in 0..100 {
let content = format!("# Agent {}\n\nAgent {} description", i, i);
test_repo.add_resource("agents", &format!("agent{:03}", i), &content)?;
}
test_repo.commit_all("Add 100 agents")?;
test_repo.tag_version("v1.0.0")?;
let repo_url = test_repo.bare_file_url(project.sources_path())?;
let manifest_content = format!(
r#"
[sources]
test-repo = "{}"
[agents]
all-agents = {{ source = "test-repo", path = "agents/*.md", version = "v1.0.0" }}
"#,
repo_url
);
project.write_manifest(&manifest_content)?;
let start = std::time::Instant::now();
let output = project.run_ccpm(&["install"])?;
assert!(output.success);
let duration = start.elapsed();
assert!(
duration.as_secs() < 30,
"Installation took too long: {:?}",
duration
);
let lockfile_content = fs::read_to_string(project.project_path().join("ccpm.lock"))?;
let agent_count = lockfile_content.matches("agent").count();
assert!(agent_count >= 100, "Not all agents were installed");
Ok(())
}