CORDA-1548 Hibernate session not flushed before handing over raw JDBC session to user code (e.g. coin selection) (#3266)

* Hibernate session flushed before handing over raw JDBC session to user code + test - inserting and selecting cash in the same transaction
* Additional two tests copied from Enterprise repo
This commit is contained in:
szymonsztuka 2018-05-30 16:19:06 +01:00 committed by GitHub
parent 9418f9191e
commit ed70fea3a7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 102 additions and 0 deletions

View File

@ -1,10 +1,18 @@
package net.corda.finance.flows
import net.corda.core.contracts.TransactionState
import net.corda.core.contracts.withoutIssuer
import net.corda.core.identity.Party
import net.corda.core.messaging.startFlow
import net.corda.core.transactions.TransactionBuilder
import net.corda.core.utilities.OpaqueBytes
import net.corda.core.utilities.getOrThrow
import net.corda.finance.DOLLARS
import net.corda.finance.contracts.asset.Cash
import net.corda.finance.contracts.asset.cash.selection.AbstractCashSelection
import net.corda.finance.contracts.getCashBalance
import net.corda.finance.issuedBy
import net.corda.testing.core.singleIdentity
import net.corda.testing.driver.DriverParameters
import net.corda.testing.driver.driver
import net.corda.testing.driver.internal.InProcessImpl
@ -34,4 +42,97 @@ class CashSelectionTest {
assertThat(availableBalanceAfterExit).isEqualTo(issuedAmount - exitedAmount)
}
}
@Test
fun `cash selection sees states added in the same transaction`() {
driver(DriverParameters(startNodesInProcess = true, extraCordappPackagesToScan = listOf("net.corda.finance"))) {
val node = startNode().getOrThrow() as InProcessImpl
val nodeIdentity = node.services.myInfo.singleIdentity()
val issuer = nodeIdentity.ref(1)
val coin = 1.DOLLARS.issuedBy(issuer)
val exitedAmount = 1.DOLLARS
val issuance = TransactionBuilder(null as Party?)
issuance.addOutputState(TransactionState(Cash.State(coin, nodeIdentity), Cash.PROGRAM_ID, defaultNotaryIdentity))
issuance.addCommand(Cash.Commands.Issue(), nodeIdentity.owningKey)
//insert ans select in the same transaction
val exitStates = node.database.transaction {
val transaction = node.services.signInitialTransaction(issuance, nodeIdentity.owningKey)
node.services.recordTransactions(transaction)
val builder = TransactionBuilder(notary = null)
AbstractCashSelection
.getInstance { node.services.jdbcSession().metaData }
.unconsumedCashStatesForSpending(node.services, exitedAmount, setOf(issuer.party), builder.notary, builder.lockId, setOf(issuer.reference))
}
val returnedCoinsNumber = 1
assertThat(exitStates.size).isEqualTo(returnedCoinsNumber)
}
}
@Test
fun `dont return extra coins if the selected amount has been reached`() {
driver(DriverParameters(startNodesInProcess = true, extraCordappPackagesToScan = listOf("net.corda.finance"))) {
val node = startNode().getOrThrow() as InProcessImpl
val nodeIdentity = node.services.myInfo.singleIdentity()
val issuer = nodeIdentity.ref(1)
val exitStates = node.database.transaction {
//issue $1 coin twice
repeat(2, {
val coin = 1.DOLLARS.issuedBy(issuer)
val issuance = TransactionBuilder(null as Party?)
issuance.addOutputState(TransactionState(Cash.State(coin, nodeIdentity), Cash.PROGRAM_ID, defaultNotaryIdentity))
issuance.addCommand(Cash.Commands.Issue(), nodeIdentity.owningKey)
val transaction = node.services.signInitialTransaction(issuance, nodeIdentity.owningKey)
node.services.recordTransactions(transaction)
})
val exitedAmount = 1.DOLLARS
val builder = TransactionBuilder(notary = null)
AbstractCashSelection
.getInstance { node.services.jdbcSession().metaData }
.unconsumedCashStatesForSpending(node.services, exitedAmount, setOf(issuer.party), builder.notary, builder.lockId, setOf(issuer.reference))
}
val returnedCoinsNumber = 1
assertThat(exitStates.size).isEqualTo(returnedCoinsNumber)
}
}
@Test
fun `select cash states issued by single transaction and give change`() {
driver(DriverParameters(startNodesInProcess = true, extraCordappPackagesToScan = listOf("net.corda.finance"))) {
val node = startNode().getOrThrow() as InProcessImpl
val nodeIdentity = node.services.myInfo.singleIdentity()
val coins = listOf(3.DOLLARS, 2.DOLLARS, 1.DOLLARS).map { it.issuedBy(nodeIdentity.ref(1)) }
//create single transaction with 3 cash outputs
val issuance = TransactionBuilder(null as Party?)
coins.map { issuance.addOutputState(TransactionState(Cash.State(it, nodeIdentity), "net.corda.finance.contracts.asset.Cash", defaultNotaryIdentity)) }
issuance.addCommand(Cash.Commands.Issue(), nodeIdentity.owningKey)
val transaction = node.services.signInitialTransaction(issuance, nodeIdentity.owningKey)
node.database.transaction {
node.services.recordTransactions(transaction)
}
val issuedAmount = coins.reduce { sum, element -> sum + element }.withoutIssuer()
val availableBalance = node.rpc.getCashBalance(issuedAmount.token)
assertThat(availableBalance).isEqualTo(issuedAmount)
val exitedAmount = 3.01.DOLLARS
node.rpc.startFlow(::CashExitFlow, exitedAmount, OpaqueBytes.of(1)).returnValue.getOrThrow()
val availableBalanceAfterExit = node.rpc.getCashBalance(issuedAmount.token)
assertThat(availableBalanceAfterExit).isEqualTo(issuedAmount - exitedAmount)
}
}
}

View File

@ -105,6 +105,7 @@ class CordaPersistence(
fun createSession(): Connection {
// We need to set the database for the current [Thread] or [Fiber] here as some tests share threads across databases.
_contextDatabase.set(this)
currentDBSession().flush()
return contextTransaction.connection
}