DataLireCoder swift package

This commit is contained in:
2025-04-27 12:53:43 +03:00
parent 2cca986016
commit 5aec6ea578
60 changed files with 7144 additions and 0 deletions

View File

@@ -0,0 +1,241 @@
import XCTest
import DataLiteCore
import DLCDecoder
@testable import DataLiteCoder
final class DateDecoderTests: XCTestCase {
func testDeferredToDate() {
let date = Date(timeIntervalSince1970: 123456789)
let dateDecoder = RowDecoder.DateDecoder(strategy: .deferredToDate)
let singleDecoder = SingleValueDecoder(
sqliteData: .real(date.timeIntervalSince1970)
)
XCTAssertEqual(try dateDecoder.decode(from: singleDecoder), date)
var row = SQLiteRow()
row[CodingKeys.key.stringValue] = .real(date.timeIntervalSince1970)
let keyedDecoder = KeyedDecoder(sqliteData: row)
XCTAssertEqual(try dateDecoder.decode(from: keyedDecoder, for: CodingKeys.key), date)
}
func testISO8601() throws {
let dateDecoder = RowDecoder.DateDecoder(strategy: .iso8601)
let formatter = ISO8601DateFormatter()
let string = "2024-04-18T13:45:00Z"
let date = formatter.date(from: string)!
let single = SingleValueDecoder(sqliteData: .text(string))
XCTAssertEqual(try dateDecoder.decode(from: single), date)
var row = SQLiteRow()
row[CodingKeys.key.stringValue] = .text(string)
let keyed = KeyedDecoder(sqliteData: row)
XCTAssertEqual(try dateDecoder.decode(from: keyed, for: CodingKeys.key), date)
}
func testFormatted() throws {
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
let string = "2023-10-15 16:30:00"
let date = formatter.date(from: string)!
let dateDecoder = RowDecoder.DateDecoder(strategy: .formatted(formatter))
let single = SingleValueDecoder(sqliteData: .text(string))
XCTAssertEqual(try dateDecoder.decode(from: single), date)
var row = SQLiteRow()
row[CodingKeys.key.stringValue] = .text(string)
let keyed = KeyedDecoder(sqliteData: row)
XCTAssertEqual(try dateDecoder.decode(from: keyed, for: CodingKeys.key), date)
}
func testFormattedInvalidDate() {
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
let invalidString = "not a date"
let dateDecoder = RowDecoder.DateDecoder(strategy: .formatted(formatter))
let single = SingleValueDecoder(sqliteData: .text(invalidString))
XCTAssertThrowsError(try dateDecoder.decode(from: single))
var row = SQLiteRow()
row[CodingKeys.key.stringValue] = .text(invalidString)
let keyed = KeyedDecoder(sqliteData: row)
XCTAssertThrowsError(try dateDecoder.decode(from: keyed, for: CodingKeys.key))
}
func testMillisecondsSince1970Int() throws {
let millis: Int64 = 1_600_000_000_000
let date = Date(timeIntervalSince1970: Double(millis) / 1000)
let dateDecoder = RowDecoder.DateDecoder(strategy: .millisecondsSince1970Int)
let single = SingleValueDecoder(sqliteData: .int(millis))
XCTAssertEqual(try dateDecoder.decode(from: single), date)
var row = SQLiteRow()
row[CodingKeys.key.stringValue] = .int(millis)
let keyed = KeyedDecoder(sqliteData: row)
XCTAssertEqual(try dateDecoder.decode(from: keyed, for: CodingKeys.key), date)
}
func testMillisecondsSince1970Double() throws {
let millis: Double = 1_600_000_000_000.0
let date = Date(timeIntervalSince1970: millis / 1000)
let dateDecoder = RowDecoder.DateDecoder(strategy: .millisecondsSince1970Double)
let single = SingleValueDecoder(sqliteData: .real(millis))
XCTAssertEqual(try dateDecoder.decode(from: single), date)
var row = SQLiteRow()
row[CodingKeys.key.stringValue] = .real(millis)
let keyed = KeyedDecoder(sqliteData: row)
XCTAssertEqual(try dateDecoder.decode(from: keyed, for: CodingKeys.key), date)
}
func testSecondsSince1970Int() throws {
let seconds: Int64 = 1_600_000_000
let date = Date(timeIntervalSince1970: Double(seconds))
let dateDecoder = RowDecoder.DateDecoder(strategy: .secondsSince1970Int)
let single = SingleValueDecoder(sqliteData: .int(seconds))
XCTAssertEqual(try dateDecoder.decode(from: single), date)
var row = SQLiteRow()
row[CodingKeys.key.stringValue] = .int(seconds)
let keyed = KeyedDecoder(sqliteData: row)
XCTAssertEqual(try dateDecoder.decode(from: keyed, for: CodingKeys.key), date)
}
func testSecondsSince1970Double() throws {
let seconds: Double = 1_600_000_000.789
let date = Date(timeIntervalSince1970: seconds)
let dateDecoder = RowDecoder.DateDecoder(strategy: .secondsSince1970Double)
let single = SingleValueDecoder(sqliteData: .real(seconds))
XCTAssertEqual(try dateDecoder.decode(from: single), date)
var row = SQLiteRow()
row[CodingKeys.key.stringValue] = .real(seconds)
let keyed = KeyedDecoder(sqliteData: row)
XCTAssertEqual(try dateDecoder.decode(from: keyed, for: CodingKeys.key), date)
}
}
private extension DateDecoderTests {
enum CodingKeys: CodingKey {
case key
}
final class SingleValueDecoder: DLCDecoder.ValueDecoder {
let sqliteData: SQLiteRawValue
let dateDecoder: DLCDecoder.DateDecoder
let codingPath: [any CodingKey]
let userInfo: [CodingUserInfoKey: Any]
init(
sqliteData: SQLiteRawValue,
dateDecoder: DLCDecoder.DateDecoder = RowDecoder.DateDecoder(strategy: .deferredToDate),
codingPath: [any CodingKey] = [],
userInfo: [CodingUserInfoKey: Any] = [:]
) {
self.sqliteData = sqliteData
self.dateDecoder = dateDecoder
self.codingPath = codingPath
self.userInfo = userInfo
}
func decodeNil() -> Bool {
fatalError()
}
func decodeDate() throws -> Date {
fatalError()
}
func decode<T: SQLiteRawRepresentable>(_ type: T.Type) throws -> T {
type.init(sqliteData)!
}
func container<Key: CodingKey>(
keyedBy type: Key.Type
) throws -> KeyedDecodingContainer<Key> {
fatalError()
}
func unkeyedContainer() throws -> any UnkeyedDecodingContainer {
fatalError()
}
func singleValueContainer() throws -> any SingleValueDecodingContainer {
fatalError()
}
}
final class KeyedDecoder: DLCDecoder.RowDecoder {
let sqliteData: SQLiteRow
let dateDecoder: DLCDecoder.DateDecoder
let codingPath: [any CodingKey]
let userInfo: [CodingUserInfoKey: Any]
var count: Int? { sqliteData.count }
init(
sqliteData: SQLiteRow,
dateDecoder: DLCDecoder.DateDecoder = RowDecoder.DateDecoder(strategy: .deferredToDate),
codingPath: [any CodingKey] = [],
userInfo: [CodingUserInfoKey: Any] = [:]
) {
self.sqliteData = sqliteData
self.dateDecoder = dateDecoder
self.codingPath = codingPath
self.userInfo = userInfo
}
func contains(_ key: any CodingKey) -> Bool {
fatalError()
}
func decodeNil(for key: any CodingKey) throws -> Bool {
fatalError()
}
func decodeDate(for key: any CodingKey) throws -> Date {
fatalError()
}
func decode<T: SQLiteRawRepresentable>(
_ type: T.Type,
for key: any CodingKey
) throws -> T {
type.init(sqliteData[key.stringValue]!)!
}
func decoder(for key: any CodingKey) -> any Decoder {
fatalError()
}
func container<Key: CodingKey>(
keyedBy type: Key.Type
) throws -> KeyedDecodingContainer<Key> {
fatalError()
}
func unkeyedContainer() throws -> any UnkeyedDecodingContainer {
fatalError()
}
func singleValueContainer() throws -> any SingleValueDecodingContainer {
fatalError()
}
}
}

View File

@@ -0,0 +1,222 @@
import XCTest
import DataLiteCore
import DLCEncoder
import DLCCommon
@testable import DataLiteCoder
final class DateEncoderTests: XCTestCase {
func testDeferredToDate() throws {
let date = Date(timeIntervalSince1970: 1234567890)
let dateEncoder = RowEncoder.DateEncoder(strategy: .deferredToDate)
let singleEncoder = SingleValueEncoder(dateEncoder: dateEncoder)
let keyedEncoder = KeyedEncoder(dateEncoder: dateEncoder)
let key = RowCodingKey(stringValue: "key1")
try dateEncoder.encode(date, to: singleEncoder)
try dateEncoder.encode(date, for: key, to: keyedEncoder)
XCTAssertEqual(singleEncoder.sqliteData, date.sqliteRawValue)
XCTAssertEqual(keyedEncoder.sqliteData[key], date.sqliteRawValue)
}
func testISO8601() throws {
let formatter = ISO8601DateFormatter()
let string = "2024-04-18T13:45:00Z"
let date = formatter.date(from: string)!
let dateEncoder = RowEncoder.DateEncoder(strategy: .iso8601)
let singleEncoder = SingleValueEncoder(dateEncoder: dateEncoder)
let keyedEncoder = KeyedEncoder(dateEncoder: dateEncoder)
let key = RowCodingKey(stringValue: "key1")
try dateEncoder.encode(date, to: singleEncoder)
try dateEncoder.encode(date, for: key, to: keyedEncoder)
XCTAssertEqual(singleEncoder.sqliteData, .text(string))
XCTAssertEqual(keyedEncoder.sqliteData[key], .text(string))
}
func testFormatted() throws {
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
let string = "2023-10-15 16:30:00"
let date = formatter.date(from: string)!
let dateEncoder = RowEncoder.DateEncoder(strategy: .formatted(formatter))
let singleEncoder = SingleValueEncoder(dateEncoder: dateEncoder)
let keyedEncoder = KeyedEncoder(dateEncoder: dateEncoder)
let key = RowCodingKey(stringValue: "key1")
try dateEncoder.encode(date, to: singleEncoder)
try dateEncoder.encode(date, for: key, to: keyedEncoder)
XCTAssertEqual(singleEncoder.sqliteData, .text(string))
XCTAssertEqual(keyedEncoder.sqliteData[key], .text(string))
}
func testMillisecondsSince1970Int() throws {
let millis: Int64 = 1_600_000_000_000
let date = Date(timeIntervalSince1970: Double(millis) / 1000)
let dateEncoder = RowEncoder.DateEncoder(strategy: .millisecondsSince1970Int)
let singleEncoder = SingleValueEncoder(dateEncoder: dateEncoder)
let keyedEncoder = KeyedEncoder(dateEncoder: dateEncoder)
let key = RowCodingKey(stringValue: "key1")
try dateEncoder.encode(date, to: singleEncoder)
try dateEncoder.encode(date, for: key, to: keyedEncoder)
XCTAssertEqual(singleEncoder.sqliteData, .int(millis))
XCTAssertEqual(keyedEncoder.sqliteData[key], .int(millis))
}
func testMillisecondsSince1970Double() throws {
let millis: Double = 1_600_000_000_000
let date = Date(timeIntervalSince1970: millis / 1000)
let dateEncoder = RowEncoder.DateEncoder(strategy: .millisecondsSince1970Double)
let singleEncoder = SingleValueEncoder(dateEncoder: dateEncoder)
let keyedEncoder = KeyedEncoder(dateEncoder: dateEncoder)
let key = RowCodingKey(stringValue: "key1")
try dateEncoder.encode(date, to: singleEncoder)
try dateEncoder.encode(date, for: key, to: keyedEncoder)
XCTAssertEqual(singleEncoder.sqliteData, .real(millis))
XCTAssertEqual(keyedEncoder.sqliteData[key], .real(millis))
}
func testSecondsSince1970Int() throws {
let seconds: Int64 = 1_600_000_000
let date = Date(timeIntervalSince1970: Double(seconds))
let dateEncoder = RowEncoder.DateEncoder(strategy: .secondsSince1970Int)
let singleEncoder = SingleValueEncoder(dateEncoder: dateEncoder)
let keyedEncoder = KeyedEncoder(dateEncoder: dateEncoder)
let key = RowCodingKey(stringValue: "key1")
try dateEncoder.encode(date, to: singleEncoder)
try dateEncoder.encode(date, for: key, to: keyedEncoder)
XCTAssertEqual(singleEncoder.sqliteData, .int(seconds))
XCTAssertEqual(keyedEncoder.sqliteData[key], .int(seconds))
}
func testSecondsSince1970Double() throws {
let seconds: Double = 1_600_000_000
let date = Date(timeIntervalSince1970: seconds)
let dateEncoder = RowEncoder.DateEncoder(strategy: .secondsSince1970Double)
let singleEncoder = SingleValueEncoder(dateEncoder: dateEncoder)
let keyedEncoder = KeyedEncoder(dateEncoder: dateEncoder)
let key = RowCodingKey(stringValue: "key1")
try dateEncoder.encode(date, to: singleEncoder)
try dateEncoder.encode(date, for: key, to: keyedEncoder)
XCTAssertEqual(singleEncoder.sqliteData, .real(seconds))
XCTAssertEqual(keyedEncoder.sqliteData[key], .real(seconds))
}
}
private extension DateEncoderTests {
final class SingleValueEncoder: DLCEncoder.ValueEncoder {
let dateEncoder: any DateEncoder
let codingPath: [any CodingKey]
let userInfo: [CodingUserInfoKey : Any]
private(set) var sqliteData: SQLiteRawValue?
init(
dateEncoder: any DateEncoder,
codingPath: [any CodingKey] = [],
userInfo: [CodingUserInfoKey : Any] = [:]
) {
self.dateEncoder = dateEncoder
self.codingPath = codingPath
self.userInfo = userInfo
}
func encodeNil() throws {
fatalError()
}
func encodeDate(_ date: Date) throws {
fatalError()
}
func encode<T: SQLiteRawBindable>(_ value: T) throws {
sqliteData = value.sqliteRawValue
}
func container<Key: CodingKey>(
keyedBy type: Key.Type
) -> KeyedEncodingContainer<Key> {
fatalError()
}
func unkeyedContainer() -> any UnkeyedEncodingContainer {
fatalError()
}
func singleValueContainer() -> any SingleValueEncodingContainer {
fatalError()
}
}
final class KeyedEncoder: DLCEncoder.RowEncoder {
let dateEncoder: any DateEncoder
let codingPath: [any CodingKey]
let userInfo: [CodingUserInfoKey : Any]
private(set) var sqliteData = SQLiteRow()
var count: Int { fatalError() }
init(
dateEncoder: any DateEncoder,
codingPath: [any CodingKey] = [],
userInfo: [CodingUserInfoKey : Any] = [:]
) {
self.dateEncoder = dateEncoder
self.codingPath = codingPath
self.userInfo = userInfo
}
func set(_ value: Any, for key: any CodingKey) throws {
fatalError()
}
func encodeNil(for key: any CodingKey) throws {
fatalError()
}
func encodeDate(_ date: Date, for key: any CodingKey) throws {
fatalError()
}
func encode<T: SQLiteRawBindable>(_ value: T, for key: any CodingKey) throws {
sqliteData[key] = value.sqliteRawValue
}
func encoder(for key: any CodingKey) throws -> any Encoder {
fatalError()
}
func container<Key: CodingKey>(
keyedBy type: Key.Type
) -> KeyedEncodingContainer<Key> {
fatalError()
}
func unkeyedContainer() -> any UnkeyedEncodingContainer {
fatalError()
}
func singleValueContainer() -> any SingleValueEncodingContainer {
fatalError()
}
}
}

View File

@@ -0,0 +1,337 @@
import XCTest
import DataLiteCore
import DataLiteCoder
final class RowDecoderTests: XCTestCase {
// MARK: - Decode SQLiteRow
func testDecodeRowWithAllTypes() throws {
let decoder = RowDecoder(
userInfo: [:],
dateDecodingStrategy: .deferredToDate
)
let model = StandardModel(
id: 123,
type: .simple,
name: "John Doe",
age: 34,
isActive: true,
score: 3.1415,
createdAt: Date(timeIntervalSince1970: 12345),
payload: "payload".data(using: .utf8)!
)
var row = SQLiteRow()
row["id"] = model.id.sqliteRawValue
row["type"] = model.type.rawValue.sqliteRawValue
row["name"] = model.name.sqliteRawValue
row["age"] = model.age.sqliteRawValue
row["isActive"] = model.isActive.sqliteRawValue
row["score"] = model.score.sqliteRawValue
row["createdAt"] = model.createdAt.sqliteRawValue
row["payload"] = model.payload.sqliteRawValue
let decoded = try decoder.decode(
StandardModel.self,
from: row
)
XCTAssertEqual(decoded, model)
}
func testDecodeRowWithOptionalValues() throws {
let decoder = RowDecoder(
userInfo: [:],
dateDecodingStrategy: .deferredToDate
)
let model = OptionalModel(
id: 123,
type: .multiple,
name: "Jane Doe",
createdAt: Date(timeIntervalSince1970: 11000),
payload: "payload".data(using: .utf8)!
)
var row = SQLiteRow()
row["id"] = model.id!.sqliteRawValue
row["type"] = model.type!.rawValue.sqliteRawValue
row["name"] = model.name!.sqliteRawValue
row["createdAt"] = model.createdAt!.sqliteRawValue
row["payload"] = model.payload!.sqliteRawValue
let decoded = try decoder.decode(
OptionalModel.self,
from: row
)
let empty = try decoder.decode(
OptionalModel.self,
from: SQLiteRow()
)
XCTAssertEqual(decoded, model)
XCTAssertEqual(empty, OptionalModel())
}
func testDecodeRowAsArray() throws {
let decoder = RowDecoder(
userInfo: [:],
dateDecodingStrategy: .deferredToDate
)
let dates = [
Date(timeIntervalSince1970: 1234),
Date(timeIntervalSince1970: 31415),
Date(timeIntervalSince1970: 123456789)
]
var row = SQLiteRow()
row["key0"] = dates[0].sqliteRawValue
row["key1"] = dates[1].sqliteRawValue
row["key2"] = dates[2].sqliteRawValue
let decoded = try decoder.decode([Date].self, from: row)
XCTAssertEqual(decoded, dates)
}
func testDecodeRowMissingRequiredField() {
let decoder = RowDecoder(
userInfo: [:],
dateDecodingStrategy: .deferredToDate
)
var row = SQLiteRow()
row["id"] = 1.sqliteRawValue
XCTAssertThrowsError(
try decoder.decode(SimpleModel.self, from: row)
) { error in
guard case DecodingError.keyNotFound = error else {
return XCTFail("Expected DecodingError.keyNotFound, but got: \(error)")
}
}
}
func testDecodeRowWrongType() {
let decoder = RowDecoder(
userInfo: [:],
dateDecodingStrategy: .deferredToDate
)
var row = SQLiteRow()
row["id"] = "not an int".sqliteRawValue
row["name"] = "test".sqliteRawValue
XCTAssertThrowsError(
try decoder.decode(SimpleModel.self, from: row)
) { error in
guard case DecodingError.typeMismatch = error else {
return XCTFail("Expected DecodingError.typeMismatch, but got: \(error)")
}
}
}
// MARK: - Decode array of SQLiteRow
func testDecodeRowArrayWithAllTypes() throws {
let decoder = RowDecoder(
userInfo: [:],
dateDecodingStrategy: .deferredToDate
)
let models = [
StandardModel(
id: 123,
type: .simple,
name: "John Doe",
age: 34,
isActive: true,
score: 3.1415,
createdAt: Date(timeIntervalSince1970: 12345),
payload: "payload".data(using: .utf8)!
),
StandardModel(
id: 456,
type: .multiple,
name: "Jane Doe",
age: 28,
isActive: false,
score: 2.7182,
createdAt: Date(timeIntervalSince1970: 67890),
payload: "another payload".data(using: .utf8)!
)
]
let rows: [SQLiteRow] = models.map { model in
var row = SQLiteRow()
row["id"] = model.id.sqliteRawValue
row["type"] = model.type.rawValue.sqliteRawValue
row["name"] = model.name.sqliteRawValue
row["age"] = model.age.sqliteRawValue
row["isActive"] = model.isActive.sqliteRawValue
row["score"] = model.score.sqliteRawValue
row["createdAt"] = model.createdAt.sqliteRawValue
row["payload"] = model.payload.sqliteRawValue
return row
}
let decoded = try decoder.decode([StandardModel].self, from: rows)
XCTAssertEqual(decoded, models)
}
func testDecodeRowArrayWithOptionalValues() throws {
let decoder = RowDecoder(
userInfo: [:],
dateDecodingStrategy: .deferredToDate
)
let models = [
OptionalModel(
id: 123,
type: .multiple,
name: "Jane Doe",
createdAt: Date(timeIntervalSince1970: 11000),
payload: "payload".data(using: .utf8)!
),
OptionalModel(
id: nil,
type: nil,
name: "John Doe",
createdAt: nil,
payload: nil
)
]
let rows: [SQLiteRow] = models.map { model in
var row = SQLiteRow()
row["id"] = model.id?.sqliteRawValue
row["type"] = model.type?.rawValue.sqliteRawValue
row["name"] = model.name?.sqliteRawValue
row["createdAt"] = model.createdAt?.sqliteRawValue
row["payload"] = model.payload?.sqliteRawValue
return row
}
let decoded = try decoder.decode([OptionalModel].self, from: rows)
XCTAssertEqual(decoded, models)
}
func testDecodeRowArrayAsDates() throws {
let decoder = RowDecoder(
userInfo: [:],
dateDecodingStrategy: .deferredToDate
)
let dates = [
[
Date(timeIntervalSince1970: 1234),
Date(timeIntervalSince1970: 31415),
Date(timeIntervalSince1970: 12345679)
],
[
Date(timeIntervalSince1970: 1234),
Date(timeIntervalSince1970: 31415),
Date(timeIntervalSince1970: 12345679)
]
]
let rows: [SQLiteRow] = dates.map { dates in
var row = SQLiteRow()
row["key0"] = dates[0].sqliteRawValue
row["key1"] = dates[1].sqliteRawValue
row["key2"] = dates[2].sqliteRawValue
return row
}
let decoded = try decoder.decode([[Date]].self, from: rows)
XCTAssertEqual(decoded, dates)
}
func testDecodeRowArrayMissingRequiredField() {
let decoder = RowDecoder(
userInfo: [:],
dateDecodingStrategy: .deferredToDate
)
var row = SQLiteRow()
row["id"] = 1.sqliteRawValue
XCTAssertThrowsError(
try decoder.decode([SimpleModel].self, from: [row])
) { error in
guard case DecodingError.keyNotFound = error else {
return XCTFail("Expected DecodingError.keyNotFound, but got: \(error)")
}
}
}
func testDecodeRowArrayWrongType() {
let decoder = RowDecoder(
userInfo: [:],
dateDecodingStrategy: .deferredToDate
)
var row = SQLiteRow()
row["id"] = "not an int".sqliteRawValue
row["name"] = "test".sqliteRawValue
XCTAssertThrowsError(
try decoder.decode([SimpleModel].self, from: [row])
) { error in
guard case DecodingError.typeMismatch = error else {
return XCTFail("Expected DecodingError.typeMismatch, but got: \(error)")
}
}
}
}
private extension RowDecoderTests {
enum `Type`: String, Decodable, Equatable {
case simple
case multiple
}
struct StandardModel: Decodable, Equatable {
let id: Int
let type: `Type`
let name: String
let age: Int
let isActive: Bool
let score: Double
let createdAt: Date
let payload: Data
}
struct OptionalModel: Decodable, Equatable {
let id: Int?
let type: `Type`?
let name: String?
let createdAt: Date?
let payload: Data?
init(
id: Int? = nil,
type: `Type`? = nil,
name: String? = nil,
createdAt: Date? = nil,
payload: Data? = nil
) {
self.id = id
self.type = type
self.name = name
self.createdAt = createdAt
self.payload = payload
}
}
struct SimpleModel: Decodable, Equatable {
let id: Int
let name: String
}
}

View File

@@ -0,0 +1,231 @@
import XCTest
import DataLiteCore
import DataLiteCoder
final class RowEncoderTests: XCTestCase {
// MARK: - Encode to SQLiteRow
func testEncodeToRowWithAllTypes() throws {
let createdAt = Date(timeIntervalSince1970: 12345)
let payload = "payload".data(using: .utf8)!
let model = StandardModel(
id: 123456,
type: .simple,
name: "John Doe",
age: 34,
isActive: true,
score: 3.1415,
createdAt: createdAt,
payload: payload
)
let row = try RowEncoder(
userInfo: [:],
dateEncodingStrategy: .deferredToDate
).encode(model)
XCTAssertEqual(row.count, 8)
XCTAssertEqual(row["id"], .int(123456))
XCTAssertEqual(row["type"], .text("simple"))
XCTAssertEqual(row["name"], .text("John Doe"))
XCTAssertEqual(row["age"], .int(34))
XCTAssertEqual(row["isActive"], .int(1))
XCTAssertEqual(row["score"], .real(3.1415))
XCTAssertEqual(row["createdAt"], createdAt.sqliteRawValue)
XCTAssertEqual(row["payload"], .blob(payload))
}
func testEncodeToRowWithOptionalValues() throws {
let createdAt = Date(timeIntervalSince1970: 12345)
let payload = "payload".data(using: .utf8)!
let model = OptionalModel(
id: 123456,
type: .multiple,
name: "Jane Doe",
createdAt: createdAt,
payload: payload
)
let row = try RowEncoder(
userInfo: [:],
dateEncodingStrategy: .deferredToDate
).encode(model)
XCTAssertEqual(row.count, 5)
XCTAssertEqual(row["id"], .int(123456))
XCTAssertEqual(row["type"], .text("multiple"))
XCTAssertEqual(row["name"], .text("Jane Doe"))
XCTAssertEqual(row["createdAt"], createdAt.sqliteRawValue)
XCTAssertEqual(row["payload"], .blob(payload))
}
func testEncodeToRowWithOptionalNilValues() throws {
let row = try RowEncoder(
userInfo: [:],
dateEncodingStrategy: .deferredToDate
).encode(OptionalModel())
XCTAssertEqual(row.count, 5)
XCTAssertEqual(row["id"], .null)
XCTAssertEqual(row["type"], .null)
XCTAssertEqual(row["name"], .null)
XCTAssertEqual(row["createdAt"], .null)
XCTAssertEqual(row["payload"], .null)
}
// MARK: - Encode to Array of SQLiteRow
func testEncodeToRowArrayWithAllTypes() throws {
let createdAt = Date(timeIntervalSince1970: 12345)
let payload = "payload".data(using: .utf8)!
let models = [
StandardModel(
id: 123456,
type: .simple,
name: "John Doe",
age: 34,
isActive: true,
score: 3.1415,
createdAt: createdAt,
payload: payload
),
StandardModel(
id: 456,
type: .multiple,
name: "Jane Doe",
age: 28,
isActive: false,
score: 2.7182,
createdAt: createdAt,
payload: payload
)
]
let rows = try RowEncoder(
userInfo: [:],
dateEncodingStrategy: .deferredToDate
).encode(models)
XCTAssertEqual(rows.count, 2)
XCTAssertEqual(rows[0].count, 8)
XCTAssertEqual(rows[1].count, 8)
XCTAssertEqual(rows[0]["id"], .int(123456))
XCTAssertEqual(rows[0]["type"], .text("simple"))
XCTAssertEqual(rows[0]["name"], .text("John Doe"))
XCTAssertEqual(rows[0]["age"], .int(34))
XCTAssertEqual(rows[0]["isActive"], .int(1))
XCTAssertEqual(rows[0]["score"], .real(3.1415))
XCTAssertEqual(rows[0]["createdAt"], createdAt.sqliteRawValue)
XCTAssertEqual(rows[0]["payload"], .blob(payload))
XCTAssertEqual(rows[1]["id"], .int(456))
XCTAssertEqual(rows[1]["type"], .text("multiple"))
XCTAssertEqual(rows[1]["name"], .text("Jane Doe"))
XCTAssertEqual(rows[1]["age"], .int(28))
XCTAssertEqual(rows[1]["isActive"], .int(0))
XCTAssertEqual(rows[1]["score"], .real(2.7182))
XCTAssertEqual(rows[1]["createdAt"], createdAt.sqliteRawValue)
XCTAssertEqual(rows[1]["payload"], .blob(payload))
}
func testEncodeToRowArrayWithOptionalValues() throws {
let createdAt = Date(timeIntervalSince1970: 12345)
let payload = "payload".data(using: .utf8)!
let models = [
OptionalModel(
id: 123,
type: .multiple,
name: "Jane Doe",
createdAt: createdAt,
payload: payload
),
OptionalModel(
id: nil,
type: nil,
name: "John Doe",
createdAt: nil,
payload: nil
)
]
let rows = try RowEncoder(
userInfo: [:],
dateEncodingStrategy: .deferredToDate
).encode(models)
XCTAssertEqual(rows.count, 2)
XCTAssertEqual(rows[0].count, 5)
XCTAssertEqual(rows[1].count, 5)
XCTAssertEqual(rows[0]["id"], .int(123))
XCTAssertEqual(rows[0]["type"], .text("multiple"))
XCTAssertEqual(rows[0]["name"], .text("Jane Doe"))
XCTAssertEqual(rows[0]["createdAt"], createdAt.sqliteRawValue)
XCTAssertEqual(rows[0]["payload"], .blob(payload))
XCTAssertEqual(rows[1]["id"], .null)
XCTAssertEqual(rows[1]["type"], .null)
XCTAssertEqual(rows[1]["name"], .text("John Doe"))
XCTAssertEqual(rows[1]["createdAt"], .null)
XCTAssertEqual(rows[1]["payload"], .null)
}
}
private extension RowEncoderTests {
enum `Type`: String, Encodable, Equatable {
case simple
case multiple
}
struct StandardModel: Encodable, Equatable {
let id: Int
let type: `Type`
let name: String
let age: Int
let isActive: Bool
let score: Double
let createdAt: Date
let payload: Data
}
struct OptionalModel: Encodable, Equatable {
let id: Int?
let type: `Type`?
let name: String?
let createdAt: Date?
let payload: Data?
init(
id: Int? = nil,
type: `Type`? = nil,
name: String? = nil,
createdAt: Date? = nil,
payload: Data? = nil
) {
self.id = id
self.type = type
self.name = name
self.createdAt = createdAt
self.payload = payload
}
enum CodingKeys: CodingKey {
case id
case type
case name
case createdAt
case payload
}
func encode(to encoder: any Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encodeIfPresent(self.id, forKey: .id)
try container.encodeIfPresent(self.type, forKey: .type)
try container.encodeIfPresent(self.name, forKey: .name)
try container.encodeIfPresent(self.createdAt, forKey: .createdAt)
try container.encodeIfPresent(self.payload, forKey: .payload)
}
}
}