use std::path::{Path, PathBuf};
use super::{MarketplaceHealth, MarketplaceOp, github_source, marketplace_health, marketplace_op};
use crate::host::{Scope, Source};
use crate::manifest::MarketplaceEntry;
const EXPECTED: &str = "/data/root/clauth/current@claude";
fn github(ref_: &'static str) -> Source {
Source::GitHub { repo: "owner/repo", ref_ }
}
fn entry(ref_: Option<&str>) -> MarketplaceEntry {
MarketplaceEntry { name: Some("m".into()), path: None, ref_: ref_.map(str::to_string), source: None }
}
fn entry_with_path(path: &str) -> MarketplaceEntry {
MarketplaceEntry { name: Some("m".into()), path: Some(path.into()), ref_: None, source: Some("directory".into()) }
}
fn github_entry(ref_: Option<&str>) -> MarketplaceEntry {
MarketplaceEntry { name: Some("m".into()), path: None, ref_: ref_.map(str::to_string), source: Some("github".into()) }
}
#[test]
fn github_source_carries_the_ref() {
assert_eq!(github_source("owner/repo", "v0.1.0"), "owner/repo@v0.1.0");
}
#[test]
fn absent_marketplace_is_added() {
assert_eq!(marketplace_op(&github("v0.1.0"), None, EXPECTED), MarketplaceOp::Add);
assert_eq!(marketplace_op(&Source::Embedded, None, EXPECTED), MarketplaceOp::Add);
}
#[test]
fn matching_github_ref_updates() {
assert_eq!(marketplace_op(&github("v0.1.0"), Some(&github_entry(Some("v0.1.0"))), EXPECTED), MarketplaceOp::Update);
}
#[test]
fn drifted_github_ref_re_adds() {
assert_eq!(marketplace_op(&github("v0.1.1"), Some(&github_entry(Some("v0.1.0"))), EXPECTED), MarketplaceOp::Add);
assert_eq!(marketplace_op(&github("v0.1.0"), Some(&github_entry(None)), EXPECTED), MarketplaceOp::Add);
}
#[test]
fn local_source_on_the_expected_pointer_updates() {
assert_eq!(marketplace_op(&Source::Embedded, Some(&entry_with_path(EXPECTED)), EXPECTED), MarketplaceOp::Update);
assert_eq!(marketplace_op(&Source::Path(PathBuf::from("/tmp/x")), Some(&entry_with_path(EXPECTED)), EXPECTED), MarketplaceOp::Update);
}
#[test]
fn local_source_diverged_from_the_pointer_re_adds() {
assert_eq!(marketplace_op(&Source::Embedded, Some(&entry_with_path("/old/checkout/plugins")), EXPECTED), MarketplaceOp::Add);
}
#[test]
fn github_registered_entry_re_adds_under_a_local_source() {
assert_eq!(marketplace_op(&Source::Embedded, Some(&github_entry(None)), EXPECTED), MarketplaceOp::Add);
}
#[test]
fn marketplace_health_absent_entry() {
assert_eq!(marketplace_health(None, &Source::Embedded, Path::new(EXPECTED)), MarketplaceHealth::Absent);
assert_eq!(marketplace_health(None, &github("v0.1.0"), Path::new(EXPECTED)), MarketplaceHealth::Absent);
assert_eq!(marketplace_health(None, &Source::Path(PathBuf::from("/tmp/x")), Path::new(EXPECTED)), MarketplaceHealth::Absent);
}
#[test]
fn marketplace_health_github_entry_healthy_under_github_source() {
assert_eq!(marketplace_health(Some(&github_entry(Some("v0.1.0"))), &github("v0.1.0"), Path::new(EXPECTED)), MarketplaceHealth::Healthy);
}
#[test]
fn marketplace_health_github_entry_dangles_under_a_local_source() {
assert_eq!(marketplace_health(Some(&github_entry(None)), &Source::Embedded, Path::new(EXPECTED)), MarketplaceHealth::Dangling);
}
#[test]
fn marketplace_health_missing_path_field_dangles() {
assert_eq!(marketplace_health(Some(&entry(None)), &Source::Embedded, Path::new(EXPECTED)), MarketplaceHealth::Dangling);
}
#[test]
fn marketplace_health_local_path_on_the_pointer_with_manifest_is_healthy() {
let root = crate::scratch::path("ez-marketplace-health-healthy");
let _ = std::fs::remove_dir_all(&root);
std::fs::create_dir_all(root.join(".claude-plugin")).unwrap();
std::fs::write(root.join(".claude-plugin").join("marketplace.json"), "{}").unwrap();
let e = MarketplaceEntry {
name: Some("m".into()),
path: Some(root.to_string_lossy().into_owned()),
ref_: None,
source: Some("directory".into()),
};
assert_eq!(marketplace_health(Some(&e), &Source::Embedded, &root), MarketplaceHealth::Healthy);
assert_eq!(marketplace_health(Some(&e), &Source::Path(PathBuf::from("/tmp/x")), &root), MarketplaceHealth::Healthy);
let _ = std::fs::remove_dir_all(&root);
}
#[test]
fn marketplace_health_local_path_missing_dangles() {
let nonexistent = "/agentgear/marketplace-health/does/not/exist";
assert!(!PathBuf::from(nonexistent).exists(), "fixture path must not exist for this assertion");
assert_eq!(
marketplace_health(Some(&entry_with_path(nonexistent)), &Source::Embedded, Path::new(EXPECTED)),
MarketplaceHealth::Dangling
);
}
#[test]
fn marketplace_health_diverged_path_dangles() {
let elsewhere = crate::scratch::path("ez-marketplace-health-elsewhere");
let _ = std::fs::remove_dir_all(&elsewhere);
std::fs::create_dir_all(elsewhere.join(".claude-plugin")).unwrap();
std::fs::write(elsewhere.join(".claude-plugin").join("marketplace.json"), "{}").unwrap();
assert_eq!(
marketplace_health(Some(&entry_with_path(&elsewhere.to_string_lossy())), &Source::Embedded, Path::new(EXPECTED)),
MarketplaceHealth::Dangling
);
let _ = std::fs::remove_dir_all(&elsewhere);
}
#[test]
fn marketplace_health_manifest_missing_dangles() {
let root = crate::scratch::path("ez-marketplace-health-no-manifest");
let _ = std::fs::remove_dir_all(&root);
std::fs::create_dir_all(root.join(".claude-plugin")).unwrap();
assert_eq!(marketplace_health(Some(&entry_with_path(&root.to_string_lossy())), &Source::Embedded, &root), MarketplaceHealth::Dangling);
let _ = std::fs::remove_dir_all(&root);
}
fn entry_at(scope: &str) -> crate::manifest::PluginEntry {
crate::manifest::PluginEntry {
id: "clauth@clauth".into(),
version: Some("0.14.1".into()),
enabled: None,
install_path: None,
errors: None,
scope: Some(scope.into()),
}
}
#[test]
fn at_scope_matches_its_own_scope() {
assert!(entry_at("user").at_scope(&Scope::User));
assert!(entry_at("project").at_scope(&Scope::Project { path: PathBuf::from("/x") }));
}
#[test]
fn at_scope_refuses_the_other_scope() {
assert!(!entry_at("project").at_scope(&Scope::User));
assert!(!entry_at("user").at_scope(&Scope::Project { path: PathBuf::from("/x") }));
}
#[test]
fn at_scope_tolerates_a_missing_scope_field() {
let mut e = entry_at("user");
e.scope = None;
assert!(e.at_scope(&Scope::User));
assert!(e.at_scope(&Scope::Project { path: PathBuf::from("/x") }));
}