Add CI/CD configuration and API documentation
This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
import XCTest
|
||||
import GRDB
|
||||
|
||||
class DatabasePoolCrashTests: GRDBCrashTestCase {
|
||||
|
||||
func testReaderCanNotStartTransaction() {
|
||||
assertCrash("DatabasePool readers can not start transactions or savepoints.") {
|
||||
try dbPool.read { db in
|
||||
let statement = try db.makeStatement(sql: "BEGIN TRANSACTION")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testReaderCanNotStartSavepoint() {
|
||||
assertCrash("DatabasePool readers can not start transactions or savepoints.") {
|
||||
try dbPool.read { db in
|
||||
let statement = try db.makeStatement(sql: "SAVEPOINT foo")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
import XCTest
|
||||
import GRDB
|
||||
|
||||
class DatabaseQueueCrashTests: GRDBCrashTestCase {
|
||||
|
||||
|
||||
// =========================================================================
|
||||
// MARK: - Reentrancy
|
||||
|
||||
func testInDatabaseIsNotReentrant() {
|
||||
assertCrash("Database methods are not reentrant.") {
|
||||
dbQueue.inDatabase { db in
|
||||
dbQueue.inDatabase { db in
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testInTransactionInsideInDatabaseIsNotReentrant() {
|
||||
assertCrash("Database methods are not reentrant.") {
|
||||
try dbQueue.inDatabase { db in
|
||||
try dbQueue.inTransaction { db in
|
||||
return .commit
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testInTransactionIsNotReentrant() {
|
||||
assertCrash("Database methods are not reentrant.") {
|
||||
try dbQueue.inTransaction { db in
|
||||
try dbQueue.inTransaction { db in
|
||||
return .commit
|
||||
}
|
||||
return .commit
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// =========================================================================
|
||||
// MARK: - Sequence iteration in wrong queue
|
||||
|
||||
func testRowSequenceCanNotBeGeneratedOutsideOfDatabaseQueue() {
|
||||
assertCrash("Database was not used on the correct thread: execute your statements inside DatabaseQueue.inDatabase() or DatabaseQueue.inTransaction(). If you get this error while iterating the result of a fetch() method, consider using the array returned by fetchAll() instead.") {
|
||||
var rows: DatabaseSequence<Row>?
|
||||
try dbQueue.inDatabase { db in
|
||||
try db.execute(sql: "CREATE TABLE persons (name TEXT)")
|
||||
rows = try Row.fetch(db, "SELECT * FROM persons")
|
||||
}
|
||||
_ = rows!.makeIterator()
|
||||
}
|
||||
}
|
||||
|
||||
func testRowSequenceCanNotBeIteratedOutsideOfDatabaseQueue() {
|
||||
assertCrash("Database was not used on the correct thread: execute your statements inside DatabaseQueue.inDatabase() or DatabaseQueue.inTransaction(). If you get this error while iterating the result of a fetch() method, consider using the array returned by fetchAll() instead.") {
|
||||
var iterator: DatabaseIterator<Row>?
|
||||
try dbQueue.inDatabase { db in
|
||||
try db.execute(sql: "CREATE TABLE persons (name TEXT)")
|
||||
iterator = try Row.fetch(db, "SELECT * FROM persons").makeIterator()
|
||||
}
|
||||
_ = iterator!.next()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// =========================================================================
|
||||
// MARK: - Concurrency
|
||||
|
||||
func testReaderCrashDuringExclusiveTransaction() {
|
||||
assertCrash("SQLite error 5 with statement `SELECT * FROM stuffs`: database is locked") {
|
||||
let dbQueue1 = try! makeDatabaseQueue(path: dbQueuepath, configuration: dbConfiguration)
|
||||
let dbQueue2 = try! makeDatabaseQueue(path: dbQueuepath, configuration: dbConfiguration)
|
||||
|
||||
try! dbQueue1.inDatabase { db in
|
||||
try db.execute(sql: "CREATE TABLE stuffs (id INTEGER PRIMARY KEY)")
|
||||
}
|
||||
|
||||
let queue = NSOperationQueue()
|
||||
queue.maxConcurrentOperationCount = 2
|
||||
queue.addOperation(NSBlockOperation {
|
||||
do {
|
||||
try dbQueue1.inTransaction(.exclusive) { db in
|
||||
sleep(2) // let other queue try to read.
|
||||
return .commit
|
||||
}
|
||||
}
|
||||
catch is DatabaseError {
|
||||
}
|
||||
catch {
|
||||
XCTFail("\(error)")
|
||||
}
|
||||
})
|
||||
|
||||
queue.addOperation(NSBlockOperation {
|
||||
dbQueue2.inDatabase { db in
|
||||
sleep(1) // let other queue open transaction
|
||||
_ = try Row.fetch(db, "SELECT * FROM stuffs") // Crash expected
|
||||
}
|
||||
})
|
||||
|
||||
queue.waitUntilAllOperationsAreFinished()
|
||||
}
|
||||
}
|
||||
}
|
||||
+87
@@ -0,0 +1,87 @@
|
||||
import XCTest
|
||||
import GRDB
|
||||
|
||||
// A type that adopts DatabaseValueConvertible but does not adopt StatementColumnConvertible
|
||||
private struct IntConvertible: DatabaseValueConvertible {
|
||||
let int: Int
|
||||
init(int: Int) {
|
||||
self.int = int
|
||||
}
|
||||
var databaseValue: DatabaseValue { int.databaseValue }
|
||||
static func fromDatabaseValue(_ dbValue: DatabaseValue) -> IntConvertible? {
|
||||
guard let int = Int.fromDatabaseValue(dbValue) else {
|
||||
return nil
|
||||
}
|
||||
return IntConvertible(int: int)
|
||||
}
|
||||
}
|
||||
|
||||
class DatabaseValueConvertibleCrashTests: GRDBCrashTestCase {
|
||||
|
||||
func testCrashFetchDatabaseValueConvertibleFromStatement() {
|
||||
assertCrash("could not convert NULL to IntConvertible.") {
|
||||
try dbQueue.inDatabase { db in
|
||||
try db.execute(sql: "CREATE TABLE ints (int Int)")
|
||||
try db.execute(sql: "INSERT INTO ints (int) VALUES (1)")
|
||||
try db.execute(sql: "INSERT INTO ints (int) VALUES (NULL)")
|
||||
|
||||
let statement = try db.makeStatement(sql: "SELECT int FROM ints ORDER BY int")
|
||||
let sequence = IntConvertible.fetch(statement)
|
||||
for _ in sequence { }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testCrashFetchAllDatabaseValueConvertibleFromStatement() {
|
||||
assertCrash("could not convert NULL to IntConvertible.") {
|
||||
try dbQueue.inDatabase { db in
|
||||
try db.execute(sql: "CREATE TABLE ints (int Int)")
|
||||
try db.execute(sql: "INSERT INTO ints (int) VALUES (1)")
|
||||
try db.execute(sql: "INSERT INTO ints (int) VALUES (NULL)")
|
||||
|
||||
let statement = try db.makeStatement(sql: "SELECT int FROM ints ORDER BY int")
|
||||
_ = IntConvertible.fetchAll(statement)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testCrashFetchDatabaseValueConvertibleFromDatabase() {
|
||||
assertCrash("could not convert NULL to IntConvertible.") {
|
||||
try dbQueue.inDatabase { db in
|
||||
try db.execute(sql: "CREATE TABLE ints (int Int)")
|
||||
try db.execute(sql: "INSERT INTO ints (int) VALUES (1)")
|
||||
try db.execute(sql: "INSERT INTO ints (int) VALUES (NULL)")
|
||||
|
||||
let sequence = IntConvertible.fetch(db, "SELECT int FROM ints ORDER BY int")
|
||||
for _ in sequence { }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testCrashFetchAllDatabaseValueConvertibleFromDatabase() {
|
||||
assertCrash("could not convert NULL to IntConvertible.") {
|
||||
try dbQueue.inDatabase { db in
|
||||
try db.execute(sql: "CREATE TABLE ints (int Int)")
|
||||
try db.execute(sql: "INSERT INTO ints (int) VALUES (1)")
|
||||
try db.execute(sql: "INSERT INTO ints (int) VALUES (NULL)")
|
||||
|
||||
_ = IntConvertible.fetchAll(db, sql: "SELECT int FROM ints ORDER BY int")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testCrashDatabaseValueConvertibleInvalidConversionFromNULL() {
|
||||
assertCrash("could not convert NULL to IntConvertible.") {
|
||||
let row = Row(["int": nil])
|
||||
_ = row["int"] as IntConvertible
|
||||
}
|
||||
}
|
||||
|
||||
func testCrashDatabaseValueConvertibleInvalidConversionFromInvalidType() {
|
||||
assertCrash("could not convert \"foo\" to IntConvertible") {
|
||||
let row = Row(["int": "foo"])
|
||||
_ = row["int"] as IntConvertible
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import XCTest
|
||||
|
||||
class GRDBCrashTestCase: GRDBTestCase {
|
||||
|
||||
// This method does not actually catch any crash.
|
||||
// But it expresses an intent :-)
|
||||
func assertCrash(message: String, @noescape block: () throws -> ()) {
|
||||
do {
|
||||
try block()
|
||||
XCTFail("Crash expected: \(message)")
|
||||
} catch {
|
||||
XCTFail("Unexpected error: \(error)")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import GRDB
|
||||
|
||||
class MigrationCrashTests: GRDBCrashTestCase {
|
||||
|
||||
func testMigrationNamesMustBeUnique() {
|
||||
assertCrash("already registered migration: \"foo\"") {
|
||||
var migrator = DatabaseMigrator()
|
||||
migrator.registerMigration("foo") { db in }
|
||||
migrator.registerMigration("foo") { db in }
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,324 @@
|
||||
import XCTest
|
||||
import GRDB
|
||||
|
||||
private class RecordWithoutDatabaseTableName: Record { }
|
||||
|
||||
private class RecordWithInexistingDatabaseTable: Record {
|
||||
override class var databaseTableName: String {
|
||||
"foo"
|
||||
}
|
||||
}
|
||||
|
||||
private class RecordWithEmptyPersistentDictionary : Record {
|
||||
override class var databaseTableName: String {
|
||||
"records"
|
||||
}
|
||||
}
|
||||
|
||||
private class RecordWithNilPrimaryKey : Record {
|
||||
override class var databaseTableName: String {
|
||||
"records"
|
||||
}
|
||||
|
||||
override func encode(to container: inout PersistenceContainer) throws {
|
||||
container["id"] = nil
|
||||
}
|
||||
}
|
||||
|
||||
private class RecordForTableWithoutPrimaryKey : Record {
|
||||
override class var databaseTableName: String {
|
||||
"records"
|
||||
}
|
||||
|
||||
override func encode(to container: inout PersistenceContainer) throws {
|
||||
container["name"] = "foo"
|
||||
}
|
||||
}
|
||||
|
||||
private class RecordForTableWithMultipleColumnsPrimaryKey : Record {
|
||||
override class var databaseTableName: String {
|
||||
"records"
|
||||
}
|
||||
|
||||
override func encode(to container: inout PersistenceContainer) throws {
|
||||
container["name"] = "foo"
|
||||
}
|
||||
}
|
||||
|
||||
private class RecordWithRowIDPrimaryKeyNotExposedInPersistentDictionary : Record {
|
||||
override class var databaseTableName: String {
|
||||
"records"
|
||||
}
|
||||
|
||||
override func encode(to container: inout PersistenceContainer) throws {
|
||||
container["name"] = "foo"
|
||||
}
|
||||
}
|
||||
|
||||
class RecordCrashTests: GRDBCrashTestCase {
|
||||
|
||||
// =========================================================================
|
||||
// MARK: - RecordWithoutDatabaseTableName
|
||||
|
||||
func testRecordWithoutDatabaseTableNameCanNotBeFetchedByID() {
|
||||
assertCrash("subclass must override") {
|
||||
dbQueue.inDatabase { db in
|
||||
_ = RecordWithoutDatabaseTableName.fetchOne(db, key: 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testRecordWithoutDatabaseTableNameCanNotBeFetchedByKey() {
|
||||
assertCrash("subclass must override") {
|
||||
dbQueue.inDatabase { db in
|
||||
_ = RecordWithoutDatabaseTableName.fetchOne(db, key: ["foo": "bar"])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testRecordWithoutDatabaseTableNameCanNotBeInserted() {
|
||||
assertCrash("subclass must override") {
|
||||
try dbQueue.inDatabase { db in
|
||||
try RecordWithoutDatabaseTableName().insert(db)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testRecordWithoutDatabaseTableNameCanNotBeUpdated() {
|
||||
assertCrash("subclass must override") {
|
||||
try dbQueue.inDatabase { db in
|
||||
try RecordWithoutDatabaseTableName().update(db)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testRecordWithoutDatabaseTableNameCanNotBeSaved() {
|
||||
assertCrash("subclass must override") {
|
||||
try dbQueue.inDatabase { db in
|
||||
try RecordWithoutDatabaseTableName().save(db)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testRecordWithoutDatabaseTableNameCanNotBeDeleted() {
|
||||
assertCrash("subclass must override") {
|
||||
try dbQueue.inDatabase { db in
|
||||
try RecordWithoutDatabaseTableName().delete(db)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testRecordWithoutDatabaseTableNameCanNotBeTestedForExistence() {
|
||||
assertCrash("subclass must override") {
|
||||
dbQueue.inDatabase { db in
|
||||
RecordWithoutDatabaseTableName().exists(db)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// =========================================================================
|
||||
// MARK: - RecordWithInexistingDatabaseTable
|
||||
|
||||
func testRecordWithInexistingDatabaseTableCanNotBeFetchedByID() {
|
||||
assertCrash("no such table: foo") {
|
||||
dbQueue.inDatabase { db in
|
||||
_ = RecordWithInexistingDatabaseTable.fetchOne(db, key: 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testRecordWithInexistingDatabaseTableCanNotBeFetchedByKey() {
|
||||
assertCrash("SQLite error 1 with statement `SELECT * FROM \"foo\" WHERE (\"id\" = ?)`: no such table: foo") {
|
||||
dbQueue.inDatabase { db in
|
||||
_ = RecordWithInexistingDatabaseTable.fetchOne(db, key: ["id": 1])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testRecordWithInexistingDatabaseTableCanNotBeInserted() {
|
||||
assertCrash("no such table: foo") {
|
||||
try dbQueue.inDatabase { db in
|
||||
try RecordWithInexistingDatabaseTable().insert(db)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testRecordWithInexistingDatabaseTableCanNotBeUpdated() {
|
||||
assertCrash("no such table: foo") {
|
||||
try dbQueue.inDatabase { db in
|
||||
try RecordWithInexistingDatabaseTable().update(db)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testRecordWithInexistingDatabaseTableCanNotBeSaved() {
|
||||
assertCrash("no such table: foo") {
|
||||
try dbQueue.inDatabase { db in
|
||||
try RecordWithInexistingDatabaseTable().save(db)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testRecordWithInexistingDatabaseTableCanNotBeDeleted() {
|
||||
assertCrash("no such table: foo") {
|
||||
try dbQueue.inDatabase { db in
|
||||
try RecordWithInexistingDatabaseTable().delete(db)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testRecordWithInexistingDatabaseTableCanNotBeTestedForExistence() {
|
||||
assertCrash("no such table: foo") {
|
||||
dbQueue.inDatabase { db in
|
||||
RecordWithInexistingDatabaseTable().exists(db)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// =========================================================================
|
||||
// MARK: - RecordWithEmptyPersistentDictionary
|
||||
|
||||
func testRecordWithEmptyPersistentDictionaryCanNotBeInserted() {
|
||||
assertCrash("RecordWithEmptyPersistentDictionary.persistentDictionary: invalid empty dictionary") {
|
||||
try dbQueue.inDatabase { db in
|
||||
try db.execute(sql: "CREATE TABLE records (id INTEGER PRIMARY KEY)")
|
||||
try RecordWithEmptyPersistentDictionary().insert(db)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testRecordWithEmptyPersistentDictionaryCanNotBeUpdated() {
|
||||
assertCrash("RecordWithEmptyPersistentDictionary.persistentDictionary: invalid empty dictionary") {
|
||||
try dbQueue.inDatabase { db in
|
||||
try db.execute(sql: "CREATE TABLE records (id INTEGER PRIMARY KEY)")
|
||||
try RecordWithEmptyPersistentDictionary().update(db)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testRecordWithEmptyPersistentDictionaryCanNotBeSaved() {
|
||||
assertCrash("RecordWithEmptyPersistentDictionary.persistentDictionary: invalid empty dictionary") {
|
||||
try dbQueue.inDatabase { db in
|
||||
try db.execute(sql: "CREATE TABLE records (id INTEGER PRIMARY KEY)")
|
||||
try RecordWithEmptyPersistentDictionary().save(db)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testRecordWithEmptyPersistentDictionaryCanNotBeDeleted() {
|
||||
assertCrash("RecordWithEmptyPersistentDictionary.persistentDictionary: invalid empty dictionary") {
|
||||
try dbQueue.inDatabase { db in
|
||||
try db.execute(sql: "CREATE TABLE records (id INTEGER PRIMARY KEY)")
|
||||
try RecordWithEmptyPersistentDictionary().delete(db)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testRecordWithEmptyPersistentDictionaryCanNotBeTestedForExistence() {
|
||||
assertCrash("RecordWithEmptyPersistentDictionary.persistentDictionary: invalid empty dictionary") {
|
||||
try dbQueue.inDatabase { db in
|
||||
try db.execute(sql: "CREATE TABLE records (id INTEGER PRIMARY KEY)")
|
||||
RecordWithEmptyPersistentDictionary().exists(db)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// =========================================================================
|
||||
// MARK: - RecordWithNilPrimaryKey
|
||||
|
||||
func testRecordWithNilPrimaryKeyCanNotBeUpdated() {
|
||||
assertCrash("invalid primary key in <RecordWithNilPrimaryKey id:nil>") {
|
||||
try dbQueue.inDatabase { db in
|
||||
try db.execute(sql: "CREATE TABLE records (id INTEGER PRIMARY KEY)")
|
||||
try RecordWithNilPrimaryKey().update(db)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testRecordWithNilPrimaryKeyCanNotBeDeleted() {
|
||||
assertCrash("invalid primary key in <RecordWithNilPrimaryKey id:nil>") {
|
||||
try dbQueue.inDatabase { db in
|
||||
try db.execute(sql: "CREATE TABLE records (id INTEGER PRIMARY KEY)")
|
||||
try RecordWithNilPrimaryKey().delete(db)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testRecordWithNilPrimaryKeyCanNotBeTestedForExistence() {
|
||||
assertCrash("invalid primary key in <RecordWithNilPrimaryKey id:nil>") {
|
||||
try dbQueue.inDatabase { db in
|
||||
try db.execute(sql: "CREATE TABLE records (id INTEGER PRIMARY KEY)")
|
||||
RecordWithNilPrimaryKey().exists(db)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// =========================================================================
|
||||
// MARK: - RecordForTableWithoutPrimaryKey
|
||||
|
||||
func testRecordForTableWithoutPrimaryKeyCanNotBeFetchedByID() {
|
||||
assertCrash("expected single column primary key in table: records") {
|
||||
try dbQueue.inDatabase { db in
|
||||
try db.execute(sql: "CREATE TABLE records (name TEXT)")
|
||||
_ = RecordForTableWithoutPrimaryKey.fetchOne(db, key: 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testRecordForTableWithoutPrimaryKeyCanNotBeUpdated() {
|
||||
assertCrash("invalid primary key in <RecordForTableWithoutPrimaryKey name:\"foo\">") {
|
||||
try dbQueue.inDatabase { db in
|
||||
try db.execute(sql: "CREATE TABLE records (name TEXT)")
|
||||
try RecordForTableWithoutPrimaryKey().update(db)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testRecordForTableWithoutPrimaryKeyCanNotBeDeleted() {
|
||||
assertCrash("invalid primary key in <RecordForTableWithoutPrimaryKey name:\"foo\">") {
|
||||
try dbQueue.inDatabase { db in
|
||||
try db.execute(sql: "CREATE TABLE records (name TEXT)")
|
||||
try RecordForTableWithoutPrimaryKey().delete(db)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testRecordForTableWithoutPrimaryKeyCanNotBeTestedForExistence() {
|
||||
assertCrash("invalid primary key in <RecordForTableWithoutPrimaryKey name:\"foo\">") {
|
||||
try dbQueue.inDatabase { db in
|
||||
try db.execute(sql: "CREATE TABLE records (name TEXT)")
|
||||
RecordForTableWithoutPrimaryKey().exists(db)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// =========================================================================
|
||||
// MARK: - RecordForTableWithMultipleColumnsPrimaryKey
|
||||
|
||||
func testRecordForTableWithMultipleColumnsPrimaryKeyCanNotBeFetchedByID() {
|
||||
assertCrash("expected single column primary key in table: records") {
|
||||
try dbQueue.inDatabase { db in
|
||||
try db.execute(sql: "CREATE TABLE records (a TEXT, b TEXT, PRIMARY KEY(a,b))")
|
||||
_ = RecordForTableWithMultipleColumnsPrimaryKey.fetchOne(db, key: 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// =========================================================================
|
||||
// MARK: - RecordWithRowIDPrimaryKeyNotExposedInPersistentDictionary
|
||||
|
||||
func testRecordWithRowIDPrimaryKeyNotExposedInPersistentDictionaryCanNotBeInserted() {
|
||||
assertCrash("invalid primary key in <RecordWithRowIDPrimaryKeyNotExposedInPersistentDictionary name:\"foo\">") {
|
||||
try dbQueue.inDatabase { db in
|
||||
try db.execute(sql: "CREATE TABLE records (id INTEGER PRIMARY KEY, name TEXT)")
|
||||
try RecordWithRowIDPrimaryKeyNotExposedInPersistentDictionary().update(db)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
import XCTest
|
||||
import GRDB
|
||||
|
||||
class StatementColumnConvertibleCrashTests: GRDBCrashTestCase {
|
||||
|
||||
func testCrashFetchStatementColumnConvertibleFromStatement() {
|
||||
assertCrash("could not convert NULL to Int.") {
|
||||
try dbQueue.inDatabase { db in
|
||||
try db.execute(sql: "CREATE TABLE ints (int Int)")
|
||||
try db.execute(sql: "INSERT INTO ints (int) VALUES (1)")
|
||||
try db.execute(sql: "INSERT INTO ints (int) VALUES (NULL)")
|
||||
|
||||
let statement = try db.makeStatement(sql: "SELECT int FROM ints ORDER BY int")
|
||||
let sequence = try Int.fetch(statement)
|
||||
for _ in sequence { }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testCrashFetchAllStatementColumnConvertibleFromStatement() {
|
||||
assertCrash("could not convert NULL to Int.") {
|
||||
try dbQueue.inDatabase { db in
|
||||
try db.execute(sql: "CREATE TABLE ints (int Int)")
|
||||
try db.execute(sql: "INSERT INTO ints (int) VALUES (1)")
|
||||
try db.execute(sql: "INSERT INTO ints (int) VALUES (NULL)")
|
||||
|
||||
let statement = try db.makeStatement(sql: "SELECT int FROM ints ORDER BY int")
|
||||
_ = try Int.fetchAll(statement)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testCrashFetchStatementColumnConvertibleFromDatabase() {
|
||||
assertCrash("could not convert NULL to Int.") {
|
||||
try dbQueue.inDatabase { db in
|
||||
try db.execute(sql: "CREATE TABLE ints (int Int)")
|
||||
try db.execute(sql: "INSERT INTO ints (int) VALUES (1)")
|
||||
try db.execute(sql: "INSERT INTO ints (int) VALUES (NULL)")
|
||||
|
||||
let sequence = try Int.fetch(db, "SELECT int FROM ints ORDER BY int")
|
||||
for _ in sequence { }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testCrashFetchAllStatementColumnConvertibleFromDatabase() {
|
||||
assertCrash("could not convert NULL to Int.") {
|
||||
try dbQueue.inDatabase { db in
|
||||
try db.execute(sql: "CREATE TABLE ints (int Int)")
|
||||
try db.execute(sql: "INSERT INTO ints (int) VALUES (1)")
|
||||
try db.execute(sql: "INSERT INTO ints (int) VALUES (NULL)")
|
||||
|
||||
_ = try Int.fetchAll(db, sql: "SELECT int FROM ints ORDER BY int")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
import XCTest
|
||||
import GRDB
|
||||
|
||||
class StatementCrashTests: GRDBCrashTestCase {
|
||||
|
||||
func testInvalidStatementArguments() {
|
||||
assertCrash("SQLite error 1 with statement `INSERT INTO persons (name, age) VALUES (:name, :age)`: missing statement argument(s): age") {
|
||||
try dbQueue.inDatabase { db in
|
||||
try db.execute(sql: "CREATE TABLE persons (name TEXT, age INT)")
|
||||
try! db.execute(sql: "INSERT INTO persons (name, age) VALUES (:name, :age)", arguments: ["name": "Arthur"])
|
||||
}
|
||||
}
|
||||
|
||||
assertCrash("SQLite error 21 with statement `INSERT INTO persons (name, age) VALUES ('Arthur', ?);`: wrong number of statement arguments: 0") {
|
||||
try dbQueue.inDatabase { db in
|
||||
try db.execute(sql: "CREATE TABLE persons (name TEXT, age INT)")
|
||||
try db.execute(sql: """
|
||||
INSERT INTO persons (name, age) VALUES ('Arthur', ?);
|
||||
INSERT INTO persons (name, age) VALUES ('Barbara', ?);
|
||||
""")
|
||||
}
|
||||
}
|
||||
|
||||
assertCrash("SQLite error 21 with statement `INSERT INTO persons (name, age) VALUES ('Barbara', ?);`: wrong number of statement arguments: 0") {
|
||||
try dbQueue.inDatabase { db in
|
||||
try db.execute(sql: "CREATE TABLE persons (name TEXT, age INT)")
|
||||
try db.execute(sql: """
|
||||
INSERT INTO persons (name, age) VALUES ('Arthur', ?);
|
||||
INSERT INTO persons (name, age) VALUES ('Barbara', ?);
|
||||
""", arguments: [41])
|
||||
}
|
||||
}
|
||||
|
||||
assertCrash("SQLite error 21 with statement `INSERT INTO persons (name, age) VALUES ('Arthur', :age1);`: missing statement argument(s): age1") {
|
||||
try dbQueue.inDatabase { db in
|
||||
try db.execute(sql: "CREATE TABLE persons (name TEXT, age INT)")
|
||||
try db.execute(sql: """
|
||||
INSERT INTO persons (name, age) VALUES ('Arthur', :age1);
|
||||
INSERT INTO persons (name, age) VALUES ('Barbara', :age2);
|
||||
""")
|
||||
}
|
||||
}
|
||||
|
||||
assertCrash("SQLite error 21 with statement `INSERT INTO persons (name, age) VALUES ('Barbara', :age2);`: missing statement argument(s): age2") {
|
||||
try dbQueue.inDatabase { db in
|
||||
try db.execute(sql: "CREATE TABLE persons (name TEXT, age INT)")
|
||||
try db.execute(sql: """
|
||||
INSERT INTO persons (name, age) VALUES ('Arthur', :age1);
|
||||
INSERT INTO persons (name, age) VALUES ('Barbara', :age2);
|
||||
""", arguments: ["age1": 41])
|
||||
}
|
||||
}
|
||||
|
||||
assertCrash("SQLite error 21: wrong number of statement arguments: 3") {
|
||||
try dbQueue.inDatabase { db in
|
||||
try db.execute(sql: "CREATE TABLE persons (name TEXT, age INT)")
|
||||
try db.execute(sql: """
|
||||
INSERT INTO persons (name, age) VALUES ('Arthur', :age1);
|
||||
INSERT INTO persons (name, age) VALUES ('Arthur', :age2);
|
||||
""", arguments: [41, 32, 17])
|
||||
}
|
||||
}
|
||||
|
||||
assertCrash("SQLite error 21: wrong number of statement arguments: 3") {
|
||||
try dbQueue.inDatabase { db in
|
||||
try db.execute(sql: "CREATE TABLE persons (name TEXT, age INT)")
|
||||
try db.execute(sql: """
|
||||
INSERT INTO persons (name, age) VALUES ('Arthur', ?);
|
||||
INSERT INTO persons (name, age) VALUES ('Arthur', ?);
|
||||
""", arguments: [41, 32, 17])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user