use anyhow::Result;
use crate::common::TestProject;
#[tokio::test]
async fn test_direct_manifest_overrides_transitive() -> Result<()> {
let test_project = TestProject::new().await?;
let helper_agent = r#"---
title: Helper Agent
---
# Helper Agent
This is a shared helper agent.
"#;
let parent_agent = r#"---
title: Parent Agent
dependencies:
agents:
- path: agents/helper.md
---
# Parent Agent
Uses the helper agent transitively.
"#;
let test_repo = test_project.create_source_repo("test").await?;
test_repo.add_resource("agents", "helper", helper_agent).await?;
test_repo.add_resource("agents", "parent", parent_agent).await?;
test_repo.commit_all("Add agents")?;
test_repo.tag_version("v1.0.0")?;
let test_url = test_repo.bare_file_url(test_project.sources_path()).await?;
let manifest = format!(
r#"[sources]
test = "{}"
[agents]
# Parent brings in helper transitively
parent = {{ source = "test", path = "agents/parent.md", version = "v1.0.0" }}
# Direct dependency with custom filename should override transitive
helper-custom = {{ source = "test", path = "agents/helper.md", version = "v1.0.0", filename = "helper-custom.md" }}
"#,
test_url
);
test_project.write_manifest(&manifest).await?;
let output = test_project.run_agpm(&["install"])?;
assert!(output.success, "Install should succeed. Stderr: {}", output.stderr);
let custom_path = test_project.project_path().join(".claude/agents/agpm/helper-custom.md");
assert!(
tokio::fs::metadata(&custom_path).await.is_ok(),
"Custom filename version should exist at {:?}",
custom_path
);
let default_path = test_project.project_path().join(".claude/agents/agpm/helper.md");
assert!(
tokio::fs::metadata(&default_path).await.is_err(),
"Should not have duplicate with default name at {:?}",
default_path
);
let lockfile_content = test_project.read_lockfile().await?;
let path_occurrences = lockfile_content.matches("agents/helper.md").count();
assert_eq!(path_occurrences, 1, "Path should only appear once in lockfile");
assert!(lockfile_content.contains("helper-custom.md"), "Lockfile should use custom filename");
Ok(())
}
#[tokio::test]
async fn test_multiple_variants_of_same_resource() -> Result<()> {
let test_project = TestProject::new().await?;
let template_agent = r#"---
title: Language Agent
agpm:
templating: true
---
# {{ project.language | capitalize }} Agent
Specialized for {{ project.language }}.
"#;
let test_repo = test_project.create_source_repo("test").await?;
test_repo.add_resource("agents", "language", template_agent).await?;
test_repo.commit_all("Add template agent")?;
test_repo.tag_version("v1.0.0")?;
let test_url = test_repo.bare_file_url(test_project.sources_path()).await?;
let manifest = format!(
r#"[sources]
test = "{}"
[agents]
lang-python = {{ source = "test", path = "agents/language.md", version = "v1.0.0", filename = "lang-python.md", template_vars = {{ project = {{ language = "python" }} }} }}
lang-rust = {{ source = "test", path = "agents/language.md", version = "v1.0.0", filename = "lang-rust.md", template_vars = {{ project = {{ language = "rust" }} }} }}
lang-typescript = {{ source = "test", path = "agents/language.md", version = "v1.0.0", filename = "lang-typescript.md", template_vars = {{ project = {{ language = "typescript" }} }} }}
"#,
test_url
);
test_project.write_manifest(&manifest).await?;
let output = test_project.run_agpm(&["install"])?;
assert!(output.success, "Install should succeed. Stderr: {}", output.stderr);
let python_path = test_project.project_path().join(".claude/agents/agpm/lang-python.md");
let rust_path = test_project.project_path().join(".claude/agents/agpm/lang-rust.md");
let typescript_path =
test_project.project_path().join(".claude/agents/agpm/lang-typescript.md");
assert!(
tokio::fs::metadata(&python_path).await.is_ok(),
"Python variant should exist at {:?}",
python_path
);
assert!(
tokio::fs::metadata(&rust_path).await.is_ok(),
"Rust variant should exist at {:?}",
rust_path
);
assert!(
tokio::fs::metadata(&typescript_path).await.is_ok(),
"TypeScript variant should exist at {:?}",
typescript_path
);
let python_content = tokio::fs::read_to_string(&python_path).await?;
let rust_content = tokio::fs::read_to_string(&rust_path).await?;
let typescript_content = tokio::fs::read_to_string(&typescript_path).await?;
assert!(python_content.contains("# Python Agent"), "Python agent should be rendered");
assert!(rust_content.contains("# Rust Agent"), "Rust agent should be rendered");
assert!(
typescript_content.contains("# Typescript Agent"),
"TypeScript agent should be rendered"
);
let lockfile_content = test_project.read_lockfile().await?;
assert!(
lockfile_content.contains(r#"manifest_alias = "lang-python""#),
"Lockfile should contain lang-python variant"
);
assert!(
lockfile_content.contains(r#"manifest_alias = "lang-rust""#),
"Lockfile should contain lang-rust variant"
);
assert!(
lockfile_content.contains(r#"manifest_alias = "lang-typescript""#),
"Lockfile should contain lang-typescript variant"
);
Ok(())
}