Skip to content

V5.0.1 integration tests #1

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: release/corda-5-0
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
name: CI

on:
push:
branches: [ feature-integration-testing, main, 'release/corda-5-0', citest ]
pull_request:
branches: [ feature-integration-testing, main, 'release/corda-5-0', citest ]


# This allows a subsequently queued workflow run to interrupt previous runs
concurrency:
group: '${{ github.workflow }} @ ${{ github.event.pull_request.head.label || github.head_ref || github.ref }}'
cancel-in-progress: true

jobs:
ci:
name: CI
strategy:
matrix:
java-version:
- 17
os:
- ubuntu-latest
# - windows-2019
# in case one combination fails, we still want to see results from others
fail-fast: false
runs-on: ${{ matrix.os }}
timeout-minutes: 30
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Set up JDK
uses: actions/setup-java@v3
with:
java-version: ${{ matrix.java-version }}
distribution: 'zulu'
- name: Setup Corda CLI
uses: manosbatsis/corda5-cli-action@v2.0.1
with:
cli-version: '5.0.1'
- name: Build with Gradle
uses: gradle/gradle-build-action@v2
with:
arguments: build integrationTest
192 changes: 109 additions & 83 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,112 +1,138 @@
# CSDE-cordapp-template-kotlin

# Corda5 Integration Testing

To help make the process of prototyping CorDapps on Corda 5 release more straight forward we have developed the Cordapp Standard Development Environment (CSDE).
This fork builds on top of CSDE to provide a Quick setup for Corda5 integration tests with Gradle and JUnit5,
both local and CI with GitHub Actions.

The CSDE is obtained by cloning this CSDE-Cordapp-Template-Kotlin repository to your local machine. The CSDE provides:
> Note: seems corda-runtime-os release-5.0.0 disappeared from GitHub. This branch (v5.0.1-integration-tests) is based on CSDE release/corda-5-0, but uses Corda 5.0.1 and Java 17.

- A pre-setup Cordapp Project which you can use as a starting point to develop your own prototypes.
You can see all my changes in [this PR](https://github.com/manosbatsis/CSDE-cordapp-integration-testing/pull/1).
There's also a Medium article [here](https://medium.com/@manosbatsis/corda5-integration-testing-4e98d6a195cd).

- A base Gradle configuration which brings in the dependencies you need to write and test a Corda 5 Cordapp.
## Prerequisites

- A set of Gradle helper tasks which speed up and simplify the development and deployment process.
Same as [CSDE](https://docs.r3.com/en/tools-corda5/csde/prerequisites.html):

- Debug configuration for debugging a local Corda cluster.
- Azul Zulu JDK 17
- Git ~v2.24.1
- Docker Engine ~v20.X.Y or Docker Desktop ~v3.5.X
- Corda CLI, see [Installing the Corda CLI](https://docs.r3.com/en/platform/corda/5.0/developing-applications/tooling/installing-corda-cli.html)

- The MyFirstFlow code which forms the basis of this getting started documentation, this is located in package com.r3.developers.csdetemplate.flowexample
## Gradle Setup

- A UTXO example in package com.r3.developers.csdetemplate.utxoexample packages
The [corda5-testutils](https://github.com/manosbatsis/corda5-testutils) includes a JUnit5 extension
for starting the Combined Worker and an API for calling flows etc. Let's add the dependency version to gradle.properties:

- Ability to configure the Members of the Local Corda Network.

To find out how to use the CSDE, please refer to the *Getting Started Using the CSDE* subsection within the *Developing Applications* section in the latest Corda 5 documentation at https://docs.r3.com/


## Chat app
We have built a simple one to one chat app to demo some functionalities of the next gen Corda platform.

In this app you can:
1. Create a new chat with a counterparty. `CreateNewChatFlow`
2. List out the chat entries you had. `ListChatsFlow`
3. Individually query out the history of one chat entry. `GetChatFlowArgs`
4. Continue chatting within the chat entry with the counterparty. `UpdateChatFlow`

### Setting up
```properties
# Corda 5 Test Utils
corda5TestutilsVersion=1.2.1
```

1. We will begin our test deployment with clicking the `startCorda`. This task will load up the combined Corda workers in docker.
A successful deployment will allow you to open the REST APIs at: https://localhost:8888/api/v1/swagger#. You can test out some of the
functions to check connectivity. (GET /cpi function call should return an empty list as for now.)
2. We will now deploy the cordapp with a click of `5-vNodeSetup` task. Upon successful deployment of the CPI, the GET /cpi function call should now return the meta data of the cpi you just upload
Now into workflows/build.gradle, we add separate configuration, sourceSets etc. for integration tests:

```kotlin
// Add integrationTest config
apply from: "${rootDir}/gradle/integration-test.gradle"
```

And the corda5-testutils dependency for launching Corda's Combined Worker:

### Running the chat app
```kotlin
dependencies {
// ...
// Kotlin Test
testImplementation("org.jetbrains.kotlin:kotlin-test-junit5")
// Corda 5 Test Utils
testImplementation "com.github.manosbatsis.corda5.testutils:integration-junit5:$corda5TestutilsVersion"
// ...
}
```

In Corda 5, flows will be triggered via `POST /flow/{holdingidentityshorthash}` and flow result will need to be view at `GET /flow/{holdingidentityshorthash}/{clientrequestid}`
* holdingidentityshorthash: the id of the network participants, ie Bob, Alice, Charlie. You can view all the short hashes of the network member with another gradle task called `ListVNodes`
* clientrequestid: the id you specify in the flow requestBody when you trigger a flow.

#### Step 1: Create Chat Entry
Pick a VNode identity to initiate the chat, and get its short hash. (Let's pick Alice. Dont pick Bob because Bob is the person who we will have the chat with).
## Sample Test

Here's our [IntegrationTests](workflows/src/integrationTest/kotlin/com/r3/developers/csdetemplate/flowexample/workflows/IntegrationTests.kt):

```kotlin
import com.github.manosbatsis.corda5.testutils.integration.junit5.CombinedWorkerMode
import com.github.manosbatsis.corda5.testutils.integration.junit5.Corda5NodesConfig
import com.github.manosbatsis.corda5.testutils.integration.junit5.Corda5NodesExtension
import com.github.manosbatsis.corda5.testutils.integration.junit5.nodehandles.NodeHandles
import com.github.manosbatsis.corda5.testutils.rest.client.model.FlowRequest
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import kotlin.test.assertEquals
import kotlin.test.assertTrue

// Add the Corda5 nodes extension
@ExtendWith(Corda5NodesExtension::class)
open class IntegrationTests {

// Optional
val config = Corda5NodesConfig(

authUsername = "admin",
authPassword = "admin",
baseUrl = "https://localhost:8888/api/v1/",
httpMaxWaitSeconds = 120,
debug = true,
projectDir = Corda5NodesConfig.gradleRootDir,
combinedWorkerMode = CombinedWorkerMode.PER_LAUNCHER

)

// Corda5 nodes extension provides the NodeHandles
@Test
fun workFlowTests(nodeHandles: NodeHandles) {
// Get node handles
val aliceNode = nodeHandles.getByCommonName("Alice")
val bobNode = nodeHandles.getByCommonName("Bob")

// Create flow args
val flowArgs = MyFirstFlowStartArgs(bobNode.memberX500Name)
// Call Flow
val response = aliceNode.waitForFlow(
FlowRequest(
flowClass = MyFirstFlow::class.java,
requestBody = flowArgs,
flowResultClass = Message::class.java
)
)

// Check status and flow result
assertTrue(response.isSuccess())
val expectedMessage = Message(bobNode.memberX500Name, "Hello Alice, best wishes from Bob")
assertEquals(expectedMessage, response.flowResult)

Go to `POST /flow/{holdingidentityshorthash}`, enter the identity short hash(Alice's hash) and request body:
```
{
"clientRequestId": "create-1",
"flowClassName": "com.r3.developers.csdetemplate.utxoexample.workflows.CreateNewChatFlow",
"requestBody": {
"chatName":"Chat with Bob",
"otherMember":"CN=Bob, OU=Test Dept, O=R3, L=London, C=GB",
"message": "Hello Bob"
}
}
}
```

After trigger the create-chat flow, hop to `GET /flow/{holdingidentityshorthash}/{clientrequestid}` and enter the short hash(Alice's hash) and clientrequestid to view the flow result
## Testing with Gradle

We have to call integration tests explicitly:

#### Step 2: List the chat
In order to continue the chat, we would need the chat ID. This step will bring out all the chat entries this entity (Alice) has.
Go to `POST /flow/{holdingidentityshorthash}`, enter the identity short hash(Alice's hash) and request body:
```
{
"clientRequestId": "list-1",
"flowClassName": "com.r3.developers.csdetemplate.utxoexample.workflows.ListChatsFlow",
"requestBody": {}
}
./gradlew build integrationTest
```
After trigger the list-chats flow, again, we need to hop to `GET /flow/{holdingidentityshorthash}/{clientrequestid}` and check the result. As the screenshot shows, in the response body,
we will see a list of chat entries, but it currently only has one entry. And we can see the id of the chat entry. Let's record that id.


#### Step 3: Continue the chat with `UpdateChatFlow`
In this step, we will continue the chat between Alice and Bob.
Goto `POST /flow/{holdingidentityshorthash}`, enter the identity short hash and request body. Note that here we can have either Alice or Bob's short hash. If you enter Alice's hash,
this message will be recorded as a message from Alice, vice versa. And the id field is the chat entry id we got from the previous step.
```
{
"clientRequestId": "update-1",
"flowClassName": "com.r3.developers.csdetemplate.utxoexample.workflows.UpdateChatFlow",
"requestBody": {
"id":" ** fill in id **",
"message": "How are you today?"
}
}
```
And as for the result of this flow, go to `GET /flow/{holdingidentityshorthash}/{clientrequestid}` and enter the required fields.
## Testing with GitHub Actions

#### Step 4: See the whole chat history of one chat entry
After a few back and forth of the messaging, you can view entire chat history by calling GetChatFlow.
The [corda5-cli-action](https://github.com/manosbatsis/corda5-cli-action) will download, install and cache Corda5 CLI,
so the Gradle build will just work. The workflow is [ci.yml](.github/workflows/ci.yml) contains the following:

```yaml
- name: Setup Corda CLI
uses: manosbatsis/corda5-cli-action@v2.0.1
with:
cli-version: '5.0.1'
- name: Build with Gradle
uses: gradle/gradle-build-action@v2
with:
arguments: build integrationTest
```
{
"clientRequestId": "get-1",
"flowClassName": "com.r3.developers.csdetemplate.utxoexample.workflows.GetChatFlow",
"requestBody": {
"id":" ** fill in id **",
"numberOfRecords":"4"
}
}
```
And as for the result, you need to go to the Get API again and enter the short hash and client request ID.
## Build Successful

All done. You can see the workflow runs in the repository’s [Actions](https://github.com/manosbatsis/CSDE-cordapp-integration-testing/actions).

Thus, we have concluded a full run through of the chat app.
25 changes: 15 additions & 10 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import static org.gradle.api.JavaVersion.VERSION_11
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

import static org.gradle.api.JavaVersion.VERSION_17
import static org.gradle.jvm.toolchain.JavaLanguageVersion.of
import static org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17
import static org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_1_8

plugins {
id 'org.jetbrains.kotlin.jvm'
Expand All @@ -13,7 +18,7 @@ allprojects {
group 'com.r3.developers.csdetemplate'
version '1.0-SNAPSHOT'

def javaVersion = VERSION_11
def javaVersion = VERSION_17

// Configure the CSDE
csde {
Expand All @@ -35,29 +40,29 @@ allprojects {
}

// Declare the set of Kotlin compiler options we need to build a CorDapp.
tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).configureEach {
kotlinOptions {
allWarningsAsErrors = false
tasks.withType(KotlinCompile).configureEach {
compilerOptions {
allWarningsAsErrors = true

// Specify the version of Kotlin that we are that we will be developing.
languageVersion = '1.7'
languageVersion = KOTLIN_1_8
// Specify the Kotlin libraries that code is compatible with
apiVersion = '1.7'
apiVersion = KOTLIN_1_8
// Note that we Need to use a version of Kotlin that will be compatible with the Corda API.
// Currently that is developed in Kotlin 1.7 so picking the same version ensures compatibility with that.

// Specify the version of Java to target.
jvmTarget = javaVersion
jvmTarget = JVM_17

// Needed for reflection to work correctly.
javaParameters = true

// -Xjvm-default determines how Kotlin supports default methods.
// JetBrains currently recommends developers use -Xjvm-default=all
// https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.jvm/-jvm-default/
freeCompilerArgs += [
freeCompilerArgs.addAll([
"-Xjvm-default=all"
]
])
}
}

Expand Down
16 changes: 10 additions & 6 deletions gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@ kotlin.code.style=official

# Specify the version of the Corda-API to use.
# This needs to match the version supported by the Corda Cluster the CorDapp will run on.
cordaApiVersion=5.0.0.765
cordaApiVersion=5.1.0.39


# Specify the version of the notary plugins to use.
# Currently packaged as part of corda-runtime-os, so should be set to a corda-runtime-os version.
cordaNotaryPluginsVersion=5.0.0.0
cordaNotaryPluginsVersion=5.0.1.0

# Specify the version of the Combined Worker to use
combinedWorkerJarVersion=5.0.0.0
combinedWorkerJarVersion=5.0.1.0

# Specify the version of the cordapp-cpb and cordapp-cpk plugins
cordaPluginsVersion=7.0.3
Expand All @@ -21,11 +22,11 @@ csdePluginVersion=1.1.0
workflowsModule=workflows

# For the time being this just needs to be set to a dummy value.
platformVersion = 999
platformVersion = 50000

# Version of Kotlin to use.
# We recommend using a version close to that used by Corda-API.
kotlinVersion = 1.7.21
kotlinVersion = 1.8.21

# Do not use default dependencies.
kotlin.stdlib.default.dependency=false
Expand All @@ -34,4 +35,7 @@ kotlin.stdlib.default.dependency=false
junitVersion = 5.8.2
mockitoKotlinVersion=4.0.0
mockitoVersion=4.6.1
hamcrestVersion=2.2
hamcrestVersion=2.2

# Corda 5 Test Utils
corda5TestutilsVersion=1.2.1
Loading