Renaming matchers

This commit is contained in:
Dominic Fox 2018-07-19 11:36:07 +01:00
parent 0780d6051e
commit fc5cc89fdb
14 changed files with 351 additions and 269 deletions

View File

@ -5,8 +5,8 @@ import com.natpryce.hamkrest.*
import com.natpryce.hamkrest.assertion.assert import com.natpryce.hamkrest.assertion.assert
import net.corda.core.contracts.Attachment import net.corda.core.contracts.Attachment
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.flows.matchers.fails import net.corda.core.flows.matchers.flow.willThrow
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.flows.mixins.WithMockNet
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
import net.corda.core.identity.Party 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. // Get node one to run a flow to fetch it and insert it.
assert.that( assert.that(
bobNode.startAttachmentFlow(id, alice), bobNode.startAttachmentFlow(id, alice),
succeedsWith(noAttachments())) willReturn(noAttachments()))
// Verify it was inserted into node one's store. // Verify it was inserted into node one's store.
val attachment = bobNode.getAttachmentWithId(id) val attachment = bobNode.getAttachmentWithId(id)
@ -62,7 +62,7 @@ class AttachmentTests : WithMockNet {
assert.that( assert.that(
bobNode.startAttachmentFlow(id, alice), bobNode.startAttachmentFlow(id, alice),
succeedsWith(soleAttachment(attachment))) willReturn(soleAttachment(attachment)))
} }
@Test @Test
@ -72,10 +72,14 @@ class AttachmentTests : WithMockNet {
// Get node one to fetch a non-existent attachment. // Get node one to fetch a non-existent attachment.
assert.that( assert.that(
bobNode.startAttachmentFlow(hash, alice), bobNode.startAttachmentFlow(hash, alice),
fails<FetchDataFlow.HashNotFound>( willThrow(withRequestedHash(hash)))
has("requested hash", { it.requested }, equalTo(hash))))
} }
fun withRequestedHash(expected: SecureHash) = has(
"requested hash",
FetchDataFlow.HashNotFound::requested,
equalTo(expected))
@Test @Test
fun maliciousResponse() { fun maliciousResponse() {
// Make a node that doesn't do sanity checking at load time. // 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. // Get n1 to fetch the attachment. Should receive corrupted bytes.
assert.that( assert.that(
bobNode.startAttachmentFlow(id, badAlice), bobNode.startAttachmentFlow(id, badAlice),
fails<FetchDataFlow.DownloadedVsRequestedDataMismatch>() willThrow<FetchDataFlow.DownloadedVsRequestedDataMismatch>()
) )
} }

View File

@ -5,8 +5,8 @@ import com.natpryce.hamkrest.assertion.assert
import net.corda.core.contracts.Command import net.corda.core.contracts.Command
import net.corda.core.contracts.StateAndContract import net.corda.core.contracts.StateAndContract
import net.corda.core.contracts.requireThat import net.corda.core.contracts.requireThat
import net.corda.core.flows.matchers.fails import net.corda.core.flows.matchers.flow.willThrow
import net.corda.core.flows.matchers.succeedsWith import net.corda.core.flows.matchers.flow.willReturn
import net.corda.core.flows.mixins.WithContracts import net.corda.core.flows.mixins.WithContracts
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
import net.corda.core.identity.Party import net.corda.core.identity.Party
@ -28,6 +28,7 @@ class CollectSignaturesFlowTests : WithContracts {
private val miniCorp = TestIdentity(CordaX500Name("MiniCorp", "London", "GB")) private val miniCorp = TestIdentity(CordaX500Name("MiniCorp", "London", "GB"))
private val miniCorpServices = MockServices(listOf("net.corda.testing.contracts"), miniCorp, rigorousMock()) 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 val classMockNet = InternalMockNetwork(cordappPackages = listOf("net.corda.testing.contracts", "net.corda.core.flows"))
private const val MAGIC_NUMBER = 1337 private const val MAGIC_NUMBER = 1337
@JvmStatic @JvmStatic
@ -35,7 +36,6 @@ class CollectSignaturesFlowTests : WithContracts {
fun tearDown() = classMockNet.stopNodes() fun tearDown() = classMockNet.stopNodes()
} }
override val magicNumber = MAGIC_NUMBER
override val mockNet = classMockNet override val mockNet = classMockNet
private val aliceNode = makeNode(ALICE_NAME) private val aliceNode = makeNode(ALICE_NAME)
@ -53,7 +53,7 @@ class CollectSignaturesFlowTests : WithContracts {
assert.that( assert.that(
aliceNode.startTestFlow(alice, bConfidentialIdentity.party, charlie), aliceNode.startTestFlow(alice, bConfidentialIdentity.party, charlie),
succeedsWith(requiredSignatures(3)) willReturn(requiredSignatures(3))
) )
} }
@ -63,7 +63,7 @@ class CollectSignaturesFlowTests : WithContracts {
assert.that( assert.that(
aliceNode.collectSignatures(ptx), aliceNode.collectSignatures(ptx),
succeedsWith(requiredSignatures(1)) willReturn(requiredSignatures(1))
) )
} }
@ -73,20 +73,21 @@ class CollectSignaturesFlowTests : WithContracts {
assert.that( assert.that(
aliceNode.collectSignatures(ptx), 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 @Test
fun `passes with multiple initial signatures`() { fun `passes with multiple initial signatures`() {
val signedByA = aliceNode.signDummyContract( val signedByA = aliceNode.signDummyContract(
alice.ref(1), alice.ref(1),
MAGIC_NUMBER,
bob.ref(2), bob.ref(2),
bob.ref(3)) bob.ref(3))
val signedByBoth = bobNode.addSignatureTo(signedByA) val signedByBoth = bobNode.addSignatureTo(signedByA)
assert.that( assert.that(
aliceNode.collectSignatures(signedByBoth), aliceNode.collectSignatures(signedByBoth),
succeedsWith(requiredSignatures(2)) willReturn(requiredSignatures(2))
) )
} }
@ -94,7 +95,7 @@ class CollectSignaturesFlowTests : WithContracts {
private fun StartedNode<*>.startTestFlow(vararg party: Party) = private fun StartedNode<*>.startTestFlow(vararg party: Party) =
startFlow( startFlow(
TestFlow.Initiator(DummyContract.MultiOwnerState( TestFlow.Initiator(DummyContract.MultiOwnerState(
magicNumber, MAGIC_NUMBER,
listOf(*party)), listOf(*party)),
mockNet.defaultNotaryIdentity)) mockNet.defaultNotaryIdentity))
.andRunNetwork() .andRunNetwork()

View File

@ -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<CordaRuntimeException>())
// 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<CordaRuntimeException>())
// 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<MockNode>, user: User): CordaRPCOps {
return startRpcClient<CordaRPCOps>(
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<WithFinality.FinalityInvoker>(),
startFlow<ContractUpgradeFlow.Initiate<*, *>>(),
startFlow<ContractUpgradeFlow.Authorise>(),
startFlow<ContractUpgradeFlow.Deauthorise>()
))
//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<DummyContract.State, DummyContractV2.State>()
private inline fun <reified FROM : Any, reified TO: Any> StartedNode<*>.hasContractUpgradeTransaction() =
has<StateAndRef<ContractState>, ContractUpgradeLedgerTransaction>(
"a contract upgrade transaction",
{ getContractUpgradeTransaction(it) },
isUpgrade<FROM, TO>())
private fun StartedNode<*>.getContractUpgradeTransaction(state: StateAndRef<ContractState>) = database.transaction {
services.validatedTransactions.getTransaction(state.ref.txhash)!!
.resolveContractUpgradeTransaction(services)
}
private inline fun <reified FROM : Any, reified TO : Any> isUpgrade() =
isUpgradeFrom<FROM>() and isUpgradeTo<TO>()
private inline fun <reified T: Any> isUpgradeFrom() =
has<ContractUpgradeLedgerTransaction, Any>("input data", { it.inputs.single().state.data }, isA<T>(anything))
private inline fun <reified T: Any> isUpgradeTo() =
has<ContractUpgradeLedgerTransaction, Any>("output data", { it.outputs.single().data }, isA<T>(anything))
//endregion
}

View File

@ -3,9 +3,9 @@ package net.corda.core.flows
import co.paralleluniverse.fibers.Suspendable import co.paralleluniverse.fibers.Suspendable
import com.natpryce.hamkrest.* import com.natpryce.hamkrest.*
import com.natpryce.hamkrest.assertion.assert import com.natpryce.hamkrest.assertion.assert
import net.corda.core.CordaRuntimeException
import net.corda.core.contracts.* 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.WithContracts
import net.corda.core.flows.mixins.WithFinality import net.corda.core.flows.mixins.WithFinality
import net.corda.core.identity.AbstractParty import net.corda.core.identity.AbstractParty
@ -48,7 +48,6 @@ class ContractUpgradeFlowTest : WithContracts, WithFinality {
} }
override val mockNet = classMockNet override val mockNet = classMockNet
override val magicNumber = 0
private val aliceNode = makeNode(ALICE_NAME) private val aliceNode = makeNode(ALICE_NAME)
private val bobNode = makeNode(BOB_NAME) private val bobNode = makeNode(BOB_NAME)
@ -60,7 +59,7 @@ class ContractUpgradeFlowTest : WithContracts, WithFinality {
@Test @Test
fun `2 parties contract upgrade`() { fun `2 parties contract upgrade`() {
// Create dummy contract. // 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) val stx = bobNode.addSignatureTo(signedByA)
aliceNode.finalise(stx, bob) 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. // The request is expected to be rejected because party B hasn't authorised the upgrade yet.
assert.that( assert.that(
aliceNode.initiateDummyContractUpgrade(atx), aliceNode.initiateDummyContractUpgrade(atx),
fails<UnexpectedFlowEndException>()) willThrow<UnexpectedFlowEndException>())
// Party B authorise the contract state upgrade, and immediately deauthorise the same. // Party B authorise the contract state upgrade, and immediately deauthorise the same.
assert.that(bobNode.authoriseDummyContractUpgrade(btx), succeeds()) assert.that(bobNode.authoriseDummyContractUpgrade(btx), willReturn())
assert.that(bobNode.deauthoriseContractUpgrade(btx), succeeds()) assert.that(bobNode.deauthoriseContractUpgrade(btx), willReturn())
// The request is expected to be rejected because party B has subsequently deauthorised a previously authorised upgrade. // The request is expected to be rejected because party B has subsequently deauthorised a previously authorised upgrade.
assert.that( assert.that(
aliceNode.initiateDummyContractUpgrade(atx), aliceNode.initiateDummyContractUpgrade(atx),
fails<UnexpectedFlowEndException>()) willThrow<UnexpectedFlowEndException>())
// Party B authorise the contract state upgrade // 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. // Party A initiates contract upgrade flow, expected to succeed this time.
assert.that( assert.that(
aliceNode.initiateDummyContractUpgrade(atx), aliceNode.initiateDummyContractUpgrade(atx),
succeedsWith( willReturn(
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<CordaRuntimeException>())
// 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<CordaRuntimeException>())
// 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(
aliceNode.hasDummyContractUpgradeTransaction() aliceNode.hasDummyContractUpgradeTransaction()
and bobNode.hasDummyContractUpgradeTransaction())) and bobNode.hasDummyContractUpgradeTransaction()))
} }
@ -164,7 +124,7 @@ class ContractUpgradeFlowTest : WithContracts, WithFinality {
assert.that(aliceNode.getBaseStateFromVault(), hasContractState(isA<Cash.State>(anything))) assert.that(aliceNode.getBaseStateFromVault(), hasContractState(isA<Cash.State>(anything)))
// Starts contract upgrade flow. // 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. // Get contract state from the vault.
val upgradedState = aliceNode.getCashStateFromVault() val upgradedState = aliceNode.getCashStateFromVault()
@ -182,7 +142,7 @@ class ContractUpgradeFlowTest : WithContracts, WithFinality {
addCommand(CashV2.Move(), alice.owningKey) addCommand(CashV2.Move(), alice.owningKey)
} }
assert.that(aliceNode.finalise(spendUpgradedTx), succeeds()) assert.that(aliceNode.finalise(spendUpgradedTx), willReturn())
assert.that(aliceNode.getCashStateFromVault(), hasContractState(equalTo(movedState))) assert.that(aliceNode.getCashStateFromVault(), hasContractState(equalTo(movedState)))
} }
@ -208,45 +168,12 @@ class ContractUpgradeFlowTest : WithContracts, WithFinality {
override fun verify(tx: LedgerTransaction) {} override fun verify(tx: LedgerTransaction) {}
} }
@StartableByRPC
class FinalityInvoker(private val transaction: SignedTransaction,
private val extraRecipients: Set<Party>) : FlowLogic<SignedTransaction>() {
@Suspendable
override fun call(): SignedTransaction = subFlow(FinalityFlow(transaction, extraRecipients))
}
//region RPC DSL
private fun RPCDriverDSL.startProxy(node: StartedNode<MockNode>, user: User): CordaRPCOps {
return startRpcClient<CordaRPCOps>(
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<FinalityInvoker>(),
startFlow<ContractUpgradeFlow.Initiate<*, *>>(),
startFlow<ContractUpgradeFlow.Authorise>(),
startFlow<ContractUpgradeFlow.Deauthorise>()
))
//endregion
//region Operations //region Operations
private fun StartedNode<*>.initiateDummyContractUpgrade(tx: SignedTransaction) = private fun StartedNode<*>.initiateDummyContractUpgrade(tx: SignedTransaction) =
initiateContractUpgrade(tx, DummyContractV2::class) initiateContractUpgrade(tx, DummyContractV2::class)
private fun StartedNode<*>.authoriseDummyContractUpgrade(tx: SignedTransaction) = private fun StartedNode<*>.authoriseDummyContractUpgrade(tx: SignedTransaction) =
authoriseContractUpgrade(tx, DummyContractV2::class) 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 //endregion
//region Matchers //region Matchers

View File

@ -2,8 +2,8 @@ package net.corda.core.flows
import com.natpryce.hamkrest.and import com.natpryce.hamkrest.and
import com.natpryce.hamkrest.assertion.assert import com.natpryce.hamkrest.assertion.assert
import net.corda.core.flows.matchers.fails import net.corda.core.flows.matchers.flow.willThrow
import net.corda.core.flows.matchers.succeedsWith import net.corda.core.flows.matchers.flow.willReturn
import net.corda.core.flows.mixins.WithFinality import net.corda.core.flows.mixins.WithFinality
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.SignedTransaction
@ -42,9 +42,9 @@ class FinalityFlowTests : WithFinality {
assert.that( assert.that(
aliceNode.finalise(stx), aliceNode.finalise(stx),
succeedsWith( willReturn(
requiredSignatures(1) requiredSignatures(1)
and transactionVisibleTo(bobNode))) and transactionVisibleTo(bobNode)))
} }
@Test @Test
@ -54,7 +54,7 @@ class FinalityFlowTests : WithFinality {
assert.that( assert.that(
aliceNode.finalise(stx), aliceNode.finalise(stx),
fails<IllegalArgumentException>()) willThrow<IllegalArgumentException>())
} }
private fun StartedNode<*>.signCashTransactionWith(other: Party): SignedTransaction { private fun StartedNode<*>.signCashTransactionWith(other: Party): SignedTransaction {

View File

@ -4,7 +4,7 @@ import co.paralleluniverse.fibers.Suspendable
import com.natpryce.hamkrest.assertion.assert import com.natpryce.hamkrest.assertion.assert
import com.natpryce.hamkrest.equalTo import com.natpryce.hamkrest.equalTo
import com.natpryce.hamkrest.isA 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.flows.mixins.WithMockNet
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.utilities.UntrustworthyData import net.corda.core.utilities.UntrustworthyData
@ -60,7 +60,7 @@ class ReceiveMultipleFlowTests : WithMockNet {
assert.that( assert.that(
nodes[0].startFlowAndRunNetwork(initiatingFlow), nodes[0].startFlowAndRunNetwork(initiatingFlow),
succeedsWith(isA(equalTo(answer)))) willReturn(isA(equalTo(answer))))
} }
@Test @Test
@ -72,7 +72,7 @@ class ReceiveMultipleFlowTests : WithMockNet {
assert.that( assert.that(
nodes[0].startFlowAndRunNetwork(ParallelAlgorithmMap(nodes[1].info.singleIdentity(), nodes[2].info.singleIdentity())), nodes[0].startFlowAndRunNetwork(ParallelAlgorithmMap(nodes[1].info.singleIdentity(), nodes[2].info.singleIdentity())),
succeedsWith(equalTo(doubleValue * stringValue.length))) willReturn(equalTo(doubleValue * stringValue.length)))
} }
@Test @Test
@ -84,7 +84,7 @@ class ReceiveMultipleFlowTests : WithMockNet {
assert.that( assert.that(
nodes[0].startFlowAndRunNetwork(ParallelAlgorithmList(nodes[1].info.singleIdentity(), nodes[2].info.singleIdentity())), 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) { class ParallelAlgorithmMap(doubleMember: Party, stringMember: Party) : AlgorithmDefinition(doubleMember, stringMember) {

View File

@ -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 <T> succeeds() = object : Matcher<FlowStateMachine<T>> {
override val description: String = "is a flow that succeeds"
override fun invoke(actual: FlowStateMachine<T>): 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 <T> succeedsWith(successMatcher: Matcher<T>) = object : Matcher<FlowStateMachine<out T>> {
override val description: String = "is a flow that succeeds with a value that ${successMatcher.description}"
override fun invoke(actual: FlowStateMachine<out T>): 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 <reified E: Exception> fails(failureMatcher: Matcher<E>) = object : Matcher<FlowStateMachine<*>> {
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 <reified E: Exception> fails() = object : Matcher<FlowStateMachine<*>> {
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}")
}
}
}

View File

@ -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 <T> willReturn() = object : Matcher<Future<T>> {
override val description: String = "is a future that will succeed"
override fun invoke(actual: Future<T>): 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 <T> willSucceedWithResult(successMatcher: Matcher<T>) = object : Matcher<Future<out T>> {
override val description: String = "is a future that will succeed with a value that ${successMatcher.description}"
override fun invoke(actual: Future<out T>): 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 <reified E: Exception> willFailWithException(failureMatcher: Matcher<E>) = object : Matcher<Future<*>> {
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 <reified E: Exception> willThrow() = object : Matcher<Future<*>> {
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}")
}
}
}

View File

@ -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 <T> rpcSucceeds() = object : Matcher<FlowHandle<T>> {
override val description: String = "is an RPC flow handle that succeeds"
override fun invoke(actual: FlowHandle<T>): 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 <T> rpcSucceedsWith(successMatcher: Matcher<T>) = object : Matcher<FlowHandle<T>> {
override val description: String = "is an RPC flow handle that succeeds with a value that ${successMatcher.description}"
override fun invoke(actual: FlowHandle<T>): 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 <reified E: Exception> rpcFails(failureMatcher: Matcher<E>) = object : Matcher<FlowHandle<*>> {
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 <reified E: Exception> rpcFails() = object : Matcher<FlowHandle<*>> {
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}")
}
}
}

View File

@ -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 <T> willReturn() = has(FlowStateMachine<T>::resultFuture, willReturn())
/**
* Matches a Flow that succeeds with a result matched by the given matcher
*/
fun <T> willReturn(successMatcher: Matcher<T>) = has(
FlowStateMachine<out T>::resultFuture,
willSucceedWithResult(successMatcher))
/**
* Matches a Flow that fails, with an exception matched by the given matcher.
*/
inline fun <reified E: Exception> willThrow(failureMatcher: Matcher<E>) = has(
FlowStateMachine<*>::resultFuture,
willFailWithException(failureMatcher))
/**
* Matches a Flow that fails, with an exception of the specified type.
*/
inline fun <reified E: Exception> willThrow() = has(
FlowStateMachine<*>::resultFuture,
willThrow<E>())

View File

@ -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 <T> willReturn() = has(FlowHandle<T>::returnValue, willReturn())
/**
* Matches a flow handle that succeeds with a result matched by the given matcher
*/
fun <T> willReturn(successMatcher: Matcher<T>) = has(FlowHandle<out T>::returnValue, willSucceedWithResult(successMatcher))
/**
* Matches a flow handle that fails, with an exception matched by the given matcher.
*/
inline fun <reified E: Exception> willThrow(failureMatcher: Matcher<E>) = has(
FlowHandle<*>::returnValue,
willFailWithException(failureMatcher))
/**
* Matches a flow handle that fails, with an exception of the specified type.
*/
inline fun <reified E: Exception> willThrow() = has(
FlowHandle<*>::returnValue,
willThrow<E>())

View File

@ -21,10 +21,8 @@ import kotlin.reflect.KClass
*/ */
interface WithContracts : WithMockNet { interface WithContracts : WithMockNet {
val magicNumber: Int
//region Generators //region Generators
fun createDummyContract(owner: PartyAndReference, vararg others: PartyAndReference) = fun createDummyContract(owner: PartyAndReference, magicNumber: Int = 0, vararg others: PartyAndReference) =
DummyContract.generateInitial( DummyContract.generateInitial(
magicNumber, magicNumber,
mockNet.defaultNotaryIdentity, mockNet.defaultNotaryIdentity,
@ -33,21 +31,11 @@ interface WithContracts : WithMockNet {
//region //region
//region Operations //region Operations
fun StartedNode<*>.createConfidentialIdentity(party: Party) = database.transaction { fun StartedNode<*>.signDummyContract(owner: PartyAndReference, magicNumber: Int = 0, vararg others: PartyAndReference) =
services.keyManagementService.freshKeyAndCert( services.signDummyContract(owner, magicNumber, *others).andRunNetwork()
services.myInfo.legalIdentitiesAndCerts.single { it.name == party.name },
false)
}
fun StartedNode<*>.verifyAndRegister(identity: PartyAndCertificate) = database.transaction { fun ServiceHub.signDummyContract(owner: PartyAndReference, magicNumber: Int = 0, vararg others: PartyAndReference) =
services.identityService.verifyAndRegisterIdentity(identity) signInitialTransaction(createDummyContract(owner, magicNumber, *others))
}
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 StartedNode<*>.collectSignatures(ptx: SignedTransaction) = fun StartedNode<*>.collectSignatures(ptx: SignedTransaction) =
startFlowAndRunNetwork(CollectSignaturesFlow(ptx, emptySet())) startFlowAndRunNetwork(CollectSignaturesFlow(ptx, emptySet()))

View File

@ -1,9 +1,12 @@
package net.corda.core.flows.mixins package net.corda.core.flows.mixins
import co.paralleluniverse.fibers.Suspendable
import com.natpryce.hamkrest.Matcher import com.natpryce.hamkrest.Matcher
import com.natpryce.hamkrest.equalTo import com.natpryce.hamkrest.equalTo
import net.corda.core.flows.ContractUpgradeFlowTest import net.corda.core.flows.ContractUpgradeFlowTest
import net.corda.core.flows.FinalityFlow 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.identity.Party
import net.corda.core.messaging.CordaRPCOps import net.corda.core.messaging.CordaRPCOps
import net.corda.core.messaging.startFlow import net.corda.core.messaging.startFlow
@ -22,7 +25,7 @@ interface WithFinality : WithMockNet {
} }
fun CordaRPCOps.finalise(stx: SignedTransaction, vararg parties: Party) = fun CordaRPCOps.finalise(stx: SignedTransaction, vararg parties: Party) =
startFlow(ContractUpgradeFlowTest::FinalityInvoker, stx, parties.toSet()) startFlow(::FinalityInvoker, stx, parties.toSet())
.andRunNetwork() .andRunNetwork()
//endregion //endregion
@ -33,4 +36,11 @@ interface WithFinality : WithMockNet {
equalTo(actual)(other.getValidatedTransaction(actual)) equalTo(actual)(other.getValidatedTransaction(actual))
} }
//endregion //endregion
@StartableByRPC
class FinalityInvoker(private val transaction: SignedTransaction,
private val extraRecipients: Set<Party>) : FlowLogic<SignedTransaction>() {
@Suspendable
override fun call(): SignedTransaction = subFlow(FinalityFlow(transaction, extraRecipients))
}
} }

View File

@ -4,6 +4,8 @@ import com.natpryce.hamkrest.*
import net.corda.core.contracts.ContractState import net.corda.core.contracts.ContractState
import net.corda.core.flows.FlowLogic import net.corda.core.flows.FlowLogic
import net.corda.core.identity.CordaX500Name 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.internal.FlowStateMachine
import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.SignedTransaction
import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.TransactionBuilder
@ -33,10 +35,7 @@ interface WithMockNet {
/** /**
* Run the mock network before proceeding * Run the mock network before proceeding
*/ */
fun <T: Any> T.andRunNetwork(): T { fun <T: Any> T.andRunNetwork(): T = apply { mockNet.runNetwork() }
mockNet.runNetwork()
return this
}
//region Operations //region Operations
/** /**
@ -62,6 +61,16 @@ interface WithMockNet {
*/ */
fun <T> StartedNode<*>.startFlowAndRunNetwork(logic: FlowLogic<T>): FlowStateMachine<T> = fun <T> StartedNode<*>.startFlowAndRunNetwork(logic: FlowLogic<T>): FlowStateMachine<T> =
startFlow(logic).andRunNetwork() 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 //endregion
//region Matchers //region Matchers