mirror of
https://github.com/corda/corda.git
synced 2025-06-22 17:09:00 +00:00
CORDA-2503: Added installCordaService to UnstartedMockNode to allow tests with custom test-only services (#4655)
This commit is contained in:
@ -9,6 +9,8 @@ Unreleased
|
|||||||
|
|
||||||
* Updating postgres dependency to 42.2.5
|
* Updating postgres dependency to 42.2.5
|
||||||
|
|
||||||
|
* Test ``CordaService`` s can be installed on mock nodes using ``UnstartedMockNode.installCordaService``.
|
||||||
|
|
||||||
.. _changelog_v4.0:
|
.. _changelog_v4.0:
|
||||||
|
|
||||||
Version 4.0
|
Version 4.0
|
||||||
|
@ -573,7 +573,7 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
|
|||||||
val loadedServices = cordappLoader.cordapps.flatMap { it.services }
|
val loadedServices = cordappLoader.cordapps.flatMap { it.services }
|
||||||
loadedServices.forEach {
|
loadedServices.forEach {
|
||||||
try {
|
try {
|
||||||
installCordaService(flowStarter, it)
|
installCordaService(it)
|
||||||
} catch (e: NoSuchMethodException) {
|
} catch (e: NoSuchMethodException) {
|
||||||
log.error("${it.name}, as a Corda service, must have a constructor with a single parameter of type " +
|
log.error("${it.name}, as a Corda service, must have a constructor with a single parameter of type " +
|
||||||
ServiceHub::class.java.name)
|
ServiceHub::class.java.name)
|
||||||
@ -637,7 +637,7 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
|
|||||||
override fun hashCode() = Objects.hash(serviceHub, flowStarter, serviceInstance)
|
override fun hashCode() = Objects.hash(serviceHub, flowStarter, serviceInstance)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun <T : SerializeAsToken> installCordaService(flowStarter: FlowStarter, serviceClass: Class<T>) {
|
fun <T : SerializeAsToken> installCordaService(serviceClass: Class<T>): T {
|
||||||
serviceClass.requireAnnotation<CordaService>()
|
serviceClass.requireAnnotation<CordaService>()
|
||||||
|
|
||||||
val service = try {
|
val service = try {
|
||||||
@ -659,6 +659,8 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
|
|||||||
|
|
||||||
service.tokenize()
|
service.tokenize()
|
||||||
log.info("Installed ${serviceClass.name} Corda service")
|
log.info("Installed ${serviceClass.name} Corda service")
|
||||||
|
|
||||||
|
return service
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun registerCordappFlows() {
|
private fun registerCordappFlows() {
|
||||||
|
@ -11,6 +11,8 @@ import net.corda.core.internal.uncheckedCast
|
|||||||
import net.corda.core.node.NetworkParameters
|
import net.corda.core.node.NetworkParameters
|
||||||
import net.corda.core.node.NodeInfo
|
import net.corda.core.node.NodeInfo
|
||||||
import net.corda.core.node.ServiceHub
|
import net.corda.core.node.ServiceHub
|
||||||
|
import net.corda.core.node.services.CordaService
|
||||||
|
import net.corda.core.serialization.SerializeAsToken
|
||||||
import net.corda.core.utilities.getOrThrow
|
import net.corda.core.utilities.getOrThrow
|
||||||
import net.corda.node.services.config.NodeConfiguration
|
import net.corda.node.services.config.NodeConfiguration
|
||||||
import net.corda.testing.common.internal.testNetworkParameters
|
import net.corda.testing.common.internal.testNetworkParameters
|
||||||
@ -137,6 +139,15 @@ class UnstartedMockNode private constructor(private val node: InternalMockNetwor
|
|||||||
/** An identifier for the node. By default this is allocated sequentially in a [MockNetwork] **/
|
/** An identifier for the node. By default this is allocated sequentially in a [MockNetwork] **/
|
||||||
val id get() : Int = node.id
|
val id get() : Int = node.id
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Install a custom test-only [CordaService].
|
||||||
|
*
|
||||||
|
* NOTE: There is no need to call this method if the service class is defined in the CorDapp and the [TestCordapp] API is used.
|
||||||
|
*
|
||||||
|
* @return the instance of the service object.
|
||||||
|
*/
|
||||||
|
fun <T : SerializeAsToken> installCordaService(serviceClass: Class<T>): T = node.installCordaService(serviceClass)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Start the node
|
* Start the node
|
||||||
*
|
*
|
||||||
@ -180,6 +191,8 @@ class StartedMockNode private constructor(private val node: TestStartedNode) {
|
|||||||
/**
|
/**
|
||||||
* Manually register an initiating-responder flow pair based on the [FlowLogic] annotations.
|
* Manually register an initiating-responder flow pair based on the [FlowLogic] annotations.
|
||||||
*
|
*
|
||||||
|
* NOTE: There is no need to call this method if the flow pair is defined in the CorDapp and the [TestCordapp] API is used.
|
||||||
|
*
|
||||||
* @param initiatedFlowClass [FlowLogic] class which is annotated with [InitiatedBy].
|
* @param initiatedFlowClass [FlowLogic] class which is annotated with [InitiatedBy].
|
||||||
* @return An [Observable] which emits responder flows each time one is executed.
|
* @return An [Observable] which emits responder flows each time one is executed.
|
||||||
*/
|
*/
|
||||||
|
@ -1,5 +1,16 @@
|
|||||||
package net.corda.testing.node
|
package net.corda.testing.node
|
||||||
|
|
||||||
|
import co.paralleluniverse.fibers.Suspendable
|
||||||
|
import net.corda.core.flows.FlowLogic
|
||||||
|
import net.corda.core.flows.FlowSession
|
||||||
|
import net.corda.core.flows.InitiatedBy
|
||||||
|
import net.corda.core.flows.InitiatingFlow
|
||||||
|
import net.corda.core.identity.Party
|
||||||
|
import net.corda.core.node.AppServiceHub
|
||||||
|
import net.corda.core.node.services.CordaService
|
||||||
|
import net.corda.core.serialization.SingletonSerializeAsToken
|
||||||
|
import net.corda.core.utilities.getOrThrow
|
||||||
|
import net.corda.core.utilities.unwrap
|
||||||
import net.corda.testing.core.*
|
import net.corda.testing.core.*
|
||||||
import org.assertj.core.api.Assertions.*
|
import org.assertj.core.api.Assertions.*
|
||||||
import org.junit.After
|
import org.junit.After
|
||||||
@ -44,4 +55,34 @@ class MockNetworkTest {
|
|||||||
val ex = assertFailsWith<IllegalStateException> { unstarted.started }
|
val ex = assertFailsWith<IllegalStateException> { unstarted.started }
|
||||||
assertThat(ex).hasMessage("Node ID=$NODE_ID is not running")
|
assertThat(ex).hasMessage("Node ID=$NODE_ID is not running")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun installCordaService() {
|
||||||
|
val unstarted = mockNetwork.createUnstartedNode()
|
||||||
|
assertThat(unstarted.installCordaService(TestService::class.java)).isNotNull()
|
||||||
|
val started = unstarted.start()
|
||||||
|
started.registerInitiatedFlow(TestResponder::class.java)
|
||||||
|
val future = started.startFlow(TestInitiator(started.info.singleIdentity()))
|
||||||
|
mockNetwork.runNetwork()
|
||||||
|
assertThat(future.getOrThrow()).isEqualTo(TestService::class.java.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
@CordaService
|
||||||
|
class TestService(services: AppServiceHub) : SingletonSerializeAsToken()
|
||||||
|
|
||||||
|
@InitiatingFlow
|
||||||
|
class TestInitiator(private val party: Party) : FlowLogic<String>() {
|
||||||
|
@Suspendable
|
||||||
|
override fun call(): String {
|
||||||
|
return initiateFlow(party).receive<String>().unwrap { it }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@InitiatedBy(TestInitiator::class)
|
||||||
|
class TestResponder(private val otherSide: FlowSession) : FlowLogic<Unit>() {
|
||||||
|
@Suspendable
|
||||||
|
override fun call() {
|
||||||
|
otherSide.send(serviceHub.cordaService(TestService::class.java).javaClass.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
Reference in New Issue
Block a user