Added tests to make sure the platform version is correctly available

This commit is contained in:
Shams Asari 2017-11-24 14:32:32 +00:00
parent 2ceb6283af
commit 4ca54b73fe
12 changed files with 125 additions and 85 deletions

View File

@ -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.

View File

@ -74,7 +74,7 @@ public class StandaloneCordaRPCJavaClientTest {
}
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) {

View File

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

View File

@ -1,5 +1,6 @@
gradlePluginsVersion=2.0.9
kotlinVersion=1.1.60
platformVersion=2
guavaVersion=21.0
bouncycastleVersion=1.57
typesafeConfigVersion=1.3.1

View File

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

View File

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

View File

@ -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

View File

@ -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

View File

@ -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,21 +51,19 @@ 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())
}
@ -76,8 +71,6 @@ class ArtemisMessagingTests {
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
}

View File

@ -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

View File

@ -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 {

View File

@ -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