#![cfg(feature = "git")]
#![allow(missing_docs)]
#![allow(unsafe_code)]
use std::path::Path;
use std::process::Command;
use rtb_credentials::CredentialRef;
use rtb_forge::git::{CloneOptions, FetchOptions, Repo, RepoError};
#[test]
fn a1_clone_options_with_credential_builder() {
let cref = CredentialRef { env: Some("RTB_TEST_TOKEN".to_string()), ..Default::default() };
let opts = CloneOptions::default().with_credential(cref);
assert!(opts.credential.is_some(), "credential should be Some after builder");
}
#[test]
fn a1_fetch_options_with_credential_builder() {
let cref = CredentialRef { env: Some("RTB_TEST_TOKEN".to_string()), ..Default::default() };
let opts = FetchOptions::default().with_credential(cref);
assert!(opts.credential.is_some(), "credential should be Some after builder");
}
#[tokio::test]
async fn a2_clone_with_unresolvable_credential_errors() {
let upstream = build_three_commit_repo();
let dst_holder = tempfile::tempdir().expect("dst tempdir");
let dst = dst_holder.path().join("cloned");
let url = format!("file://{}", upstream.path().display());
let cref = CredentialRef { env: Some("RTB_UNSET_TOKEN_A2".to_string()), ..Default::default() };
unsafe {
std::env::remove_var("RTB_UNSET_TOKEN_A2");
}
let opts = CloneOptions::default().with_credential(cref);
let err = Repo::clone(&url, &dst, opts).await.expect_err("must fail");
assert!(matches!(err, RepoError::Auth(_)), "got {err:?}");
}
#[tokio::test]
async fn a3_fetch_with_unresolvable_credential_errors() {
let upstream = build_three_commit_repo();
let dst_holder = tempfile::tempdir().expect("dst tempdir");
let dst = dst_holder.path().join("cloned");
let url = format!("file://{}", upstream.path().display());
let cloned = Repo::clone(&url, &dst, CloneOptions::default()).await.expect("clone");
let cref = CredentialRef { env: Some("RTB_UNSET_TOKEN_A3".to_string()), ..Default::default() };
unsafe {
std::env::remove_var("RTB_UNSET_TOKEN_A3");
}
let opts = FetchOptions::default().with_credential(cref);
let err = cloned.fetch("origin", opts).await.expect_err("must fail");
assert!(matches!(err, RepoError::Auth(_)), "got {err:?}");
}
#[tokio::test]
async fn a4_clone_with_resolved_credential_against_file_succeeds() {
let upstream = build_three_commit_repo();
let dst_holder = tempfile::tempdir().expect("dst tempdir");
let dst = dst_holder.path().join("cloned");
let url = format!("file://{}", upstream.path().display());
unsafe {
std::env::set_var("RTB_TEST_TOKEN_A4", "dummy-token-value");
}
let cref = CredentialRef { env: Some("RTB_TEST_TOKEN_A4".to_string()), ..Default::default() };
let opts = CloneOptions::default().with_credential(cref);
let _repo = Repo::clone(&url, &dst, opts).await.expect("clone with auth env set");
assert!(dst.join(".git").is_dir(), "clone produced a git repository");
unsafe {
std::env::remove_var("RTB_TEST_TOKEN_A4");
}
}
#[tokio::test]
async fn a5_fetch_with_resolved_credential_against_file_succeeds() {
let upstream = build_three_commit_repo();
let dst_holder = tempfile::tempdir().expect("dst tempdir");
let dst = dst_holder.path().join("cloned");
let url = format!("file://{}", upstream.path().display());
let cloned = Repo::clone(&url, &dst, CloneOptions::default()).await.expect("clone");
unsafe {
std::env::set_var("RTB_TEST_TOKEN_A5", "dummy-token-value");
}
let cref = CredentialRef { env: Some("RTB_TEST_TOKEN_A5".to_string()), ..Default::default() };
let opts = FetchOptions::default().with_credential(cref);
cloned.fetch("origin", opts).await.expect("fetch with auth env set");
unsafe {
std::env::remove_var("RTB_TEST_TOKEN_A5");
}
}
fn build_three_commit_repo() -> tempfile::TempDir {
let dir = tempfile::tempdir().expect("tempdir");
let path = dir.path();
git_init(path);
write(path, "README.md", "v1\n");
git_add_all(path);
git_commit(path, "alice", "initial");
write(path, "README.md", "v2\n");
git_add_all(path);
git_commit(path, "alice", "second");
write(path, "README.md", "v3\n");
git_add_all(path);
git_commit(path, "alice", "third");
dir
}
fn git_init(path: &Path) {
Command::new("git").args(["init", "-b", "main"]).current_dir(path).output().expect("git init");
}
fn git_add_all(path: &Path) {
Command::new("git").args(["add", "-A"]).current_dir(path).output().expect("git add");
}
fn git_commit(path: &Path, author: &str, message: &str) {
Command::new("git")
.args(["commit", "--allow-empty", "-m", message])
.env("GIT_AUTHOR_NAME", author)
.env("GIT_AUTHOR_EMAIL", format!("{author}@example.test"))
.env("GIT_COMMITTER_NAME", author)
.env("GIT_COMMITTER_EMAIL", format!("{author}@example.test"))
.env("GIT_AUTHOR_DATE", "2026-05-19T00:00:00Z")
.env("GIT_COMMITTER_DATE", "2026-05-19T00:00:00Z")
.current_dir(path)
.output()
.expect("git commit");
}
fn write(path: &Path, file: &str, contents: &str) {
std::fs::write(path.join(file), contents).expect("write");
}