mod common; use gitks::pb::archive_service_server::ArchiveService; use gitks::pb::pack_service_server::PackService; use gitks::pb::*; fn hdr(name: &str) -> RepositoryHeader { RepositoryHeader { relative_path: name.into(), ..Default::default() } } #[tokio::test] async fn test_get_archive_tar() { let (dir, gb) = common::setup_bare_repo(); let svc = common::setup_service(dir.path()); let chunks = svc .get_archive(tonic::Request::new(ArchiveRequest { repository: Some(hdr("test-repo")), treeish: Some(ObjectSelector { selector: Some(object_selector::Selector::Revision(ObjectName { revision: "main".into(), })), }), options: Some(ArchiveOptions { format: archive_options::Format::ArchiveFormatTar as i32, ..Default::default() }), })) .await .unwrap() .into_inner(); let chunks: Vec<_> = tokio_stream::StreamExt::collect(chunks).await; assert!(!chunks.is_empty(), "should produce archive data"); let total_size: usize = chunks.iter().map(|c| c.as_ref().unwrap().data.len()).sum(); assert!(total_size > 0, "archive should not be empty"); } #[tokio::test] async fn test_get_archive_zip() { let (dir, gb) = common::setup_bare_repo(); let svc = common::setup_service(dir.path()); let chunks = svc .get_archive(tonic::Request::new(ArchiveRequest { repository: Some(hdr("test-repo")), treeish: Some(ObjectSelector { selector: Some(object_selector::Selector::Revision(ObjectName { revision: "main".into(), })), }), options: Some(ArchiveOptions { format: archive_options::Format::ArchiveFormatZip as i32, ..Default::default() }), })) .await .unwrap() .into_inner(); let chunks: Vec<_> = tokio_stream::StreamExt::collect(chunks).await; assert!(!chunks.is_empty()); let data = &chunks[0].as_ref().unwrap().data; assert!( data.starts_with(b"PK"), "zip archive should start with PK magic bytes" ); } #[tokio::test] async fn test_list_archive_entries() { let (dir, gb) = common::setup_bare_repo(); let svc = common::setup_service(dir.path()); let result = svc .list_archive_entries(tonic::Request::new(ListArchiveEntriesRequest { repository: Some(hdr("test-repo")), treeish: Some(ObjectSelector { selector: Some(object_selector::Selector::Revision(ObjectName { revision: "main".into(), })), }), pathspec: vec![], pagination: None, })) .await .unwrap() .into_inner(); assert!(!result.entries.is_empty(), "should list entries"); let paths: Vec<&str> = result.entries.iter().map(|e| e.path.as_str()).collect(); assert!( paths.iter().any(|p| p.contains("README.md")), "should include README.md, got: {:?}", paths ); } #[tokio::test] async fn test_get_archive_with_prefix() { let (dir, gb) = common::setup_bare_repo(); let svc = common::setup_service(dir.path()); let chunks = svc .get_archive(tonic::Request::new(ArchiveRequest { repository: Some(hdr("test-repo")), treeish: Some(ObjectSelector { selector: Some(object_selector::Selector::Revision(ObjectName { revision: "main".into(), })), }), options: Some(ArchiveOptions { format: archive_options::Format::ArchiveFormatTar as i32, prefix: "project/".into(), ..Default::default() }), })) .await .unwrap() .into_inner(); let chunks: Vec<_> = tokio_stream::StreamExt::collect(chunks).await; assert!(!chunks.is_empty()); } #[tokio::test] async fn test_fsck_clean_repo() { let (dir, gb) = common::setup_bare_repo(); let svc = common::setup_service(dir.path()); let result = svc .fsck(tonic::Request::new(FsckRequest { repository: Some(hdr("test-repo")), strict: false, connectivity_only: false, })) .await .unwrap() .into_inner(); assert!(result.ok); assert!(result.errors.is_empty()); } #[tokio::test] async fn test_fsck_strict() { let (dir, _gb) = common::setup_bare_repo(); let svc = common::setup_service(dir.path()); let result = svc .fsck(tonic::Request::new(FsckRequest { repository: Some(hdr("test-repo")), strict: true, connectivity_only: false, })) .await .unwrap() .into_inner(); assert!(result.ok, "strict fsck should pass on clean repo"); } #[tokio::test] async fn test_fsck_connectivity_only() { let (dir, _gb) = common::setup_bare_repo(); let svc = common::setup_service(dir.path()); let result = svc .fsck(tonic::Request::new(FsckRequest { repository: Some(hdr("test-repo")), strict: false, connectivity_only: true, })) .await .unwrap() .into_inner(); assert!(result.ok, "connectivity-only fsck should pass"); } #[tokio::test] async fn test_list_packfiles() { let (dir, gb) = common::setup_bare_repo(); let svc = common::setup_service(dir.path()); duct::cmd( "git", [ "--git-dir", gb.bare_dir.to_string_lossy().as_ref(), "gc", "--aggressive", ], ) .run() .expect("git gc"); let result = svc .list_packfiles(tonic::Request::new(ListPackfilesRequest { repository: Some(hdr("test-repo")), pagination: None, })) .await .unwrap() .into_inner(); assert!( !result.packfiles.is_empty(), "bare repo should have packfiles after gc" ); for pf in &result.packfiles { assert!(pf.size_bytes > 0, "packfile should have size"); assert!(pf.name.ends_with(".pack")); } } #[tokio::test] async fn test_advertise_refs() { let (dir, gb) = common::setup_bare_repo(); let svc = common::setup_service(dir.path()); let result = svc .advertise_refs(tonic::Request::new(AdvertiseRefsRequest { repository: Some(hdr("test-repo")), protocol: None, service: String::new(), })) .await .unwrap() .into_inner(); assert!(!result.references.is_empty(), "should have refs"); let ref_names: Vec<&str> = result.references.iter().map(|r| r.name.as_str()).collect(); assert!( ref_names.iter().any(|n| n.contains("refs/heads/main")), "should include main branch ref" ); assert!( !result.capabilities.is_empty(), "should advertise capabilities" ); }