Files

239 lines
8.0 KiB
Swift

import XCTest
@testable import GRDB
// a -> b* -> c
// -> d*
// -> c*
private struct A: TableRecord, FetchableRecord, Decodable {
static let databaseTableName = "a"
static let b = hasOne(B.self)
static let c = hasOne(C.self)
var id: Int64
}
private struct B: TableRecord, FetchableRecord, Decodable {
static let databaseTableName = "b"
static let c = hasOne(C.self)
static let d = hasOne(D.self)
var id: Int64
var aid: Int64
}
private struct C: TableRecord, FetchableRecord, Decodable {
static let databaseTableName = "c"
var id: Int64
var aid: Int64?
var bid: Int64?
}
private struct D: TableRecord, FetchableRecord, Decodable {
static let databaseTableName = "d"
var id: Int64
var bid: Int64
}
class AssociationRowScopeSearchTests: GRDBTestCase {
override func setup(_ dbWriter: some DatabaseWriter) throws {
try dbWriter.write { db in
// 1. Prepare data
try db.create(table: "a") { t in
t.primaryKey("id", .integer)
}
try db.create(table: "b") { t in
t.primaryKey("id", .integer)
t.column("aid", .integer).references("a")
}
try db.create(table: "c") { t in
t.primaryKey("id", .integer)
t.column("aid", .integer).references("a")
t.column("bid", .integer).references("b")
}
try db.create(table: "d") { t in
t.primaryKey("id", .integer)
t.column("bid", .integer).references("b")
}
try db.execute(sql: "INSERT INTO a (id) VALUES (1)")
try db.execute(sql: "INSERT INTO b (id, aid) VALUES (2, 1)")
try db.execute(sql: "INSERT INTO c (id, aid, bid) VALUES (3, NULL, 2)")
try db.execute(sql: "INSERT INTO c (id, aid, bid) VALUES (4, 1, NULL)")
try db.execute(sql: "INSERT INTO d (id, bid) VALUES (5, 2)")
}
}
private let testedRequest = A
.including(required: A.b
.including(required: B.c)
.including(required: B.d)
)
.including(required: A.c)
func testTestedRequest() throws {
// Assert that we fetch the expected nested scopes before we start testing breadth-first lookup
let dbQueue = try makeDatabaseQueue()
try dbQueue.inDatabase { db in
let rows = try testedRequest.asRequest(of: Row.self).fetchAll(db)
XCTAssertEqual(rows.count, 1)
let row = rows[0]
XCTAssertEqual(row.unscoped, ["id": 1])
XCTAssertEqual(row.scopes["b"]!.unscoped, ["id": 2, "aid": 1])
XCTAssertEqual(row.scopes["b"]!.scopes["c"]!, ["id": 3, "aid": nil, "bid": 2])
XCTAssertEqual(row.scopes["b"]!.scopes["d"]!, ["id": 5, "bid": 2])
XCTAssertEqual(row.scopes["c"]!, ["id": 4, "aid": 1, "bid": nil])
}
}
func testBreadthFirstScopeLookup() throws {
let dbQueue = try makeDatabaseQueue()
try dbQueue.inDatabase { db in
let row = try testedRequest.asRequest(of: Row.self).fetchOne(db)!
XCTAssertEqual(row.scopesTree["b"]!.unscoped, ["id": 2, "aid": 1])
XCTAssertEqual(row.scopesTree["c"]!, ["id": 4, "aid": 1, "bid": nil])
XCTAssertEqual(row.scopesTree["d"]!, ["id": 5, "bid": 2])
}
}
func testFetchableRecordDecoding() throws {
struct Record: FetchableRecord {
var a: A
var b: B
var c: C
var d: D
init(row: Row) throws {
a = try A(row: row)
b = row["b"]
c = row["c"]
d = row["d"]
}
}
let dbQueue = try makeDatabaseQueue()
try dbQueue.inDatabase { db in
let record = try testedRequest.asRequest(of: Record.self).fetchOne(db)!
XCTAssertEqual(record.a.id, 1)
XCTAssertEqual(record.b.id, 2)
XCTAssertEqual(record.b.aid, 1)
XCTAssertEqual(record.c.id, 4)
XCTAssertEqual(record.c.aid, 1)
XCTAssertNil(record.c.bid)
XCTAssertEqual(record.d.id, 5)
XCTAssertEqual(record.d.bid, 2)
}
}
func testFlatDecodableRecordDecoding() throws {
struct Record: FetchableRecord, Decodable {
var a: A
var b: B
var c: C
var d: D
}
let dbQueue = try makeDatabaseQueue()
try dbQueue.inDatabase { db in
let record = try testedRequest.asRequest(of: Record.self).fetchOne(db)!
XCTAssertEqual(record.a.id, 1)
XCTAssertEqual(record.b.id, 2)
XCTAssertEqual(record.b.aid, 1)
XCTAssertEqual(record.c.id, 4)
XCTAssertEqual(record.c.aid, 1)
XCTAssertNil(record.c.bid)
XCTAssertEqual(record.d.id, 5)
XCTAssertEqual(record.d.bid, 2)
}
}
func testNestedDecodableRecordDecoding() throws {
struct NestedB: Decodable {
var b: B
var c: C
var d: D
}
struct Record: FetchableRecord, Decodable {
var a: A
var b: NestedB
var c: C
}
let dbQueue = try makeDatabaseQueue()
try dbQueue.inDatabase { db in
let record = try testedRequest.asRequest(of: Record.self).fetchOne(db)!
XCTAssertEqual(record.a.id, 1)
XCTAssertEqual(record.b.b.id, 2)
XCTAssertEqual(record.b.b.aid, 1)
XCTAssertEqual(record.b.c.id, 3)
XCTAssertNil(record.b.c.aid)
XCTAssertEqual(record.b.c.bid, 2)
XCTAssertEqual(record.b.d.id, 5)
XCTAssertEqual(record.b.d.bid, 2)
XCTAssertEqual(record.c.id, 4)
XCTAssertEqual(record.c.aid, 1)
XCTAssertNil(record.c.bid)
}
}
func testDecodableWithCustomRowDecoding() throws {
struct CustomA: FetchableRecord, Decodable {
var id: Int64
var custom: Bool?
init(row: Row) {
id = row["id"]
custom = true
}
}
struct CustomB: FetchableRecord, Decodable {
var id: Int64
var aid: Int64
var custom: Bool?
init(row: Row) {
id = row["id"]
aid = row["aid"]
custom = true
}
}
struct CustomC: FetchableRecord, Decodable {
var id: Int64
var aid: Int64?
var bid: Int64?
var custom: Bool?
init(row: Row) {
id = row["id"]
aid = row["aid"]
bid = row["bid"]
custom = true
}
}
struct CustomD: FetchableRecord, Decodable {
var id: Int64
var bid: Int64
var custom: Bool?
init(row: Row) {
id = row["id"]
bid = row["bid"]
custom = true
}
}
struct Record: FetchableRecord, Decodable {
var a: CustomA
var b: CustomB
var c: CustomC
var d: CustomD
}
let dbQueue = try makeDatabaseQueue()
try dbQueue.inDatabase { db in
let record = try testedRequest.asRequest(of: Record.self).fetchOne(db)!
XCTAssertEqual(record.a.id, 1)
XCTAssertEqual(record.a.custom, true)
XCTAssertEqual(record.b.id, 2)
XCTAssertEqual(record.b.aid, 1)
XCTAssertEqual(record.b.custom, true)
XCTAssertEqual(record.c.id, 4)
XCTAssertEqual(record.c.aid, 1)
XCTAssertNil(record.c.bid)
XCTAssertEqual(record.c.custom, true)
XCTAssertEqual(record.d.id, 5)
XCTAssertEqual(record.d.bid, 2)
XCTAssertEqual(record.d.custom, true)
}
}
}