Files
max-sixty__worktrunk/tests/integration_tests/cache_sharing.rs
Maximilian Roos db4e0e5ddd Use structured output instead of parsing error messages (#570)
Replace fragile error message parsing with structured alternatives:

- merge_base: Use exit codes (0=found, 1=orphan, 128+=error) instead of
  parsing "no merge base" or "Not a valid commit" strings
- DiffStats: Use --numstat (locale-independent) instead of --shortstat
  which uses localized keywords like "insertions"/"Einfügungen"
- Handle orphan branches gracefully: return (0,0) for ahead_behind and
  LineDiff::default() for branch_diff_stats instead of erroring

Other changes:
- Add run_command_output helper for inspecting git exit codes
- Simplify DiffStats to use plain usize fields (Option was pointless)
- Add CLAUDE.md guidance about preferring structured output
- Delete unused from_shortstat and its tests

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-12 16:35:40 -08:00

118 lines
3.9 KiB
Rust

//! Tests for Repository cache sharing.
//!
//! These tests verify that when Repository is cloned, the cache is shared
//! across clones via Arc. This is important for performance in `wt list`
//! where parallel tasks share cached git data.
use rstest::rstest;
use worktrunk::git::Repository;
mod common {
pub use crate::common::*;
}
use common::{TestRepo, repo};
/// Test that cloning a Repository shares the same cache via Arc.
#[rstest]
fn test_repository_clone_shares_cache(repo: TestRepo) {
let repo1 = Repository::at(repo.root_path()).unwrap();
let repo2 = repo1.clone();
// Both should point to the same cache (Arc pointer equality)
assert!(
repo1.shares_cache_with(&repo2),
"Cloned repositories should share the same cache"
);
}
/// Test that cached values computed by one clone are visible to another.
#[rstest]
fn test_cache_shared_between_clones(repo: TestRepo) {
let repo1 = Repository::at(repo.root_path()).unwrap();
let repo2 = repo1.clone();
// Call default_branch() on repo1 - this caches the result
let default1 = repo1.default_branch().unwrap();
// Call default_branch() on repo2 - should return cached value
let default2 = repo2.default_branch().unwrap();
assert_eq!(default1, default2);
assert_eq!(default1, "main"); // TestRepo uses main as default branch
}
/// Test that merge_base cache is shared across clones.
#[rstest]
fn test_merge_base_cache_shared(mut repo: TestRepo) {
// Create a branch with a commit
repo.add_worktree("feature");
let feature_path = repo.worktree_path("feature");
repo.commit_in_worktree(feature_path, "feature.txt", "content", "feature commit");
let repo1 = Repository::at(repo.root_path()).unwrap();
let repo2 = repo1.clone();
// Get HEAD commits
let main_head = repo.head_sha();
let feature_head = repo.head_sha_in(feature_path);
// Call merge_base on repo1 - caches the result
let base1 = repo1.merge_base(&main_head, &feature_head).unwrap();
// Call merge_base on repo2 - should use cached value
let base2 = repo2.merge_base(&main_head, &feature_head).unwrap();
assert_eq!(base1, base2);
// The merge base should be main's HEAD since feature branched from there
assert_eq!(base1, Some(main_head));
}
/// Test that parallel tasks share the cache when cloning Repository.
#[rstest]
fn test_parallel_tasks_share_cache(mut repo: TestRepo) {
use std::thread;
// Create multiple worktrees
repo.add_worktree("feature-a");
repo.add_worktree("feature-b");
let repo1 = Repository::at(repo.root_path()).unwrap();
// Spawn threads that clone the repo and access cached values
let handles: Vec<_> = (0..4)
.map(|i| {
let repo_clone = repo1.clone();
thread::spawn(move || {
// Each thread accesses the same cached default_branch
let default = repo_clone.default_branch().unwrap();
(i, default)
})
})
.collect();
// All threads should get the same cached value
let results: Vec<_> = handles.into_iter().map(|h| h.join().unwrap()).collect();
for (_, branch) in &results {
assert_eq!(branch, "main");
}
}
/// Test that separate Repository instances (not clones) have separate caches.
#[rstest]
fn test_separate_repositories_have_separate_caches(repo: TestRepo) {
// Create two separate Repository instances (not clones)
let repo1 = Repository::at(repo.root_path()).unwrap();
let repo2 = Repository::at(repo.root_path()).unwrap();
// They should NOT share the same cache
assert!(
!repo1.shares_cache_with(&repo2),
"Separately created repositories should have independent caches"
);
// But they should compute the same values
let default1 = repo1.default_branch().unwrap();
let default2 = repo2.default_branch().unwrap();
assert_eq!(default1, default2);
}