use tempfile::TempDir;
use super::inventory::{Inventory, PoolEntry, WorktreeStatus};
use super::lock::PoolLock;
fn make_entry(name: &str, status: WorktreeStatus) -> PoolEntry {
PoolEntry {
name: name.to_string(),
path: format!("/tmp/test/.worktrees/{name}"),
branch: format!("pool/{name}"),
status,
created_at: 1700000000,
acquired_at: None,
acquired_by: None,
}
}
#[test]
fn test_new_inventory_is_empty() {
let inv = Inventory::new();
assert_eq!(inv.version, 1);
assert!(inv.worktrees.is_empty());
}
#[test]
fn test_default_equals_new() {
let a = Inventory::new();
let b = Inventory::default();
assert_eq!(a.version, b.version);
assert_eq!(a.worktrees.len(), b.worktrees.len());
}
#[test]
fn test_save_and_load_round_trip() {
let dir = TempDir::new().unwrap();
let path = dir.path().join("inventory.json");
let mut inv = Inventory::new();
inv.worktrees
.push(make_entry("pool-001", WorktreeStatus::Available));
inv.worktrees
.push(make_entry("pool-002", WorktreeStatus::Acquired));
inv.save(&path).unwrap();
let loaded = Inventory::load(&path).unwrap();
assert_eq!(loaded.version, 1);
assert_eq!(loaded.worktrees.len(), 2);
assert_eq!(loaded.worktrees[0].name, "pool-001");
assert_eq!(loaded.worktrees[0].status, WorktreeStatus::Available);
assert_eq!(loaded.worktrees[1].name, "pool-002");
assert_eq!(loaded.worktrees[1].status, WorktreeStatus::Acquired);
}
#[test]
fn test_load_nonexistent_returns_empty() {
let dir = TempDir::new().unwrap();
let path = dir.path().join("does-not-exist.json");
let inv = Inventory::load(&path).unwrap();
assert!(inv.worktrees.is_empty());
}
#[test]
fn test_load_invalid_json_returns_error() {
let dir = TempDir::new().unwrap();
let path = dir.path().join("bad.json");
std::fs::write(&path, "not valid json").unwrap();
let result = Inventory::load(&path);
assert!(result.is_err());
}
#[test]
fn test_acquired_fields_round_trip() {
let dir = TempDir::new().unwrap();
let path = dir.path().join("inventory.json");
let mut inv = Inventory::new();
let mut entry = make_entry("pool-001", WorktreeStatus::Acquired);
entry.acquired_at = Some(1700000099);
entry.acquired_by = Some(12345);
inv.worktrees.push(entry);
inv.save(&path).unwrap();
let loaded = Inventory::load(&path).unwrap();
assert_eq!(loaded.worktrees[0].acquired_at, Some(1700000099));
assert_eq!(loaded.worktrees[0].acquired_by, Some(12345));
}
#[test]
fn test_null_acquired_fields_round_trip() {
let dir = TempDir::new().unwrap();
let path = dir.path().join("inventory.json");
let mut inv = Inventory::new();
inv.worktrees
.push(make_entry("pool-001", WorktreeStatus::Available));
inv.save(&path).unwrap();
let loaded = Inventory::load(&path).unwrap();
assert_eq!(loaded.worktrees[0].acquired_at, None);
assert_eq!(loaded.worktrees[0].acquired_by, None);
}
#[test]
fn test_count_by_status_empty() {
let inv = Inventory::new();
assert_eq!(inv.count_by_status(&WorktreeStatus::Available), 0);
assert_eq!(inv.count_by_status(&WorktreeStatus::Acquired), 0);
}
#[test]
fn test_count_by_status_mixed() {
let mut inv = Inventory::new();
inv.worktrees
.push(make_entry("pool-001", WorktreeStatus::Available));
inv.worktrees
.push(make_entry("pool-002", WorktreeStatus::Acquired));
inv.worktrees
.push(make_entry("pool-003", WorktreeStatus::Available));
assert_eq!(inv.count_by_status(&WorktreeStatus::Available), 2);
assert_eq!(inv.count_by_status(&WorktreeStatus::Acquired), 1);
}
#[test]
fn test_next_name_empty() {
let inv = Inventory::new();
assert_eq!(inv.next_name(), "pool-001");
}
#[test]
fn test_next_name_sequential() {
let mut inv = Inventory::new();
inv.worktrees
.push(make_entry("pool-001", WorktreeStatus::Available));
inv.worktrees
.push(make_entry("pool-002", WorktreeStatus::Available));
assert_eq!(inv.next_name(), "pool-003");
}
#[test]
fn test_next_name_with_gap() {
let mut inv = Inventory::new();
inv.worktrees
.push(make_entry("pool-001", WorktreeStatus::Available));
inv.worktrees
.push(make_entry("pool-005", WorktreeStatus::Available));
assert_eq!(inv.next_name(), "pool-006");
}
#[test]
fn test_find_available_none() {
let mut inv = Inventory::new();
inv.worktrees
.push(make_entry("pool-001", WorktreeStatus::Acquired));
assert_eq!(inv.find_available(), None);
}
#[test]
fn test_find_available_returns_first() {
let mut inv = Inventory::new();
inv.worktrees
.push(make_entry("pool-001", WorktreeStatus::Acquired));
inv.worktrees
.push(make_entry("pool-002", WorktreeStatus::Available));
inv.worktrees
.push(make_entry("pool-003", WorktreeStatus::Available));
assert_eq!(inv.find_available(), Some(1));
}
#[test]
fn test_find_available_empty_inventory() {
let inv = Inventory::new();
assert_eq!(inv.find_available(), None);
}
#[test]
fn test_find_by_name() {
let mut inv = Inventory::new();
inv.worktrees
.push(make_entry("pool-001", WorktreeStatus::Available));
inv.worktrees
.push(make_entry("pool-002", WorktreeStatus::Acquired));
assert_eq!(inv.find_by_name_or_path("pool-002"), Some(1));
}
#[test]
fn test_find_by_path() {
let mut inv = Inventory::new();
inv.worktrees
.push(make_entry("pool-001", WorktreeStatus::Available));
assert_eq!(
inv.find_by_name_or_path("/tmp/test/.worktrees/pool-001"),
Some(0)
);
}
#[test]
fn test_find_nonexistent() {
let mut inv = Inventory::new();
inv.worktrees
.push(make_entry("pool-001", WorktreeStatus::Available));
assert_eq!(inv.find_by_name_or_path("pool-999"), None);
assert_eq!(inv.find_by_name_or_path("/wrong/path"), None);
}
#[test]
fn test_status_display() {
assert_eq!(WorktreeStatus::Available.to_string(), "available");
assert_eq!(WorktreeStatus::Acquired.to_string(), "acquired");
}
#[test]
fn test_status_serde_round_trip() {
let json = serde_json::to_string(&WorktreeStatus::Available).unwrap();
assert_eq!(json, "\"available\"");
let json = serde_json::to_string(&WorktreeStatus::Acquired).unwrap();
assert_eq!(json, "\"acquired\"");
let parsed: WorktreeStatus = serde_json::from_str("\"available\"").unwrap();
assert_eq!(parsed, WorktreeStatus::Available);
}
#[test]
fn test_lock_creates_dir_and_file() {
let dir = TempDir::new().unwrap();
let pool_dir = dir.path().join("pool");
assert!(!pool_dir.exists());
let _lock = PoolLock::acquire(&pool_dir).unwrap();
assert!(pool_dir.exists());
assert!(pool_dir.join("pool.lock").exists());
}
#[test]
fn test_lock_released_on_drop() {
let dir = TempDir::new().unwrap();
let pool_dir = dir.path().join("pool");
{
let _lock = PoolLock::acquire(&pool_dir).unwrap();
}
let _lock2 = PoolLock::acquire(&pool_dir).unwrap();
}
#[test]
fn test_lock_exclusive() {
let dir = TempDir::new().unwrap();
let pool_dir = dir.path().join("pool");
let _lock1 = PoolLock::acquire(&pool_dir).unwrap();
let result = PoolLock::acquire(&pool_dir);
assert!(result.is_err());
}