Initial commit
This commit is contained in:
269
src-tauri/tests/commands.rs
Normal file
269
src-tauri/tests/commands.rs
Normal file
@@ -0,0 +1,269 @@
|
||||
//! Integration tests for the Tauri command layer.
|
||||
//!
|
||||
//! These tests exercise `AppState` and the command logic end-to-end using a
|
||||
//! real `Brittle<FsStore>` repository in a temp directory. Tauri IPC is not
|
||||
//! involved — the functions under test are plain Rust.
|
||||
|
||||
use brittle_app::state::AppState;
|
||||
use brittle_core::{Brittle, EntryType, Person};
|
||||
|
||||
fn open_state() -> (AppState, tempfile::TempDir) {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let state = AppState::new();
|
||||
let brittle = Brittle::create(tmp.path()).unwrap();
|
||||
*state.brittle.lock().unwrap() = Some(brittle);
|
||||
(state, tmp)
|
||||
}
|
||||
|
||||
// ── AppState ─────────────────────────────────────────────────────────────────
|
||||
|
||||
#[test]
|
||||
fn no_repo_open_returns_error() {
|
||||
let state = AppState::new();
|
||||
let err = state.with_repo(|_| Ok(())).unwrap_err();
|
||||
assert_eq!(err, "no repository open");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn with_repo_propagates_brittle_errors() {
|
||||
let (state, _tmp) = open_state();
|
||||
// Trying to get a non-existent reference propagates the StoreError.
|
||||
let err = state
|
||||
.with_repo_read(|b| {
|
||||
use brittle_core::model::ids::ReferenceId;
|
||||
b.get_reference(ReferenceId::new())
|
||||
})
|
||||
.unwrap_err();
|
||||
assert!(!err.is_empty());
|
||||
}
|
||||
|
||||
// ── Repository lifecycle ──────────────────────────────────────────────────────
|
||||
|
||||
#[test]
|
||||
fn create_and_reopen_repository() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let state = AppState::new();
|
||||
|
||||
// Create
|
||||
{
|
||||
let brittle = Brittle::create(tmp.path()).unwrap();
|
||||
*state.brittle.lock().unwrap() = Some(brittle);
|
||||
}
|
||||
|
||||
// Close
|
||||
*state.brittle.lock().unwrap() = None;
|
||||
assert!(state.with_repo_read(|_| Ok(())).is_err());
|
||||
|
||||
// Reopen
|
||||
{
|
||||
let brittle = Brittle::open(tmp.path()).unwrap();
|
||||
*state.brittle.lock().unwrap() = Some(brittle);
|
||||
}
|
||||
assert!(state.with_repo_read(|_| Ok(())).is_ok());
|
||||
}
|
||||
|
||||
// ── Reference CRUD ───────────────────────────────────────────────────────────
|
||||
|
||||
#[test]
|
||||
fn create_and_list_references() {
|
||||
let (state, _tmp) = open_state();
|
||||
|
||||
state
|
||||
.with_repo(|b| b.create_reference("turing1950", EntryType::Article))
|
||||
.unwrap();
|
||||
state
|
||||
.with_repo(|b| b.create_reference("knuth1984", EntryType::Book))
|
||||
.unwrap();
|
||||
|
||||
let refs = state.with_repo_read(|b| b.list_references()).unwrap();
|
||||
assert_eq!(refs.len(), 2);
|
||||
let keys: Vec<&str> = refs.iter().map(|r| r.cite_key.as_str()).collect();
|
||||
assert!(keys.contains(&"turing1950"));
|
||||
assert!(keys.contains(&"knuth1984"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn delete_reference_removes_it() {
|
||||
let (state, _tmp) = open_state();
|
||||
|
||||
let r = state
|
||||
.with_repo(|b| b.create_reference("gone2024", EntryType::Misc))
|
||||
.unwrap();
|
||||
|
||||
state.with_repo(|b| b.delete_reference(r.id)).unwrap();
|
||||
|
||||
let refs = state.with_repo_read(|b| b.list_references()).unwrap();
|
||||
assert!(refs.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn set_and_remove_field() {
|
||||
let (state, _tmp) = open_state();
|
||||
|
||||
let r = state
|
||||
.with_repo(|b| b.create_reference("fields2024", EntryType::Article))
|
||||
.unwrap();
|
||||
|
||||
state
|
||||
.with_repo(|b| b.set_field(r.id, "title", "A Test Title"))
|
||||
.unwrap();
|
||||
|
||||
let fetched = state.with_repo_read(|b| b.get_reference(r.id)).unwrap();
|
||||
assert_eq!(
|
||||
fetched.fields.get("title").map(String::as_str),
|
||||
Some("A Test Title")
|
||||
);
|
||||
|
||||
state.with_repo(|b| b.remove_field(r.id, "title")).unwrap();
|
||||
|
||||
let fetched2 = state.with_repo_read(|b| b.get_reference(r.id)).unwrap();
|
||||
assert!(!fetched2.fields.contains_key("title"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn search_references_filters_by_query() {
|
||||
let (state, _tmp) = open_state();
|
||||
|
||||
state
|
||||
.with_repo(|b| b.create_reference("turing1950", EntryType::Article))
|
||||
.unwrap();
|
||||
state
|
||||
.with_repo(|b| b.create_reference("knuth1984", EntryType::Book))
|
||||
.unwrap();
|
||||
|
||||
let results = state
|
||||
.with_repo_read(|b| b.search_references("turing"))
|
||||
.unwrap();
|
||||
assert_eq!(results.len(), 1);
|
||||
assert_eq!(results[0].cite_key, "turing1950");
|
||||
}
|
||||
|
||||
// ── Library ───────────────────────────────────────────────────────────────────
|
||||
|
||||
#[test]
|
||||
fn create_nested_libraries_and_query_hierarchy() {
|
||||
let (state, _tmp) = open_state();
|
||||
|
||||
let root = state.with_repo(|b| b.create_library("Root", None)).unwrap();
|
||||
let child = state
|
||||
.with_repo(|b| b.create_library("Child", Some(root.id)))
|
||||
.unwrap();
|
||||
|
||||
let roots = state.with_repo_read(|b| b.list_root_libraries()).unwrap();
|
||||
assert_eq!(roots.len(), 1);
|
||||
assert_eq!(roots[0].id, root.id);
|
||||
|
||||
let children = state
|
||||
.with_repo_read(|b| b.list_child_libraries(root.id))
|
||||
.unwrap();
|
||||
assert_eq!(children.len(), 1);
|
||||
assert_eq!(children[0].id, child.id);
|
||||
|
||||
let ancestors = state
|
||||
.with_repo_read(|b| b.get_library_ancestors(child.id))
|
||||
.unwrap();
|
||||
assert_eq!(ancestors.len(), 1);
|
||||
assert_eq!(ancestors[0].id, root.id);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn add_reference_to_library_and_query() {
|
||||
let (state, _tmp) = open_state();
|
||||
|
||||
let r = state
|
||||
.with_repo(|b| b.create_reference("member2024", EntryType::Article))
|
||||
.unwrap();
|
||||
let lib = state.with_repo(|b| b.create_library("Lib", None)).unwrap();
|
||||
|
||||
state.with_repo(|b| b.add_to_library(lib.id, r.id)).unwrap();
|
||||
|
||||
let members = state
|
||||
.with_repo_read(|b| b.list_library_references(lib.id))
|
||||
.unwrap();
|
||||
assert_eq!(members.len(), 1);
|
||||
assert_eq!(members[0].id, r.id);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn force_delete_library_removes_subtree() {
|
||||
let (state, _tmp) = open_state();
|
||||
|
||||
let root = state.with_repo(|b| b.create_library("Root", None)).unwrap();
|
||||
state
|
||||
.with_repo(|b| b.create_library("Child", Some(root.id)))
|
||||
.unwrap();
|
||||
|
||||
state
|
||||
.with_repo(|b| b.force_delete_library(root.id))
|
||||
.unwrap();
|
||||
|
||||
let all = state.with_repo_read(|b| b.list_root_libraries()).unwrap();
|
||||
assert!(all.is_empty());
|
||||
}
|
||||
|
||||
// ── BibTeX export ─────────────────────────────────────────────────────────────
|
||||
|
||||
#[test]
|
||||
fn export_library_bibtex_contains_entries() {
|
||||
let (state, _tmp) = open_state();
|
||||
|
||||
let mut r = state
|
||||
.with_repo(|b| b.create_reference("turing1950", EntryType::Article))
|
||||
.unwrap();
|
||||
r.authors.push(Person::new("Turing"));
|
||||
r.fields.insert(
|
||||
"title".into(),
|
||||
"Computing Machinery and Intelligence".into(),
|
||||
);
|
||||
r.fields.insert("journal".into(), "Mind".into());
|
||||
r.fields.insert("year".into(), "1950".into());
|
||||
let r = state.with_repo(|b| b.update_reference(r)).unwrap();
|
||||
|
||||
let lib = state.with_repo(|b| b.create_library("CS", None)).unwrap();
|
||||
state.with_repo(|b| b.add_to_library(lib.id, r.id)).unwrap();
|
||||
|
||||
let (bibtex, errors) = state
|
||||
.with_repo_read(|b| b.export_library_bibtex(lib.id))
|
||||
.unwrap();
|
||||
|
||||
assert!(errors.is_empty());
|
||||
assert!(bibtex.contains("@article{turing1950,"));
|
||||
assert!(bibtex.contains("Computing Machinery and Intelligence"));
|
||||
}
|
||||
|
||||
// ── Snapshot ──────────────────────────────────────────────────────────────────
|
||||
|
||||
#[test]
|
||||
fn snapshot_and_discard_changes() {
|
||||
let (state, _tmp) = open_state();
|
||||
|
||||
state
|
||||
.with_repo(|b| b.create_reference("snap2024", EntryType::Misc))
|
||||
.unwrap();
|
||||
|
||||
let snap = state.with_repo(|b| b.create_snapshot("baseline")).unwrap();
|
||||
assert!(!snap.id.is_empty());
|
||||
|
||||
let snapshots = state.with_repo_read(|b| b.list_snapshots()).unwrap();
|
||||
assert!(snapshots.iter().any(|s| s.message == "baseline"));
|
||||
|
||||
// Delete the reference (uncommitted change).
|
||||
let r_id = state
|
||||
.with_repo_read(|b| b.list_references())
|
||||
.unwrap()
|
||||
.into_iter()
|
||||
.next()
|
||||
.unwrap()
|
||||
.id;
|
||||
state.with_repo(|b| b.delete_reference(r_id)).unwrap();
|
||||
|
||||
assert!(state
|
||||
.with_repo_read(|b| b.has_uncommitted_changes())
|
||||
.unwrap());
|
||||
|
||||
// Discard → reference comes back.
|
||||
state.with_repo(|b| b.discard_changes()).unwrap();
|
||||
|
||||
let refs = state.with_repo_read(|b| b.list_references()).unwrap();
|
||||
assert_eq!(refs.len(), 1);
|
||||
}
|
||||
Reference in New Issue
Block a user