use crate::common::{ManifestBuilder, TestProject};
use agpm_cli::utils::platform::normalize_path_for_storage;
use anyhow::Result;
#[tokio::test]
async fn test_local_dependencies_same_path_different_checksums_conflict() -> Result<()> {
let project = TestProject::new().await?;
let repo1 = project.create_source_repo("local1").await?;
let repo2 = project.create_source_repo("local2").await?;
repo1
.add_resource(
"agents",
"shared-resource",
r#"---
name: Shared Resource
---
# Shared Resource from repo1
This is version 1 of the shared resource.
"#,
)
.await?;
repo1.commit_all("Add shared resource v1")?;
repo2
.add_resource(
"agents",
"shared-resource",
r#"---
name: Shared Resource
---
# Shared Resource from repo2
This is version 2 of the shared resource with different content.
"#,
)
.await?;
repo2.commit_all("Add shared resource v2")?;
let repo1_path = format!("file://{}", normalize_path_for_storage(&repo1.path));
let repo2_path = format!("file://{}", normalize_path_for_storage(&repo2.path));
let manifest = ManifestBuilder::new()
.add_source("local1", &repo1_path)
.add_source("local2", &repo2_path)
.add_agent("resource1", |d| d.source("local1").path("agents/shared-resource.md"))
.add_agent("resource2", |d| d.source("local2").path("agents/shared-resource.md"))
.build();
project.write_manifest(&manifest).await?;
let output = project.run_agpm(&["install"])?;
assert!(
!output.success,
"Install should fail due to checksum conflict. stdout: {}\nstderr: {}",
output.stdout, output.stderr
);
let error_output = format!("{} {}", output.stdout, output.stderr);
assert!(
error_output.contains("conflict") || error_output.contains("Conflict"),
"Error should mention conflict. Got: {}",
error_output
);
Ok(())
}
#[tokio::test]
async fn test_local_dependencies_same_path_same_checksums_no_conflict() -> Result<()> {
let project = TestProject::new().await?;
let repo1 = project.create_source_repo("local").await?;
let resource_content = r#"---
name: Shared Resource
---
# Shared Resource
This is identical content.
"#;
repo1.add_resource("agents", "shared-resource", resource_content).await?;
repo1.commit_all("Add shared resource")?;
let repo_path = format!("file://{}", normalize_path_for_storage(&repo1.path));
let manifest = ManifestBuilder::new()
.add_source("local", &repo_path)
.add_agent("resource", |d| d.source("local").path("agents/shared-resource.md"))
.build();
project.write_manifest(&manifest).await?;
let output = project.run_agpm(&["install"])?;
assert!(
output.success,
"Install should succeed with no conflicts. stderr: {}\nstdout: {}",
output.stderr, output.stdout
);
let lockfile = project.load_lockfile()?;
assert_eq!(lockfile.agents.len(), 1, "Should have exactly 1 agent");
let agent = lockfile
.agents
.first()
.ok_or_else(|| anyhow::anyhow!("Expected at least one agent in lockfile"))?;
assert_eq!(agent.name, "agents/shared-resource");
assert_eq!(agent.path, "agents/shared-resource.md");
assert!(agent.checksum.starts_with("sha256:"));
Ok(())
}
#[tokio::test]
async fn test_mixed_git_local_dependencies_same_path_conflict() -> Result<()> {
let project = TestProject::new().await?;
let git_repo = project.create_source_repo("community").await?;
git_repo
.add_resource(
"agents",
"shared-resource",
r#"---
name: Shared Resource
---
# Shared Resource from Git repo
This is the Git version of the shared resource.
"#,
)
.await?;
git_repo.commit_all("Add shared resource")?;
git_repo.tag_version("v1.0.0")?;
let local_repo = project.create_source_repo("local").await?;
local_repo
.add_resource(
"agents",
"shared-resource",
r#"---
name: Shared Resource
---
# Shared Resource from local repo
This is the local version of the shared resource with different content.
"#,
)
.await?;
local_repo.commit_all("Add shared resource")?;
let git_repo_url = git_repo.bare_file_url(project.sources_path()).await?;
let local_repo_path = format!("file://{}", normalize_path_for_storage(&local_repo.path));
let manifest = ManifestBuilder::new()
.add_source("community", &git_repo_url)
.add_source("local", &local_repo_path)
.add_agent("git-resource", |d| {
d.source("community").path("agents/shared-resource.md").version("v1.0.0")
})
.add_agent("local-resource", |d| d.source("local").path("agents/shared-resource.md"))
.build();
project.write_manifest(&manifest).await?;
let output = project.run_agpm(&["install"])?;
assert!(
!output.success,
"Install should fail due to Git/local conflict. stdout: {}\nstderr: {}",
output.stdout, output.stderr
);
let error_output = format!("{} {}", output.stdout, output.stderr);
assert!(
error_output.contains("conflict") || error_output.contains("Conflict"),
"Error should mention conflict. Got: {}",
error_output
);
Ok(())
}
#[tokio::test]
async fn test_transitive_local_dependencies_checksum_conflict() -> Result<()> {
let project = TestProject::new().await?;
let repo1 = project.create_source_repo("local1").await?;
let repo2 = project.create_source_repo("local2").await?;
repo1
.add_resource(
"agents",
"shared-resource",
r#"---
name: Shared Resource
---
# Shared Resource from repo1
This is version 1 from repository 1.
dependencies:
agents:
- path: ./transitive-resource.md
"#,
)
.await?;
repo1
.add_resource(
"agents",
"transitive-resource",
r#"---
name: Transitive Resource
---
# Transitive Resource from repo1
This is a transitive dependency.
"#,
)
.await?;
repo1.commit_all("Add resources")?;
repo2
.add_resource(
"agents",
"shared-resource",
r#"---
name: Shared Resource
---
# Shared Resource from repo2
This is version 2 from repository 2.
dependencies:
agents:
- path: ./transitive-resource.md
"#,
)
.await?;
repo2
.add_resource(
"agents",
"transitive-resource",
r#"---
name: Transitive Resource
---
# Transitive Resource from repo2
This is a different transitive dependency.
"#,
)
.await?;
repo2.commit_all("Add resources")?;
let repo1_path = format!("file://{}", normalize_path_for_storage(&repo1.path));
let repo2_path = format!("file://{}", normalize_path_for_storage(&repo2.path));
let manifest = ManifestBuilder::new()
.add_source("local1", &repo1_path)
.add_source("local2", &repo2_path)
.add_agent("dep1", |d| d.source("local1").path("agents/shared-resource.md"))
.add_agent("dep2", |d| d.source("local2").path("agents/shared-resource.md"))
.build();
project.write_manifest(&manifest).await?;
let output = project.run_agpm(&["install"])?;
assert!(
!output.success,
"Install should fail due to transitive checksum conflict. stdout: {}\nstderr: {}",
output.stdout, output.stderr
);
let error_output = format!("{} {}", output.stdout, output.stderr);
assert!(
error_output.contains("conflict") || error_output.contains("Conflict"),
"Error should mention conflict. Got: {}",
error_output
);
Ok(())
}