343 lines
12 KiB
Swift
343 lines
12 KiB
Swift
import XCTest
|
|
import Foundation
|
|
import GRDB
|
|
|
|
class DatabaseValueConvertibleEncodableTests: GRDBTestCase {
|
|
func testDatabaseValueConvertibleImplementationDerivedFromEncodable1() {
|
|
struct Value : Encodable, DatabaseValueConvertible {
|
|
let string: String
|
|
|
|
func encode(to encoder: Encoder) throws {
|
|
var container = encoder.singleValueContainer()
|
|
try container.encode(string)
|
|
}
|
|
|
|
static func fromDatabaseValue(_ databaseValue: DatabaseValue) -> Value? {
|
|
preconditionFailure("not tested")
|
|
}
|
|
|
|
// Inferred, tested
|
|
// var databaseValue: DatabaseValue { ... }
|
|
}
|
|
|
|
let dbValue = Value(string: "foo").databaseValue
|
|
XCTAssertEqual(dbValue.storage.value as! String, "foo")
|
|
}
|
|
|
|
func testDatabaseValueConvertibleImplementationDerivedFromEncodable2() {
|
|
struct Value : Encodable, DatabaseValueConvertible {
|
|
let string: String
|
|
|
|
func encode(to encoder: Encoder) throws {
|
|
var container = encoder.singleValueContainer()
|
|
try container.encode(string)
|
|
}
|
|
|
|
static func fromDatabaseValue(_ databaseValue: DatabaseValue) -> Value? {
|
|
preconditionFailure("not tested")
|
|
}
|
|
}
|
|
|
|
struct Wrapper : Encodable, DatabaseValueConvertible {
|
|
let value: Value
|
|
|
|
func encode(to encoder: Encoder) throws {
|
|
var container = encoder.singleValueContainer()
|
|
try container.encode(value)
|
|
}
|
|
|
|
static func fromDatabaseValue(_ databaseValue: DatabaseValue) -> Wrapper? {
|
|
preconditionFailure("not tested")
|
|
}
|
|
|
|
// Inferred, tested
|
|
// var databaseValue: DatabaseValue { ... }
|
|
}
|
|
|
|
let dbValue = Wrapper(value: Value(string: "foo")).databaseValue
|
|
XCTAssertEqual(dbValue.storage.value as! String, "foo")
|
|
}
|
|
|
|
func testDatabaseValueConvertibleImplementationDerivedFromEncodable3() {
|
|
struct Wrapper : Encodable, DatabaseValueConvertible {
|
|
struct Nested : Encodable {
|
|
let string: String
|
|
|
|
func encode(to encoder: Encoder) throws {
|
|
var container = encoder.singleValueContainer()
|
|
try container.encode(string)
|
|
}
|
|
}
|
|
let nested: Nested
|
|
|
|
func encode(to encoder: Encoder) throws {
|
|
var container = encoder.singleValueContainer()
|
|
try container.encode(nested)
|
|
}
|
|
|
|
static func fromDatabaseValue(_ databaseValue: DatabaseValue) -> Wrapper? {
|
|
preconditionFailure("not tested")
|
|
}
|
|
|
|
// Inferred, tested
|
|
// var databaseValue: DatabaseValue { ... }
|
|
}
|
|
|
|
let dbValue = Wrapper(nested: Wrapper.Nested(string: "foo")).databaseValue
|
|
XCTAssertEqual(dbValue.storage.value as! String, "foo")
|
|
}
|
|
|
|
func testDatabaseValueConvertibleImplementationDerivedFromEncodable4() throws {
|
|
struct Value: Encodable, DatabaseValueConvertible {
|
|
let strings: [String]
|
|
|
|
static func fromDatabaseValue(_ databaseValue: DatabaseValue) -> Value? {
|
|
preconditionFailure("not tested")
|
|
}
|
|
}
|
|
|
|
do {
|
|
let dbValue = Value(strings: ["foo", "bar"]).databaseValue
|
|
XCTAssertEqual(dbValue.storage.value as! String, #"{"strings":["foo","bar"]}"#)
|
|
}
|
|
|
|
do {
|
|
let dbValue = Value(strings: []).databaseValue
|
|
XCTAssertEqual(dbValue.storage.value as! String, #"{"strings":[]}"#)
|
|
}
|
|
}
|
|
|
|
func testDatabaseValueConvertibleImplementationDerivedFromEncodable5() throws {
|
|
struct Value: Encodable, DatabaseValueConvertible {
|
|
let dictionary: [String: Int]
|
|
|
|
func encode(to encoder: Encoder) throws {
|
|
try dictionary.encode(to: encoder)
|
|
}
|
|
|
|
static func fromDatabaseValue(_ databaseValue: DatabaseValue) -> Value? {
|
|
preconditionFailure("not tested")
|
|
}
|
|
}
|
|
|
|
do {
|
|
let dbValue = Value(dictionary: ["foo": 1]).databaseValue
|
|
XCTAssertEqual(dbValue.storage.value as! String, #"{"foo":1}"#)
|
|
}
|
|
|
|
do {
|
|
let dbValue = Value(dictionary: [:]).databaseValue
|
|
XCTAssertEqual(dbValue.storage.value as! String, #"{}"#)
|
|
}
|
|
}
|
|
|
|
func testDatabaseValueConvertibleImplementationDerivedFromEncodable6() throws {
|
|
struct Value: Encodable, DatabaseValueConvertible {
|
|
let strings: [String]
|
|
|
|
func encode(to encoder: Encoder) throws {
|
|
try strings.encode(to: encoder)
|
|
}
|
|
|
|
static func fromDatabaseValue(_ databaseValue: DatabaseValue) -> Value? {
|
|
preconditionFailure("not tested")
|
|
}
|
|
}
|
|
|
|
do {
|
|
let dbValue = Value(strings: ["foo", "bar"]).databaseValue
|
|
XCTAssertEqual(dbValue.storage.value as! String, #"["foo","bar"]"#)
|
|
}
|
|
|
|
do {
|
|
let dbValue = Value(strings: []).databaseValue
|
|
XCTAssertEqual(dbValue.storage.value as! String, #"[]"#)
|
|
}
|
|
}
|
|
|
|
func testDatabaseValueConvertibleImplementationDerivedFromEncodable7() throws {
|
|
struct Value: Encodable, DatabaseValueConvertible {
|
|
let dictionary: [String: Int]
|
|
|
|
func encode(to encoder: Encoder) throws {
|
|
var container = encoder.singleValueContainer()
|
|
try container.encode(dictionary)
|
|
}
|
|
|
|
static func fromDatabaseValue(_ databaseValue: DatabaseValue) -> Value? {
|
|
preconditionFailure("not tested")
|
|
}
|
|
}
|
|
|
|
do {
|
|
let dbValue = Value(dictionary: ["foo": 1]).databaseValue
|
|
XCTAssertEqual(dbValue.storage.value as! String, #"{"foo":1}"#)
|
|
}
|
|
|
|
do {
|
|
let dbValue = Value(dictionary: [:]).databaseValue
|
|
XCTAssertEqual(dbValue.storage.value as! String, #"{}"#)
|
|
}
|
|
}
|
|
|
|
func testDatabaseValueConvertibleImplementationDerivedFromEncodable8() throws {
|
|
struct Value: Encodable, DatabaseValueConvertible {
|
|
let strings: [String]
|
|
|
|
func encode(to encoder: Encoder) throws {
|
|
var container = encoder.singleValueContainer()
|
|
try container.encode(strings)
|
|
}
|
|
|
|
static func fromDatabaseValue(_ databaseValue: DatabaseValue) -> Value? {
|
|
preconditionFailure("not tested")
|
|
}
|
|
}
|
|
|
|
do {
|
|
let dbValue = Value(strings: ["foo", "bar"]).databaseValue
|
|
XCTAssertEqual(dbValue.storage.value as! String, #"["foo","bar"]"#)
|
|
}
|
|
|
|
do {
|
|
let dbValue = Value(strings: []).databaseValue
|
|
XCTAssertEqual(dbValue.storage.value as! String, #"[]"#)
|
|
}
|
|
}
|
|
|
|
func testEncodableRawRepresentable() {
|
|
// Test that the rawValue is encoded with DatabaseValueConvertible, not with Encodable
|
|
struct Value : RawRepresentable, Encodable, DatabaseValueConvertible {
|
|
let rawValue: Date
|
|
}
|
|
|
|
let dbValue = Value(rawValue: Date()).databaseValue
|
|
XCTAssertTrue(dbValue.storage.value is String)
|
|
}
|
|
|
|
func testEncodableRawRepresentableEnum() {
|
|
// Make sure this kind of declaration is possible
|
|
enum Value : String, Encodable, DatabaseValueConvertible {
|
|
case foo, bar
|
|
}
|
|
let dbValue = Value.foo.databaseValue
|
|
XCTAssertEqual(dbValue.storage.value as! String, "foo")
|
|
}
|
|
}
|
|
|
|
// MARK: - Foundation Codable Types
|
|
|
|
extension DatabaseValueConvertibleEncodableTests {
|
|
func testDateProperty() {
|
|
struct Value : Encodable, DatabaseValueConvertible {
|
|
let date: Date
|
|
|
|
func encode(to encoder: Encoder) throws {
|
|
var container = encoder.singleValueContainer()
|
|
try container.encode(date)
|
|
}
|
|
|
|
static func fromDatabaseValue(_ databaseValue: DatabaseValue) -> Value? {
|
|
preconditionFailure("not tested")
|
|
}
|
|
|
|
// Inferred, tested
|
|
// var databaseValue: DatabaseValue { ... }
|
|
}
|
|
|
|
let value = Value(date: Date())
|
|
let dbValue = value.databaseValue
|
|
|
|
// Date has a default Encodable implementation which encodes a Double.
|
|
// We expect here a String, because DatabaseValueConvertible has
|
|
// precedence over Encodable.
|
|
XCTAssert(dbValue.storage.value is String)
|
|
|
|
let encodedDate = Date.fromDatabaseValue(dbValue)!
|
|
XCTAssert(abs(encodedDate.timeIntervalSince(value.date)) < 0.001)
|
|
}
|
|
|
|
func testURLProperty() {
|
|
struct Value : Encodable, DatabaseValueConvertible {
|
|
let url: URL
|
|
|
|
func encode(to encoder: Encoder) throws {
|
|
var container = encoder.singleValueContainer()
|
|
try container.encode(url)
|
|
}
|
|
|
|
static func fromDatabaseValue(_ databaseValue: DatabaseValue) -> Value? {
|
|
preconditionFailure("not tested")
|
|
}
|
|
|
|
// Inferred, tested
|
|
// var databaseValue: DatabaseValue { ... }
|
|
}
|
|
|
|
let value = Value(url: URL(string: "https://github.com")!)
|
|
let dbValue = value.databaseValue
|
|
XCTAssert(dbValue.storage.value is String)
|
|
let encodedURL = URL.fromDatabaseValue(dbValue)!
|
|
XCTAssertEqual(encodedURL, value.url)
|
|
}
|
|
|
|
func testUUIDProperty() {
|
|
struct Value : Encodable, DatabaseValueConvertible {
|
|
let uuid: UUID
|
|
|
|
func encode(to encoder: Encoder) throws {
|
|
var container = encoder.singleValueContainer()
|
|
try container.encode(uuid)
|
|
}
|
|
|
|
static func fromDatabaseValue(_ databaseValue: DatabaseValue) -> Value? {
|
|
preconditionFailure("not tested")
|
|
}
|
|
|
|
// Inferred, tested
|
|
// var databaseValue: DatabaseValue { ... }
|
|
}
|
|
|
|
let value = Value(uuid: UUID())
|
|
let dbValue = value.databaseValue
|
|
XCTAssert(dbValue.storage.value is Data)
|
|
let encodedUUID = UUID.fromDatabaseValue(dbValue)!
|
|
XCTAssertEqual(encodedUUID, value.uuid)
|
|
}
|
|
|
|
func testDatabaseValueConvertibleImplementationDerivedFromEncodableWithCustomJsonEncoder() throws {
|
|
struct Value: Encodable, DatabaseValueConvertible {
|
|
let duration: Double
|
|
|
|
static func fromDatabaseValue(_ databaseValue: DatabaseValue) -> Value? {
|
|
preconditionFailure("not tested")
|
|
}
|
|
|
|
public static func databaseJSONEncoder() -> JSONEncoder {
|
|
let encoder = JSONEncoder()
|
|
encoder.dataEncodingStrategy = .base64
|
|
encoder.dateEncodingStrategy = .millisecondsSince1970
|
|
encoder.nonConformingFloatEncodingStrategy = .convertToString(
|
|
positiveInfinity: "+InF",
|
|
negativeInfinity: "-InF",
|
|
nan: "NaN"
|
|
)
|
|
// guarantee some stability in order to ease value comparison
|
|
encoder.outputFormatting = .sortedKeys
|
|
return encoder
|
|
}
|
|
}
|
|
|
|
do {
|
|
let dbValue = Value(duration: .infinity).databaseValue
|
|
XCTAssertEqual(dbValue.storage.value as! String, #"{"duration":"+InF"}"#)
|
|
|
|
let dbValue2 = Value(duration: -Double.infinity).databaseValue
|
|
XCTAssertEqual(dbValue2.storage.value as! String, #"{"duration":"-InF"}"#)
|
|
|
|
let dbValue3 = Value(duration: .nan).databaseValue
|
|
XCTAssertEqual(dbValue3.storage.value as! String, #"{"duration":"NaN"}"#)
|
|
}
|
|
}
|
|
}
|