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 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<FetchDataFlow.HashNotFound>(
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<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.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()

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 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<UnexpectedFlowEndException>())
willThrow<UnexpectedFlowEndException>())
// 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<UnexpectedFlowEndException>())
willThrow<UnexpectedFlowEndException>())
// 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<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(
willReturn(
aliceNode.hasDummyContractUpgradeTransaction()
and bobNode.hasDummyContractUpgradeTransaction()))
}
@ -164,7 +124,7 @@ class ContractUpgradeFlowTest : WithContracts, WithFinality {
assert.that(aliceNode.getBaseStateFromVault(), hasContractState(isA<Cash.State>(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<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
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

View File

@ -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<IllegalArgumentException>())
willThrow<IllegalArgumentException>())
}
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.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) {

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 {
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()))

View File

@ -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<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.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: Any> T.andRunNetwork(): T {
mockNet.runNetwork()
return this
}
fun <T: Any> T.andRunNetwork(): T = apply { mockNet.runNetwork() }
//region Operations
/**
@ -62,6 +61,16 @@ interface WithMockNet {
*/
fun <T> StartedNode<*>.startFlowAndRunNetwork(logic: FlowLogic<T>): FlowStateMachine<T> =
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