use std::path::Path;
use super::download::{is_safe_path, parse_frontmatter_name, strip_prefix};
use super::local::source_spec_string;
use super::*;
#[test]
fn parse_github_source() {
let s = InstallSource::parse("github:Hmbown/test-skill").unwrap();
assert_eq!(
s,
InstallSource::GitHubRepo("Hmbown/test-skill".to_string())
);
}
#[test]
fn parse_github_source_rejects_missing_repo() {
let err = InstallSource::parse("github:Hmbown").unwrap_err();
assert!(err.to_string().contains("github source must"), "got: {err}");
}
#[test]
fn parse_github_source_rejects_extra_slashes() {
let err = InstallSource::parse("github:Hmbown/repo/extra").unwrap_err();
assert!(err.to_string().contains("github source must"), "got: {err}");
}
#[test]
fn parse_direct_url_source() {
let s = InstallSource::parse("https://example.com/skill.tar.gz").unwrap();
assert_eq!(
s,
InstallSource::DirectUrl("https://example.com/skill.tar.gz".to_string())
);
let s = InstallSource::parse("http://example.com/skill.tar.gz").unwrap();
assert_eq!(
s,
InstallSource::DirectUrl("http://example.com/skill.tar.gz".to_string())
);
}
#[test]
fn parse_github_browser_url_routes_to_github_repo() {
for spec in [
"https://github.com/obra/superpowers",
"https://github.com/obra/superpowers/",
"https://github.com/obra/superpowers.git",
"https://github.com/obra/superpowers.git/",
"https://www.github.com/obra/superpowers",
"http://github.com/obra/superpowers",
" https://github.com/obra/superpowers ",
] {
let parsed =
InstallSource::parse(spec).unwrap_or_else(|err| panic!("parse({spec}) failed: {err}"));
assert_eq!(
parsed,
InstallSource::GitHubRepo("obra/superpowers".to_string()),
"spec {spec} must route to GitHubRepo",
);
}
}
#[test]
fn parse_github_archive_url_stays_direct() {
for spec in [
"https://github.com/obra/superpowers/archive/refs/heads/main.tar.gz",
"https://github.com/obra/superpowers/blob/main/README.md",
"https://github.com/obra/superpowers/tree/main",
] {
let parsed = InstallSource::parse(spec).unwrap();
assert!(
matches!(parsed, InstallSource::DirectUrl(_)),
"spec {spec} must stay DirectUrl, got {parsed:?}",
);
}
}
#[test]
fn parse_registry_source() {
let s = InstallSource::parse("my-skill").unwrap();
assert_eq!(s, InstallSource::Registry("my-skill".to_string()));
}
#[test]
fn parse_rejects_empty() {
assert!(InstallSource::parse("").is_err());
assert!(InstallSource::parse(" ").is_err());
}
#[test]
fn is_safe_path_rejects_traversal() {
assert!(!is_safe_path(Path::new("../etc/passwd")));
assert!(!is_safe_path(Path::new("foo/../bar")));
assert!(!is_safe_path(Path::new("/etc/passwd")));
assert!(is_safe_path(Path::new("foo/bar/baz")));
assert!(is_safe_path(Path::new("SKILL.md")));
}
#[test]
fn parse_frontmatter_extracts_name() {
let body = b"---\nname: hello\ndescription: greeter\n---\nbody\n";
assert_eq!(parse_frontmatter_name(body).unwrap(), "hello");
}
#[test]
fn parse_frontmatter_missing_name_fails() {
let body = b"---\ndescription: x\n---\n";
let err = parse_frontmatter_name(body).unwrap_err();
assert!(format!("{err}").contains("name"));
}
#[test]
fn parse_frontmatter_missing_description_fails() {
let body = b"---\nname: x\n---\n";
let err = parse_frontmatter_name(body).unwrap_err();
assert!(format!("{err}").contains("description"));
}
#[test]
fn parse_frontmatter_rejects_unsafe_name() {
let body = b"---\nname: ../evil\ndescription: x\n---\n";
assert!(parse_frontmatter_name(body).is_err());
let body = b"---\nname: a name with spaces\ndescription: x\n---\n";
assert!(parse_frontmatter_name(body).is_err());
}
#[test]
fn parse_frontmatter_requires_opening_fence() {
let body = b"name: hello\ndescription: x\n";
assert!(parse_frontmatter_name(body).is_err());
}
#[test]
fn strip_prefix_handles_all_cases() {
assert_eq!(strip_prefix("foo/bar", "foo"), "bar");
assert_eq!(strip_prefix("foo", "foo"), "");
assert_eq!(strip_prefix("baz/bar", "foo"), "baz/bar");
assert_eq!(strip_prefix("foo/bar", ""), "foo/bar");
}
#[test]
fn source_spec_string_roundtrips() {
assert_eq!(
source_spec_string(&InstallSource::GitHubRepo("a/b".into())),
"github:a/b"
);
assert_eq!(
source_spec_string(&InstallSource::DirectUrl("https://x".into())),
"https://x"
);
assert_eq!(
source_spec_string(&InstallSource::Registry("x".into())),
"x"
);
}