Test DB rollback precedence in SQLite
This commit is contained in:
parent
04e80c384e
commit
f9bbdd56bf
@ -33,6 +33,7 @@ import net.sergeych.lyng.obj.ObjInstant
|
|||||||
import net.sergeych.lyng.obj.ObjMap
|
import net.sergeych.lyng.obj.ObjMap
|
||||||
import net.sergeych.lyng.obj.ObjNull
|
import net.sergeych.lyng.obj.ObjNull
|
||||||
import net.sergeych.lyng.obj.ObjString
|
import net.sergeych.lyng.obj.ObjString
|
||||||
|
import net.sergeych.lyng.obj.raiseAsExecutionError
|
||||||
import net.sergeych.lyng.obj.requiredArg
|
import net.sergeych.lyng.obj.requiredArg
|
||||||
import net.sergeych.lyng.requireScope
|
import net.sergeych.lyng.requireScope
|
||||||
import kotlinx.datetime.TimeZone
|
import kotlinx.datetime.TimeZone
|
||||||
@ -134,6 +135,56 @@ class LyngSqliteModuleTest {
|
|||||||
assertEquals(1L, count.value)
|
assertEquals(1L, count.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testRollbackExceptionRollsBackAndPropagates() = runTest {
|
||||||
|
val scope = Script.newScope()
|
||||||
|
withTempDb(scope) { db ->
|
||||||
|
db.invokeInstanceMethod(
|
||||||
|
scope,
|
||||||
|
"transaction",
|
||||||
|
ObjExternCallable.fromBridge {
|
||||||
|
val tx = requiredArg<Obj>(0)
|
||||||
|
tx.invokeInstanceMethod(
|
||||||
|
requireScope(),
|
||||||
|
"execute",
|
||||||
|
ObjString("create table items(id integer primary key autoincrement, name text not null)")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
val error = assertFailsWith<ExecutionError> {
|
||||||
|
db.invokeInstanceMethod(
|
||||||
|
scope,
|
||||||
|
"transaction",
|
||||||
|
ObjExternCallable.fromBridge {
|
||||||
|
val tx = requiredArg<Obj>(0)
|
||||||
|
tx.invokeInstanceMethod(
|
||||||
|
requireScope(),
|
||||||
|
"execute",
|
||||||
|
ObjString("insert into items(name) values(?)"),
|
||||||
|
ObjString("rolled-back")
|
||||||
|
)
|
||||||
|
rollbackException(requireScope(), "stop here").raiseAsExecutionError(requireScope())
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
assertEquals("RollbackException", error.errorObject.objClass.className)
|
||||||
|
|
||||||
|
val count = db.invokeInstanceMethod(
|
||||||
|
scope,
|
||||||
|
"transaction",
|
||||||
|
ObjExternCallable.fromBridge {
|
||||||
|
val tx = requiredArg<Obj>(0)
|
||||||
|
val resultSet = tx.invokeInstanceMethod(requireScope(), "select", ObjString("select count(*) as count from items"))
|
||||||
|
rowsOf(requireScope(), resultSet)[0].getAt(requireScope(), ObjString("count"))
|
||||||
|
}
|
||||||
|
) as ObjInt
|
||||||
|
|
||||||
|
assertEquals(0L, count.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testResultSetFailsAfterTransactionEnds() = runTest {
|
fun testResultSetFailsAfterTransactionEnds() = runTest {
|
||||||
val scope = Script.newScope()
|
val scope = Script.newScope()
|
||||||
@ -543,6 +594,65 @@ class LyngSqliteModuleTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testCommitFailureBecomesPrimaryAfterNormalCompletion() = runTest {
|
||||||
|
val scope = Script.newScope()
|
||||||
|
val db = openMemoryDb(scope)
|
||||||
|
|
||||||
|
val error = assertFailsWith<ExecutionError> {
|
||||||
|
db.invokeInstanceMethod(
|
||||||
|
scope,
|
||||||
|
"transaction",
|
||||||
|
ObjExternCallable.fromBridge {
|
||||||
|
val tx = requiredArg<Obj>(0)
|
||||||
|
tx.invokeInstanceMethod(requireScope(), "execute", ObjString("rollback"))
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
assertEquals("SqlExecutionException", error.errorObject.objClass.className)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testUserExceptionStaysPrimaryWhenRollbackFails() = runTest {
|
||||||
|
val scope = Script.newScope()
|
||||||
|
val db = openMemoryDb(scope)
|
||||||
|
|
||||||
|
val error = assertFailsWith<IllegalStateException> {
|
||||||
|
db.invokeInstanceMethod(
|
||||||
|
scope,
|
||||||
|
"transaction",
|
||||||
|
ObjExternCallable.fromBridge {
|
||||||
|
val tx = requiredArg<Obj>(0)
|
||||||
|
tx.invokeInstanceMethod(requireScope(), "execute", ObjString("rollback"))
|
||||||
|
throw IllegalStateException("boom")
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
assertEquals("boom", error.message)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testRollbackFailureBecomesPrimaryAfterRollbackException() = runTest {
|
||||||
|
val scope = Script.newScope()
|
||||||
|
val db = openMemoryDb(scope)
|
||||||
|
|
||||||
|
val error = assertFailsWith<ExecutionError> {
|
||||||
|
db.invokeInstanceMethod(
|
||||||
|
scope,
|
||||||
|
"transaction",
|
||||||
|
ObjExternCallable.fromBridge {
|
||||||
|
val tx = requiredArg<Obj>(0)
|
||||||
|
tx.invokeInstanceMethod(requireScope(), "execute", ObjString("rollback"))
|
||||||
|
rollbackException(requireScope(), "rollback requested").raiseAsExecutionError(requireScope())
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
assertEquals("SqlExecutionException", error.errorObject.objClass.className)
|
||||||
|
}
|
||||||
|
|
||||||
private suspend fun ModuleScope.callFn(name: String, vararg args: Obj): Obj {
|
private suspend fun ModuleScope.callFn(name: String, vararg args: Obj): Obj {
|
||||||
val callee = get(name)?.value ?: error("Missing $name in module")
|
val callee = get(name)?.value ?: error("Missing $name in module")
|
||||||
return callee.invoke(this, ObjNull, *args)
|
return callee.invoke(this, ObjNull, *args)
|
||||||
@ -587,6 +697,12 @@ class LyngSqliteModuleTest {
|
|||||||
return decimalClass.invokeInstanceMethod(scope, "fromString", ObjString(value))
|
return decimalClass.invokeInstanceMethod(scope, "fromString", ObjString(value))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private suspend fun rollbackException(scope: Scope, message: String): net.sergeych.lyng.obj.ObjException {
|
||||||
|
val dbModule = scope.currentImportProvider.createModuleScope(scope.pos, "lyng.io.db")
|
||||||
|
val rollbackClass = dbModule.requireClass("RollbackException")
|
||||||
|
return rollbackClass.invoke(scope, ObjNull, ObjString(message)) as net.sergeych.lyng.obj.ObjException
|
||||||
|
}
|
||||||
|
|
||||||
private suspend fun dateOf(scope: Scope, value: String): Obj {
|
private suspend fun dateOf(scope: Scope, value: String): Obj {
|
||||||
val timeModule = scope.currentImportProvider.createModuleScope(scope.pos, "lyng.time")
|
val timeModule = scope.currentImportProvider.createModuleScope(scope.pos, "lyng.time")
|
||||||
val dateClass = timeModule.requireClass("Date")
|
val dateClass = timeModule.requireClass("Date")
|
||||||
|
|||||||
@ -34,6 +34,7 @@ import net.sergeych.lyng.obj.ObjInstant
|
|||||||
import net.sergeych.lyng.obj.ObjMap
|
import net.sergeych.lyng.obj.ObjMap
|
||||||
import net.sergeych.lyng.obj.ObjNull
|
import net.sergeych.lyng.obj.ObjNull
|
||||||
import net.sergeych.lyng.obj.ObjString
|
import net.sergeych.lyng.obj.ObjString
|
||||||
|
import net.sergeych.lyng.obj.raiseAsExecutionError
|
||||||
import net.sergeych.lyng.obj.requiredArg
|
import net.sergeych.lyng.obj.requiredArg
|
||||||
import net.sergeych.lyng.requireScope
|
import net.sergeych.lyng.requireScope
|
||||||
import okio.FileSystem
|
import okio.FileSystem
|
||||||
@ -109,6 +110,56 @@ class LyngSqliteModuleNativeTest {
|
|||||||
assertEquals(1L, count.value)
|
assertEquals(1L, count.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testRollbackExceptionRollsBackAndPropagates() = runTest {
|
||||||
|
val scope = Script.newScope()
|
||||||
|
withTempDb(scope) { db ->
|
||||||
|
db.invokeInstanceMethod(
|
||||||
|
scope,
|
||||||
|
"transaction",
|
||||||
|
ObjExternCallable.fromBridge {
|
||||||
|
val tx = requiredArg<Obj>(0)
|
||||||
|
tx.invokeInstanceMethod(
|
||||||
|
requireScope(),
|
||||||
|
"execute",
|
||||||
|
ObjString("create table items(id integer primary key autoincrement, name text not null)")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
val error = assertFailsWith<ExecutionError> {
|
||||||
|
db.invokeInstanceMethod(
|
||||||
|
scope,
|
||||||
|
"transaction",
|
||||||
|
ObjExternCallable.fromBridge {
|
||||||
|
val tx = requiredArg<Obj>(0)
|
||||||
|
tx.invokeInstanceMethod(
|
||||||
|
requireScope(),
|
||||||
|
"execute",
|
||||||
|
ObjString("insert into items(name) values(?)"),
|
||||||
|
ObjString("rolled-back")
|
||||||
|
)
|
||||||
|
rollbackException(requireScope(), "stop here").raiseAsExecutionError(requireScope())
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
assertEquals("RollbackException", error.errorObject.objClass.className)
|
||||||
|
|
||||||
|
val count = db.invokeInstanceMethod(
|
||||||
|
scope,
|
||||||
|
"transaction",
|
||||||
|
ObjExternCallable.fromBridge {
|
||||||
|
val tx = requiredArg<Obj>(0)
|
||||||
|
val resultSet = tx.invokeInstanceMethod(requireScope(), "select", ObjString("select count(*) as count from items"))
|
||||||
|
rowsOf(requireScope(), resultSet)[0].getAt(requireScope(), ObjString("count"))
|
||||||
|
}
|
||||||
|
) as ObjInt
|
||||||
|
|
||||||
|
assertEquals(0L, count.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testResultSetFailsAfterTransactionEnds() = runTest {
|
fun testResultSetFailsAfterTransactionEnds() = runTest {
|
||||||
val scope = Script.newScope()
|
val scope = Script.newScope()
|
||||||
@ -438,6 +489,65 @@ class LyngSqliteModuleNativeTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testCommitFailureBecomesPrimaryAfterNormalCompletion() = runTest {
|
||||||
|
val scope = Script.newScope()
|
||||||
|
val db = openMemoryDb(scope)
|
||||||
|
|
||||||
|
val error = assertFailsWith<ExecutionError> {
|
||||||
|
db.invokeInstanceMethod(
|
||||||
|
scope,
|
||||||
|
"transaction",
|
||||||
|
ObjExternCallable.fromBridge {
|
||||||
|
val tx = requiredArg<Obj>(0)
|
||||||
|
tx.invokeInstanceMethod(requireScope(), "execute", ObjString("rollback"))
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
assertEquals("SqlExecutionException", error.errorObject.objClass.className)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testUserExceptionStaysPrimaryWhenRollbackFails() = runTest {
|
||||||
|
val scope = Script.newScope()
|
||||||
|
val db = openMemoryDb(scope)
|
||||||
|
|
||||||
|
val error = assertFailsWith<IllegalStateException> {
|
||||||
|
db.invokeInstanceMethod(
|
||||||
|
scope,
|
||||||
|
"transaction",
|
||||||
|
ObjExternCallable.fromBridge {
|
||||||
|
val tx = requiredArg<Obj>(0)
|
||||||
|
tx.invokeInstanceMethod(requireScope(), "execute", ObjString("rollback"))
|
||||||
|
throw IllegalStateException("boom")
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
assertEquals("boom", error.message)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testRollbackFailureBecomesPrimaryAfterRollbackException() = runTest {
|
||||||
|
val scope = Script.newScope()
|
||||||
|
val db = openMemoryDb(scope)
|
||||||
|
|
||||||
|
val error = assertFailsWith<ExecutionError> {
|
||||||
|
db.invokeInstanceMethod(
|
||||||
|
scope,
|
||||||
|
"transaction",
|
||||||
|
ObjExternCallable.fromBridge {
|
||||||
|
val tx = requiredArg<Obj>(0)
|
||||||
|
tx.invokeInstanceMethod(requireScope(), "execute", ObjString("rollback"))
|
||||||
|
rollbackException(requireScope(), "rollback requested").raiseAsExecutionError(requireScope())
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
assertEquals("SqlExecutionException", error.errorObject.objClass.className)
|
||||||
|
}
|
||||||
|
|
||||||
private suspend fun ModuleScope.callFn(name: String, vararg args: Obj): Obj {
|
private suspend fun ModuleScope.callFn(name: String, vararg args: Obj): Obj {
|
||||||
val callee = get(name)?.value ?: error("Missing $name in module")
|
val callee = get(name)?.value ?: error("Missing $name in module")
|
||||||
return callee.invoke(this, ObjNull, *args)
|
return callee.invoke(this, ObjNull, *args)
|
||||||
@ -488,6 +598,12 @@ class LyngSqliteModuleNativeTest {
|
|||||||
return decimalClass.invokeInstanceMethod(scope, "fromString", ObjString(value))
|
return decimalClass.invokeInstanceMethod(scope, "fromString", ObjString(value))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private suspend fun rollbackException(scope: Scope, message: String): net.sergeych.lyng.obj.ObjException {
|
||||||
|
val dbModule = scope.currentImportProvider.createModuleScope(scope.pos, "lyng.io.db")
|
||||||
|
val rollbackClass = dbModule.requireClass("RollbackException")
|
||||||
|
return rollbackClass.invoke(scope, ObjNull, ObjString(message)) as net.sergeych.lyng.obj.ObjException
|
||||||
|
}
|
||||||
|
|
||||||
private suspend fun dateOf(scope: Scope, value: String): Obj {
|
private suspend fun dateOf(scope: Scope, value: String): Obj {
|
||||||
val timeModule = scope.currentImportProvider.createModuleScope(scope.pos, "lyng.time")
|
val timeModule = scope.currentImportProvider.createModuleScope(scope.pos, "lyng.time")
|
||||||
val dateClass = timeModule.requireClass("Date")
|
val dateClass = timeModule.requireClass("Date")
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user