Made CashSelectionTest a unit test by using mock network. (#3570)

Also includes adding a default value of emptyList() to InternalMockNetwork.cordappPackages
This commit is contained in:
Shams Asari 2018-07-12 11:55:51 +01:00 committed by GitHub
parent a4355ce198
commit f4a248f81f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 149 additions and 152 deletions

View File

@ -15,7 +15,7 @@ class SwapIdentitiesFlowTests {
@Before @Before
fun setup() { fun setup() {
// We run this in parallel threads to help catch any race conditions that may exist. // We run this in parallel threads to help catch any race conditions that may exist.
mockNet = InternalMockNetwork(emptyList(), networkSendManuallyPumped = false, threadPerNode = true) mockNet = InternalMockNetwork(networkSendManuallyPumped = false, threadPerNode = true)
} }
@Test @Test

View File

@ -30,7 +30,7 @@ class AttachmentTests {
@Before @Before
fun setUp() { fun setUp() {
mockNet = InternalMockNetwork(emptyList()) mockNet = InternalMockNetwork()
} }
@After @After

View File

@ -13,7 +13,7 @@ import org.junit.After
import org.junit.Test import org.junit.Test
class ReceiveMultipleFlowTests { class ReceiveMultipleFlowTests {
private val mockNet = InternalMockNetwork(emptyList()) private val mockNet = InternalMockNetwork()
private val nodes = (0..2).map { mockNet.createPartyNode() } private val nodes = (0..2).map { mockNet.createPartyNode() }
@After @After
fun stopNodes() { fun stopNodes() {

View File

@ -72,7 +72,7 @@ class AttachmentSerializationTest {
@Before @Before
fun setUp() { fun setUp() {
mockNet = InternalMockNetwork(emptyList()) mockNet = InternalMockNetwork()
server = mockNet.createNode(InternalMockNodeParameters(legalName = ALICE_NAME)) server = mockNet.createNode(InternalMockNodeParameters(legalName = ALICE_NAME))
client = mockNet.createNode(InternalMockNodeParameters(legalName = BOB_NAME)) client = mockNet.createNode(InternalMockNodeParameters(legalName = BOB_NAME))
client.internals.disableDBCloseOnStop() // Otherwise the in-memory database may disappear (taking the checkpoint with it) while we reboot the client. client.internals.disableDBCloseOnStop() // Otherwise the in-memory database may disappear (taking the checkpoint with it) while we reboot the client.

View File

@ -1,138 +0,0 @@
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
import org.assertj.core.api.Assertions.assertThat
import org.junit.Test
class CashSelectionTest {
@Test
fun `unconsumed cash states`() {
driver(DriverParameters(startNodesInProcess = true, extraCordappPackagesToScan = listOf("net.corda.finance"))) {
val node = startNode().getOrThrow() as InProcessImpl
val issuerRef = OpaqueBytes.of(0)
val issuedAmount = 1000.DOLLARS
node.rpc.startFlow(::CashIssueFlow, issuedAmount, issuerRef, defaultNotaryIdentity).returnValue.getOrThrow()
val availableBalance = node.rpc.getCashBalance(issuedAmount.token)
assertThat(availableBalance).isEqualTo(issuedAmount)
val exitedAmount = 300.DOLLARS
node.rpc.startFlow(::CashExitFlow, exitedAmount, issuerRef).returnValue.getOrThrow()
val availableBalanceAfterExit = node.rpc.getCashBalance(issuedAmount.token)
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

@ -0,0 +1,136 @@
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.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.node.internal.InternalMockNetwork
import net.corda.testing.node.internal.startFlow
import org.assertj.core.api.Assertions.assertThat
import org.junit.After
import org.junit.Test
class CashSelectionTest {
private val mockNet = InternalMockNetwork(cordappPackages = listOf("net.corda.finance"), threadPerNode = true)
@After
fun cleanUp() {
mockNet.stopNodes()
}
@Test
fun `unconsumed cash states`() {
val issuerRef = OpaqueBytes.of(0)
val issuedAmount = 1000.DOLLARS
val node = mockNet.createNode()
node.services.startFlow(CashIssueFlow(issuedAmount, issuerRef, mockNet.defaultNotaryIdentity)).resultFuture.getOrThrow()
val availableBalance = node.services.getCashBalance(issuedAmount.token)
assertThat(availableBalance).isEqualTo(issuedAmount)
val exitedAmount = 300.DOLLARS
node.services.startFlow(CashExitFlow(exitedAmount, issuerRef)).resultFuture.getOrThrow()
val availableBalanceAfterExit = node.services.getCashBalance(issuedAmount.token)
assertThat(availableBalanceAfterExit).isEqualTo(issuedAmount - exitedAmount)
}
@Test
fun `cash selection sees states added in the same transaction`() {
val node = mockNet.createNode()
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, mockNet.defaultNotaryIdentity))
issuance.addCommand(Cash.Commands.Issue(), nodeIdentity.owningKey)
// Insert and 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 `don't return extra coins if the selected amount has been reached`() {
val node = mockNet.createNode()
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, mockNet.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`() {
val node = mockNet.createNode()
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.forEach {
issuance.addOutputState(TransactionState(Cash.State(it, nodeIdentity), "net.corda.finance.contracts.asset.Cash", mockNet.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.services.getCashBalance(issuedAmount.token)
assertThat(availableBalance).isEqualTo(issuedAmount)
val exitedAmount = 3.01.DOLLARS
node.services.startFlow(CashExitFlow(exitedAmount, OpaqueBytes.of(1))).resultFuture.getOrThrow()
val availableBalanceAfterExit = node.services.getCashBalance(issuedAmount.token)
assertThat(availableBalanceAfterExit).isEqualTo(issuedAmount - exitedAmount)
}
}

View File

@ -29,8 +29,7 @@ import kotlin.test.assertFails
class NetworkParametersTest { class NetworkParametersTest {
private val mockNet = InternalMockNetwork( private val mockNet = InternalMockNetwork(
emptyList(), defaultParameters = MockNetworkParameters(networkSendManuallyPumped = true),
MockNetworkParameters(networkSendManuallyPumped = true),
notarySpecs = listOf(MockNetworkNotarySpec(DUMMY_NOTARY_NAME))) notarySpecs = listOf(MockNetworkNotarySpec(DUMMY_NOTARY_NAME)))
@After @After

View File

@ -18,7 +18,7 @@ class InMemoryMessagingTests {
@Before @Before
fun setUp() { fun setUp() {
mockNet = InternalMockNetwork(emptyList()) mockNet = InternalMockNetwork()
} }
@After @After

View File

@ -35,7 +35,7 @@ class FinalityHandlerTest {
fun `sent to flow hospital on error and attempted retry on node restart`() { fun `sent to flow hospital on error and attempted retry on node restart`() {
// Setup a network where only Alice has the finance CorDapp and it sends a cash tx to Bob who doesn't have the // Setup a network where only Alice has the finance CorDapp and it sends a cash tx to Bob who doesn't have the
// CorDapp. Bob's FinalityHandler will error when validating the tx. // CorDapp. Bob's FinalityHandler will error when validating the tx.
mockNet = InternalMockNetwork(cordappPackages = emptyList()) mockNet = InternalMockNetwork()
val alice = mockNet.createNode(InternalMockNodeParameters( val alice = mockNet.createNode(InternalMockNodeParameters(
legalName = ALICE_NAME, legalName = ALICE_NAME,

View File

@ -18,7 +18,7 @@ import kotlin.test.assertNotNull
import kotlin.test.assertNull import kotlin.test.assertNull
class NetworkMapCacheTest { class NetworkMapCacheTest {
private val mockNet = InternalMockNetwork(emptyList()) private val mockNet = InternalMockNetwork()
@After @After
fun teardown() { fun teardown() {

View File

@ -40,7 +40,7 @@ class NodeSchemaServiceTest {
@Test @Test
fun `check node runs with minimal core schema set`() { fun `check node runs with minimal core schema set`() {
val mockNet = InternalMockNetwork(cordappPackages = emptyList()) val mockNet = InternalMockNetwork()
val mockNode = mockNet.createNode() val mockNode = mockNet.createNode()
val schemaService = mockNode.services.schemaService val schemaService = mockNode.services.schemaService
@ -52,7 +52,7 @@ class NodeSchemaServiceTest {
@Test @Test
fun `check node runs inclusive of notary node schema set`() { fun `check node runs inclusive of notary node schema set`() {
val mockNet = InternalMockNetwork(cordappPackages = emptyList()) val mockNet = InternalMockNetwork()
val mockNotaryNode = mockNet.notaryNodes.first() val mockNotaryNode = mockNet.notaryNodes.first()
val schemaService = mockNotaryNode.services.schemaService val schemaService = mockNotaryNode.services.schemaService

View File

@ -10,7 +10,7 @@ class InternalMockNetworkIntegrationTests {
companion object { companion object {
@JvmStatic @JvmStatic
fun main(args: Array<String>) { fun main(args: Array<String>) {
InternalMockNetwork(emptyList()).run { InternalMockNetwork().run {
repeat(2) { createNode() } repeat(2) { createNode() }
runNetwork() runNetwork()
stopNodes() stopNodes()

View File

@ -97,7 +97,7 @@ data class InternalMockNodeParameters(
) )
} }
open class InternalMockNetwork(private val cordappPackages: List<String>, open class InternalMockNetwork(private val cordappPackages: List<String> = emptyList(),
defaultParameters: MockNetworkParameters = MockNetworkParameters(), defaultParameters: MockNetworkParameters = MockNetworkParameters(),
val networkSendManuallyPumped: Boolean = defaultParameters.networkSendManuallyPumped, val networkSendManuallyPumped: Boolean = defaultParameters.networkSendManuallyPumped,
val threadPerNode: Boolean = defaultParameters.threadPerNode, val threadPerNode: Boolean = defaultParameters.threadPerNode,

View File

@ -9,7 +9,7 @@ class InternalMockNetworkTests {
fun `does not leak serialization env if init fails`() { fun `does not leak serialization env if init fails`() {
val e = Exception("didn't work") val e = Exception("didn't work")
assertThatThrownBy { assertThatThrownBy {
object : InternalMockNetwork(emptyList()) { object : InternalMockNetwork() {
override fun createNotaries() = throw e override fun createNotaries() = throw e
} }
}.isSameAs(e) }.isSameAs(e)