mirror of
https://github.com/corda/corda.git
synced 2025-06-17 14:48:16 +00:00
Add tests to confirm that if a Hibernate error occurs the transaction will fail and no states will be persisted
Add tests to NodeVaultServiceTest Add VaultFlowTest Add UniqueDummyFungibleContract and UniqueLinearContract to be used in the constraint tests
This commit is contained in:
@ -34,7 +34,7 @@ import net.corda.testing.contracts.DummyState
|
||||
import net.corda.testing.core.*
|
||||
import net.corda.testing.internal.LogHelper
|
||||
import net.corda.testing.internal.rigorousMock
|
||||
import net.corda.testing.internal.vault.VaultFiller
|
||||
import net.corda.testing.internal.vault.*
|
||||
import net.corda.testing.node.MockServices
|
||||
import net.corda.testing.node.makeTestIdentityService
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
@ -48,13 +48,15 @@ import java.math.BigDecimal
|
||||
import java.util.*
|
||||
import java.util.concurrent.CountDownLatch
|
||||
import java.util.concurrent.Executors
|
||||
import javax.persistence.*
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertFalse
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
class NodeVaultServiceTest {
|
||||
private companion object {
|
||||
val cordappPackages = listOf("net.corda.finance.contracts.asset", CashSchemaV1::class.packageName, "net.corda.testing.contracts")
|
||||
val cordappPackages = listOf("net.corda.finance.contracts.asset", CashSchemaV1::class.packageName, "net.corda.testing.contracts",
|
||||
"net.corda.testing.internal.vault")
|
||||
val dummyCashIssuer = TestIdentity(CordaX500Name("Snake Oil Issuer", "London", "GB"), 10)
|
||||
val DUMMY_CASH_ISSUER = dummyCashIssuer.ref(1)
|
||||
val bankOfCorda = TestIdentity(BOC_NAME)
|
||||
@ -769,4 +771,65 @@ class NodeVaultServiceTest {
|
||||
|
||||
// We should never see 2 or 7.
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Unique column constraint failing causes linear state to not persist to vault`() {
|
||||
fun createTx(): SignedTransaction {
|
||||
return services.signInitialTransaction(TransactionBuilder(DUMMY_NOTARY).apply {
|
||||
addOutputState(UniqueDummyLinearContract.State(listOf(megaCorp.party), "Dummy linear id"), UNIQUE_DUMMY_LINEAR_CONTRACT_PROGRAM_ID)
|
||||
addCommand(DummyCommandData, listOf(megaCorp.publicKey))
|
||||
})
|
||||
}
|
||||
|
||||
services.recordTransactions(StatesToRecord.ONLY_RELEVANT, listOf(createTx()))
|
||||
assertThatExceptionOfType(PersistenceException::class.java).isThrownBy {
|
||||
services.recordTransactions(StatesToRecord.ONLY_RELEVANT, listOf(createTx()))
|
||||
}
|
||||
assertEquals(1, database.transaction {
|
||||
vaultService.queryBy<UniqueDummyLinearContract.State>().states.size
|
||||
})
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Unique column constraint failing causes fungible state to not persist to vault`() {
|
||||
fun createTx(): SignedTransaction {
|
||||
return services.signInitialTransaction(TransactionBuilder(DUMMY_NOTARY).apply {
|
||||
addOutputState(UniqueDummyFungibleContract.State(10.DOLLARS `issued by` DUMMY_CASH_ISSUER, megaCorp.party), UNIQUE_DUMMY_FUNGIBLE_CONTRACT_PROGRAM_ID)
|
||||
addCommand(DummyCommandData, listOf(megaCorp.publicKey))
|
||||
})
|
||||
}
|
||||
|
||||
services.recordTransactions(StatesToRecord.ONLY_RELEVANT, listOf(createTx()))
|
||||
assertThatExceptionOfType(PersistenceException::class.java).isThrownBy {
|
||||
services.recordTransactions(StatesToRecord.ONLY_RELEVANT, listOf(createTx()))
|
||||
}
|
||||
assertEquals(1, database.transaction {
|
||||
vaultService.queryBy<UniqueDummyFungibleContract.State>().states.size
|
||||
})
|
||||
assertEquals(10.DOLLARS.quantity, database.transaction {
|
||||
vaultService.queryBy<UniqueDummyFungibleContract.State>().states.first().state.data.amount.quantity
|
||||
})
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Unique column constraint failing causes all states in transaction to fail`() {
|
||||
fun createTx(): SignedTransaction {
|
||||
return services.signInitialTransaction(TransactionBuilder(DUMMY_NOTARY).apply {
|
||||
addOutputState(UniqueDummyLinearContract.State(listOf(megaCorp.party), "Dummy linear id"), UNIQUE_DUMMY_LINEAR_CONTRACT_PROGRAM_ID)
|
||||
addOutputState(DummyDealContract.State(listOf(megaCorp.party), "Dummy linear id"), DUMMY_DEAL_PROGRAM_ID)
|
||||
addCommand(DummyCommandData, listOf(megaCorp.publicKey))
|
||||
})
|
||||
}
|
||||
|
||||
services.recordTransactions(StatesToRecord.ONLY_RELEVANT, listOf(createTx()))
|
||||
assertThatExceptionOfType(PersistenceException::class.java).isThrownBy {
|
||||
services.recordTransactions(StatesToRecord.ONLY_RELEVANT, listOf(createTx()))
|
||||
}
|
||||
assertEquals(1, database.transaction {
|
||||
vaultService.queryBy<UniqueDummyLinearContract.State>().states.size
|
||||
})
|
||||
assertEquals(1, database.transaction {
|
||||
vaultService.queryBy<DummyDealContract.State>().states.size
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,91 @@
|
||||
package net.corda.node.services.vault
|
||||
|
||||
import co.paralleluniverse.fibers.Suspendable
|
||||
import net.corda.core.flows.FinalityFlow
|
||||
import net.corda.core.flows.FlowLogic
|
||||
import net.corda.core.flows.InitiatingFlow
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.node.services.queryBy
|
||||
import net.corda.core.transactions.TransactionBuilder
|
||||
import net.corda.testing.core.DummyCommandData
|
||||
import net.corda.testing.core.singleIdentity
|
||||
import net.corda.testing.internal.vault.DUMMY_DEAL_PROGRAM_ID
|
||||
import net.corda.testing.internal.vault.DummyDealContract
|
||||
import net.corda.testing.internal.vault.UNIQUE_DUMMY_LINEAR_CONTRACT_PROGRAM_ID
|
||||
import net.corda.testing.internal.vault.UniqueDummyLinearContract
|
||||
import net.corda.testing.node.MockNetwork
|
||||
import net.corda.testing.node.MockNetworkNotarySpec
|
||||
import net.corda.testing.node.MockNodeParameters
|
||||
import net.corda.testing.node.StartedMockNode
|
||||
import org.assertj.core.api.Assertions
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import java.util.concurrent.ExecutionException
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
class VaultFlowTest {
|
||||
|
||||
private lateinit var mockNetwork: MockNetwork
|
||||
private lateinit var partyA: StartedMockNode
|
||||
private lateinit var partyB: StartedMockNode
|
||||
private lateinit var notaryNode: MockNetworkNotarySpec
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
notaryNode = MockNetworkNotarySpec(CordaX500Name("Notary", "London", "GB"))
|
||||
mockNetwork = MockNetwork(
|
||||
listOf(
|
||||
"net.corda.node.services.vault", "net.corda.testing.internal.vault"
|
||||
),
|
||||
notarySpecs = listOf(notaryNode),
|
||||
threadPerNode = true,
|
||||
networkSendManuallyPumped = false
|
||||
)
|
||||
partyA =
|
||||
mockNetwork.createNode(MockNodeParameters(legalName = CordaX500Name("PartyA", "Berlin", "DE")))
|
||||
|
||||
partyB =
|
||||
mockNetwork.createNode(MockNodeParameters(legalName = CordaX500Name("PartyB", "Berlin", "DE")))
|
||||
mockNetwork.startNodes()
|
||||
}
|
||||
|
||||
@After
|
||||
fun tearDown() {
|
||||
mockNetwork.stopNodes()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Unique column constraint failing causes states to not persist to vaults`() {
|
||||
partyA.startFlow(Initiator(listOf(partyA.info.singleIdentity(), partyB.info.singleIdentity()))).get()
|
||||
Assertions.assertThatExceptionOfType(ExecutionException::class.java).isThrownBy {
|
||||
partyA.startFlow(Initiator(listOf(partyA.info.singleIdentity(), partyB.info.singleIdentity()))).get()
|
||||
}
|
||||
assertEquals(1, partyA.transaction {
|
||||
partyA.services.vaultService.queryBy<UniqueDummyLinearContract.State>().states.size
|
||||
})
|
||||
assertEquals(1, partyB.transaction {
|
||||
partyB.services.vaultService.queryBy<UniqueDummyLinearContract.State>().states.size
|
||||
})
|
||||
assertEquals(1, partyA.transaction {
|
||||
partyA.services.vaultService.queryBy<DummyDealContract.State>().states.size
|
||||
})
|
||||
assertEquals(1, partyB.transaction {
|
||||
partyB.services.vaultService.queryBy<DummyDealContract.State>().states.size
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@InitiatingFlow
|
||||
class Initiator(private val participants: List<Party>) : FlowLogic<Unit>() {
|
||||
@Suspendable
|
||||
override fun call() {
|
||||
val stx = serviceHub.signInitialTransaction(TransactionBuilder(serviceHub.networkMapCache.notaryIdentities.first()).apply {
|
||||
addOutputState(UniqueDummyLinearContract.State(participants, "Dummy linear id"), UNIQUE_DUMMY_LINEAR_CONTRACT_PROGRAM_ID)
|
||||
addOutputState(DummyDealContract.State(participants, "linear id"), DUMMY_DEAL_PROGRAM_ID)
|
||||
addCommand(DummyCommandData, listOf(ourIdentity.owningKey))
|
||||
})
|
||||
subFlow(FinalityFlow(stx))
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user