use std::path::PathBuf;
use super::{BackendState, PresentAction, classify, github_marketplace_source, present_action};
use crate::cli::{CopilotMarketplace, CopilotPlugin, parse_marketplace_list, parse_plugin_list, parse_version_anywhere};
use crate::host::Source;
fn plugin(name: &str, mkt: &str, version: Option<&str>) -> CopilotPlugin {
CopilotPlugin { plugin: name.into(), marketplace: mkt.into(), version: version.map(str::to_string) }
}
fn github() -> Source {
Source::GitHub { repo: "owner/repo", ref_: "v0.1.0" }
}
#[test]
fn parse_plugin_list_reads_id_and_version() {
let out = "Installed plugins:\n • ez-fixture-plugin@ez-fixture (v0.1.0)\n";
assert_eq!(parse_plugin_list(out), vec![plugin("ez-fixture-plugin", "ez-fixture", Some("0.1.0"))]);
}
#[test]
fn parse_plugin_list_handles_multiple_rows_and_skips_the_header() {
let out = "Installed plugins:\n • a@one (v1.2.3)\n • b@two (v0.0.9)\n";
assert_eq!(parse_plugin_list(out), vec![plugin("a", "one", Some("1.2.3")), plugin("b", "two", Some("0.0.9"))]);
}
#[test]
fn parse_plugin_list_empty_when_none_installed() {
assert!(parse_plugin_list("No plugins installed.\n").is_empty());
assert!(parse_plugin_list("").is_empty());
}
const MARKETPLACE_OUT: &str = "\
Included with GitHub Copilot:
◆ copilot-plugins (GitHub: github/copilot-plugins)
◆ awesome-copilot (GitHub: github/awesome-copilot)
Registered marketplaces:
• ez-fixture (Local: /abs/path/current)
";
#[test]
fn parse_marketplace_list_returns_only_registered_local() {
assert_eq!(
parse_marketplace_list(MARKETPLACE_OUT),
vec![CopilotMarketplace { name: "ez-fixture".into(), path: Some("/abs/path/current".into()) }]
);
}
#[test]
fn parse_marketplace_list_skips_builtin_only_output() {
let only_builtin = "Included with GitHub Copilot:\n ◆ copilot-plugins (GitHub: github/copilot-plugins)\n";
assert!(parse_marketplace_list(only_builtin).is_empty(), "built-in marketplaces must never count as ours");
}
#[test]
fn parse_marketplace_list_reads_a_registered_github_source() {
let out = "Registered marketplaces:\n • mine (GitHub: owner/repo)\n";
assert_eq!(parse_marketplace_list(out), vec![CopilotMarketplace { name: "mine".into(), path: None }]);
}
#[test]
fn parse_version_anywhere_finds_the_trailing_version() {
assert_eq!(parse_version_anywhere("GitHub Copilot CLI 1.0.71."), Some((1, 0, 71)));
assert_eq!(parse_version_anywhere("nope"), None);
}
#[test]
fn github_marketplace_source_drops_the_unpinnable_ref() {
assert_eq!(github_marketplace_source("owner/repo", "v0.1.0"), "owner/repo");
assert_eq!(github_marketplace_source("owner/repo", ""), "owner/repo");
}
#[test]
fn present_action_updates_only_a_stale_non_github_install() {
let src = Source::Embedded;
assert_eq!(present_action(&src, Some("0.1.0"), "0.2.0"), PresentAction::Update);
assert_eq!(present_action(&src, Some("0.2.0"), "0.2.0"), PresentAction::NoOp);
assert_eq!(present_action(&src, None, "0.2.0"), PresentAction::NoOp);
assert_eq!(present_action(&src, Some("weird"), "0.2.0"), PresentAction::NoOp);
}
#[test]
fn present_action_freezes_a_strictly_newer_install() {
let src = Source::Embedded;
assert_eq!(present_action(&src, Some("0.3.0"), "0.2.0"), PresentAction::Frozen);
assert_eq!(present_action(&src, Some("1.0.0"), "0.2.0"), PresentAction::Frozen);
assert_eq!(present_action(&Source::Path(PathBuf::from("/tmp/tree")), Some("0.3.0"), "0.2.0"), PresentAction::Frozen);
}
#[test]
fn present_action_github_present_is_converged_no_version_churn() {
assert_eq!(present_action(&github(), Some("0.1.0"), "0.2.0"), PresentAction::NoOp);
assert_eq!(present_action(&github(), Some("0.9.0"), "0.2.0"), PresentAction::NoOp);
assert_eq!(present_action(&github(), None, "0.2.0"), PresentAction::NoOp);
}
#[test]
fn present_action_github_outranks_the_freeze() {
assert_eq!(present_action(&github(), Some("9.9.9"), "0.2.0"), PresentAction::NoOp);
}
#[test]
fn classify_maps_presence_and_version_to_state() {
let src = Source::Embedded;
assert!(matches!(classify(&src, None, "0.2.0"), BackendState::Absent));
assert!(matches!(classify(&src, Some(&plugin("p", "m", Some("0.1.0"))), "0.2.0"), BackendState::NeedsRepair));
assert!(matches!(classify(&src, Some(&plugin("p", "m", Some("0.2.0"))), "0.2.0"), BackendState::Healthy));
assert!(matches!(classify(&src, Some(&plugin("p", "m", Some("0.9.0"))), "0.2.0"), BackendState::Healthy));
}
#[test]
fn classify_github_present_is_healthy_regardless_of_version() {
assert!(matches!(classify(&github(), None, "0.2.0"), BackendState::Absent));
assert!(matches!(classify(&github(), Some(&plugin("p", "m", Some("0.1.0"))), "0.2.0"), BackendState::Healthy));
}