Skip to content

Commit 939bf67

Browse files
authored
Merge pull request #46 from pointfreeco/in-memory-metadatabase
Allow in-memory databases in tests/previews
2 parents 0714578 + ac127aa commit 939bf67

File tree

3 files changed

+73
-41
lines changed

3 files changed

+73
-41
lines changed

Sources/SharingGRDBCore/CloudKit/Metadatabase.swift

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,25 @@ func defaultMetadatabase(
2323
at: .applicationSupportDirectory,
2424
withIntermediateDirectories: true
2525
)
26-
let metadatabase = try DatabasePool(
27-
path: url.path(percentEncoded: false),
28-
configuration: configuration
29-
)
26+
27+
@Dependency(\.context) var context
28+
guard !url.isInMemory || context != .live
29+
else {
30+
struct InMemoryDatabase: Error {}
31+
throw InMemoryDatabase()
32+
}
33+
34+
let metadatabase: any DatabaseWriter = if url.isInMemory {
35+
try DatabaseQueue(
36+
path: url.absoluteString,
37+
configuration: configuration
38+
)
39+
} else {
40+
try DatabasePool(
41+
path: url.path(percentEncoded: false),
42+
configuration: configuration
43+
)
44+
}
3045
// TODO: go towards idempotent migrations instead of GRDB migrator by the end of all of this
3146
var migrator = DatabaseMigrator()
3247
// TODO: do we want this?

Sources/SharingGRDBCore/CloudKit/SyncEngine.swift

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1417,19 +1417,16 @@
14171417
databasePath: String,
14181418
containerIdentifier: String?
14191419
) throws -> URL {
1420-
guard
1421-
let databaseURL = URL(string: databasePath),
1422-
!databaseURL.isInMemory
1420+
guard let databaseURL = URL(string: databasePath)
14231421
else {
1424-
throw SyncEngine.SchemaError(
1425-
reason: .inMemoryDatabase,
1426-
debugDescription: """
1427-
Can't synchronize temporary/in-memory database: it must be written to the file system.
1428-
"""
1429-
)
1422+
struct InvalidDatabsePath: Error {}
1423+
throw InvalidDatabsePath()
1424+
}
1425+
guard !databaseURL.isInMemory
1426+
else {
1427+
return URL(string: "file:\(String.sqliteDataCloudKitSchemaName)?mode=memory&cache=shared")!
14301428
}
1431-
return
1432-
databaseURL
1429+
return databaseURL
14331430
.deletingLastPathComponent()
14341431
.appending(component: ".\(databaseURL.deletingPathExtension().lastPathComponent)")
14351432
.appendingPathExtension("metadata\(containerIdentifier.map { "-\($0)" } ?? "").sqlite")

Tests/SharingGRDBTests/CloudKitTests/SyncEngineTests.swift

Lines changed: 46 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import CloudKit
22
import CustomDump
3+
import DependenciesTestSupport
34
import Foundation
45
import InlineSnapshotTesting
56
import SharingGRDB
@@ -8,29 +9,7 @@ import Testing
89

910
extension BaseCloudKitTests {
1011
@MainActor
11-
final class SyncEngineTests: BaseCloudKitTests, @unchecked Sendable {
12-
#if os(macOS) && compiler(>=6.2)
13-
@available(iOS 17, macOS 14, tvOS 17, watchOS 10, *)
14-
@Test func foreignKeysDisabled() throws {
15-
let result = #expect(
16-
processExitsWith: .failure,
17-
observing: [\.standardErrorContent]
18-
) {
19-
// TODO: finish in Xcode 26
20-
// _ = try SyncEngine(
21-
// syncEngine.private: MockSyncEngine(scope: .private, state: MockSyncEngineState()),
22-
// syncEngine.shared: MockSyncEngine(scope: .shared, state: MockSyncEngineState()),
23-
// database: databaseWithForeignKeys(),
24-
// tables: []
25-
// )
26-
}
27-
#expect(
28-
String(decoding: try #require(result).standardOutputContent, as: UTF8.self)
29-
== "Foreign key support must be disabled to synchronize with CloudKit."
30-
)
31-
}
32-
#endif
33-
12+
final class SyncEngineTests {
3413
@Test func inMemory() throws {
3514
#expect(URL(string: "")?.isInMemory == nil)
3615
#expect(URL(string: ":memory:")?.isInMemory == true)
@@ -39,14 +18,55 @@ extension BaseCloudKitTests {
3918
#expect(URL(string: "file:memdb1?mode=memory&cache=shared")?.isInMemory == true)
4019
}
4120

21+
@Test func inMemoryUserDatabase() async throws {
22+
let syncEngine = try await SyncEngine(
23+
container: MockCloudContainer(
24+
containerIdentifier: "test",
25+
privateCloudDatabase: MockCloudDatabase(databaseScope: .private),
26+
sharedCloudDatabase: MockCloudDatabase(databaseScope: .shared)
27+
),
28+
userDatabase: UserDatabase(database: DatabaseQueue()),
29+
tables: []
30+
)
31+
32+
try await syncEngine.userDatabase.read { db in
33+
try SQLQueryExpression(
34+
"""
35+
SELECT 1 FROM "sqlitedata_icloud_metadata"
36+
"""
37+
)
38+
.execute(db)
39+
}
40+
}
41+
42+
@Test(.dependency(\.context, .live))
43+
func inMemoryUserDatabase_LiveContext() async throws {
44+
let error = await #expect(throws: (any Error).self) {
45+
try await SyncEngine(
46+
container: MockCloudContainer(
47+
containerIdentifier: "test",
48+
privateCloudDatabase: MockCloudDatabase(databaseScope: .private),
49+
sharedCloudDatabase: MockCloudDatabase(databaseScope: .shared)
50+
),
51+
userDatabase: UserDatabase(database: DatabaseQueue()),
52+
tables: []
53+
)
54+
}
55+
assertInlineSnapshot(of: error, as: .customDump) {
56+
"""
57+
InMemoryDatabase()
58+
"""
59+
}
60+
}
61+
4262
@Test func metadatabaseMismatch() async throws {
4363
let error = await #expect(throws: (any Error).self) {
4464
var configuration = Configuration()
4565
configuration.prepareDatabase { db in
4666
try db.attachMetadatabase(containerIdentifier: "iCloud.co.pointfree")
4767
}
4868
let database = try DatabasePool(
49-
path: NSTemporaryDirectory() + UUID().uuidString,
69+
path: "/tmp/db.sqlite",
5070
configuration: configuration
5171
)
5272
_ = try await SyncEngine(
@@ -63,8 +83,8 @@ extension BaseCloudKitTests {
6383
#"""
6484
SyncEngine.SchemaError(
6585
reason: .metadatabaseMismatch(
66-
attachedPath: "/private/var/folders/vj/bzr5j4ld7cz6jgpphc5kbs8m0000gn/T/.C1938F73-8A6E-40BA-BCF5-A10C07CA1EB6.metadata-iCloud.co.pointfree.sqlite",
67-
syncEngineConfiguredPath: "/var/folders/vj/bzr5j4ld7cz6jgpphc5kbs8m0000gn/T/.C1938F73-8A6E-40BA-BCF5-A10C07CA1EB6.metadata-iCloud.co.point-free.sqlite"
86+
attachedPath: "/private/tmp/.db.metadata-iCloud.co.pointfree.sqlite",
87+
syncEngineConfiguredPath: "/tmp/.db.metadata-iCloud.co.point-free.sqlite"
6888
),
6989
debugDescription: "Metadatabase attached in \'prepareDatabase\' does not match metadatabase prepared in \'SyncEngine.init\'. Are the CloudKit container identifiers different?"
7090
)

0 commit comments

Comments
 (0)