/* * DatabaseTests.vala * * Unit tests for database layer. */ public class RSSuper.DatabaseTests { private Database? db; private string test_db_path; public void run_subscription_crud() { try { test_db_path = "/tmp/rssuper_test_%d.db".printf((int)new DateTime.now_local().to_unix()); db = new Database(test_db_path); } catch (DBError e) { warning("Failed to create test database: %s", e.message); return; } try { var store = new SubscriptionStore(db); // Create test subscription var subscription = new FeedSubscription.with_values( "sub_1", "https://example.com/feed.xml", "Example Feed", 60, "Technology", true, "2024-01-01T00:00:00Z", "2024-01-01T00:00:00Z" ); // Test add store.add(subscription); var retrieved = store.get_by_id("sub_1"); if (retrieved == null) { printerr("FAIL: Expected subscription to exist after add\n"); return; } // Test get if (retrieved.title != "Example Feed") { printerr("FAIL: Expected title 'Example Feed', got '%s'\n", retrieved.title); return; } if (retrieved.url != "https://example.com/feed.xml") { printerr("FAIL: Expected url 'https://example.com/feed.xml', got '%s'\n", retrieved.url); return; } // Test update retrieved.title = "Updated Feed"; store.update(retrieved); var updated = store.get_by_id("sub_1"); if (updated.title != "Updated Feed") { printerr("FAIL: Expected title 'Updated Feed', got '%s'\n", updated.title); return; } // Test delete store.remove_subscription("sub_1"); var deleted = store.get_by_id("sub_1"); if (deleted != null) { printerr("FAIL: Expected subscription to be deleted\n"); return; } print("PASS: test_subscription_crud\n"); } finally { cleanup(); } } public void run_subscription_list() { try { test_db_path = "/tmp/rssuper_test_%d.db".printf((int)new DateTime.now_local().to_unix()); db = new Database(test_db_path); } catch (DBError e) { warning("Failed to create test database: %s", e.message); return; } try { var store = new SubscriptionStore(db); // Add multiple subscriptions var sub1 = new FeedSubscription.with_values("sub_1", "https://feed1.com", "Feed 1"); var sub2 = new FeedSubscription.with_values("sub_2", "https://feed2.com", "Feed 2"); var sub3 = new FeedSubscription.with_values("sub_3", "https://feed3.com", "Feed 3", 60, null, false); store.add(sub1); store.add(sub2); store.add(sub3); // Test get_all var all = store.get_all(); if (all.length != 3) { printerr("FAIL: Expected 3 subscriptions, got %d\n", all.length); return; } // Test get_enabled var enabled = store.get_enabled(); if (enabled.length != 2) { printerr("FAIL: Expected 2 enabled subscriptions, got %d\n", enabled.length); return; } print("PASS: test_subscription_list\n"); } finally { cleanup(); } } public void run_feed_item_crud() { try { test_db_path = "/tmp/rssuper_test_%d.db".printf((int)new DateTime.now_local().to_unix()); db = new Database(test_db_path); } catch (DBError e) { warning("Failed to create test database: %s", e.message); return; } try { var sub_store = new SubscriptionStore(db); var item_store = new FeedItemStore(db); // Create subscription first var subscription = new FeedSubscription.with_values( "sub_1", "https://example.com/feed.xml", "Example Feed" ); sub_store.add(subscription); // Create test item var item = new FeedItem.with_values( "item_1", "Test Article", "https://example.com/article", "This is a test description", "Full content of the article", "John Doe", "2024-01-01T12:00:00Z", "2024-01-01T12:00:00Z", {"Technology", "News"}, null, null, null, null, "sub_1" // subscription_id (stored as subscription_title in DB) ); // Test add item_store.add(item); var retrieved = item_store.get_by_id("item_1"); if (retrieved == null) { printerr("FAIL: Expected item to exist after add\n"); return; } if (retrieved.title != "Test Article") { printerr("FAIL: Expected title 'Test Article', got '%s'\n", retrieved.title); return; } // Test get by subscription var items = item_store.get_by_subscription("sub_1"); if (items.length != 1) { printerr("FAIL: Expected 1 item, got %d\n", items.length); return; } // Test mark as read item_store.mark_as_read("item_1"); // Test delete item_store.delete("item_1"); var deleted = item_store.get_by_id("item_1"); if (deleted != null) { printerr("FAIL: Expected item to be deleted\n"); return; } print("PASS: test_feed_item_crud\n"); } finally { cleanup(); } } public void run_feed_item_batch() { try { test_db_path = "/tmp/rssuper_test_%d.db".printf((int)new DateTime.now_local().to_unix()); db = new Database(test_db_path); } catch (DBError e) { warning("Failed to create test database: %s", e.message); return; } try { var sub_store = new SubscriptionStore(db); var item_store = new FeedItemStore(db); // Create subscription var subscription = new FeedSubscription.with_values( "sub_1", "https://example.com/feed.xml", "Example Feed" ); sub_store.add(subscription); // Create multiple items var items = new FeedItem[5]; for (var i = 0; i < 5; i++) { items[i] = new FeedItem.with_values( "item_%d".printf(i), "Article %d".printf(i), "https://example.com/article%d".printf(i), "Description %d".printf(i), null, "Author %d".printf(i), "2024-01-%02dT12:00:00Z".printf(i + 1), null, null, null, null, null, null, "sub_1" // subscription_id ); } // Test batch insert item_store.add_batch(items); var all = item_store.get_by_subscription("sub_1"); if (all.length != 5) { printerr("FAIL: Expected 5 items, got %d\n", all.length); return; } print("PASS: test_feed_item_batch\n"); } finally { cleanup(); } } public void run_search_history() { try { test_db_path = "/tmp/rssuper_test_%d.db".printf((int)new DateTime.now_local().to_unix()); db = new Database(test_db_path); } catch (DBError e) { warning("Failed to create test database: %s", e.message); return; } try { var store = new SearchHistoryStore(db); // Create test queries var query1 = SearchQuery("test query", 1, 20, null, SearchSortOption.RELEVANCE); var query2 = SearchQuery("another search", 1, 10, null, SearchSortOption.DATE_DESC); // Test record store.record_search(query1, 15); store.record_search(query2, 8); // Test get_history var history = store.get_history(); if (history.length != 2) { printerr("FAIL: Expected 2 history entries, got %d\n", history.length); return; } // Check that both queries are in history (order may vary due to timing) bool found_test_query = false; bool found_another_search = false; foreach (var q in history) { if (q.query == "test query") found_test_query = true; if (q.query == "another search") found_another_search = true; } if (!found_test_query || !found_another_search) { printerr("FAIL: Expected both queries in history\n"); return; } // Test get_recent var recent = store.get_recent(); if (recent.length != 2) { printerr("FAIL: Expected 2 recent entries, got %d\n", recent.length); return; } print("PASS: test_search_history\n"); } finally { cleanup(); } } public void run_fts_search() { try { test_db_path = "/tmp/rssuper_test_%d.db".printf((int)new DateTime.now_local().to_unix()); db = new Database(test_db_path); } catch (DBError e) { warning("Failed to create test database: %s", e.message); return; } try { var sub_store = new SubscriptionStore(db); var item_store = new FeedItemStore(db); // Create subscription var subscription = new FeedSubscription.with_values( "sub_1", "https://example.com/feed.xml", "Example Feed" ); sub_store.add(subscription); // Add items with searchable content var item1 = new FeedItem.with_values( "item_1", "Swift Programming Guide", "https://example.com/swift", "Learn Swift programming language basics", "A comprehensive guide to Swift", "Apple Developer", "2024-01-01T12:00:00Z", null, null, null, null, null, null, "sub_1" // subscription_id ); var item2 = new FeedItem.with_values( "item_2", "Python for Data Science", "https://example.com/python", "Data analysis with Python and pandas", "Complete Python data science tutorial", "Data Team", "2024-01-02T12:00:00Z", null, null, null, null, null, null, "sub_1" // subscription_id ); item_store.add(item1); item_store.add(item2); // Test FTS search (returns SearchResult) var results = item_store.search("swift"); if (results.length != 1) { printerr("FAIL: Expected 1 result for 'swift', got %d\n", results.length); return; } if (results[0].title != "Swift Programming Guide") { printerr("FAIL: Expected 'Swift Programming Guide', got '%s'\n", results[0].title); return; } results = item_store.search("python"); if (results.length != 1) { printerr("FAIL: Expected 1 result for 'python', got %d\n", results.length); return; } if (results[0].title != "Python for Data Science") { printerr("FAIL: Expected 'Python for Data Science', got '%s'\n", results[0].title); return; } // Test fuzzy search results = item_store.search_fuzzy("swif"); if (results.length != 1) { printerr("FAIL: Expected 1 result for fuzzy 'swif', got %d\n", results.length); return; } print("PASS: test_fts_search\n"); } finally { cleanup(); } } private void cleanup() { if (db != null) { db.close(); db = null; } // Clean up test database if (test_db_path != null && test_db_path.length > 0) { var file = File.new_for_path(test_db_path); if (file.query_exists()) { try { file.delete(); } catch (Error e) { warning("Failed to delete test database: %s", e.message); } } // Clean up WAL file var wal_file = File.new_for_path(test_db_path + "-wal"); if (wal_file.query_exists()) { try { wal_file.delete(); } catch (Error e) { warning("Failed to delete WAL file: %s", e.message); } } } } public void run_search_service() { try { test_db_path = "/tmp/rssuper_test_%d.db".printf((int)new DateTime.now_local().to_unix()); db = new Database(test_db_path); } catch (DBError e) { warning("Failed to create test database: %s", e.message); return; } try { // Create subscription var sub_store = new SubscriptionStore(db); var subscription = new FeedSubscription.with_values( "sub_1", "https://example.com/feed.xml", "Example Feed" ); sub_store.add(subscription); // Add test items var item_store = new FeedItemStore(db); var item1 = new FeedItem.with_values( "item_1", "Introduction to Rust Programming", "https://example.com/rust", "Learn Rust programming language", "Complete Rust tutorial for beginners", "Rust Team", "2024-01-01T12:00:00Z", null, null, null, null, null, null, "sub_1" ); var item2 = new FeedItem.with_values( "item_2", "Advanced Rust Patterns", "https://example.com/rust-advanced", "Advanced Rust programming patterns", "Deep dive into Rust patterns and best practices", "Rust Team", "2024-01-02T12:00:00Z", null, null, null, null, null, null, "sub_1" ); item_store.add(item1); item_store.add(item2); // Test search service var search_service = new SearchService(db); // Perform search var results = search_service.search("rust"); if (results.length != 2) { printerr("FAIL: Expected 2 results for 'rust', got %d\n", results.length); return; } // Check history var history = search_service.get_history(); if (history.length != 1) { printerr("FAIL: Expected 1 history entry, got %d\n", history.length); return; } if (history[0].query != "rust") { printerr("FAIL: Expected query 'rust', got '%s'\n", history[0].query); return; } // Test fuzzy search results = search_service.search("rus"); if (results.length != 2) { printerr("FAIL: Expected 2 results for fuzzy 'rus', got %d\n", results.length); return; } // Test suggestions var suggestions = search_service.get_suggestions("rust"); if (suggestions.length == 0) { printerr("FAIL: Expected at least 1 suggestion for 'rust'\n"); return; } print("PASS: test_search_service\n"); } finally { cleanup(); } } public void run_bookmark_store() { try { test_db_path = "/tmp/rssuper_test_%d.db".printf((int)new DateTime.now_local().to_unix()); db = new Database(test_db_path); } catch (DBError e) { warning("Failed to create test database: %s", e.message); return; } try { // Create subscription var sub_store = new SubscriptionStore(db); var subscription = new FeedSubscription.with_values( "sub_1", "https://example.com/feed.xml", "Example Feed" ); sub_store.add(subscription); // Add test item var item_store = new FeedItemStore(db); var item = new FeedItem.with_values( "item_1", "Test Article", "https://example.com/test", "Test description", "Test content", "Test Author", "2024-01-01T12:00:00Z", null, null, null, null, null, null, "sub_1" ); item_store.add(item); // Test bookmark store var bookmark_store = new BookmarkStore(db); // Create bookmark var bookmark = new Bookmark.with_values( "bookmark_1", "item_1", "Test Article", "https://example.com/test", "Test description", "Test content", "2024-01-01T12:00:00Z", "test,important" ); // Add bookmark bookmark_store.add(bookmark); // Get bookmark by ID var retrieved = bookmark_store.get_by_id("bookmark_1"); if (retrieved == null) { printerr("FAIL: Expected bookmark to exist after add\n"); return; } if (retrieved.title != "Test Article") { printerr("FAIL: Expected title 'Test Article', got '%s'\n", retrieved.title); return; } // Get all bookmarks var all = bookmark_store.get_all(); if (all.length != 1) { printerr("FAIL: Expected 1 bookmark, got %d\n", all.length); return; } // Get bookmark count var count = bookmark_store.count(); if (count != 1) { printerr("FAIL: Expected count 1, got %d\n", count); return; } // Get bookmarks by tag var tagged = bookmark_store.get_by_tag("test"); if (tagged.length != 1) { printerr("FAIL: Expected 1 bookmark by tag 'test', got %d\n", tagged.length); return; } // Update bookmark retrieved.tags = "updated,important"; bookmark_store.update(retrieved); // Delete bookmark bookmark_store.delete("bookmark_1"); // Verify deletion var deleted = bookmark_store.get_by_id("bookmark_1"); if (deleted != null) { printerr("FAIL: Expected bookmark to be deleted\n"); return; } // Check count after deletion count = bookmark_store.count(); if (count != 0) { printerr("FAIL: Expected count 0 after delete, got %d\n", count); return; } print("PASS: test_bookmark_store\n"); } finally { cleanup(); } } public static int main(string[] args) { print("Running database tests...\n"); var tests = new DatabaseTests(); print("\n=== Running subscription CRUD tests ==="); tests.run_subscription_crud(); print("\n=== Running subscription list tests ==="); tests.run_subscription_list(); print("\n=== Running feed item CRUD tests ==="); tests.run_feed_item_crud(); print("\n=== Running feed item batch tests ==="); tests.run_feed_item_batch(); print("\n=== Running search history tests ==="); tests.run_search_history(); print("\n=== Running FTS search tests ==="); tests.run_fts_search(); print("\n=== Running search service tests ==="); tests.run_search_service(); print("\n=== Running bookmark store tests ==="); tests.run_bookmark_store(); print("\nAll tests completed!\n"); return 0; } }