mod test_helpers;
use std::process::Command;
use tempfile::TempDir;
use test_helpers::run_jjj;
#[allow(dead_code)]
fn run_jj(dir: &std::path::Path, args: &[&str]) -> std::process::Output {
Command::new("jj")
.current_dir(dir)
.args(args)
.output()
.expect("Failed to execute jj")
}
fn create_bare_remote() -> TempDir {
let temp_dir = TempDir::new().expect("Failed to create temp dir for bare repo");
let status = Command::new("git")
.current_dir(temp_dir.path())
.args(["init", "--bare"])
.status()
.expect("Failed to init bare repo");
assert!(status.success(), "Failed to create bare git repo");
temp_dir
}
fn setup_repo_with_remote(remote_path: &std::path::Path) -> TempDir {
let temp_dir = TempDir::new().expect("Failed to create temp dir");
let status = Command::new("jj")
.current_dir(temp_dir.path())
.args(["git", "init", "--colocate"])
.status()
.expect("Failed to run jj init");
assert!(status.success(), "jj init failed");
Command::new("jj")
.current_dir(temp_dir.path())
.args(["config", "set", "--repo", "user.name", "Test User"])
.status()
.expect("Failed to set user.name");
Command::new("jj")
.current_dir(temp_dir.path())
.args(["config", "set", "--repo", "user.email", "test@example.com"])
.status()
.expect("Failed to set user.email");
let remote_url = format!("file://{}", remote_path.display());
let status = Command::new("jj")
.current_dir(temp_dir.path())
.args(["git", "remote", "add", "origin", &remote_url])
.status()
.expect("Failed to add remote");
assert!(status.success(), "Failed to add git remote");
temp_dir
}
#[test]
fn test_push_to_bare_remote() {
if jjj::jj::find_executable("jj").is_none() {
eprintln!("Skipping test: jj not found");
return;
}
let remote_dir = create_bare_remote();
let repo_dir = setup_repo_with_remote(remote_dir.path());
let dir = repo_dir.path();
let output = run_jjj(dir, &["init"]);
assert!(
output.status.success(),
"jjj init failed: {}",
String::from_utf8_lossy(&output.stderr)
);
let output = run_jjj(dir, &["problem", "new", "Test Problem for Push"]);
assert!(
output.status.success(),
"problem new failed: {}",
String::from_utf8_lossy(&output.stderr)
);
let output = run_jjj(
dir,
&[
"solution",
"new",
"Test Solution",
"--problem",
"Test Problem for Push",
],
);
assert!(
output.status.success(),
"solution new failed: {}",
String::from_utf8_lossy(&output.stderr)
);
let output = run_jjj(dir, &["push", "--remote", "origin"]);
let stdout = String::from_utf8_lossy(&output.stdout);
let stderr = String::from_utf8_lossy(&output.stderr);
println!("Push stdout: {}", stdout);
println!("Push stderr: {}", stderr);
assert!(output.status.success(), "jjj push failed: {}", stderr);
assert!(
stdout.contains("Pushing jjj"),
"Should mention pushing jjj bookmark"
);
let output = Command::new("git")
.current_dir(remote_dir.path())
.args(["branch", "-a"])
.output()
.expect("Failed to list branches");
let branches = String::from_utf8_lossy(&output.stdout);
assert!(
branches.contains("jjj"),
"Remote should have jjj branch. Got: {}",
branches
);
}
#[test]
fn test_fetch_from_remote() {
if jjj::jj::find_executable("jj").is_none() {
eprintln!("Skipping test: jj not found");
return;
}
let remote_dir = create_bare_remote();
let alice_dir = setup_repo_with_remote(remote_dir.path());
run_jjj(alice_dir.path(), &["init"]);
run_jjj(alice_dir.path(), &["problem", "new", "Shared Problem"]);
run_jjj(
alice_dir.path(),
&[
"solution",
"new",
"Alice Solution",
"--problem",
"Shared Problem",
],
);
let output = run_jjj(alice_dir.path(), &["push", "--remote", "origin"]);
assert!(
output.status.success(),
"Alice push failed: {}",
String::from_utf8_lossy(&output.stderr)
);
let bob_dir = setup_repo_with_remote(remote_dir.path());
run_jjj(bob_dir.path(), &["init"]);
let output = run_jjj(bob_dir.path(), &["fetch", "--remote", "origin"]);
let stdout = String::from_utf8_lossy(&output.stdout);
let stderr = String::from_utf8_lossy(&output.stderr);
println!("Fetch stdout: {}", stdout);
println!("Fetch stderr: {}", stderr);
assert!(output.status.success(), "jjj fetch failed: {}", stderr);
let output = run_jjj(bob_dir.path(), &["problem", "list"]);
let stdout = String::from_utf8_lossy(&output.stdout);
assert!(
stdout.contains("Shared Problem"),
"Bob should see Shared Problem after fetch. Got: {}",
stdout
);
let output = run_jjj(bob_dir.path(), &["solution", "list"]);
let stdout = String::from_utf8_lossy(&output.stdout);
assert!(
stdout.contains("Alice Solution"),
"Bob should see Alice Solution after fetch. Got: {}",
stdout
);
}
#[test]
fn test_push_fetch_roundtrip() {
if jjj::jj::find_executable("jj").is_none() {
eprintln!("Skipping test: jj not found");
return;
}
let remote_dir = create_bare_remote();
let alice_dir = setup_repo_with_remote(remote_dir.path());
run_jjj(alice_dir.path(), &["init"]);
run_jjj(alice_dir.path(), &["problem", "new", "Auth timeout bug"]);
let output = run_jjj(alice_dir.path(), &["push", "--remote", "origin"]);
assert!(output.status.success(), "Alice initial push failed");
let bob_dir = setup_repo_with_remote(remote_dir.path());
run_jjj(bob_dir.path(), &["init"]);
let output = run_jjj(bob_dir.path(), &["fetch", "--remote", "origin"]);
assert!(output.status.success(), "Bob fetch failed");
let output = run_jjj(
bob_dir.path(),
&[
"solution",
"new",
"Token refresh fix",
"--problem",
"Auth timeout bug",
"--force",
],
);
let stdout = String::from_utf8_lossy(&output.stdout);
let stderr = String::from_utf8_lossy(&output.stderr);
println!("Bob solution new stdout: {}", stdout);
println!("Bob solution new stderr: {}", stderr);
assert!(
output.status.success(),
"Bob solution new failed: {}",
stderr
);
let output = run_jjj(bob_dir.path(), &["push", "--remote", "origin"]);
let stdout = String::from_utf8_lossy(&output.stdout);
let stderr = String::from_utf8_lossy(&output.stderr);
println!("Bob push stdout: {}", stdout);
println!("Bob push stderr: {}", stderr);
assert!(output.status.success(), "Bob push failed: {}", stderr);
let output = run_jjj(alice_dir.path(), &["fetch", "--remote", "origin"]);
assert!(
output.status.success(),
"Alice fetch failed: {}",
String::from_utf8_lossy(&output.stderr)
);
let output = run_jjj(alice_dir.path(), &["solution", "list"]);
let stdout = String::from_utf8_lossy(&output.stdout);
assert!(
stdout.contains("Token refresh fix"),
"Alice should see Bob's solution after fetch. Got: {}",
stdout
);
}
#[test]
fn test_push_dry_run() {
if jjj::jj::find_executable("jj").is_none() {
eprintln!("Skipping test: jj not found");
return;
}
let remote_dir = create_bare_remote();
let repo_dir = setup_repo_with_remote(remote_dir.path());
let dir = repo_dir.path();
run_jjj(dir, &["init"]);
run_jjj(dir, &["problem", "new", "Dry Run Test"]);
let output = run_jjj(dir, &["push", "--remote", "origin", "--dry-run"]);
let stdout = String::from_utf8_lossy(&output.stdout);
assert!(output.status.success(), "Dry run should succeed");
assert!(
stdout.contains("Would push"),
"Should indicate what would be pushed. Got: {}",
stdout
);
let output = Command::new("git")
.current_dir(remote_dir.path())
.args(["branch", "-a"])
.output()
.expect("Failed to list branches");
let branches = String::from_utf8_lossy(&output.stdout);
assert!(
!branches.contains("jjj"),
"Dry run should not push. Remote branches: {}",
branches
);
}
#[test]
fn test_push_validates_before_pushing() {
if jjj::jj::find_executable("jj").is_none() {
eprintln!("Skipping test: jj not found");
return;
}
let remote_dir = create_bare_remote();
let repo_dir = setup_repo_with_remote(remote_dir.path());
let dir = repo_dir.path();
let output = run_jjj(dir, &["init"]);
assert!(
output.status.success(),
"init failed: {}",
String::from_utf8_lossy(&output.stderr)
);
let output = run_jjj(dir, &["problem", "new", "Validation Test"]);
assert!(
output.status.success(),
"problem new failed: {}",
String::from_utf8_lossy(&output.stderr)
);
let output = run_jjj(dir, &["push", "--remote", "origin"]);
let stdout = String::from_utf8_lossy(&output.stdout);
let stderr = String::from_utf8_lossy(&output.stderr);
println!("Push stdout: {}", stdout);
println!("Push stderr: {}", stderr);
assert!(output.status.success(), "Push failed: {}", stderr);
assert!(
stdout.contains("Validating") || stdout.contains("checks passed"),
"Should show validation. Got: {}",
stdout
);
}