// // DatabaseManagerTests.swift // RSSuperTests // // Created on 3/29/26. // import XCTest @testable import RSSuper final class DatabaseManagerTests: XCTestCase { private var databaseManager: DatabaseManager! private var testSubscriptionId: String! override func setUp() { super.setUp() databaseManager = DatabaseManager.shared testSubscriptionId = UUID().uuidString // Clean up any existing test data try? databaseManager.deleteSubscription(id: testSubscriptionId) } override func tearDown() { // Clean up test data try? databaseManager.deleteSubscription(id: testSubscriptionId) databaseManager = nil super.tearDown() } // MARK: - Subscription CRUD Tests func testCreateSubscription() throws { let subscription = try databaseManager.createSubscription( id: testSubscriptionId, url: "https://example.com/feed.xml", title: "Test Subscription", category: "Technology", enabled: true, fetchInterval: 3600 ) XCTAssertEqual(subscription.id, testSubscriptionId) XCTAssertEqual(subscription.url, "https://example.com/feed.xml") XCTAssertEqual(subscription.title, "Test Subscription") XCTAssertEqual(subscription.category, "Technology") XCTAssertTrue(subscription.enabled) XCTAssertEqual(subscription.fetchInterval, 3600) } func testFetchSubscription() throws { // Create first _ = try databaseManager.createSubscription( id: testSubscriptionId, url: "https://example.com/feed.xml", title: "Test Subscription" ) // Fetch let fetched = try databaseManager.fetchSubscription(id: testSubscriptionId) XCTAssertNotNil(fetched) XCTAssertEqual(fetched?.id, testSubscriptionId) XCTAssertEqual(fetched?.title, "Test Subscription") } func testFetchSubscriptionNotFound() throws { let fetched = try databaseManager.fetchSubscription(id: "non-existent-id") XCTAssertNil(fetched) } func testFetchAllSubscriptions() throws { let id1 = UUID().uuidString let id2 = UUID().uuidString try databaseManager.createSubscription(id: id1, url: "https://example1.com", title: "Sub 1") try databaseManager.createSubscription(id: id2, url: "https://example2.com", title: "Sub 2") let subscriptions = try databaseManager.fetchAllSubscriptions() XCTAssertGreaterThanOrEqual(subscriptions.count, 2) // Cleanup try databaseManager.deleteSubscription(id: id1) try databaseManager.deleteSubscription(id: id2) } func testFetchEnabledSubscriptions() throws { let id1 = UUID().uuidString let id2 = UUID().uuidString try databaseManager.createSubscription(id: id1, url: "https://example1.com", title: "Sub 1", enabled: true) try databaseManager.createSubscription(id: id2, url: "https://example2.com", title: "Sub 2", enabled: false) let subscriptions = try databaseManager.fetchEnabledSubscriptions() XCTAssertTrue(subscriptions.allSatisfy { $0.enabled }) // Cleanup try databaseManager.deleteSubscription(id: id1) try databaseManager.deleteSubscription(id: id2) } func testUpdateSubscription() throws { _ = try databaseManager.createSubscription( id: testSubscriptionId, url: "https://example.com", title: "Original Title" ) let updated = try databaseManager.updateSubscription( try databaseManager.fetchSubscription(id: testSubscriptionId)!, title: "Updated Title", enabled: false ) XCTAssertEqual(updated.title, "Updated Title") XCTAssertFalse(updated.enabled) } func testDeleteSubscription() throws { _ = try databaseManager.createSubscription( id: testSubscriptionId, url: "https://example.com", title: "To Delete" ) try databaseManager.deleteSubscription(id: testSubscriptionId) let fetched = try databaseManager.fetchSubscription(id: testSubscriptionId) XCTAssertNil(fetched) } // MARK: - FeedItem CRUD Tests func testCreateFeedItem() throws { let subscription = try databaseManager.createSubscription( id: testSubscriptionId, url: "https://example.com", title: "Test Sub" ) let item = FeedItem( id: UUID().uuidString, title: "Test Article", link: "https://example.com/article", description: "Article description", content: "Full article content", author: "John Doe", published: Date(), subscriptionId: subscription.id, subscriptionTitle: subscription.title ) let created = try databaseManager.createFeedItem(item) XCTAssertEqual(created.id, item.id) XCTAssertEqual(created.title, "Test Article") XCTAssertEqual(created.subscriptionId, testSubscriptionId) } func testFetchFeedItem() throws { let subscription = try databaseManager.createSubscription( id: testSubscriptionId, url: "https://example.com", title: "Test Sub" ) let itemId = UUID().uuidString let item = FeedItem( id: itemId, title: "To Fetch", subscriptionId: subscription.id ) _ = try databaseManager.createFeedItem(item) let fetched = try databaseManager.fetchFeedItem(id: itemId) XCTAssertNotNil(fetched) XCTAssertEqual(fetched?.id, itemId) } func testFetchFeedItemsForSubscription() throws { let subscription = try databaseManager.createSubscription( id: testSubscriptionId, url: "https://example.com", title: "Test Sub" ) for i in 1...3 { let item = FeedItem( id: UUID().uuidString, title: "Article \(i)", subscriptionId: subscription.id ) _ = try databaseManager.createFeedItem(item) } let items = try databaseManager.fetchFeedItems(for: testSubscriptionId) XCTAssertEqual(items.count, 3) } func testFetchUnreadFeedItems() throws { let subscription = try databaseManager.createSubscription( id: testSubscriptionId, url: "https://example.com", title: "Test Sub" ) let readItem = FeedItem(id: UUID().uuidString, title: "Read", subscriptionId: subscription.id, read: true) let unreadItem = FeedItem(id: UUID().uuidString, title: "Unread", subscriptionId: subscription.id, read: false) _ = try databaseManager.createFeedItem(readItem) _ = try databaseManager.createFeedItem(unreadItem) let unreadItems = try databaseManager.fetchUnreadFeedItems() XCTAssertTrue(unreadItems.allSatisfy { !$0.read }) } func testMarkAsRead() throws { let subscription = try databaseManager.createSubscription( id: testSubscriptionId, url: "https://example.com", title: "Test Sub" ) let item1 = FeedItem(id: UUID().uuidString, title: "Item 1", subscriptionId: subscription.id, read: false) let item2 = FeedItem(id: UUID().uuidString, title: "Item 2", subscriptionId: subscription.id, read: false) _ = try databaseManager.createFeedItem(item1) _ = try databaseManager.createFeedItem(item2) try databaseManager.markAsRead(ids: [item1.id, item2.id]) let fetched1 = try databaseManager.fetchFeedItem(id: item1.id) let fetched2 = try databaseManager.fetchFeedItem(id: item2.id) XCTAssertTrue(fetched1?.read ?? false) XCTAssertTrue(fetched2?.read ?? false) } func testDeleteFeedItem() throws { let subscription = try databaseManager.createSubscription( id: testSubscriptionId, url: "https://example.com", title: "Test Sub" ) let item = FeedItem(id: UUID().uuidString, title: "To Delete", subscriptionId: subscription.id) _ = try databaseManager.createFeedItem(item) try databaseManager.deleteFeedItem(id: item.id) let fetched = try databaseManager.fetchFeedItem(id: item.id) XCTAssertNil(fetched) } // MARK: - SearchHistory Tests func testAddToSearchHistory() throws { let item = try databaseManager.addToSearchHistory(query: "test query") XCTAssertEqual(item.query, "test query") XCTAssertNotNil(item.id) } func testFetchSearchHistory() throws { try databaseManager.addToSearchHistory(query: "Query 1") try databaseManager.addToSearchHistory(query: "Query 2") let history = try databaseManager.fetchSearchHistory() XCTAssertGreaterThanOrEqual(history.count, 2) } func testClearSearchHistory() throws { try databaseManager.addToSearchHistory(query: "To Clear") try databaseManager.clearSearchHistory() let history = try databaseManager.fetchSearchHistory() XCTAssertTrue(history.isEmpty) } // MARK: - FTS Search Tests func testFullTextSearch() throws { let subscription = try databaseManager.createSubscription( id: testSubscriptionId, url: "https://example.com", title: "Test Sub" ) let searchableItem = FeedItem( id: UUID().uuidString, title: "Unique Title for Search Test", description: "This has special content", subscriptionId: subscription.id ) let nonMatchingItem = FeedItem( id: UUID().uuidString, title: "Completely Different", subscriptionId: subscription.id ) _ = try databaseManager.createFeedItem(searchableItem) _ = try databaseManager.createFeedItem(nonMatchingItem) let results = try databaseManager.fullTextSearch(query: "Unique") XCTAssertTrue(results.contains { $0.id == searchableItem.id || $0.title.contains("Unique") }) XCTAssertFalse(results.contains { $0.id == nonMatchingItem.id }) } func testAdvancedSearch() throws { let subscription = try databaseManager.createSubscription( id: testSubscriptionId, url: "https://example.com", title: "Test Sub" ) let item1 = FeedItem( id: UUID().uuidString, title: "Searchable Title", author: "Test Author", subscriptionId: subscription.id ) let item2 = FeedItem( id: UUID().uuidString, title: "Different Title", author: "Other Author", subscriptionId: subscription.id ) _ = try databaseManager.createFeedItem(item1) _ = try databaseManager.createFeedItem(item2) let results = try databaseManager.advancedSearch(title: "Searchable", author: "Test") XCTAssertTrue(results.contains { $0.id == item1.id }) XCTAssertFalse(results.contains { $0.id == item2.id }) } // MARK: - Batch Operations Tests func testMarkAllAsRead() throws { let subscription = try databaseManager.createSubscription( id: testSubscriptionId, url: "https://example.com", title: "Test Sub" ) _ = try databaseManager.createFeedItem(FeedItem(id: UUID().uuidString, title: "Item 1", subscriptionId: subscription.id, read: false)) _ = try databaseManager.createFeedItem(FeedItem(id: UUID().uuidString, title: "Item 2", subscriptionId: subscription.id, read: false)) try databaseManager.markAllAsRead(for: testSubscriptionId) let items = try databaseManager.fetchFeedItems(for: testSubscriptionId) XCTAssertTrue(items.allSatisfy { $0.read }) } func testCleanupOldItems() throws { let subscription = try databaseManager.createSubscription( id: testSubscriptionId, url: "https://example.com", title: "Test Sub" ) let oldItem = FeedItem( id: UUID().uuidString, title: "Old Item", published: Calendar.current.date(byAdding: .day, value: -30, to: Date()), subscriptionId: subscription.id ) let newItem = FeedItem( id: UUID().uuidString, title: "New Item", published: Date(), subscriptionId: subscription.id ) _ = try databaseManager.createFeedItem(oldItem) _ = try databaseManager.createFeedItem(newItem) let deletedCount = try databaseManager.cleanupOldItems(olderThan: 7, for: testSubscriptionId) XCTAssertEqual(deletedCount, 1) let remainingItems = try databaseManager.fetchFeedItems(for: testSubscriptionId) XCTAssertEqual(remainingItems.count, 1) XCTAssertEqual(remainingItems.first?.id, newItem.id) } }