Skip to content

Conversation

@rcarver
Copy link
Contributor

@rcarver rcarver commented Jul 19, 2025

This adds tests to ensure GRDB behaves correctly in async contexts, and thus that SharingGRDB and related tools behave correctly.

  • In general, do TaskLocal behave as expected inside async read/write
  • Specifically, does assertQuery behave correctly in async read/write

The GRDB side in progress per this discussion: groue/GRDB.swift#1793

To verify the changes to GRDB:

  1. Run tests, they will fail on GRDB 7.5.0
  2. Update GRDB to development branch and confirm tests pass

@rcarver rcarver force-pushed the test-grdb-tasklocal branch from abc7a51 to bb18a7f Compare July 19, 2025 21:41
@mbrandonw
Copy link
Member

Hi @rcarver, thanks for taking the time to do this, but I'm not sure we need these tests in our library. They are only testing behavior in GRDB proper. It would also force us to update our dependence on GRDB to be the newest in order to guarantee tests pass, but I don't think it's necessary to force that update on everyone using this library.

I am going to close this for now, but if you have reason to believe that we should definitely have these tests in SharingGRDB we're open to discussing more!

@mbrandonw mbrandonw closed this Jul 20, 2025
@rcarver
Copy link
Contributor Author

rcarver commented Jul 20, 2025

@mbrandonw totally makes sense, and was not sure how to handle this. I'd note that using GRDB < 7.6.0 (just released) in async contexts:

  1. Can cause unexpected behavior as TaskLocal values may not be set correctly. That affects many of your libraries, specifically TCA has sharp edges as running queries in effects and tests both force the use of async read/write.
  2. assertQuery is broken in confusing ways. I originally encountered these issues by trying to use assertQuery in TCA tests

Absolutely up to you about including the tests, really just wanted to prove that GRDB is working now

@mbrandonw
Copy link
Member

Hi @rcarver, we typically like to be a little conservative with bumping our dependencies. We've seen many times people get stuck in a difficult spot where they can no longer update dependencies because they have to restrict one library's version for whatever reason.

So we think it's ok for people to just update to the newest GRDB themselves if they run into this problem.

@rcarver
Copy link
Contributor Author

rcarver commented Jul 20, 2025

@mbrandonw I found a new issue with GRDB+SnapshotTesting. It's technically different, but related enough I thought I'd keep the discussion here for now (all issues contribute to not being able to use assertQuery with GRDB)

Description

Any use of assertInlineSnapshot within database.read (sync) in a test tagged @MainActor will crash on this line of swift-snapshot-testing

This is unrelated the async issue, as I can confirm with GRDB 7.4.0 to 7.6.0

import GRDB
import InlineSnapshotTesting
import Testing

@MainActor
@Test
func snapshotInsideReadMainActor() throws {
  let database = try DatabaseQueue()
  try database.read { db in
    assertInlineSnapshot(of: "foo", as: .lines)  // 💥 crashes
  }
}

The same crash happens in XCTest without tagging @MainActor

import GRDB
import InlineSnapshotTesting
import XCTest

final class SnapshotXCTests: XCTestCase {
  func testInlineSnapshotInsideDatabaseRead() throws {
    let database = try DatabaseQueue()
    try database.read { db in
      assertInlineSnapshot(of: "foo", as: .lines)  // 💥 crashes
    }
  }
}

You can also see the crash by simply adding @MainActor to this test from this PR https://github.com/pointfreeco/sharing-grdb/pull/104/files#diff-feea7bd1c079229efc7dc970c6c9fb9a114307d7952149f0b2d76dd75c7f6b04R28


Here is a full set of sync/async MainActor/non-MainActor tests just to fully understand what works and what doesn't.

import GRDB
import InlineSnapshotTesting
import Testing

@Test
func snapshotInsideRead() throws {
  let database = try DatabaseQueue()
  try database.read { db in
    assertInlineSnapshot(of: "foo", as: .lines) {
      """
      foo
      """
    }
  }
}

@MainActor
@Test("always fails")
func snapshotInsideReadMainActor() throws {
  let database = try DatabaseQueue()
  try database.read { db in
    assertInlineSnapshot(of: "foo", as: .lines)  // 💥 crashes
  }
}

@Test("works with GRDB 7.6.0 only")
func snapshotInsideAsyncRead() async throws {
  let database = try DatabaseQueue()
  try await database.read { db in
    assertInlineSnapshot(of: "foo", as: .lines) {
      """
      foo
      """
    }
  }
}

@MainActor
@Test("also only works with GRDB 7.6.0")
func snapshotInsideAsyncReadMainActor() async throws {
  let database = try DatabaseQueue()
  try await database.read { db in
    assertInlineSnapshot(of: "foo", as: .lines) {
      """
      foo
      """
    }
  }
}

@mbrandonw
Copy link
Member

Hi @rcarver, good catch! This is easy to fix and we will have a PR soon.

@stephencelis
Copy link
Member

@rcarver Can you point your project to this branch of swift-snapshot-testing and report back? pointfreeco/swift-snapshot-testing#1012

@rcarver
Copy link
Contributor Author

rcarver commented Jul 22, 2025

@stephencelis works great!

  • Confirmed this branch fixes the above unit tests
  • Confirmed this branch fixed using assertQuery in my app's tests in a sync context
  • Further confirmed that this branch + GRDB 7.6.0 fixes all known issues with async read/write, using dependencies and inline snapshot testing

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants