Repository: Automattic/pocket-casts-ios
File: PocketCastsTests/Helpers/DBTestCase.swift (Swift)

Code before:
import XCTest
@testable import PocketCastsDataModel
import FMDB
@testable import podcasts

class DBTestCase: XCTestCase {
    var dataManager: DataManager!
    var downloadManager: DownloadManager!
    var podcast: Podcast!
    var episode: Episode!

    override func setUp() async throws {
        try await super.setUp()
        try setupData()
    }

    private func setupDatabase() throws -> DataManager {
        DataManager.newTestDataManager()
    }

    private func setupData() throws {
        let dataManager = try setupDatabase()
        let downloadManager = DownloadManager(dataManager: dataManager)

        let podcast = Podcast()
        podcast.uuid = UUID().uuidString
        podcast.subscribed = 0
        podcast.addedDate = Date().addingTimeInterval(-1.week)
        podcast.syncStatus = SyncStatus.synced.rawValue

        dataManager.save(podcast: podcast)

        let episode = Episode()
        episode.uuid = UUID().uuidString
        episode.podcastUuid = podcast.uuid
        episode.podcast_id = podcast.id
        episode.addedDate = podcast.addedDate

Review comment (bug):
@bjtitus I think you originally worked on these tests, so I just want to provide some more information that wouldn't be possible in this comment.

**What was happening?**

`PodcastManagerTests.testTaskCancellationForUnusednDeletion` was failing.

**Why?**

We are actually creating download tasks when doing those tests. The issue is that while the tests are running the `DownloadManager+URLSessionDelegate.urlSession(_ session: URLSession, task: URLSessionTask, didFinishCollecting metrics: URLSessionTaskMetrics)` method was triggering.

This is where the issue begins: FMDB suppresses errors, with GRDB, we're surfacing them purposely, given we want to test it. 

This was leading to a crash because the "app" `DataManager` would try to search for a given episode in a database connection that was already **closed**.

This is fixed by ensuring just one `DataManager` is shared across these tests.

But I just want to point out that I'm not sure we want this level of integration tests (where delegates are triggered). We should probably create these tasks as [ephemeral](https://developer.apple.com/documentation/foundation/urlsessionconfiguration/ephemeral) ones — this way the delegates won't be triggered.

This is not a big problem to leave as it is now but I want to add additional context because at first I wasn't unsure if the tests were pointing to a problem or if it was an integration problem.

I added some logs that explain what is happening:

```
$$ added F8D605DC-96E9-4FAB-BBBF-624B15398241 in 0x0000600003b6f640
$$ Checking for F8D605DC-96E9-4FAB-BBBF-624B15398241 0x0000600003b6f640
$$ Checking for F8D605DC-96E9-4FAB-BBBF-624B15398241 0x0000600003b6f640
$$ added 5289B3F2-7D96-4EF6-960B-354B4D2BC365 in 0x0000600003b708c0
$$ Checking for 5289B3F2-7D96-4EF6-960B-354B4D2BC365 0x0000600003b6f640
```

The memory pointer is a reference to `DataManager`. Notice how the penultimate line add an episode to download in a DataManager, but in the next line the app tries to find it in the previous DataManager — which the connection with the database has already been closed.

**What to do?**

We don't need to do anything because it's a crash happening due to how the tests are set up.

Code after:
import XCTest
@testable import PocketCastsDataModel
import FMDB
@testable import podcasts

class DBTestCase: XCTestCase {
    // We use a single DataManager instance for tests based on DBTestCase,
    // since some tests interact with the download logic.
    // Creating multiple DataManager instances cause the app delegate
    // to reference an outdated one with a closed database.
    // This issue is silently ignored when using FMDB, but GRDB surfaces an error.
    static var dataManager: DataManager!
    var dataManager: DataManager! {
        Self.dataManager
    }
    var downloadManager: DownloadManager!
    var podcast: Podcast!
    var episode: Episode!

    override func setUp() async throws {
        try await super.setUp()
        try setupData()
    }

    private func setupDatabase() throws -> DataManager {
        DataManager.newTestDataManager()
    }

    private func setupData() throws {
        let dataManager = Self.dataManager == nil ? try setupDatabase() : Self.dataManager!
        let downloadManager = DownloadManager(dataManager: dataManager)

        let podcast = Podcast()
        podcast.uuid = UUID().uuidString
        podcast.subscribed = 0
        podcast.addedDate = Date().addingTimeInterval(-1.week)
        podcast.syncStatus = SyncStatus.synced.rawValue