//! Item mutation and database access methods for the feed generator. use bb_db::Database; use super::{FeedError, FeedGenerator}; impl FeedGenerator { /// Mark an item as read #[tracing::instrument(skip_all)] pub async fn mark_read(&self, item_id: &str, is_read: bool) -> Result<(), FeedError> { // Find item by external_id if let Some(item) = self.db.items().get_by_external_id(item_id).await? { self.db.items().mark_read(item.id, is_read).await?; } Ok(()) } /// Mark an item as starred #[tracing::instrument(skip_all)] pub async fn mark_starred(&self, item_id: &str, is_starred: bool) -> Result<(), FeedError> { if let Some(item) = self.db.items().get_by_external_id(item_id).await? { self.db.items().mark_starred(item.id, is_starred).await?; } Ok(()) } /// Get the database handle #[tracing::instrument(skip_all)] pub fn database(&self) -> &Database { &self.db } } #[cfg(test)] mod tests { use super::super::tests::*; use super::super::FeedGenerator; // ── mark_read / mark_starred ───────────────────────────────── #[tokio::test] async fn mark_read_updates_item() { let db = test_db().await; let feed = seed_feed(&db, "rss", "Feed").await; seed_item(&db, &feed, "rss:mr", 0).await; let fg = FeedGenerator::new(db.clone()); fg.mark_read("rss:mr", true).await.unwrap(); let item = db.items().get_by_external_id("rss:mr").await.unwrap().unwrap(); assert!(item.is_read); } #[tokio::test] async fn mark_starred_updates_item() { let db = test_db().await; let feed = seed_feed(&db, "rss", "Feed").await; seed_item(&db, &feed, "rss:ms", 0).await; let fg = FeedGenerator::new(db.clone()); fg.mark_starred("rss:ms", true).await.unwrap(); let item = db.items().get_by_external_id("rss:ms").await.unwrap().unwrap(); assert!(item.is_starred); } #[tokio::test] async fn mark_read_nonexistent_is_noop() { let db = test_db().await; let fg = FeedGenerator::new(db); // Should not error on missing item fg.mark_read("nonexistent:1", true).await.unwrap(); } // ── mark_starred edge case ────────────────────────────────── #[tokio::test] async fn mark_starred_nonexistent_is_noop() { let db = test_db().await; let fg = FeedGenerator::new(db); fg.mark_starred("nonexistent:1", true).await.unwrap(); } #[tokio::test] async fn mark_read_toggle_off() { let db = test_db().await; let feed = seed_feed(&db, "rss", "Feed").await; seed_item(&db, &feed, "rss:toggle", 0).await; let fg = FeedGenerator::new(db.clone()); fg.mark_read("rss:toggle", true).await.unwrap(); let item = db.items().get_by_external_id("rss:toggle").await.unwrap().unwrap(); assert!(item.is_read); fg.mark_read("rss:toggle", false).await.unwrap(); let item = db.items().get_by_external_id("rss:toggle").await.unwrap().unwrap(); assert!(!item.is_read); } #[tokio::test] async fn mark_starred_toggle_off() { let db = test_db().await; let feed = seed_feed(&db, "rss", "Feed").await; seed_item(&db, &feed, "rss:toggle", 0).await; let fg = FeedGenerator::new(db.clone()); fg.mark_starred("rss:toggle", true).await.unwrap(); let item = db.items().get_by_external_id("rss:toggle").await.unwrap().unwrap(); assert!(item.is_starred); fg.mark_starred("rss:toggle", false).await.unwrap(); let item = db.items().get_by_external_id("rss:toggle").await.unwrap().unwrap(); assert!(!item.is_starred); } }