271 lines
13 KiB
Swift
271 lines
13 KiB
Swift
import XCTest
|
|
import GRDB
|
|
|
|
private struct Team: Codable, FetchableRecord, PersistableRecord {
|
|
static let databaseTableName = "teams"
|
|
var id: Int64
|
|
var name: String
|
|
}
|
|
|
|
private struct Player: Codable, FetchableRecord, PersistableRecord {
|
|
static let databaseTableName = "players"
|
|
static let defaultTeam = belongsTo(Team.self)
|
|
static let customTeam = belongsTo(Team.self, key: "customTeam")
|
|
var id: Int64
|
|
var teamId: Int64?
|
|
var name: String
|
|
}
|
|
|
|
/// Test row scopes
|
|
class AssociationBelongsToRowScopeTests: GRDBTestCase {
|
|
|
|
override func setup(_ dbWriter: some DatabaseWriter) throws {
|
|
try dbWriter.write { db in
|
|
try db.create(table: "teams") { t in
|
|
t.primaryKey("id", .integer)
|
|
t.column("name", .text)
|
|
}
|
|
try db.create(table: "players") { t in
|
|
t.primaryKey("id", .integer)
|
|
t.belongsTo("team")
|
|
t.column("name", .text)
|
|
}
|
|
|
|
try Team(id: 1, name: "Reds").insert(db)
|
|
try Player(id: 1, teamId: 1, name: "Arthur").insert(db)
|
|
try Player(id: 2, teamId: nil, name: "Barbara").insert(db)
|
|
}
|
|
}
|
|
|
|
func testJoiningDoesNotUseAnyRowAdapter() throws {
|
|
let dbQueue = try makeDatabaseQueue()
|
|
try dbQueue.inDatabase { db in
|
|
let request = Player.joining(required: Player.defaultTeam)
|
|
let adapter = try request.makePreparedRequest(db, forSingleResult: false).adapter
|
|
XCTAssertNil(adapter)
|
|
}
|
|
}
|
|
|
|
func testDefaultScopeIncludingRequired() throws {
|
|
let dbQueue = try makeDatabaseQueue()
|
|
let request = Player.including(required: Player.defaultTeam)
|
|
let rows = try dbQueue.inDatabase { try Row.fetchAll($0, request) }
|
|
XCTAssertEqual(rows.count, 1)
|
|
XCTAssertEqual(rows[0].unscoped, ["id":1, "teamId":1, "name":"Arthur"])
|
|
XCTAssertEqual(Set(rows[0].scopes.names), ["team"])
|
|
XCTAssertEqual(rows[0].scopes["team"]!, ["id":1, "name":"Reds"])
|
|
}
|
|
|
|
func testDefaultScopeIncludingRequiredEmptySelection() throws {
|
|
let dbQueue = try makeDatabaseQueue()
|
|
let request = Player.select([]).including(required: Player.defaultTeam)
|
|
let rows = try dbQueue.inDatabase { try Row.fetchAll($0, request) }
|
|
XCTAssertEqual(rows.count, 1)
|
|
XCTAssertEqual(rows[0].unscoped, [:])
|
|
XCTAssertEqual(Set(rows[0].scopes.names), ["team"])
|
|
XCTAssertEqual(rows[0].scopes["team"]!, ["id":1, "name":"Reds"])
|
|
}
|
|
|
|
func testDefaultScopeAnnotatedWithRequired() throws {
|
|
let dbQueue = try makeDatabaseQueue()
|
|
let request = Player.annotated(withRequired: Player.defaultTeam)
|
|
let rows = try dbQueue.inDatabase { try Row.fetchAll($0, request) }
|
|
XCTAssertEqual(rows.count, 1)
|
|
XCTAssertEqual(rows[0], ["id":1, "teamId":1, "name":"Arthur", "id":1, "name":"Reds"])
|
|
}
|
|
|
|
func testDefaultScopeAnnotatedWithRequiredCustomSelection() throws {
|
|
let dbQueue = try makeDatabaseQueue()
|
|
let request = Player.annotated(withRequired: Player.defaultTeam.select(Column("name").forKey("teamName")))
|
|
let rows = try dbQueue.inDatabase { try Row.fetchAll($0, request) }
|
|
XCTAssertEqual(rows.count, 1)
|
|
XCTAssertEqual(rows[0], ["id":1, "teamId":1, "name":"Arthur", "teamName":"Reds"])
|
|
}
|
|
|
|
func testDefaultScopeIncludingOptional() throws {
|
|
let dbQueue = try makeDatabaseQueue()
|
|
let request = Player.including(optional: Player.defaultTeam)
|
|
let rows = try dbQueue.inDatabase { try Row.fetchAll($0, request) }
|
|
XCTAssertEqual(rows.count, 2)
|
|
XCTAssertEqual(rows[0].unscoped, ["id":1, "teamId":1, "name":"Arthur"])
|
|
XCTAssertEqual(Set(rows[0].scopes.names), ["team"])
|
|
XCTAssertEqual(rows[0].scopes["team"]!, ["id":1, "name":"Reds"])
|
|
XCTAssertEqual(rows[1].unscoped, ["id":2, "teamId":nil, "name":"Barbara"])
|
|
XCTAssertEqual(Set(rows[1].scopes.names), ["team"])
|
|
XCTAssertEqual(rows[1].scopes["team"]!, ["id":nil, "name":nil])
|
|
}
|
|
|
|
func testDefaultScopeAnnotatedWithOptional() throws {
|
|
let dbQueue = try makeDatabaseQueue()
|
|
let request = Player.annotated(withOptional: Player.defaultTeam)
|
|
let rows = try dbQueue.inDatabase { try Row.fetchAll($0, request) }
|
|
XCTAssertEqual(rows.count, 2)
|
|
XCTAssertEqual(rows[0], ["id":1, "teamId":1, "name":"Arthur", "id":1, "name":"Reds"])
|
|
XCTAssertEqual(rows[1], ["id":2, "teamId":nil, "name":"Barbara", "id":nil, "name":nil])
|
|
}
|
|
|
|
func testDefaultScopeAnnotatedWithOptionalCustomSelection() throws {
|
|
let dbQueue = try makeDatabaseQueue()
|
|
let request = Player.annotated(withOptional: Player.defaultTeam.select(Column("name").forKey("teamName")))
|
|
let rows = try dbQueue.inDatabase { try Row.fetchAll($0, request) }
|
|
XCTAssertEqual(rows.count, 2)
|
|
XCTAssertEqual(rows[0], ["id":1, "teamId":1, "name":"Arthur", "teamName":"Reds"])
|
|
XCTAssertEqual(rows[1], ["id":2, "teamId":nil, "name":"Barbara", "teamName":nil])
|
|
}
|
|
|
|
func testDefaultScopeJoiningRequired() throws {
|
|
let dbQueue = try makeDatabaseQueue()
|
|
let request = Player.joining(required: Player.defaultTeam)
|
|
let rows = try dbQueue.inDatabase { try Row.fetchAll($0, request) }
|
|
XCTAssertEqual(rows.count, 1)
|
|
XCTAssertEqual(rows[0].unscoped, ["id":1, "teamId":1, "name":"Arthur"])
|
|
XCTAssertTrue(rows[0].scopes.names.isEmpty)
|
|
}
|
|
|
|
func testDefaultScopeJoiningOptional() throws {
|
|
let dbQueue = try makeDatabaseQueue()
|
|
let request = Player.joining(optional: Player.defaultTeam)
|
|
let rows = try dbQueue.inDatabase { try Row.fetchAll($0, request) }
|
|
XCTAssertEqual(rows.count, 2)
|
|
XCTAssertEqual(rows[0].unscoped, ["id":1, "teamId":1, "name":"Arthur"])
|
|
XCTAssertTrue(rows[0].scopes.names.isEmpty)
|
|
XCTAssertEqual(rows[1].unscoped, ["id":2, "teamId":nil, "name":"Barbara"])
|
|
XCTAssertTrue(rows[0].scopes.names.isEmpty)
|
|
}
|
|
|
|
func testCustomScopeIncludingRequired() throws {
|
|
let dbQueue = try makeDatabaseQueue()
|
|
let request = Player.including(required: Player.customTeam)
|
|
let rows = try dbQueue.inDatabase { try Row.fetchAll($0, request) }
|
|
XCTAssertEqual(rows.count, 1)
|
|
XCTAssertEqual(rows[0].unscoped, ["id":1, "teamId":1, "name":"Arthur"])
|
|
XCTAssertEqual(Set(rows[0].scopes.names), ["customTeam"])
|
|
XCTAssertEqual(rows[0].scopes["customTeam"]!, ["id":1, "name":"Reds"])
|
|
}
|
|
|
|
func testCustomScopeIncludingRequiredEmptySelection() throws {
|
|
let dbQueue = try makeDatabaseQueue()
|
|
let request = Player.select([]).including(required: Player.customTeam)
|
|
let rows = try dbQueue.inDatabase { try Row.fetchAll($0, request) }
|
|
XCTAssertEqual(rows.count, 1)
|
|
XCTAssertEqual(rows[0].unscoped, [:])
|
|
XCTAssertEqual(Set(rows[0].scopes.names), ["customTeam"])
|
|
XCTAssertEqual(rows[0].scopes["customTeam"]!, ["id":1, "name":"Reds"])
|
|
}
|
|
|
|
func testCustomScopeAnnotatedWithRequired() throws {
|
|
let dbQueue = try makeDatabaseQueue()
|
|
let request = Player.annotated(withRequired: Player.customTeam)
|
|
let rows = try dbQueue.inDatabase { try Row.fetchAll($0, request) }
|
|
XCTAssertEqual(rows.count, 1)
|
|
XCTAssertEqual(rows[0], ["id":1, "teamId":1, "name":"Arthur", "id":1, "name":"Reds"])
|
|
}
|
|
|
|
func testCustomScopeAnnotatedWithRequiredCustomSelection() throws {
|
|
let dbQueue = try makeDatabaseQueue()
|
|
let request = Player.annotated(withRequired: Player.customTeam.select(Column("name").forKey("teamName")))
|
|
let rows = try dbQueue.inDatabase { try Row.fetchAll($0, request) }
|
|
XCTAssertEqual(rows.count, 1)
|
|
XCTAssertEqual(rows[0], ["id":1, "teamId":1, "name":"Arthur", "teamName":"Reds"])
|
|
}
|
|
|
|
func testCustomScopeIncludingOptional() throws {
|
|
let dbQueue = try makeDatabaseQueue()
|
|
let request = Player.including(optional: Player.customTeam)
|
|
let rows = try dbQueue.inDatabase { try Row.fetchAll($0, request) }
|
|
XCTAssertEqual(rows.count, 2)
|
|
XCTAssertEqual(rows[0].unscoped, ["id":1, "teamId":1, "name":"Arthur"])
|
|
XCTAssertEqual(Set(rows[0].scopes.names), ["customTeam"])
|
|
XCTAssertEqual(rows[0].scopes["customTeam"]!, ["id":1, "name":"Reds"])
|
|
XCTAssertEqual(rows[1].unscoped, ["id":2, "teamId":nil, "name":"Barbara"])
|
|
XCTAssertEqual(Set(rows[1].scopes.names), ["customTeam"])
|
|
XCTAssertEqual(rows[1].scopes["customTeam"]!, ["id":nil, "name":nil])
|
|
}
|
|
|
|
func testCustomScopeAnnotatedWithOptional() throws {
|
|
let dbQueue = try makeDatabaseQueue()
|
|
let request = Player.annotated(withOptional: Player.customTeam)
|
|
let rows = try dbQueue.inDatabase { try Row.fetchAll($0, request) }
|
|
XCTAssertEqual(rows.count, 2)
|
|
XCTAssertEqual(rows[0], ["id":1, "teamId":1, "name":"Arthur", "id":1, "name":"Reds"])
|
|
XCTAssertEqual(rows[1], ["id":2, "teamId":nil, "name":"Barbara", "id":nil, "name":nil])
|
|
}
|
|
|
|
func testCustomScopeAnnotatedWithOptionalCustomSelection() throws {
|
|
let dbQueue = try makeDatabaseQueue()
|
|
let request = Player.annotated(withOptional: Player.customTeam.select(Column("name").forKey("teamName")))
|
|
let rows = try dbQueue.inDatabase { try Row.fetchAll($0, request) }
|
|
XCTAssertEqual(rows.count, 2)
|
|
XCTAssertEqual(rows[0], ["id":1, "teamId":1, "name":"Arthur", "teamName":"Reds"])
|
|
XCTAssertEqual(rows[1], ["id":2, "teamId":nil, "name":"Barbara", "teamName":nil])
|
|
}
|
|
|
|
func testCustomPluralScopeIncludingRequired() throws {
|
|
// Make sure explicit plural keys are preserved
|
|
let dbQueue = try makeDatabaseQueue()
|
|
do {
|
|
let request = Player.including(required: Player.belongsTo(Team.self, key: "teams"))
|
|
let rows = try dbQueue.inDatabase { try Row.fetchAll($0, request) }
|
|
XCTAssertEqual(rows.count, 1)
|
|
XCTAssertEqual(rows[0].unscoped, ["id":1, "teamId":1, "name":"Arthur"])
|
|
XCTAssertEqual(Set(rows[0].scopes.names), ["teams"])
|
|
XCTAssertEqual(rows[0].scopes["teams"]!, ["id":1, "name":"Reds"])
|
|
}
|
|
do {
|
|
let request = Player.including(required: Player.defaultTeam.forKey("teams"))
|
|
let rows = try dbQueue.inDatabase { try Row.fetchAll($0, request) }
|
|
XCTAssertEqual(rows.count, 1)
|
|
XCTAssertEqual(rows[0].unscoped, ["id":1, "teamId":1, "name":"Arthur"])
|
|
XCTAssertEqual(Set(rows[0].scopes.names), ["teams"])
|
|
XCTAssertEqual(rows[0].scopes["teams"]!, ["id":1, "name":"Reds"])
|
|
}
|
|
}
|
|
|
|
func testCustomPluralScopeIncludingOptional() throws {
|
|
// Make sure explicit plural keys are preserved
|
|
let dbQueue = try makeDatabaseQueue()
|
|
do {
|
|
let request = Player.including(optional: Player.belongsTo(Team.self, key: "teams"))
|
|
let rows = try dbQueue.inDatabase { try Row.fetchAll($0, request) }
|
|
XCTAssertEqual(rows.count, 2)
|
|
XCTAssertEqual(rows[0].unscoped, ["id":1, "teamId":1, "name":"Arthur"])
|
|
XCTAssertEqual(Set(rows[0].scopes.names), ["teams"])
|
|
XCTAssertEqual(rows[0].scopes["teams"]!, ["id":1, "name":"Reds"])
|
|
XCTAssertEqual(rows[1].unscoped, ["id":2, "teamId":nil, "name":"Barbara"])
|
|
XCTAssertEqual(Set(rows[1].scopes.names), ["teams"])
|
|
XCTAssertEqual(rows[1].scopes["teams"]!, ["id":nil, "name":nil])
|
|
}
|
|
do {
|
|
let request = Player.including(optional: Player.defaultTeam.forKey("teams"))
|
|
let rows = try dbQueue.inDatabase { try Row.fetchAll($0, request) }
|
|
XCTAssertEqual(rows.count, 2)
|
|
XCTAssertEqual(rows[0].unscoped, ["id":1, "teamId":1, "name":"Arthur"])
|
|
XCTAssertEqual(Set(rows[0].scopes.names), ["teams"])
|
|
XCTAssertEqual(rows[0].scopes["teams"]!, ["id":1, "name":"Reds"])
|
|
XCTAssertEqual(rows[1].unscoped, ["id":2, "teamId":nil, "name":"Barbara"])
|
|
XCTAssertEqual(Set(rows[1].scopes.names), ["teams"])
|
|
XCTAssertEqual(rows[1].scopes["teams"]!, ["id":nil, "name":nil])
|
|
}
|
|
}
|
|
|
|
func testCustomScopeJoiningRequired() throws {
|
|
let dbQueue = try makeDatabaseQueue()
|
|
let request = Player.joining(required: Player.customTeam)
|
|
let rows = try dbQueue.inDatabase { try Row.fetchAll($0, request) }
|
|
XCTAssertEqual(rows.count, 1)
|
|
XCTAssertEqual(rows[0].unscoped, ["id":1, "teamId":1, "name":"Arthur"])
|
|
XCTAssertTrue(rows[0].scopes.names.isEmpty)
|
|
}
|
|
|
|
func testCustomScopeJoiningOptional() throws {
|
|
let dbQueue = try makeDatabaseQueue()
|
|
let request = Player.joining(optional: Player.customTeam)
|
|
let rows = try dbQueue.inDatabase { try Row.fetchAll($0, request) }
|
|
XCTAssertEqual(rows.count, 2)
|
|
XCTAssertEqual(rows[0].unscoped, ["id":1, "teamId":1, "name":"Arthur"])
|
|
XCTAssertTrue(rows[0].scopes.names.isEmpty)
|
|
XCTAssertEqual(rows[1].unscoped, ["id":2, "teamId":nil, "name":"Barbara"])
|
|
XCTAssertTrue(rows[0].scopes.names.isEmpty)
|
|
}
|
|
}
|