Added new integration test for the IRSDemo and refactored the demo to

run in integration tests.
This commit is contained in:
Clinton Alexander 2016-06-03 16:36:39 +01:00 committed by Andras Slemmer
parent 9639768069
commit 507d9ea4ae
3 changed files with 138 additions and 15 deletions

View File

@ -52,7 +52,7 @@ class ConfigurationException(message: String) : Exception(message)
* Listed clientAPI classes are assumed to have to take a single APIServer constructor parameter * 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 * @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<ServiceType>, networkMapAddress: NodeInfo?, advertisedServices: Set<ServiceType>,
clock: Clock = NodeClock(), clock: Clock = NodeClock(),
val clientAPIs: List<Class<*>> = listOf()) : AbstractNode(dir, configuration, networkMapAddress, advertisedServices, clock) { val clientAPIs: List<Class<*>> = listOf()) : AbstractNode(dir, configuration, networkMapAddress, advertisedServices, clock) {

View File

@ -4,6 +4,7 @@ import com.google.common.net.HostAndPort
import com.typesafe.config.ConfigFactory import com.typesafe.config.ConfigFactory
import com.r3corda.core.crypto.Party import com.r3corda.core.crypto.Party
import com.r3corda.core.logElapsedTime import com.r3corda.core.logElapsedTime
import com.r3corda.core.messaging.MessagingService
import com.r3corda.node.internal.Node import com.r3corda.node.internal.Node
import com.r3corda.node.services.config.NodeConfiguration import com.r3corda.node.services.config.NodeConfiguration
import com.r3corda.node.services.config.NodeConfigurationFromConfig import com.r3corda.node.services.config.NodeConfigurationFromConfig
@ -26,6 +27,7 @@ import com.r3corda.node.services.transactions.SimpleNotaryService
import joptsimple.OptionParser import joptsimple.OptionParser
import joptsimple.OptionSet import joptsimple.OptionSet
import joptsimple.OptionSpec import joptsimple.OptionSpec
import joptsimple.OptionSpecBuilder
import java.io.DataOutputStream import java.io.DataOutputStream
import java.io.File import java.io.File
import java.net.HttpURLConnection import java.net.HttpURLConnection
@ -33,6 +35,7 @@ import java.net.URL
import java.nio.file.Files import java.nio.file.Files
import java.nio.file.Path import java.nio.file.Path
import java.nio.file.Paths import java.nio.file.Paths
import java.time.Clock
import java.util.* import java.util.*
import kotlin.concurrent.fixedRateTimer import kotlin.concurrent.fixedRateTimer
import kotlin.system.exitProcess import kotlin.system.exitProcess
@ -83,7 +86,28 @@ private class NotSetupException: Throwable {
constructor(message: String): super(message) {} constructor(message: String): super(message) {}
} }
val messageNetwork = InMemoryMessagingNetwork()
class DemoNode(messagingService: MessagingService, dir: Path, p2pAddr: HostAndPort, config: NodeConfiguration,
networkMapAddress: NodeInfo?, advertisedServices: Set<ServiceType>,
clock: Clock, clientAPIs: List<Class<*>> = 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<String>) { fun main(args: Array<String>) {
exitProcess(runIRSDemo(args))
}
fun runIRSDemo(args: Array<String>, useInMemoryMessaging: Boolean = false): Int {
val parser = OptionParser() val parser = OptionParser()
val demoArgs = setupArgs(parser) val demoArgs = setupArgs(parser)
val options = try { val options = try {
@ -91,7 +115,7 @@ fun main(args: Array<String>) {
} catch (e: Exception) { } catch (e: Exception) {
println(e.message) println(e.message)
printHelp() printHelp()
exitProcess(1) return 1
} }
// Suppress the Artemis MQ noise, and activate the demo logging. // Suppress the Artemis MQ noise, and activate the demo logging.
@ -114,14 +138,12 @@ fun main(args: Array<String>) {
"http://localhost:" + (Node.DEFAULT_PORT + 1) "http://localhost:" + (Node.DEFAULT_PORT + 1)
} }
if (runTrade(tradeId, host)) { if (!runTrade(tradeId, host)) {
exitProcess(0) return 1
} else {
exitProcess(1)
} }
} else { } else {
println("Please provide a trade ID") println("Please provide a trade ID")
exitProcess(1) return 1
} }
} else if(role == IRSDemoRole.Date) { } else if(role == IRSDemoRole.Date) {
val dateStrArgs = options.valuesOf(demoArgs.nonOptions) val dateStrArgs = options.valuesOf(demoArgs.nonOptions)
@ -133,10 +155,12 @@ fun main(args: Array<String>) {
"http://localhost:" + (Node.DEFAULT_PORT + 1) "http://localhost:" + (Node.DEFAULT_PORT + 1)
} }
runDateChange(dateStr, host) if(!runDateChange(dateStr)) {
return 1
}
} else { } else {
println("Please provide a date") println("Please provide a date")
exitProcess(1) return 1
} }
} else { } else {
// If these directory and identity file arguments aren't specified then we can assume a default setup and // 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<String>) {
} }
try { try {
runNode(configureNodeParams(role, demoArgs, options)) runNode(configureNodeParams(role, demoArgs, options), useInMemoryMessaging)
} catch (e: NotSetupException) { } catch (e: NotSetupException) {
println(e.message) println(e.message)
exitProcess(1) return 1
} }
exitProcess(0)
} }
return 0
} }
private fun setupArgs(parser: OptionParser): DemoArgs { private fun setupArgs(parser: OptionParser): DemoArgs {
@ -233,8 +257,11 @@ private fun configureNodeParams(role: IRSDemoRole, args: DemoArgs, options: Opti
return nodeParams return nodeParams
} }
private fun runNode(nodeParams : NodeParams) : Unit { private fun runNode(nodeParams : NodeParams, useInMemoryMessaging: Boolean) : Unit {
val node = startNode(nodeParams) val node = when(useInMemoryMessaging) {
true -> startDemoNode(nodeParams)
false -> startNode(nodeParams)
}
// Register handlers for the demo // Register handlers for the demo
AutoOfferProtocol.Handler.register(node) AutoOfferProtocol.Handler.register(node)
UpdateBusinessDayProtocol.Handler.register(node) UpdateBusinessDayProtocol.Handler.register(node)
@ -417,6 +444,36 @@ private fun startNode(params : NodeParams) : Node {
return node return node
} }
private fun startDemoNode(params : NodeParams) : Node {
val config = createNodeConfig(params)
val advertisedServices: Set<ServiceType>
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 { private fun getRoleDir(role: IRSDemoRole) : Path {
when(role) { when(role) {
IRSDemoRole.NodeA -> return Paths.get("nodeA") IRSDemoRole.NodeA -> return Paths.get("nodeA")

View File

@ -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()
}