import XCTest import Foundation @testable import GRDB private protocol StrategyProvider { static var strategy: DatabaseUUIDEncodingStrategy { get } } private enum StrategyDeferredToUUID: StrategyProvider { static let strategy: DatabaseUUIDEncodingStrategy = .deferredToUUID } private enum StrategyUppercaseString: StrategyProvider { static let strategy: DatabaseUUIDEncodingStrategy = .uppercaseString } private enum StrategyLowercaseString: StrategyProvider { static let strategy: DatabaseUUIDEncodingStrategy = .lowercaseString } private struct RecordWithUUID: EncodableRecord, Encodable { static var databaseUUIDEncodingStrategy: DatabaseUUIDEncodingStrategy { Strategy.strategy } var uuid: UUID } @available(iOS 13, macOS 10.15, tvOS 13, watchOS 6, *) extension RecordWithUUID: Identifiable { var id: UUID { uuid } } private struct RecordWithOptionalUUID: EncodableRecord, Encodable { static var databaseUUIDEncodingStrategy: DatabaseUUIDEncodingStrategy { Strategy.strategy } var uuid: UUID? } @available(iOS 13, macOS 10.15, tvOS 13, watchOS 6, *) extension RecordWithOptionalUUID: Identifiable { var id: UUID? { uuid } } class DatabaseUUIDEncodingStrategyTests: GRDBTestCase { private func test( record: T, expectedStorage: DatabaseValue.Storage) throws { var container = PersistenceContainer() try record.encode(to: &container) if let dbValue = container["uuid"]?.databaseValue { XCTAssertEqual(dbValue.storage, expectedStorage) } else { XCTAssertEqual(.null, expectedStorage) } } private func test( strategy: Strategy.Type, encodesUUID uuid: UUID, as value: any DatabaseValueConvertible) throws { try test(record: RecordWithUUID(uuid: uuid), expectedStorage: value.databaseValue.storage) try test(record: RecordWithOptionalUUID(uuid: uuid), expectedStorage: value.databaseValue.storage) } private func testNullEncoding(strategy: Strategy.Type) throws { try test(record: RecordWithOptionalUUID(uuid: nil), expectedStorage: .null) } } // MARK: - deferredToUUID extension DatabaseUUIDEncodingStrategyTests { func testDeferredToUUID() throws { try testNullEncoding(strategy: StrategyDeferredToUUID.self) try test( strategy: StrategyDeferredToUUID.self, encodesUUID: UUID(uuidString: "61626364-6566-6768-696A-6B6C6D6E6F70")!, as: "abcdefghijklmnop".data(using: .utf8)!) } } // MARK: - UppercaseString extension DatabaseUUIDEncodingStrategyTests { func testUppercaseString() throws { try testNullEncoding(strategy: StrategyUppercaseString.self) try test( strategy: StrategyUppercaseString.self, encodesUUID: UUID(uuidString: "61626364-6566-6768-696A-6B6C6D6E6F70")!, as: "61626364-6566-6768-696A-6B6C6D6E6F70") try test( strategy: StrategyUppercaseString.self, encodesUUID: UUID(uuidString: "56e7d8d3-e9e4-48b6-968e-8d102833af00")!, as: "56E7D8D3-E9E4-48B6-968E-8D102833AF00") let uuid = UUID() try test( strategy: StrategyUppercaseString.self, encodesUUID: uuid, as: uuid.uuidString.uppercased()) // Assert stable casing } } // MARK: - LowercaseString extension DatabaseUUIDEncodingStrategyTests { func testLowercaseString() throws { try testNullEncoding(strategy: StrategyLowercaseString.self) try test( strategy: StrategyLowercaseString.self, encodesUUID: UUID(uuidString: "61626364-6566-6768-696A-6B6C6D6E6F70")!, as: "61626364-6566-6768-696a-6b6c6d6e6f70") try test( strategy: StrategyLowercaseString.self, encodesUUID: UUID(uuidString: "56e7d8d3-e9e4-48b6-968e-8d102833af00")!, as: "56e7d8d3-e9e4-48b6-968e-8d102833af00") let uuid = UUID() try test( strategy: StrategyLowercaseString.self, encodesUUID: uuid, as: uuid.uuidString.lowercased()) // Assert stable casing } } // MARK: - Filter extension DatabaseUUIDEncodingStrategyTests { func testFilterKey() throws { try makeDatabaseQueue().write { db in try db.create(table: "t") { $0.primaryKey("id", .blob) } let uuids = [ UUID(uuidString: "61626364-6566-6768-696A-6B6C6D6E6F70")!, UUID(uuidString: "56e7d8d3-e9e4-48b6-968e-8d102833af00")!, ] do { let request = Table>("t").filter(key: uuids[0]) try assertEqualSQL(db, request, """ SELECT * FROM "t" WHERE "id" = x'6162636465666768696a6b6c6d6e6f70' """) } do { let request = Table>("t").filter(keys: uuids) try assertEqualSQL(db, request, """ SELECT * FROM "t" WHERE "id" IN (x'6162636465666768696a6b6c6d6e6f70', x'56e7d8d3e9e448b6968e8d102833af00') """) } do { let request = Table>("t").filter(key: uuids[0]) try assertEqualSQL(db, request, """ SELECT * FROM "t" WHERE "id" = '61626364-6566-6768-696A-6B6C6D6E6F70' """) } do { let request = Table>("t").filter(keys: uuids) try assertEqualSQL(db, request, """ SELECT * FROM "t" WHERE "id" IN ('61626364-6566-6768-696A-6B6C6D6E6F70', '56E7D8D3-E9E4-48B6-968E-8D102833AF00') """) } do { let request = Table>("t").filter(key: uuids[0]) try assertEqualSQL(db, request, """ SELECT * FROM "t" WHERE "id" = '61626364-6566-6768-696a-6b6c6d6e6f70' """) } do { let request = Table>("t").filter(keys: uuids) try assertEqualSQL(db, request, """ SELECT * FROM "t" WHERE "id" IN ('61626364-6566-6768-696a-6b6c6d6e6f70', '56e7d8d3-e9e4-48b6-968e-8d102833af00') """) } } } func testFilterID() throws { guard #available(iOS 13, macOS 10.15, tvOS 13, watchOS 6, *) else { throw XCTSkip("Identifiable not available") } try makeDatabaseQueue().write { db in try db.create(table: "t") { $0.primaryKey("id", .blob) } let uuids = [ UUID(uuidString: "61626364-6566-6768-696A-6B6C6D6E6F70")!, UUID(uuidString: "56e7d8d3-e9e4-48b6-968e-8d102833af00")!, ] do { let request = Table>("t").filter(id: uuids[0]) try assertEqualSQL(db, request, """ SELECT * FROM "t" WHERE "id" = x'6162636465666768696a6b6c6d6e6f70' """) } do { let request = Table>("t").filter(ids: uuids) try assertEqualSQL(db, request, """ SELECT * FROM "t" WHERE "id" IN (x'6162636465666768696a6b6c6d6e6f70', x'56e7d8d3e9e448b6968e8d102833af00') """) } do { let request = Table>("t").filter(id: uuids[0]) try assertEqualSQL(db, request, """ SELECT * FROM "t" WHERE "id" = '61626364-6566-6768-696A-6B6C6D6E6F70' """) } do { let request = Table>("t").filter(ids: uuids) try assertEqualSQL(db, request, """ SELECT * FROM "t" WHERE "id" IN ('61626364-6566-6768-696A-6B6C6D6E6F70', '56E7D8D3-E9E4-48B6-968E-8D102833AF00') """) } do { let request = Table>("t").filter(id: uuids[0]) try assertEqualSQL(db, request, """ SELECT * FROM "t" WHERE "id" = '61626364-6566-6768-696a-6b6c6d6e6f70' """) } do { let request = Table>("t").filter(ids: uuids) try assertEqualSQL(db, request, """ SELECT * FROM "t" WHERE "id" IN ('61626364-6566-6768-696a-6b6c6d6e6f70', '56e7d8d3-e9e4-48b6-968e-8d102833af00') """) } do { let request = Table>("t").filter(id: nil) try assertEqualSQL(db, request, """ SELECT * FROM "t" WHERE 0 """) } do { let request = Table>("t").filter(id: uuids[0]) try assertEqualSQL(db, request, """ SELECT * FROM "t" WHERE "id" = x'6162636465666768696a6b6c6d6e6f70' """) } do { let request = Table>("t").filter(ids: uuids) try assertEqualSQL(db, request, """ SELECT * FROM "t" WHERE "id" IN (x'6162636465666768696a6b6c6d6e6f70', x'56e7d8d3e9e448b6968e8d102833af00') """) } do { let request = Table>("t").filter(id: nil) try assertEqualSQL(db, request, """ SELECT * FROM "t" WHERE 0 """) } do { let request = Table>("t").filter(id: uuids[0]) try assertEqualSQL(db, request, """ SELECT * FROM "t" WHERE "id" = '61626364-6566-6768-696A-6B6C6D6E6F70' """) } do { let request = Table>("t").filter(ids: uuids) try assertEqualSQL(db, request, """ SELECT * FROM "t" WHERE "id" IN ('61626364-6566-6768-696A-6B6C6D6E6F70', '56E7D8D3-E9E4-48B6-968E-8D102833AF00') """) } do { let request = Table>("t").filter(id: nil) try assertEqualSQL(db, request, """ SELECT * FROM "t" WHERE 0 """) } do { let request = Table>("t").filter(id: uuids[0]) try assertEqualSQL(db, request, """ SELECT * FROM "t" WHERE "id" = '61626364-6566-6768-696a-6b6c6d6e6f70' """) } do { let request = Table>("t").filter(ids: uuids) try assertEqualSQL(db, request, """ SELECT * FROM "t" WHERE "id" IN ('61626364-6566-6768-696a-6b6c6d6e6f70', '56e7d8d3-e9e4-48b6-968e-8d102833af00') """) } } } func testDeleteID() throws { guard #available(iOS 13, macOS 10.15, tvOS 13, watchOS 6, *) else { throw XCTSkip("Identifiable not available") } try makeDatabaseQueue().write { db in try db.create(table: "t") { $0.primaryKey("id", .blob) } let uuids = [ UUID(uuidString: "61626364-6566-6768-696A-6B6C6D6E6F70")!, UUID(uuidString: "56e7d8d3-e9e4-48b6-968e-8d102833af00")!, ] do { try Table>("t").deleteOne(db, id: uuids[0]) XCTAssertEqual(lastSQLQuery, """ DELETE FROM "t" WHERE "id" = x'6162636465666768696a6b6c6d6e6f70' """) } do { try Table>("t").deleteAll(db, ids: uuids) XCTAssertEqual(lastSQLQuery, """ DELETE FROM "t" WHERE "id" IN (x'6162636465666768696a6b6c6d6e6f70', x'56e7d8d3e9e448b6968e8d102833af00') """) } do { try Table>("t").deleteOne(db, id: uuids[0]) XCTAssertEqual(lastSQLQuery, """ DELETE FROM "t" WHERE "id" = '61626364-6566-6768-696A-6B6C6D6E6F70' """) } do { try Table>("t").deleteAll(db, ids: uuids) XCTAssertEqual(lastSQLQuery, """ DELETE FROM "t" WHERE "id" IN ('61626364-6566-6768-696A-6B6C6D6E6F70', '56E7D8D3-E9E4-48B6-968E-8D102833AF00') """) } do { try Table>("t").deleteOne(db, id: uuids[0]) XCTAssertEqual(lastSQLQuery, """ DELETE FROM "t" WHERE "id" = '61626364-6566-6768-696a-6b6c6d6e6f70' """) } do { try Table>("t").deleteAll(db, ids: uuids) XCTAssertEqual(lastSQLQuery, """ DELETE FROM "t" WHERE "id" IN ('61626364-6566-6768-696a-6b6c6d6e6f70', '56e7d8d3-e9e4-48b6-968e-8d102833af00') """) } do { sqlQueries.removeAll() try Table>("t").deleteOne(db, id: nil) XCTAssertNil(lastSQLQuery) // Database not hit } do { try Table>("t").deleteOne(db, id: uuids[0]) XCTAssertEqual(lastSQLQuery, """ DELETE FROM "t" WHERE "id" = x'6162636465666768696a6b6c6d6e6f70' """) } do { try Table>("t").deleteAll(db, ids: uuids) XCTAssertEqual(lastSQLQuery, """ DELETE FROM "t" WHERE "id" IN (x'6162636465666768696a6b6c6d6e6f70', x'56e7d8d3e9e448b6968e8d102833af00') """) } do { sqlQueries.removeAll() try Table>("t").deleteOne(db, id: nil) XCTAssertNil(lastSQLQuery) // Database not hit } do { try Table>("t").deleteOne(db, id: uuids[0]) XCTAssertEqual(lastSQLQuery, """ DELETE FROM "t" WHERE "id" = '61626364-6566-6768-696A-6B6C6D6E6F70' """) } do { try Table>("t").deleteAll(db, ids: uuids) XCTAssertEqual(lastSQLQuery, """ DELETE FROM "t" WHERE "id" IN ('61626364-6566-6768-696A-6B6C6D6E6F70', '56E7D8D3-E9E4-48B6-968E-8D102833AF00') """) } do { sqlQueries.removeAll() try Table>("t").deleteOne(db, id: nil) XCTAssertNil(lastSQLQuery) // Database not hit } do { try Table>("t").deleteOne(db, id: uuids[0]) XCTAssertEqual(lastSQLQuery, """ DELETE FROM "t" WHERE "id" = '61626364-6566-6768-696a-6b6c6d6e6f70' """) } do { try Table>("t").deleteAll(db, ids: uuids) XCTAssertEqual(lastSQLQuery, """ DELETE FROM "t" WHERE "id" IN ('61626364-6566-6768-696a-6b6c6d6e6f70', '56e7d8d3-e9e4-48b6-968e-8d102833af00') """) } } } }