use tokio::fs;
use crate::common::{ManifestBuilder, TestProject};
use crate::fixtures::ManifestFixture;
#[tokio::test]
async fn test_outdated_all_up_to_date() {
let project = TestProject::new().await.unwrap();
let repo = project.create_source_repo("official").await.unwrap();
fs::create_dir_all(repo.path.join("agents")).await.unwrap();
fs::write(repo.path.join("agents/my-agent.md"), "# My Agent\n\nContent").await.unwrap();
repo.git.add_all().unwrap();
repo.git.commit("Initial commit").unwrap();
repo.git.tag("v2.0.0").unwrap();
let url = repo.bare_file_url(project.sources_path()).await.unwrap();
let manifest = ManifestBuilder::new()
.add_source("official", &url)
.add_agent("my-agent", |d| {
d.source("official").path("agents/my-agent.md").version("v2.0.0")
})
.build();
project.write_manifest(&manifest).await.unwrap();
let install_output = project.run_agpm(&["install"]).unwrap();
install_output.assert_success();
let output = project.run_agpm(&["outdated"]).unwrap();
output.assert_success().assert_stdout_contains("All dependencies are up to date!");
}
#[tokio::test]
async fn test_outdated_no_lockfile() {
let project = TestProject::new().await.unwrap();
let manifest_content = ManifestFixture::basic().content;
project.write_manifest(&manifest_content).await.unwrap();
let output = project.run_agpm(&["outdated"]).unwrap();
assert!(!output.success, "Expected command to fail without lockfile");
assert!(
output.stderr.contains("agpm.lock") || output.stderr.contains("Run 'agpm install' first"),
"Expected lockfile error message, got: {}",
output.stderr
);
}
#[tokio::test]
async fn test_outdated_without_project() {
let project = TestProject::new().await.unwrap();
let output = project.run_agpm(&["outdated"]).unwrap();
assert!(!output.success, "Expected command to fail without project");
assert!(
output.stderr.contains("agpm.toml not found"),
"Expected manifest not found error, got: {}",
output.stderr
);
}
#[tokio::test]
async fn test_outdated_json_format() {
let project = TestProject::new().await.unwrap();
let repo = project.create_source_repo("official").await.unwrap();
fs::create_dir_all(repo.path.join("agents")).await.unwrap();
fs::write(repo.path.join("agents/my-agent.md"), "# My Agent\n\nContent").await.unwrap();
repo.git.add_all().unwrap();
repo.git.commit("Initial commit").unwrap();
repo.git.tag("v1.0.0").unwrap();
let url = repo.bare_file_url(project.sources_path()).await.unwrap();
let manifest = ManifestBuilder::new()
.add_source("official", &url)
.add_agent("my-agent", |d| {
d.source("official").path("agents/my-agent.md").version("v1.0.0")
})
.build();
project.write_manifest(&manifest).await.unwrap();
let install_output = project.run_agpm(&["install"]).unwrap();
install_output.assert_success();
let output = project.run_agpm(&["outdated", "--format", "json"]).unwrap();
output.assert_success();
assert!(
output.stdout.contains("{") && output.stdout.contains("}"),
"Expected JSON output, got: {}",
output.stdout
);
assert!(
output.stdout.contains("\"outdated\"") && output.stdout.contains("\"summary\""),
"Expected JSON structure with outdated and summary fields, got: {}",
output.stdout
);
}
#[tokio::test]
async fn test_outdated_check_flag() {
let project = TestProject::new().await.unwrap();
let repo = project.create_source_repo("official").await.unwrap();
fs::create_dir_all(repo.path.join("agents")).await.unwrap();
fs::write(repo.path.join("agents/my-agent.md"), "# My Agent\n\nContent").await.unwrap();
repo.git.add_all().unwrap();
repo.git.commit("Initial commit").unwrap();
repo.git.tag("v2.0.0").unwrap();
let url = repo.bare_file_url(project.sources_path()).await.unwrap();
let manifest = ManifestBuilder::new()
.add_source("official", &url)
.add_agent("my-agent", |d| {
d.source("official").path("agents/my-agent.md").version("v2.0.0")
})
.build();
project.write_manifest(&manifest).await.unwrap();
let install_output = project.run_agpm(&["install"]).unwrap();
install_output.assert_success();
let output = project.run_agpm(&["outdated", "--check"]).unwrap();
output.assert_success();
}
#[tokio::test]
async fn test_outdated_specific_dependencies() {
let project = TestProject::new().await.unwrap();
let repo = project.create_source_repo("official").await.unwrap();
fs::create_dir_all(repo.path.join("agents")).await.unwrap();
fs::write(repo.path.join("agents/my-agent.md"), "# My Agent\n\nContent").await.unwrap();
fs::write(repo.path.join("agents/helper.md"), "# Helper Agent\n\nContent").await.unwrap();
repo.git.add_all().unwrap();
repo.git.commit("Initial commit").unwrap();
repo.git.tag("v1.0.0").unwrap();
let url = repo.bare_file_url(project.sources_path()).await.unwrap();
let manifest = ManifestBuilder::new()
.add_source("official", &url)
.add_agent("my-agent", |d| {
d.source("official").path("agents/my-agent.md").version("^1.0.0")
})
.add_agent("helper", |d| d.source("official").path("agents/helper.md").version("^1.0.0"))
.build();
project.write_manifest(&manifest).await.unwrap();
let install_output = project.run_agpm(&["install"]).unwrap();
install_output.assert_success();
let output = project.run_agpm(&["outdated", "my-agent"]).unwrap();
output.assert_success();
}
#[tokio::test]
async fn test_outdated_with_prefixed_version_constraints() {
let project = TestProject::new().await.unwrap();
let repo = project.create_source_repo("monorepo").await.unwrap();
fs::create_dir_all(repo.path.join("agents")).await.unwrap();
fs::write(repo.path.join("agents/my-agent.md"), "# Agent v1.0.0\n\nContent v1.0.0")
.await
.unwrap();
repo.git.add_all().unwrap();
repo.git.commit("Release agents-v1.0.0").unwrap();
repo.git.tag("agents-v1.0.0").unwrap();
let url = repo.bare_file_url(project.sources_path()).await.unwrap();
let manifest = ManifestBuilder::new()
.add_source("monorepo", &url)
.add_agent("ai-helper", |d| {
d.source("monorepo").path("agents/my-agent.md").version("agents-^v1.0.0")
})
.build();
project.write_manifest(&manifest).await.unwrap();
let install_output = project.run_agpm(&["install"]).unwrap();
install_output.assert_success();
let lockfile_path = project.project_path().join("agpm.lock");
assert!(lockfile_path.exists(), "Lockfile should exist after install");
let lockfile_content = fs::read_to_string(&lockfile_path).await.unwrap();
assert!(lockfile_content.contains("agents-v1.0.0"), "Lockfile should contain agents-v1.0.0");
fs::write(repo.path.join("agents/my-agent.md"), "# Agent v1.5.0\n\nContent v1.5.0")
.await
.unwrap();
repo.git.add_all().unwrap();
repo.git.commit("Release agents-v1.5.0").unwrap();
repo.git.tag("agents-v1.5.0").unwrap();
let output = project.run_agpm(&["outdated"]).unwrap();
output.assert_success();
let has_update_info =
output.stdout.contains("ai-helper") && output.stdout.contains("agents-v1");
let is_up_to_date = output.stdout.contains("up to date");
assert!(
has_update_info || is_up_to_date,
"Expected outdated to either show version info or 'up to date'.\nGot stdout:\n{}",
output.stdout
);
}
#[tokio::test]
async fn test_outdated_with_multiple_version_prefixes() {
let project = TestProject::new().await.unwrap();
let repo = project.create_source_repo("monorepo").await.unwrap();
fs::create_dir_all(repo.path.join("agents")).await.unwrap();
fs::create_dir_all(repo.path.join("snippets")).await.unwrap();
fs::write(repo.path.join("agents/my-agent.md"), "# Agent v1.0.0").await.unwrap();
fs::write(repo.path.join("snippets/my-snippet.md"), "# Snippet v2.0.0").await.unwrap();
repo.git.add_all().unwrap();
repo.git.commit("Initial versions").unwrap();
repo.git.tag("agents-v1.0.0").unwrap();
repo.git.tag("snippets-v2.0.0").unwrap();
fs::write(repo.path.join("agents/my-agent.md"), "# Agent v1.1.0").await.unwrap();
fs::write(repo.path.join("snippets/my-snippet.md"), "# Snippet v2.1.0").await.unwrap();
repo.git.add_all().unwrap();
repo.git.commit("Update versions").unwrap();
repo.git.tag("agents-v1.1.0").unwrap();
repo.git.tag("snippets-v2.1.0").unwrap();
let url = repo.bare_file_url(project.sources_path()).await.unwrap();
let manifest = ManifestBuilder::new()
.add_source("monorepo", &url)
.add_agent("ai-agent", |d| {
d.source("monorepo").path("agents/my-agent.md").version("agents-^v1.0.0")
})
.add_snippet("utils", |d| {
d.source("monorepo").path("snippets/my-snippet.md").version("snippets-^v2.0.0")
})
.build();
project.write_manifest(&manifest).await.unwrap();
let install_output = project.run_agpm(&["install"]).unwrap();
install_output.assert_success();
let lockfile_path = project.project_path().join("agpm.lock");
assert!(lockfile_path.exists(), "Lockfile should exist after install");
let lockfile_content = fs::read_to_string(&lockfile_path).await.unwrap();
assert!(
lockfile_content.contains("agents-v1.0.0") || lockfile_content.contains("agents-v1.1.0"),
"Lockfile should contain agents version"
);
assert!(
lockfile_content.contains("snippets-v2.0.0")
|| lockfile_content.contains("snippets-v2.1.0"),
"Lockfile should contain snippets version"
);
let output = project.run_agpm(&["outdated"]).unwrap();
output.assert_success();
if output.stdout.contains("ai-agent") {
assert!(
output.stdout.contains("agents-v1.1.0") || output.stdout.contains("agents-v1.0.0"),
"Expected agents namespace to show agents version\nGot:\n{}",
output.stdout
);
let lines_with_agent: Vec<&str> =
output.stdout.lines().filter(|line| line.contains("ai-agent")).collect();
for line in &lines_with_agent {
assert!(
!line.contains("snippets-v"),
"agents-^v1.0.0 should not match snippets-v* tags\nGot line:\n{}",
line
);
}
}
if output.stdout.contains("utils") {
assert!(
output.stdout.contains("snippets-v2.1.0") || output.stdout.contains("snippets-v2.0.0"),
"Expected snippets namespace to show snippets version\nGot:\n{}",
output.stdout
);
let lines_with_snippet: Vec<&str> =
output.stdout.lines().filter(|line| line.contains("utils")).collect();
for line in &lines_with_snippet {
assert!(
!line.contains("agents-v"),
"snippets-^v2.0.0 should not match agents-v* tags\nGot line:\n{}",
line
);
}
}
}