mirror of
https://github.com/corda/corda.git
synced 2025-02-22 10:10:59 +00:00
Added tests to make sure the platform version is correctly available
This commit is contained in:
parent
2ceb6283af
commit
4ca54b73fe
@ -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.
|
||||
|
@ -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) {
|
||||
|
@ -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,5 +1,6 @@
|
||||
gradlePluginsVersion=2.0.9
|
||||
kotlinVersion=1.1.60
|
||||
platformVersion=2
|
||||
guavaVersion=21.0
|
||||
bouncycastleVersion=1.57
|
||||
typesafeConfigVersion=1.3.1
|
@ -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
|
||||
}
|
||||
}
|
@ -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()
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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
|
Loading…
x
Reference in New Issue
Block a user