use predicates::prelude::*;
use std::fs;
mod common;
mod fixtures;
use common::TestProject;
use fixtures::ManifestFixture;
#[test]
fn test_validate_valid_manifest() {
let project = TestProject::new().unwrap();
let official_repo = project.create_source_repo("official").unwrap();
official_repo
.add_resource("agents", "my-agent", "# My Agent\n\nA test agent")
.unwrap();
official_repo
.add_resource("snippets", "utils", "# Utils\n\nA test snippet")
.unwrap();
official_repo.commit_all("Initial commit").unwrap();
official_repo.tag_version("v1.0.0").unwrap();
let community_repo = project.create_source_repo("community").unwrap();
community_repo
.add_resource("agents", "helper", "# Helper Agent\n\nA test agent")
.unwrap();
community_repo.commit_all("Initial commit").unwrap();
community_repo.tag_version("v1.0.0").unwrap();
let official_url = official_repo.bare_file_url(project.sources_path()).unwrap();
let community_url = community_repo
.bare_file_url(project.sources_path())
.unwrap();
let manifest_content = format!(
r#"
[sources]
official = "{official_url}"
community = "{community_url}"
[agents]
my-agent = {{ source = "official", path = "agents/my-agent.md", version = "v1.0.0" }}
helper = {{ source = "community", path = "agents/helper.md", version = "v1.0.0" }}
[snippets]
utils = {{ source = "official", path = "snippets/utils.md", version = "v1.0.0" }}
"#
);
project.write_manifest(&manifest_content).unwrap();
let output = project.run_ccpm(&["validate"]).unwrap();
assert!(output.success);
assert!(output.stdout.contains("✓"));
assert!(output.stdout.contains("Valid"));
}
#[test]
fn test_validate_no_manifest() {
let project = TestProject::new().unwrap();
let output = project.run_ccpm(&["validate"]).unwrap();
assert!(!output.success);
assert!(output.stdout.contains("✗"));
assert!(output.stdout.contains("No ccpm.toml found"));
}
#[test]
fn test_validate_invalid_syntax() {
let project = TestProject::new().unwrap();
let manifest = ManifestFixture::invalid_syntax();
project.write_manifest(&manifest.content).unwrap();
let output = project.run_ccpm(&["validate"]).unwrap();
assert!(!output.success);
assert!(output.stdout.contains("✗"));
assert!(output.stdout.contains("Syntax error"));
assert!(output.stdout.contains("TOML parsing failed"));
}
#[test]
fn test_validate_missing_fields() {
let project = TestProject::new().unwrap();
let manifest = ManifestFixture::missing_fields();
project.write_manifest(&manifest.content).unwrap();
let output = project.run_ccpm(&["validate"]).unwrap();
assert!(!output.success);
assert!(output.stdout.contains("✗"));
assert!(output.stdout.contains("Missing required field"));
assert!(output.stdout.contains("path"));
}
#[test]
fn test_validate_sources() {
let project = TestProject::new().unwrap();
let official_repo = project.create_source_repo("official").unwrap();
official_repo
.add_resource("agents", "my-agent", "# My Agent\n\nA test agent")
.unwrap();
official_repo
.add_resource("snippets", "utils", "# Utils\n\nA test snippet")
.unwrap();
official_repo.commit_all("Initial commit").unwrap();
official_repo.tag_version("v1.0.0").unwrap();
let community_repo = project.create_source_repo("community").unwrap();
community_repo
.add_resource("agents", "helper", "# Helper Agent\n\nA test agent")
.unwrap();
community_repo.commit_all("Initial commit").unwrap();
community_repo.tag_version("v1.0.0").unwrap();
let official_url = official_repo.bare_file_url(project.sources_path()).unwrap();
let community_url = community_repo
.bare_file_url(project.sources_path())
.unwrap();
let manifest_content = format!(
r#"
[sources]
official = "{official_url}"
community = "{community_url}"
[agents]
my-agent = {{ source = "official", path = "agents/my-agent.md", version = "v1.0.0" }}
helper = {{ source = "community", path = "agents/helper.md", version = "v1.0.0" }}
[snippets]
utils = {{ source = "official", path = "snippets/utils.md", version = "v1.0.0" }}
"#
);
project.write_manifest(&manifest_content).unwrap();
let output = project.run_ccpm(&["validate", "--sources"]).unwrap();
assert!(output.success);
assert!(output.stdout.contains("✓"));
assert!(output.stdout.contains("Sources accessible"));
}
#[test]
fn test_validate_inaccessible_sources() {
let project = TestProject::new().unwrap();
let manifest_content = r#"
[sources]
official = "file:///non/existent/path"
community = "file:///another/non/existent/path"
[agents]
my-agent = { source = "official", path = "agents/my-agent.md", version = "v1.0.0" }
helper = { source = "community", path = "agents/helper.md", version = "v1.0.0" }
[snippets]
utils = { source = "official", path = "snippets/utils.md", version = "v1.0.0" }
"#;
project.write_manifest(manifest_content).unwrap();
let output = project.run_ccpm(&["validate", "--sources"]).unwrap();
assert!(!output.success);
assert!(output.stdout.contains("✗"));
assert!(output.stdout.contains("Source not accessible"));
}
#[test]
fn test_validate_dependencies() {
let project = TestProject::new().unwrap();
let official_repo = project.create_source_repo("official").unwrap();
official_repo
.add_resource("agents", "my-agent", "# My Agent\n\nA test agent")
.unwrap();
official_repo
.add_resource("snippets", "utils", "# Utils\n\nA test snippet")
.unwrap();
official_repo.commit_all("Initial commit").unwrap();
official_repo.tag_version("v1.0.0").unwrap();
let community_repo = project.create_source_repo("community").unwrap();
community_repo
.add_resource("agents", "helper", "# Helper Agent\n\nA test agent")
.unwrap();
community_repo.commit_all("Initial commit").unwrap();
community_repo.tag_version("v1.0.0").unwrap();
let official_url = official_repo.bare_file_url(project.sources_path()).unwrap();
let community_url = community_repo
.bare_file_url(project.sources_path())
.unwrap();
let manifest_content = format!(
r#"
[sources]
official = "{official_url}"
community = "{community_url}"
[agents]
my-agent = {{ source = "official", path = "agents/my-agent.md", version = "v1.0.0" }}
helper = {{ source = "community", path = "agents/helper.md", version = "v1.0.0" }}
[snippets]
utils = {{ source = "official", path = "snippets/utils.md", version = "v1.0.0" }}
"#
);
project.write_manifest(&manifest_content).unwrap();
let output = project.run_ccpm(&["validate", "--resolve"]).unwrap();
assert!(output.success);
assert!(output.stdout.contains("✓"));
assert!(output.stdout.contains("Dependencies resolvable"));
}
#[test]
fn test_validate_missing_dependencies() {
let project = TestProject::new().unwrap();
let official_repo = project.create_source_repo("official").unwrap();
official_repo
.add_resource(
"agents",
"other-agent",
"# Other Agent\n\nA different agent",
)
.unwrap();
official_repo.commit_all("Initial commit").unwrap();
official_repo.tag_version("v1.0.0").unwrap();
let official_url = official_repo.bare_file_url(project.sources_path()).unwrap();
let manifest_content = format!(
r#"
[sources]
official = "{official_url}"
[agents]
my-agent = {{ source = "official", path = "agents/my-agent.md", version = "v1.0.0" }}
[snippets]
utils = {{ source = "official", path = "snippets/utils.md", version = "v1.0.0" }}
"#
);
project.write_manifest(&manifest_content).unwrap();
let output = project.run_ccpm(&["validate", "--resolve"]).unwrap();
assert!(output.success); assert!(output.stdout.contains("✓"));
assert!(output.stdout.contains("Dependencies resolvable"));
}
#[test]
fn test_validate_local_paths() {
let project = TestProject::new().unwrap();
let manifest = ManifestFixture::with_local();
project.write_manifest(&manifest.content).unwrap();
let project_parent = project.project_path().parent().unwrap();
let local_agents_dir = project_parent.join("local-agents");
fs::create_dir_all(&local_agents_dir).unwrap();
fs::write(
local_agents_dir.join("helper.md"),
"# Helper Agent\n\nThis is a test agent.",
)
.unwrap();
let snippets_dir = project.project_path().join("snippets");
fs::create_dir_all(&snippets_dir).unwrap();
fs::write(
snippets_dir.join("local-utils.md"),
"# Local Utils Snippet\n\nThis is a test snippet.",
)
.unwrap();
let output = project.run_ccpm(&["validate", "--paths"]).unwrap();
assert!(output.success);
assert!(output.stdout.contains("✓"));
assert!(output.stdout.contains("Local paths exist"));
}
#[test]
fn test_validate_missing_local_paths() {
let project = TestProject::new().unwrap();
let manifest = ManifestFixture::with_local();
project.write_manifest(&manifest.content).unwrap();
let output = project.run_ccpm(&["validate", "--paths"]).unwrap();
assert!(!output.success);
assert!(output.stdout.contains("✗"));
assert!(output.stdout.contains("Local path not found"));
assert!(output.stdout.contains("../local-agents/helper.md"));
assert!(output.stdout.contains("./snippets/local-utils.md"));
}
#[test]
fn test_validate_lockfile_consistent() {
let project = TestProject::new().unwrap();
let official_repo = project.create_source_repo("official").unwrap();
official_repo
.add_resource("agents", "my-agent", "# My Agent\n\nA test agent")
.unwrap();
official_repo
.add_resource("snippets", "utils", "# Utils\n\nA test snippet")
.unwrap();
official_repo.commit_all("Initial commit").unwrap();
official_repo.tag_version("v1.0.0").unwrap();
let community_repo = project.create_source_repo("community").unwrap();
community_repo
.add_resource("agents", "helper", "# Helper Agent\n\nA test agent")
.unwrap();
community_repo.commit_all("Initial commit").unwrap();
community_repo.tag_version("v1.0.0").unwrap();
let official_url = official_repo.bare_file_url(project.sources_path()).unwrap();
let community_url = community_repo
.bare_file_url(project.sources_path())
.unwrap();
let manifest_content = format!(
r#"
[sources]
official = "{official_url}"
community = "{community_url}"
[agents]
my-agent = {{ source = "official", path = "agents/my-agent.md", version = "v1.0.0" }}
helper = {{ source = "community", path = "agents/helper.md", version = "v1.0.0" }}
[snippets]
utils = {{ source = "official", path = "snippets/utils.md", version = "v1.0.0" }}
"#
);
project.write_manifest(&manifest_content).unwrap();
let lockfile_content = format!(
r#"
# Auto-generated lockfile - DO NOT EDIT
version = 1
[[sources]]
name = "official"
url = "{official_url}"
commit = "abc123456789abcdef123456789abcdef12345678"
fetched_at = "2024-01-01T00:00:00Z"
[[sources]]
name = "community"
url = "{community_url}"
commit = "def456789abcdef123456789abcdef123456789ab"
fetched_at = "2024-01-01T00:00:00Z"
[[agents]]
name = "my-agent"
source = "official"
path = "agents/my-agent.md"
version = "v1.0.0"
resolved_commit = "abc123456789abcdef123456789abcdef12345678"
checksum = "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
installed_at = "agents/my-agent.md"
[[agents]]
name = "helper"
source = "community"
path = "agents/helper.md"
version = "v1.0.0"
resolved_commit = "def456789abcdef123456789abcdef123456789ab"
checksum = "sha256:38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da"
installed_at = "agents/helper.md"
[[snippets]]
name = "utils"
source = "official"
path = "snippets/utils.md"
version = "v1.0.0"
resolved_commit = "abc123456789abcdef123456789abcdef12345678"
checksum = "sha256:74e6f7298a9c2d168935f58c6b6c5b5ea4c3df6a0b6b8d2e7b2a2b8c3d4e5f6a"
installed_at = "snippets/utils.md"
"#
);
fs::write(
project.project_path().join("ccpm.lock"),
lockfile_content.trim(),
)
.unwrap();
let output = project.run_ccpm(&["validate", "--check-lock"]).unwrap();
assert!(output.success);
assert!(output.stdout.contains("✓"));
assert!(output.stdout.contains("Lockfile consistent"));
}
#[test]
fn test_validate_lockfile_inconsistent() {
let project = TestProject::new().unwrap();
let manifest_content = r#"
[sources]
official = "file:///fake/url"
[agents]
my-agent = { source = "official", path = "agents/my-agent.md", version = "v1.0.0" }
"#;
project.write_manifest(manifest_content).unwrap();
let inconsistent_lockfile = r#"
# Auto-generated lockfile - DO NOT EDIT
version = 1
[[sources]]
name = "different"
url = "https://github.com/different/repo.git"
commit = "abc123456789abcdef123456789abcdef12345678"
fetched_at = "2024-01-01T00:00:00Z"
[[agents]]
name = "different-agent"
source = "different"
path = "agents/different.md"
version = "v1.0.0"
resolved_commit = "abc123456789abcdef123456789abcdef12345678"
checksum = "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
installed_at = "agents/different.md"
"#;
fs::write(
project.project_path().join("ccpm.lock"),
inconsistent_lockfile,
)
.unwrap();
let output = project.run_ccpm(&["validate", "--check-lock"]).unwrap();
assert!(!output.success);
assert!(output.stdout.contains("✗"));
assert!(output.stdout.contains("Lockfile inconsistent"));
}
#[test]
fn test_validate_corrupted_lockfile() {
let project = TestProject::new().unwrap();
let manifest_content = r#"
[sources]
official = "file:///fake/url"
[agents]
my-agent = { source = "official", path = "agents/my-agent.md", version = "v1.0.0" }
"#;
project.write_manifest(manifest_content).unwrap();
fs::write(
project.project_path().join("ccpm.lock"),
"corrupted content",
)
.unwrap();
let output = project.run_ccpm(&["validate", "--check-lock"]).unwrap();
assert!(!output.success);
assert!(output.stdout.contains("✗"));
assert!(output.stdout.contains("Failed to parse lockfile"));
assert!(output.stdout.contains("corrupted") || output.stdout.contains("Invalid"));
}
#[test]
fn test_validate_all() {
let project = TestProject::new().unwrap();
let official_repo = project.create_source_repo("official").unwrap();
official_repo
.add_resource("agents", "my-agent", "# My Agent\n\nA test agent")
.unwrap();
official_repo
.add_resource("snippets", "utils", "# Utils\n\nA test snippet")
.unwrap();
official_repo.commit_all("Initial commit").unwrap();
official_repo.tag_version("v1.0.0").unwrap();
let community_repo = project.create_source_repo("community").unwrap();
community_repo
.add_resource("agents", "helper", "# Helper Agent\n\nA test agent")
.unwrap();
community_repo.commit_all("Initial commit").unwrap();
community_repo.tag_version("v1.0.0").unwrap();
let official_url = official_repo.bare_file_url(project.sources_path()).unwrap();
let community_url = community_repo
.bare_file_url(project.sources_path())
.unwrap();
let manifest_content = format!(
r#"
[sources]
official = "{official_url}"
community = "{community_url}"
[agents]
my-agent = {{ source = "official", path = "agents/my-agent.md", version = "v1.0.0" }}
helper = {{ source = "community", path = "agents/helper.md", version = "v1.0.0" }}
[snippets]
utils = {{ source = "official", path = "snippets/utils.md", version = "v1.0.0" }}
"#
);
project.write_manifest(&manifest_content).unwrap();
let lockfile_content = format!(
r#"
# Auto-generated lockfile - DO NOT EDIT
version = 1
[[sources]]
name = "official"
url = "{official_url}"
commit = "abc123456789abcdef123456789abcdef12345678"
fetched_at = "2024-01-01T00:00:00Z"
[[sources]]
name = "community"
url = "{community_url}"
commit = "def456789abcdef123456789abcdef123456789ab"
fetched_at = "2024-01-01T00:00:00Z"
[[agents]]
name = "my-agent"
source = "official"
path = "agents/my-agent.md"
version = "v1.0.0"
resolved_commit = "abc123456789abcdef123456789abcdef12345678"
checksum = "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
installed_at = "agents/my-agent.md"
[[agents]]
name = "helper"
source = "community"
path = "agents/helper.md"
version = "v1.0.0"
resolved_commit = "def456789abcdef123456789abcdef123456789ab"
checksum = "sha256:38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da"
installed_at = "agents/helper.md"
[[snippets]]
name = "utils"
source = "official"
path = "snippets/utils.md"
version = "v1.0.0"
resolved_commit = "abc123456789abcdef123456789abcdef12345678"
checksum = "sha256:74e6f7298a9c2d168935f58c6b6c5b5ea4c3df6a0b6b8d2e7b2a2b8c3d4e5f6a"
installed_at = "snippets/utils.md"
"#
);
fs::write(
project.project_path().join("ccpm.lock"),
lockfile_content.trim(),
)
.unwrap();
let output = project
.run_ccpm(&["validate", "--resolve", "--check-lock"])
.unwrap();
assert!(output.success);
assert!(output.stdout.contains("✓"));
}
#[test]
fn test_validate_verbose() {
let project = TestProject::new().unwrap();
let manifest_content = r#"
[sources]
official = "file:///fake/url"
[agents]
my-agent = { source = "official", path = "agents/my-agent.md", version = "v1.0.0" }
"#;
project.write_manifest(manifest_content).unwrap();
let output = project.run_ccpm(&["validate", "--verbose"]).unwrap();
assert!(output.success);
assert!(output.stdout.contains("Validating"));
assert!(output.stdout.contains("✓"));
}
#[test]
fn test_validate_quiet() {
let project = TestProject::new().unwrap();
let manifest_content = r#"
[sources]
official = "file:///fake/url"
[agents]
my-agent = { source = "official", path = "agents/my-agent.md", version = "v1.0.0" }
"#;
project.write_manifest(manifest_content).unwrap();
let output = project.run_ccpm(&["validate", "--quiet"]).unwrap();
assert!(output.success);
}
#[test]
fn test_validate_json_output() {
let project = TestProject::new().unwrap();
let manifest_content = r#"
[sources]
official = "file:///fake/url"
[agents]
my-agent = { source = "official", path = "agents/my-agent.md", version = "v1.0.0" }
"#;
project.write_manifest(manifest_content).unwrap();
let output = project.run_ccpm(&["validate", "--format", "json"]).unwrap();
assert!(output.success);
assert!(output.stdout.contains("{"));
assert!(output.stdout.contains("\"valid\""));
assert!(output.stdout.contains("\"errors\""));
assert!(output.stdout.contains("\"warnings\""));
}
#[test]
fn test_validate_specific_file() {
let project = TestProject::new().unwrap();
let sources_path_str = project
.sources_path()
.display()
.to_string()
.replace('\\', "/");
let manifest_content = format!(
r#"
[sources]
official = "file://{sources_path_str}/official"
community = "file://{sources_path_str}/community"
[agents]
my-agent = {{ source = "official", path = "agents/my-agent.md", version = "v1.0.0" }}
helper = {{ source = "community", path = "agents/helper.md", version = "v1.0.0" }}
[snippets]
utils = {{ source = "official", path = "snippets/utils.md", version = "v1.0.0" }}
"#
);
let manifest_path = project.project_path().join("ccpm.toml");
fs::write(&manifest_path, manifest_content.trim()).unwrap();
let output = project
.run_ccpm(&["validate", manifest_path.to_str().unwrap()])
.unwrap();
assert!(output.success);
assert!(output.stdout.contains("✓"));
assert!(output.stdout.contains("Valid"));
}
#[test]
fn test_validate_with_warnings() {
let project = TestProject::new().unwrap();
let manifest_content = r#"
[sources]
official = "https://github.com/example-org/ccpm-official.git"
[agents]
old-agent = { source = "official", path = "agents/old.md", version = "v0.1.0" }
deprecated-agent = { source = "official", path = "agents/deprecated.md", version = "~0.5.0" }
"#;
project.write_manifest(manifest_content).unwrap();
let output = project.run_ccpm(&["validate"]).unwrap();
assert!(output.success);
assert!(output.stdout.contains("✓"));
assert!(output.stdout.contains("Valid"));
assert!(output.stdout.contains("⚠"));
assert!(output.stdout.contains("Warning"));
}
#[test]
fn test_validate_strict_mode() {
let project = TestProject::new().unwrap();
let manifest_content = r#"
[sources]
official = "https://github.com/example-org/ccpm-official.git"
[agents]
old-agent = { source = "official", path = "agents/old.md", version = "v0.1.0" }
"#;
project.write_manifest(manifest_content).unwrap();
let output = project.run_ccpm(&["validate", "--strict"]).unwrap();
assert!(!output.success);
assert!(output.stdout.contains("✗"));
assert!(output.stdout.contains("Strict mode"));
assert!(output.stdout.contains("Warnings treated as errors"));
}
#[test]
fn test_validate_help() {
let mut cmd = assert_cmd::Command::cargo_bin("ccpm").unwrap();
cmd.arg("validate")
.arg("--help")
.assert()
.success()
.stdout(predicate::str::contains("--sources"))
.stdout(predicate::str::contains("--resolve"));
}
#[test]
fn test_validate_empty_manifest() {
let project = TestProject::new().unwrap();
let empty_manifest = r"
# Empty manifest
";
project.write_manifest(empty_manifest).unwrap();
let output = project.run_ccpm(&["validate"]).unwrap();
assert!(output.success);
assert!(output.stdout.contains("✓"));
assert!(output.stdout.contains("Valid"));
assert!(output.stdout.contains("⚠"));
assert!(output.stdout.contains("No dependencies defined"));
}
#[test]
fn test_validate_circular_dependencies() {
let project = TestProject::new().unwrap();
let manifest_content = r#"
[sources]
source1 = "https://github.com/test/repo1.git"
source2 = "https://github.com/test/repo2.git"
[agents]
agent-a = { source = "source1", path = "agents/a.md", version = "v1.0.0" }
agent-b = { source = "source2", path = "agents/b.md", version = "v1.0.0" }
"#;
project.write_manifest(manifest_content).unwrap();
let output = project.run_ccpm(&["validate", "--dependencies"]).unwrap();
assert!(output.success); assert!(output.stdout.contains("✓"));
}