mod common; use gitks::pb::tree_service_server::TreeService; use gitks::pb::*; fn hdr() -> RepositoryHeader { RepositoryHeader { relative_path: "test-repo".into(), ..Default::default() } } #[tokio::test] async fn test_list_tree_recursive() { let (dir, _gb) = common::setup_bare_repo(); let svc = common::setup_service(dir.path()); let result = svc .list_tree(tonic::Request::new(ListTreeRequest { repository: Some(hdr()), revision: Some(ObjectSelector { selector: Some(object_selector::Selector::Revision(ObjectName { revision: "main".into(), })), }), path: String::new(), recursive: true, pagination: None, })) .await .unwrap() .into_inner(); let paths: Vec = result.entries.iter().map(|e| e.path.clone()).collect(); assert!( paths.iter().any(|p| p.contains("src/lib/mod.rs")), "should include nested files, got: {:?}", paths ); } #[tokio::test] async fn test_get_tree_subpath() { let (dir, _gb) = common::setup_bare_repo(); let svc = common::setup_service(dir.path()); let result = svc .get_tree(tonic::Request::new(GetTreeRequest { repository: Some(hdr()), revision: Some(ObjectSelector { selector: Some(object_selector::Selector::Revision(ObjectName { revision: "main".into(), })), }), path: "src".into(), })) .await .unwrap() .into_inner(); assert!(result.oid.is_some()); let root_tree = svc .get_tree(tonic::Request::new(GetTreeRequest { repository: Some(hdr()), revision: Some(ObjectSelector { selector: Some(object_selector::Selector::Revision(ObjectName { revision: "main".into(), })), }), path: String::new(), })) .await .unwrap() .into_inner(); assert_ne!( result.oid.unwrap().hex, root_tree.oid.unwrap().hex, "subtree OID should differ from root" ); } #[tokio::test] async fn test_find_files() { let (dir, _gb) = common::setup_bare_repo(); let svc = common::setup_service(dir.path()); let result = svc .find_files(tonic::Request::new(FindFilesRequest { repository: Some(hdr()), revision: Some(ObjectSelector { selector: Some(object_selector::Selector::Revision(ObjectName { revision: "main".into(), })), }), pattern: "mod.rs".into(), pathspec: vec![], pagination: None, })) .await .unwrap() .into_inner(); assert!(!result.files.is_empty()); assert!(result.files.iter().all(|f| f.path.contains("mod.rs"))); } #[tokio::test] async fn test_get_blob() { let (dir, _gb) = common::setup_bare_repo(); let svc = common::setup_service(dir.path()); let blob = svc .get_blob(tonic::Request::new(GetBlobRequest { repository: Some(hdr()), revision: Some(ObjectSelector { selector: Some(object_selector::Selector::Revision(ObjectName { revision: "main".into(), })), }), path: "README.md".into(), oid: None, max_bytes: 0, })) .await .unwrap() .into_inner(); let content = String::from_utf8_lossy(&blob.data); assert!(content.contains("# Test")); assert!(blob.size > 0); assert!(!blob.binary); } #[tokio::test] async fn test_get_blob_with_truncation() { let (dir, _gb) = common::setup_bare_repo(); let svc = common::setup_service(dir.path()); let blob = svc .get_blob(tonic::Request::new(GetBlobRequest { repository: Some(hdr()), revision: Some(ObjectSelector { selector: Some(object_selector::Selector::Revision(ObjectName { revision: "main".into(), })), }), path: "README.md".into(), oid: None, max_bytes: 5, })) .await .unwrap() .into_inner(); assert_eq!(blob.data.len(), 5); assert!(blob.truncated); assert!( blob.size > 5, "size should be original size, not truncated size" ); } #[tokio::test] async fn test_get_file_metadata() { let (dir, _gb) = common::setup_bare_repo(); let svc = common::setup_service(dir.path()); let meta = svc .get_file_metadata(tonic::Request::new(GetFileMetadataRequest { repository: Some(hdr()), revision: Some(ObjectSelector { selector: Some(object_selector::Selector::Revision(ObjectName { revision: "main".into(), })), }), path: "README.md".into(), })) .await .unwrap() .into_inner(); assert_eq!(meta.path, "README.md"); assert!(meta.oid.is_some()); assert_eq!(meta.r#type, ObjectType::Blob as i32); } #[tokio::test] async fn test_list_tree_with_pagination() { let (dir, _gb) = common::setup_bare_repo(); let svc = common::setup_service(dir.path()); let result = svc .list_tree(tonic::Request::new(ListTreeRequest { repository: Some(hdr()), revision: Some(ObjectSelector { selector: Some(object_selector::Selector::Revision(ObjectName { revision: "main".into(), })), }), path: String::new(), recursive: false, pagination: Some(Pagination { page_size: 1, page_token: String::new(), }), })) .await .unwrap() .into_inner(); assert_eq!(result.entries.len(), 1); let pi = result.page_info.unwrap(); assert!(pi.has_next_page); } #[tokio::test] async fn test_get_raw_blob() { let (dir, _gb) = common::setup_bare_repo(); let svc = common::setup_service(dir.path()); let stream = svc .get_raw_blob(tonic::Request::new(GetRawBlobRequest { repository: Some(hdr()), revision: Some(ObjectSelector { selector: Some(object_selector::Selector::Revision(ObjectName { revision: "main".into(), })), }), path: "README.md".into(), oid: None, })) .await .unwrap() .into_inner(); let chunks: Vec<_> = tokio_stream::StreamExt::collect(stream).await; assert!(!chunks.is_empty(), "should have raw blob data"); let data = &chunks[0].as_ref().unwrap().data; assert!(!data.is_empty(), "raw blob should not be empty"); let content = String::from_utf8_lossy(data); assert!(content.contains("# Test")); }