use anyhow::Result;
use std::path::Path;
use tokio::fs;
mod common;
mod fixtures;
use common::TestProject;
async fn path_to_file_url(path: &Path) -> String {
let path_str = path.display().to_string().replace('\\', "/");
format!("file://{path_str}")
}
#[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 = format!(
r#"
[sources]
local = "{}"
[agents]
test-agent = {{ source = "local", path = "agents/test.md", version = "v2.0.0" }}
"#,
file_url
);
project.write_manifest(&manifest).await?;
project.run_agpm(&["install"])?.assert_success();
let installed_path = project.project_path().join(".claude/agents/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 = format!(
r#"
[sources]
local = "{}"
[agents]
test-agent = {{ source = "local", path = "agents/test.md", version = "v1.0.0" }}
"#,
file_url
);
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/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 = format!(
r#"
[sources]
local = "{}"
[agents]
test-agent = {{ source = "local", path = "agents/test.md", version = "v2.0.0" }}
"#,
file_url
);
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/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 = format!(
r#"
[sources]
local = "{}"
[agents]
test-agent = {{ source = "local", path = "agents/test.md", version = "v1.0.0" }}
"#,
file_url
);
project.write_manifest(&manifest).await?;
project.run_agpm(&["install"])?.assert_success();
let installed_content =
fs::read_to_string(project.project_path().join(".claude/agents/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(())
}