diff --git a/core/src/test/kotlin/net/corda/core/flows/AttachmentTests.kt b/core/src/test/kotlin/net/corda/core/flows/AttachmentTests.kt index d7884903a8..9c45a72c67 100644 --- a/core/src/test/kotlin/net/corda/core/flows/AttachmentTests.kt +++ b/core/src/test/kotlin/net/corda/core/flows/AttachmentTests.kt @@ -5,8 +5,8 @@ import com.natpryce.hamkrest.* import com.natpryce.hamkrest.assertion.assert import net.corda.core.contracts.Attachment import net.corda.core.crypto.SecureHash -import net.corda.core.flows.matchers.fails -import net.corda.core.flows.matchers.succeedsWith +import net.corda.core.flows.matchers.flow.willThrow +import net.corda.core.flows.matchers.flow.willReturn import net.corda.core.flows.mixins.WithMockNet import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party @@ -51,7 +51,7 @@ class AttachmentTests : WithMockNet { // Get node one to run a flow to fetch it and insert it. assert.that( bobNode.startAttachmentFlow(id, alice), - succeedsWith(noAttachments())) + willReturn(noAttachments())) // Verify it was inserted into node one's store. val attachment = bobNode.getAttachmentWithId(id) @@ -62,7 +62,7 @@ class AttachmentTests : WithMockNet { assert.that( bobNode.startAttachmentFlow(id, alice), - succeedsWith(soleAttachment(attachment))) + willReturn(soleAttachment(attachment))) } @Test @@ -72,10 +72,14 @@ class AttachmentTests : WithMockNet { // Get node one to fetch a non-existent attachment. assert.that( bobNode.startAttachmentFlow(hash, alice), - fails( - has("requested hash", { it.requested }, equalTo(hash)))) + willThrow(withRequestedHash(hash))) } + fun withRequestedHash(expected: SecureHash) = has( + "requested hash", + FetchDataFlow.HashNotFound::requested, + equalTo(expected)) + @Test fun maliciousResponse() { // Make a node that doesn't do sanity checking at load time. @@ -96,7 +100,7 @@ class AttachmentTests : WithMockNet { // Get n1 to fetch the attachment. Should receive corrupted bytes. assert.that( bobNode.startAttachmentFlow(id, badAlice), - fails() + willThrow() ) } diff --git a/core/src/test/kotlin/net/corda/core/flows/CollectSignaturesFlowTests.kt b/core/src/test/kotlin/net/corda/core/flows/CollectSignaturesFlowTests.kt index 649f6bed7a..576522606a 100644 --- a/core/src/test/kotlin/net/corda/core/flows/CollectSignaturesFlowTests.kt +++ b/core/src/test/kotlin/net/corda/core/flows/CollectSignaturesFlowTests.kt @@ -5,8 +5,8 @@ import com.natpryce.hamkrest.assertion.assert import net.corda.core.contracts.Command import net.corda.core.contracts.StateAndContract import net.corda.core.contracts.requireThat -import net.corda.core.flows.matchers.fails -import net.corda.core.flows.matchers.succeedsWith +import net.corda.core.flows.matchers.flow.willThrow +import net.corda.core.flows.matchers.flow.willReturn import net.corda.core.flows.mixins.WithContracts import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party @@ -28,6 +28,7 @@ class CollectSignaturesFlowTests : WithContracts { private val miniCorp = TestIdentity(CordaX500Name("MiniCorp", "London", "GB")) private val miniCorpServices = MockServices(listOf("net.corda.testing.contracts"), miniCorp, rigorousMock()) private val classMockNet = InternalMockNetwork(cordappPackages = listOf("net.corda.testing.contracts", "net.corda.core.flows")) + private const val MAGIC_NUMBER = 1337 @JvmStatic @@ -35,7 +36,6 @@ class CollectSignaturesFlowTests : WithContracts { fun tearDown() = classMockNet.stopNodes() } - override val magicNumber = MAGIC_NUMBER override val mockNet = classMockNet private val aliceNode = makeNode(ALICE_NAME) @@ -53,7 +53,7 @@ class CollectSignaturesFlowTests : WithContracts { assert.that( aliceNode.startTestFlow(alice, bConfidentialIdentity.party, charlie), - succeedsWith(requiredSignatures(3)) + willReturn(requiredSignatures(3)) ) } @@ -63,7 +63,7 @@ class CollectSignaturesFlowTests : WithContracts { assert.that( aliceNode.collectSignatures(ptx), - succeedsWith(requiredSignatures(1)) + willReturn(requiredSignatures(1)) ) } @@ -73,20 +73,21 @@ class CollectSignaturesFlowTests : WithContracts { assert.that( aliceNode.collectSignatures(ptx), - fails(errorMessage("The Initiator of CollectSignaturesFlow must have signed the transaction."))) + willThrow(errorMessage("The Initiator of CollectSignaturesFlow must have signed the transaction."))) } @Test fun `passes with multiple initial signatures`() { val signedByA = aliceNode.signDummyContract( alice.ref(1), + MAGIC_NUMBER, bob.ref(2), bob.ref(3)) val signedByBoth = bobNode.addSignatureTo(signedByA) assert.that( aliceNode.collectSignatures(signedByBoth), - succeedsWith(requiredSignatures(2)) + willReturn(requiredSignatures(2)) ) } @@ -94,7 +95,7 @@ class CollectSignaturesFlowTests : WithContracts { private fun StartedNode<*>.startTestFlow(vararg party: Party) = startFlow( TestFlow.Initiator(DummyContract.MultiOwnerState( - magicNumber, + MAGIC_NUMBER, listOf(*party)), mockNet.defaultNotaryIdentity)) .andRunNetwork() diff --git a/core/src/test/kotlin/net/corda/core/flows/ContractUpgradeFlowRPCTest.kt b/core/src/test/kotlin/net/corda/core/flows/ContractUpgradeFlowRPCTest.kt new file mode 100644 index 0000000000..f625fe8be4 --- /dev/null +++ b/core/src/test/kotlin/net/corda/core/flows/ContractUpgradeFlowRPCTest.kt @@ -0,0 +1,144 @@ +package net.corda.core.flows + +import co.paralleluniverse.fibers.Suspendable +import com.natpryce.hamkrest.* +import com.natpryce.hamkrest.assertion.assert +import net.corda.core.CordaRuntimeException +import net.corda.core.contracts.* +import net.corda.core.flows.matchers.rpc.willThrow +import net.corda.core.flows.matchers.rpc.willReturn +import net.corda.core.flows.mixins.WithContracts +import net.corda.core.flows.mixins.WithFinality +import net.corda.core.identity.AbstractParty +import net.corda.core.identity.Party +import net.corda.core.internal.Emoji +import net.corda.core.messaging.CordaRPCOps +import net.corda.core.transactions.ContractUpgradeLedgerTransaction +import net.corda.core.transactions.LedgerTransaction +import net.corda.core.transactions.SignedTransaction +import net.corda.finance.contracts.asset.Cash +import net.corda.node.internal.StartedNode +import net.corda.node.services.Permissions.Companion.startFlow +import net.corda.testing.contracts.DummyContract +import net.corda.testing.contracts.DummyContractV2 +import net.corda.testing.core.ALICE_NAME +import net.corda.testing.core.BOB_NAME +import net.corda.testing.core.singleIdentity +import net.corda.testing.node.User +import net.corda.testing.node.internal.* +import net.corda.testing.node.internal.InternalMockNetwork.MockNode +import org.junit.AfterClass +import org.junit.Test + +class ContractUpgradeFlowRPCTest : WithContracts, WithFinality { + companion object { + private val classMockNet = InternalMockNetwork(cordappPackages = listOf( + "net.corda.testing.contracts", + "net.corda.finance.contracts.asset", + "net.corda.core.flows")) + + @JvmStatic + @AfterClass + fun tearDown() = classMockNet.stopNodes() + } + + override val mockNet = classMockNet + + private val aliceNode = makeNode(ALICE_NAME) + private val bobNode = makeNode(BOB_NAME) + + private val alice = aliceNode.info.singleIdentity() + private val bob = bobNode.info.singleIdentity() + + @Test + fun `2 parties contract upgrade using RPC`() = rpcDriver { + val testUser = createTestUser() + val rpcA = startProxy(aliceNode, testUser) + val rpcB = startProxy(bobNode, testUser) + + // Create, sign and finalise dummy contract. + val signedByA = aliceNode.signDummyContract(alice.ref(1), 0, bob.ref(1)) + val stx = bobNode.addSignatureTo(signedByA) + assert.that(rpcA.finalise(stx, bob), willReturn()) + + val atx = aliceNode.getValidatedTransaction(stx) + val btx = bobNode.getValidatedTransaction(stx) + + // Cannot upgrade contract without prior authorisation from counterparty + assert.that( + rpcA.initiateDummyContractUpgrade(atx), + willThrow()) + + // Party B authorises the contract state upgrade, and immediately deauthorises the same. + assert.that(rpcB.authoriseDummyContractUpgrade(btx), willReturn()) + assert.that(rpcB.deauthoriseContractUpgrade(btx), willReturn()) + + // Cannot upgrade contract if counterparty has deauthorised a previously-given authority + assert.that( + rpcA.initiateDummyContractUpgrade(atx), + willThrow()) + + // Party B authorise the contract state upgrade. + assert.that(rpcB.authoriseDummyContractUpgrade(btx), willReturn()) + + // Party A initiates contract upgrade flow, expected to succeed this time. + assert.that( + rpcA.initiateDummyContractUpgrade(atx), + willReturn( + aliceNode.hasDummyContractUpgradeTransaction() + and bobNode.hasDummyContractUpgradeTransaction())) + } + + //region RPC DSL + private fun RPCDriverDSL.startProxy(node: StartedNode, user: User): CordaRPCOps { + return startRpcClient( + rpcAddress = startRpcServer( + rpcUser = user, + ops = node.rpcOps + ).get().broker.hostAndPort!!, + username = user.username, + password = user.password + ).get() + } + + private fun RPCDriverDSL.createTestUser() = rpcTestUser.copy(permissions = setOf( + startFlow(), + startFlow>(), + startFlow(), + startFlow() + )) + //endregion + + //region Operations + private fun CordaRPCOps.initiateDummyContractUpgrade(tx: SignedTransaction) = + initiateContractUpgrade(tx, DummyContractV2::class) + + private fun CordaRPCOps.authoriseDummyContractUpgrade(tx: SignedTransaction) = + authoriseContractUpgrade(tx, DummyContractV2::class) + //endregion + + //region Matchers + private fun StartedNode<*>.hasDummyContractUpgradeTransaction() = + hasContractUpgradeTransaction() + + private inline fun StartedNode<*>.hasContractUpgradeTransaction() = + has, ContractUpgradeLedgerTransaction>( + "a contract upgrade transaction", + { getContractUpgradeTransaction(it) }, + isUpgrade()) + + private fun StartedNode<*>.getContractUpgradeTransaction(state: StateAndRef) = database.transaction { + services.validatedTransactions.getTransaction(state.ref.txhash)!! + .resolveContractUpgradeTransaction(services) + } + + private inline fun isUpgrade() = + isUpgradeFrom() and isUpgradeTo() + + private inline fun isUpgradeFrom() = + has("input data", { it.inputs.single().state.data }, isA(anything)) + + private inline fun isUpgradeTo() = + has("output data", { it.outputs.single().data }, isA(anything)) + //endregion +} diff --git a/core/src/test/kotlin/net/corda/core/flows/ContractUpgradeFlowTest.kt b/core/src/test/kotlin/net/corda/core/flows/ContractUpgradeFlowTest.kt index 464628bb82..b91b3265ff 100644 --- a/core/src/test/kotlin/net/corda/core/flows/ContractUpgradeFlowTest.kt +++ b/core/src/test/kotlin/net/corda/core/flows/ContractUpgradeFlowTest.kt @@ -3,9 +3,9 @@ package net.corda.core.flows import co.paralleluniverse.fibers.Suspendable import com.natpryce.hamkrest.* import com.natpryce.hamkrest.assertion.assert -import net.corda.core.CordaRuntimeException import net.corda.core.contracts.* -import net.corda.core.flows.matchers.* +import net.corda.core.flows.matchers.flow.willThrow +import net.corda.core.flows.matchers.flow.willReturn import net.corda.core.flows.mixins.WithContracts import net.corda.core.flows.mixins.WithFinality import net.corda.core.identity.AbstractParty @@ -48,7 +48,6 @@ class ContractUpgradeFlowTest : WithContracts, WithFinality { } override val mockNet = classMockNet - override val magicNumber = 0 private val aliceNode = makeNode(ALICE_NAME) private val bobNode = makeNode(BOB_NAME) @@ -60,7 +59,7 @@ class ContractUpgradeFlowTest : WithContracts, WithFinality { @Test fun `2 parties contract upgrade`() { // Create dummy contract. - val signedByA = aliceNode.signDummyContract(alice.ref(1), bob.ref(1)) + val signedByA = aliceNode.signDummyContract(alice.ref(1),0, bob.ref(1)) val stx = bobNode.addSignatureTo(signedByA) aliceNode.finalise(stx, bob) @@ -71,63 +70,24 @@ class ContractUpgradeFlowTest : WithContracts, WithFinality { // The request is expected to be rejected because party B hasn't authorised the upgrade yet. assert.that( aliceNode.initiateDummyContractUpgrade(atx), - fails()) + willThrow()) // Party B authorise the contract state upgrade, and immediately deauthorise the same. - assert.that(bobNode.authoriseDummyContractUpgrade(btx), succeeds()) - assert.that(bobNode.deauthoriseContractUpgrade(btx), succeeds()) + assert.that(bobNode.authoriseDummyContractUpgrade(btx), willReturn()) + assert.that(bobNode.deauthoriseContractUpgrade(btx), willReturn()) // The request is expected to be rejected because party B has subsequently deauthorised a previously authorised upgrade. assert.that( aliceNode.initiateDummyContractUpgrade(atx), - fails()) + willThrow()) // Party B authorise the contract state upgrade - assert.that(bobNode.authoriseDummyContractUpgrade(btx), succeeds()) + assert.that(bobNode.authoriseDummyContractUpgrade(btx), willReturn()) // Party A initiates contract upgrade flow, expected to succeed this time. assert.that( aliceNode.initiateDummyContractUpgrade(atx), - succeedsWith( - aliceNode.hasDummyContractUpgradeTransaction() - and bobNode.hasDummyContractUpgradeTransaction())) - } - - @Test - fun `2 parties contract upgrade using RPC`() = rpcDriver { - val testUser = createTestUser() - val rpcA = startProxy(aliceNode, testUser) - val rpcB = startProxy(bobNode, testUser) - - // Create, sign and finalise dummy contract. - val signedByA = aliceNode.signDummyContract(alice.ref(1), bob.ref(1)) - val stx = bobNode.addSignatureTo(signedByA) - assert.that(rpcA.finalise(stx, bob), rpcSucceeds()) - - val atx = aliceNode.getValidatedTransaction(stx) - val btx = bobNode.getValidatedTransaction(stx) - - // Cannot upgrade contract without prior authorisation from counterparty - assert.that( - rpcA.initiateDummyContractUpgrade(atx), - rpcFails()) - - // Party B authorises the contract state upgrade, and immediately deauthorises the same. - assert.that(rpcB.authoriseDummyContractUpgrade(btx), rpcSucceeds()) - assert.that(rpcB.deauthoriseContractUpgrade(btx), rpcSucceeds()) - - // Cannot upgrade contract if counterparty has deauthorised a previously-given authority - assert.that( - rpcA.initiateDummyContractUpgrade(atx), - rpcFails()) - - // Party B authorise the contract state upgrade. - assert.that(rpcB.authoriseDummyContractUpgrade(btx), rpcSucceeds()) - - // Party A initiates contract upgrade flow, expected to succeed this time. - assert.that( - rpcA.initiateDummyContractUpgrade(atx), - rpcSucceedsWith( + willReturn( aliceNode.hasDummyContractUpgradeTransaction() and bobNode.hasDummyContractUpgradeTransaction())) } @@ -164,7 +124,7 @@ class ContractUpgradeFlowTest : WithContracts, WithFinality { assert.that(aliceNode.getBaseStateFromVault(), hasContractState(isA(anything))) // Starts contract upgrade flow. - assert.that(aliceNode.initiateContractUpgrade(stateAndRef, CashV2::class), succeeds()) + assert.that(aliceNode.initiateContractUpgrade(stateAndRef, CashV2::class), willReturn()) // Get contract state from the vault. val upgradedState = aliceNode.getCashStateFromVault() @@ -182,7 +142,7 @@ class ContractUpgradeFlowTest : WithContracts, WithFinality { addCommand(CashV2.Move(), alice.owningKey) } - assert.that(aliceNode.finalise(spendUpgradedTx), succeeds()) + assert.that(aliceNode.finalise(spendUpgradedTx), willReturn()) assert.that(aliceNode.getCashStateFromVault(), hasContractState(equalTo(movedState))) } @@ -208,45 +168,12 @@ class ContractUpgradeFlowTest : WithContracts, WithFinality { override fun verify(tx: LedgerTransaction) {} } - @StartableByRPC - class FinalityInvoker(private val transaction: SignedTransaction, - private val extraRecipients: Set) : FlowLogic() { - @Suspendable - override fun call(): SignedTransaction = subFlow(FinalityFlow(transaction, extraRecipients)) - } - - //region RPC DSL - private fun RPCDriverDSL.startProxy(node: StartedNode, user: User): CordaRPCOps { - return startRpcClient( - rpcAddress = startRpcServer( - rpcUser = user, - ops = node.rpcOps - ).get().broker.hostAndPort!!, - username = user.username, - password = user.password - ).get() - } - - private fun RPCDriverDSL.createTestUser() = rpcTestUser.copy(permissions = setOf( - startFlow(), - startFlow>(), - startFlow(), - startFlow() - )) - //endregion - //region Operations private fun StartedNode<*>.initiateDummyContractUpgrade(tx: SignedTransaction) = initiateContractUpgrade(tx, DummyContractV2::class) private fun StartedNode<*>.authoriseDummyContractUpgrade(tx: SignedTransaction) = authoriseContractUpgrade(tx, DummyContractV2::class) - - private fun CordaRPCOps.initiateDummyContractUpgrade(tx: SignedTransaction) = - initiateContractUpgrade(tx, DummyContractV2::class) - - private fun CordaRPCOps.authoriseDummyContractUpgrade(tx: SignedTransaction) = - authoriseContractUpgrade(tx, DummyContractV2::class) //endregion //region Matchers diff --git a/core/src/test/kotlin/net/corda/core/flows/FinalityFlowTests.kt b/core/src/test/kotlin/net/corda/core/flows/FinalityFlowTests.kt index 0aee66bf00..c53473d727 100644 --- a/core/src/test/kotlin/net/corda/core/flows/FinalityFlowTests.kt +++ b/core/src/test/kotlin/net/corda/core/flows/FinalityFlowTests.kt @@ -2,8 +2,8 @@ package net.corda.core.flows import com.natpryce.hamkrest.and import com.natpryce.hamkrest.assertion.assert -import net.corda.core.flows.matchers.fails -import net.corda.core.flows.matchers.succeedsWith +import net.corda.core.flows.matchers.flow.willThrow +import net.corda.core.flows.matchers.flow.willReturn import net.corda.core.flows.mixins.WithFinality import net.corda.core.identity.Party import net.corda.core.transactions.SignedTransaction @@ -42,9 +42,9 @@ class FinalityFlowTests : WithFinality { assert.that( aliceNode.finalise(stx), - succeedsWith( - requiredSignatures(1) - and transactionVisibleTo(bobNode))) + willReturn( + requiredSignatures(1) + and transactionVisibleTo(bobNode))) } @Test @@ -54,7 +54,7 @@ class FinalityFlowTests : WithFinality { assert.that( aliceNode.finalise(stx), - fails()) + willThrow()) } private fun StartedNode<*>.signCashTransactionWith(other: Party): SignedTransaction { diff --git a/core/src/test/kotlin/net/corda/core/flows/ReceiveAllFlowTests.kt b/core/src/test/kotlin/net/corda/core/flows/ReceiveAllFlowTests.kt index 3d1db0c11d..903fd8d674 100644 --- a/core/src/test/kotlin/net/corda/core/flows/ReceiveAllFlowTests.kt +++ b/core/src/test/kotlin/net/corda/core/flows/ReceiveAllFlowTests.kt @@ -4,7 +4,7 @@ import co.paralleluniverse.fibers.Suspendable import com.natpryce.hamkrest.assertion.assert import com.natpryce.hamkrest.equalTo import com.natpryce.hamkrest.isA -import net.corda.core.flows.matchers.succeedsWith +import net.corda.core.flows.matchers.flow.willReturn import net.corda.core.flows.mixins.WithMockNet import net.corda.core.identity.Party import net.corda.core.utilities.UntrustworthyData @@ -60,7 +60,7 @@ class ReceiveMultipleFlowTests : WithMockNet { assert.that( nodes[0].startFlowAndRunNetwork(initiatingFlow), - succeedsWith(isA(equalTo(answer)))) + willReturn(isA(equalTo(answer)))) } @Test @@ -72,7 +72,7 @@ class ReceiveMultipleFlowTests : WithMockNet { assert.that( nodes[0].startFlowAndRunNetwork(ParallelAlgorithmMap(nodes[1].info.singleIdentity(), nodes[2].info.singleIdentity())), - succeedsWith(equalTo(doubleValue * stringValue.length))) + willReturn(equalTo(doubleValue * stringValue.length))) } @Test @@ -84,7 +84,7 @@ class ReceiveMultipleFlowTests : WithMockNet { assert.that( nodes[0].startFlowAndRunNetwork(ParallelAlgorithmList(nodes[1].info.singleIdentity(), nodes[2].info.singleIdentity())), - succeedsWith(equalTo(listOf(value1, value2)))) + willReturn(equalTo(listOf(value1, value2)))) } class ParallelAlgorithmMap(doubleMember: Party, stringMember: Party) : AlgorithmDefinition(doubleMember, stringMember) { diff --git a/core/src/test/kotlin/net/corda/core/flows/matchers/FlowMatchers.kt b/core/src/test/kotlin/net/corda/core/flows/matchers/FlowMatchers.kt deleted file mode 100644 index a1aaaf1dd7..0000000000 --- a/core/src/test/kotlin/net/corda/core/flows/matchers/FlowMatchers.kt +++ /dev/null @@ -1,69 +0,0 @@ -package net.corda.core.flows.matchers - -import com.natpryce.hamkrest.MatchResult -import com.natpryce.hamkrest.Matcher -import net.corda.core.internal.FlowStateMachine -import net.corda.core.utilities.getOrThrow - -/** - * Matches a Flow that succeeds with a result matched by the given matcher - */ -fun succeeds() = object : Matcher> { - override val description: String = "is a flow that succeeds" - - override fun invoke(actual: FlowStateMachine): MatchResult = try { - actual.resultFuture.getOrThrow() - MatchResult.Match - } catch (e: Exception) { - MatchResult.Mismatch("Failed with $e") - } -} - -/** - * Matches a Flow that succeeds with a result matched by the given matcher - */ -fun succeedsWith(successMatcher: Matcher) = object : Matcher> { - override val description: String = "is a flow that succeeds with a value that ${successMatcher.description}" - - override fun invoke(actual: FlowStateMachine): MatchResult = try { - successMatcher(actual.resultFuture.getOrThrow()) - } catch (e: Exception) { - MatchResult.Mismatch("Failed with $e") - } -} - -/** - * Matches a Flow that fails, with an exception matched by the given matcher. - */ -inline fun fails(failureMatcher: Matcher) = object : Matcher> { - override val description: String - get() = "is a flow that fails with a ${E::class.java.simpleName} that ${failureMatcher.description}" - - override fun invoke(actual: FlowStateMachine<*>): MatchResult = try { - actual.resultFuture.getOrThrow() - MatchResult.Mismatch("Succeeded") - } catch (e: Exception) { - when(e) { - is E -> failureMatcher(e) - else -> MatchResult.Mismatch("Failure class was ${e.javaClass}") - } - } -} - -/** - * Matches a Flow that fails, with an exception of the specified type. - */ -inline fun fails() = object : Matcher> { - override val description: String - get() = "is a flow that fails with a ${E::class.java}" - - override fun invoke(actual: FlowStateMachine<*>): MatchResult = try { - actual.resultFuture.getOrThrow() - MatchResult.Mismatch("Succeeded") - } catch (e: Exception) { - when(e) { - is E -> MatchResult.Match - else -> MatchResult.Mismatch("Failure class was ${e.javaClass}") - } - } -} \ No newline at end of file diff --git a/core/src/test/kotlin/net/corda/core/flows/matchers/FutureMatchers.kt b/core/src/test/kotlin/net/corda/core/flows/matchers/FutureMatchers.kt new file mode 100644 index 0000000000..13c9dc96f8 --- /dev/null +++ b/core/src/test/kotlin/net/corda/core/flows/matchers/FutureMatchers.kt @@ -0,0 +1,69 @@ +package net.corda.core.flows.matchers + +import com.natpryce.hamkrest.MatchResult +import com.natpryce.hamkrest.Matcher +import net.corda.core.utilities.getOrThrow +import java.util.concurrent.Future + +/** + * Matches a Flow that succeeds with a result matched by the given matcher + */ +fun willReturn() = object : Matcher> { + override val description: String = "is a future that will succeed" + + override fun invoke(actual: Future): MatchResult = try { + actual.getOrThrow() + MatchResult.Match + } catch (e: Exception) { + MatchResult.Mismatch("Failed with $e") + } +} + +/** + * Matches a Flow that succeeds with a result matched by the given matcher + */ +fun willSucceedWithResult(successMatcher: Matcher) = object : Matcher> { + override val description: String = "is a future that will succeed with a value that ${successMatcher.description}" + + override fun invoke(actual: Future): MatchResult = try { + successMatcher(actual.getOrThrow()) + } catch (e: Exception) { + MatchResult.Mismatch("Failed with $e") + } +} + +/** + * Matches a Flow that fails, with an exception matched by the given matcher. + */ +inline fun willFailWithException(failureMatcher: Matcher) = object : Matcher> { + override val description: String + get() = "is a future that will fail with a ${E::class.java.simpleName} that ${failureMatcher.description}" + + override fun invoke(actual: Future<*>): MatchResult = try { + actual.getOrThrow() + MatchResult.Mismatch("Succeeded") + } catch (e: Exception) { + when(e) { + is E -> failureMatcher(e) + else -> MatchResult.Mismatch("Failure class was ${e.javaClass}") + } + } +} + +/** + * Matches a Flow that fails, with an exception of the specified type. + */ +inline fun willThrow() = object : Matcher> { + override val description: String + get() = "is a future that will fail with a ${E::class.java}" + + override fun invoke(actual: Future<*>): MatchResult = try { + actual.getOrThrow() + MatchResult.Mismatch("Succeeded") + } catch (e: Exception) { + when(e) { + is E -> MatchResult.Match + else -> MatchResult.Mismatch("Failure class was ${e.javaClass}") + } + } +} \ No newline at end of file diff --git a/core/src/test/kotlin/net/corda/core/flows/matchers/RpcMatchers.kt b/core/src/test/kotlin/net/corda/core/flows/matchers/RpcMatchers.kt deleted file mode 100644 index cd0ca6c286..0000000000 --- a/core/src/test/kotlin/net/corda/core/flows/matchers/RpcMatchers.kt +++ /dev/null @@ -1,69 +0,0 @@ -package net.corda.core.flows.matchers - -import com.natpryce.hamkrest.MatchResult -import com.natpryce.hamkrest.Matcher -import net.corda.core.messaging.FlowHandle -import net.corda.core.utilities.getOrThrow - -/** - * Matches an RPC flow handle that succeeds with a result matched by the given matcher - */ -fun rpcSucceeds() = object : Matcher> { - override val description: String = "is an RPC flow handle that succeeds" - - override fun invoke(actual: FlowHandle): MatchResult = try { - actual.returnValue.getOrThrow() - MatchResult.Match - } catch (e: Exception) { - MatchResult.Mismatch("Failed with $e") - } -} - -/** - * Matches an RPC flow handle that succeeds with a result matched by the given matcher - */ -fun rpcSucceedsWith(successMatcher: Matcher) = object : Matcher> { - override val description: String = "is an RPC flow handle that succeeds with a value that ${successMatcher.description}" - - override fun invoke(actual: FlowHandle): MatchResult = try { - successMatcher(actual.returnValue.getOrThrow()) - } catch (e: Exception) { - MatchResult.Mismatch("Failed with $e") - } -} - -/** - * Matches an RPC Flow handle that fails, with an exception matched by the given matcher. - */ -inline fun rpcFails(failureMatcher: Matcher) = object : Matcher> { - override val description: String - get() = "is an RPC flow handle that fails with a ${E::class.java.simpleName} that ${failureMatcher.description}" - - override fun invoke(actual: FlowHandle<*>): MatchResult = try { - actual.returnValue.getOrThrow() - MatchResult.Mismatch("Succeeded") - } catch (e: Exception) { - when(e) { - is E -> failureMatcher(e) - else -> MatchResult.Mismatch("Failure class was ${e.javaClass}") - } - } -} - -/** - * Matches a Flow that fails, with an exception of the specified type. - */ -inline fun rpcFails() = object : Matcher> { - override val description: String - get() = "is an RPC flow handle that fails with a ${E::class.java}" - - override fun invoke(actual: FlowHandle<*>): MatchResult = try { - actual.returnValue.getOrThrow() - MatchResult.Mismatch("Succeeded") - } catch (e: Exception) { - when(e) { - is E -> MatchResult.Match - else -> MatchResult.Mismatch("Failure class was ${e.javaClass}") - } - } -} \ No newline at end of file diff --git a/core/src/test/kotlin/net/corda/core/flows/matchers/flow/FlowMatchers.kt b/core/src/test/kotlin/net/corda/core/flows/matchers/flow/FlowMatchers.kt new file mode 100644 index 0000000000..a28814d221 --- /dev/null +++ b/core/src/test/kotlin/net/corda/core/flows/matchers/flow/FlowMatchers.kt @@ -0,0 +1,35 @@ +package net.corda.core.flows.matchers.flow + +import com.natpryce.hamkrest.Matcher +import com.natpryce.hamkrest.has +import net.corda.core.flows.matchers.willThrow +import net.corda.core.flows.matchers.willFailWithException +import net.corda.core.flows.matchers.willReturn +import net.corda.core.flows.matchers.willSucceedWithResult +import net.corda.core.internal.FlowStateMachine + +/** + * Matches a Flow that succeeds with a result matched by the given matcher + */ +fun willReturn() = has(FlowStateMachine::resultFuture, willReturn()) + +/** + * Matches a Flow that succeeds with a result matched by the given matcher + */ +fun willReturn(successMatcher: Matcher) = has( + FlowStateMachine::resultFuture, + willSucceedWithResult(successMatcher)) + +/** + * Matches a Flow that fails, with an exception matched by the given matcher. + */ +inline fun willThrow(failureMatcher: Matcher) = has( + FlowStateMachine<*>::resultFuture, + willFailWithException(failureMatcher)) + +/** + * Matches a Flow that fails, with an exception of the specified type. + */ +inline fun willThrow() = has( + FlowStateMachine<*>::resultFuture, + willThrow()) \ No newline at end of file diff --git a/core/src/test/kotlin/net/corda/core/flows/matchers/rpc/RpcMatchers.kt b/core/src/test/kotlin/net/corda/core/flows/matchers/rpc/RpcMatchers.kt new file mode 100644 index 0000000000..0583754ec4 --- /dev/null +++ b/core/src/test/kotlin/net/corda/core/flows/matchers/rpc/RpcMatchers.kt @@ -0,0 +1,33 @@ +package net.corda.core.flows.matchers.rpc + +import com.natpryce.hamkrest.Matcher +import com.natpryce.hamkrest.has +import net.corda.core.flows.matchers.willThrow +import net.corda.core.flows.matchers.willFailWithException +import net.corda.core.flows.matchers.willReturn +import net.corda.core.flows.matchers.willSucceedWithResult +import net.corda.core.messaging.FlowHandle + +/** + * Matches a flow handle that succeeds with a result matched by the given matcher + */ +fun willReturn() = has(FlowHandle::returnValue, willReturn()) + +/** + * Matches a flow handle that succeeds with a result matched by the given matcher + */ +fun willReturn(successMatcher: Matcher) = has(FlowHandle::returnValue, willSucceedWithResult(successMatcher)) + +/** + * Matches a flow handle that fails, with an exception matched by the given matcher. + */ +inline fun willThrow(failureMatcher: Matcher) = has( + FlowHandle<*>::returnValue, + willFailWithException(failureMatcher)) + +/** + * Matches a flow handle that fails, with an exception of the specified type. + */ +inline fun willThrow() = has( + FlowHandle<*>::returnValue, + willThrow()) \ No newline at end of file diff --git a/core/src/test/kotlin/net/corda/core/flows/mixins/WithContracts.kt b/core/src/test/kotlin/net/corda/core/flows/mixins/WithContracts.kt index 3da8648ddf..846b4ded41 100644 --- a/core/src/test/kotlin/net/corda/core/flows/mixins/WithContracts.kt +++ b/core/src/test/kotlin/net/corda/core/flows/mixins/WithContracts.kt @@ -21,10 +21,8 @@ import kotlin.reflect.KClass */ interface WithContracts : WithMockNet { - val magicNumber: Int - //region Generators - fun createDummyContract(owner: PartyAndReference, vararg others: PartyAndReference) = + fun createDummyContract(owner: PartyAndReference, magicNumber: Int = 0, vararg others: PartyAndReference) = DummyContract.generateInitial( magicNumber, mockNet.defaultNotaryIdentity, @@ -33,21 +31,11 @@ interface WithContracts : WithMockNet { //region //region Operations - fun StartedNode<*>.createConfidentialIdentity(party: Party) = database.transaction { - services.keyManagementService.freshKeyAndCert( - services.myInfo.legalIdentitiesAndCerts.single { it.name == party.name }, - false) - } + fun StartedNode<*>.signDummyContract(owner: PartyAndReference, magicNumber: Int = 0, vararg others: PartyAndReference) = + services.signDummyContract(owner, magicNumber, *others).andRunNetwork() - fun StartedNode<*>.verifyAndRegister(identity: PartyAndCertificate) = database.transaction { - services.identityService.verifyAndRegisterIdentity(identity) - } - - fun StartedNode<*>.signDummyContract(owner: PartyAndReference, vararg others: PartyAndReference) = - services.signDummyContract(owner, *others).andRunNetwork() - - fun ServiceHub.signDummyContract(owner: PartyAndReference, vararg others: PartyAndReference) = - signInitialTransaction(createDummyContract(owner, *others)) + fun ServiceHub.signDummyContract(owner: PartyAndReference, magicNumber: Int = 0, vararg others: PartyAndReference) = + signInitialTransaction(createDummyContract(owner, magicNumber, *others)) fun StartedNode<*>.collectSignatures(ptx: SignedTransaction) = startFlowAndRunNetwork(CollectSignaturesFlow(ptx, emptySet())) diff --git a/core/src/test/kotlin/net/corda/core/flows/mixins/WithFinality.kt b/core/src/test/kotlin/net/corda/core/flows/mixins/WithFinality.kt index 73592590d0..417326c44d 100644 --- a/core/src/test/kotlin/net/corda/core/flows/mixins/WithFinality.kt +++ b/core/src/test/kotlin/net/corda/core/flows/mixins/WithFinality.kt @@ -1,9 +1,12 @@ package net.corda.core.flows.mixins +import co.paralleluniverse.fibers.Suspendable import com.natpryce.hamkrest.Matcher import com.natpryce.hamkrest.equalTo import net.corda.core.flows.ContractUpgradeFlowTest import net.corda.core.flows.FinalityFlow +import net.corda.core.flows.FlowLogic +import net.corda.core.flows.StartableByRPC import net.corda.core.identity.Party import net.corda.core.messaging.CordaRPCOps import net.corda.core.messaging.startFlow @@ -22,7 +25,7 @@ interface WithFinality : WithMockNet { } fun CordaRPCOps.finalise(stx: SignedTransaction, vararg parties: Party) = - startFlow(ContractUpgradeFlowTest::FinalityInvoker, stx, parties.toSet()) + startFlow(::FinalityInvoker, stx, parties.toSet()) .andRunNetwork() //endregion @@ -33,4 +36,11 @@ interface WithFinality : WithMockNet { equalTo(actual)(other.getValidatedTransaction(actual)) } //endregion + + @StartableByRPC + class FinalityInvoker(private val transaction: SignedTransaction, + private val extraRecipients: Set) : FlowLogic() { + @Suspendable + override fun call(): SignedTransaction = subFlow(FinalityFlow(transaction, extraRecipients)) + } } \ No newline at end of file diff --git a/core/src/test/kotlin/net/corda/core/flows/mixins/WithMockNet.kt b/core/src/test/kotlin/net/corda/core/flows/mixins/WithMockNet.kt index e04da0aac2..41a75a6aa2 100644 --- a/core/src/test/kotlin/net/corda/core/flows/mixins/WithMockNet.kt +++ b/core/src/test/kotlin/net/corda/core/flows/mixins/WithMockNet.kt @@ -4,6 +4,8 @@ import com.natpryce.hamkrest.* import net.corda.core.contracts.ContractState import net.corda.core.flows.FlowLogic import net.corda.core.identity.CordaX500Name +import net.corda.core.identity.Party +import net.corda.core.identity.PartyAndCertificate import net.corda.core.internal.FlowStateMachine import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.TransactionBuilder @@ -33,10 +35,7 @@ interface WithMockNet { /** * Run the mock network before proceeding */ - fun T.andRunNetwork(): T { - mockNet.runNetwork() - return this - } + fun T.andRunNetwork(): T = apply { mockNet.runNetwork() } //region Operations /** @@ -62,6 +61,16 @@ interface WithMockNet { */ fun StartedNode<*>.startFlowAndRunNetwork(logic: FlowLogic): FlowStateMachine = startFlow(logic).andRunNetwork() + + fun StartedNode<*>.createConfidentialIdentity(party: Party) = database.transaction { + services.keyManagementService.freshKeyAndCert( + services.myInfo.legalIdentitiesAndCerts.single { it.name == party.name }, + false) + } + + fun StartedNode<*>.verifyAndRegister(identity: PartyAndCertificate) = database.transaction { + services.identityService.verifyAndRegisterIdentity(identity) + } //endregion //region Matchers