Check vault updates in IssuerFlowTest

Modify issuer flow test to verify the consumed/produced states, rather than just checking the transaction matches the value returned via the flow state machine. This is both a simpler and more relevant test.
This commit is contained in:
Ross Nicoll 2017-07-11 15:30:21 +01:00 committed by GitHub
parent 7caee508ec
commit d6d5edc33b
3 changed files with 124 additions and 48 deletions

View File

@ -5,7 +5,13 @@ import net.corda.core.contracts.DOLLARS
import net.corda.core.contracts.`issued by`
import net.corda.core.getOrThrow
import net.corda.core.identity.Party
import net.corda.core.node.services.Vault
import net.corda.core.node.services.trackBy
import net.corda.core.node.services.vault.QueryCriteria
import net.corda.core.utilities.OpaqueBytes
import net.corda.node.utilities.transaction
import net.corda.testing.expect
import net.corda.testing.expectEvents
import net.corda.testing.node.InMemoryMessagingNetwork.ServicePeerAllocationStrategy.RoundRobin
import net.corda.testing.node.MockNetwork
import net.corda.testing.node.MockNetwork.MockNode
@ -51,15 +57,39 @@ class CashPaymentFlowTests {
val payTo = notaryNode.info.legalIdentity
val expectedPayment = 500.DOLLARS
val expectedChange = 1500.DOLLARS
val future = bankOfCordaNode.services.startFlow(CashPaymentFlow(expectedPayment,
payTo)).resultFuture
mockNet.runNetwork()
val (paymentTx, receipient) = future.getOrThrow()
val states = paymentTx.tx.outputs.map { it.data }.filterIsInstance<Cash.State>()
val paymentState: Cash.State = states.single { it.owner == receipient }
val changeState: Cash.State = states.single { it != paymentState }
assertEquals(expectedChange.`issued by`(bankOfCorda.ref(ref)), changeState.amount)
assertEquals(expectedPayment.`issued by`(bankOfCorda.ref(ref)), paymentState.amount)
bankOfCordaNode.database.transaction {
// Register for vault updates
val criteria = QueryCriteria.VaultQueryCriteria(status = Vault.StateStatus.ALL)
val (_, vaultUpdatesBoc) = bankOfCordaNode.services.vaultQueryService.trackBy<Cash.State>(criteria)
val (_, vaultUpdatesBankClient) = notaryNode.services.vaultQueryService.trackBy<Cash.State>(criteria)
val future = bankOfCordaNode.services.startFlow(CashPaymentFlow(expectedPayment,
payTo)).resultFuture
mockNet.runNetwork()
future.getOrThrow()
// Check Bank of Corda vault updates - we take in some issued cash and split it into $500 to the notary
// and $1,500 back to us, so we expect to consume one state, produce one state for our own vault
vaultUpdatesBoc.expectEvents {
expect { update ->
require(update.consumed.size == 1) { "Expected 1 consumed states, actual: $update" }
require(update.produced.size == 1) { "Expected 1 produced states, actual: $update" }
val changeState = update.produced.single().state.data as Cash.State
assertEquals(expectedChange.`issued by`(bankOfCorda.ref(ref)), changeState.amount)
}
}
// Check notary node vault updates
vaultUpdatesBankClient.expectEvents {
expect { update ->
require(update.consumed.isEmpty()) { update.consumed.size }
require(update.produced.size == 1) { update.produced.size }
val paymentState = update.produced.single().state.data as Cash.State
assertEquals(expectedPayment.`issued by`(bankOfCorda.ref(ref)), paymentState.amount)
}
}
}
}
@Test

View File

@ -1,30 +1,28 @@
package net.corda.flows
import com.google.common.util.concurrent.ListenableFuture
import net.corda.testing.contracts.calculateRandomlySizedAmounts
import net.corda.contracts.asset.Cash
import net.corda.core.contracts.Amount
import net.corda.core.contracts.DOLLARS
import net.corda.core.contracts.currency
import net.corda.core.flows.FlowException
import net.corda.core.internal.FlowStateMachine
import net.corda.core.getOrThrow
import net.corda.core.identity.Party
import net.corda.core.map
import net.corda.core.utilities.OpaqueBytes
import net.corda.core.toFuture
import net.corda.core.node.services.Vault
import net.corda.core.node.services.trackBy
import net.corda.core.node.services.vault.QueryCriteria
import net.corda.core.transactions.SignedTransaction
import net.corda.testing.DUMMY_NOTARY
import net.corda.core.utilities.OpaqueBytes
import net.corda.flows.IssuerFlow.IssuanceRequester
import net.corda.testing.BOC
import net.corda.testing.MEGA_CORP
import net.corda.node.utilities.transaction
import net.corda.testing.*
import net.corda.testing.contracts.calculateRandomlySizedAmounts
import net.corda.testing.node.MockNetwork
import net.corda.testing.node.MockNetwork.MockNode
import org.junit.After
import org.junit.Before
import org.junit.Test
import rx.Observable
import java.util.*
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
class IssuerFlowTest {
@ -39,11 +37,6 @@ class IssuerFlowTest {
notaryNode = mockNet.createNotaryNode(null, DUMMY_NOTARY.name)
bankOfCordaNode = mockNet.createPartyNode(notaryNode.network.myAddress, BOC.name)
bankClientNode = mockNet.createPartyNode(notaryNode.network.myAddress, MEGA_CORP.name)
val nodes = listOf(notaryNode, bankOfCordaNode, bankClientNode)
nodes.forEach { node ->
nodes.map { it.info.legalIdentityAndCert }.forEach(node.services.identityService::registerIdentity)
}
}
@After
@ -53,24 +46,82 @@ class IssuerFlowTest {
@Test
fun `test issuer flow`() {
// using default IssueTo Party Reference
val (issuer, issuerResult) = runIssuerAndIssueRequester(bankOfCordaNode, bankClientNode, 1000000.DOLLARS,
bankClientNode.info.legalIdentity, OpaqueBytes.of(123))
assertEquals(issuerResult.get().stx, issuer.get().resultFuture.get())
val (vaultUpdatesBoc, vaultUpdatesBankClient) = bankOfCordaNode.database.transaction {
// Register for vault updates
val criteria = QueryCriteria.VaultQueryCriteria(status = Vault.StateStatus.ALL)
val (_, vaultUpdatesBoc) = bankOfCordaNode.services.vaultQueryService.trackBy<Cash.State>(criteria)
val (_, vaultUpdatesBankClient) = bankClientNode.services.vaultQueryService.trackBy<Cash.State>(criteria)
// using default IssueTo Party Reference
val issuerResult = runIssuerAndIssueRequester(bankOfCordaNode, bankClientNode, 1000000.DOLLARS,
bankClientNode.info.legalIdentity, OpaqueBytes.of(123))
issuerResult.get()
Pair(vaultUpdatesBoc, vaultUpdatesBankClient)
}
// Check Bank of Corda Vault Updates
vaultUpdatesBoc.expectEvents {
sequence(
// ISSUE
expect { update ->
require(update.consumed.isEmpty()) { "Expected 0 consumed states, actual: $update" }
require(update.produced.size == 1) { "Expected 1 produced states, actual: $update" }
val issued = update.produced.single().state.data as Cash.State
require(issued.owner == bankOfCordaNode.info.legalIdentity)
require(issued.owner != bankClientNode.info.legalIdentity)
},
// MOVE
expect { update ->
require(update.consumed.size == 1) { "Expected 1 consumed states, actual: $update" }
require(update.produced.isEmpty()) { "Expected 0 produced states, actual: $update" }
}
)
}
// Check Bank Client Vault Updates
vaultUpdatesBankClient.expectEvents {
// MOVE
expect { update ->
require(update.consumed.isEmpty()) { update.consumed.size }
require(update.produced.size == 1) { update.produced.size }
val paidState = update.produced.single().state.data as Cash.State
require(paidState.owner == bankClientNode.info.legalIdentity)
}
}
}
@Test
fun `test issuer flow rejects restricted`() {
// try to issue an amount of a restricted currency
assertFailsWith<FlowException> {
runIssuerAndIssueRequester(bankOfCordaNode, bankClientNode, Amount(100000L, currency("BRL")),
bankClientNode.info.legalIdentity, OpaqueBytes.of(123)).issueRequestResult.getOrThrow()
bankClientNode.info.legalIdentity, OpaqueBytes.of(123)).getOrThrow()
}
}
@Test
fun `test issue flow to self`() {
// using default IssueTo Party Reference
val (issuer, issuerResult) = runIssuerAndIssueRequester(bankOfCordaNode, bankOfCordaNode, 1000000.DOLLARS,
bankOfCordaNode.info.legalIdentity, OpaqueBytes.of(123))
assertEquals(issuerResult.get().stx, issuer.get().resultFuture.get())
val vaultUpdatesBoc = bankOfCordaNode.database.transaction {
val criteria = QueryCriteria.VaultQueryCriteria(status = Vault.StateStatus.ALL)
val (_, vaultUpdatesBoc) = bankOfCordaNode.services.vaultQueryService.trackBy<Cash.State>(criteria)
// using default IssueTo Party Reference
runIssuerAndIssueRequester(bankOfCordaNode, bankOfCordaNode, 1000000.DOLLARS,
bankOfCordaNode.info.legalIdentity, OpaqueBytes.of(123)).getOrThrow()
vaultUpdatesBoc
}
// Check Bank of Corda Vault Updates
vaultUpdatesBoc.expectEvents {
sequence(
// ISSUE
expect { update ->
require(update.consumed.isEmpty()) { "Expected 0 consumed states, actual: $update" }
require(update.produced.size == 1) { "Expected 1 produced states, actual: $update" }
}
)
}
}
@Test
@ -83,7 +134,7 @@ class IssuerFlowTest {
bankClientNode.info.legalIdentity, OpaqueBytes.of(123))
}
handles.forEach {
require(it.issueRequestResult.get().stx is SignedTransaction)
require(it.get().stx is SignedTransaction)
}
}
@ -91,20 +142,10 @@ class IssuerFlowTest {
issueToNode: MockNode,
amount: Amount<Currency>,
party: Party,
ref: OpaqueBytes): RunResult {
ref: OpaqueBytes): ListenableFuture<AbstractCashFlow.Result> {
val issueToPartyAndRef = party.ref(ref)
val issuerFlows: Observable<IssuerFlow.Issuer> = issuerNode.registerInitiatedFlow(IssuerFlow.Issuer::class.java)
val firstIssuerFiber = issuerFlows.toFuture().map { it.stateMachine }
val issueRequest = IssuanceRequester(amount, party, issueToPartyAndRef.reference, issuerNode.info.legalIdentity,
anonymous = false)
val issueRequestResultFuture = issueToNode.services.startFlow(issueRequest).resultFuture
return IssuerFlowTest.RunResult(firstIssuerFiber, issueRequestResultFuture)
return issueToNode.services.startFlow(issueRequest).resultFuture
}
private data class RunResult(
val issuer: ListenableFuture<FlowStateMachine<*>>,
val issueRequestResult: ListenableFuture<AbstractCashFlow.Result>
)
}

View File

@ -1,10 +1,14 @@
package net.corda.bank
import com.google.common.util.concurrent.Futures
import net.corda.contracts.asset.Cash
import net.corda.core.contracts.DOLLARS
import net.corda.core.getOrThrow
import net.corda.core.messaging.startFlow
import net.corda.core.node.services.ServiceInfo
import net.corda.core.node.services.Vault
import net.corda.core.node.services.trackBy
import net.corda.core.node.services.vault.QueryCriteria
import net.corda.flows.IssuerFlow.IssuanceRequester
import net.corda.testing.driver.driver
import net.corda.node.services.startFlowPermission
@ -33,10 +37,11 @@ class BankOfCordaRPCClientTest {
val bigCorpProxy = bigCorpClient.start("bigCorpCFO", "password2").proxy
// Register for Bank of Corda Vault updates
val vaultUpdatesBoc = bocProxy.vaultAndUpdates().second
val criteria = QueryCriteria.VaultQueryCriteria(status = Vault.StateStatus.ALL)
val (_, vaultUpdatesBoc) = bocProxy.vaultTrackByCriteria<Cash.State>(Cash.State::class.java, criteria)
// Register for Big Corporation Vault updates
val vaultUpdatesBigCorp = bigCorpProxy.vaultAndUpdates().second
val (_, vaultUpdatesBigCorp) = bigCorpProxy.vaultTrackByCriteria<Cash.State>(Cash.State::class.java, criteria)
// Kick-off actual Issuer Flow
// TODO: Update checks below to reflect states consumed/produced under anonymisation