tests/cache_test.rs
Ref: Size: 6.5 KiB
mod common;
use common::{alice, bob, add_comment, close_issue, init_repo, open_issue, create_patch, add_review};
use git_collab::cache;
use git_collab::event::ReviewVerdict;
use git_collab::state::{IssueState, IssueStatus, PatchState, PatchStatus};
use tempfile::TempDir;
#[test]
fn cache_miss_walks_dag_and_caches() {
let dir = TempDir::new().unwrap();
let repo = init_repo(dir.path(), &alice());
let (ref_name, id) = open_issue(&repo, &alice(), "Cache test issue");
// No cache exists yet — should return None
let cached: Option<IssueState> = cache::get_cached_state(&repo, &ref_name);
assert!(cached.is_none());
// from_ref should walk the DAG and populate the cache
let state = IssueState::from_ref(&repo, &ref_name, &id).unwrap();
assert_eq!(state.title, "Cache test issue");
assert_eq!(state.status, IssueStatus::Open);
// Now the cache should have an entry
let cached: Option<IssueState> = cache::get_cached_state(&repo, &ref_name);
assert!(cached.is_some());
let cached = cached.unwrap();
assert_eq!(cached.title, "Cache test issue");
assert_eq!(cached.id, id);
}
#[test]
fn cache_hit_returns_cached_state() {
let dir = TempDir::new().unwrap();
let repo = init_repo(dir.path(), &alice());
let (ref_name, id) = open_issue(&repo, &alice(), "Cached issue");
// Populate cache
let state1 = IssueState::from_ref(&repo, &ref_name, &id).unwrap();
// Second call should hit cache and return same result
let state2 = IssueState::from_ref(&repo, &ref_name, &id).unwrap();
assert_eq!(state1.title, state2.title);
assert_eq!(state1.id, state2.id);
assert_eq!(state1.status, state2.status);
}
#[test]
fn cache_invalidation_on_new_event() {
let dir = TempDir::new().unwrap();
let repo = init_repo(dir.path(), &alice());
let (ref_name, id) = open_issue(&repo, &alice(), "Invalidation test");
// Populate cache
let state1 = IssueState::from_ref(&repo, &ref_name, &id).unwrap();
assert_eq!(state1.comments.len(), 0);
// Append a comment — this changes the ref tip OID
add_comment(&repo, &ref_name, &bob(), "Hello!");
// Cache should be invalidated (tip changed), DAG should be re-walked
let state2 = IssueState::from_ref(&repo, &ref_name, &id).unwrap();
assert_eq!(state2.comments.len(), 1);
assert_eq!(state2.comments[0].body, "Hello!");
}
#[test]
fn cache_invalidation_on_close() {
let dir = TempDir::new().unwrap();
let repo = init_repo(dir.path(), &alice());
let (ref_name, id) = open_issue(&repo, &alice(), "Close test");
let state1 = IssueState::from_ref(&repo, &ref_name, &id).unwrap();
assert_eq!(state1.status, IssueStatus::Open);
close_issue(&repo, &ref_name, &alice());
let state2 = IssueState::from_ref(&repo, &ref_name, &id).unwrap();
assert_eq!(state2.status, IssueStatus::Closed);
}
#[test]
fn no_cache_dir_falls_back_gracefully() {
let dir = TempDir::new().unwrap();
let repo = init_repo(dir.path(), &alice());
let (ref_name, id) = open_issue(&repo, &alice(), "No cache dir");
// Ensure no cache directory exists (it shouldn't by default)
let cache_path = repo.path().join("collab").join("cache");
if cache_path.exists() {
std::fs::remove_dir_all(&cache_path).unwrap();
}
// Should still work via DAG walk
let state = IssueState::from_ref(&repo, &ref_name, &id).unwrap();
assert_eq!(state.title, "No cache dir");
}
#[test]
fn corrupted_cache_falls_back_to_dag_walk() {
let dir = TempDir::new().unwrap();
let repo = init_repo(dir.path(), &alice());
let (ref_name, id) = open_issue(&repo, &alice(), "Corrupted cache");
// Populate cache
IssueState::from_ref(&repo, &ref_name, &id).unwrap();
// Corrupt the cache file
let cache_dir = repo.path().join("collab").join("cache");
let sanitized = ref_name.replace('/', "_");
let cache_file = cache_dir.join(sanitized);
std::fs::write(&cache_file, "not valid json").unwrap();
// Should fall back gracefully
let state = IssueState::from_ref(&repo, &ref_name, &id).unwrap();
assert_eq!(state.title, "Corrupted cache");
}
#[test]
fn patch_cache_miss_and_hit() {
let dir = TempDir::new().unwrap();
let repo = init_repo(dir.path(), &alice());
let (ref_name, id) = create_patch(&repo, &alice(), "Cache patch");
// No cache
let cached: Option<PatchState> = cache::get_cached_state(&repo, &ref_name);
assert!(cached.is_none());
// from_ref populates cache
let state = PatchState::from_ref(&repo, &ref_name, &id).unwrap();
assert_eq!(state.title, "Cache patch");
assert_eq!(state.status, PatchStatus::Open);
// Cache hit
let cached: Option<PatchState> = cache::get_cached_state(&repo, &ref_name);
assert!(cached.is_some());
assert_eq!(cached.unwrap().title, "Cache patch");
}
#[test]
fn patch_cache_invalidation_on_review() {
let dir = TempDir::new().unwrap();
let repo = init_repo(dir.path(), &alice());
let (ref_name, id) = create_patch(&repo, &alice(), "Review patch");
let state1 = PatchState::from_ref(&repo, &ref_name, &id).unwrap();
assert_eq!(state1.reviews.len(), 0);
add_review(&repo, &ref_name, &bob(), ReviewVerdict::Approve);
let state2 = PatchState::from_ref(&repo, &ref_name, &id).unwrap();
assert_eq!(state2.reviews.len(), 1);
}
#[test]
fn cache_invalidate_removes_entry() {
let dir = TempDir::new().unwrap();
let repo = init_repo(dir.path(), &alice());
let (ref_name, id) = open_issue(&repo, &alice(), "Invalidate test");
// Populate cache
IssueState::from_ref(&repo, &ref_name, &id).unwrap();
assert!(cache::get_cached_state::<IssueState>(&repo, &ref_name).is_some());
// Explicitly invalidate
cache::invalidate(&repo, &ref_name);
assert!(cache::get_cached_state::<IssueState>(&repo, &ref_name).is_none());
}
#[test]
fn list_issues_uses_cache() {
let dir = TempDir::new().unwrap();
let repo = init_repo(dir.path(), &alice());
open_issue(&repo, &alice(), "List issue 1");
open_issue(&repo, &alice(), "List issue 2");
// First call walks DAGs and populates cache
let issues1 = git_collab::state::list_issues(&repo).unwrap();
assert_eq!(issues1.len(), 2);
// Second call should use cache
let issues2 = git_collab::state::list_issues(&repo).unwrap();
assert_eq!(issues2.len(), 2);
// Titles should match (order may differ)
let mut titles1: Vec<_> = issues1.iter().map(|i| &i.title).collect();
let mut titles2: Vec<_> = issues2.iter().map(|i| &i.title).collect();
titles1.sort();
titles2.sort();
assert_eq!(titles1, titles2);
}