mirror of
https://github.com/corda/corda.git
synced 2025-06-19 15:43:52 +00:00
Corda Behave: compendium of minor changes and improvements (#3008)
* Compendium of minor changes and improvements: - build fat behave-jar so can run scenarios from shell scripts (from TC) - additional run script to execute basic scenarios (for TC) - default staging path shortened to "corda" (removed deps) - toned down logging (info -> debug) - fixed all compiler warnings - fixed couple of bugs in startup checking steps - base scenarios use variables declared using Examples parameterization * Added missing braces * Changes to address PR feedback. * Mark underlying Cucumber libraries for future de-coupling.
This commit is contained in:
1
.idea/compiler.xml
generated
1
.idea/compiler.xml
generated
@ -10,6 +10,7 @@
|
|||||||
<module name="bank-of-corda-demo_integrationTest" target="1.8" />
|
<module name="bank-of-corda-demo_integrationTest" target="1.8" />
|
||||||
<module name="bank-of-corda-demo_main" target="1.8" />
|
<module name="bank-of-corda-demo_main" target="1.8" />
|
||||||
<module name="bank-of-corda-demo_test" target="1.8" />
|
<module name="bank-of-corda-demo_test" target="1.8" />
|
||||||
|
<module name="behave_behave" target="1.8" />
|
||||||
<module name="behave_main" target="1.8" />
|
<module name="behave_main" target="1.8" />
|
||||||
<module name="behave_scenario" target="1.8" />
|
<module name="behave_scenario" target="1.8" />
|
||||||
<module name="behave_test" target="1.8" />
|
<module name="behave_test" target="1.8" />
|
||||||
|
@ -29,19 +29,19 @@ repositories {
|
|||||||
}
|
}
|
||||||
|
|
||||||
sourceSets {
|
sourceSets {
|
||||||
scenario {
|
behave {
|
||||||
java {
|
java {
|
||||||
compileClasspath += main.output
|
compileClasspath += main.output
|
||||||
runtimeClasspath += main.output
|
runtimeClasspath += main.output
|
||||||
srcDir file('src/scenario/kotlin')
|
srcDirs = ["src/main/kotlin", "src/scenario/kotlin"]
|
||||||
}
|
}
|
||||||
resources.srcDir file('src/scenario/resources')
|
resources.srcDir file('src/scenario/resources')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
configurations {
|
configurations {
|
||||||
scenarioCompile.extendsFrom testCompile
|
behaveCompile.extendsFrom testCompile
|
||||||
scenarioRuntime.extendsFrom testRuntime
|
behaveRuntime.extendsFrom testRuntime
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
@ -88,9 +88,9 @@ dependencies {
|
|||||||
|
|
||||||
// Scenarios / End-to-End Tests
|
// Scenarios / End-to-End Tests
|
||||||
|
|
||||||
scenarioCompile "info.cukes:cucumber-java8:$cucumber_version"
|
behaveCompile "info.cukes:cucumber-java8:$cucumber_version"
|
||||||
scenarioCompile "info.cukes:cucumber-junit:$cucumber_version"
|
behaveCompile "info.cukes:cucumber-junit:$cucumber_version"
|
||||||
scenarioCompile "info.cukes:cucumber-picocontainer:$cucumber_version"
|
behaveCompile "info.cukes:cucumber-picocontainer:$cucumber_version"
|
||||||
}
|
}
|
||||||
|
|
||||||
compileKotlin {
|
compileKotlin {
|
||||||
@ -101,24 +101,25 @@ compileTestKotlin {
|
|||||||
kotlinOptions.jvmTarget = "1.8"
|
kotlinOptions.jvmTarget = "1.8"
|
||||||
}
|
}
|
||||||
|
|
||||||
compileScenarioKotlin {
|
|
||||||
kotlinOptions.jvmTarget = "1.8"
|
|
||||||
}
|
|
||||||
|
|
||||||
test {
|
test {
|
||||||
testLogging.showStandardStreams = true
|
testLogging.showStandardStreams = true
|
||||||
}
|
}
|
||||||
|
|
||||||
task scenarios(type: Test) {
|
task behaveJar(type: Jar) {
|
||||||
setTestClassesDirs sourceSets.scenario.output.getClassesDirs()
|
baseName "corda-behave"
|
||||||
classpath = sourceSets.scenario.runtimeClasspath
|
from sourceSets.behave.output
|
||||||
outputs.upToDateWhen { false }
|
from {
|
||||||
|
configurations.behaveCompile.collect {
|
||||||
if (project.hasProperty("tags")) {
|
it.isDirectory() ? it : zipTree(it)
|
||||||
systemProperty "cucumber.options", "--tags $tags"
|
}
|
||||||
logger.warn("Only running tests tagged with: $tags ...")
|
}
|
||||||
|
zip64 true
|
||||||
|
exclude("features/**")
|
||||||
|
exclude("scripts/**")
|
||||||
|
exclude("META-INF/*.DSA")
|
||||||
|
exclude("META-INF/*.RSA")
|
||||||
|
exclude("META-INF/*.SF")
|
||||||
|
manifest {
|
||||||
|
attributes 'Main-Class': 'net.corda.behave.scenarios.ScenarioRunner'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//scenarios.mustRunAfter test
|
|
||||||
//scenarios.dependsOn test
|
|
@ -6,26 +6,30 @@ set -x
|
|||||||
# For example:
|
# For example:
|
||||||
# corda-master => git clone https://github.com/corda/corda
|
# corda-master => git clone https://github.com/corda/corda
|
||||||
# r3corda-master => git clone https://github.com/corda/enterprise
|
# r3corda-master => git clone https://github.com/corda/enterprise
|
||||||
VERSION=corda-3.0
|
VERSION=corda-master
|
||||||
STAGING_DIR=deps/corda/${VERSION}
|
STAGING_DIR=~/staging
|
||||||
DRIVERS_DIR=deps/drivers
|
CORDA_DIR=${STAGING_DIR}/corda/${VERSION}
|
||||||
|
CORDAPP_DIR=${CORDA_DIR}/apps
|
||||||
|
DRIVERS_DIR=${STAGING_DIR}/drivers
|
||||||
|
|
||||||
# Set up directories
|
# Set up directories
|
||||||
mkdir -p ${STAGING_DIR}/apps
|
mkdir -p ${STAGING_DIR}
|
||||||
|
mkdir -p ${CORDA_DIR}
|
||||||
|
mkdir -p ${CORDAPP_DIR}
|
||||||
mkdir -p ${DRIVERS_DIR}
|
mkdir -p ${DRIVERS_DIR}
|
||||||
|
|
||||||
# Copy Corda capsule into deps
|
# Copy Corda capsule into deps
|
||||||
cd ../..
|
cd ../..
|
||||||
./gradlew clean :node:capsule:buildCordaJar :finance:jar
|
./gradlew clean :node:capsule:buildCordaJar :finance:jar
|
||||||
cp -v $(ls node/capsule/build/libs/corda-*.jar | tail -n1) experimental/behave/${STAGING_DIR}/corda.jar
|
cp -v $(ls node/capsule/build/libs/corda-*.jar | tail -n1) ${CORDA_DIR}/corda.jar
|
||||||
|
|
||||||
# Copy finance library
|
# Copy finance library
|
||||||
cp -v $(ls finance/build/libs/corda-finance-*.jar | tail -n1) experimental/behave/${STAGING_DIR}/apps
|
cp -v $(ls finance/build/libs/corda-finance-*.jar | tail -n1) ${CORDAPP_DIR}
|
||||||
|
|
||||||
# Download database drivers
|
# Download database drivers
|
||||||
curl "https://search.maven.org/remotecontent?filepath=com/h2database/h2/1.4.196/h2-1.4.196.jar" > experimental/behave/${DRIVERS_DIR}/h2-1.4.196.jar
|
curl "https://search.maven.org/remotecontent?filepath=com/h2database/h2/1.4.196/h2-1.4.196.jar" > ${DRIVERS_DIR}/h2-1.4.196.jar
|
||||||
curl -L "http://central.maven.org/maven2/org/postgresql/postgresql/42.1.4/postgresql-42.1.4.jar" > experimental/behave/${DRIVERS_DIR}/postgresql-42.1.4.jar
|
curl -L "http://central.maven.org/maven2/org/postgresql/postgresql/42.1.4/postgresql-42.1.4.jar" > ${DRIVERS_DIR}/postgresql-42.1.4.jar
|
||||||
|
|
||||||
# Build Network Bootstrapper
|
# Build Network Bootstrapper
|
||||||
./gradlew buildBootstrapperJar
|
./gradlew buildBootstrapperJar
|
||||||
cp -v $(ls tools/bootstrapper/build/libs/*.jar | tail -n1) experimental/behave/${STAGING_DIR}/network-bootstrapper.jar
|
cp -v $(ls tools/bootstrapper/build/libs/*.jar | tail -n1) ${CORDA_DIR}/network-bootstrapper.jar
|
||||||
|
@ -93,11 +93,8 @@ class Network private constructor(
|
|||||||
|
|
||||||
fun copyDatabaseDrivers() {
|
fun copyDatabaseDrivers() {
|
||||||
val driverDirectory = (targetDirectory / "libs").createDirectories()
|
val driverDirectory = (targetDirectory / "libs").createDirectories()
|
||||||
log.info("Copying database drivers from $stagingRoot/deps/drivers to $driverDirectory")
|
log.info("Copying database drivers from $stagingRoot/drivers to $driverDirectory")
|
||||||
FileUtils.copyDirectory(
|
FileUtils.copyDirectory((stagingRoot / "drivers").toFile(), driverDirectory.toFile())
|
||||||
(stagingRoot / "deps" / "drivers").toFile(),
|
|
||||||
driverDirectory.toFile()
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun configureNodes(): Boolean {
|
fun configureNodes(): Boolean {
|
||||||
|
@ -85,7 +85,7 @@ class Distribution private constructor(
|
|||||||
|
|
||||||
private val distributions = mutableListOf<Distribution>()
|
private val distributions = mutableListOf<Distribution>()
|
||||||
|
|
||||||
private val nodePrefix = stagingRoot / "deps/corda"
|
private val nodePrefix = stagingRoot / "corda"
|
||||||
|
|
||||||
val MASTER = fromJarFile("corda-master")
|
val MASTER = fromJarFile("corda-master")
|
||||||
|
|
||||||
|
@ -220,7 +220,7 @@ class Node(
|
|||||||
|
|
||||||
private fun installApps() {
|
private fun installApps() {
|
||||||
val version = config.distribution.version
|
val version = config.distribution.version
|
||||||
val appDirectory = stagingRoot / "deps" / "corda" / version / "apps"
|
val appDirectory = stagingRoot / "corda" / version / "apps"
|
||||||
if (appDirectory.exists()) {
|
if (appDirectory.exists()) {
|
||||||
val targetAppDirectory = runtimeDirectory / "cordapps"
|
val targetAppDirectory = runtimeDirectory / "cordapps"
|
||||||
FileUtils.copyDirectory(appDirectory.toFile(), targetAppDirectory.toFile())
|
FileUtils.copyDirectory(appDirectory.toFile(), targetAppDirectory.toFile())
|
||||||
|
@ -45,7 +45,7 @@ class Configuration(
|
|||||||
|
|
||||||
fun writeToFile(file: Path) {
|
fun writeToFile(file: Path) {
|
||||||
file.writeText(this.generate())
|
file.writeText(this.generate())
|
||||||
log.info(this.generate())
|
log.debug(this.generate())
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun generate() = listOf(basicConfig, database.config(), extraConfig)
|
private fun generate() = listOf(basicConfig, database.config(), extraConfig)
|
||||||
|
@ -1,12 +1,11 @@
|
|||||||
package net.corda.behave.process
|
package net.corda.behave.process
|
||||||
|
|
||||||
import java.io.File
|
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import java.time.Duration
|
import java.time.Duration
|
||||||
|
|
||||||
class JarCommand(
|
class JarCommand(
|
||||||
jarFile: Path,
|
jarFile: Path,
|
||||||
arguments: Array<String>,
|
arguments: Array<out String>,
|
||||||
directory: Path,
|
directory: Path,
|
||||||
timeout: Duration,
|
timeout: Duration,
|
||||||
enableRemoteDebugging: Boolean = false
|
enableRemoteDebugging: Boolean = false
|
||||||
|
@ -17,9 +17,6 @@ class Cash(state: ScenarioState) : Substeps(state) {
|
|||||||
|
|
||||||
fun numberOfIssuableCurrencies(nodeName: String): Int {
|
fun numberOfIssuableCurrencies(nodeName: String): Int {
|
||||||
return withClient(nodeName) {
|
return withClient(nodeName) {
|
||||||
for (flow in it.registeredFlows()) {
|
|
||||||
log.info(flow)
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
val config = it.startFlow(::CashConfigDataFlow).returnValue.get(10, TimeUnit.SECONDS)
|
val config = it.startFlow(::CashConfigDataFlow).returnValue.get(10, TimeUnit.SECONDS)
|
||||||
for (supportedCurrency in config.supportedCurrencies) {
|
for (supportedCurrency in config.supportedCurrencies) {
|
||||||
|
@ -10,7 +10,7 @@ class Startup(state: ScenarioState) : Substeps(state) {
|
|||||||
fun hasLoggingInformation(nodeName: String) {
|
fun hasLoggingInformation(nodeName: String) {
|
||||||
withNetwork {
|
withNetwork {
|
||||||
log.info("Retrieving logging information for node '$nodeName' ...")
|
log.info("Retrieving logging information for node '$nodeName' ...")
|
||||||
if (!node(nodeName).nodeInfoGenerationOutput.find("Logs can be found in.*").any()) {
|
if (!node(nodeName).logOutput.find("Logs can be found in.*").any()) {
|
||||||
fail("Unable to find logging information for node $nodeName")
|
fail("Unable to find logging information for node $nodeName")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -26,7 +26,7 @@ class Startup(state: ScenarioState) : Substeps(state) {
|
|||||||
fun hasDatabaseDetails(nodeName: String) {
|
fun hasDatabaseDetails(nodeName: String) {
|
||||||
withNetwork {
|
withNetwork {
|
||||||
log.info("Retrieving database details for node '$nodeName' ...")
|
log.info("Retrieving database details for node '$nodeName' ...")
|
||||||
if (!node(nodeName).nodeInfoGenerationOutput.find("Database connection url is.*").any()) {
|
if (!node(nodeName).logOutput.find("Database connection url is.*").any()) {
|
||||||
fail("Unable to find database details for node $nodeName")
|
fail("Unable to find database details for node $nodeName")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -104,7 +104,7 @@ class Startup(state: ScenarioState) : Substeps(state) {
|
|||||||
val cordappDirectory = node(nodeName).config.distribution.cordappDirectory
|
val cordappDirectory = node(nodeName).config.distribution.cordappDirectory
|
||||||
val cordappJar = cordappDirectory / "$cordapp.jar"
|
val cordappJar = cordappDirectory / "$cordapp.jar"
|
||||||
// Execute
|
// Execute
|
||||||
val command = JarCommand(cordappJar, args as Array<String>, cordappDirectory, 1.minutes)
|
val command = JarCommand(cordappJar, args, cordappDirectory, 1.minutes)
|
||||||
command.start()
|
command.start()
|
||||||
if (!command.waitFor())
|
if (!command.waitFor())
|
||||||
fail("Failed to successfully run the CorDapp jar: $cordaApp")
|
fail("Failed to successfully run the CorDapp jar: $cordaApp")
|
||||||
|
@ -21,6 +21,7 @@ class VaultSteps : StepsBlock {
|
|||||||
|
|
||||||
Then<String, Int, String>("^node (\\w+) vault contains (\\d+) (\\w+) states$") { node, count, contractType ->
|
Then<String, Int, String>("^node (\\w+) vault contains (\\d+) (\\w+) states$") { node, count, contractType ->
|
||||||
try {
|
try {
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
val contractStateTypeClass = Class.forName(contractType) as Class<ContractState>
|
val contractStateTypeClass = Class.forName(contractType) as Class<ContractState>
|
||||||
if (vault.query(node, contractStateTypeClass).size == count)
|
if (vault.query(node, contractStateTypeClass).size == count)
|
||||||
succeed()
|
succeed()
|
||||||
@ -33,7 +34,7 @@ class VaultSteps : StepsBlock {
|
|||||||
|
|
||||||
Then<String, Long, String>("^node (\\w+) vault contains total cash of (\\d+) (\\w+)$") { node, total, currency ->
|
Then<String, Long, String>("^node (\\w+) vault contains total cash of (\\d+) (\\w+)$") { node, total, currency ->
|
||||||
val cashStates = vault.query(node, Cash.State::class.java)
|
val cashStates = vault.query(node, Cash.State::class.java)
|
||||||
val sumCashStates = cashStates.filter { it.state.data.amount.token.product.currencyCode == currency }?.sumByLong { it.state.data.amount.quantity }
|
val sumCashStates = cashStates.filter { it.state.data.amount.token.product.currencyCode == currency }.sumByLong { it.state.data.amount.quantity }
|
||||||
print((sumCashStates))
|
print((sumCashStates))
|
||||||
if (sumCashStates == total)
|
if (sumCashStates == total)
|
||||||
succeed()
|
succeed()
|
||||||
|
@ -2,22 +2,34 @@
|
|||||||
Feature: Cash - Issuable Currencies
|
Feature: Cash - Issuable Currencies
|
||||||
To have cash on ledger, certain nodes must have the ability to issue cash of various currencies.
|
To have cash on ledger, certain nodes must have the ability to issue cash of various currencies.
|
||||||
|
|
||||||
Scenario: Node can issue no currencies by default
|
Scenario Outline: Node can issue no currencies by default
|
||||||
Given a node PartyA of version master
|
Given a node PartyA of version <Node-Version>
|
||||||
And node PartyA has the finance app installed
|
And node PartyA has the finance app installed
|
||||||
When the network is ready
|
When the network is ready
|
||||||
Then node PartyA has 0 issuable currencies
|
Then node PartyA has 0 issuable currencies
|
||||||
|
|
||||||
Scenario: Node has an issuable currency
|
Examples:
|
||||||
Given a node PartyA of version master
|
| Node-Version |
|
||||||
|
| master |
|
||||||
|
|
||||||
|
Scenario Outline: Node has an issuable currency
|
||||||
|
Given a node PartyA of version <Node-Version>
|
||||||
And node PartyA can issue currencies of denomination USD
|
And node PartyA can issue currencies of denomination USD
|
||||||
And node PartyA has the finance app installed
|
And node PartyA has the finance app installed
|
||||||
When the network is ready
|
When the network is ready
|
||||||
Then node PartyA has 1 issuable currency
|
Then node PartyA has 1 issuable currency
|
||||||
|
|
||||||
Scenario: Node can issue a currency
|
Examples:
|
||||||
Given a node PartyA of version master
|
| Node-Version |
|
||||||
And a nonvalidating notary Notary of version master
|
| master |
|
||||||
|
|
||||||
|
Scenario Outline: Node can issue a currency
|
||||||
|
Given a node PartyA of version <Node-Version>
|
||||||
|
And a nonvalidating notary Notary of version <Node-Version>
|
||||||
And node PartyA has the finance app installed
|
And node PartyA has the finance app installed
|
||||||
When the network is ready
|
When the network is ready
|
||||||
Then node PartyA can issue 100 USD
|
Then node PartyA can issue 100 USD
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
| Node-Version |
|
||||||
|
| master |
|
||||||
|
@ -11,4 +11,6 @@ Feature: Database - Connection
|
|||||||
Examples:
|
Examples:
|
||||||
| Node-Version | Database-Type |
|
| Node-Version | Database-Type |
|
||||||
| master | H2 |
|
| master | H2 |
|
||||||
|
|
||||||
|
# To run this scenario using postgreSQL you must ensure that Docker is running locally
|
||||||
# | master | postgreSQL |
|
# | master | postgreSQL |
|
@ -3,19 +3,32 @@ Feature: Startup Information - Logging
|
|||||||
A Corda node should inform the user of important parameters during startup so that he/she can confirm the setup and
|
A Corda node should inform the user of important parameters during startup so that he/she can confirm the setup and
|
||||||
configure / connect relevant software to said node.
|
configure / connect relevant software to said node.
|
||||||
|
|
||||||
Scenario: Node shows logging information on startup
|
Scenario Outline: Node shows logging information on startup
|
||||||
Given a node PartyA of version master
|
Given a node PartyA of version <Node-Version>
|
||||||
And node PartyA uses database of type H2
|
And node PartyA uses database of type <Database-Type>
|
||||||
And node PartyA is located in London, GB
|
And node PartyA is located in London, GB
|
||||||
When the network is ready
|
When the network is ready
|
||||||
Then user can retrieve logging information for node PartyA
|
Then user can retrieve logging information for node PartyA
|
||||||
|
|
||||||
Scenario: Node shows database details on startup
|
Examples:
|
||||||
Given a node PartyA of version master
|
| Node-Version | Database-Type |
|
||||||
|
| master | H2 |
|
||||||
|
|
||||||
|
Scenario Outline: Node shows database details on startup
|
||||||
|
Given a node PartyA of version <Node-Version>
|
||||||
|
And node PartyA uses database of type <Database-Type>
|
||||||
When the network is ready
|
When the network is ready
|
||||||
Then user can retrieve database details for node PartyA
|
Then user can retrieve database details for node PartyA
|
||||||
|
|
||||||
Scenario: Node shows version information on startup
|
Examples:
|
||||||
Given a node PartyA of version master
|
| Node-Version | Database-Type |
|
||||||
Then node PartyA is on platform version 4
|
| master | H2 |
|
||||||
And node PartyA is on release version corda-4.0-snapshot
|
|
||||||
|
Scenario Outline: Node shows version information on startup
|
||||||
|
Given a node PartyA of version <Node-Version>
|
||||||
|
Then node PartyA is on platform version <Platform-Version>
|
||||||
|
And node PartyA is on release version <Release-Version>
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
| Node-Version | Platform-Version | Release-Version |
|
||||||
|
| master | 4 | corda-4.0-snapshot |
|
||||||
|
26
experimental/behave/src/scenario/resources/scripts/run-behave-features.sh
Executable file
26
experimental/behave/src/scenario/resources/scripts/run-behave-features.sh
Executable file
@ -0,0 +1,26 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
#
|
||||||
|
# Run this script from the experimental/behave directory
|
||||||
|
#
|
||||||
|
# $ pwd
|
||||||
|
# ./IdeaProjects/corda-reviews/experimental/behave
|
||||||
|
# $ src/scenario/resources/scripts/run-behave-features.sh
|
||||||
|
#
|
||||||
|
# Note: please ensure you have configured your staging environment by running the top-level script: prepare.sh
|
||||||
|
|
||||||
|
BUILD_DIR=$PWD
|
||||||
|
cd ${BUILD_DIR}
|
||||||
|
../../gradlew behaveJar
|
||||||
|
|
||||||
|
BEHAVE_JAR=$(ls build/libs/corda-behave-*.jar | tail -n1)
|
||||||
|
STAGING_ROOT=~/staging
|
||||||
|
|
||||||
|
# startup
|
||||||
|
java -DSTAGING_ROOT=${STAGING_ROOT} -jar ${BEHAVE_JAR} --glue net.corda.behave.scenarios -path ./src/scenario/resources/features/startup/logging.feature
|
||||||
|
|
||||||
|
# cash
|
||||||
|
java -DSTAGING_ROOT=${STAGING_ROOT} -jar ${BEHAVE_JAR} --glue net.corda.behave.scenarios -path ./src/scenario/resources/features/cash/currencies.feature
|
||||||
|
|
||||||
|
# database
|
||||||
|
java -DSTAGING_ROOT=${STAGING_ROOT} -jar ${BEHAVE_JAR} --glue net.corda.behave.scenarios -path ./src/scenario/resources/features/cash/currencies.feature
|
Reference in New Issue
Block a user