use agpm_cli::utils::normalize_path_for_storage;
use anyhow::Result;
use std::path::Path;
use tokio::fs;
use crate::common::{ManifestBuilder, TestProject};
async fn path_to_file_url(path: &Path) -> String {
format!("file://{}", normalize_path_for_storage(path))
}
#[tokio::test]
async fn test_file_url_source_repo_not_modified() -> Result<()> {
let project = TestProject::new().await?;
let source_repo = project.create_source_repo("file-url-source").await?;
let git = &source_repo.git;
fs::create_dir_all(source_repo.path.join("agents")).await?;
fs::write(source_repo.path.join("agents/test.md"), "# Test Agent v1").await?;
git.add_all()?;
git.commit("Initial commit")?;
git.tag("v1.0.0")?;
fs::write(source_repo.path.join("agents/test.md"), "# Test Agent v2").await?;
git.add_all()?;
git.commit("Update to v2")?;
git.tag("v2.0.0")?;
git.checkout("v1.0.0")?;
let source_head_before = git.get_commit_hash()?;
let file_url = path_to_file_url(git.repo_path()).await;
let manifest = ManifestBuilder::new()
.add_source("local", &file_url)
.add_agent("test-agent", |d| d.source("local").path("agents/test.md").version("v2.0.0"))
.build();
project.write_manifest(&manifest).await?;
project.run_agpm(&["install"])?.assert_success();
let installed_path = project.project_path().join(".claude/agents/agpm/test.md");
let installed_content = fs::read_to_string(installed_path).await?;
assert!(installed_content.contains("v2"), "Installed file should be from v2.0.0");
let source_head_after = git.get_commit_hash()?;
assert_eq!(
source_head_before.trim(),
source_head_after.trim(),
"Source repository HEAD should not change"
);
let status = git.status_porcelain()?;
assert!(status.trim().is_empty(), "Source repository should have no modifications");
let source_content = fs::read_to_string(source_repo.path.join("agents/test.md")).await?;
assert!(source_content.contains("v1"), "Source repo working directory should remain on v1.0.0");
Ok(())
}
#[tokio::test]
async fn test_file_url_updates_work() -> Result<()> {
let project = TestProject::new().await?;
let source_repo = project.create_source_repo("file-url-updates").await?;
let git = &source_repo.git;
fs::create_dir_all(source_repo.path.join("agents")).await?;
fs::write(source_repo.path.join("agents/test.md"), "# Test Agent v1").await?;
git.add_all()?;
git.commit("Initial commit")?;
git.tag("v1.0.0")?;
let file_url = path_to_file_url(git.repo_path()).await;
let manifest_v1 = ManifestBuilder::new()
.add_source("local", &file_url)
.add_standard_agent("test-agent", "local", "agents/test.md")
.build();
project.write_manifest(&manifest_v1).await?;
project.run_agpm(&["install"])?.assert_success();
let installed_v1 =
fs::read_to_string(project.project_path().join(".claude/agents/agpm/test.md")).await?;
assert!(installed_v1.contains("v1"), "Should have v1 installed");
fs::write(source_repo.path.join("agents/test.md"), "# Test Agent v2").await?;
git.add_all()?;
git.commit("Update to v2")?;
git.tag("v2.0.0")?;
let manifest_v2 = ManifestBuilder::new()
.add_source("local", &file_url)
.add_agent("test-agent", |d| d.source("local").path("agents/test.md").version("v2.0.0"))
.build();
project.write_manifest(&manifest_v2).await?;
project.run_agpm(&["install"])?.assert_success();
let installed_v2 =
fs::read_to_string(project.project_path().join(".claude/agents/agpm/test.md")).await?;
assert!(installed_v2.contains("v2"), "Should have v2 installed after auto-update");
Ok(())
}
#[tokio::test]
async fn test_file_url_with_uncommitted_changes() -> Result<()> {
let project = TestProject::new().await?;
let source_repo = project.create_source_repo("file-url-uncommitted").await?;
let git = &source_repo.git;
fs::create_dir_all(source_repo.path.join("agents")).await?;
fs::write(source_repo.path.join("agents/test.md"), "# Test Agent v1").await?;
git.add_all()?;
git.commit("Initial commit")?;
git.tag("v1.0.0")?;
fs::write(source_repo.path.join("agents/test.md"), "# Test Agent - Work in Progress").await?;
fs::write(source_repo.path.join("new_file.txt"), "Uncommitted work").await?;
let status_before = git.status_porcelain()?;
assert!(!status_before.trim().is_empty(), "Source repo should have uncommitted changes");
let file_url = path_to_file_url(git.repo_path()).await;
let manifest = ManifestBuilder::new()
.add_source("local", &file_url)
.add_standard_agent("test-agent", "local", "agents/test.md")
.build();
project.write_manifest(&manifest).await?;
project.run_agpm(&["install"])?.assert_success();
let installed_content =
fs::read_to_string(project.project_path().join(".claude/agents/agpm/test.md")).await?;
assert!(installed_content.contains("v1"), "Install should use committed content");
assert!(
!installed_content.contains("Work in Progress"),
"Uncommitted changes must not be installed"
);
let status_after = git.status_porcelain()?;
assert_eq!(
status_before.trim(),
status_after.trim(),
"Uncommitted changes should remain after install"
);
let source_content = fs::read_to_string(source_repo.path.join("agents/test.md")).await?;
assert!(
source_content.contains("Work in Progress"),
"Source repo should still contain uncommitted changes"
);
Ok(())
}