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 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.messaging.startFlow
import net.corda.core.transactions.TransactionBuilder
import net.corda.core.utilities.OpaqueBytes import net.corda.core.utilities.OpaqueBytes
import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.getOrThrow
import net.corda.finance.DOLLARS 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.contracts.getCashBalance
import net.corda.finance.issuedBy
import net.corda.testing.core.singleIdentity
import net.corda.testing.driver.DriverParameters import net.corda.testing.driver.DriverParameters
import net.corda.testing.driver.driver import net.corda.testing.driver.driver
import net.corda.testing.driver.internal.InProcessImpl import net.corda.testing.driver.internal.InProcessImpl
@ -34,4 +42,97 @@ class CashSelectionTest {
assertThat(availableBalanceAfterExit).isEqualTo(issuedAmount - exitedAmount) 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 { fun createSession(): Connection {
// We need to set the database for the current [Thread] or [Fiber] here as some tests share threads across databases. // We need to set the database for the current [Thread] or [Fiber] here as some tests share threads across databases.
_contextDatabase.set(this) _contextDatabase.set(this)
currentDBSession().flush()
return contextTransaction.connection return contextTransaction.connection
} }