Skip to main content

max / balanced_breakfast

3.9 KB · 115 lines History Blame Raw
1 //! Item mutation and database access methods for the feed generator.
2
3 use bb_db::Database;
4 use super::{FeedError, FeedGenerator};
5
6 impl FeedGenerator {
7 /// Mark an item as read
8 #[tracing::instrument(skip_all)]
9 pub async fn mark_read(&self, item_id: &str, is_read: bool) -> Result<(), FeedError> {
10 // Find item by external_id
11 if let Some(item) = self.db.items().get_by_external_id(item_id).await? {
12 self.db.items().mark_read(item.id, is_read).await?;
13 }
14 Ok(())
15 }
16
17 /// Mark an item as starred
18 #[tracing::instrument(skip_all)]
19 pub async fn mark_starred(&self, item_id: &str, is_starred: bool) -> Result<(), FeedError> {
20 if let Some(item) = self.db.items().get_by_external_id(item_id).await? {
21 self.db.items().mark_starred(item.id, is_starred).await?;
22 }
23 Ok(())
24 }
25
26 /// Get the database handle
27 #[tracing::instrument(skip_all)]
28 pub fn database(&self) -> &Database {
29 &self.db
30 }
31 }
32
33 #[cfg(test)]
34 mod tests {
35 use super::super::tests::*;
36 use super::super::FeedGenerator;
37
38 // ── mark_read / mark_starred ─────────────────────────────────
39
40 #[tokio::test]
41 async fn mark_read_updates_item() {
42 let db = test_db().await;
43 let feed = seed_feed(&db, "rss", "Feed").await;
44 seed_item(&db, &feed, "rss:mr", 0).await;
45
46 let fg = FeedGenerator::new(db.clone());
47 fg.mark_read("rss:mr", true).await.unwrap();
48
49 let item = db.items().get_by_external_id("rss:mr").await.unwrap().unwrap();
50 assert!(item.is_read);
51 }
52
53 #[tokio::test]
54 async fn mark_starred_updates_item() {
55 let db = test_db().await;
56 let feed = seed_feed(&db, "rss", "Feed").await;
57 seed_item(&db, &feed, "rss:ms", 0).await;
58
59 let fg = FeedGenerator::new(db.clone());
60 fg.mark_starred("rss:ms", true).await.unwrap();
61
62 let item = db.items().get_by_external_id("rss:ms").await.unwrap().unwrap();
63 assert!(item.is_starred);
64 }
65
66 #[tokio::test]
67 async fn mark_read_nonexistent_is_noop() {
68 let db = test_db().await;
69 let fg = FeedGenerator::new(db);
70 // Should not error on missing item
71 fg.mark_read("nonexistent:1", true).await.unwrap();
72 }
73
74 // ── mark_starred edge case ──────────────────────────────────
75
76 #[tokio::test]
77 async fn mark_starred_nonexistent_is_noop() {
78 let db = test_db().await;
79 let fg = FeedGenerator::new(db);
80 fg.mark_starred("nonexistent:1", true).await.unwrap();
81 }
82
83 #[tokio::test]
84 async fn mark_read_toggle_off() {
85 let db = test_db().await;
86 let feed = seed_feed(&db, "rss", "Feed").await;
87 seed_item(&db, &feed, "rss:toggle", 0).await;
88
89 let fg = FeedGenerator::new(db.clone());
90 fg.mark_read("rss:toggle", true).await.unwrap();
91 let item = db.items().get_by_external_id("rss:toggle").await.unwrap().unwrap();
92 assert!(item.is_read);
93
94 fg.mark_read("rss:toggle", false).await.unwrap();
95 let item = db.items().get_by_external_id("rss:toggle").await.unwrap().unwrap();
96 assert!(!item.is_read);
97 }
98
99 #[tokio::test]
100 async fn mark_starred_toggle_off() {
101 let db = test_db().await;
102 let feed = seed_feed(&db, "rss", "Feed").await;
103 seed_item(&db, &feed, "rss:toggle", 0).await;
104
105 let fg = FeedGenerator::new(db.clone());
106 fg.mark_starred("rss:toggle", true).await.unwrap();
107 let item = db.items().get_by_external_id("rss:toggle").await.unwrap().unwrap();
108 assert!(item.is_starred);
109
110 fg.mark_starred("rss:toggle", false).await.unwrap();
111 let item = db.items().get_by_external_id("rss:toggle").await.unwrap().unwrap();
112 assert!(!item.is_starred);
113 }
114 }
115