mirror of
https://github.com/corda/corda.git
synced 2025-06-21 16:49:45 +00:00
Fixes relating to testing flows and services.
Fixed issue where Corda services installed in unit tests were not being marked as serialise as singleton. Also the driver now automatically picks up the scanning annotations. This required moving the NodeFactory used in smoke tests into a separate module.
This commit is contained in:
@ -1,48 +0,0 @@
|
||||
package net.corda.kotlin.rpc
|
||||
|
||||
import com.typesafe.config.*
|
||||
import net.corda.core.crypto.commonName
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.nodeapi.User
|
||||
|
||||
class NodeConfig(
|
||||
val party: Party,
|
||||
val p2pPort: Int,
|
||||
val rpcPort: Int,
|
||||
val webPort: Int,
|
||||
val extraServices: List<String>,
|
||||
val users: List<User>,
|
||||
var networkMap: NodeConfig? = null
|
||||
) {
|
||||
companion object {
|
||||
val renderOptions: ConfigRenderOptions = ConfigRenderOptions.defaults().setOriginComments(false)
|
||||
}
|
||||
|
||||
val commonName: String = party.name.commonName
|
||||
|
||||
/*
|
||||
* The configuration object depends upon the networkMap,
|
||||
* which is mutable.
|
||||
*/
|
||||
fun toFileConfig(): Config = ConfigFactory.empty()
|
||||
.withValue("myLegalName", valueFor(party.name.toString()))
|
||||
.withValue("p2pAddress", addressValueFor(p2pPort))
|
||||
.withValue("extraAdvertisedServiceIds", valueFor(extraServices))
|
||||
.withFallback(optional("networkMapService", networkMap, { c, n ->
|
||||
c.withValue("address", addressValueFor(n.p2pPort))
|
||||
.withValue("legalName", valueFor(n.party.name.toString()))
|
||||
}))
|
||||
.withValue("webAddress", addressValueFor(webPort))
|
||||
.withValue("rpcAddress", addressValueFor(rpcPort))
|
||||
.withValue("rpcUsers", valueFor(users.map(User::toMap).toList()))
|
||||
.withValue("useTestClock", valueFor(true))
|
||||
|
||||
fun toText(): String = toFileConfig().root().render(renderOptions)
|
||||
|
||||
private fun <T> valueFor(any: T): ConfigValue? = ConfigValueFactory.fromAnyRef(any)
|
||||
private fun addressValueFor(port: Int) = valueFor("localhost:$port")
|
||||
private inline fun <T> optional(path: String, obj: T?, body: (Config, T) -> Config): Config {
|
||||
val config = ConfigFactory.empty()
|
||||
return if (obj == null) config else body(config, obj).atPath(path)
|
||||
}
|
||||
}
|
@ -1,107 +0,0 @@
|
||||
package net.corda.kotlin.rpc
|
||||
|
||||
import com.google.common.net.HostAndPort
|
||||
import net.corda.client.rpc.CordaRPCClient
|
||||
import net.corda.client.rpc.CordaRPCConnection
|
||||
import net.corda.core.utilities.loggerFor
|
||||
import java.io.File
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.Paths
|
||||
import java.util.concurrent.Executors
|
||||
import java.util.concurrent.TimeUnit.SECONDS
|
||||
import kotlin.test.*
|
||||
|
||||
class NodeProcess(
|
||||
val config: NodeConfig,
|
||||
val nodeDir: Path,
|
||||
private val node: Process,
|
||||
private val client: CordaRPCClient
|
||||
) : AutoCloseable {
|
||||
private companion object {
|
||||
val log = loggerFor<NodeProcess>()
|
||||
val javaPath: Path = Paths.get(System.getProperty("java.home"), "bin", "java")
|
||||
val corda = File(this::class.java.getResource("/corda.jar").toURI())
|
||||
val buildDir: Path = Paths.get(System.getProperty("build.dir"))
|
||||
val capsuleDir: Path = buildDir.resolve("capsule")
|
||||
}
|
||||
|
||||
fun connect(): CordaRPCConnection {
|
||||
val user = config.users[0]
|
||||
return client.start(user.username, user.password)
|
||||
}
|
||||
|
||||
override fun close() {
|
||||
log.info("Stopping node '${config.commonName}'")
|
||||
node.destroy()
|
||||
if (!node.waitFor(60, SECONDS)) {
|
||||
log.warn("Node '${config.commonName}' has not shutdown correctly")
|
||||
node.destroyForcibly()
|
||||
}
|
||||
|
||||
log.info("Deleting Artemis directories, because they're large!")
|
||||
nodeDir.resolve("artemis").toFile().deleteRecursively()
|
||||
}
|
||||
|
||||
class Factory(val nodesDir: Path) {
|
||||
init {
|
||||
assertTrue(nodesDir.toFile().forceDirectory(), "Directory '$nodesDir' does not exist")
|
||||
}
|
||||
|
||||
fun create(config: NodeConfig): NodeProcess {
|
||||
val nodeDir = Files.createTempDirectory(nodesDir, config.commonName)
|
||||
log.info("Node directory: {}", nodeDir)
|
||||
|
||||
val confFile = nodeDir.resolve("node.conf").toFile()
|
||||
confFile.writeText(config.toText())
|
||||
|
||||
val process = startNode(nodeDir)
|
||||
val client = CordaRPCClient(HostAndPort.fromParts("localhost", config.rpcPort))
|
||||
val user = config.users[0]
|
||||
|
||||
val setupExecutor = Executors.newSingleThreadScheduledExecutor()
|
||||
try {
|
||||
setupExecutor.scheduleWithFixedDelay({
|
||||
try {
|
||||
if (!process.isAlive) {
|
||||
log.error("Node '${config.commonName}' has died.")
|
||||
return@scheduleWithFixedDelay
|
||||
}
|
||||
val conn = client.start(user.username, user.password)
|
||||
conn.close()
|
||||
|
||||
// Cancel the "setup" task now that we've created the RPC client.
|
||||
setupExecutor.shutdown()
|
||||
} catch (e: Exception) {
|
||||
log.warn("Node '{}' not ready yet (Error: {})", config.commonName, e.message)
|
||||
}
|
||||
}, 5, 1, SECONDS)
|
||||
|
||||
val setupOK = setupExecutor.awaitTermination(120, SECONDS)
|
||||
assertTrue(setupOK && process.isAlive, "Failed to create RPC connection")
|
||||
} catch (e: Exception) {
|
||||
process.destroyForcibly()
|
||||
throw e
|
||||
} finally {
|
||||
setupExecutor.shutdownNow()
|
||||
}
|
||||
|
||||
return NodeProcess(config, nodeDir, process, client)
|
||||
}
|
||||
|
||||
private fun startNode(nodeDir: Path): Process {
|
||||
val builder = ProcessBuilder()
|
||||
.command(javaPath.toString(), "-jar", corda.path)
|
||||
.directory(nodeDir.toFile())
|
||||
|
||||
builder.environment().putAll(mapOf(
|
||||
"CAPSULE_CACHE_DIR" to capsuleDir.toString()
|
||||
))
|
||||
|
||||
return builder.start()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun File.forceDirectory(): Boolean = this.isDirectory || this.mkdirs()
|
||||
|
@ -2,17 +2,11 @@ package net.corda.kotlin.rpc
|
||||
|
||||
import com.google.common.hash.Hashing
|
||||
import com.google.common.hash.HashingInputStream
|
||||
import java.io.FilterInputStream
|
||||
import java.io.InputStream
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.Paths
|
||||
import java.time.Duration.ofSeconds
|
||||
import java.util.Currency
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
import kotlin.test.*
|
||||
import net.corda.client.rpc.CordaRPCConnection
|
||||
import net.corda.client.rpc.notUsed
|
||||
import net.corda.core.contracts.*
|
||||
import net.corda.core.contracts.DOLLARS
|
||||
import net.corda.core.contracts.POUNDS
|
||||
import net.corda.core.contracts.SWISS_FRANCS
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.getOrThrow
|
||||
import net.corda.core.identity.Party
|
||||
@ -20,27 +14,34 @@ import net.corda.core.messaging.CordaRPCOps
|
||||
import net.corda.core.messaging.StateMachineUpdate
|
||||
import net.corda.core.messaging.startFlow
|
||||
import net.corda.core.messaging.startTrackedFlow
|
||||
import net.corda.core.seconds
|
||||
import net.corda.core.serialization.OpaqueBytes
|
||||
import net.corda.core.sizedInputStreamAndHash
|
||||
import net.corda.core.utilities.DUMMY_NOTARY
|
||||
import net.corda.core.utilities.loggerFor
|
||||
import net.corda.flows.CashIssueFlow
|
||||
import net.corda.nodeapi.User
|
||||
import net.corda.smoketesting.NodeConfig
|
||||
import net.corda.smoketesting.NodeProcess
|
||||
import org.apache.commons.io.output.NullOutputStream
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import java.io.FilterInputStream
|
||||
import java.io.InputStream
|
||||
import java.util.*
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertFalse
|
||||
import kotlin.test.assertNotEquals
|
||||
|
||||
class StandaloneCordaRPClientTest {
|
||||
private companion object {
|
||||
val log = loggerFor<StandaloneCordaRPClientTest>()
|
||||
val buildDir: Path = Paths.get(System.getProperty("build.dir"))
|
||||
val nodesDir: Path = buildDir.resolve("nodes")
|
||||
val user = User("user1", "test", permissions = setOf("ALL"))
|
||||
val factory = NodeProcess.Factory(nodesDir)
|
||||
val port = AtomicInteger(15000)
|
||||
const val attachmentSize = 2116
|
||||
const val timeout = 60L
|
||||
val timeout = 60.seconds
|
||||
}
|
||||
|
||||
private lateinit var notary: NodeProcess
|
||||
@ -59,7 +60,7 @@ class StandaloneCordaRPClientTest {
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
notary = factory.create(notaryConfig)
|
||||
notary = NodeProcess.Factory().create(notaryConfig)
|
||||
connection = notary.connect()
|
||||
rpcProxy = connection.proxy
|
||||
notaryIdentity = fetchNotaryIdentity()
|
||||
@ -91,7 +92,7 @@ class StandaloneCordaRPClientTest {
|
||||
@Test
|
||||
fun `test starting flow`() {
|
||||
rpcProxy.startFlow(::CashIssueFlow, 127.POUNDS, OpaqueBytes.of(0), notaryIdentity, notaryIdentity)
|
||||
.returnValue.getOrThrow(ofSeconds(timeout))
|
||||
.returnValue.getOrThrow(timeout)
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -104,7 +105,7 @@ class StandaloneCordaRPClientTest {
|
||||
log.info("Flow>> $msg")
|
||||
++trackCount
|
||||
}
|
||||
handle.returnValue.getOrThrow(ofSeconds(timeout))
|
||||
handle.returnValue.getOrThrow(timeout)
|
||||
assertNotEquals(0, trackCount)
|
||||
}
|
||||
|
||||
@ -128,7 +129,7 @@ class StandaloneCordaRPClientTest {
|
||||
|
||||
// Now issue some cash
|
||||
rpcProxy.startFlow(::CashIssueFlow, 513.SWISS_FRANCS, OpaqueBytes.of(0), notaryIdentity, notaryIdentity)
|
||||
.returnValue.getOrThrow(ofSeconds(timeout))
|
||||
.returnValue.getOrThrow(timeout)
|
||||
assertEquals(1, updateCount)
|
||||
}
|
||||
|
||||
@ -145,7 +146,7 @@ class StandaloneCordaRPClientTest {
|
||||
|
||||
// Now issue some cash
|
||||
rpcProxy.startFlow(::CashIssueFlow, 629.POUNDS, OpaqueBytes.of(0), notaryIdentity, notaryIdentity)
|
||||
.returnValue.getOrThrow(ofSeconds(timeout))
|
||||
.returnValue.getOrThrow(timeout)
|
||||
assertNotEquals(0, updateCount)
|
||||
|
||||
// Check that this cash exists in the vault
|
||||
|
Reference in New Issue
Block a user