Files
RSSuper/linux/src/tests/database-tests.vala
Michael Freno 14efe072fa feat: implement cross-platform features and UI integration
- iOS: Add BackgroundSyncService, SyncScheduler, SyncWorker, BookmarkViewModel, FeedViewModel
- iOS: Add BackgroundSyncService, SyncScheduler, SyncWorker services
- Linux: Add settings-store.vala, State.vala signals, view widgets (FeedList, FeedDetail, AddFeed, Search, Settings, Bookmark)
- Linux: Add bookmark-store.vala, bookmark vala model, search-service.vala
- Android: Add NotificationService, NotificationManager, NotificationPreferencesStore
- Android: Add BookmarkDao, BookmarkRepository, SettingsStore
- Add unit tests for iOS, Android, Linux
- Add integration tests
- Add performance benchmarks
- Update tasks and documentation

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-03-30 23:06:12 -04:00

639 lines
22 KiB
Vala

/*
* 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;
}
}