upleb.uk

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

summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorDanConwayDev <DanConwayDev@protonmail.com>2026-01-08 00:50:54 +0000
committerDanConwayDev <DanConwayDev@protonmail.com>2026-01-08 00:50:54 +0000
commitf75e1c59aacf5ce668fd327e4e3d827511661c2a (patch)
tree867926c7503e7c587e86c67896a9e7347600447b /tests
parent3f14f998d64b5fa15bdddd7570b4f72874eb9f29 (diff)
chore: cargo fmt
Diffstat (limited to 'tests')
-rw-r--r--tests/common/git_server.rs98
-rw-r--r--tests/common/mock_relay.rs23
-rw-r--r--tests/common/purgatory_helpers.rs29
-rw-r--r--tests/common/relay.rs5
4 files changed, 99 insertions, 56 deletions
diff --git a/tests/common/git_server.rs b/tests/common/git_server.rs
index adf66b5..d0d727e 100644
--- a/tests/common/git_server.rs
+++ b/tests/common/git_server.rs
@@ -301,7 +301,10 @@ fn find_free_port() -> u16 {
301 use std::net::TcpListener; 301 use std::net::TcpListener;
302 302
303 let listener = TcpListener::bind("127.0.0.1:0").expect("Failed to bind to random port"); 303 let listener = TcpListener::bind("127.0.0.1:0").expect("Failed to bind to random port");
304 let port = listener.local_addr().expect("Failed to get local addr").port(); 304 let port = listener
305 .local_addr()
306 .expect("Failed to get local addr")
307 .port();
305 drop(listener); 308 drop(listener);
306 port 309 port
307} 310}
@@ -320,7 +323,10 @@ async fn wait_for_server_ready(port: u16) {
320 } 323 }
321 Err(_) => { 324 Err(_) => {
322 if attempt == max_attempts - 1 { 325 if attempt == max_attempts - 1 {
323 panic!("SimpleGitServer failed to start after {} attempts", max_attempts); 326 panic!(
327 "SimpleGitServer failed to start after {} attempts",
328 max_attempts
329 );
324 } 330 }
325 tokio::time::sleep(delay).await; 331 tokio::time::sleep(delay).await;
326 } 332 }
@@ -366,10 +372,13 @@ mod tests {
366 .await 372 .await
367 .expect("Failed to fetch info/refs"); 373 .expect("Failed to fetch info/refs");
368 374
369 assert!(response.status().is_success(), "info/refs should be accessible"); 375 assert!(
376 response.status().is_success(),
377 "info/refs should be accessible"
378 );
370 379
371 let body = response.text().await.expect("Failed to read response body"); 380 let body = response.text().await.expect("Failed to read response body");
372 381
373 // Should contain at least one ref (HEAD or refs/heads/main) 382 // Should contain at least one ref (HEAD or refs/heads/main)
374 assert!( 383 assert!(
375 body.contains("refs/heads/main") || body.contains("HEAD"), 384 body.contains("refs/heads/main") || body.contains("HEAD"),
@@ -404,7 +413,7 @@ mod tests {
404 ); 413 );
405 414
406 let stdout = String::from_utf8_lossy(&output.stdout); 415 let stdout = String::from_utf8_lossy(&output.stdout);
407 416
408 // Should list the main branch with the correct commit 417 // Should list the main branch with the correct commit
409 assert!( 418 assert!(
410 stdout.contains(&commit_hash), 419 stdout.contains(&commit_hash),
@@ -433,7 +442,7 @@ mod tests {
433 442
434 // Create a destination repo to fetch into 443 // Create a destination repo to fetch into
435 let dest_dir = tempfile::tempdir().expect("Failed to create dest dir"); 444 let dest_dir = tempfile::tempdir().expect("Failed to create dest dir");
436 445
437 // Initialize empty repo (using tokio::process::Command) 446 // Initialize empty repo (using tokio::process::Command)
438 let output = tokio::process::Command::new("git") 447 let output = tokio::process::Command::new("git")
439 .args(["init"]) 448 .args(["init"])
@@ -487,14 +496,23 @@ mod tests {
487 #[test] 496 #[test]
488 fn test_is_safe_path_blocks_traversal() { 497 fn test_is_safe_path_blocks_traversal() {
489 let repo_path = Path::new("/tmp/repo"); 498 let repo_path = Path::new("/tmp/repo");
490 499
491 // Safe paths 500 // Safe paths
492 assert!(is_safe_path(Path::new("/tmp/repo/info/refs"), repo_path)); 501 assert!(is_safe_path(Path::new("/tmp/repo/info/refs"), repo_path));
493 assert!(is_safe_path(Path::new("/tmp/repo/objects/pack/file.pack"), repo_path)); 502 assert!(is_safe_path(
494 503 Path::new("/tmp/repo/objects/pack/file.pack"),
504 repo_path
505 ));
506
495 // Unsafe paths (path traversal) 507 // Unsafe paths (path traversal)
496 assert!(!is_safe_path(Path::new("/tmp/repo/../etc/passwd"), repo_path)); 508 assert!(!is_safe_path(
497 assert!(!is_safe_path(Path::new("/tmp/repo/../../etc/passwd"), repo_path)); 509 Path::new("/tmp/repo/../etc/passwd"),
510 repo_path
511 ));
512 assert!(!is_safe_path(
513 Path::new("/tmp/repo/../../etc/passwd"),
514 repo_path
515 ));
498 } 516 }
499} 517}
500 518
@@ -563,17 +581,19 @@ impl SmartGitServer {
563 } 581 }
564 582
565 // 3. Create and bind listener (eliminates port race condition) 583 // 3. Create and bind listener (eliminates port race condition)
566 let std_listener = std::net::TcpListener::bind("127.0.0.1:0") 584 let std_listener =
567 .expect("Failed to bind to random port"); 585 std::net::TcpListener::bind("127.0.0.1:0").expect("Failed to bind to random port");
568 let port = std_listener.local_addr() 586 let port = std_listener
587 .local_addr()
569 .expect("Failed to get local addr") 588 .expect("Failed to get local addr")
570 .port(); 589 .port();
571 590
572 // Convert to tokio listener (keeps port bound) 591 // Convert to tokio listener (keeps port bound)
573 std_listener.set_nonblocking(true) 592 std_listener
593 .set_nonblocking(true)
574 .expect("Failed to set non-blocking"); 594 .expect("Failed to set non-blocking");
575 let listener = TcpListener::from_std(std_listener) 595 let listener =
576 .expect("Failed to convert to tokio listener"); 596 TcpListener::from_std(std_listener).expect("Failed to convert to tokio listener");
577 597
578 // 4. Create shutdown channel 598 // 4. Create shutdown channel
579 let (shutdown_tx, mut shutdown_rx) = oneshot::channel::<()>(); 599 let (shutdown_tx, mut shutdown_rx) = oneshot::channel::<()>();
@@ -690,15 +710,13 @@ async fn handle_smart_request(
690 // Route: GET /info/refs?service=git-upload-pack 710 // Route: GET /info/refs?service=git-upload-pack
691 if method == hyper::Method::GET && path.ends_with("/info/refs") { 711 if method == hyper::Method::GET && path.ends_with("/info/refs") {
692 // Parse service from query string 712 // Parse service from query string
693 let service = query 713 let service = query.split('&').find_map(|param| {
694 .split('&') 714 let mut parts = param.splitn(2, '=');
695 .find_map(|param| { 715 match (parts.next(), parts.next()) {
696 let mut parts = param.splitn(2, '='); 716 (Some("service"), Some(svc)) => Some(svc),
697 match (parts.next(), parts.next()) { 717 _ => None,
698 (Some("service"), Some(svc)) => Some(svc), 718 }
699 _ => None, 719 });
700 }
701 });
702 720
703 match service { 721 match service {
704 Some("git-upload-pack") => { 722 Some("git-upload-pack") => {
@@ -714,7 +732,9 @@ async fn handle_smart_request(
714 _ => { 732 _ => {
715 return Ok(Response::builder() 733 return Ok(Response::builder()
716 .status(StatusCode::BAD_REQUEST) 734 .status(StatusCode::BAD_REQUEST)
717 .body(Full::new(Bytes::from("Missing or invalid service parameter"))) 735 .body(Full::new(Bytes::from(
736 "Missing or invalid service parameter",
737 )))
718 .unwrap()); 738 .unwrap());
719 } 739 }
720 } 740 }
@@ -740,8 +760,8 @@ async fn handle_info_refs_upload_pack(
740 git_protocol_version: Option<&str>, 760 git_protocol_version: Option<&str>,
741) -> Result<Response<Full<Bytes>>, hyper::Error> { 761) -> Result<Response<Full<Bytes>>, hyper::Error> {
742 use std::process::Stdio; 762 use std::process::Stdio;
743 use tokio::process::Command as TokioCommand;
744 use tokio::io::AsyncReadExt; 763 use tokio::io::AsyncReadExt;
764 use tokio::process::Command as TokioCommand;
745 765
746 // Spawn git upload-pack --advertise-refs 766 // Spawn git upload-pack --advertise-refs
747 let mut cmd = TokioCommand::new("git"); 767 let mut cmd = TokioCommand::new("git");
@@ -763,8 +783,7 @@ async fn handle_info_refs_upload_pack(
763 .stdout(Stdio::piped()) 783 .stdout(Stdio::piped())
764 .stderr(Stdio::piped()); 784 .stderr(Stdio::piped());
765 785
766 let mut child = match cmd.spawn() 786 let mut child = match cmd.spawn() {
767 {
768 Ok(child) => child, 787 Ok(child) => child,
769 Err(e) => { 788 Err(e) => {
770 eprintln!("Failed to spawn git upload-pack: {}", e); 789 eprintln!("Failed to spawn git upload-pack: {}", e);
@@ -800,7 +819,7 @@ async fn handle_info_refs_upload_pack(
800 let len = service_line.len() + 4; 819 let len = service_line.len() + 4;
801 response_body.extend_from_slice(format!("{:04x}", len).as_bytes()); 820 response_body.extend_from_slice(format!("{:04x}", len).as_bytes());
802 response_body.extend_from_slice(service_line.as_bytes()); 821 response_body.extend_from_slice(service_line.as_bytes());
803 822
804 // Flush packet 823 // Flush packet
805 response_body.extend_from_slice(b"0000"); 824 response_body.extend_from_slice(b"0000");
806 825
@@ -809,7 +828,10 @@ async fn handle_info_refs_upload_pack(
809 828
810 Ok(Response::builder() 829 Ok(Response::builder()
811 .status(StatusCode::OK) 830 .status(StatusCode::OK)
812 .header("Content-Type", "application/x-git-upload-pack-advertisement") 831 .header(
832 "Content-Type",
833 "application/x-git-upload-pack-advertisement",
834 )
813 .header("Cache-Control", "no-cache") 835 .header("Cache-Control", "no-cache")
814 .body(Full::new(Bytes::from(response_body))) 836 .body(Full::new(Bytes::from(response_body)))
815 .unwrap()) 837 .unwrap())
@@ -850,8 +872,7 @@ async fn handle_upload_pack(
850 .stdout(Stdio::piped()) 872 .stdout(Stdio::piped())
851 .stderr(Stdio::piped()); 873 .stderr(Stdio::piped());
852 874
853 let mut child = match cmd.spawn() 875 let mut child = match cmd.spawn() {
854 {
855 Ok(child) => child, 876 Ok(child) => child,
856 Err(e) => { 877 Err(e) => {
857 eprintln!("Failed to spawn git upload-pack: {}", e); 878 eprintln!("Failed to spawn git upload-pack: {}", e);
@@ -957,7 +978,10 @@ mod smart_git_server_tests {
957 content_type 978 content_type
958 ); 979 );
959 980
960 let body = response.bytes().await.expect("Failed to read response body"); 981 let body = response
982 .bytes()
983 .await
984 .expect("Failed to read response body");
961 985
962 // Should start with service advertisement pkt-line 986 // Should start with service advertisement pkt-line
963 let body_str = String::from_utf8_lossy(&body); 987 let body_str = String::from_utf8_lossy(&body);
@@ -1077,7 +1101,7 @@ mod smart_git_server_tests {
1077 #[tokio::test] 1101 #[tokio::test]
1078 async fn test_smart_git_server_shallow_fetch() { 1102 async fn test_smart_git_server_shallow_fetch() {
1079 // This is the KEY test - shallow fetch requires smart HTTP protocol 1103 // This is the KEY test - shallow fetch requires smart HTTP protocol
1080 1104
1081 // Create a source repo with a commit 1105 // Create a source repo with a commit
1082 let source_dir = tempfile::tempdir().expect("Failed to create source dir"); 1106 let source_dir = tempfile::tempdir().expect("Failed to create source dir");
1083 let commit_hash = create_test_repo_with_commit(source_dir.path(), CommitVariant::StateTest) 1107 let commit_hash = create_test_repo_with_commit(source_dir.path(), CommitVariant::StateTest)
diff --git a/tests/common/mock_relay.rs b/tests/common/mock_relay.rs
index b6376a7..e81c453 100644
--- a/tests/common/mock_relay.rs
+++ b/tests/common/mock_relay.rs
@@ -73,8 +73,8 @@ impl MockRelay {
73 /// in an in-memory database. 73 /// in an in-memory database.
74 pub async fn start() -> Self { 74 pub async fn start() -> Self {
75 // Create and bind listener (eliminates port race condition) 75 // Create and bind listener (eliminates port race condition)
76 let std_listener = std::net::TcpListener::bind("127.0.0.1:0") 76 let std_listener =
77 .expect("Failed to bind to random port"); 77 std::net::TcpListener::bind("127.0.0.1:0").expect("Failed to bind to random port");
78 let port = std_listener 78 let port = std_listener
79 .local_addr() 79 .local_addr()
80 .expect("Failed to get local addr") 80 .expect("Failed to get local addr")
@@ -84,8 +84,8 @@ impl MockRelay {
84 std_listener 84 std_listener
85 .set_nonblocking(true) 85 .set_nonblocking(true)
86 .expect("Failed to set non-blocking"); 86 .expect("Failed to set non-blocking");
87 let listener = TcpListener::from_std(std_listener) 87 let listener =
88 .expect("Failed to convert to tokio listener"); 88 TcpListener::from_std(std_listener).expect("Failed to convert to tokio listener");
89 89
90 Self::start_with_listener(listener, port).await 90 Self::start_with_listener(listener, port).await
91 } 91 }
@@ -258,7 +258,10 @@ fn derive_accept_key(request_key: &[u8]) -> String {
258 engine.input(request_key); 258 engine.input(request_key);
259 engine.input(WS_GUID); 259 engine.input(WS_GUID);
260 let hash = Sha1Hash::from_engine(engine); 260 let hash = Sha1Hash::from_engine(engine);
261 base64::Engine::encode(&base64::engine::general_purpose::STANDARD, hash.as_byte_array()) 261 base64::Engine::encode(
262 &base64::engine::general_purpose::STANDARD,
263 hash.as_byte_array(),
264 )
262} 265}
263 266
264/// Wait for the server to be ready to accept connections. 267/// Wait for the server to be ready to accept connections.
@@ -275,10 +278,7 @@ async fn wait_for_server_ready(port: u16) {
275 } 278 }
276 Err(_) => { 279 Err(_) => {
277 if attempt == max_attempts - 1 { 280 if attempt == max_attempts - 1 {
278 panic!( 281 panic!("MockRelay failed to start after {} attempts", max_attempts);
279 "MockRelay failed to start after {} attempts",
280 max_attempts
281 );
282 } 282 }
283 tokio::time::sleep(delay).await; 283 tokio::time::sleep(delay).await;
284 } 284 }
@@ -309,7 +309,10 @@ mod tests {
309 // Create a client and connect 309 // Create a client and connect
310 let keys = Keys::generate(); 310 let keys = Keys::generate();
311 let client = Client::new(keys.clone()); 311 let client = Client::new(keys.clone());
312 client.add_relay(mock.url()).await.expect("Failed to add relay"); 312 client
313 .add_relay(mock.url())
314 .await
315 .expect("Failed to add relay");
313 client.connect().await; 316 client.connect().await;
314 317
315 // Wait for connection 318 // Wait for connection
diff --git a/tests/common/purgatory_helpers.rs b/tests/common/purgatory_helpers.rs
index 125f485..b39982e 100644
--- a/tests/common/purgatory_helpers.rs
+++ b/tests/common/purgatory_helpers.rs
@@ -51,7 +51,7 @@ pub fn create_test_repo_with_commit(path: &Path, variant: CommitVariant) -> Resu
51 // Configure git user for commits 51 // Configure git user for commits
52 run_git(path, &["config", "user.email", "test@example.com"])?; 52 run_git(path, &["config", "user.email", "test@example.com"])?;
53 run_git(path, &["config", "user.name", "Test User"])?; 53 run_git(path, &["config", "user.name", "Test User"])?;
54 54
55 // Disable GPG signing for tests (prevents yubikey prompts) 55 // Disable GPG signing for tests (prevents yubikey prompts)
56 run_git(path, &["config", "commit.gpgsign", "false"])?; 56 run_git(path, &["config", "commit.gpgsign", "false"])?;
57 run_git(path, &["config", "tag.gpgsign", "false"])?; 57 run_git(path, &["config", "tag.gpgsign", "false"])?;
@@ -710,7 +710,8 @@ mod tests {
710 // Check d-tag 710 // Check d-tag
711 let has_d_tag = event.tags.iter().any(|tag| { 711 let has_d_tag = event.tags.iter().any(|tag| {
712 let slice = tag.as_slice(); 712 let slice = tag.as_slice();
713 slice.first().is_some_and(|t| t == "d") && slice.get(1).is_some_and(|v| v == "test-repo") 713 slice.first().is_some_and(|t| t == "d")
714 && slice.get(1).is_some_and(|v| v == "test-repo")
714 }); 715 });
715 assert!(has_d_tag, "Event should have 'd' tag with identifier"); 716 assert!(has_d_tag, "Event should have 'd' tag with identifier");
716 717
@@ -751,7 +752,8 @@ mod tests {
751 // Check a-tag 752 // Check a-tag
752 let has_a_tag = event.tags.iter().any(|tag| { 753 let has_a_tag = event.tags.iter().any(|tag| {
753 let slice = tag.as_slice(); 754 let slice = tag.as_slice();
754 slice.first().is_some_and(|t| t == "a") && slice.get(1).is_some_and(|v| v == &repo_coord) 755 slice.first().is_some_and(|t| t == "a")
756 && slice.get(1).is_some_and(|v| v == &repo_coord)
755 }); 757 });
756 assert!(has_a_tag, "Event should have 'a' tag"); 758 assert!(has_a_tag, "Event should have 'a' tag");
757 759
@@ -806,7 +808,10 @@ mod tests {
806 &repo_coord, 808 &repo_coord,
807 "abc123def456", 809 "abc123def456",
808 "Test PR with clone", 810 "Test PR with clone",
809 &["http://fork-server.com/repo.git", "http://another-server.com/repo.git"], 811 &[
812 "http://fork-server.com/repo.git",
813 "http://another-server.com/repo.git",
814 ],
810 ) 815 )
811 .expect("Failed to create PR event with clone"); 816 .expect("Failed to create PR event with clone");
812 817
@@ -815,7 +820,8 @@ mod tests {
815 // Check a-tag 820 // Check a-tag
816 let has_a_tag = event.tags.iter().any(|tag| { 821 let has_a_tag = event.tags.iter().any(|tag| {
817 let slice = tag.as_slice(); 822 let slice = tag.as_slice();
818 slice.first().is_some_and(|t| t == "a") && slice.get(1).is_some_and(|v| v == &repo_coord) 823 slice.first().is_some_and(|t| t == "a")
824 && slice.get(1).is_some_and(|v| v == &repo_coord)
819 }); 825 });
820 assert!(has_a_tag, "Event should have 'a' tag"); 826 assert!(has_a_tag, "Event should have 'a' tag");
821 827
@@ -831,8 +837,12 @@ mod tests {
831 let has_clone_tag = event.tags.iter().any(|tag| { 837 let has_clone_tag = event.tags.iter().any(|tag| {
832 let slice = tag.as_slice(); 838 let slice = tag.as_slice();
833 slice.first().is_some_and(|t| t == "clone") 839 slice.first().is_some_and(|t| t == "clone")
834 && slice.get(1).is_some_and(|v| v == "http://fork-server.com/repo.git") 840 && slice
835 && slice.get(2).is_some_and(|v| v == "http://another-server.com/repo.git") 841 .get(1)
842 .is_some_and(|v| v == "http://fork-server.com/repo.git")
843 && slice
844 .get(2)
845 .is_some_and(|v| v == "http://another-server.com/repo.git")
836 }); 846 });
837 assert!(has_clone_tag, "Event should have 'clone' tag with URLs"); 847 assert!(has_clone_tag, "Event should have 'clone' tag with URLs");
838 } 848 }
@@ -855,6 +865,9 @@ mod tests {
855 let slice = tag.as_slice(); 865 let slice = tag.as_slice();
856 slice.first().is_some_and(|t| t == "clone") 866 slice.first().is_some_and(|t| t == "clone")
857 }); 867 });
858 assert!(!has_clone_tag, "Event should not have 'clone' tag when no URLs provided"); 868 assert!(
869 !has_clone_tag,
870 "Event should not have 'clone' tag when no URLs provided"
871 );
859 } 872 }
860} 873}
diff --git a/tests/common/relay.rs b/tests/common/relay.rs
index 8d20da6..fb5d421 100644
--- a/tests/common/relay.rs
+++ b/tests/common/relay.rs
@@ -144,7 +144,10 @@ impl TestRelay {
144 .env("NGIT_SYNC_STARTUP_JITTER_MS", "0") // No jitter for tests 144 .env("NGIT_SYNC_STARTUP_JITTER_MS", "0") // No jitter for tests
145 .env("NGIT_SYNC_DISCONNECT_CHECK_INTERVAL_SECS", "1") // Fast reconnect attempts for tests 145 .env("NGIT_SYNC_DISCONNECT_CHECK_INTERVAL_SECS", "1") // Fast reconnect attempts for tests
146 .env("NGIT_SYNC_BASE_BACKOFF_SECS", "1") // Fast backoff for tests (1s instead of 5s default) 146 .env("NGIT_SYNC_BASE_BACKOFF_SECS", "1") // Fast backoff for tests (1s instead of 5s default)
147 .env("RUST_LOG", std::env::var("RUST_LOG").unwrap_or_else(|_| "info".to_string())) // Use RUST_LOG from environment or default to info 147 .env(
148 "RUST_LOG",
149 std::env::var("RUST_LOG").unwrap_or_else(|_| "info".to_string()),
150 ) // Use RUST_LOG from environment or default to info
148 .stdout(Stdio::null()) // Suppress stdout for cleaner test output 151 .stdout(Stdio::null()) // Suppress stdout for cleaner test output
149 .stderr(Stdio::null()); // Suppress stderr for cleaner test output 152 .stderr(Stdio::null()); // Suppress stderr for cleaner test output
150 153