Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 2 additions & 8 deletions src/commands/plugin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -409,13 +409,7 @@ fn update(name: Option<String>, yes: bool) -> Result<()> {

match status {
Ok(s) if s.success() => {
registry::install_plugin(
&pl.name,
std::path::Path::new(&pl.path),
&pl.source,
&pl.starforge_version,
&pl.plugin_version,
)?;
registry::install_plugin(&pl.name, std::path::Path::new(&pl.path), &pl.source, &pl.starforge_version, &pl.plugin_version)?;
p::success(&format!(" '{}' updated via cargo install", pl.name));
updated += 1;
}
Expand Down Expand Up @@ -447,7 +441,7 @@ fn update(name: Option<String>, yes: bool) -> Result<()> {
})
.unwrap_or(0);

let installed_epoch = 0;
let installed_epoch = 0u64;

if modified > installed_epoch {
// Library on disk is newer — refresh the registry entry.
Expand Down
171 changes: 171 additions & 0 deletions src/utils/templates.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1991,4 +1991,175 @@ mod tests {
));
assert!(assert_template_compatible(&entry).is_err());
}

// ── parse_semver edge cases ────────────────────────────────────────────────

#[test]
fn parse_semver_large_numbers() {
assert_eq!(parse_semver("999.0.0"), Ok((999, 0, 0)));
assert_eq!(parse_semver("0.0.999999"), Ok((0, 0, 999999)));
}

#[test]
fn parse_semver_rejects_single_component() {
assert!(parse_semver("1").is_err());
}

#[test]
fn parse_semver_rejects_two_components() {
assert!(parse_semver("1.2").is_err());
}

#[test]
fn parse_semver_rejects_extra_dots() {
assert!(parse_semver("1.2.3.4").is_err(), "four components should fail");
}

#[test]
fn parse_semver_rejects_whitespace() {
assert!(parse_semver(" 1.2.3").is_err());
assert!(parse_semver("1.2.3 ").is_err());
assert!(parse_semver("1. 2.3").is_err());
}

#[test]
fn parse_semver_rejects_negative_component() {
// A leading '-' makes the component non-numeric.
assert!(parse_semver("1.-2.3").is_err());
}

#[test]
fn parse_semver_rejects_alpha_component() {
assert!(parse_semver("1.2.alpha").is_err());
assert!(parse_semver("v1.2.3").is_err());
}

// ── check_version_range payload verification ───────────────────────────────

#[test]
fn check_version_range_too_old_carries_correct_payload() {
let result = check_version_range("0.0.9", Some("0.1.0"), None);
match result {
CompatibilityStatus::TooOld {
required_min,
running,
} => {
assert_eq!(required_min, "0.1.0");
assert_eq!(running, "0.0.9");
}
other => panic!("expected TooOld, got {:?}", other),
}
}

#[test]
fn check_version_range_too_new_carries_correct_payload() {
let result = check_version_range("2.0.0", None, Some("1.99.99"));
match result {
CompatibilityStatus::TooNew {
required_max,
running,
} => {
assert_eq!(required_max, "1.99.99");
assert_eq!(running, "2.0.0");
}
other => panic!("expected TooNew, got {:?}", other),
}
}

#[test]
fn check_version_range_exact_min_boundary_is_compatible() {
// version == min should be Compatible, not TooOld.
assert_eq!(
check_version_range("1.0.0", Some("1.0.0"), None),
CompatibilityStatus::Compatible
);
}

#[test]
fn check_version_range_exact_max_boundary_is_compatible() {
// version == max should be Compatible, not TooNew.
assert_eq!(
check_version_range("1.0.0", None, Some("1.0.0")),
CompatibilityStatus::Compatible
);
}

#[test]
fn check_version_range_min_only_above_min_is_compatible() {
assert_eq!(
check_version_range("1.2.0", Some("1.0.0"), None),
CompatibilityStatus::Compatible
);
}

#[test]
fn check_version_range_max_only_below_max_is_compatible() {
assert_eq!(
check_version_range("0.9.0", None, Some("1.0.0")),
CompatibilityStatus::Compatible
);
}

#[test]
fn check_version_range_malformed_running_version_is_error() {
// The running version itself being malformed should yield MalformedMetadata.
let result = check_version_range("not-a-version", Some("0.1.0"), None);
assert!(matches!(
result,
CompatibilityStatus::MalformedMetadata { .. }
));
}

#[test]
fn check_version_range_malformed_max_carries_reason() {
let result = check_version_range("0.1.0", None, Some("1.x.0"));
match result {
CompatibilityStatus::MalformedMetadata { reason } => {
assert!(!reason.is_empty(), "reason should not be empty");
}
other => panic!("expected MalformedMetadata, got {:?}", other),
}
}

// ── assert_template_compatible error message content ──────────────────────

#[test]
fn assert_template_compatible_too_old_message_contains_min_and_running() {
let mut entry = make_entry("future-tpl");
let (major, _, _) = parse_semver(CLI_VERSION).unwrap();
let min = format!("{}.0.0", major + 100);
entry.cli_version_min = Some(min.clone());
let err = assert_template_compatible(&entry).unwrap_err();
let msg = err.to_string();
assert!(msg.contains(&min), "error should contain required_min");
assert!(msg.contains(CLI_VERSION), "error should contain running version");
assert!(msg.contains("future-tpl"), "error should contain template name");
}

#[test]
fn assert_template_compatible_too_new_message_contains_max_and_running() {
let mut entry = make_entry("old-tpl");
let (major, minor, _) = parse_semver(CLI_VERSION).unwrap();
if major > 0 || minor > 0 {
entry.cli_version_max = Some("0.0.0".to_string());
let err = assert_template_compatible(&entry).unwrap_err();
let msg = err.to_string();
assert!(msg.contains("0.0.0"), "error should contain required_max");
assert!(msg.contains(CLI_VERSION), "error should contain running version");
assert!(msg.contains("old-tpl"), "error should contain template name");
}
}

#[test]
fn assert_template_compatible_malformed_message_contains_reason() {
let mut entry = make_entry("broken-tpl");
entry.cli_version_min = Some("bad-version".to_string());
let err = assert_template_compatible(&entry).unwrap_err();
let msg = err.to_string();
assert!(msg.contains("broken-tpl"), "error should contain template name");
assert!(
msg.contains("malformed") || msg.contains("bad-version"),
"error should describe the problem"
);
}
}