From 507d9ea4ae4b224b946d7898a81a604ea3929ce1 Mon Sep 17 00:00:00 2001 From: Clinton Alexander Date: Fri, 3 Jun 2016 16:36:39 +0100 Subject: [PATCH] Added new integration test for the IRSDemo and refactored the demo to run in integration tests. --- .../kotlin/com/r3corda/node/internal/Node.kt | 2 +- src/main/kotlin/com/r3corda/demos/IRSDemo.kt | 85 ++++++++++++++++--- .../com/r3corda/core/testing/IRSDemoTest.kt | 66 ++++++++++++++ 3 files changed, 138 insertions(+), 15 deletions(-) create mode 100644 src/test/kotlin/com/r3corda/core/testing/IRSDemoTest.kt diff --git a/node/src/main/kotlin/com/r3corda/node/internal/Node.kt b/node/src/main/kotlin/com/r3corda/node/internal/Node.kt index 6c3ddbdda4..f78fa99c71 100644 --- a/node/src/main/kotlin/com/r3corda/node/internal/Node.kt +++ b/node/src/main/kotlin/com/r3corda/node/internal/Node.kt @@ -52,7 +52,7 @@ class ConfigurationException(message: String) : Exception(message) * Listed clientAPI classes are assumed to have to take a single APIServer constructor parameter * @param clock The clock used within the node and by all protocols etc */ -class Node(dir: Path, val p2pAddr: HostAndPort, configuration: NodeConfiguration, +open class Node(dir: Path, val p2pAddr: HostAndPort, configuration: NodeConfiguration, networkMapAddress: NodeInfo?, advertisedServices: Set, clock: Clock = NodeClock(), val clientAPIs: List> = listOf()) : AbstractNode(dir, configuration, networkMapAddress, advertisedServices, clock) { diff --git a/src/main/kotlin/com/r3corda/demos/IRSDemo.kt b/src/main/kotlin/com/r3corda/demos/IRSDemo.kt index 7fef5004d5..49300dbb4a 100644 --- a/src/main/kotlin/com/r3corda/demos/IRSDemo.kt +++ b/src/main/kotlin/com/r3corda/demos/IRSDemo.kt @@ -4,6 +4,7 @@ import com.google.common.net.HostAndPort import com.typesafe.config.ConfigFactory import com.r3corda.core.crypto.Party import com.r3corda.core.logElapsedTime +import com.r3corda.core.messaging.MessagingService import com.r3corda.node.internal.Node import com.r3corda.node.services.config.NodeConfiguration import com.r3corda.node.services.config.NodeConfigurationFromConfig @@ -26,6 +27,7 @@ import com.r3corda.node.services.transactions.SimpleNotaryService import joptsimple.OptionParser import joptsimple.OptionSet import joptsimple.OptionSpec +import joptsimple.OptionSpecBuilder import java.io.DataOutputStream import java.io.File import java.net.HttpURLConnection @@ -33,6 +35,7 @@ import java.net.URL import java.nio.file.Files import java.nio.file.Path import java.nio.file.Paths +import java.time.Clock import java.util.* import kotlin.concurrent.fixedRateTimer import kotlin.system.exitProcess @@ -83,7 +86,28 @@ private class NotSetupException: Throwable { constructor(message: String): super(message) {} } +val messageNetwork = InMemoryMessagingNetwork() + +class DemoNode(messagingService: MessagingService, dir: Path, p2pAddr: HostAndPort, config: NodeConfiguration, + networkMapAddress: NodeInfo?, advertisedServices: Set, + clock: Clock, clientAPIs: List> = listOf()) + : Node(dir, p2pAddr, config, networkMapAddress, advertisedServices, clock, clientAPIs) { + + val messagingService = messagingService + override fun makeMessagingService(): MessagingService { + return messagingService + } + + override fun startMessagingService() { + + } +} + fun main(args: Array) { + exitProcess(runIRSDemo(args)) +} + +fun runIRSDemo(args: Array, useInMemoryMessaging: Boolean = false): Int { val parser = OptionParser() val demoArgs = setupArgs(parser) val options = try { @@ -91,7 +115,7 @@ fun main(args: Array) { } catch (e: Exception) { println(e.message) printHelp() - exitProcess(1) + return 1 } // Suppress the Artemis MQ noise, and activate the demo logging. @@ -114,14 +138,12 @@ fun main(args: Array) { "http://localhost:" + (Node.DEFAULT_PORT + 1) } - if (runTrade(tradeId, host)) { - exitProcess(0) - } else { - exitProcess(1) + if (!runTrade(tradeId, host)) { + return 1 } } else { println("Please provide a trade ID") - exitProcess(1) + return 1 } } else if(role == IRSDemoRole.Date) { val dateStrArgs = options.valuesOf(demoArgs.nonOptions) @@ -133,10 +155,12 @@ fun main(args: Array) { "http://localhost:" + (Node.DEFAULT_PORT + 1) } - runDateChange(dateStr, host) + if(!runDateChange(dateStr)) { + return 1 + } } else { println("Please provide a date") - exitProcess(1) + return 1 } } else { // If these directory and identity file arguments aren't specified then we can assume a default setup and @@ -147,14 +171,14 @@ fun main(args: Array) { } try { - runNode(configureNodeParams(role, demoArgs, options)) + runNode(configureNodeParams(role, demoArgs, options), useInMemoryMessaging) } catch (e: NotSetupException) { println(e.message) - exitProcess(1) + return 1 } - - exitProcess(0) } + + return 0 } private fun setupArgs(parser: OptionParser): DemoArgs { @@ -233,8 +257,11 @@ private fun configureNodeParams(role: IRSDemoRole, args: DemoArgs, options: Opti return nodeParams } -private fun runNode(nodeParams : NodeParams) : Unit { - val node = startNode(nodeParams) +private fun runNode(nodeParams : NodeParams, useInMemoryMessaging: Boolean) : Unit { + val node = when(useInMemoryMessaging) { + true -> startDemoNode(nodeParams) + false -> startNode(nodeParams) + } // Register handlers for the demo AutoOfferProtocol.Handler.register(node) UpdateBusinessDayProtocol.Handler.register(node) @@ -417,6 +444,36 @@ private fun startNode(params : NodeParams) : Node { return node } +private fun startDemoNode(params : NodeParams) : Node { + val config = createNodeConfig(params) + val advertisedServices: Set + val myNetAddr = HostAndPort.fromString(params.address).withDefaultPort(Node.DEFAULT_PORT) + val networkMapId = if (params.mapAddress.equals(params.address)) { + // This node provides network map and notary services + advertisedServices = setOf(NetworkMapService.Type, NotaryService.Type) + null + } else { + advertisedServices = setOf(NodeInterestRates.Type) + + val handle = InMemoryMessagingNetwork.Handle(createNodeAParams().id, params.defaultLegalName) + nodeInfo(handle, params.identityFile, setOf(NetworkMapService.Type, NotaryService.Type)) + } + + val messageService = messageNetwork.createNodeWithID(false, params.id).start().get() + val node = logElapsedTime("Node startup") { DemoNode(messageService, params.dir, myNetAddr, config, networkMapId, + advertisedServices, DemoClock(), + listOf(InterestRateSwapAPI::class.java)).setup().start() } + + // TODO: This should all be replaced by the identity service being updated + // as the network map changes. + val identityFile = params.tradeWithIdentities[0] + val handle = InMemoryMessagingNetwork.Handle(1 - params.id, "Other Node") + val peerId = nodeInfo(handle, identityFile) + node.services.identityService.registerIdentity(peerId.identity) + + return node +} + private fun getRoleDir(role: IRSDemoRole) : Path { when(role) { IRSDemoRole.NodeA -> return Paths.get("nodeA") diff --git a/src/test/kotlin/com/r3corda/core/testing/IRSDemoTest.kt b/src/test/kotlin/com/r3corda/core/testing/IRSDemoTest.kt new file mode 100644 index 0000000000..d922be68fe --- /dev/null +++ b/src/test/kotlin/com/r3corda/core/testing/IRSDemoTest.kt @@ -0,0 +1,66 @@ +package com.r3corda.core.testing + +import com.r3corda.demos.runIRSDemo +import kotlin.concurrent.thread +import kotlin.test.assertEquals +import org.junit.Test +import java.nio.file.Path +import java.nio.file.Paths + +class IRSDemoTest { + @Test fun `runs IRS demo`() { + val dirA = Paths.get("./nodeA") + val dirB = Paths.get("./nodeB") + try { + setupNodeA(dirA) + setupNodeB(dirB) + startNodeA(dirA) + startNodeB(dirB) + runTrade() + runDateChange() + stopNodeA() + stopNodeB() + } finally { + cleanup(dirA) + cleanup(dirB) + } + } +} + +private fun setupNodeA(dir: Path) { + runIRSDemo(arrayOf("--role", "SetupNodeA", "--dir", dir.toString())) +} + +private fun setupNodeB(dir: Path) { + runIRSDemo(arrayOf("--role", "SetupNodeB", "--dir", dir.toString())) +} +private fun startNodeA(dir: Path) { + thread(true, false, null, "NodeA", -1, { runIRSDemo(arrayOf("--role", "NodeA", "--dir", dir.toString()), true) }) + Thread.sleep(15000) +} + +private fun startNodeB(dir: Path) { + thread(true, false, null, "NodeB", -1, { runIRSDemo(arrayOf("--role", "NodeB", "--dir", dir.toString()), true) }) + Thread.sleep(15000) +} + +private fun stopNodeA() { + +} + +private fun stopNodeB() { + +} + +private fun runTrade() { + assertEquals(runIRSDemo(arrayOf("--role", "Trade", "trade1")), 0) +} + +private fun runDateChange() { + assertEquals(runIRSDemo(arrayOf("--role", "Date", "2017-01-02")), 0) +} + +private fun cleanup(dir: Path) { + println("Erasing: " + dir.toString()) + dir.toFile().deleteRecursively() +} \ No newline at end of file