import XCTest import Foundation @testable import GRDB private protocol StrategyProvider { static var strategy: DatabaseDataEncodingStrategy { get } } private enum StrategyDeferredToData: StrategyProvider { static let strategy: DatabaseDataEncodingStrategy = .deferredToData } private enum StrategyTextUTF8: StrategyProvider { static let strategy: DatabaseDataEncodingStrategy = .text } private enum StrategyCustom: StrategyProvider { static let strategy: DatabaseDataEncodingStrategy = .custom { _ in "custom" } } private struct RecordWithData: EncodableRecord, Encodable { static var databaseDataEncodingStrategy: DatabaseDataEncodingStrategy { Strategy.strategy } var data: Data } @available(iOS 13, macOS 10.15, tvOS 13, watchOS 6, *) extension RecordWithData: Identifiable { var id: Data { data } } private struct RecordWithOptionalData: EncodableRecord, Encodable { static var databaseDataEncodingStrategy: DatabaseDataEncodingStrategy { Strategy.strategy } var data: Data? } @available(iOS 13, macOS 10.15, tvOS 13, watchOS 6, *) extension RecordWithOptionalData: Identifiable { var id: Data? { data } } class DatabaseDataEncodingStrategyTests: GRDBTestCase { let testedDatas = [ "foo".data(using: .utf8)!, Data(), ] private func test( record: T, expectedStorage: DatabaseValue.Storage) throws { var container = PersistenceContainer() try record.encode(to: &container) if let dbValue = container["data"]?.databaseValue { XCTAssertEqual(dbValue.storage, expectedStorage) } else { XCTAssertEqual(.null, expectedStorage) } } private func test( strategy: Strategy.Type, encodesData data: Data, as value: some DatabaseValueConvertible) throws { try test(record: RecordWithData(data: data), expectedStorage: value.databaseValue.storage) try test(record: RecordWithOptionalData(data: data), expectedStorage: value.databaseValue.storage) } private func testNullEncoding(strategy: Strategy.Type) throws { try test(record: RecordWithOptionalData(data: nil), expectedStorage: .null) } } // MARK: - deferredToData extension DatabaseDataEncodingStrategyTests { func testDeferredToData() throws { try testNullEncoding(strategy: StrategyDeferredToData.self) for (data, value) in zip(testedDatas, [ "foo".data(using: .utf8)!, Data(), ]) { try test(strategy: StrategyDeferredToData.self, encodesData: data, as: value) } } } // MARK: - text(UTF8) extension DatabaseDataEncodingStrategyTests { func testTextUTF8() throws { try testNullEncoding(strategy: StrategyTextUTF8.self) for (data, value) in zip(testedDatas, [ "foo", "", ]) { try test(strategy: StrategyTextUTF8.self, encodesData: data, as: value) } } } // MARK: - custom((Data) -> DatabaseValueConvertible?) extension DatabaseDataEncodingStrategyTests { func testCustom() throws { try testNullEncoding(strategy: StrategyCustom.self) for (data, value) in zip(testedDatas, [ "custom", "custom", ]) { try test(strategy: StrategyCustom.self, encodesData: data, as: value) } } } // MARK: - Filter extension DatabaseDataEncodingStrategyTests { func testFilterKey() throws { try makeDatabaseQueue().write { db in try db.create(table: "t") { $0.primaryKey("id", .blob) } do { let request = Table>("t").filter(key: testedDatas[0]) try assertEqualSQL(db, request, """ SELECT * FROM "t" WHERE "id" = x'666f6f' """) } do { let request = Table>("t").filter(keys: testedDatas) try assertEqualSQL(db, request, """ SELECT * FROM "t" WHERE "id" IN (x'666f6f', x'') """) } do { let request = Table>("t").filter(key: testedDatas[0]) try assertEqualSQL(db, request, """ SELECT * FROM "t" WHERE "id" = 'foo' """) } do { let request = Table>("t").filter(keys: testedDatas) try assertEqualSQL(db, request, """ SELECT * FROM "t" WHERE "id" IN ('foo', '') """) } } } 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) } do { let request = Table>("t").filter(id: testedDatas[0]) try assertEqualSQL(db, request, """ SELECT * FROM "t" WHERE "id" = x'666f6f' """) } do { let request = Table>("t").filter(ids: testedDatas) try assertEqualSQL(db, request, """ SELECT * FROM "t" WHERE "id" IN (x'666f6f', x'') """) } do { let request = Table>("t").filter(id: testedDatas[0]) try assertEqualSQL(db, request, """ SELECT * FROM "t" WHERE "id" = 'foo' """) } do { let request = Table>("t").filter(ids: testedDatas) try assertEqualSQL(db, request, """ SELECT * FROM "t" WHERE "id" IN ('foo', '') """) } do { let request = Table>("t").filter(id: nil) try assertEqualSQL(db, request, """ SELECT * FROM "t" WHERE 0 """) } do { let request = Table>("t").filter(id: testedDatas[0]) try assertEqualSQL(db, request, """ SELECT * FROM "t" WHERE "id" = x'666f6f' """) } do { let request = Table>("t").filter(ids: testedDatas) try assertEqualSQL(db, request, """ SELECT * FROM "t" WHERE "id" IN (x'666f6f', x'') """) } do { let request = Table>("t").filter(id: nil) try assertEqualSQL(db, request, """ SELECT * FROM "t" WHERE 0 """) } do { let request = Table>("t").filter(id: testedDatas[0]) try assertEqualSQL(db, request, """ SELECT * FROM "t" WHERE "id" = 'foo' """) } do { let request = Table>("t").filter(ids: testedDatas) try assertEqualSQL(db, request, """ SELECT * FROM "t" WHERE "id" IN ('foo', '') """) } } } 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) } do { try Table>("t").deleteOne(db, id: testedDatas[0]) XCTAssertEqual(lastSQLQuery, """ DELETE FROM "t" WHERE "id" = x'666f6f' """) } do { try Table>("t").deleteAll(db, ids: testedDatas) XCTAssertEqual(lastSQLQuery, """ DELETE FROM "t" WHERE "id" IN (x'666f6f', x'') """) } do { try Table>("t").deleteOne(db, id: testedDatas[0]) XCTAssertEqual(lastSQLQuery, """ DELETE FROM "t" WHERE "id" = 'foo' """) } do { try Table>("t").deleteAll(db, ids: testedDatas) XCTAssertEqual(lastSQLQuery, """ DELETE FROM "t" WHERE "id" IN ('foo', '') """) } do { sqlQueries.removeAll() try Table>("t").deleteOne(db, id: nil) XCTAssertNil(lastSQLQuery) // Database not hit } do { try Table>("t").deleteOne(db, id: testedDatas[0]) XCTAssertEqual(lastSQLQuery, """ DELETE FROM "t" WHERE "id" = x'666f6f' """) } do { try Table>("t").deleteAll(db, ids: testedDatas) XCTAssertEqual(lastSQLQuery, """ DELETE FROM "t" WHERE "id" IN (x'666f6f', x'') """) } do { sqlQueries.removeAll() try Table>("t").deleteOne(db, id: nil) XCTAssertNil(lastSQLQuery) // Database not hit } do { try Table>("t").deleteOne(db, id: testedDatas[0]) XCTAssertEqual(lastSQLQuery, """ DELETE FROM "t" WHERE "id" = 'foo' """) } do { try Table>("t").deleteAll(db, ids: testedDatas) XCTAssertEqual(lastSQLQuery, """ DELETE FROM "t" WHERE "id" IN ('foo', '') """) } } } }