From 4de127f2d210b0c07301ea4b805c20e486ea8863 Mon Sep 17 00:00:00 2001 From: Marc Plano-Lesay Date: Wed, 30 Apr 2025 22:19:26 +1000 Subject: [PATCH] Reject duplicate links --- .../2025-04-30-120032_unique_magnet/down.sql | 1 + .../2025-04-30-120032_unique_magnet/up.sql | 1 + src/db.rs | 76 +++++++++++++++++++ 3 files changed, 78 insertions(+) create mode 100644 migrations/2025-04-30-120032_unique_magnet/down.sql create mode 100644 migrations/2025-04-30-120032_unique_magnet/up.sql diff --git a/migrations/2025-04-30-120032_unique_magnet/down.sql b/migrations/2025-04-30-120032_unique_magnet/down.sql new file mode 100644 index 0000000..3057655 --- /dev/null +++ b/migrations/2025-04-30-120032_unique_magnet/down.sql @@ -0,0 +1 @@ +DROP INDEX magnets_unique_link; \ No newline at end of file diff --git a/migrations/2025-04-30-120032_unique_magnet/up.sql b/migrations/2025-04-30-120032_unique_magnet/up.sql new file mode 100644 index 0000000..3047930 --- /dev/null +++ b/migrations/2025-04-30-120032_unique_magnet/up.sql @@ -0,0 +1 @@ +CREATE UNIQUE INDEX magnets_unique_link ON magnets(link); \ No newline at end of file diff --git a/src/db.rs b/src/db.rs index fb1a126..9a04bd8 100644 --- a/src/db.rs +++ b/src/db.rs @@ -69,6 +69,73 @@ mod tests { assert_eq!(magnet.published_at, expected_timestamp); } } + + #[test] + fn test_prevent_duplicate_magnets() { + let temp_dir = tempdir().unwrap(); + let db_path = temp_dir.path().join("test.db"); + + let mut db = Database::new(&db_path).unwrap(); + + // Create initial post with magnet links + let post_info = PostInfo { + title: "Test Title".to_string(), + submitter: "test_user".to_string(), + subreddit: "test_subreddit".to_string(), + magnet_links: vec![ + "magnet:?xt=urn:btih:test1".to_string(), + "magnet:?xt=urn:btih:test2".to_string(), + ], + timestamp: Utc::now(), + }; + + // First insertion should succeed and insert 2 links + let result = db.store_magnets(&post_info); + assert!(result.is_ok()); + assert_eq!(result.unwrap(), 2); // 2 links inserted + + // Create a second post with some duplicate and some new links + let post_info2 = PostInfo { + title: "Test Title 2".to_string(), + submitter: "test_user2".to_string(), + subreddit: "test_subreddit2".to_string(), + magnet_links: vec![ + "magnet:?xt=urn:btih:test1".to_string(), // Duplicate + "magnet:?xt=urn:btih:test3".to_string(), // New + ], + timestamp: Utc::now(), + }; + + // Second insertion should succeed but only insert the new link + let result2 = db.store_magnets(&post_info2); + assert!(result2.is_ok()); + assert_eq!(result2.unwrap(), 1); // Only 1 new link inserted + + // Verify we have 3 total links in the database + let magnets = db.get_all_magnets().unwrap(); + assert_eq!(magnets.len(), 3); + + // Try inserting only duplicates + let post_info3 = PostInfo { + title: "Test Title 3".to_string(), + submitter: "test_user3".to_string(), + subreddit: "test_subreddit3".to_string(), + magnet_links: vec![ + "magnet:?xt=urn:btih:test1".to_string(), // Duplicate + "magnet:?xt=urn:btih:test2".to_string(), // Duplicate + ], + timestamp: Utc::now(), + }; + + // Third insertion should succeed but insert 0 links + let result3 = db.store_magnets(&post_info3); + assert!(result3.is_ok()); + assert_eq!(result3.unwrap(), 0); // No new links inserted + + // Verify we still have 3 total links in the database + let magnets = db.get_all_magnets().unwrap(); + assert_eq!(magnets.len(), 3); + } } impl Database { @@ -103,9 +170,18 @@ impl Database { pub fn store_magnets(&mut self, post: &PostInfo) -> Result { let published_at = post.timestamp.naive_utc(); + + // Filter out magnet links that already exist in the database + let existing_links: Vec = magnets::table + .select(magnets::link) + .filter(magnets::link.eq_any(&post.magnet_links)) + .load(&mut self.conn) + .wrap_err("Failed to query existing magnets")?; + let links = post .magnet_links .iter() + .filter(|link| !existing_links.contains(link)) .map(|m| NewMagnet { title: post.title.as_str(), submitter: post.submitter.as_str(),