diff --git a/src-tauri/src/notify.rs b/src-tauri/src/notify.rs index 2500e26..1cfc57a 100644 --- a/src-tauri/src/notify.rs +++ b/src-tauri/src/notify.rs @@ -9,14 +9,16 @@ use crate::patterns::MatchedEvent; static PENDING: Mutex> = Mutex::new(vec![]); pub fn dispatch(app: &AppHandle, event: MatchedEvent) { - let level = { - let state = app.state::(); - let config = state.config.lock().unwrap(); - config.display.notification_level.clone() - }; + let level = app + .state::() + .config + .lock() + .map(|c| c.display.notification_level.clone()) + .unwrap_or_default(); - if let Ok(mut pending) = PENDING.lock() { - pending.push(event.clone()); + match PENDING.lock() { + Ok(mut pending) => pending.push(event.clone()), + Err(e) => log::warn!("notify: pending queue lock poisoned — event dropped: {e}"), } match level { @@ -48,3 +50,36 @@ pub fn take_pending() -> Vec { .map(|mut v| std::mem::take(&mut *v)) .unwrap_or_default() } + +#[cfg(test)] +mod tests { + use super::*; + + fn make_event(id: &str) -> MatchedEvent { + MatchedEvent { + pattern_id: id.into(), + title: "Title".into(), + body: "Body".into(), + severity: "warn".into(), + timestamp: 0, + } + } + + #[test] + fn take_pending_drains_queue() { + // Ensure clean state — drain any events from other tests sharing the static + let _ = take_pending(); + + if let Ok(mut q) = PENDING.lock() { + q.push(make_event("a")); + q.push(make_event("b")); + } + + let first = take_pending(); + assert_eq!(first.len(), 2); + assert_eq!(first[0].pattern_id, "a"); + + let second = take_pending(); + assert!(second.is_empty(), "queue must be empty after drain"); + } +}