CORDA-2345: Updated docs to use the new TestCordapp API, rather than the old scan cordapp packages (#4491)

Also made some improvements to the API, especially for Java users.
This commit is contained in:
Shams Asari 2019-01-03 17:57:28 +00:00 committed by GitHub
parent ad1a96fefb
commit f590300cdf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 143 additions and 347 deletions

View File

@ -311,70 +311,6 @@ For example:
For Contracts that are annotated with ``@NoConstraintPropagation``, the platform requires that the Transaction Builder specifies
an actual constraint for the output states (the ``AutomaticPlaceholderConstraint`` can't be used) .
Testing
-------
Since all tests involving transactions now require attachments it is also required to load the correct attachments
for tests. Unit test environments in JVM ecosystems tend to use class directories rather than JARs, and so CorDapp JARs
typically aren't built for testing. Requiring this would add significant complexity to the build systems of Corda
and CorDapps, so the test suite has a set of convenient functions to generate CorDapps from package names or
to specify JAR URLs in the case that the CorDapp(s) involved in testing already exist. You can also just use
``AlwaysAcceptAttachmentConstraint`` in your tests to disable the constraints mechanism.
MockNetwork/MockNode
********************
The simplest way to ensure that a vanilla instance of a MockNode generates the correct CorDapps is to use the
``cordappPackages`` constructor parameter (Kotlin) or the ``setCordappPackages`` method on ``MockNetworkParameters`` (Java)
when creating the MockNetwork. This will cause the ``AbstractNode`` to use the named packages as sources for CorDapps. All files
within those packages will be zipped into a JAR and added to the attachment store and loaded as CorDapps by the
``CordappLoader``.
An example of this usage would be:
.. sourcecode:: java
class SomeTestClass {
MockNetwork network = null;
@Before
void setup() {
network = new MockNetwork(new MockNetworkParameters().setCordappPackages(Arrays.asList("com.domain.cordapp")))
}
... // Your tests go here
}
MockServices
************
If your test uses a ``MockServices`` directly you can instantiate it using a constructor that takes a list of packages
to use as CorDapps using the ``cordappPackages`` parameter.
.. sourcecode:: java
MockServices mockServices = new MockServices(Arrays.asList("com.domain.cordapp"))
However - there is an easier way! If your unit tests are in the same package as the contract code itself, then you
can use the no-args constructor of ``MockServices``. The package to be scanned for CorDapps will be the same as the
the package of the class that constructed the object. This is a convenient default.
Driver
******
The driver takes a parameter called ``extraCordappPackagesToScan`` which is a list of packages to use as CorDapps.
.. sourcecode:: java
driver(new DriverParameters().setExtraCordappPackagesToScan(Arrays.asList("com.domain.cordapp"))) ...
Full Nodes
**********
When testing against full nodes simply place your CorDapp into the cordapps directory of the node.
Debugging
---------
If an attachment constraint cannot be resolved, a ``MissingContractAttachments`` exception is thrown. There are two

View File

@ -22,108 +22,20 @@ A ``MockNetwork`` is created as follows:
.. container:: codeset
.. sourcecode:: kotlin
.. literalinclude:: ../../docs/source/example-code/src/main/kotlin/net/corda/docs/kotlin/MockNetworkTestsTutorial.kt
:language: kotlin
:start-after: DOCSTART 1
:end-before: DOCEND 1
class FlowTests {
private lateinit var mockNet: MockNetwork
.. literalinclude:: ../../docs/source/example-code/src/main/java/net/corda/docs/java/MockNetworkTestsTutorial.java
:language: java
:start-after: DOCSTART 1
:end-before: DOCEND 1
@Before
fun setup() {
network = MockNetwork(listOf("my.cordapp.package", "my.other.cordapp.package"))
}
}
The ``MockNetwork`` requires at a minimum a list of CorDapps to be installed on each ``StartedMockNode``. The CorDapps are looked up on the
classpath by package name, using ``TestCordapp.findCordapp``.
.. sourcecode:: java
public class IOUFlowTests {
private MockNetwork network;
@Before
public void setup() {
network = new MockNetwork(ImmutableList.of("my.cordapp.package", "my.other.cordapp.package"));
}
}
The ``MockNetwork`` requires at a minimum a list of packages. Each package is packaged into a CorDapp JAR and installed
as a CorDapp on each ``StartedMockNode``.
Configuring the ``MockNetwork``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The ``MockNetwork`` is configured automatically. You can tweak its configuration using a ``MockNetworkParameters``
object, or by using named parameters in Kotlin:
.. container:: codeset
.. sourcecode:: kotlin
val network = MockNetwork(
// A list of packages to scan. Any contracts, flows and Corda services within these
// packages will be automatically available to any nodes within the mock network
cordappPackages = listOf("my.cordapp.package", "my.other.cordapp.package"),
// If true then each node will be run in its own thread. This can result in race conditions in your
// code if not carefully written, but is more realistic and may help if you have flows in your app that
// do long blocking operations.
threadPerNode = false,
// The notaries to use on the mock network. By default you get one mock notary and that is usually
// sufficient.
notarySpecs = listOf(MockNetworkNotarySpec(DUMMY_NOTARY_NAME)),
// If true then messages will not be routed from sender to receiver until you use the
// [MockNetwork.runNetwork] method. This is useful for writing single-threaded unit test code that can
// examine the state of the mock network before and after a message is sent, without races and without
// the receiving node immediately sending a response.
networkSendManuallyPumped = false,
// How traffic is allocated in the case where multiple nodes share a single identity, which happens for
// notaries in a cluster. You don't normally ever need to change this: it is mostly useful for testing
// notary implementations.
servicePeerAllocationStrategy = InMemoryMessagingNetwork.ServicePeerAllocationStrategy.Random())
val network2 = MockNetwork(
// A list of packages to scan. Any contracts, flows and Corda services within these
// packages will be automatically available to any nodes within the mock network
listOf("my.cordapp.package", "my.other.cordapp.package"), MockNetworkParameters(
// If true then each node will be run in its own thread. This can result in race conditions in your
// code if not carefully written, but is more realistic and may help if you have flows in your app that
// do long blocking operations.
threadPerNode = false,
// The notaries to use on the mock network. By default you get one mock notary and that is usually
// sufficient.
notarySpecs = listOf(MockNetworkNotarySpec(DUMMY_NOTARY_NAME)),
// If true then messages will not be routed from sender to receiver until you use the
// [MockNetwork.runNetwork] method. This is useful for writing single-threaded unit test code that can
// examine the state of the mock network before and after a message is sent, without races and without
// the receiving node immediately sending a response.
networkSendManuallyPumped = false,
// How traffic is allocated in the case where multiple nodes share a single identity, which happens for
// notaries in a cluster. You don't normally ever need to change this: it is mostly useful for testing
// notary implementations.
servicePeerAllocationStrategy = InMemoryMessagingNetwork.ServicePeerAllocationStrategy.Random())
)
.. sourcecode:: java
MockNetwork network = MockNetwork(
// A list of packages to scan. Any contracts, flows and Corda services within these
// packages will be automatically available to any nodes within the mock network
ImmutableList.of("my.cordapp.package", "my.other.cordapp.package"),
new MockNetworkParameters()
// If true then each node will be run in its own thread. This can result in race conditions in
// your code if not carefully written, but is more realistic and may help if you have flows in
// your app that do long blocking operations.
.setThreadPerNode(false)
// The notaries to use on the mock network. By default you get one mock notary and that is
// usually sufficient.
.setNotarySpecs(ImmutableList.of(new MockNetworkNotarySpec(DUMMY_NOTARY_NAME)))
// If true then messages will not be routed from sender to receiver until you use the
// [MockNetwork.runNetwork] method. This is useful for writing single-threaded unit test code
// that can examine the state of the mock network before and after a message is sent, without
// races and without the receiving node immediately sending a response.
.setNetworkSendManuallyPumped(false)
// How traffic is allocated in the case where multiple nodes share a single identity, which
// happens for notaries in a cluster. You don't normally ever need to change this: it is mostly
// useful for testing notary implementations.
.setServicePeerAllocationStrategy(new InMemoryMessagingNetwork.ServicePeerAllocationStrategy.Random()));
``MockNetworkParameters`` provides other properties for the network which can be tweaked. They default to sensible values if not specified.
Adding nodes to the network
^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -132,73 +44,21 @@ Nodes are created on the ``MockNetwork`` using:
.. container:: codeset
.. sourcecode:: kotlin
.. literalinclude:: ../../docs/source/example-code/src/main/kotlin/net/corda/docs/kotlin/MockNetworkTestsTutorial.kt
:language: kotlin
:start-after: DOCSTART 2
:end-before: DOCEND 2
class FlowTests {
private lateinit var mockNet: MockNetwork
lateinit var nodeA: StartedMockNode
lateinit var nodeB: StartedMockNode
.. literalinclude:: ../../docs/source/example-code/src/main/java/net/corda/docs/java/MockNetworkTestsTutorial.java
:language: java
:start-after: DOCSTART 2
:end-before: DOCEND 2
@Before
fun setup() {
network = MockNetwork(listOf("my.cordapp.package", "my.other.cordapp.package"))
nodeA = network.createPartyNode()
// We can optionally give the node a name.
nodeB = network.createPartyNode(CordaX500Name("Bank B", "London", "GB"))
}
}
.. sourcecode:: java
public class IOUFlowTests {
private MockNetwork network;
private StartedMockNode a;
private StartedMockNode b;
@Before
public void setup() {
network = new MockNetwork(ImmutableList.of("my.cordapp.package", "my.other.cordapp.package"));
nodeA = network.createPartyNode(null);
// We can optionally give the node a name.
nodeB = network.createPartyNode(new CordaX500Name("Bank B", "London", "GB"));
}
}
Nodes added using ``createPartyNode`` are provided a default set of node parameters. However, it is also possible to
provide different parameters to each node using the following methods on ``MockNetwork``:
.. container:: codeset
.. sourcecode:: kotlin
/**
* Create a started node with the given parameters.
*
* @param legalName The node's legal name.
* @param forcedID A unique identifier for the node.
* @param entropyRoot The initial entropy value to use when generating keys. Defaults to an (insecure) random value,
* but can be overridden to cause nodes to have stable or colliding identity/service keys.
* @param configOverrides Add/override the default configuration/behaviour of the node
* @param extraCordappPackages Extra CorDapp packages to add for this node.
*/
@JvmOverloads
fun createNode(legalName: CordaX500Name? = null,
forcedID: Int? = null,
entropyRoot: BigInteger = BigInteger.valueOf(random63BitValue()),
configOverrides: MockNodeConfigOverrides? = null,
extraCordappPackages: List<String> = emptyList()
): StartedMockNode
/** Create a started node with the given parameters. **/
fun createNode(parameters: MockNodeParameters = MockNodeParameters()): StartedMockNode
As you can see above, parameters can be added individually or encapsulated within a ``MockNodeParameters`` object. Of
particular interest are ``configOverrides`` which allow you to override some of the default node
configuration options. Please refer to the ``MockNodeConfigOverrides`` class for details what can currently be overridden.
Also, the ``extraCordappPackages`` parameter allows you to add extra CorDapps to a
specific node. This is useful when you wish for all nodes to load a common CorDapp but for a subset of nodes to load
CorDapps specific to their role in the network.
Nodes added using ``createNode`` are provided a default set of node parameters. However, it is also possible to
provide different parameters to each node using ``MockNodeParameters``. Of particular interest are ``configOverrides`` which allow you to
override some of the default node configuration options. Please refer to the ``MockNodeConfigOverrides`` class for details what can currently
be overridden. Also, the ``additionalCordapps`` parameter allows you to add extra CorDapps to a specific node. This is useful when you wish
for all nodes to load a common CorDapp but for a subset of nodes to load CorDapps specific to their role in the network.
Running the network
^^^^^^^^^^^^^^^^^^^

View File

@ -0,0 +1,35 @@
package net.corda.docs.java;
// DOCSTART 1
import net.corda.core.identity.CordaX500Name;
import net.corda.testing.node.MockNetwork;
import net.corda.testing.node.MockNetworkParameters;
import net.corda.testing.node.StartedMockNode;
import org.junit.After;
import org.junit.Before;
import static java.util.Collections.singletonList;
import static net.corda.testing.node.TestCordapp.findCordapp;
public class MockNetworkTestsTutorial {
private final MockNetwork mockNet = new MockNetwork(new MockNetworkParameters(singletonList(findCordapp("com.mycordapp.package"))));
@After
public void cleanUp() {
mockNet.stopNodes();
}
// DOCEND 1
// DOCSTART 2
private StartedMockNode nodeA;
private StartedMockNode nodeB;
@Before
public void setUp() {
nodeA = mockNet.createNode();
// We can optionally give the node a name.
nodeB = mockNet.createNode(new CordaX500Name("Bank B", "London", "GB"));
}
// DOCEND 2
}

View File

@ -0,0 +1,33 @@
package net.corda.docs.kotlin
// DOCSTART 1
import net.corda.core.identity.CordaX500Name
import net.corda.testing.node.MockNetwork
import net.corda.testing.node.MockNetworkParameters
import net.corda.testing.node.StartedMockNode
import net.corda.testing.node.TestCordapp.Companion.findCordapp
import org.junit.After
import org.junit.Before
class MockNetworkTestsTutorial {
private val mockNet = MockNetwork(MockNetworkParameters(listOf(findCordapp("com.mycordapp.package"))))
@After
fun cleanUp() {
mockNet.stopNodes()
}
// DOCEND 1
// DOCSTART 2
private lateinit var nodeA: StartedMockNode
private lateinit var nodeB: StartedMockNode
@Before
fun setUp() {
nodeA = mockNet.createNode()
// We can optionally give the node a name.
nodeB = mockNet.createNode(CordaX500Name("Bank B", "London", "GB"))
}
// DOCEND 2
}

View File

@ -269,19 +269,10 @@ Here's an example of it in action from ``FixingFlow.Fixer``.
Testing
-------
The ``MockNetwork`` allows the creation of ``MockNode`` instances, which are simplified nodes which can be used for
testing (see :doc:`api-testing`). When creating the ``MockNetwork`` you supply a list of packages to scan for CorDapps.
Make sure the packages you provide include your oracle service, and it automatically be installed in the test nodes.
Then you can create an oracle node on the ``MockNetwork`` and insert any initialisation logic you want to use. In this
case, our ``Oracle`` service is in the ``net.corda.irs.api`` package, so the following test setup will install
the service in each node. Then an oracle node with an oracle service which is initialised with some data is created on
the mock network:
.. literalinclude:: ../../samples/irs-demo/cordapp/src/test/kotlin/net/corda/irs/api/OracleNodeTearOffTests.kt
:language: kotlin
:start-after: DOCSTART 1
:end-before: DOCEND 1
:dedent: 4
The ``MockNetwork`` allows the creation of ``MockNode`` instances, which are simplified nodes which can be used for testing (see :doc:`api-testing`).
When creating the ``MockNetwork`` you supply a list of ``TestCordapp`` objects which point to CorDapps on
the classpath. These CorDapps will be installed on each node on the network. Make sure the packages you provide reference to the CorDapp
containing your oracle service.
You can then write tests on your mock network to verify the nodes interact with your Oracle correctly.

View File

@ -10,6 +10,7 @@ import net.corda.core.identity.Party
import net.corda.core.utilities.unwrap
import net.corda.testing.core.singleIdentity
import net.corda.testing.node.MockNetwork
import net.corda.testing.node.MockNetworkParameters
import net.corda.testing.node.MockNodeParameters
import net.corda.testing.node.StartedMockNode
import org.junit.After
@ -26,7 +27,7 @@ class FlowRegistrationTest {
@Before
fun setup() {
// no cordapps scanned so it can be tested in isolation
mockNetwork = MockNetwork()
mockNetwork = MockNetwork(MockNetworkParameters())
initiator = mockNetwork.createNode(MockNodeParameters(legalName = CordaX500Name("initiator", "Reading", "GB")))
responder = mockNetwork.createNode(MockNodeParameters(legalName = CordaX500Name("responder", "Reading", "GB")))
mockNetwork.runNetwork()

View File

@ -68,7 +68,7 @@ class OracleNodeTearOffTests {
mockNet.stopNodes()
}
// DOCSTART 2
// DOCSTART 1
@Test
fun `verify that the oracle signs the transaction if the interest rate within allowed limit`() {
// Create a partial transaction
@ -93,7 +93,7 @@ class OracleNodeTearOffTests {
// Check that the transaction has been signed by the oracle
assertContains(fix.signers, oracle.owningKey)
}
// DOCEND 2
// DOCEND 1
@Test
fun `verify that the oracle rejects the transaction if the interest rate is outside the allowed limit`() {

View File

@ -10,7 +10,7 @@ class MockNetworkIntegrationTests {
companion object {
@JvmStatic
fun main(args: Array<String>) {
MockNetwork().run {
MockNetwork(MockNetworkParameters()).run {
repeat(2) { createNode() }
runNetwork()
stopNodes()

View File

@ -248,7 +248,9 @@ data class DriverParameters(
val notaryCustomOverrides: Map<String, Any?> = emptyMap(),
val inMemoryDB: Boolean = true,
val cordappsForAllNodes: Collection<TestCordapp>? = null
) {
) {
constructor(cordappsForAllNodes: Collection<TestCordapp>) : this(isDebug = false, cordappsForAllNodes = cordappsForAllNodes)
constructor(
isDebug: Boolean = false,
driverDirectory: Path = Paths.get("build") / "node-driver" / getTimestampAsDirectoryName(),
@ -313,38 +315,6 @@ data class DriverParameters(
cordappsForAllNodes = null
)
constructor(
isDebug: Boolean,
driverDirectory: Path,
portAllocation: PortAllocation,
debugPortAllocation: PortAllocation,
systemProperties: Map<String, String>,
useTestClock: Boolean,
startNodesInProcess: Boolean,
waitForAllNodesToFinish: Boolean,
notarySpecs: List<NotarySpec>,
extraCordappPackagesToScan: List<String>,
jmxPolicy: JmxPolicy,
networkParameters: NetworkParameters,
cordappsForAllNodes: Collection<TestCordapp>? = null
) : this(
isDebug,
driverDirectory,
portAllocation,
debugPortAllocation,
systemProperties,
useTestClock,
startNodesInProcess,
waitForAllNodesToFinish,
notarySpecs,
extraCordappPackagesToScan,
jmxPolicy,
networkParameters,
emptyMap(),
true,
cordappsForAllNodes
)
constructor(
isDebug: Boolean,
driverDirectory: Path,
@ -377,39 +347,6 @@ data class DriverParameters(
cordappsForAllNodes = null
)
constructor(
isDebug: Boolean,
driverDirectory: Path,
portAllocation: PortAllocation,
debugPortAllocation: PortAllocation,
systemProperties: Map<String, String>,
useTestClock: Boolean,
startNodesInProcess: Boolean,
waitForAllNodesToFinish: Boolean,
notarySpecs: List<NotarySpec>,
extraCordappPackagesToScan: List<String>,
jmxPolicy: JmxPolicy,
networkParameters: NetworkParameters,
inMemoryDB: Boolean,
cordappsForAllNodes: Set<TestCordapp>? = null
) : this(
isDebug,
driverDirectory,
portAllocation,
debugPortAllocation,
systemProperties,
useTestClock,
startNodesInProcess,
waitForAllNodesToFinish,
notarySpecs,
extraCordappPackagesToScan,
jmxPolicy,
networkParameters,
emptyMap(),
inMemoryDB,
cordappsForAllNodes
)
fun withIsDebug(isDebug: Boolean): DriverParameters = copy(isDebug = isDebug)
fun withDriverDirectory(driverDirectory: Path): DriverParameters = copy(driverDirectory = driverDirectory)
fun withPortAllocation(portAllocation: PortAllocation): DriverParameters = copy(portAllocation = portAllocation)
@ -419,6 +356,8 @@ data class DriverParameters(
fun withStartNodesInProcess(startNodesInProcess: Boolean): DriverParameters = copy(startNodesInProcess = startNodesInProcess)
fun withWaitForAllNodesToFinish(waitForAllNodesToFinish: Boolean): DriverParameters = copy(waitForAllNodesToFinish = waitForAllNodesToFinish)
fun withNotarySpecs(notarySpecs: List<NotarySpec>): DriverParameters = copy(notarySpecs = notarySpecs)
@Deprecated("extraCordappPackagesToScan does not preserve the original CorDapp's versioning and metadata, which may lead to " +
"misleading results in tests. Use withCordappsForAllNodes instead.")
fun withExtraCordappPackagesToScan(extraCordappPackagesToScan: List<String>): DriverParameters = copy(extraCordappPackagesToScan = extraCordappPackagesToScan)
fun withJmxPolicy(jmxPolicy: JmxPolicy): DriverParameters = copy(jmxPolicy = jmxPolicy)
fun withNetworkParameters(networkParameters: NetworkParameters): DriverParameters = copy(networkParameters = networkParameters)

View File

@ -61,8 +61,7 @@ data class MockNodeParameters(
}
/**
* Immutable builder for configuring a [MockNetwork]. Kotlin users can also use named parameters to the constructor
* of [MockNetwork], which is more convenient.
* Immutable builder for configuring a [MockNetwork].
*
* @property networkSendManuallyPumped If false then messages will not be routed from sender to receiver until you use
* the [MockNetwork.runNetwork] method. This is useful for writing single-threaded unit test code that can examine the
@ -95,6 +94,8 @@ data class MockNetworkParameters(
networkParameters: NetworkParameters
) : this(networkSendManuallyPumped, threadPerNode, servicePeerAllocationStrategy, notarySpecs, networkParameters, emptyList())
constructor(cordappsForAllNodes: Collection<TestCordapp>) : this(threadPerNode = false, cordappsForAllNodes = cordappsForAllNodes)
fun withNetworkParameters(networkParameters: NetworkParameters): MockNetworkParameters = copy(networkParameters = networkParameters)
fun withNetworkSendManuallyPumped(networkSendManuallyPumped: Boolean): MockNetworkParameters = copy(networkSendManuallyPumped = networkSendManuallyPumped)
fun withThreadPerNode(threadPerNode: Boolean): MockNetworkParameters = copy(threadPerNode = threadPerNode)
@ -108,7 +109,8 @@ data class MockNetworkParameters(
threadPerNode: Boolean,
servicePeerAllocationStrategy: InMemoryMessagingNetwork.ServicePeerAllocationStrategy,
notarySpecs: List<MockNetworkNotarySpec>,
networkParameters: NetworkParameters): MockNetworkParameters {
networkParameters: NetworkParameters
): MockNetworkParameters {
return MockNetworkParameters(networkSendManuallyPumped, threadPerNode, servicePeerAllocationStrategy, notarySpecs, networkParameters, emptyList())
}
}
@ -243,9 +245,9 @@ class StartedMockNode private constructor(private val node: TestStartedNode) {
* By default a single notary node is automatically started, which forms part of the network parameters for all the nodes.
* This node is available by calling [defaultNotaryNode].
*
* @property defaultParameters The default parameters for the network. If any of the remaining constructor parameters are specified then
* their values are taken instead of the corresponding value in [defaultParameters].
* @property cordappPackages A [List] of cordapp packages to scan for any cordapp code, e.g. contract verification code, flows and services.
* @property defaultParameters A [MockNetworkParameters] object which contains the same parameters as the constructor, provided
* as a convenience for Java users.
* @property networkSendManuallyPumped If false then messages will not be routed from sender to receiver until you use
* the [MockNetwork.runNetwork] method. This is useful for writing single-threaded unit test code that can examine the
* state of the mock network before and after a message is sent, without races and without the receiving node immediately
@ -279,8 +281,7 @@ open class MockNetwork(
cordappPackages, defaultParameters = parameters
)
@JvmOverloads
constructor(parameters: MockNetworkParameters = MockNetworkParameters()) : this(emptyList(), parameters)
constructor(parameters: MockNetworkParameters) : this(emptyList(), defaultParameters = parameters)
private val internalMockNetwork = InternalMockNetwork(
cordappPackages,
@ -317,7 +318,7 @@ open class MockNetwork(
fun createPartyNode(legalName: CordaX500Name? = null): StartedMockNode = StartedMockNode.create(internalMockNetwork.createPartyNode(legalName))
/** Create a started node with the given parameters. **/
fun createNode(parameters: MockNodeParameters = MockNodeParameters()): StartedMockNode {
fun createNode(parameters: MockNodeParameters): StartedMockNode {
return StartedMockNode.create(internalMockNetwork.createNode(InternalMockNodeParameters(parameters)))
}

View File

@ -6,7 +6,7 @@ import net.corda.testing.driver.NodeParameters
import net.corda.testing.node.internal.TestCordappImpl
/**
* Encapsulates a CorDapp that exists on the current classpath, which can be pulled in for testing. Use [TestCordapp.Factory.findCordapp]
* Encapsulates a CorDapp that exists on the current classpath, which can be pulled in for testing. Use [TestCordapp.findCordapp]
* to locate an existing CorDapp.
*
* @see DriverParameters.cordappsForAllNodes
@ -15,27 +15,25 @@ import net.corda.testing.node.internal.TestCordappImpl
* @see MockNodeParameters.additionalCordapps
*/
@DoNotImplement
interface TestCordapp {
abstract class TestCordapp {
/** The package used to find the CorDapp. This may not be the root package of the CorDapp. */
val scanPackage: String
abstract val scanPackage: String
/** Returns the config for on this CorDapp, defaults to empty if not specified. */
val config: Map<String, Any>
abstract val config: Map<String, Any>
/** Returns a copy of this [TestCordapp] but with the specified CorDapp config. */
fun withConfig(config: Map<String, Any>): TestCordapp
abstract fun withConfig(config: Map<String, Any>): TestCordapp
class Factory {
companion object {
/**
* Scans the current classpath to find the CorDapp that contains the given package. All the CorDapp's metdata present in its
* MANIFEST are inherited. If more than one location containing the package is found then an exception is thrown. An exception
* is also thrown if no CorDapp is found.
*
* @param scanPackage The package name used to find the CorDapp. This does not need to be the root package.
*/
@JvmStatic
fun findCordapp(scanPackage: String): TestCordapp = TestCordappImpl(scanPackage = scanPackage, config = emptyMap())
}
companion object {
/**
* Scans the current classpath to find the CorDapp that contains the given package. All the CorDapp's metdata present in its
* MANIFEST are inherited. If more than one location containing the package is found then an exception is thrown. An exception
* is also thrown if no CorDapp is found.
*
* @param scanPackage The package name used to find the CorDapp. This does not need to be the root package of the CorDapp.
*/
@JvmStatic
fun findCordapp(scanPackage: String): TestCordapp = TestCordappImpl(scanPackage = scanPackage, config = emptyMap())
}
}

View File

@ -32,7 +32,7 @@ data class CustomCordapp(
val classes: Set<Class<*>> = emptySet(),
val signingInfo: SigningInfo? = null,
override val config: Map<String, Any> = emptyMap()
) : TestCordappInternal {
) : TestCordappInternal() {
init {
require(packages.isNotEmpty() || classes.isNotEmpty()) { "At least one package or class must be specified" }
}

View File

@ -13,12 +13,12 @@ import kotlin.streams.toList
/**
* Implementation of the public [TestCordapp] API.
*
* As described in [TestCordapp.Factory.findCordapp], this represents a single CorDapp jar on the current classpath. The [scanPackage] may
* As described in [TestCordapp.findCordapp], this represents a single CorDapp jar on the current classpath. The [scanPackage] may
* be for an external dependency to the project that's using this API, in which case that dependency jar is referenced as is. On the other hand,
* the [scanPackage] may reference a gradle CorDapp project on the local system. In this scenerio the project's "jar" task is executed to
* build the CorDapp jar. This allows us to inherit the CorDapp's MANIFEST information without having to do any extra processing.
*/
data class TestCordappImpl(override val scanPackage: String, override val config: Map<String, Any>) : TestCordappInternal {
data class TestCordappImpl(override val scanPackage: String, override val config: Map<String, Any>) : TestCordappInternal() {
override fun withConfig(config: Map<String, Any>): TestCordappImpl = copy(config = config)
override fun withOnlyJarContents(): TestCordappImpl = copy(config = emptyMap())
@ -27,9 +27,11 @@ data class TestCordappImpl(override val scanPackage: String, override val config
get() {
val jars = TestCordappImpl.findJars(scanPackage)
when (jars.size) {
0 -> throw IllegalArgumentException("Package $scanPackage does not exist")
0 -> throw IllegalArgumentException("There are no CorDapps containing the package $scanPackage on the classpath. Make sure " +
"the package name is correct and that the CorDapp is added as a gradle dependency.")
1 -> return jars.first()
else -> throw IllegalArgumentException("More than one jar found containing package $scanPackage: $jars")
else -> throw IllegalArgumentException("There is more than one CorDapp containing the package $scanPackage on the classpath " +
"$jars. Specify a package name which is unique to the CorDapp.")
}
}

View File

@ -14,11 +14,11 @@ import java.nio.file.StandardCopyOption.REPLACE_EXISTING
*
* @property jarFile The jar file this CorDapp represents. Different CorDapps may point to the same file.
*/
interface TestCordappInternal : TestCordapp {
val jarFile: Path
abstract class TestCordappInternal : TestCordapp() {
abstract val jarFile: Path
/** Return a copy of this TestCordappInternal but without any metadata, such as configs and signing information. */
fun withOnlyJarContents(): TestCordappInternal
abstract fun withOnlyJarContents(): TestCordappInternal
companion object {
fun installCordapps(baseDirectory: Path,

View File

@ -44,9 +44,9 @@ fun cordappForClasses(vararg classes: Class<*>): CustomCordapp = CustomCordapp(p
/**
* Find the single CorDapp jar on the current classpath which contains the given package. This is a convenience method for
* [TestCordapp.Factory.findCordapp] but returns the internal [TestCordappImpl].
* [TestCordapp.findCordapp] but returns the internal [TestCordappImpl].
*/
fun findCordapp(scanPackage: String): TestCordappImpl = TestCordapp.Factory.findCordapp(scanPackage) as TestCordappImpl
fun findCordapp(scanPackage: String): TestCordappImpl = TestCordapp.findCordapp(scanPackage) as TestCordappImpl
fun getCallerClass(directCallerClass: KClass<*>): Class<*>? {
val stackTrace = Throwable().stackTrace

View File

@ -16,7 +16,7 @@ class MockNetworkTest {
@Before
fun setup() {
mockNetwork = MockNetwork()
mockNetwork = MockNetwork(MockNetworkParameters())
}
@After