714 lines
26 KiB
Swift
714 lines
26 KiB
Swift
import XCTest
|
|
import GRDB
|
|
|
|
private class Person : Record {
|
|
var id: Int64!
|
|
var name: String!
|
|
var age: Int?
|
|
var creationDate: Date!
|
|
|
|
init(id: Int64? = nil, name: String? = nil, age: Int? = nil, creationDate: Date? = nil) {
|
|
self.id = id
|
|
self.name = name
|
|
self.age = age
|
|
self.creationDate = creationDate
|
|
super.init()
|
|
}
|
|
|
|
static func setup(inDatabase db: Database) throws {
|
|
try db.execute(sql: """
|
|
CREATE TABLE persons (
|
|
id INTEGER PRIMARY KEY,
|
|
creationDate TEXT NOT NULL,
|
|
name TEXT NOT NULL,
|
|
age INT)
|
|
""")
|
|
}
|
|
|
|
// Record
|
|
|
|
override class var databaseTableName: String {
|
|
"persons"
|
|
}
|
|
|
|
required init(row: Row) throws {
|
|
id = row["id"]
|
|
age = row["age"]
|
|
name = row["name"]
|
|
creationDate = row["creationDate"]
|
|
try super.init(row: row)
|
|
}
|
|
|
|
override func encode(to container: inout PersistenceContainer) throws {
|
|
container["id"] = id
|
|
container["name"] = name
|
|
container["age"] = age
|
|
container["creationDate"] = creationDate
|
|
}
|
|
|
|
override func willInsert(_ db: Database) throws {
|
|
if creationDate == nil {
|
|
creationDate = Date()
|
|
}
|
|
try super.willInsert(db)
|
|
}
|
|
|
|
override func didInsert(_ inserted: InsertionSuccess) {
|
|
super.didInsert(inserted)
|
|
id = inserted.rowID
|
|
}
|
|
}
|
|
|
|
private class IntegerPropertyOnRealAffinityColumn : Record {
|
|
var value: Int?
|
|
|
|
init(value: Int?) {
|
|
self.value = value
|
|
super.init()
|
|
}
|
|
|
|
// Record
|
|
|
|
required init(row: Row) throws {
|
|
value = row["value"]
|
|
try super.init(row: row)
|
|
}
|
|
|
|
override func encode(to container: inout PersistenceContainer) throws {
|
|
container["value"] = value
|
|
}
|
|
}
|
|
|
|
private class PersonWithModifiedCaseColumns: Record {
|
|
var id: Int64!
|
|
var name: String!
|
|
var age: Int?
|
|
var creationDate: Date!
|
|
|
|
init(id: Int64? = nil, name: String? = nil, age: Int? = nil, creationDate: Date? = nil) {
|
|
self.id = id
|
|
self.name = name
|
|
self.age = age
|
|
self.creationDate = creationDate
|
|
super.init()
|
|
}
|
|
|
|
// Record
|
|
|
|
override class var databaseTableName: String {
|
|
"persons"
|
|
}
|
|
|
|
required init(row: Row) throws {
|
|
id = row["ID"]
|
|
age = row["AGE"]
|
|
name = row["NAME"]
|
|
creationDate = row["CREATIONDATE"]
|
|
try super.init(row: row)
|
|
}
|
|
|
|
override func encode(to container: inout PersistenceContainer) throws {
|
|
container["ID"] = id
|
|
container["NAME"] = name
|
|
container["AGE"] = age
|
|
container["CREATIONDATE"] = creationDate
|
|
}
|
|
|
|
override func willInsert(_ db: Database) throws {
|
|
if creationDate == nil {
|
|
creationDate = Date()
|
|
}
|
|
try super.willInsert(db)
|
|
}
|
|
|
|
override func didInsert(_ inserted: InsertionSuccess) {
|
|
super.didInsert(inserted)
|
|
id = inserted.rowID
|
|
}
|
|
}
|
|
|
|
class RecordEditedTests: GRDBTestCase {
|
|
|
|
override func setup(_ dbWriter: some DatabaseWriter) throws {
|
|
var migrator = DatabaseMigrator()
|
|
migrator.registerMigration("createPerson", migrate: Person.setup)
|
|
try migrator.migrate(dbWriter)
|
|
}
|
|
|
|
func testRecordIsEditedAfterInit() {
|
|
// Create a Record. No fetch has happen, so we don't know if it is
|
|
// identical to its eventual row in the database. So it is edited.
|
|
let person = Person(name: "Arthur", age: 41)
|
|
XCTAssertTrue(person.hasDatabaseChanges)
|
|
}
|
|
|
|
func testRecordIsEditedAfterInitFromRow() throws {
|
|
// Create a Record from a row. The row may not come from the database.
|
|
// So it is edited.
|
|
let row = Row(["name": "Arthur", "age": 41])
|
|
let person = try Person(row: row)
|
|
XCTAssertTrue(person.hasDatabaseChanges)
|
|
}
|
|
|
|
func testRecordIsNotEditedAfterFullFetch() throws {
|
|
// Fetch a record from a row that contains all the columns in
|
|
// persistence container: An update statement, which only saves the
|
|
// columns in persistence container would perform no change. So the
|
|
// record is not edited.
|
|
let dbQueue = try makeDatabaseQueue()
|
|
try dbQueue.inDatabase { db in
|
|
try Person(name: "Arthur", age: 41).insert(db)
|
|
let person = try Person.fetchOne(db, sql: "SELECT * FROM persons")!
|
|
XCTAssertFalse(person.hasDatabaseChanges)
|
|
}
|
|
try dbQueue.inDatabase { db in
|
|
try PersonWithModifiedCaseColumns(name: "Arthur", age: 41).insert(db)
|
|
let person = try PersonWithModifiedCaseColumns.fetchOne(db, sql: "SELECT * FROM persons")!
|
|
XCTAssertFalse(person.hasDatabaseChanges)
|
|
}
|
|
}
|
|
|
|
func testRecordIsNotEditedAfterFullFetchWithIntegerPropertyOnRealAffinityColumn() throws {
|
|
let dbQueue = try makeDatabaseQueue()
|
|
try dbQueue.inDatabase { db in
|
|
try db.execute(sql: "CREATE TABLE t (value REAL)")
|
|
try db.execute(sql: "INSERT INTO t (value) VALUES (1)")
|
|
let record = try IntegerPropertyOnRealAffinityColumn.fetchOne(db, sql: "SELECT * FROM t")!
|
|
XCTAssertFalse(record.hasDatabaseChanges)
|
|
}
|
|
}
|
|
|
|
func testRecordIsNotEditedAfterWiderThanFullFetch() throws {
|
|
// Fetch a record from a row that contains all the columns in
|
|
// persistence container, plus extra ones: An update statement,
|
|
// which only saves the columns in persistence container would
|
|
// perform no change. So the record is not edited.
|
|
let dbQueue = try makeDatabaseQueue()
|
|
try dbQueue.inDatabase { db in
|
|
try Person(name: "Arthur", age: 41).insert(db)
|
|
let person = try Person.fetchOne(db, sql: "SELECT *, 1 AS foo FROM persons")!
|
|
XCTAssertFalse(person.hasDatabaseChanges)
|
|
}
|
|
try dbQueue.inDatabase { db in
|
|
try PersonWithModifiedCaseColumns(name: "Arthur", age: 41).insert(db)
|
|
let person = try PersonWithModifiedCaseColumns.fetchOne(db, sql: "SELECT *, 1 AS foo FROM persons")!
|
|
XCTAssertFalse(person.hasDatabaseChanges)
|
|
}
|
|
}
|
|
|
|
func testRecordIsEditedAfterPartialFetch() throws {
|
|
// Fetch a record from a row that does not contain all the columns in
|
|
// persistence container: An update statement saves the columns in
|
|
// persistence container, so it may perform unpredictable change.
|
|
// So the record is edited.
|
|
let dbQueue = try makeDatabaseQueue()
|
|
try dbQueue.inDatabase { db in
|
|
try Person(name: "Arthur", age: 41).insert(db)
|
|
let person = try Person.fetchOne(db, sql: "SELECT name FROM persons")!
|
|
XCTAssertTrue(person.hasDatabaseChanges)
|
|
}
|
|
try dbQueue.inDatabase { db in
|
|
try PersonWithModifiedCaseColumns(name: "Arthur", age: 41).insert(db)
|
|
let person = try PersonWithModifiedCaseColumns.fetchOne(db, sql: "SELECT name FROM persons")!
|
|
XCTAssertTrue(person.hasDatabaseChanges)
|
|
}
|
|
}
|
|
|
|
func testRecordIsNotEditedAfterInsert() throws {
|
|
// After insertion, a record is not edited.
|
|
let dbQueue = try makeDatabaseQueue()
|
|
try dbQueue.inDatabase { db in
|
|
let person = Person(name: "Arthur", age: 41)
|
|
try person.insert(db)
|
|
XCTAssertFalse(person.hasDatabaseChanges)
|
|
}
|
|
try dbQueue.inDatabase { db in
|
|
let person = PersonWithModifiedCaseColumns(name: "Arthur", age: 41)
|
|
try person.insert(db)
|
|
XCTAssertFalse(person.hasDatabaseChanges)
|
|
}
|
|
}
|
|
|
|
func testRecordIsEditedAfterValueChange() throws {
|
|
// Any change in a value exposed in persistence container yields a
|
|
// record that is edited.
|
|
let dbQueue = try makeDatabaseQueue()
|
|
try dbQueue.inDatabase { db in
|
|
do {
|
|
let person = Person(name: "Arthur")
|
|
try person.insert(db)
|
|
XCTAssertTrue(person.name != nil)
|
|
person.name = "Bobby" // non-nil vs. non-nil
|
|
XCTAssertTrue(person.hasDatabaseChanges)
|
|
}
|
|
do {
|
|
let person = Person(name: "Arthur")
|
|
try person.insert(db)
|
|
XCTAssertTrue(person.name != nil)
|
|
person.name = nil // non-nil vs. nil
|
|
XCTAssertTrue(person.hasDatabaseChanges)
|
|
}
|
|
do {
|
|
let person = Person(name: "Arthur")
|
|
try person.insert(db)
|
|
XCTAssertTrue(person.age == nil)
|
|
person.age = 41 // nil vs. non-nil
|
|
XCTAssertTrue(person.hasDatabaseChanges)
|
|
}
|
|
}
|
|
try dbQueue.inDatabase { db in
|
|
do {
|
|
let person = PersonWithModifiedCaseColumns(name: "Arthur")
|
|
try person.insert(db)
|
|
XCTAssertTrue(person.name != nil)
|
|
person.name = "Bobby" // non-nil vs. non-nil
|
|
XCTAssertTrue(person.hasDatabaseChanges)
|
|
}
|
|
do {
|
|
let person = PersonWithModifiedCaseColumns(name: "Arthur")
|
|
try person.insert(db)
|
|
XCTAssertTrue(person.name != nil)
|
|
person.name = nil // non-nil vs. nil
|
|
XCTAssertTrue(person.hasDatabaseChanges)
|
|
}
|
|
do {
|
|
let person = PersonWithModifiedCaseColumns(name: "Arthur")
|
|
try person.insert(db)
|
|
XCTAssertTrue(person.age == nil)
|
|
person.age = 41 // nil vs. non-nil
|
|
XCTAssertTrue(person.hasDatabaseChanges)
|
|
}
|
|
}
|
|
}
|
|
|
|
func testRecordIsNotEditedAfterSameValueChange() throws {
|
|
let dbQueue = try makeDatabaseQueue()
|
|
try dbQueue.inDatabase { db in
|
|
do {
|
|
let person = Person(name: "Arthur")
|
|
try person.insert(db)
|
|
XCTAssertTrue(person.name != nil)
|
|
person.name = "Arthur" // non-nil vs. non-nil
|
|
XCTAssertFalse(person.hasDatabaseChanges)
|
|
}
|
|
do {
|
|
let person = Person(name: "Arthur")
|
|
try person.insert(db)
|
|
XCTAssertTrue(person.age == nil)
|
|
person.age = nil // nil vs. nil
|
|
XCTAssertFalse(person.hasDatabaseChanges)
|
|
}
|
|
}
|
|
try dbQueue.inDatabase { db in
|
|
do {
|
|
let person = PersonWithModifiedCaseColumns(name: "Arthur")
|
|
try person.insert(db)
|
|
XCTAssertTrue(person.name != nil)
|
|
person.name = "Arthur" // non-nil vs. non-nil
|
|
XCTAssertFalse(person.hasDatabaseChanges)
|
|
}
|
|
do {
|
|
let person = PersonWithModifiedCaseColumns(name: "Arthur")
|
|
try person.insert(db)
|
|
XCTAssertTrue(person.age == nil)
|
|
person.age = nil // nil vs. nil
|
|
XCTAssertFalse(person.hasDatabaseChanges)
|
|
}
|
|
}
|
|
}
|
|
|
|
func testRecordIsNotEditedAfterUpdate() throws {
|
|
// After update, a record is not edited.
|
|
let dbQueue = try makeDatabaseQueue()
|
|
try dbQueue.inDatabase { db in
|
|
let person = Person(name: "Arthur", age: 41)
|
|
try person.insert(db)
|
|
person.name = "Bobby"
|
|
try person.update(db)
|
|
XCTAssertFalse(person.hasDatabaseChanges)
|
|
}
|
|
try dbQueue.inDatabase { db in
|
|
let person = PersonWithModifiedCaseColumns(name: "Arthur", age: 41)
|
|
try person.insert(db)
|
|
person.name = "Bobby"
|
|
try person.update(db)
|
|
XCTAssertFalse(person.hasDatabaseChanges)
|
|
}
|
|
}
|
|
|
|
func testRecordIsNotEditedAfterSave() throws {
|
|
// After save, a record is not edited.
|
|
let dbQueue = try makeDatabaseQueue()
|
|
try dbQueue.inDatabase { db in
|
|
let person = Person(name: "Arthur", age: 41)
|
|
try person.save(db)
|
|
XCTAssertFalse(person.hasDatabaseChanges)
|
|
person.name = "Bobby"
|
|
XCTAssertTrue(person.hasDatabaseChanges)
|
|
try person.save(db)
|
|
XCTAssertFalse(person.hasDatabaseChanges)
|
|
}
|
|
try dbQueue.inDatabase { db in
|
|
let person = PersonWithModifiedCaseColumns(name: "Arthur", age: 41)
|
|
try person.save(db)
|
|
XCTAssertFalse(person.hasDatabaseChanges)
|
|
person.name = "Bobby"
|
|
XCTAssertTrue(person.hasDatabaseChanges)
|
|
try person.save(db)
|
|
XCTAssertFalse(person.hasDatabaseChanges)
|
|
}
|
|
}
|
|
|
|
func testRecordIsEditedAfterPrimaryKeyChange() throws {
|
|
// After reload, a record is not edited.
|
|
let dbQueue = try makeDatabaseQueue()
|
|
try dbQueue.inDatabase { db in
|
|
let person = Person(name: "Arthur", age: 41)
|
|
try person.insert(db)
|
|
person.id = person.id + 1
|
|
XCTAssertTrue(person.hasDatabaseChanges)
|
|
}
|
|
try dbQueue.inDatabase { db in
|
|
let person = PersonWithModifiedCaseColumns(name: "Arthur", age: 41)
|
|
try person.insert(db)
|
|
person.id = person.id + 1
|
|
XCTAssertTrue(person.hasDatabaseChanges)
|
|
}
|
|
}
|
|
|
|
func testChangesAfterInit() throws {
|
|
let person = Person(name: "Arthur", age: 41)
|
|
let changes = try person.databaseChanges
|
|
XCTAssertEqual(changes.count, 4)
|
|
for (column, old) in changes {
|
|
switch column {
|
|
case "id":
|
|
XCTAssertTrue(old == nil)
|
|
case "name":
|
|
XCTAssertTrue(old == nil)
|
|
case "age":
|
|
XCTAssertTrue(old == nil)
|
|
case "creationDate":
|
|
XCTAssertTrue(old == nil)
|
|
default:
|
|
XCTFail("Unexpected column: \(column)")
|
|
}
|
|
}
|
|
}
|
|
|
|
func testChangesAfterInitFromRow() throws {
|
|
let person = try Person(row: Row(["name": "Arthur", "age": 41]))
|
|
let changes = try person.databaseChanges
|
|
XCTAssertEqual(changes.count, 4)
|
|
for (column, old) in changes {
|
|
switch column {
|
|
case "id":
|
|
XCTAssertTrue(old == nil)
|
|
case "name":
|
|
XCTAssertTrue(old == nil)
|
|
case "age":
|
|
XCTAssertTrue(old == nil)
|
|
case "creationDate":
|
|
XCTAssertTrue(old == nil)
|
|
default:
|
|
XCTFail("Unexpected column: \(column)")
|
|
}
|
|
}
|
|
}
|
|
|
|
func testChangesAfterFullFetch() throws {
|
|
// Fetch a record from a row that contains all the columns in
|
|
// persistence container: An update statement, which only saves the
|
|
// columns in persistence container would perform no change. So the
|
|
// record is not edited.
|
|
let dbQueue = try makeDatabaseQueue()
|
|
try dbQueue.inDatabase { db in
|
|
try Person(name: "Arthur", age: 41).insert(db)
|
|
do {
|
|
let person = try Person.fetchOne(db, sql: "SELECT * FROM persons")!
|
|
let changes = try person.databaseChanges
|
|
XCTAssertEqual(changes.count, 0)
|
|
}
|
|
do {
|
|
let persons = try Person.fetchAll(db, sql: "SELECT * FROM persons")
|
|
let changes = try persons[0].databaseChanges
|
|
XCTAssertEqual(changes.count, 0)
|
|
}
|
|
do {
|
|
let persons = try Person.fetchCursor(db, sql: "SELECT * FROM persons")
|
|
let changes = try persons.next()!.databaseChanges
|
|
XCTAssertEqual(changes.count, 0)
|
|
}
|
|
do {
|
|
let person = try Person.fetchOne(db, sql: "SELECT * FROM persons", adapter: SuffixRowAdapter(fromIndex: 0))!
|
|
let changes = try person.databaseChanges
|
|
XCTAssertEqual(changes.count, 0)
|
|
}
|
|
}
|
|
try dbQueue.inDatabase { db in
|
|
try PersonWithModifiedCaseColumns(name: "Arthur", age: 41).insert(db)
|
|
let person = try PersonWithModifiedCaseColumns.fetchOne(db, sql: "SELECT * FROM persons")!
|
|
let changes = try person.databaseChanges
|
|
XCTAssertEqual(changes.count, 0)
|
|
}
|
|
}
|
|
|
|
func testChangesAfterPartialFetch() throws {
|
|
// Fetch a record from a row that does not contain all the columns in
|
|
// persistence container: An update statement saves the columns in
|
|
// persistence container, so it may perform unpredictable change.
|
|
// So the record is edited.
|
|
let dbQueue = try makeDatabaseQueue()
|
|
try dbQueue.inDatabase { db in
|
|
try Person(name: "Arthur", age: 41).insert(db)
|
|
let person = try Person.fetchOne(db, sql: "SELECT name FROM persons")!
|
|
let changes = try person.databaseChanges
|
|
XCTAssertEqual(changes.count, 3)
|
|
for (column, old) in changes {
|
|
switch column {
|
|
case "id":
|
|
XCTAssertTrue(old == nil)
|
|
case "age":
|
|
XCTAssertTrue(old == nil)
|
|
case "creationDate":
|
|
XCTAssertTrue(old == nil)
|
|
default:
|
|
XCTFail("Unexpected column: \(column)")
|
|
}
|
|
}
|
|
}
|
|
try dbQueue.inDatabase { db in
|
|
try PersonWithModifiedCaseColumns(name: "Arthur", age: 41).insert(db)
|
|
let person = try PersonWithModifiedCaseColumns.fetchOne(db, sql: "SELECT name FROM persons")!
|
|
let changes = try person.databaseChanges
|
|
XCTAssertEqual(changes.count, 3)
|
|
for (column, old) in changes {
|
|
switch column {
|
|
case "ID":
|
|
XCTAssertTrue(old == nil)
|
|
case "AGE":
|
|
XCTAssertTrue(old == nil)
|
|
case "CREATIONDATE":
|
|
XCTAssertTrue(old == nil)
|
|
default:
|
|
XCTFail("Unexpected column: \(column)")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func testChangesAfterInsert() throws {
|
|
// After insertion, a record is not edited.
|
|
let dbQueue = try makeDatabaseQueue()
|
|
try dbQueue.inDatabase { db in
|
|
let person = Person(name: "Arthur", age: 41)
|
|
try person.insert(db)
|
|
let changes = try person.databaseChanges
|
|
XCTAssertEqual(changes.count, 0)
|
|
}
|
|
try dbQueue.inDatabase { db in
|
|
let person = PersonWithModifiedCaseColumns(name: "Arthur", age: 41)
|
|
try person.insert(db)
|
|
let changes = try person.databaseChanges
|
|
XCTAssertEqual(changes.count, 0)
|
|
}
|
|
}
|
|
|
|
func testChangesAfterValueChange() throws {
|
|
// Any change in a value exposed in persistence container yields a
|
|
// record that is edited.
|
|
let dbQueue = try makeDatabaseQueue()
|
|
try dbQueue.inDatabase { db in
|
|
let person = Person(name: "Arthur", age: nil)
|
|
try person.insert(db)
|
|
|
|
person.name = "Bobby" // non-nil -> non-nil
|
|
person.age = 41 // nil -> non-nil
|
|
person.creationDate = nil // non-nil -> nil
|
|
let changes = try person.databaseChanges
|
|
XCTAssertEqual(changes.count, 3)
|
|
for (column, old) in changes {
|
|
switch column {
|
|
case "name":
|
|
XCTAssertEqual(old, "Arthur".databaseValue)
|
|
case "age":
|
|
XCTAssertEqual(old, DatabaseValue.null)
|
|
case "creationDate":
|
|
XCTAssertTrue(Date.fromDatabaseValue(old!) != nil)
|
|
default:
|
|
XCTFail("Unexpected column: \(column)")
|
|
}
|
|
}
|
|
}
|
|
try dbQueue.inDatabase { db in
|
|
let person = PersonWithModifiedCaseColumns(name: "Arthur", age: nil)
|
|
try person.insert(db)
|
|
|
|
person.name = "Bobby" // non-nil -> non-nil
|
|
person.age = 41 // nil -> non-nil
|
|
person.creationDate = nil // non-nil -> nil
|
|
let changes = try person.databaseChanges
|
|
XCTAssertEqual(changes.count, 3)
|
|
for (column, old) in changes {
|
|
switch column {
|
|
case "NAME":
|
|
XCTAssertEqual(old, "Arthur".databaseValue)
|
|
case "AGE":
|
|
XCTAssertEqual(old, DatabaseValue.null)
|
|
case "CREATIONDATE":
|
|
XCTAssertTrue(Date.fromDatabaseValue(old!) != nil)
|
|
default:
|
|
XCTFail("Unexpected column: \(column)")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func testChangesAfterUpdate() throws {
|
|
// After update, a record is not edited.
|
|
let dbQueue = try makeDatabaseQueue()
|
|
try dbQueue.inDatabase { db in
|
|
let person = Person(name: "Arthur", age: 41)
|
|
try person.insert(db)
|
|
person.name = "Bobby"
|
|
try person.update(db)
|
|
XCTAssertFalse(person.hasDatabaseChanges)
|
|
try XCTAssert(person.databaseChanges.isEmpty)
|
|
}
|
|
try dbQueue.inDatabase { db in
|
|
let person = PersonWithModifiedCaseColumns(name: "Arthur", age: 41)
|
|
try person.insert(db)
|
|
person.name = "Bobby"
|
|
try person.update(db)
|
|
XCTAssertFalse(person.hasDatabaseChanges)
|
|
try XCTAssert(person.databaseChanges.isEmpty)
|
|
}
|
|
}
|
|
|
|
func testChangesAfterSave() throws {
|
|
// After save, a record is not edited.
|
|
let dbQueue = try makeDatabaseQueue()
|
|
try dbQueue.inDatabase { db in
|
|
let person = Person(name: "Arthur", age: 41)
|
|
try person.save(db)
|
|
XCTAssertFalse(person.hasDatabaseChanges)
|
|
try XCTAssert(person.databaseChanges.isEmpty)
|
|
|
|
person.name = "Bobby"
|
|
let changes = try person.databaseChanges
|
|
XCTAssertEqual(changes.count, 1)
|
|
for (column, old) in changes {
|
|
switch column {
|
|
case "name":
|
|
XCTAssertEqual(old, "Arthur".databaseValue)
|
|
default:
|
|
XCTFail("Unexpected column: \(column)")
|
|
}
|
|
}
|
|
try person.save(db)
|
|
XCTAssertFalse(person.hasDatabaseChanges)
|
|
try XCTAssert(person.databaseChanges.isEmpty)
|
|
}
|
|
try dbQueue.inDatabase { db in
|
|
let person = PersonWithModifiedCaseColumns(name: "Arthur", age: 41)
|
|
try person.save(db)
|
|
XCTAssertFalse(person.hasDatabaseChanges)
|
|
try XCTAssert(person.databaseChanges.isEmpty)
|
|
|
|
person.name = "Bobby"
|
|
let changes = try person.databaseChanges
|
|
XCTAssertEqual(changes.count, 1)
|
|
for (column, old) in changes {
|
|
switch column {
|
|
case "NAME":
|
|
XCTAssertEqual(old, "Arthur".databaseValue)
|
|
default:
|
|
XCTFail("Unexpected column: \(column)")
|
|
}
|
|
}
|
|
try person.save(db)
|
|
XCTAssertFalse(person.hasDatabaseChanges)
|
|
try XCTAssert(person.databaseChanges.isEmpty)
|
|
}
|
|
}
|
|
|
|
func testChangesAfterPrimaryKeyChange() throws {
|
|
// After reload, a record is not edited.
|
|
let dbQueue = try makeDatabaseQueue()
|
|
try dbQueue.inDatabase { db in
|
|
let person = Person(name: "Arthur", age: 41)
|
|
try person.insert(db)
|
|
person.id = person.id + 1
|
|
let changes = try person.databaseChanges
|
|
XCTAssertEqual(changes.count, 1)
|
|
for (column, old) in changes {
|
|
switch column {
|
|
case "id":
|
|
XCTAssertEqual(old, (person.id - 1).databaseValue)
|
|
default:
|
|
XCTFail("Unexpected column: \(column)")
|
|
}
|
|
}
|
|
}
|
|
try dbQueue.inDatabase { db in
|
|
let person = PersonWithModifiedCaseColumns(name: "Arthur", age: 41)
|
|
try person.insert(db)
|
|
person.id = person.id + 1
|
|
let changes = try person.databaseChanges
|
|
XCTAssertEqual(changes.count, 1)
|
|
for (column, old) in changes {
|
|
switch column {
|
|
case "ID":
|
|
XCTAssertEqual(old, (person.id - 1).databaseValue)
|
|
default:
|
|
XCTFail("Unexpected column: \(column)")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func testUpdateChanges() throws {
|
|
let dbQueue = try makeDatabaseQueue()
|
|
try dbQueue.inDatabase { db in
|
|
let person = Person(name: "Arthur", age: 41)
|
|
|
|
do {
|
|
XCTAssertTrue(person.hasDatabaseChanges)
|
|
try person.updateChanges(db)
|
|
XCTFail("Expected RecordError")
|
|
} catch RecordError.recordNotFound(databaseTableName: "persons", key: ["id": .null]) { }
|
|
|
|
try person.insert(db)
|
|
|
|
// Nothing to update
|
|
let initialChangesCount = db.totalChangesCount
|
|
XCTAssertFalse(person.hasDatabaseChanges)
|
|
try XCTAssertFalse(person.updateChanges(db))
|
|
XCTAssertEqual(db.totalChangesCount, initialChangesCount)
|
|
|
|
// Nothing to update
|
|
person.age = 41
|
|
XCTAssertFalse(person.hasDatabaseChanges)
|
|
try XCTAssertFalse(person.updateChanges(db))
|
|
XCTAssertEqual(db.totalChangesCount, initialChangesCount)
|
|
|
|
// Update single column
|
|
person.age = 42
|
|
try XCTAssertEqual(Set(person.databaseChanges.keys), ["age"])
|
|
try XCTAssertTrue(person.updateChanges(db))
|
|
XCTAssertEqual(db.totalChangesCount, initialChangesCount + 1)
|
|
XCTAssertEqual(lastSQLQuery, "UPDATE \"persons\" SET \"age\"=42 WHERE \"id\"=1")
|
|
|
|
// Update two columns
|
|
person.name = "Barbara"
|
|
person.age = 43
|
|
try XCTAssertEqual(Set(person.databaseChanges.keys), ["age", "name"])
|
|
try XCTAssertTrue(person.updateChanges(db))
|
|
XCTAssertEqual(db.totalChangesCount, initialChangesCount + 2)
|
|
let fetchedPerson = try Person.fetchOne(db, key: person.id)
|
|
XCTAssertEqual(fetchedPerson?.name, person.name)
|
|
XCTAssertEqual(fetchedPerson?.age, person.age)
|
|
}
|
|
}
|
|
}
|