10a4398e81
- Remove unnecessary sorting in advertise_refs for deterministic output - Add path traversal detection and validation in bare_dir construction - Implement symlink resolution checks to prevent security vulnerabilities - Refactor cache system with CRC validation and improved metrics - Integrate repo-specific cache invalidation using indexed keys - Add comprehensive unit tests for commit operations and diff functionality - Move configuration constants to centralized config module - Optimize string operations in disk cache random value generation - Enhance license detection algorithm with cleaner matching logic - Streamline argument processing in various git operations - Update dependencies including crc32fast and flate2 for performance - Add signal handling capability to tokio runtime configuration
345 lines
10 KiB
Rust
345 lines
10 KiB
Rust
mod common;
|
|
|
|
use gitks::pb::commit_service_server::CommitService;
|
|
use gitks::pb::diff_service_server::DiffService;
|
|
use gitks::pb::*;
|
|
|
|
fn hdr() -> RepositoryHeader {
|
|
RepositoryHeader {
|
|
relative_path: "test-repo".into(),
|
|
..Default::default()
|
|
}
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_get_diff() {
|
|
let (dir, _gb) = common::setup_bare_repo();
|
|
let svc = common::setup_service(dir.path());
|
|
let result = svc
|
|
.get_diff(tonic::Request::new(GetDiffRequest {
|
|
repository: Some(hdr()),
|
|
base: Some(ObjectSelector {
|
|
selector: Some(object_selector::Selector::Revision(ObjectName {
|
|
revision: "main~3".into(),
|
|
})),
|
|
}),
|
|
head: Some(ObjectSelector {
|
|
selector: Some(object_selector::Selector::Revision(ObjectName {
|
|
revision: "main".into(),
|
|
})),
|
|
}),
|
|
options: None,
|
|
pagination: None,
|
|
}))
|
|
.await
|
|
.unwrap()
|
|
.into_inner();
|
|
|
|
assert!(!result.files.is_empty());
|
|
let paths: Vec<&str> = result.files.iter().map(|f| f.new_path.as_str()).collect();
|
|
assert!(
|
|
paths.iter().any(|p| p.contains("src.txt")),
|
|
"should include src.txt, got: {:?}",
|
|
paths
|
|
);
|
|
let stats = result.stats.unwrap();
|
|
assert!(stats.additions > 0 || stats.changed_files > 0);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_get_diff_with_patch() {
|
|
let (dir, _gb) = common::setup_bare_repo();
|
|
let svc = common::setup_service(dir.path());
|
|
let result = svc
|
|
.get_diff(tonic::Request::new(GetDiffRequest {
|
|
repository: Some(hdr()),
|
|
base: Some(ObjectSelector {
|
|
selector: Some(object_selector::Selector::Revision(ObjectName {
|
|
revision: "main~1".into(),
|
|
})),
|
|
}),
|
|
head: Some(ObjectSelector {
|
|
selector: Some(object_selector::Selector::Revision(ObjectName {
|
|
revision: "main".into(),
|
|
})),
|
|
}),
|
|
options: Some(DiffOptions {
|
|
include_patch: true,
|
|
context_lines: 3,
|
|
..Default::default()
|
|
}),
|
|
pagination: None,
|
|
}))
|
|
.await
|
|
.unwrap()
|
|
.into_inner();
|
|
|
|
assert!(!result.files.is_empty());
|
|
for file in &result.files {
|
|
if !file.binary {
|
|
assert!(
|
|
!file.patch.is_empty(),
|
|
"non-binary file should have patch: {}",
|
|
file.new_path
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_get_diff_with_rename_detection() {
|
|
let (dir, _gb) = common::setup_bare_repo();
|
|
let svc = common::setup_service(dir.path());
|
|
|
|
svc.create_commit(tonic::Request::new(CreateCommitRequest {
|
|
repository: Some(hdr()),
|
|
branch: "main".into(),
|
|
message: "rename file".into(),
|
|
author: None,
|
|
committer: None,
|
|
actions: vec![
|
|
CreateCommitAction {
|
|
action: create_commit_action::Action::CreateCommitActionCreate as i32,
|
|
file_path: "renamed.txt".into(),
|
|
previous_path: String::new(),
|
|
content: b"source\n".to_vec(),
|
|
encoding: String::new(),
|
|
executable: false,
|
|
last_commit_oid: None,
|
|
},
|
|
CreateCommitAction {
|
|
action: create_commit_action::Action::CreateCommitActionDelete as i32,
|
|
file_path: "src.txt".into(),
|
|
previous_path: String::new(),
|
|
content: vec![],
|
|
encoding: String::new(),
|
|
executable: false,
|
|
last_commit_oid: None,
|
|
},
|
|
],
|
|
start_revision: Some(ObjectSelector {
|
|
selector: Some(object_selector::Selector::Revision(ObjectName {
|
|
revision: "main".into(),
|
|
})),
|
|
}),
|
|
force: false,
|
|
trailers: vec![],
|
|
}))
|
|
.await
|
|
.unwrap();
|
|
|
|
let result = svc
|
|
.get_diff(tonic::Request::new(GetDiffRequest {
|
|
repository: Some(hdr()),
|
|
base: Some(ObjectSelector {
|
|
selector: Some(object_selector::Selector::Revision(ObjectName {
|
|
revision: "main~1".into(),
|
|
})),
|
|
}),
|
|
head: Some(ObjectSelector {
|
|
selector: Some(object_selector::Selector::Revision(ObjectName {
|
|
revision: "main".into(),
|
|
})),
|
|
}),
|
|
options: Some(DiffOptions {
|
|
rename_detection: true,
|
|
..Default::default()
|
|
}),
|
|
pagination: None,
|
|
}))
|
|
.await
|
|
.unwrap()
|
|
.into_inner();
|
|
|
|
let has_rename = result
|
|
.files
|
|
.iter()
|
|
.any(|f| f.change_type == diff_file::ChangeType::DiffFileChangeTypeRenamed as i32);
|
|
assert!(
|
|
has_rename,
|
|
"should detect rename, files: {:?}",
|
|
result.files.len()
|
|
);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_get_commit_diff_root() {
|
|
let (dir, _gb) = common::setup_bare_repo();
|
|
let svc = common::setup_service(dir.path());
|
|
let commits = svc
|
|
.list_commits(tonic::Request::new(ListCommitsRequest {
|
|
repository: Some(hdr()),
|
|
revision: Some(ObjectSelector {
|
|
selector: Some(object_selector::Selector::Revision(ObjectName {
|
|
revision: "main".into(),
|
|
})),
|
|
}),
|
|
path: String::new(),
|
|
since: None,
|
|
until: None,
|
|
first_parent: false,
|
|
all: false,
|
|
reverse: true,
|
|
max_parents: 0,
|
|
min_parents: 0,
|
|
pagination: Some(Pagination {
|
|
page_size: 1,
|
|
page_token: String::new(),
|
|
}),
|
|
}))
|
|
.await
|
|
.unwrap()
|
|
.into_inner();
|
|
let root_oid = commits.commits[0].oid.as_ref().unwrap().hex.clone();
|
|
|
|
let result = svc
|
|
.get_commit_diff(tonic::Request::new(GetCommitDiffRequest {
|
|
repository: Some(hdr()),
|
|
commit: Some(ObjectSelector {
|
|
selector: Some(object_selector::Selector::Revision(ObjectName {
|
|
revision: root_oid,
|
|
})),
|
|
}),
|
|
options: None,
|
|
pagination: None,
|
|
}))
|
|
.await
|
|
.unwrap()
|
|
.into_inner();
|
|
|
|
assert!(!result.files.is_empty(), "root commit should have files");
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_get_diff_stats() {
|
|
let (dir, _gb) = common::setup_bare_repo();
|
|
let svc = common::setup_service(dir.path());
|
|
let stats = svc
|
|
.get_diff_stats(tonic::Request::new(GetDiffStatsRequest {
|
|
repository: Some(hdr()),
|
|
base: Some(ObjectSelector {
|
|
selector: Some(object_selector::Selector::Revision(ObjectName {
|
|
revision: "main~3".into(),
|
|
})),
|
|
}),
|
|
head: Some(ObjectSelector {
|
|
selector: Some(object_selector::Selector::Revision(ObjectName {
|
|
revision: "main".into(),
|
|
})),
|
|
}),
|
|
options: None,
|
|
}))
|
|
.await
|
|
.unwrap()
|
|
.into_inner();
|
|
assert!(stats.additions > 0 || stats.changed_files > 0);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_get_patch() {
|
|
let (dir, _gb) = common::setup_bare_repo();
|
|
let svc = common::setup_service(dir.path());
|
|
let stream = svc
|
|
.get_patch(tonic::Request::new(GetPatchRequest {
|
|
repository: Some(hdr()),
|
|
base: Some(ObjectSelector {
|
|
selector: Some(object_selector::Selector::Revision(ObjectName {
|
|
revision: "main~1".into(),
|
|
})),
|
|
}),
|
|
head: Some(ObjectSelector {
|
|
selector: Some(object_selector::Selector::Revision(ObjectName {
|
|
revision: "main".into(),
|
|
})),
|
|
}),
|
|
options: None,
|
|
}))
|
|
.await
|
|
.unwrap()
|
|
.into_inner();
|
|
|
|
let patches: Vec<_> = tokio_stream::StreamExt::collect(stream).await;
|
|
assert!(!patches.is_empty());
|
|
let combined: String = patches
|
|
.iter()
|
|
.map(|p| String::from_utf8_lossy(&p.as_ref().unwrap().data).to_string())
|
|
.collect();
|
|
assert!(combined.contains("diff --git") || combined.contains("@@"));
|
|
}
|
|
|
|
|
|
#[test]
|
|
fn test_find_changed_paths() {
|
|
let (_dir, gb) = common::setup_bare_repo();
|
|
let feature_oid = common::get_feature_oid(&gb);
|
|
let main_oid = common::get_main_oid(&gb);
|
|
let resp = gb.find_changed_paths(FindChangedPathsRequest {
|
|
repository: Some(hdr()),
|
|
base: feature_oid,
|
|
head: main_oid,
|
|
paths: vec![],
|
|
}).unwrap();
|
|
assert!(!resp.paths.is_empty());
|
|
}
|
|
|
|
#[test]
|
|
fn test_find_changed_paths_same_ref() {
|
|
let (_dir, gb) = common::setup_bare_repo();
|
|
let oid = common::get_main_oid(&gb);
|
|
let resp = gb.find_changed_paths(FindChangedPathsRequest {
|
|
repository: Some(hdr()),
|
|
base: oid.clone(),
|
|
head: oid,
|
|
paths: vec![],
|
|
}).unwrap();
|
|
assert!(resp.paths.is_empty());
|
|
}
|
|
|
|
|
|
#[test]
|
|
fn test_raw_diff() {
|
|
let (_dir, gb) = common::setup_bare_repo();
|
|
let feature_oid = common::get_feature_oid(&gb);
|
|
let main_oid = common::get_main_oid(&gb);
|
|
let chunks = gb.raw_diff(RawDiffRequest {
|
|
repository: Some(hdr()),
|
|
base: feature_oid,
|
|
head: main_oid,
|
|
options: None,
|
|
}).unwrap();
|
|
assert!(!chunks.is_empty());
|
|
let combined: Vec<u8> = chunks.iter().flat_map(|c| c.data.clone()).collect();
|
|
let text = String::from_utf8_lossy(&combined);
|
|
assert!(text.contains("diff"));
|
|
}
|
|
|
|
#[test]
|
|
fn test_raw_patch() {
|
|
let (_dir, gb) = common::setup_bare_repo();
|
|
let feature_oid = common::get_feature_oid(&gb);
|
|
let main_oid = common::get_main_oid(&gb);
|
|
let chunks = gb.raw_patch(RawPatchRequest {
|
|
repository: Some(hdr()),
|
|
base: feature_oid,
|
|
head: main_oid,
|
|
}).unwrap();
|
|
assert!(!chunks.is_empty());
|
|
let combined: Vec<u8> = chunks.iter().flat_map(|c| c.data.clone()).collect();
|
|
let text = String::from_utf8_lossy(&combined);
|
|
assert!(text.contains("From"));
|
|
}
|
|
|
|
|
|
#[test]
|
|
fn test_get_raw_changes() {
|
|
let (_dir, gb) = common::setup_bare_repo();
|
|
let feature_oid = common::get_feature_oid(&gb);
|
|
let main_oid = common::get_main_oid(&gb);
|
|
let resp = gb.get_raw_changes(GetRawChangesRequest {
|
|
repository: Some(hdr()),
|
|
base: feature_oid,
|
|
head: main_oid,
|
|
}).unwrap();
|
|
assert!(!resp.changes.is_empty());
|
|
}
|