upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
path: root/tests/common/git_server.rs
diff options
context:
space:
mode:
authorDanConwayDev <DanConwayDev@protonmail.com>2026-01-07 21:54:53 +0000
committerDanConwayDev <DanConwayDev@protonmail.com>2026-01-07 22:04:33 +0000
commitfba872909559c42ac48b6acd697e61ab987e0f71 (patch)
tree8c98768df4f759eb5f80558a6915e6eb92698fd3 /tests/common/git_server.rs
parent457e296d90e2f7c2808e216f2ef0608b70f76553 (diff)
test: fix hanging unit tests issue
Diffstat (limited to 'tests/common/git_server.rs')
-rw-r--r--tests/common/git_server.rs105
1 files changed, 44 insertions, 61 deletions
diff --git a/tests/common/git_server.rs b/tests/common/git_server.rs
index 6121adc..adf66b5 100644
--- a/tests/common/git_server.rs
+++ b/tests/common/git_server.rs
@@ -390,10 +390,11 @@ mod tests {
390 // Start server 390 // Start server
391 let server = SimpleGitServer::start(temp_dir.path()).await; 391 let server = SimpleGitServer::start(temp_dir.path()).await;
392 392
393 // Run git ls-remote against the server 393 // Run git ls-remote against the server (using tokio::process::Command)
394 let output = Command::new("git") 394 let output = tokio::process::Command::new("git")
395 .args(["ls-remote", server.url()]) 395 .args(["ls-remote", server.url()])
396 .output() 396 .output()
397 .await
397 .expect("Failed to run git ls-remote"); 398 .expect("Failed to run git ls-remote");
398 399
399 assert!( 400 assert!(
@@ -433,27 +434,30 @@ mod tests {
433 // Create a destination repo to fetch into 434 // Create a destination repo to fetch into
434 let dest_dir = tempfile::tempdir().expect("Failed to create dest dir"); 435 let dest_dir = tempfile::tempdir().expect("Failed to create dest dir");
435 436
436 // Initialize empty repo 437 // Initialize empty repo (using tokio::process::Command)
437 let output = Command::new("git") 438 let output = tokio::process::Command::new("git")
438 .args(["init"]) 439 .args(["init"])
439 .current_dir(dest_dir.path()) 440 .current_dir(dest_dir.path())
440 .output() 441 .output()
442 .await
441 .expect("Failed to init dest repo"); 443 .expect("Failed to init dest repo");
442 assert!(output.status.success()); 444 assert!(output.status.success());
443 445
444 // Add the server as a remote 446 // Add the server as a remote
445 let output = Command::new("git") 447 let output = tokio::process::Command::new("git")
446 .args(["remote", "add", "origin", server.url()]) 448 .args(["remote", "add", "origin", server.url()])
447 .current_dir(dest_dir.path()) 449 .current_dir(dest_dir.path())
448 .output() 450 .output()
451 .await
449 .expect("Failed to add remote"); 452 .expect("Failed to add remote");
450 assert!(output.status.success()); 453 assert!(output.status.success());
451 454
452 // Fetch from the server 455 // Fetch from the server
453 let output = Command::new("git") 456 let output = tokio::process::Command::new("git")
454 .args(["fetch", "origin"]) 457 .args(["fetch", "origin"])
455 .current_dir(dest_dir.path()) 458 .current_dir(dest_dir.path())
456 .output() 459 .output()
460 .await
457 .expect("Failed to fetch"); 461 .expect("Failed to fetch");
458 462
459 assert!( 463 assert!(
@@ -463,10 +467,11 @@ mod tests {
463 ); 467 );
464 468
465 // Verify the commit was fetched 469 // Verify the commit was fetched
466 let output = Command::new("git") 470 let output = tokio::process::Command::new("git")
467 .args(["rev-parse", "origin/main"]) 471 .args(["rev-parse", "origin/main"])
468 .current_dir(dest_dir.path()) 472 .current_dir(dest_dir.path())
469 .output() 473 .output()
474 .await
470 .expect("Failed to rev-parse"); 475 .expect("Failed to rev-parse");
471 476
472 assert!(output.status.success()); 477 assert!(output.status.success());
@@ -538,12 +543,10 @@ impl SmartGitServer {
538 /// # Panics 543 /// # Panics
539 /// Panics if the git operations fail or the server cannot start 544 /// Panics if the git operations fail or the server cannot start
540 pub async fn start(source_repo: &Path) -> Self { 545 pub async fn start(source_repo: &Path) -> Self {
541 println!("[SmartGitServer::start] Creating temp dir");
542 // 1. Create temp directory for bare repo 546 // 1. Create temp directory for bare repo
543 let temp_dir = tempfile::tempdir().expect("Failed to create temp dir for git server"); 547 let temp_dir = tempfile::tempdir().expect("Failed to create temp dir for git server");
544 let bare_repo_path = temp_dir.path().join("repo.git"); 548 let bare_repo_path = temp_dir.path().join("repo.git");
545 549
546 println!("[SmartGitServer::start] Cloning bare repo from {:?}", source_repo);
547 // 2. Create bare clone 550 // 2. Create bare clone
548 let output = Command::new("git") 551 let output = Command::new("git")
549 .args(["clone", "--bare"]) 552 .args(["clone", "--bare"])
@@ -558,44 +561,41 @@ impl SmartGitServer {
558 String::from_utf8_lossy(&output.stderr) 561 String::from_utf8_lossy(&output.stderr)
559 ); 562 );
560 } 563 }
561 println!("[SmartGitServer::start] Bare clone created");
562 564
563 // 3. Find a free port 565 // 3. Create and bind listener (eliminates port race condition)
564 println!("[SmartGitServer::start] Finding free port"); 566 let std_listener = std::net::TcpListener::bind("127.0.0.1:0")
565 let port = find_free_port(); 567 .expect("Failed to bind to random port");
566 println!("[SmartGitServer::start] Found port {}", port); 568 let port = std_listener.local_addr()
567 let addr: SocketAddr = ([127, 0, 0, 1], port).into(); 569 .expect("Failed to get local addr")
570 .port();
571
572 // Convert to tokio listener (keeps port bound)
573 std_listener.set_nonblocking(true)
574 .expect("Failed to set non-blocking");
575 let listener = TcpListener::from_std(std_listener)
576 .expect("Failed to convert to tokio listener");
568 577
569 // 4. Create shutdown channel 578 // 4. Create shutdown channel
570 let (shutdown_tx, mut shutdown_rx) = oneshot::channel::<()>(); 579 let (shutdown_tx, mut shutdown_rx) = oneshot::channel::<()>();
571 580
572 println!("[SmartGitServer::start] Binding to {}", addr);
573 // 5. Start the HTTP server 581 // 5. Start the HTTP server
574 let repo_path = Arc::new(bare_repo_path); 582 let repo_path = Arc::new(bare_repo_path);
575 let listener = TcpListener::bind(addr)
576 .await
577 .expect("Failed to bind to address");
578 println!("[SmartGitServer::start] Listener bound successfully");
579 583
580 let handle = tokio::spawn(async move { 584 let handle = tokio::spawn(async move {
581 eprintln!("[SmartGitServer] Server loop started, waiting for connections...");
582 loop { 585 loop {
583 tokio::select! { 586 tokio::select! {
584 accept_result = listener.accept() => { 587 accept_result = listener.accept() => {
585 match accept_result { 588 match accept_result {
586 Ok((stream, addr)) => { 589 Ok((stream, _addr)) => {
587 eprintln!("[SmartGitServer] Accepted connection from {}", addr);
588 let repo_path = Arc::clone(&repo_path); 590 let repo_path = Arc::clone(&repo_path);
589 let io = TokioIo::new(stream); 591 let io = TokioIo::new(stream);
590 592
591 tokio::spawn(async move { 593 tokio::spawn(async move {
592 eprintln!("[SmartGitServer] Spawning handler for connection");
593 let service = service_fn(move |req| { 594 let service = service_fn(move |req| {
594 let repo_path = Arc::clone(&repo_path); 595 let repo_path = Arc::clone(&repo_path);
595 async move { handle_smart_request(req, &repo_path).await } 596 async move { handle_smart_request(req, &repo_path).await }
596 }); 597 });
597 598
598 eprintln!("[SmartGitServer] About to serve_connection");
599 if let Err(e) = http1::Builder::new() 599 if let Err(e) = http1::Builder::new()
600 .serve_connection(io, service) 600 .serve_connection(io, service)
601 .await 601 .await
@@ -605,7 +605,6 @@ impl SmartGitServer {
605 eprintln!("SmartGitServer connection error: {}", e); 605 eprintln!("SmartGitServer connection error: {}", e);
606 } 606 }
607 } 607 }
608 eprintln!("[SmartGitServer] Connection handler finished");
609 }); 608 });
610 } 609 }
611 Err(e) => { 610 Err(e) => {
@@ -615,20 +614,16 @@ impl SmartGitServer {
615 } 614 }
616 _ = &mut shutdown_rx => { 615 _ = &mut shutdown_rx => {
617 // Shutdown signal received 616 // Shutdown signal received
618 eprintln!("[SmartGitServer] Shutdown signal received");
619 break; 617 break;
620 } 618 }
621 } 619 }
622 } 620 }
623 eprintln!("[SmartGitServer] Server loop exited");
624 }); 621 });
625 622
626 let url = format!("http://127.0.0.1:{}", port); 623 let url = format!("http://127.0.0.1:{}", port);
627 624
628 println!("[SmartGitServer::start] Waiting for server to be ready on port {}", port);
629 // 6. Wait for server to be ready 625 // 6. Wait for server to be ready
630 wait_for_server_ready(port).await; 626 wait_for_server_ready(port).await;
631 println!("[SmartGitServer::start] Server is ready!");
632 627
633 Self { 628 Self {
634 shutdown_tx: Some(shutdown_tx), 629 shutdown_tx: Some(shutdown_tx),
@@ -684,9 +679,6 @@ async fn handle_smart_request(
684 let query = req.uri().query().unwrap_or(""); 679 let query = req.uri().query().unwrap_or("");
685 let method = req.method(); 680 let method = req.method();
686 681
687 println!("[SmartGitServer] {} {} query={}", method, path, query);
688 eprintln!("[SmartGitServer] {} {} query={}", method, path, query);
689
690 // Extract Git-Protocol header (for protocol version 2) 682 // Extract Git-Protocol header (for protocol version 2)
691 // We need to clone it to avoid borrowing issues when moving req 683 // We need to clone it to avoid borrowing issues when moving req
692 let git_protocol = req 684 let git_protocol = req
@@ -695,10 +687,6 @@ async fn handle_smart_request(
695 .and_then(|v| v.to_str().ok()) 687 .and_then(|v| v.to_str().ok())
696 .map(|s| s.to_string()); 688 .map(|s| s.to_string());
697 689
698 if let Some(ref proto) = git_protocol {
699 eprintln!("[SmartGitServer] Git-Protocol: {}", proto);
700 }
701
702 // Route: GET /info/refs?service=git-upload-pack 690 // Route: GET /info/refs?service=git-upload-pack
703 if method == hyper::Method::GET && path.ends_with("/info/refs") { 691 if method == hyper::Method::GET && path.ends_with("/info/refs") {
704 // Parse service from query string 692 // Parse service from query string
@@ -714,7 +702,6 @@ async fn handle_smart_request(
714 702
715 match service { 703 match service {
716 Some("git-upload-pack") => { 704 Some("git-upload-pack") => {
717 eprintln!("[SmartGitServer] Handling info/refs for upload-pack");
718 return handle_info_refs_upload_pack(repo_path, git_protocol.as_deref()).await; 705 return handle_info_refs_upload_pack(repo_path, git_protocol.as_deref()).await;
719 } 706 }
720 Some("git-receive-pack") => { 707 Some("git-receive-pack") => {
@@ -735,7 +722,6 @@ async fn handle_smart_request(
735 722
736 // Route: POST /git-upload-pack 723 // Route: POST /git-upload-pack
737 if method == hyper::Method::POST && path.ends_with("/git-upload-pack") { 724 if method == hyper::Method::POST && path.ends_with("/git-upload-pack") {
738 eprintln!("[SmartGitServer] Handling POST /git-upload-pack");
739 return handle_upload_pack(req, repo_path, git_protocol.as_deref()).await; 725 return handle_upload_pack(req, repo_path, git_protocol.as_deref()).await;
740 } 726 }
741 727
@@ -986,31 +972,20 @@ mod smart_git_server_tests {
986 972
987 #[tokio::test] 973 #[tokio::test]
988 async fn test_smart_git_server_ls_remote() { 974 async fn test_smart_git_server_ls_remote() {
989 println!("[TEST] Starting test_smart_git_server_ls_remote");
990 eprintln!("[TEST] Starting test_smart_git_server_ls_remote");
991
992 // Create a test repo 975 // Create a test repo
993 let temp_dir = tempfile::tempdir().expect("Failed to create temp dir"); 976 let temp_dir = tempfile::tempdir().expect("Failed to create temp dir");
994 println!("[TEST] Created temp dir");
995 let commit_hash = create_test_repo_with_commit(temp_dir.path(), CommitVariant::StateTest) 977 let commit_hash = create_test_repo_with_commit(temp_dir.path(), CommitVariant::StateTest)
996 .expect("Failed to create test repo"); 978 .expect("Failed to create test repo");
997 println!("[TEST] Created test repo with commit {}", commit_hash);
998 979
999 // Start server 980 // Start server
1000 println!("[TEST] About to start SmartGitServer");
1001 let server = SmartGitServer::start(temp_dir.path()).await; 981 let server = SmartGitServer::start(temp_dir.path()).await;
1002 println!("[TEST] Server started at {}", server.url());
1003
1004 // Give the server loop task a chance to print
1005 tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
1006 982
1007 // Run git ls-remote against the server 983 // Run git ls-remote against the server (using tokio::process::Command)
1008 println!("[TEST] Running git ls-remote {}", server.url()); 984 let output = tokio::process::Command::new("git")
1009 let output = Command::new("git")
1010 .args(["ls-remote", server.url()]) 985 .args(["ls-remote", server.url()])
1011 .output() 986 .output()
987 .await
1012 .expect("Failed to run git ls-remote"); 988 .expect("Failed to run git ls-remote");
1013 println!("[TEST] git ls-remote completed");
1014 989
1015 assert!( 990 assert!(
1016 output.status.success(), 991 output.status.success(),
@@ -1050,26 +1025,29 @@ mod smart_git_server_tests {
1050 let dest_dir = tempfile::tempdir().expect("Failed to create dest dir"); 1025 let dest_dir = tempfile::tempdir().expect("Failed to create dest dir");
1051 1026
1052 // Initialize empty repo 1027 // Initialize empty repo
1053 let output = Command::new("git") 1028 let output = tokio::process::Command::new("git")
1054 .args(["init"]) 1029 .args(["init"])
1055 .current_dir(dest_dir.path()) 1030 .current_dir(dest_dir.path())
1056 .output() 1031 .output()
1032 .await
1057 .expect("Failed to init dest repo"); 1033 .expect("Failed to init dest repo");
1058 assert!(output.status.success()); 1034 assert!(output.status.success());
1059 1035
1060 // Add the server as a remote 1036 // Add the server as a remote
1061 let output = Command::new("git") 1037 let output = tokio::process::Command::new("git")
1062 .args(["remote", "add", "origin", server.url()]) 1038 .args(["remote", "add", "origin", server.url()])
1063 .current_dir(dest_dir.path()) 1039 .current_dir(dest_dir.path())
1064 .output() 1040 .output()
1041 .await
1065 .expect("Failed to add remote"); 1042 .expect("Failed to add remote");
1066 assert!(output.status.success()); 1043 assert!(output.status.success());
1067 1044
1068 // Fetch from the server 1045 // Fetch from the server
1069 let output = Command::new("git") 1046 let output = tokio::process::Command::new("git")
1070 .args(["fetch", "origin"]) 1047 .args(["fetch", "origin"])
1071 .current_dir(dest_dir.path()) 1048 .current_dir(dest_dir.path())
1072 .output() 1049 .output()
1050 .await
1073 .expect("Failed to fetch"); 1051 .expect("Failed to fetch");
1074 1052
1075 assert!( 1053 assert!(
@@ -1079,10 +1057,11 @@ mod smart_git_server_tests {
1079 ); 1057 );
1080 1058
1081 // Verify the commit was fetched 1059 // Verify the commit was fetched
1082 let output = Command::new("git") 1060 let output = tokio::process::Command::new("git")
1083 .args(["rev-parse", "origin/main"]) 1061 .args(["rev-parse", "origin/main"])
1084 .current_dir(dest_dir.path()) 1062 .current_dir(dest_dir.path())
1085 .output() 1063 .output()
1064 .await
1086 .expect("Failed to rev-parse"); 1065 .expect("Failed to rev-parse");
1087 1066
1088 assert!(output.status.success()); 1067 assert!(output.status.success());
@@ -1111,26 +1090,29 @@ mod smart_git_server_tests {
1111 let dest_dir = tempfile::tempdir().expect("Failed to create dest dir"); 1090 let dest_dir = tempfile::tempdir().expect("Failed to create dest dir");
1112 1091
1113 // Initialize empty repo 1092 // Initialize empty repo
1114 let output = Command::new("git") 1093 let output = tokio::process::Command::new("git")
1115 .args(["init"]) 1094 .args(["init"])
1116 .current_dir(dest_dir.path()) 1095 .current_dir(dest_dir.path())
1117 .output() 1096 .output()
1097 .await
1118 .expect("Failed to init dest repo"); 1098 .expect("Failed to init dest repo");
1119 assert!(output.status.success()); 1099 assert!(output.status.success());
1120 1100
1121 // Add the server as a remote 1101 // Add the server as a remote
1122 let output = Command::new("git") 1102 let output = tokio::process::Command::new("git")
1123 .args(["remote", "add", "origin", server.url()]) 1103 .args(["remote", "add", "origin", server.url()])
1124 .current_dir(dest_dir.path()) 1104 .current_dir(dest_dir.path())
1125 .output() 1105 .output()
1106 .await
1126 .expect("Failed to add remote"); 1107 .expect("Failed to add remote");
1127 assert!(output.status.success()); 1108 assert!(output.status.success());
1128 1109
1129 // Shallow fetch from the server - THIS IS WHAT PURGATORY SYNC USES 1110 // Shallow fetch from the server - THIS IS WHAT PURGATORY SYNC USES
1130 let output = Command::new("git") 1111 let output = tokio::process::Command::new("git")
1131 .args(["fetch", "--depth=1", "origin", &commit_hash]) 1112 .args(["fetch", "--depth=1", "origin", &commit_hash])
1132 .current_dir(dest_dir.path()) 1113 .current_dir(dest_dir.path())
1133 .output() 1114 .output()
1115 .await
1134 .expect("Failed to fetch"); 1116 .expect("Failed to fetch");
1135 1117
1136 assert!( 1118 assert!(
@@ -1140,10 +1122,11 @@ mod smart_git_server_tests {
1140 ); 1122 );
1141 1123
1142 // Verify the commit was fetched 1124 // Verify the commit was fetched
1143 let output = Command::new("git") 1125 let output = tokio::process::Command::new("git")
1144 .args(["cat-file", "-t", &commit_hash]) 1126 .args(["cat-file", "-t", &commit_hash])
1145 .current_dir(dest_dir.path()) 1127 .current_dir(dest_dir.path())
1146 .output() 1128 .output()
1129 .await
1147 .expect("Failed to cat-file"); 1130 .expect("Failed to cat-file");
1148 1131
1149 assert!( 1132 assert!(