Skip to content

Commit

Permalink
Added tests to make sure the platform version is correctly available
Browse files Browse the repository at this point in the history
shamsasari committed Nov 27, 2017
1 parent 2ceb628 commit 4ca54b7
Showing 12 changed files with 125 additions and 85 deletions.
3 changes: 1 addition & 2 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -6,8 +6,7 @@ buildscript {
// Our version: bump this on release.
ext.corda_release_version = "3.0-SNAPSHOT"
// Increment this on any release that changes public APIs anywhere in the Corda platform
// TODO This is going to be difficult until we have a clear separation throughout the code of what is public and what is internal
ext.corda_platform_version = 2
ext.corda_platform_version = constants.getProperty("platformVersion")
ext.gradle_plugins_version = constants.getProperty("gradlePluginsVersion")

// Dependency versions. Can run 'gradle dependencyUpdates' to find new versions of things.
Original file line number Diff line number Diff line change
@@ -74,7 +74,7 @@ public void done() {
}

private void copyFinanceCordapp() {
Path cordappsDir = (factory.baseDirectory(notaryConfig).resolve("cordapps"));
Path cordappsDir = (factory.baseDirectory(notaryConfig).resolve(NodeProcess.CORDAPPS_DIR_NAME));
try {
Files.createDirectories(cordappsDir);
} catch (IOException ex) {
Original file line number Diff line number Diff line change
@@ -86,7 +86,7 @@ class StandaloneCordaRPClientTest {
}

private fun copyFinanceCordapp() {
val cordappsDir = (factory.baseDirectory(notaryConfig) / "cordapps").createDirectories()
val cordappsDir = (factory.baseDirectory(notaryConfig) / NodeProcess.CORDAPPS_DIR_NAME).createDirectories()
// Find the finance jar file for the smoke tests of this module
val financeJar = Paths.get("build", "resources", "smokeTest").list {
it.filter { "corda-finance" in it.toString() }.toList().single()
1 change: 1 addition & 0 deletions constants.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
gradlePluginsVersion=2.0.9
kotlinVersion=1.1.60
platformVersion=2
guavaVersion=21.0
bouncycastleVersion=1.57
typesafeConfigVersion=1.3.1
75 changes: 75 additions & 0 deletions core/src/smoke-test/kotlin/net/corda/core/NodeVersioningTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package net.corda.core

import co.paralleluniverse.fibers.Suspendable
import net.corda.core.flows.FlowLogic
import net.corda.core.flows.StartableByRPC
import net.corda.core.identity.CordaX500Name
import net.corda.core.internal.*
import net.corda.core.messaging.startFlow
import net.corda.core.utilities.getOrThrow
import net.corda.nodeapi.User
import net.corda.smoketesting.NodeConfig
import net.corda.smoketesting.NodeProcess
import net.corda.testing.common.internal.ProjectStructure
import org.assertj.core.api.Assertions.assertThat
import org.junit.Test
import java.nio.file.Paths
import java.util.*
import java.util.concurrent.atomic.AtomicInteger
import java.util.jar.JarFile
import kotlin.streams.toList

class NodeVersioningTest {
private companion object {
val user = User("user1", "test", permissions = setOf("ALL"))
val port = AtomicInteger(15100)

val expectedPlatformVersion = (ProjectStructure.projectRootDir / "constants.properties").read {
val constants = Properties()
constants.load(it)
constants.getProperty("platformVersion").toInt()
}
}

private val factory = NodeProcess.Factory()

private val aliceConfig = NodeConfig(
legalName = CordaX500Name(organisation = "Alice Corp", locality = "Madrid", country = "ES"),
p2pPort = port.andIncrement,
rpcPort = port.andIncrement,
webPort = port.andIncrement,
isNotary = false,
users = listOf(user)
)

@Test
fun `platform version in manifest file`() {
val manifest = JarFile(factory.cordaJar.toFile()).manifest
assertThat(manifest.mainAttributes.getValue("Corda-Platform-Version").toInt()).isEqualTo(expectedPlatformVersion)
}

@Test
fun `platform version from RPC`() {
val cordappsDir = (factory.baseDirectory(aliceConfig) / NodeProcess.CORDAPPS_DIR_NAME).createDirectories()
// Find the jar file for the smoke tests of this module
val selfCordapp = Paths.get("build", "libs").list {
it.filter { "-smokeTests" in it.toString() }.toList().single()
}
selfCordapp.copyToDirectory(cordappsDir)

factory.create(aliceConfig).use { alice ->
alice.connect().use {
val rpc = it.proxy
assertThat(rpc.protocolVersion).isEqualTo(expectedPlatformVersion)
assertThat(rpc.nodeInfo().platformVersion).isEqualTo(expectedPlatformVersion)
assertThat(rpc.startFlow(NodeVersioningTest::GetPlatformVersionFlow).returnValue.getOrThrow()).isEqualTo(expectedPlatformVersion)
}
}
}

@StartableByRPC
class GetPlatformVersionFlow : FlowLogic<Int>() {
@Suspendable
override fun call(): Int = serviceHub.myInfo.platformVersion
}
}
Original file line number Diff line number Diff line change
@@ -14,6 +14,7 @@ import net.corda.core.utilities.unwrap
import net.corda.nodeapi.User
import net.corda.smoketesting.NodeConfig
import net.corda.smoketesting.NodeProcess
import net.corda.smoketesting.NodeProcess.Companion.CORDAPPS_DIR_NAME
import org.assertj.core.api.Assertions.assertThat
import org.junit.Test
import java.nio.file.Paths
@@ -22,7 +23,6 @@ import kotlin.streams.toList

class CordappSmokeTest {
private companion object {
private const val CORDAPPS_DIR_NAME = "cordapps"
val user = User("user1", "test", permissions = setOf("ALL"))
val port = AtomicInteger(15100)
}
@@ -38,7 +38,6 @@ class CordappSmokeTest {
users = listOf(user)
)


@Test
fun `FlowContent appName returns the filename of the CorDapp jar`() {
val cordappsDir = (factory.baseDirectory(aliceConfig) / CORDAPPS_DIR_NAME).createDirectories()
Original file line number Diff line number Diff line change
@@ -10,7 +10,7 @@ import net.corda.node.internal.NodeStartup
import net.corda.node.services.Permissions.Companion.startFlow
import net.corda.nodeapi.User
import net.corda.testing.ALICE
import net.corda.testing.ProjectStructure.projectRootDir
import net.corda.testing.common.internal.ProjectStructure.projectRootDir
import net.corda.testing.driver.driver
import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.assertThatThrownBy
Original file line number Diff line number Diff line change
@@ -190,11 +190,13 @@ fun <M : Any> MessagingService.onNext(topic: String, sessionId: Long): CordaFutu
return messageFuture
}

fun MessagingService.send(topic: String, sessionID: Long, payload: Any, to: MessageRecipients, uuid: UUID = UUID.randomUUID())
= send(TopicSession(topic, sessionID), payload, to, uuid)
fun MessagingService.send(topic: String, sessionID: Long, payload: Any, to: MessageRecipients, uuid: UUID = UUID.randomUUID()) {
send(TopicSession(topic, sessionID), payload, to, uuid)
}

fun MessagingService.send(topicSession: TopicSession, payload: Any, to: MessageRecipients, uuid: UUID = UUID.randomUUID(), retryId: Long? = null)
= send(createMessage(topicSession, payload.serialize().bytes, uuid), to, retryId)
fun MessagingService.send(topicSession: TopicSession, payload: Any, to: MessageRecipients, uuid: UUID = UUID.randomUUID(), retryId: Long? = null) {
send(createMessage(topicSession, payload.serialize().bytes, uuid), to, retryId)
}

interface MessageHandlerRegistration

Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
package net.corda.node.services.messaging

import net.corda.core.concurrent.CordaFuture
import net.corda.core.crypto.generateKeyPair
import net.corda.core.internal.concurrent.doneFuture
import net.corda.core.internal.concurrent.openFuture
import net.corda.core.utilities.NetworkHostAndPort
import net.corda.node.services.RPCUserService
import net.corda.node.services.RPCUserServiceImpl
@@ -27,13 +24,13 @@ import org.junit.Rule
import org.junit.Test
import org.junit.rules.TemporaryFolder
import java.net.ServerSocket
import java.util.concurrent.BlockingQueue
import java.util.concurrent.LinkedBlockingQueue
import java.util.concurrent.TimeUnit.MILLISECONDS
import kotlin.concurrent.thread
import kotlin.test.assertEquals
import kotlin.test.assertNull

//TODO This needs to be merged into P2PMessagingTest as that creates a more realistic environment
class ArtemisMessagingTests {
companion object {
const val TOPIC = "platform.self"
@@ -54,30 +51,26 @@ class ArtemisMessagingTests {
private lateinit var config: NodeConfiguration
private lateinit var database: CordaPersistence
private lateinit var userService: RPCUserService
private lateinit var networkMapRegistrationFuture: CordaFuture<Unit>
private var messagingClient: P2PMessagingClient? = null
private var messagingServer: ArtemisMessagingServer? = null

private lateinit var networkMapCache: NetworkMapCacheImpl

@Before
fun setUp() {
val baseDirectory = temporaryFolder.root.toPath()
userService = RPCUserServiceImpl(emptyList())
config = testNodeConfiguration(
baseDirectory = baseDirectory,
baseDirectory = temporaryFolder.root.toPath(),
myLegalName = ALICE.name)
LogHelper.setLevel(PersistentUniquenessProvider::class)
database = configureDatabase(makeTestDataSourceProperties(), DatabaseConfig(), rigorousMock())
networkMapRegistrationFuture = doneFuture(Unit)
networkMapCache = NetworkMapCacheImpl(PersistentNetworkMapCache(database), rigorousMock())
}

@After
fun cleanUp() {
messagingClient?.stop()
messagingServer?.stop()
messagingClient = null
messagingServer = null
database.close()
LogHelper.reset(PersistentUniquenessProvider::class)
}
@@ -120,9 +113,7 @@ class ArtemisMessagingTests {

@Test
fun `client should be able to send message to itself`() {
val receivedMessages = LinkedBlockingQueue<Message>()

val messagingClient = createAndStartClientAndServer(receivedMessages)
val (messagingClient, receivedMessages) = createAndStartClientAndServer()
val message = messagingClient.createMessage(TOPIC, data = "first msg".toByteArray())
messagingClient.send(message, messagingClient.myAddress)

@@ -132,76 +123,45 @@ class ArtemisMessagingTests {
}

@Test
fun `client should be able to send message to itself before network map is available, and receive after`() {
val settableFuture = openFuture<Unit>()
networkMapRegistrationFuture = settableFuture

val receivedMessages = LinkedBlockingQueue<Message>()

val messagingClient = createAndStartClientAndServer(receivedMessages)
fun `platform version is included in the message`() {
val (messagingClient, receivedMessages) = createAndStartClientAndServer(platformVersion = 3)
val message = messagingClient.createMessage(TOPIC, data = "first msg".toByteArray())
messagingClient.send(message, messagingClient.myAddress)

settableFuture.set(Unit)
val firstActual: Message = receivedMessages.take()
assertEquals("first msg", String(firstActual.data))
assertNull(receivedMessages.poll(200, MILLISECONDS))
}

@Test
fun `client should be able to send large numbers of messages to itself before network map is available and survive restart, then receive messages`() {
// Crank the iteration up as high as you want... just takes longer to run.
val iterations = 100
networkMapRegistrationFuture = openFuture()

val receivedMessages = LinkedBlockingQueue<Message>()

val messagingClient = createAndStartClientAndServer(receivedMessages)
for (iter in 1..iterations) {
val message = messagingClient.createMessage(TOPIC, data = "first msg $iter".toByteArray())
messagingClient.send(message, messagingClient.myAddress)
}

// Stop client and server and create afresh.
messagingClient.stop()
messagingServer?.stop()

networkMapRegistrationFuture = doneFuture(Unit)

createAndStartClientAndServer(receivedMessages)
for (iter in 1..iterations) {
val firstActual: Message = receivedMessages.take()
assertThat(String(firstActual.data)).isEqualTo("first msg $iter")
}
assertNull(receivedMessages.poll(200, MILLISECONDS))
val received = receivedMessages.take()
assertThat(received.platformVersion).isEqualTo(3)
}

private fun startNodeMessagingClient() {
messagingClient!!.start()
}

private fun createAndStartClientAndServer(receivedMessages: LinkedBlockingQueue<Message>): P2PMessagingClient {
private fun createAndStartClientAndServer(platformVersion: Int = 1): Pair<P2PMessagingClient, BlockingQueue<ReceivedMessage>> {
val receivedMessages = LinkedBlockingQueue<ReceivedMessage>()

createMessagingServer().start()

val messagingClient = createMessagingClient()
val messagingClient = createMessagingClient(platformVersion = platformVersion)
startNodeMessagingClient()
messagingClient.addMessageHandler(TOPIC) { message, _ ->
receivedMessages.add(message)
}
// Run after the handlers are added, otherwise (some of) the messages get delivered and discarded / dead-lettered.
thread { messagingClient.run() }
return messagingClient
thread(isDaemon = true) { messagingClient.run() }

return Pair(messagingClient, receivedMessages)
}

private fun createMessagingClient(server: NetworkHostAndPort = NetworkHostAndPort("localhost", serverPort)): P2PMessagingClient {
private fun createMessagingClient(server: NetworkHostAndPort = NetworkHostAndPort("localhost", serverPort), platformVersion: Int = 1): P2PMessagingClient {
return database.transaction {
P2PMessagingClient(
config,
MOCK_VERSION_INFO,
MOCK_VERSION_INFO.copy(platformVersion = platformVersion),
server,
identity.public,
ServiceAffinityExecutor("ArtemisMessagingTests", 1),
database).apply {
database
).apply {
config.configureWithDevSSLCertificate()
messagingClient = this
}
Original file line number Diff line number Diff line change
@@ -9,7 +9,7 @@ import net.corda.node.internal.NodeStartup
import net.corda.testing.DUMMY_BANK_A
import net.corda.testing.DUMMY_NOTARY
import net.corda.testing.DUMMY_REGULATOR
import net.corda.testing.ProjectStructure.projectRootDir
import net.corda.testing.common.internal.ProjectStructure.projectRootDir
import net.corda.testing.node.NotarySpec
import org.assertj.core.api.Assertions.assertThat
import org.junit.Test
Original file line number Diff line number Diff line change
@@ -21,7 +21,8 @@ class NodeProcess(
private val node: Process,
private val client: CordaRPCClient
) : AutoCloseable {
private companion object {
companion object {
const val CORDAPPS_DIR_NAME = "cordapps"
private val log = contextLogger()
}

@@ -42,9 +43,11 @@ class NodeProcess(
(nodeDir / "artemis").toFile().deleteRecursively()
}

// TODO All use of this factory have duplicate code which is either bundling the calling module or a 3rd party module
// as a CorDapp for the nodes.
class Factory(
private val buildDirectory: Path = Paths.get("build"),
private val cordaJar: Path = Paths.get(this::class.java.getResource("/corda.jar").toURI())
val buildDirectory: Path = Paths.get("build"),
val cordaJar: Path = Paths.get(this::class.java.getResource("/corda.jar").toURI())
) {
private companion object {
val javaPath: Path = Paths.get(System.getProperty("java.home"), "bin", "java")
@@ -71,36 +74,37 @@ class NodeProcess(

val process = startNode(nodeDir)
val client = CordaRPCClient(NetworkHostAndPort("localhost", config.rpcPort))
val user = config.users[0]
waitForNode(process, config, client)
return NodeProcess(config, nodeDir, process, client)
}

val setupExecutor = Executors.newSingleThreadScheduledExecutor()
private fun waitForNode(process: Process, config: NodeConfig, client: CordaRPCClient) {
val executor = Executors.newSingleThreadScheduledExecutor()
try {
setupExecutor.scheduleWithFixedDelay({
executor.scheduleWithFixedDelay({
try {
if (!process.isAlive) {
log.error("Node '${config.commonName}' has died.")
return@scheduleWithFixedDelay
}
val conn = client.start(user.username, user.password)
conn.close()
val rpcConnection = config.users[0].let { client.start(it.username, it.password) }
rpcConnection.close()

// Cancel the "setup" task now that we've created the RPC client.
setupExecutor.shutdown()
executor.shutdown()
} catch (e: Exception) {
log.warn("Node '{}' not ready yet (Error: {})", config.commonName, e.message)
}
}, 5, 1, SECONDS)

val setupOK = setupExecutor.awaitTermination(120, SECONDS)
val setupOK = executor.awaitTermination(120, SECONDS)
check(setupOK && process.isAlive) { "Failed to create RPC connection" }
} catch (e: Exception) {
process.destroyForcibly()
throw e
} finally {
setupExecutor.shutdownNow()
executor.shutdownNow()
}

return NodeProcess(config, nodeDir, process, client)
}

private fun startNode(nodeDir: Path): Process {
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package net.corda.testing
package net.corda.testing.common.internal

import net.corda.core.internal.div
import net.corda.core.internal.isDirectory

0 comments on commit 4ca54b7

Please sign in to comment.