mod common;
use bonds_core::BondError;
use common::setup;
use tempfile::TempDir;
#[test]
#[cfg_attr(windows, ignore)]
fn full_lifecycle_create_list_delete() {
let (mgr, _db) = setup();
let src = TempDir::new().unwrap();
let tgt_dir = TempDir::new().unwrap();
let tgt = tgt_dir.path().join("my_bond");
let bond = mgr.create_bond(src.path(), &tgt, None).unwrap();
assert!(tgt.exists());
assert!(tgt.symlink_metadata().unwrap().file_type().is_symlink());
let bonds = mgr.list_bonds().unwrap();
assert_eq!(bonds.len(), 1);
assert_eq!(bonds[0].id(), bond.id());
mgr.delete_bond(bond.id(), false).unwrap();
assert!(!tgt.exists());
assert!(mgr.list_bonds().unwrap().is_empty());
}
#[test]
#[cfg_attr(windows, ignore)]
fn symlink_resolves_to_source_contents() {
let (mgr, _db) = setup();
let src = TempDir::new().unwrap();
std::fs::write(src.path().join("hello.txt"), "world").unwrap();
let tgt_dir = TempDir::new().unwrap();
let tgt = tgt_dir.path().join("link");
mgr.create_bond(src.path(), &tgt, None).unwrap();
let content = std::fs::read_to_string(tgt.join("hello.txt")).unwrap();
assert_eq!(content, "world");
}
#[test]
#[cfg_attr(windows, ignore)]
fn delete_with_target_removes_actual_files() {
let (mgr, _db) = setup();
let src = TempDir::new().unwrap();
let tgt_dir = TempDir::new().unwrap();
let tgt = tgt_dir.path().join("link");
let bond = mgr.create_bond(src.path(), &tgt, None).unwrap();
std::fs::remove_file(&tgt).unwrap();
std::fs::create_dir(&tgt).unwrap();
std::fs::write(tgt.join("file.txt"), "data").unwrap();
let err = mgr.delete_bond(bond.id(), false).unwrap_err();
assert!(matches!(err, BondError::InvalidPath(_)));
let removed = mgr.delete_bond(bond.id(), true).unwrap();
assert_eq!(removed.id(), bond.id());
assert!(!tgt.exists());
}
#[test]
#[cfg_attr(windows, ignore)]
fn update_bond_target() {
let (mgr, _db) = setup();
let src = TempDir::new().unwrap();
let tgt_dir = TempDir::new().unwrap();
let old_tgt = tgt_dir.path().join("old_link");
let new_tgt = tgt_dir.path().join("new_link");
let bond = mgr.create_bond(src.path(), &old_tgt, None).unwrap();
let updated = mgr
.update_bond(bond.id(), None, Some(new_tgt.clone()), None)
.unwrap();
assert_eq!(updated.id(), bond.id());
assert_eq!(updated.target(), new_tgt);
assert_eq!(updated.source(), bond.source());
assert!(!old_tgt.exists());
assert!(new_tgt.symlink_metadata().unwrap().file_type().is_symlink());
let fetched = mgr.get_bond(bond.id()).unwrap();
assert_eq!(fetched.target(), new_tgt);
}
#[test]
#[cfg_attr(windows, ignore)]
fn update_bond_source() {
let (mgr, _db) = setup();
let old_src = TempDir::new().unwrap();
std::fs::write(old_src.path().join("a.txt"), "aaa").unwrap();
let new_src = TempDir::new().unwrap();
std::fs::write(new_src.path().join("b.txt"), "bbb").unwrap();
let tgt_dir = TempDir::new().unwrap();
let tgt = tgt_dir.path().join("link");
let bond = mgr.create_bond(old_src.path(), &tgt, None).unwrap();
assert!(tgt.join("a.txt").exists());
let updated = mgr
.update_bond(bond.id(), Some(new_src.path().to_path_buf()), None, None)
.unwrap();
assert_eq!(updated.source(), new_src.path());
assert!(tgt.join("b.txt").exists());
assert!(!tgt.join("a.txt").exists());
}
#[test]
#[cfg_attr(windows, ignore)]
fn update_bond_rejects_missing_source() {
let (mgr, _db) = setup();
let src = TempDir::new().unwrap();
let tgt_dir = TempDir::new().unwrap();
let tgt = tgt_dir.path().join("link");
let bond = mgr.create_bond(src.path(), &tgt, None).unwrap();
let bad_src = std::path::PathBuf::from("/nonexistent/path");
let err = mgr
.update_bond(bond.id(), Some(bad_src), None, None)
.unwrap_err();
assert!(matches!(err, BondError::InvalidPath(_)));
assert!(tgt.symlink_metadata().unwrap().file_type().is_symlink());
}
#[test]
#[cfg_attr(windows, ignore)]
fn update_bond_rejects_occupied_target() {
let (mgr, _db) = setup();
let src = TempDir::new().unwrap();
let tgt_dir = TempDir::new().unwrap();
let tgt = tgt_dir.path().join("link");
let bond = mgr.create_bond(src.path(), &tgt, None).unwrap();
let occupied = tgt_dir.path().join("occupied");
std::fs::create_dir(&occupied).unwrap();
let err = mgr
.update_bond(bond.id(), None, Some(occupied), None)
.unwrap_err();
assert!(matches!(err, BondError::AlreadyExists));
}
#[test]
#[cfg_attr(windows, ignore)]
fn create_and_lookup_by_name() {
let (mgr, _db) = setup();
let src = TempDir::new().unwrap();
let tgt_dir = TempDir::new().unwrap();
let tgt = tgt_dir.path().join("link");
let bond = mgr
.create_bond(src.path(), &tgt, Some("my-project".into()))
.unwrap();
assert_eq!(bond.name(), Some("my-project"));
let found = mgr.get_bond("my-project").unwrap();
assert_eq!(found.id(), bond.id());
let all = mgr.list_bonds().unwrap();
assert_eq!(all[0].name(), Some("my-project"));
}
#[test]
#[cfg_attr(windows, ignore)]
fn duplicate_name_rejected() {
let (mgr, _db) = setup();
let src1 = TempDir::new().unwrap();
let src2 = TempDir::new().unwrap();
let tgt_dir = TempDir::new().unwrap();
mgr.create_bond(src1.path(), tgt_dir.path().join("a"), Some("taken".into()))
.unwrap();
let err = mgr
.create_bond(src2.path(), tgt_dir.path().join("b"), Some("taken".into()))
.unwrap_err();
assert!(matches!(err, BondError::AlreadyExists));
}
#[test]
#[cfg_attr(windows, ignore)]
fn get_bond_by_prefix() {
let (mgr, _db) = setup();
let src = TempDir::new().unwrap();
let tgt_dir = TempDir::new().unwrap();
let tgt = tgt_dir.path().join("link");
let bond = mgr.create_bond(src.path(), &tgt, None).unwrap();
let prefix = &bond.id()[..8];
let found = mgr.get_bond(prefix).unwrap();
assert_eq!(found.id(), bond.id());
}
#[test]
#[cfg_attr(windows, ignore)]
fn update_bond_name() {
let (mgr, _db) = setup();
let src = TempDir::new().unwrap();
let tgt_dir = TempDir::new().unwrap();
let tgt = tgt_dir.path().join("link");
let bond = mgr
.create_bond(src.path(), &tgt, Some("old-name".into()))
.unwrap();
let updated = mgr
.update_bond(bond.id(), None, None, Some("new-name".into()))
.unwrap();
assert_eq!(updated.name(), Some("new-name"));
let found = mgr.get_bond("new-name").unwrap();
assert_eq!(found.id(), bond.id());
}
#[test]
#[cfg_attr(windows, ignore)]
fn create_bond_into_empty_dir() {
let (mgr, _db) = setup();
let src = TempDir::new().unwrap();
let tgt_dir = TempDir::new().unwrap();
let tgt = tgt_dir.path().join("link");
std::fs::create_dir(&tgt).unwrap();
let _bond = mgr.create_bond(src.path(), &tgt, None).unwrap();
assert!(tgt.symlink_metadata().unwrap().file_type().is_symlink());
}