Tahinli/forge
public0 watching
★ 0
| 1 | use std::path::{Path, PathBuf}; |
| 2 | use std::process::Command; |
| 3 | |
| 4 | use forge_git::command::{GitCommand, GitEnv}; |
| 5 | use forge_git::pktline::{FLUSH_PKT, pkt_line, service_advertisement_header}; |
| 6 | use forge_git::repo::{init_bare, list_branches, repo_path}; |
| 7 | use forge_git::service::{GitService, service_command}; |
| 8 | use forge_git::setup::{write_hook_shims, write_managed_gitconfig}; |
| 9 | use forge_git::version::parse_git_version; |
| 10 | |
| 11 | fn env_for(data_dir: &Path) -> GitEnv { |
| 12 | GitEnv { |
| 13 | git_bin: PathBuf::from("/usr/bin/git"), |
| 14 | data_dir: data_dir.to_path_buf(), |
| 15 | } |
| 16 | } |
| 17 | |
| 18 | const ULID: &str = "01arz3ndektsv4rrffq69g5fav"; |
| 19 | |
| 20 | #[test] |
| 21 | fn version_parse() { |
| 22 | assert_eq!(parse_git_version("git version 2.56.0\n"), Some((2, 56, 0))); |
| 23 | assert_eq!( |
| 24 | parse_git_version("git version 2.39.2.windows.1"), |
| 25 | Some((2, 39, 2)) |
| 26 | ); |
| 27 | assert_eq!(parse_git_version("git version 2.46"), Some((2, 46, 0))); |
| 28 | assert_eq!( |
| 29 | parse_git_version("git version 2.46.0-rc1"), |
| 30 | Some((2, 46, 0)) |
| 31 | ); |
| 32 | assert_eq!(parse_git_version("garbage"), None); |
| 33 | } |
| 34 | |
| 35 | #[test] |
| 36 | fn pkt_line_encoding() { |
| 37 | assert_eq!(pkt_line("a\n"), b"0006a\n".to_vec()); |
| 38 | assert_eq!(pkt_line(""), b"0004".to_vec()); |
| 39 | assert_eq!(FLUSH_PKT, b"0000"); |
| 40 | let header = service_advertisement_header(GitService::UploadPack); |
| 41 | assert_eq!(header, b"001e# service=git-upload-pack\n0000".to_vec()); |
| 42 | } |
| 43 | |
| 44 | #[test] |
| 45 | fn repo_path_layout() { |
| 46 | let p = repo_path(Path::new("/data"), &ULID.to_uppercase()).unwrap(); |
| 47 | assert_eq!(p, PathBuf::from(format!("/data/repos/01/ar/{ULID}.git"))); |
| 48 | assert!(repo_path(Path::new("/data"), "not-a-ulid").is_err()); |
| 49 | assert!(repo_path(Path::new("/data"), "01arz3ndektsv4rrffq69g5fa").is_err()); |
| 50 | assert!(repo_path(Path::new("/data"), "01arz3ndektsv4rrffq69g5fau").is_err()); |
| 51 | } |
| 52 | |
| 53 | #[tokio::test] |
| 54 | async fn init_bare_and_list_branches_empty() { |
| 55 | let dir = tempfile::tempdir().unwrap(); |
| 56 | let env = env_for(dir.path()); |
| 57 | let repo = repo_path(dir.path(), ULID).unwrap(); |
| 58 | init_bare(&env, &repo, "trunk").await.unwrap(); |
| 59 | assert!(repo.join("HEAD").exists()); |
| 60 | let branches = list_branches(&env, &repo).await.unwrap(); |
| 61 | assert!(branches.is_empty()); |
| 62 | } |
| 63 | |
| 64 | #[tokio::test] |
| 65 | async fn init_bare_rejects_bad_branch() { |
| 66 | let dir = tempfile::tempdir().unwrap(); |
| 67 | let env = env_for(dir.path()); |
| 68 | let repo = dir.path().join("x.git"); |
| 69 | assert!(init_bare(&env, &repo, "-x").await.is_err()); |
| 70 | assert!(init_bare(&env, &repo, "a..b").await.is_err()); |
| 71 | assert!(!repo.exists()); |
| 72 | } |
| 73 | |
| 74 | #[test] |
| 75 | fn user_revs_rejects_option_like_values() { |
| 76 | let dir = tempfile::tempdir().unwrap(); |
| 77 | let env = env_for(dir.path()); |
| 78 | assert!(GitCommand::new(&env, "log").user_revs(&["-x"]).is_err()); |
| 79 | assert!(GitCommand::new(&env, "log").user_revs(&[""]).is_err()); |
| 80 | assert!(GitCommand::new(&env, "log").user_revs(&["main"]).is_ok()); |
| 81 | } |
| 82 | |
| 83 | #[test] |
| 84 | fn gitconfig_is_written() { |
| 85 | let dir = tempfile::tempdir().unwrap(); |
| 86 | write_managed_gitconfig(dir.path()).unwrap(); |
| 87 | let cfg = dir.path().join("gitconfig"); |
| 88 | let out = Command::new("/usr/bin/git") |
| 89 | .args(["config", "--file"]) |
| 90 | .arg(&cfg) |
| 91 | .args(["--get", "core.hooksPath"]) |
| 92 | .output() |
| 93 | .unwrap(); |
| 94 | assert!(out.status.success()); |
| 95 | let hooks = String::from_utf8_lossy(&out.stdout); |
| 96 | assert_eq!(hooks.trim(), dir.path().join("hooks").to_str().unwrap()); |
| 97 | |
| 98 | let out = Command::new("/usr/bin/git") |
| 99 | .args(["config", "--file"]) |
| 100 | .arg(&cfg) |
| 101 | .args(["--get", "receive.denyNonFastForwards"]) |
| 102 | .output() |
| 103 | .unwrap(); |
| 104 | assert_eq!(String::from_utf8_lossy(&out.stdout).trim(), "false"); |
| 105 | } |
| 106 | |
| 107 | #[test] |
| 108 | fn hook_shims_are_executable() { |
| 109 | use std::os::unix::fs::PermissionsExt; |
| 110 | let dir = tempfile::tempdir().unwrap(); |
| 111 | write_hook_shims(dir.path(), Path::new("/usr/local/bin/forge")).unwrap(); |
| 112 | for name in ["pre-receive", "post-receive"] { |
| 113 | let p = dir.path().join("hooks").join(name); |
| 114 | let mode = std::fs::metadata(&p).unwrap().permissions().mode(); |
| 115 | assert_eq!(mode & 0o777, 0o755, "{name}"); |
| 116 | let body = std::fs::read_to_string(&p).unwrap(); |
| 117 | assert_eq!( |
| 118 | body, |
| 119 | format!("#!/bin/sh\nexec '/usr/local/bin/forge' hook {name}\n") |
| 120 | ); |
| 121 | } |
| 122 | assert!(write_hook_shims(dir.path(), Path::new("/tmp/it's")).is_err()); |
| 123 | assert!(write_hook_shims(dir.path(), Path::new("/tmp/a\nb")).is_err()); |
| 124 | } |
| 125 | |
| 126 | #[tokio::test] |
| 127 | async fn upload_pack_advertise_exits_zero() { |
| 128 | let dir = tempfile::tempdir().unwrap(); |
| 129 | let env = env_for(dir.path()); |
| 130 | let repo = repo_path(dir.path(), ULID).unwrap(); |
| 131 | init_bare(&env, &repo, "main").await.unwrap(); |
| 132 | service_command(&env, GitService::UploadPack, &repo, true, true, None) |
| 133 | .status() |
| 134 | .await |
| 135 | .unwrap(); |
| 136 | } |