use std::path::PathBuf;
use std::process::Command;
use super::*;
use tempfile::TempDir;
fn detect_project_at_test(root: &Path, explicit: Option<&str>) -> String {
detect_project_at_internal(root, explicit, Some(String::new()))
}
fn create_git_repo() -> TempDir {
let dir = TempDir::new().expect("create temp dir for git repo");
init_git_repo(dir.path());
dir
}
fn init_git_repo(path: &Path) {
Command::new("git")
.args(["-C", path.to_str().unwrap(), "init", "-b", "main"])
.env("GIT_TERMINAL_PROMPT", "0")
.output()
.expect("git init failed");
Command::new("git")
.args([
"-C",
path.to_str().unwrap(),
"config",
"user.name",
"Test User",
])
.output()
.expect("git config user.name failed");
Command::new("git")
.args([
"-C",
path.to_str().unwrap(),
"config",
"user.email",
"test@example.com",
])
.output()
.expect("git config user.email failed");
}
fn add_remote(path: &Path, name: &str, url: &str) {
Command::new("git")
.args(["-C", path.to_str().unwrap(), "remote", "add", name, url])
.output()
.expect("git remote add failed");
}
fn create_subdirectory(repo_path: &Path) -> PathBuf {
let sub = repo_path.join("src").join("deep");
std::fs::create_dir_all(&sub).expect("create subdirectory");
sub
}
#[test]
fn test_parse_ssh_remote() {
assert_eq!(
parse_git_remote("git@github.com:owner/repo.git"),
"owner/repo"
);
assert_eq!(parse_git_remote("git@github.com:owner/repo"), "owner/repo");
}
#[test]
fn test_parse_https_remote() {
assert_eq!(
parse_git_remote("https://github.com/owner/repo.git"),
"owner/repo"
);
assert_eq!(
parse_git_remote("https://github.com/owner/repo"),
"owner/repo"
);
}
#[test]
fn test_parse_ssh_url_with_protocol() {
assert_eq!(
parse_git_remote("ssh://git@github.com/owner/repo.git"),
"owner/repo"
);
}
#[test]
fn test_git_suffix_stripping() {
assert_eq!(parse_git_remote("owner/repo.git"), "owner/repo");
}
#[test]
fn test_fallback_when_no_domain() {
assert_eq!(parse_git_remote("just-name"), "just-name");
}
#[test]
fn test_parse_mixed_case_host_and_owner() {
assert_eq!(
parse_git_remote("git@GitHub.com:Owner/Repo.git"),
"Owner/Repo"
);
assert_eq!(
parse_git_remote("https://GitHub.com/Owner/Repo.git"),
"Owner/Repo"
);
}
#[test]
fn test_explicit_override() {
let dir = TempDir::new().expect("temp dir");
assert_eq!(
detect_project_at(dir.path(), Some("my-project")),
"my-project"
);
}
#[test]
fn test_explicit_override_empty() {
let dir = create_git_repo();
let result = detect_project_at_test(dir.path(), Some(""));
assert_eq!(result, dir.path().file_name().unwrap().to_str().unwrap());
}
#[test]
fn test_explicit_override_whitespace() {
let dir = create_git_repo();
let result = detect_project_at_test(dir.path(), Some(" \t "));
assert_eq!(result, dir.path().file_name().unwrap().to_str().unwrap());
}
#[test]
fn test_env_var_whitespace() {
let dir = create_git_repo();
let result = detect_project_at_internal(dir.path(), None, Some(" ".to_string()));
assert_eq!(result, dir.path().file_name().unwrap().to_str().unwrap());
}
#[test]
fn test_env_var_override_with_no_git() {
let dir = TempDir::new().expect("temp dir");
let result = detect_project_at_internal(dir.path(), None, Some("env-project".to_string()));
assert_eq!(result, "env-project");
}
#[test]
fn test_env_var_trimmed() {
let dir = TempDir::new().expect("temp dir");
let result = detect_project_at_internal(dir.path(), None, Some(" trimmed ".to_string()));
assert_eq!(result, "trimmed");
}
#[test]
fn test_detect_https_remote() {
let dir = create_git_repo();
add_remote(
dir.path(),
"origin",
"https://github.com/randomm/vipune.git",
);
assert_eq!(detect_project_at_test(dir.path(), None), "randomm/vipune");
}
#[test]
fn test_detect_ssh_remote() {
let dir = create_git_repo();
add_remote(dir.path(), "origin", "git@github.com:randomm/vipune.git");
assert_eq!(detect_project_at_test(dir.path(), None), "randomm/vipune");
}
#[test]
fn test_detect_ssh_url_with_protocol() {
let dir = create_git_repo();
add_remote(
dir.path(),
"origin",
"ssh://git@github.com/randomm/vipune.git",
);
assert_eq!(detect_project_at_test(dir.path(), None), "randomm/vipune");
}
#[test]
fn test_detect_remote_without_git_suffix() {
let dir = create_git_repo();
add_remote(dir.path(), "origin", "https://github.com/randomm/vipune");
assert_eq!(detect_project_at_test(dir.path(), None), "randomm/vipune");
}
#[test]
fn test_detect_only_upstream_remote_uses_dir_name() {
let dir = create_git_repo();
add_remote(
dir.path(),
"upstream",
"https://github.com/canonical/project.git",
);
let result = detect_project_at_test(dir.path(), None);
assert_eq!(result, dir.path().file_name().unwrap().to_str().unwrap());
assert_ne!(result, "canonical/project");
}
#[test]
fn test_detect_no_git_repo() {
let dir = TempDir::new().expect("temp dir");
let result = detect_project_at_test(dir.path(), None);
assert!(!result.is_empty());
}
#[test]
fn test_detect_same_id_from_root_and_subdirectory() {
let dir = create_git_repo();
add_remote(dir.path(), "origin", "https://github.com/owner/repo.git");
let sub = create_subdirectory(dir.path());
let from_root = detect_project_at_test(dir.path(), None);
let from_sub = detect_project_at_test(&sub, None);
assert_eq!(from_root, "owner/repo");
assert_eq!(from_sub, "owner/repo");
assert_eq!(from_root, from_sub);
}
#[test]
fn test_detect_fallback_same_from_root_and_subdirectory() {
let dir = create_git_repo();
let sub = create_subdirectory(dir.path());
let from_root = detect_project_at_test(dir.path(), None);
let from_sub = detect_project_at_test(&sub, None);
let expected = dir.path().file_name().unwrap().to_str().unwrap();
assert_eq!(from_root, expected);
assert_eq!(from_sub, expected);
}
#[test]
fn test_fallback_no_remotes_yields_dir_name() {
let dir = create_git_repo();
let result = detect_project_at_test(dir.path(), None);
assert_eq!(result, dir.path().file_name().unwrap().to_str().unwrap());
}
#[test]
fn test_fallback_warning_message_includes_project_id() {
let dir = create_git_repo();
let msg = build_fallback_warning_message(
dir.path().file_name().unwrap().to_str().unwrap(),
dir.path(),
);
assert!(msg.contains("using directory name as project_id"));
assert!(msg.contains(dir.path().file_name().unwrap().to_str().unwrap()));
}
#[test]
fn test_fallback_warning_message_lists_other_remotes() {
let dir = create_git_repo();
add_remote(
dir.path(),
"upstream",
"https://github.com/canonical/project.git",
);
let msg = build_fallback_warning_message(
dir.path().file_name().unwrap().to_str().unwrap(),
dir.path(),
);
assert!(msg.contains("other remotes"));
assert!(msg.contains("upstream"));
}
#[test]
fn test_fallback_warning_message_no_other_remotes() {
let dir = create_git_repo();
let msg = build_fallback_warning_message(
dir.path().file_name().unwrap().to_str().unwrap(),
dir.path(),
);
assert!(!msg.contains("other remotes"));
assert!(msg.contains("This project_id may differ"));
}
#[test]
fn test_remote_derived_ids_unchanged() {
let cases = [
("https://github.com/randomm/vipune.git", "randomm/vipune"),
("https://github.com/randomm/vipune", "randomm/vipune"),
("git@github.com:randomm/vipune.git", "randomm/vipune"),
("git@github.com:randomm/vipune", "randomm/vipune"),
("ssh://git@github.com/randomm/vipune.git", "randomm/vipune"),
(
"https://gitlab.example.com/group/subgroup/project.git",
"subgroup/project",
),
(
"git@gitlab.example.com:group/subgroup/project.git",
"group/subgroup/project",
),
];
for (remote_url, expected_id) in cases {
let dir = create_git_repo();
add_remote(dir.path(), "origin", remote_url);
let result = detect_project_at_test(dir.path(), None);
assert_eq!(
result, expected_id,
"remote '{}' should produce project_id '{}'",
remote_url, expected_id
);
}
}
#[test]
fn test_detect_project_delegates_to_current_dir() {
let project = detect_project(None);
assert!(!project.is_empty());
}
#[test]
fn test_detect_project_explicit_override() {
assert_eq!(detect_project(Some("custom-id")), "custom-id");
}