From d6d5edc33bcf1da3f3b89bd5de88af92e1f712cf Mon Sep 17 00:00:00 2001 From: Ross Nicoll Date: Tue, 11 Jul 2017 15:30:21 +0100 Subject: [PATCH] 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. --- .../net/corda/flows/CashPaymentFlowTests.kt | 48 ++++++-- .../kotlin/net/corda/flows/IssuerFlowTest.kt | 115 ++++++++++++------ .../corda/bank/BankOfCordaRPCClientTest.kt | 9 +- 3 files changed, 124 insertions(+), 48 deletions(-) diff --git a/finance/src/test/kotlin/net/corda/flows/CashPaymentFlowTests.kt b/finance/src/test/kotlin/net/corda/flows/CashPaymentFlowTests.kt index 5e04b65b67..3bdf21b4da 100644 --- a/finance/src/test/kotlin/net/corda/flows/CashPaymentFlowTests.kt +++ b/finance/src/test/kotlin/net/corda/flows/CashPaymentFlowTests.kt @@ -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() - 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(criteria) + val (_, vaultUpdatesBankClient) = notaryNode.services.vaultQueryService.trackBy(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 diff --git a/finance/src/test/kotlin/net/corda/flows/IssuerFlowTest.kt b/finance/src/test/kotlin/net/corda/flows/IssuerFlowTest.kt index 97156d6a6d..3007a13a5c 100644 --- a/finance/src/test/kotlin/net/corda/flows/IssuerFlowTest.kt +++ b/finance/src/test/kotlin/net/corda/flows/IssuerFlowTest.kt @@ -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(criteria) + val (_, vaultUpdatesBankClient) = bankClientNode.services.vaultQueryService.trackBy(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 { 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(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, party: Party, - ref: OpaqueBytes): RunResult { + ref: OpaqueBytes): ListenableFuture { val issueToPartyAndRef = party.ref(ref) - val issuerFlows: Observable = 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>, - val issueRequestResult: ListenableFuture - ) } \ No newline at end of file diff --git a/samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaRPCClientTest.kt b/samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaRPCClientTest.kt index ba3ce39b22..8c2ec6d765 100644 --- a/samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaRPCClientTest.kt +++ b/samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaRPCClientTest.kt @@ -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::class.java, criteria) // Register for Big Corporation Vault updates - val vaultUpdatesBigCorp = bigCorpProxy.vaultAndUpdates().second + val (_, vaultUpdatesBigCorp) = bigCorpProxy.vaultTrackByCriteria(Cash.State::class.java, criteria) // Kick-off actual Issuer Flow // TODO: Update checks below to reflect states consumed/produced under anonymisation