/* * SPDX-FileCopyrightText: 2020 Stalwart Labs LLC * * SPDX-License-Identifier: Apache-2.0 OR MIT */ use rusqlite::{Connection, params}; pub fn unique_role( conn: &Connection, role: Option<&str>, exclude_id: Option, ) -> rusqlite::Result> { let Some(r) = role else { return Ok(None); }; let taken: bool = conn.query_row( "SELECT EXISTS(SELECT 1 FROM mailboxes WHERE role = ?1 AND (?2 IS NULL OR id != ?2))", params![r, exclude_id], |row| row.get(0), )?; Ok((!taken).then(|| r.to_owned())) } #[cfg(test)] mod tests { use super::*; use crate::db::init; fn mem() -> Connection { let conn = Connection::open_in_memory().unwrap(); init::apply_schema(&conn).unwrap(); conn } fn insert(conn: &Connection, name: &str, role: Option<&str>) -> i64 { conn.execute( "INSERT INTO mailboxes (name, parent_id, role, sort_order, is_subscribed) VALUES (?1, NULL, ?2, 0, 1)", params![name, role], ) .unwrap(); conn.last_insert_rowid() } #[test] fn first_claimant_keeps_role_second_is_dropped() { let conn = mem(); let first = unique_role(&conn, Some("sent"), None).unwrap(); assert_eq!(first.as_deref(), Some("sent")); insert(&conn, "Sent", first.as_deref()); let second = unique_role(&conn, Some("sent"), None).unwrap(); assert_eq!(second, None); } #[test] fn null_role_stays_null() { let conn = mem(); assert_eq!(unique_role(&conn, None, None).unwrap(), None); } #[test] fn distinct_roles_are_independent() { let conn = mem(); insert(&conn, "Sent", Some("sent")); assert_eq!( unique_role(&conn, Some("trash"), None).unwrap().as_deref(), Some("trash") ); } #[test] fn update_excludes_own_row() { let conn = mem(); let id = insert(&conn, "Sent", Some("sent")); assert_eq!( unique_role(&conn, Some("sent"), Some(id)) .unwrap() .as_deref(), Some("sent") ); } #[test] fn update_detects_other_holder() { let conn = mem(); insert(&conn, "Sent", Some("sent")); let other = insert(&conn, "Envoyés", None); assert_eq!(unique_role(&conn, Some("sent"), Some(other)).unwrap(), None); } }