CORDA-2088: Simplified the TestCordapp public API (#4064)

The entry point to the API has been simplified to just requireing a list of packages to scan, with sensible defaults provided for the metadata. Because of the wither methods, having parameters for the metadata (with default values) seems unnecessary. Also the ability to scan just individual classes has been made internal, as it seems unlikely app developers would need that level of control when testing their apps.

TestCordappImpl is a data class and thus acts as a natural key for the Jar caching, where previously the key was the package names. This fixes an issue where it was not possible to create two CorDapp Jars of the same package but different metadata.
This commit is contained in:
Shams Asari
2018-10-15 10:11:18 +01:00
committed by GitHub
parent b769ad80bd
commit 2c9a942e1a
26 changed files with 475 additions and 753 deletions

View File

@ -4,7 +4,6 @@ import co.paralleluniverse.fibers.Suspendable
import net.corda.core.flows.*
import net.corda.core.identity.Party
import net.corda.core.internal.concurrent.transpose
import net.corda.core.internal.packageName
import net.corda.core.messaging.startFlow
import net.corda.core.utilities.getOrThrow
import net.corda.core.utilities.unwrap
@ -12,8 +11,8 @@ import net.corda.testing.core.ALICE_NAME
import net.corda.testing.core.BOB_NAME
import net.corda.testing.core.singleIdentity
import net.corda.testing.driver.DriverParameters
import net.corda.testing.driver.TestCorDapp
import net.corda.testing.driver.driver
import net.corda.testing.node.internal.cordappForClasses
import org.junit.Test
import kotlin.test.assertEquals
@ -46,23 +45,18 @@ class AsymmetricCorDappsTests {
}
@Test
fun noSharedCorDappsWithAsymmetricSpecificClasses() {
fun `no shared cordapps with asymmetric specific classes`() {
driver(DriverParameters(startNodesInProcess = false, cordappsForAllNodes = emptySet())) {
val nodeA = startNode(providedName = ALICE_NAME, additionalCordapps = setOf(TestCorDapp.Factory.create("Szymon CorDapp", "1.0", classes = setOf(Ping::class.java)))).getOrThrow()
val nodeB = startNode(providedName = BOB_NAME, additionalCordapps = setOf(TestCorDapp.Factory.create("Szymon CorDapp", "1.0", classes = setOf(Ping::class.java, Pong::class.java)))).getOrThrow()
val nodeA = startNode(providedName = ALICE_NAME, additionalCordapps = setOf(cordappForClasses(Ping::class.java))).getOrThrow()
val nodeB = startNode(providedName = BOB_NAME, additionalCordapps = setOf(cordappForClasses(Ping::class.java, Pong::class.java))).getOrThrow()
nodeA.rpc.startFlow(::Ping, nodeB.nodeInfo.singleIdentity(), 1).returnValue.getOrThrow()
}
}
@Test
fun sharedCorDappsWithAsymmetricSpecificClasses() {
val resourceName = "cordapp.properties"
val cordappPropertiesResource = this::class.java.getResource(resourceName)
val sharedCordapp = TestCorDapp.Factory.create("shared", "1.0", classes = setOf(Ping::class.java)).plusResource("${AsymmetricCorDappsTests::class.java.packageName}.$resourceName", cordappPropertiesResource)
val cordappForNodeB = TestCorDapp.Factory.create("nodeB_only", "1.0", classes = setOf(Pong::class.java))
fun `shared cordapps with asymmetric specific classes`() {
val sharedCordapp = cordappForClasses(Ping::class.java)
val cordappForNodeB = cordappForClasses(Pong::class.java)
driver(DriverParameters(startNodesInProcess = false, cordappsForAllNodes = setOf(sharedCordapp))) {
val (nodeA, nodeB) = listOf(startNode(providedName = ALICE_NAME), startNode(providedName = BOB_NAME, additionalCordapps = setOf(cordappForNodeB))).transpose().getOrThrow()
@ -71,12 +65,9 @@ class AsymmetricCorDappsTests {
}
@Test
fun sharedCorDappsWithAsymmetricSpecificClassesInProcess() {
val resourceName = "cordapp.properties"
val cordappPropertiesResource = this::class.java.getResource(resourceName)
val sharedCordapp = TestCorDapp.Factory.create("shared", "1.0", classes = setOf(Ping::class.java)).plusResource("${AsymmetricCorDappsTests::class.java.packageName}.$resourceName", cordappPropertiesResource)
val cordappForNodeB = TestCorDapp.Factory.create("nodeB_only", "1.0", classes = setOf(Pong::class.java))
fun `shared cordapps with asymmetric specific classes in process`() {
val sharedCordapp = cordappForClasses(Ping::class.java)
val cordappForNodeB = cordappForClasses(Pong::class.java)
driver(DriverParameters(startNodesInProcess = true, cordappsForAllNodes = setOf(sharedCordapp))) {
val (nodeA, nodeB) = listOf(startNode(providedName = ALICE_NAME), startNode(providedName = BOB_NAME, additionalCordapps = setOf(cordappForNodeB))).transpose().getOrThrow()

View File

@ -1,27 +1,30 @@
package net.corda.node.flows
import net.corda.client.rpc.CordaRPCClient
import net.corda.core.internal.concurrent.transpose
import net.corda.core.internal.div
import net.corda.core.internal.list
import net.corda.core.internal.moveTo
import net.corda.core.internal.readLines
import net.corda.core.messaging.startTrackedFlow
import net.corda.core.utilities.getOrThrow
import net.corda.node.internal.CheckpointIncompatibleException
import net.corda.node.internal.NodeStartup
import net.corda.node.services.Permissions.Companion.invokeRpc
import net.corda.node.services.Permissions.Companion.startFlow
import net.corda.testMessage.Message
import net.corda.testMessage.MessageState
import net.corda.testMessage.*
import net.corda.testing.core.ALICE_NAME
import net.corda.testing.core.BOB_NAME
import net.corda.testing.core.singleIdentity
import net.corda.testing.driver.DriverDSL
import net.corda.testing.driver.DriverParameters
import net.corda.testing.driver.TestCorDapp
import net.corda.testing.driver.driver
import net.corda.testing.node.User
import net.corda.testing.node.TestCordapp
import net.corda.testing.node.internal.ListenProcessDeathException
import net.corda.testing.node.internal.TestCordappDirectories
import net.corda.testing.node.internal.cordappForClasses
import net.test.cordapp.v1.Record
import net.test.cordapp.v1.SendMessageFlow
import org.junit.Test
import java.nio.file.StandardCopyOption.REPLACE_EXISTING
import java.util.*
import java.util.concurrent.TimeUnit
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
@ -30,125 +33,111 @@ import kotlin.test.assertNotNull
class FlowCheckpointVersionNodeStartupCheckTest {
companion object {
val message = Message("Hello world!")
val classes = setOf(net.corda.testMessage.MessageState::class.java,
net.corda.testMessage.MessageContract::class.java,
net.test.cordapp.v1.SendMessageFlow::class.java,
net.corda.testMessage.MessageSchema::class.java,
net.corda.testMessage.MessageSchemaV1::class.java,
net.test.cordapp.v1.Record::class.java)
val user = User("mark", "dadada", setOf(startFlow<SendMessageFlow>(), invokeRpc("vaultQuery"), invokeRpc("vaultTrack")))
val defaultCordapp = cordappForClasses(
MessageState::class.java,
MessageContract::class.java,
SendMessageFlow::class.java,
MessageSchema::class.java,
MessageSchemaV1::class.java,
Record::class.java
)
}
@Test
fun `restart node successfully with suspended flow`() {
val cordapps = setOf(TestCorDapp.Factory.create("testJar", "1.0", classes = classes))
return driver(DriverParameters(isDebug = true, startNodesInProcess = false, inMemoryDB = false, cordappsForAllNodes = cordapps)) {
{
val alice = startNode(rpcUsers = listOf(user), providedName = ALICE_NAME).getOrThrow()
val bob = startNode(rpcUsers = listOf(user), providedName = BOB_NAME).getOrThrow()
alice.stop()
CordaRPCClient(bob.rpcAddress).start(user.username, user.password).use {
val flowTracker = it.proxy.startTrackedFlow(::SendMessageFlow, message, defaultNotaryIdentity, alice.nodeInfo.singleIdentity()).progress
//wait until Bob progresses as far as possible because alice node is off
flowTracker.takeFirst { it == SendMessageFlow.Companion.FINALISING_TRANSACTION.label }.toBlocking().single()
}
bob.stop()
}()
val result = {
//Bob will resume the flow
val alice = startNode(rpcUsers = listOf(user), providedName = ALICE_NAME, customOverrides = mapOf("devMode" to false)).getOrThrow()
startNode(providedName = BOB_NAME, rpcUsers = listOf(user), customOverrides = mapOf("devMode" to false)).getOrThrow()
CordaRPCClient(alice.rpcAddress).start(user.username, user.password).use {
val page = it.proxy.vaultTrack(MessageState::class.java)
if (page.snapshot.states.isNotEmpty()) {
page.snapshot.states.first()
} else {
val r = page.updates.timeout(5, TimeUnit.SECONDS).take(1).toBlocking().single()
if (r.consumed.isNotEmpty()) r.consumed.first() else r.produced.first()
}
}
}()
return driver(parametersForRestartingNodes(listOf(defaultCordapp))) {
createSuspendedFlowInBob(cordapps = emptySet())
// Bob will resume the flow
val alice = startNode(providedName = ALICE_NAME).getOrThrow()
startNode(providedName = BOB_NAME).getOrThrow()
val page = alice.rpc.vaultTrack(MessageState::class.java)
val result = if (page.snapshot.states.isNotEmpty()) {
page.snapshot.states.first()
} else {
val r = page.updates.timeout(5, TimeUnit.SECONDS).take(1).toBlocking().single()
if (r.consumed.isNotEmpty()) r.consumed.first() else r.produced.first()
}
assertNotNull(result)
assertEquals(message, result.state.data.message)
}
}
private fun assertNodeRestartFailure(
cordapps: Set<TestCorDapp>?,
cordappsVersionAtStartup: Set<TestCorDapp>,
cordappsVersionAtRestart: Set<TestCorDapp>,
reuseAdditionalCordappsAtRestart: Boolean,
assertNodeLogs: String
) {
@Test
fun `restart node with incompatible version of suspended flow due to different jar name`() {
driver(parametersForRestartingNodes()) {
val cordapp = defaultCordapp.withName("different-jar-name-test-${UUID.randomUUID()}")
// Create the CorDapp jar file manually first to get hold of the directory that will contain it so that we can
// rename the filename later. The cordappDir, which acts as pointer to the jar file, does not get renamed.
val cordappDir = TestCordappDirectories.getJarDirectory(cordapp)
val cordappJar = cordappDir.list().single()
return driver(DriverParameters(
startNodesInProcess = false, // start nodes in separate processes to ensure CordappLoader is not shared between restarts
inMemoryDB = false, // ensure database is persisted between node restarts so we can keep suspended flow in Bob's node
cordappsForAllNodes = cordapps)
) {
val bobLogFolder = {
val alice = startNode(rpcUsers = listOf(user), providedName = ALICE_NAME, additionalCordapps = cordappsVersionAtStartup).getOrThrow()
val bob = startNode(rpcUsers = listOf(user), providedName = BOB_NAME, additionalCordapps = cordappsVersionAtStartup).getOrThrow()
alice.stop()
CordaRPCClient(bob.rpcAddress).start(user.username, user.password).use {
val flowTracker = it.proxy.startTrackedFlow(::SendMessageFlow, message, defaultNotaryIdentity, alice.nodeInfo.singleIdentity()).progress
// wait until Bob progresses as far as possible because Alice node is offline
flowTracker.takeFirst { it == SendMessageFlow.Companion.FINALISING_TRANSACTION.label }.toBlocking().single()
}
val logFolder = bob.baseDirectory / NodeStartup.LOGS_DIRECTORY_NAME
// SendMessageFlow suspends in Bob node
bob.stop()
logFolder
}()
createSuspendedFlowInBob(setOf(cordapp))
startNode(rpcUsers = listOf(user), providedName = ALICE_NAME, customOverrides = mapOf("devMode" to false),
additionalCordapps = cordappsVersionAtRestart, regenerateCordappsOnStart = !reuseAdditionalCordappsAtRestart).getOrThrow()
// Rename the jar file. TestCordappDirectories caches the location of the jar file but the use of the random
// UUID in the name means there's zero chance of contaminating another test.
cordappJar.moveTo(cordappDir / "renamed-${cordappJar.fileName}")
assertFailsWith(ListenProcessDeathException::class) {
startNode(providedName = BOB_NAME, rpcUsers = listOf(user), customOverrides = mapOf("devMode" to false),
additionalCordapps = cordappsVersionAtRestart, regenerateCordappsOnStart = !reuseAdditionalCordappsAtRestart).getOrThrow()
}
val logFile = bobLogFolder.list { it.filter { it.fileName.toString().endsWith(".log") }.findAny().get() }
val numberOfNodesThatLogged = logFile.readLines { it.filter { assertNodeLogs in it }.count() }
assertEquals(1, numberOfNodesThatLogged)
assertBobFailsToStartWithLogMessage(
setOf(cordapp),
CheckpointIncompatibleException.FlowNotInstalledException(SendMessageFlow::class.java).message
)
}
}
@Test
fun `restart nodes with incompatible version of suspended flow due to different jar name`() {
fun `restart node with incompatible version of suspended flow due to different jar hash`() {
driver(parametersForRestartingNodes()) {
val originalCordapp = defaultCordapp.withName("different-jar-hash-test-${UUID.randomUUID()}")
val originalCordappJar = TestCordappDirectories.getJarDirectory(originalCordapp).list().single()
assertNodeRestartFailure(
emptySet(),
setOf(TestCorDapp.Factory.create("testJar", "1.0", classes = classes)),
setOf(TestCorDapp.Factory.create("testJar2", "1.0", classes = classes)),
false,
CheckpointIncompatibleException.FlowNotInstalledException(SendMessageFlow::class.java).message)
createSuspendedFlowInBob(setOf(originalCordapp))
// The vendor is part of the MANIFEST so changing it is sufficient to change the jar hash
val modifiedCordapp = originalCordapp.withVendor("${originalCordapp.vendor}-modified")
val modifiedCordappJar = TestCordappDirectories.getJarDirectory(modifiedCordapp).list().single()
modifiedCordappJar.moveTo(originalCordappJar, REPLACE_EXISTING)
assertBobFailsToStartWithLogMessage(
setOf(originalCordapp),
// The part of the log message generated by CheckpointIncompatibleException.FlowVersionIncompatibleException
"that is incompatible with the current installed version of"
)
}
}
@Test
fun `restart nodes with incompatible version of suspended flow`() {
assertNodeRestartFailure(
emptySet(),
setOf(TestCorDapp.Factory.create("testJar", "1.0", classes = classes)),
setOf(TestCorDapp.Factory.create("testJar", "1.0", classes = classes + net.test.cordapp.v1.SendMessageFlow::class.java)),
false,
// the part of the log message generated by CheckpointIncompatibleException.FlowVersionIncompatibleException
"that is incompatible with the current installed version of")
private fun DriverDSL.createSuspendedFlowInBob(cordapps: Set<TestCordapp>) {
val (alice, bob) = listOf(ALICE_NAME, BOB_NAME)
.map { startNode(providedName = it, additionalCordapps = cordapps) }
.transpose()
.getOrThrow()
alice.stop()
val flowTracker = bob.rpc.startTrackedFlow(::SendMessageFlow, message, defaultNotaryIdentity, alice.nodeInfo.singleIdentity()).progress
// Wait until Bob progresses as far as possible because Alice node is offline
flowTracker.takeFirst { it == SendMessageFlow.Companion.FINALISING_TRANSACTION.label }.toBlocking().single()
bob.stop()
}
@Test
fun `restart nodes with incompatible version of suspended flow due to different timestamps only`() {
private fun DriverDSL.assertBobFailsToStartWithLogMessage(cordapps: Collection<TestCordapp>, logMessage: String) {
assertFailsWith(ListenProcessDeathException::class) {
startNode(
providedName = BOB_NAME,
customOverrides = mapOf("devMode" to false),
additionalCordapps = cordapps,
regenerateCordappsOnStart = true
).getOrThrow()
}
assertNodeRestartFailure(
emptySet(),
setOf(TestCorDapp.Factory.create("testJar", "1.0", classes = classes)),
setOf(TestCorDapp.Factory.create("testJar", "1.0", classes = classes)),
false,
// the part of the log message generated by CheckpointIncompatibleException.FlowVersionIncompatibleException
"that is incompatible with the current installed version of")
val logDir = baseDirectory(BOB_NAME) / NodeStartup.LOGS_DIRECTORY_NAME
val logFile = logDir.list { it.filter { it.fileName.toString().endsWith(".log") }.findAny().get() }
val matchingLineCount = logFile.readLines { it.filter { line -> logMessage in line }.count() }
assertEquals(1, matchingLineCount)
}
}
private fun parametersForRestartingNodes(cordappsForAllNodes: List<TestCordapp> = emptyList()): DriverParameters {
return DriverParameters(
startNodesInProcess = false, // Start nodes in separate processes to ensure CordappLoader is not shared between restarts
inMemoryDB = false, // Ensure database is persisted between node restarts so we can keep suspended flows
cordappsForAllNodes = cordappsForAllNodes
)
}
}

View File

@ -3,17 +3,11 @@ package net.corda.node.services
import com.nhaarman.mockito_kotlin.doReturn
import com.nhaarman.mockito_kotlin.whenever
import net.corda.core.CordaRuntimeException
import net.corda.core.contracts.Contract
import net.corda.core.contracts.ContractState
import net.corda.core.contracts.PartyAndReference
import net.corda.core.contracts.StateAndRef
import net.corda.core.contracts.StateRef
import net.corda.core.contracts.TransactionState
import net.corda.core.contracts.*
import net.corda.core.cordapp.CordappProvider
import net.corda.core.flows.FlowLogic
import net.corda.core.identity.CordaX500Name
import net.corda.core.identity.Party
import net.corda.core.internal.concurrent.transpose
import net.corda.core.internal.toLedgerTransaction
import net.corda.core.node.NetworkParameters
import net.corda.core.node.ServicesForResolution
@ -21,19 +15,16 @@ import net.corda.core.node.services.AttachmentStorage
import net.corda.core.node.services.IdentityService
import net.corda.core.serialization.SerializationFactory
import net.corda.core.transactions.TransactionBuilder
import net.corda.core.utilities.contextLogger
import net.corda.core.utilities.getOrThrow
import net.corda.node.VersionInfo
import net.corda.node.internal.cordapp.JarScanningCordappLoader
import net.corda.node.internal.cordapp.CordappProviderImpl
import net.corda.node.internal.cordapp.JarScanningCordappLoader
import net.corda.testing.common.internal.testNetworkParameters
import net.corda.testing.core.DUMMY_BANK_A_NAME
import net.corda.testing.core.DUMMY_NOTARY_NAME
import net.corda.testing.core.SerializationEnvironmentRule
import net.corda.testing.core.TestIdentity
import net.corda.testing.driver.DriverDSL
import net.corda.testing.driver.DriverParameters
import net.corda.testing.driver.NodeHandle
import net.corda.testing.driver.driver
import net.corda.testing.internal.MockCordappConfigProvider
import net.corda.testing.internal.rigorousMock
@ -59,7 +50,6 @@ class AttachmentLoadingTests {
private val appContext get() = provider.getAppContext(cordapp)
private companion object {
private val logger = contextLogger()
val isolatedJAR = AttachmentLoadingTests::class.java.getResource("isolated.jar")!!
const val ISOLATED_CONTRACT_ID = "net.corda.finance.contracts.isolated.AnotherDummyContract"
@ -70,12 +60,6 @@ class AttachmentLoadingTests {
.asSubclass(FlowLogic::class.java)
val DUMMY_BANK_A = TestIdentity(DUMMY_BANK_A_NAME, 40).party
val DUMMY_NOTARY = TestIdentity(DUMMY_NOTARY_NAME, 20).party
private fun DriverDSL.createTwoNodes(): List<NodeHandle> {
return listOf(
startNode(providedName = bankAName),
startNode(providedName = bankBName)
).transpose().getOrThrow()
}
}
private val services = object : ServicesForResolution {

View File

@ -61,11 +61,13 @@ class JarScanningCordappLoader private constructor(private val cordappJarPaths:
/**
* Creates a CordappLoader from multiple directories.
*
* @param corDappDirectories Directories used to scan for CorDapp JARs.
* @param cordappDirs Directories used to scan for CorDapp JARs.
*/
fun fromDirectories(corDappDirectories: Iterable<Path>, versionInfo: VersionInfo = VersionInfo.UNKNOWN, extraCordapps: List<CordappImpl> = emptyList()): JarScanningCordappLoader {
logger.info("Looking for CorDapps in ${corDappDirectories.distinct().joinToString(", ", "[", "]")}")
val paths = corDappDirectories.distinct().flatMap(this::jarUrlsInDirectory).map { it.restricted() }
fun fromDirectories(cordappDirs: Collection<Path>,
versionInfo: VersionInfo = VersionInfo.UNKNOWN,
extraCordapps: List<CordappImpl> = emptyList()): JarScanningCordappLoader {
logger.info("Looking for CorDapps in ${cordappDirs.distinct().joinToString(", ", "[", "]")}")
val paths = cordappDirs.distinct().flatMap(this::jarUrlsInDirectory).map { it.restricted() }
return JarScanningCordappLoader(paths, versionInfo, extraCordapps)
}

View File

@ -5,7 +5,7 @@ import net.corda.core.internal.cordapp.CordappImpl.Info.Companion.UNKNOWN_VALUE
import java.util.jar.Attributes
import java.util.jar.Manifest
fun createTestManifest(name: String, title: String, version: String, vendor: String): Manifest {
fun createTestManifest(name: String, title: String, version: String, vendor: String, targetVersion: Int): Manifest {
val manifest = Manifest()
// Mandatory manifest attribute. If not present, all other entries are silently skipped.
@ -20,6 +20,7 @@ fun createTestManifest(name: String, title: String, version: String, vendor: Str
manifest["Implementation-Title"] = title
manifest["Implementation-Version"] = version
manifest["Implementation-Vendor"] = vendor
manifest["Target-Platform-Version"] = targetVersion.toString()
return manifest
}

View File

@ -2,14 +2,12 @@ package net.corda.node.internal.cordapp
import co.paralleluniverse.fibers.Suspendable
import net.corda.core.flows.*
import net.corda.core.internal.packageName
import net.corda.node.VersionInfo
import net.corda.node.cordapp.CordappLoader
import net.corda.testing.node.internal.cordappsForPackages
import net.corda.testing.node.internal.getTimestampAsDirectoryName
import net.corda.testing.node.internal.packageInDirectory
import net.corda.testing.node.internal.TestCordappDirectories
import net.corda.testing.node.internal.cordappForPackages
import org.assertj.core.api.Assertions.assertThat
import org.junit.Test
import java.nio.file.Path
import java.nio.file.Paths
@InitiatingFlow
@ -38,7 +36,6 @@ class DummyRPCFlow : FlowLogic<Unit>() {
class JarScanningCordappLoaderTest {
private companion object {
const val testScanPackage = "net.corda.node.internal.cordapp"
const val isolatedContractId = "net.corda.finance.contracts.isolated.AnotherDummyContract"
const val isolatedFlowName = "net.corda.finance.contracts.isolated.IsolatedDummyFlow\$Initiator"
}
@ -70,32 +67,18 @@ class JarScanningCordappLoaderTest {
@Test
fun `flows are loaded by loader`() {
val loader = cordappLoaderForPackages(listOf(testScanPackage))
val dir = TestCordappDirectories.getJarDirectory(cordappForPackages(javaClass.packageName))
val loader = JarScanningCordappLoader.fromDirectories(listOf(dir))
val actual = loader.cordapps.toTypedArray()
// One cordapp from this source tree. In gradle it will also pick up the node jar.
assertThat(actual.size == 0 || actual.size == 1).isTrue()
assertThat(loader.cordapps).isNotEmpty
val actualCordapp = actual.single { !it.initiatedFlows.isEmpty() }
val actualCordapp = loader.cordapps.single { !it.initiatedFlows.isEmpty() }
assertThat(actualCordapp.initiatedFlows).first().hasSameClassAs(DummyFlow::class.java)
assertThat(actualCordapp.rpcFlows).first().hasSameClassAs(DummyRPCFlow::class.java)
assertThat(actualCordapp.schedulableFlows).first().hasSameClassAs(DummySchedulableFlow::class.java)
}
@Test
fun `duplicate packages are ignored`() {
val loader = cordappLoaderForPackages(listOf(testScanPackage, testScanPackage))
val cordapps = loader.cordapps.filter { LoaderTestFlow::class.java in it.initiatedFlows }
assertThat(cordapps).hasSize(1)
}
@Test
fun `sub-packages are ignored`() {
val loader = cordappLoaderForPackages(listOf("net.corda.core", testScanPackage))
val cordapps = loader.cordapps.filter { LoaderTestFlow::class.java in it.initiatedFlows }
assertThat(cordapps).hasSize(1)
}
// This test exists because the appClassLoader is used by serialisation and we need to ensure it is the classloader
// being used internally. Later iterations will use a classloader per cordapp and this test can be retired.
@Test
@ -159,16 +142,4 @@ class JarScanningCordappLoaderTest {
val loader = JarScanningCordappLoader.fromJarUrls(listOf(jar), VersionInfo.UNKNOWN.copy(platformVersion = 2))
assertThat(loader.cordapps).hasSize(1)
}
private fun cordappLoaderForPackages(packages: Iterable<String>): CordappLoader {
val cordapps = cordappsForPackages(packages)
return testDirectory().let { directory ->
cordapps.packageInDirectory(directory)
JarScanningCordappLoader.fromDirectories(listOf(directory))
}
}
private fun testDirectory(): Path {
return Paths.get("build", getTimestampAsDirectoryName())
}
}

View File

@ -15,11 +15,7 @@ import net.corda.node.services.statemachine.StaffedFlowHospital.MedicalRecord.Ke
import net.corda.testing.core.ALICE_NAME
import net.corda.testing.core.BOB_NAME
import net.corda.testing.core.singleIdentity
import net.corda.testing.driver.TestCorDapp
import net.corda.testing.node.internal.InternalMockNetwork
import net.corda.testing.node.internal.InternalMockNodeParameters
import net.corda.testing.node.internal.TestStartedNode
import net.corda.testing.node.internal.startFlow
import net.corda.testing.node.internal.*
import org.assertj.core.api.Assertions.assertThat
import org.junit.After
import org.junit.Test
@ -38,8 +34,7 @@ class FinalityHandlerTest {
// CorDapp. Bob's FinalityHandler will error when validating the tx.
mockNet = InternalMockNetwork()
val assertCordapp = TestCorDapp.Factory.create("net.corda.finance.contracts.asset", "1.0").plusPackage("net.corda.finance.contracts.asset")
val alice = mockNet.createNode(InternalMockNodeParameters(legalName = ALICE_NAME, additionalCordapps = setOf(assertCordapp)))
val alice = mockNet.createNode(InternalMockNodeParameters(legalName = ALICE_NAME, additionalCordapps = setOf(FINANCE_CORDAPP)))
var bob = mockNet.createNode(InternalMockNodeParameters(legalName = BOB_NAME))