mod common; use gitks::bare::GitBare; use gitks::pb::repository_service_server::RepositoryService; use gitks::pb::*; fn header(gb: &GitBare) -> RepositoryHeader { let name = gb .bare_dir .file_name() .unwrap() .to_string_lossy() .into_owned(); RepositoryHeader { relative_path: name, ..Default::default() } } fn req(gb: &GitBare, f: impl FnOnce(RepositoryHeader) -> T) -> tonic::Request { tonic::Request::new(f(header(gb))) } #[tokio::test] async fn test_get_repository() { let (dir, gb) = common::setup_bare_repo(); let svc = common::setup_service(dir.path()); let repo = svc .get_repository(req(&gb, |r| GetRepositoryRequest { repository: Some(r), })) .await .unwrap() .into_inner(); assert!(repo.bare); assert_eq!(repo.object_format, ObjectFormat::Sha1 as i32); assert_eq!(repo.default_branch, "main"); } #[tokio::test] async fn test_init_and_delete_repository() { let dir = tempfile::tempdir().unwrap(); let svc = common::setup_service(dir.path()); let hdr = RepositoryHeader { relative_path: "new-repo".into(), ..Default::default() }; svc.init_repository(tonic::Request::new(InitRepositoryRequest { repository: Some(hdr.clone()), bare: true, ..Default::default() })) .await .unwrap(); assert!( svc.repository_exists(tonic::Request::new(RepositoryExistsRequest { repository: Some(hdr.clone()) })) .await .unwrap() .into_inner() .exists ); svc.delete_repository(tonic::Request::new(DeleteRepositoryRequest { repository: Some(hdr.clone()), })) .await .unwrap(); assert!( !svc.repository_exists(tonic::Request::new(RepositoryExistsRequest { repository: Some(hdr) })) .await .unwrap() .into_inner() .exists ); } #[tokio::test] async fn test_get_object_format() { let (dir, gb) = common::setup_bare_repo(); let svc = common::setup_service(dir.path()); let resp = svc .get_object_format(req(&gb, |r| RepositoryObjectFormatRequest { repository: Some(r), })) .await .unwrap() .into_inner(); assert_eq!(resp.object_format, ObjectFormat::Sha1 as i32); } #[tokio::test] async fn test_get_set_default_branch() { let (dir, gb) = common::setup_bare_repo(); let svc = common::setup_service(dir.path()); let h = header(&gb); assert_eq!( svc.get_default_branch(tonic::Request::new(GetDefaultBranchRequest { repository: Some(h.clone()) })) .await .unwrap() .into_inner() .name, "main" ); svc.set_default_branch(tonic::Request::new(SetDefaultBranchRequest { repository: Some(h.clone()), name: "feature".into(), })) .await .unwrap(); assert_eq!( svc.get_default_branch(tonic::Request::new(GetDefaultBranchRequest { repository: Some(h) })) .await .unwrap() .into_inner() .name, "feature" ); } #[tokio::test] async fn test_get_set_repository_config() { let (dir, gb) = common::setup_bare_repo(); let svc = common::setup_service(dir.path()); svc.set_repository_config(tonic::Request::new(SetRepositoryConfigRequest { repository: Some(header(&gb)), entries: vec![RepositoryConfigEntry { key: "test.key".into(), values: vec!["val1".into(), "val2".into()], }], })) .await .unwrap(); let entry = svc .get_repository_config(tonic::Request::new(GetRepositoryConfigRequest { repository: Some(header(&gb)), keys: vec!["test.key".into()], })) .await .unwrap() .into_inner() .entries .into_iter() .find(|e| e.key == "test.key") .unwrap(); assert_eq!(entry.values, vec!["val1", "val2"]); } #[tokio::test] async fn test_get_repository_statistics() { let (dir, gb) = common::setup_bare_repo(); let svc = common::setup_service(dir.path()); let s = svc .get_repository_statistics(req(&gb, |r| RepositoryStatisticsRequest { repository: Some(r), })) .await .unwrap() .into_inner(); assert!(s.size_bytes > 0); assert!(s.loose_object_count > 0 || s.packed_object_count > 0); assert!(s.reference_count >= 2); } #[tokio::test] async fn test_check_repository_health() { let (dir, gb) = common::setup_bare_repo(); let svc = common::setup_service(dir.path()); let h = svc .check_repository_health(tonic::Request::new(RepositoryHealthRequest { repository: Some(header(&gb)), connectivity_only: true, })) .await .unwrap() .into_inner(); assert!(h.ok && h.errors.is_empty()); } #[tokio::test] async fn test_garbage_collect() { let (dir, gb) = common::setup_bare_repo(); let svc = common::setup_service(dir.path()); assert!( svc.garbage_collect(tonic::Request::new(GarbageCollectRequest { repository: Some(header(&gb)), ..Default::default() })) .await .unwrap() .into_inner() .ok ); } #[tokio::test] async fn test_repack() { let (dir, gb) = common::setup_bare_repo(); let svc = common::setup_service(dir.path()); assert!( svc.repack(tonic::Request::new(RepackRequest { repository: Some(header(&gb)), ..Default::default() })) .await .unwrap() .into_inner() .ok ); } #[tokio::test] async fn test_write_commit_graph() { let (dir, gb) = common::setup_bare_repo(); let svc = common::setup_service(dir.path()); assert!( svc.write_commit_graph(tonic::Request::new(WriteCommitGraphRequest { repository: Some(header(&gb)), ..Default::default() })) .await .unwrap() .into_inner() .ok ); } #[tokio::test] async fn test_resolve_none_header() { let dir = tempfile::tempdir().unwrap(); let svc = common::setup_service(dir.path()); let result = svc .get_repository(tonic::Request::new(GetRepositoryRequest { repository: None, })) .await; assert!(result.is_err(), "should fail with None header"); let err = result.unwrap_err(); assert_eq!(err.code(), tonic::Code::InvalidArgument); } #[tokio::test] async fn test_resolve_empty_relative_path() { let dir = tempfile::tempdir().unwrap(); let svc = common::setup_service(dir.path()); let result = svc .get_repository(tonic::Request::new(GetRepositoryRequest { repository: Some(RepositoryHeader { relative_path: String::new(), ..Default::default() }), })) .await; assert!(result.is_err(), "should fail with empty relative_path"); } #[tokio::test] async fn test_resolve_nonexistent_repo() { let dir = tempfile::tempdir().unwrap(); let svc = common::setup_service(dir.path()); let result = svc .get_repository(tonic::Request::new(GetRepositoryRequest { repository: Some(RepositoryHeader { relative_path: "does-not-exist".into(), ..Default::default() }), })) .await; assert!(result.is_err(), "should fail for nonexistent repo"); } #[tokio::test] async fn test_init_empty_relative_path() { let dir = tempfile::tempdir().unwrap(); let svc = common::setup_service(dir.path()); let result = svc .init_repository(tonic::Request::new(InitRepositoryRequest { repository: Some(RepositoryHeader { relative_path: String::new(), ..Default::default() }), bare: true, ..Default::default() })) .await; assert!(result.is_err(), "should fail with empty relative_path"); } #[tokio::test] async fn test_delete_nonexistent_repo() { let dir = tempfile::tempdir().unwrap(); let svc = common::setup_service(dir.path()); // Deleting a non-existent path should succeed (fs::remove_dir_all on non-existent is ok) // or fail gracefully let result = svc .delete_repository(tonic::Request::new(DeleteRepositoryRequest { repository: Some(RepositoryHeader { relative_path: "ghost-repo".into(), ..Default::default() }), })) .await; // It either succeeds (dir doesn't exist, nothing to delete) or fails // Both are acceptable behaviors let _ = result; } #[tokio::test] async fn test_exists_nonexistent_repo() { let dir = tempfile::tempdir().unwrap(); let svc = common::setup_service(dir.path()); let result = svc .repository_exists(tonic::Request::new(RepositoryExistsRequest { repository: Some(RepositoryHeader { relative_path: "nonexistent".into(), ..Default::default() }), })) .await .unwrap() .into_inner(); assert!(!result.exists); }