diff --git a/tests/integration-tests/.editorconfig b/tests/integration-tests/.editorconfig new file mode 100644 index 0000000000..6e8a563aa0 --- /dev/null +++ b/tests/integration-tests/.editorconfig @@ -0,0 +1,7 @@ +root = true + +[*.{kt,kts}] +ktlint_code_style = intellij_idea +ij_kotlin_packages_to_use_import_on_demand = ** +ij_kotlin_name_count_to_use_star_import = 3 +ij_kotlin_name_count_to_use_star_import_for_members = 3 diff --git a/tests/integration-tests/README.md b/tests/integration-tests/README.md index 0fce7fd2b4..679a28b011 100644 --- a/tests/integration-tests/README.md +++ b/tests/integration-tests/README.md @@ -397,7 +397,7 @@ The following variables must be set before running the tests: * `OPEN_ENTERPRISE_AGENT_VERSION`: version of the OEA docker image to use. ```shell -TESTS_CONFIG=/configs/basic.conf PRISM_NODE_VERSION=2.2.1 OPEN_ENTERPRISE_AGENT_VERSION=1.19.1 ./gradlew test +TESTS_CONFIG=/configs/basic.conf PRISM_NODE_VERSION=2.2.1 OPEN_ENTERPRISE_AGENT_VERSION=1.30.1 ./gradlew test ``` > Please note: there is no need to pass environment variables if you're using already running agents. @@ -407,6 +407,24 @@ Additional `-Dcucumber.filter.tags` option can be used to specify the tags to in TESTS_CONFIG=/configs/mt_keycloak.conf ./gradlew test -Dcucumber.filter.tags="@connection and @credentials" ``` +### Gradle task + +To simplify the execution, each configuration file creates a new `gradle` task. The naming rule is `test_{fileName}`. + +It's possible to execute the configuration file as + +```shell +PRISM_NODE_VERSION=2.2.1 OPEN_ENTERPRISE_AGENT_VERSION=1.30.1 ./gradlew test_basic +``` + +Also, it's possible to execute the integration tests to all configurations files. The task is named `regression`, it should take a lot of time to execute. + +Note: report is not working due constrains in Serenity BDD reporting system. + +```shell +PRISM_NODE_VERSION=2.2.1 OPEN_ENTERPRISE_AGENT_VERSION=1.30.1 ./gradlew regression +``` + ### Running scenarios in IntelliJ IDEA To run the scenarios in IntelliJ IDEA, you need to create a new run configuration. diff --git a/tests/integration-tests/build.gradle.kts b/tests/integration-tests/build.gradle.kts index 0bc2566fde..7db4bdc666 100644 --- a/tests/integration-tests/build.gradle.kts +++ b/tests/integration-tests/build.gradle.kts @@ -1,19 +1,17 @@ plugins { + kotlin("jvm") version "1.9.21" idea - id("org.jetbrains.kotlin.jvm") version "1.9.0" - id("net.serenity-bdd.serenity-gradle-plugin") version "4.0.14" - id("org.jlleitschuh.gradle.ktlint") version "11.5.0" + java + id("net.serenity-bdd.serenity-gradle-plugin") version "4.0.46" + id("org.jlleitschuh.gradle.ktlint") version "12.1.0" } +group = "io.iohk.atala.prism" +version = "1.0-SNAPSHOT" + repositories { + mavenLocal() mavenCentral() - maven { - url = uri("https://maven.pkg.github.com/input-output-hk/atala-automation/") - credentials { - username = System.getenv("ATALA_GITHUB_ACTOR") - password = System.getenv("ATALA_GITHUB_TOKEN") - } - } maven { url = uri("https://maven.pkg.github.com/hyperledger-labs/open-enterprise-agent/") credentials { @@ -24,45 +22,27 @@ repositories { } dependencies { - // Logging - implementation("org.slf4j:slf4j-log4j12:2.0.5") - // Beautify async waits - implementation("org.awaitility:awaitility-kotlin:4.2.0") - // Test engines and reports - testImplementation("junit:junit:4.13.2") - implementation("net.serenity-bdd:serenity-core:4.0.14") - implementation("net.serenity-bdd:serenity-cucumber:4.0.14") - implementation("net.serenity-bdd:serenity-screenplay-rest:4.0.14") - testImplementation("net.serenity-bdd:serenity-ensure:4.0.14") // HTTP listener - implementation("io.ktor:ktor-server-netty:2.3.0") - implementation("io.ktor:ktor-client-apache:2.3.0") + testImplementation("io.ktor:ktor-server-netty:2.3.0") + testImplementation("io.ktor:ktor-client-apache:2.3.0") // RestAPI client - implementation("io.iohk.atala.prism:prism-kotlin-client:1.30.0") + testImplementation("io.iohk.atala.prism:prism-kotlin-client:1.30.0") // Test helpers library - testImplementation("io.iohk.atala:atala-automation:0.3.0") + testImplementation("io.iohk.atala:atala-automation:0.3.2") // Hoplite for configuration - implementation("com.sksamuel.hoplite:hoplite-core:2.7.5") - implementation("com.sksamuel.hoplite:hoplite-hocon:2.7.5") + testImplementation("com.sksamuel.hoplite:hoplite-core:2.7.5") + testImplementation("com.sksamuel.hoplite:hoplite-hocon:2.7.5") // Kotlin compose testImplementation("org.testcontainers:testcontainers:1.19.1") -} -buildscript { - dependencies { - classpath("net.serenity-bdd:serenity-single-page-report:4.0.14") - classpath("net.serenity-bdd:serenity-json-summary-report:4.0.14") - } } -/** - * Add HTML one-pager and JSON summary report to be produced - */ -serenity { - reports = listOf("single-page-html", "json-summary") +tasks.register("cleanTarget") { + delete("target") } tasks.test { + dependsOn("cleanTarget") testLogging.showStandardStreams = true systemProperty("cucumber.filter.tags", System.getProperty("cucumber.filter.tags")) } @@ -71,6 +51,42 @@ kotlin { jvmToolchain(19) } -ktlint { - disabledRules.set(setOf("no-wildcard-imports")) +configure { + version.set("1.2.1") +} + +/** + * Creates a new entry in verification group for each conf file + */ +afterEvaluate { + val folderPath = "src/test/resources/configs" // Change this to the path of your folder + + val configFiles = fileTree(folderPath) + .matching { include("**/*.conf") } + .map { it.name.replace(".conf", "") } + .toList() + + configFiles.forEach { fileName -> + tasks.register("test_$fileName") { + group = "verification" + testLogging.showStandardStreams = true + systemProperty("TESTS_CONFIG", "/configs/$fileName.conf") + systemProperty("PRISM_NODE_VERSION", System.getenv("PRISM_NODE_VERSION") ?: "") + systemProperty("OPEN_ENTERPRISE_AGENT_VERSION", System.getenv("OPEN_ENTERPRISE_AGENT_VERSION") ?: "") + systemProperty("cucumber.filter.tags", System.getProperty("cucumber.filter.tags")) + finalizedBy("aggregate") + } + } + + /** + * Runs the integration suite for each config file present + * Restrictions: aggregation of all executions doesn't work because of serenity configuration + */ + tasks.register("regression") { + dependsOn("cleanTarget") + group = "verification" + configFiles.forEach { + dependsOn(tasks.getByName("test_$it")) + } + } } diff --git a/tests/integration-tests/gradle/wrapper/gradle-wrapper.jar b/tests/integration-tests/gradle/wrapper/gradle-wrapper.jar index 943f0cbfa7..d64cd49177 100644 Binary files a/tests/integration-tests/gradle/wrapper/gradle-wrapper.jar and b/tests/integration-tests/gradle/wrapper/gradle-wrapper.jar differ diff --git a/tests/integration-tests/gradle/wrapper/gradle-wrapper.properties b/tests/integration-tests/gradle/wrapper/gradle-wrapper.properties index f398c33c4b..a80b22ce5c 100644 --- a/tests/integration-tests/gradle/wrapper/gradle-wrapper.properties +++ b/tests/integration-tests/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip networkTimeout=10000 +validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/tests/integration-tests/gradlew b/tests/integration-tests/gradlew index 65dcd68d65..1aa94a4269 100755 --- a/tests/integration-tests/gradlew +++ b/tests/integration-tests/gradlew @@ -83,10 +83,8 @@ done # This is normally unused # shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} -APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -133,10 +131,13 @@ location of your Java installation." fi else JAVACMD=java - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." + fi fi # Increase the maximum file descriptors if we can. @@ -144,7 +145,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then case $MAX_FD in #( max*) # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC3045 + # shellcheck disable=SC2039,SC3045 MAX_FD=$( ulimit -H -n ) || warn "Could not query maximum file descriptor limit" esac @@ -152,7 +153,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then '' | soft) :;; #( *) # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC3045 + # shellcheck disable=SC2039,SC3045 ulimit -n "$MAX_FD" || warn "Could not set maximum file descriptor limit to $MAX_FD" esac @@ -197,11 +198,15 @@ if "$cygwin" || "$msys" ; then done fi -# Collect all arguments for the java command; -# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of -# shell script including quotes and variable substitutions, so put them in -# double quotes to make sure that they get re-expanded; and -# * put everything else in single quotes, so that it's not re-expanded. + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ diff --git a/tests/integration-tests/gradlew.bat b/tests/integration-tests/gradlew.bat index 93e3f59f13..7101f8e467 100644 --- a/tests/integration-tests/gradlew.bat +++ b/tests/integration-tests/gradlew.bat @@ -1,92 +1,92 @@ -@rem -@rem Copyright 2015 the original author or authors. -@rem -@rem Licensed under the Apache License, Version 2.0 (the "License"); -@rem you may not use this file except in compliance with the License. -@rem You may obtain a copy of the License at -@rem -@rem https://www.apache.org/licenses/LICENSE-2.0 -@rem -@rem Unless required by applicable law or agreed to in writing, software -@rem distributed under the License is distributed on an "AS IS" BASIS, -@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -@rem See the License for the specific language governing permissions and -@rem limitations under the License. -@rem - -@if "%DEBUG%"=="" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%"=="" set DIRNAME=. -@rem This is normally unused -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Resolve any "." and ".." in APP_HOME to make it shorter. -for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if %ERRORLEVEL% equ 0 goto execute - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto execute - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* - -:end -@rem End local scope for the variables with windows NT shell -if %ERRORLEVEL% equ 0 goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -set EXIT_CODE=%ERRORLEVEL% -if %EXIT_CODE% equ 0 set EXIT_CODE=1 -if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% -exit /b %EXIT_CODE% - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/tests/integration-tests/serenity.properties b/tests/integration-tests/serenity.properties index 52ed622b2f..c8682e00d5 100644 --- a/tests/integration-tests/serenity.properties +++ b/tests/integration-tests/serenity.properties @@ -1,8 +1,9 @@ serenity.project.name=Open Enterprise Agent Integration tests -serenity.reports.show.step.details=true -serenity.console.colors=true -simplified.stack.traces=false jira.url=https://input-output.atlassian.net jira.project=ATL -serenity.logging=VERBOSE -serenity.console.headings=normal +serenity.reports.show.step.details=true +serenity.report.show.manual.tests=false +serenity.simplified.stack.traces=false +serenity.report.accessibility=true +json.pretty.printing=true +serenity.console.colors=true diff --git a/tests/integration-tests/src/test/kotlin/runners/IntegrationTestsRunner.kt b/tests/integration-tests/src/test/kotlin/IntegrationTestsRunner.kt similarity index 56% rename from tests/integration-tests/src/test/kotlin/runners/IntegrationTestsRunner.kt rename to tests/integration-tests/src/test/kotlin/IntegrationTestsRunner.kt index 4613f9d205..c7890cb725 100644 --- a/tests/integration-tests/src/test/kotlin/runners/IntegrationTestsRunner.kt +++ b/tests/integration-tests/src/test/kotlin/IntegrationTestsRunner.kt @@ -1,19 +1,11 @@ -package runners - import io.cucumber.junit.CucumberOptions import net.serenitybdd.cucumber.CucumberWithSerenity import org.junit.runner.RunWith @CucumberOptions( - features = [ - "src/test/resources/features" - ], - glue = ["features"], + features = ["src/test/resources/features"], snippets = CucumberOptions.SnippetType.CAMELCASE, - plugin = [ - "pretty", - "json:target/serenity-reports/cucumber_report.json" - ] + plugin = ["pretty"], ) @RunWith(CucumberWithSerenity::class) class IntegrationTestsRunner diff --git a/tests/integration-tests/src/test/kotlin/abilities/ListenToEvents.kt b/tests/integration-tests/src/test/kotlin/abilities/ListenToEvents.kt index 23a6b2744f..6a360d92f9 100644 --- a/tests/integration-tests/src/test/kotlin/abilities/ListenToEvents.kt +++ b/tests/integration-tests/src/test/kotlin/abilities/ListenToEvents.kt @@ -19,7 +19,7 @@ import java.time.OffsetDateTime open class ListenToEvents( private val url: URL, - webhookPort: Int? + webhookPort: Int?, ) : Ability, HasTeardown { private val server: ApplicationEngine @@ -68,7 +68,7 @@ open class ListenToEvents( Netty, port = webhookPort ?: url.port, host = "0.0.0.0", - module = { route(this) } + module = { route(this) }, ) .start(wait = false) } diff --git a/tests/integration-tests/src/test/kotlin/common/TestConstants.kt b/tests/integration-tests/src/test/kotlin/common/TestConstants.kt index 694049f6e1..8d0e42410d 100644 --- a/tests/integration-tests/src/test/kotlin/common/TestConstants.kt +++ b/tests/integration-tests/src/test/kotlin/common/TestConstants.kt @@ -7,7 +7,7 @@ import java.time.Duration import java.util.* object TestConstants { - val TESTS_CONFIG = System.getenv("TESTS_CONFIG") ?: "/configs/basic.conf" + val TESTS_CONFIG = System.getProperty("TESTS_CONFIG") ?: "/configs/basic.conf" val TEST_VERIFICATION_POLICY = VerificationPolicyInput( name = "Trusted Issuer and SchemaID", description = "Verification Policy with trusted issuer and schemaId", @@ -16,10 +16,10 @@ object TestConstants { schemaId = "http://atalaprism.io/schemas/1.0/StudentCredential", trustedIssuers = listOf( "did:example:123456789abcdefghi", - "did:example:123456789abcdefghj" - ) - ) - ) + "did:example:123456789abcdefghj", + ), + ), + ), ) val CREDENTIAL_SCHEMA_TYPE = "https://w3c-ccg.github.io/vc-json-schemas/schema/2.0/schema.json" val SCHEMA_TYPE_JSON = "https://json-schema.org/draft/2020-12/schema" @@ -30,20 +30,10 @@ object TestConstants { type = "object", properties = mutableMapOf( "name" to JsonSchemaProperty(type = "string"), - "age" to JsonSchemaProperty(type = "integer") - ) + "age" to JsonSchemaProperty(type = "integer"), + ), ) - fun generate_with_name_suffix_and_author(suffix: String, author: String): CredentialSchemaInput { - return CredentialSchemaInput( - author = author, - name = "${UUID.randomUUID()} $suffix", - description = "Simple student credentials schema", - type = CREDENTIAL_SCHEMA_TYPE, - schema = jsonSchema, - tags = listOf("school", "students"), - version = "1.0.0" - ) - } + val STUDENT_SCHEMA = CredentialSchemaInput( author = "did:prism:agent", name = UUID.randomUUID().toString(), @@ -51,21 +41,22 @@ object TestConstants { type = CREDENTIAL_SCHEMA_TYPE, schema = jsonSchema, tags = listOf("school", "students"), - version = "1.0.0" + version = "1.0.0", ) + val DID_UPDATE_PUBLISH_MAX_WAIT_5_MIN = Duration.ofSeconds(60L) val PRISM_DID_AUTH_KEY = ManagedDIDKeyTemplate("auth-1", Purpose.AUTHENTICATION) val PRISM_DID_UPDATE_NEW_AUTH_KEY = ManagedDIDKeyTemplate("auth-2", Purpose.AUTHENTICATION) val PRISM_DID_SERVICE_FOR_UPDATE = Service( "https://update.com", listOf("LinkedDomains"), - Json("https://update.com/") + Json("https://update.com/"), ) val PRISM_DID_UPDATE_NEW_SERVICE_URL = "https://bar.foo.com/" val PRISM_DID_UPDATE_NEW_SERVICE = Service( "https://new.service.com", listOf("LinkedDomains"), - Json("https://new.service.com/") + Json("https://new.service.com/"), ) val EVENT_TYPE_CONNECTION_UPDATED = "ConnectionUpdated" val EVENT_TYPE_ISSUE_CREDENTIAL_RECORD_UPDATED = "IssueCredentialRecordUpdated" diff --git a/tests/integration-tests/src/test/kotlin/common/Utils.kt b/tests/integration-tests/src/test/kotlin/common/Utils.kt index 2ccbd5b5c1..627dab4357 100644 --- a/tests/integration-tests/src/test/kotlin/common/Utils.kt +++ b/tests/integration-tests/src/test/kotlin/common/Utils.kt @@ -2,7 +2,6 @@ package common import org.awaitility.Awaitility import org.awaitility.core.ConditionTimeoutException -import org.awaitility.kotlin.withPollInterval import org.awaitility.pollinterval.FixedPollInterval import java.time.Duration @@ -11,10 +10,10 @@ object Utils { blockToWait: () -> Boolean, errorMessage: String, poolInterval: FixedPollInterval = FixedPollInterval(Duration.ofMillis(500L)), - timeout: Duration = Duration.ofSeconds(120L) + timeout: Duration = Duration.ofSeconds(120L), ) { try { - Awaitility.await().withPollInterval(poolInterval) + Awaitility.await().with().pollInterval(poolInterval) .pollInSameThread() .atMost(timeout) .until { @@ -22,7 +21,7 @@ object Utils { } } catch (err: ConditionTimeoutException) { throw ConditionTimeoutException( - errorMessage + errorMessage, ) } } diff --git a/tests/integration-tests/src/test/kotlin/config/Config.kt b/tests/integration-tests/src/test/kotlin/config/Config.kt index 506ee7290f..49c59291c8 100644 --- a/tests/integration-tests/src/test/kotlin/config/Config.kt +++ b/tests/integration-tests/src/test/kotlin/config/Config.kt @@ -6,5 +6,5 @@ import config.services.Service data class Config( val roles: List, val agents: List?, - val services: Service? + val services: Service?, ) diff --git a/tests/integration-tests/src/test/kotlin/config/VaultAuthType.kt b/tests/integration-tests/src/test/kotlin/config/VaultAuthType.kt new file mode 100644 index 0000000000..0bda8e5904 --- /dev/null +++ b/tests/integration-tests/src/test/kotlin/config/VaultAuthType.kt @@ -0,0 +1,6 @@ +package config + +enum class VaultAuthType { + APP_ROLE, + TOKEN +} diff --git a/tests/integration-tests/src/test/kotlin/config/Webhook.kt b/tests/integration-tests/src/test/kotlin/config/Webhook.kt index c1b174329e..283904d839 100644 --- a/tests/integration-tests/src/test/kotlin/config/Webhook.kt +++ b/tests/integration-tests/src/test/kotlin/config/Webhook.kt @@ -6,5 +6,5 @@ import java.net.URL data class Webhook( val url: URL, @ConfigAlias("local_port") val localPort: Int? = null, - @ConfigAlias("init_required") val initRequired: Boolean = true + @ConfigAlias("init_required") val initRequired: Boolean = true, ) diff --git a/tests/integration-tests/src/test/kotlin/config/services/Agent.kt b/tests/integration-tests/src/test/kotlin/config/services/Agent.kt index 6201dfb605..1c25fb0efb 100644 --- a/tests/integration-tests/src/test/kotlin/config/services/Agent.kt +++ b/tests/integration-tests/src/test/kotlin/config/services/Agent.kt @@ -1,6 +1,7 @@ package config.services import com.sksamuel.hoplite.ConfigAlias +import config.VaultAuthType import org.testcontainers.containers.ComposeContainer import org.testcontainers.containers.wait.strategy.Wait import java.io.File @@ -18,16 +19,16 @@ data class Agent( @ConfigAlias("keep_running") override val keepRunning: Boolean = false ) : ServiceBase { - override val env: ComposeContainer = ComposeContainer( - File("src/test/resources/containers/agent.yml") - ).withEnv( - mapOf( + override val container: ComposeContainer + + init { + val env = mutableMapOf( "OPEN_ENTERPRISE_AGENT_VERSION" to version, "API_KEY_ENABLED" to authEnabled.toString(), "AGENT_DIDCOMM_PORT" to didcommPort.toString(), - "DIDCOMM_SERVICE_URL" to (didcommServiceUrl ?: "http://host.docker.internal:${didcommPort}"), + "DIDCOMM_SERVICE_URL" to (didcommServiceUrl ?: "http://host.docker.internal:$didcommPort"), "AGENT_HTTP_PORT" to httpPort.toString(), - "REST_SERVICE_URL" to (restServiceUrl ?: "http://host.docker.internal:${httpPort}"), + "REST_SERVICE_URL" to (restServiceUrl ?: "http://host.docker.internal:$httpPort"), "PRISM_NODE_PORT" to (prismNode?.httpPort?.toString() ?: ""), "SECRET_STORAGE_BACKEND" to if (vault != null) "vault" else "postgres", "VAULT_HTTP_PORT" to (vault?.httpPort?.toString() ?: ""), @@ -35,7 +36,20 @@ data class Agent( "KEYCLOAK_HTTP_PORT" to (keycloak?.httpPort?.toString() ?: ""), "KEYCLOAK_REALM" to (keycloak?.realm ?: ""), "KEYCLOAK_CLIENT_ID" to (keycloak?.clientId ?: ""), - "KEYCLOAK_CLIENT_SECRET" to (keycloak?.clientSecret ?: "") + "KEYCLOAK_CLIENT_SECRET" to (keycloak?.clientSecret ?: ""), ) - ).waitingFor("open-enterprise-agent", Wait.forHealthcheck()) + + // setup token authentication + if (vault?.authType == VaultAuthType.TOKEN) { + env["VAULT_TOKEN"] = "root" + } else { + env["VAULT_APPROLE_ROLE_ID"] = "agent" + env["VAULT_APPROLE_SECRET_ID"] = "agent-secret" + } + + container = ComposeContainer( + File("src/test/resources/containers/agent.yml")) + .withEnv(env) + .waitingFor("open-enterprise-agent", Wait.forHealthcheck()) + } } diff --git a/tests/integration-tests/src/test/kotlin/config/services/Keycloak.kt b/tests/integration-tests/src/test/kotlin/config/services/Keycloak.kt index eb1a564e43..289cb1cc68 100644 --- a/tests/integration-tests/src/test/kotlin/config/services/Keycloak.kt +++ b/tests/integration-tests/src/test/kotlin/config/services/Keycloak.kt @@ -3,6 +3,7 @@ package config.services import com.sksamuel.hoplite.ConfigAlias import config.AgentRole import config.Role +import io.iohk.atala.automation.utils.Logger import io.restassured.RestAssured import io.restassured.builder.RequestSpecBuilder import io.restassured.specification.RequestSpecification @@ -16,22 +17,28 @@ data class Keycloak( val realm: String = "atala-demo", @ConfigAlias("client_id") val clientId: String = "prism-agent", @ConfigAlias("client_secret") val clientSecret: String = "prism-agent-demo-secret", - @ConfigAlias("keep_running") override val keepRunning: Boolean = false + @ConfigAlias("keep_running") override val keepRunning: Boolean = false, ) : ServiceBase { - + private val logger = Logger.get() private val keycloakComposeFile = "src/test/resources/containers/keycloak.yml" private val keycloakEnvConfig: Map = mapOf( - "KEYCLOAK_HTTP_PORT" to httpPort.toString() + "KEYCLOAK_HTTP_PORT" to httpPort.toString(), ) private val keycloakClientRoles: List = AgentRole.values().map { it.roleName } - override val env: ComposeContainer = + override val container: ComposeContainer = ComposeContainer(File(keycloakComposeFile)).withEnv(keycloakEnvConfig) .waitingFor("keycloak", Wait.forLogMessage(".*Running the server.*", 1)) private val keycloakBaseUrl = "http://localhost:$httpPort/" private var requestBuilder: RequestSpecification? = null + private var users: List = emptyList() + + fun setUsers(users: List): ServiceBase { + this.users = users + return this + } - fun start(users: List) { - super.start() + override fun postStart() { + logger.info("Setting up Keycloak") initRequestBuilder() createRealm() createClient() @@ -77,8 +84,8 @@ data class Keycloak( mapOf( "realm" to realm, "enabled" to true, - "accessTokenLifespan" to 3600000 - ) + "accessTokenLifespan" to 3600000, + ), ) .post("/admin/realms") .then().statusCode(HttpStatus.SC_CREATED) @@ -92,8 +99,8 @@ data class Keycloak( "directAccessGrantsEnabled" to true, "authorizationServicesEnabled" to true, "serviceAccountsEnabled" to true, - "secret" to clientSecret - ) + "secret" to clientSecret, + ), ) .post("/admin/realms/$realm/clients") .then().statusCode(HttpStatus.SC_CREATED) @@ -104,8 +111,8 @@ data class Keycloak( RestAssured.given().spec(requestBuilder) .body( mapOf( - "name" to roleName - ) + "name" to roleName, + ), ) .post("/admin/realms/$realm/clients/$clientId/roles") .then().statusCode(HttpStatus.SC_CREATED) @@ -125,10 +132,10 @@ data class Keycloak( "credentials" to listOf( mapOf( "value" to keycloakUser, - "temporary" to false - ) - ) - ) + "temporary" to false, + ), + ), + ), ) .post("/admin/realms/$realm/users") .thenReturn() @@ -154,9 +161,9 @@ data class Keycloak( listOf( mapOf( "name" to role.roleName, - "id" to clientRoleId - ) - ) + "id" to clientRoleId, + ), + ), ) .post("/admin/realms/$realm/users/$userId/role-mappings/clients/$clientId") .then().statusCode(HttpStatus.SC_NO_CONTENT) diff --git a/tests/integration-tests/src/test/kotlin/config/services/PrismNode.kt b/tests/integration-tests/src/test/kotlin/config/services/PrismNode.kt index 177ce72033..b9e712bea7 100644 --- a/tests/integration-tests/src/test/kotlin/config/services/PrismNode.kt +++ b/tests/integration-tests/src/test/kotlin/config/services/PrismNode.kt @@ -8,16 +8,16 @@ import java.io.File data class PrismNode( @ConfigAlias("http_port") val httpPort: Int, val version: String, - @ConfigAlias("keep_running") override val keepRunning: Boolean = false + @ConfigAlias("keep_running") override val keepRunning: Boolean = false, ) : ServiceBase { private val vdrComposeFile = "src/test/resources/containers/vdr.yml" - override val env: ComposeContainer = ComposeContainer(File(vdrComposeFile)).withEnv( + override val container: ComposeContainer = ComposeContainer(File(vdrComposeFile)).withEnv( mapOf( "PRISM_NODE_VERSION" to version, - "PRISM_NODE_PORT" to httpPort.toString() - ) + "PRISM_NODE_PORT" to httpPort.toString(), + ), ).waitingFor( "prism-node", - Wait.forLogMessage(".*Server started, listening on.*", 1) + Wait.forLogMessage(".*Server started, listening on.*", 1), ) } diff --git a/tests/integration-tests/src/test/kotlin/config/services/Service.kt b/tests/integration-tests/src/test/kotlin/config/services/Service.kt index c9a16443bb..cef1310379 100644 --- a/tests/integration-tests/src/test/kotlin/config/services/Service.kt +++ b/tests/integration-tests/src/test/kotlin/config/services/Service.kt @@ -5,5 +5,5 @@ import com.sksamuel.hoplite.ConfigAlias data class Service( @ConfigAlias("prism_node") val prismNode: PrismNode?, val keycloak: Keycloak?, - val vault: Vault? + val vault: Vault?, ) diff --git a/tests/integration-tests/src/test/kotlin/config/services/ServiceBase.kt b/tests/integration-tests/src/test/kotlin/config/services/ServiceBase.kt index 739a7e17a8..8b3bbf97ca 100644 --- a/tests/integration-tests/src/test/kotlin/config/services/ServiceBase.kt +++ b/tests/integration-tests/src/test/kotlin/config/services/ServiceBase.kt @@ -2,20 +2,27 @@ package config.services import com.sksamuel.hoplite.ConfigAlias import org.testcontainers.containers.ComposeContainer +import org.testcontainers.lifecycle.Startable -interface ServiceBase { +interface ServiceBase : Startable { - val env: ComposeContainer + val container: ComposeContainer @ConfigAlias("keep_running") val keepRunning: Boolean - fun start() { - env.start() + + override fun start() { + container.start() + postStart() + } + + fun postStart() { + } - fun stop() { + override fun stop() { if (!keepRunning) { - env.stop() + container.stop() } } } diff --git a/tests/integration-tests/src/test/kotlin/config/services/Vault.kt b/tests/integration-tests/src/test/kotlin/config/services/Vault.kt index e2febf6889..2053e82c91 100644 --- a/tests/integration-tests/src/test/kotlin/config/services/Vault.kt +++ b/tests/integration-tests/src/test/kotlin/config/services/Vault.kt @@ -2,6 +2,8 @@ package config.services import com.github.dockerjava.zerodep.shaded.org.apache.hc.core5.http.HttpStatus import com.sksamuel.hoplite.ConfigAlias +import config.VaultAuthType +import io.iohk.atala.automation.utils.Logger import io.restassured.RestAssured import io.restassured.builder.RequestSpecBuilder import io.restassured.specification.RequestSpecification @@ -11,29 +13,33 @@ import java.io.File data class Vault( @ConfigAlias("http_port") val httpPort: Int, - @ConfigAlias("keep_running") override val keepRunning: Boolean = false + @ConfigAlias("keep_running") override val keepRunning: Boolean = false, + @ConfigAlias("vault_auth_type") val authType: VaultAuthType = VaultAuthType.APP_ROLE, ) : ServiceBase { - + private val logger = Logger.get() private val vaultComposeFile: String = "src/test/resources/containers/vault.yml" - override val env: ComposeContainer = ComposeContainer(File(vaultComposeFile)).withEnv( + override val container: ComposeContainer = ComposeContainer(File(vaultComposeFile)).withEnv( mapOf( - "VAULT_PORT" to httpPort.toString() - ) + "VAULT_PORT" to httpPort.toString(), + + ), ).waitingFor( "vault", - Wait.forHealthcheck() + Wait.forHealthcheck(), ) private val vaultBaseUrl = "http://localhost:$httpPort/" private var requestBuilder: RequestSpecification? = null private val appRoleName = "agent" private val appRolePolicyName = "agent-policy" - override fun start() { - super.start() - initRequestBuilder() - enableAppRoleAuth() - createAppRolePolicy() - createAppRole() + override fun postStart() { + if (authType == VaultAuthType.APP_ROLE) { + logger.info("Setting up Vault app roles") + initRequestBuilder() + enableAppRoleAuth() + createAppRolePolicy() + createAppRole() + } } private fun initRequestBuilder() { @@ -48,8 +54,8 @@ data class Vault( RestAssured.given().spec(requestBuilder) .body( mapOf( - "type" to "approle" - ) + "type" to "approle", + ), ) .post("/v1/sys/auth/approle") .then().statusCode(HttpStatus.SC_NO_CONTENT) @@ -63,8 +69,8 @@ data class Vault( path "secret/*" { capabilities = ["create", "read", "update", "patch", "delete", "list"] } - """ - ) + """, + ), ) .post("/v1/sys/policy/$appRolePolicyName") .then().statusCode(HttpStatus.SC_NO_CONTENT) @@ -75,8 +81,8 @@ data class Vault( .body( mapOf( "token_policies" to appRolePolicyName, - "token_ttl" to "60s" - ) + "token_ttl" to "60s", + ), ) .post("/v1/auth/approle/role/$appRoleName") .then().statusCode(HttpStatus.SC_NO_CONTENT) @@ -85,8 +91,8 @@ data class Vault( RestAssured.given().spec(requestBuilder) .body( mapOf( - "role_id" to "agent" - ) + "role_id" to "agent", + ), ) .post("/v1/auth/approle/role/$appRoleName/role-id") .then().statusCode(HttpStatus.SC_NO_CONTENT) @@ -95,8 +101,8 @@ data class Vault( RestAssured.given().spec(requestBuilder) .body( mapOf( - "secret_id" to "agent-secret" - ) + "secret_id" to "agent-secret", + ), ) .post("/v1/auth/approle/role/$appRoleName/custom-secret-id") .then().statusCode(HttpStatus.SC_OK) diff --git a/tests/integration-tests/src/test/kotlin/features/Init.kt b/tests/integration-tests/src/test/kotlin/features/Init.kt deleted file mode 100644 index 77b6579987..0000000000 --- a/tests/integration-tests/src/test/kotlin/features/Init.kt +++ /dev/null @@ -1,171 +0,0 @@ -package features - -import abilities.ListenToEvents -import com.sksamuel.hoplite.ConfigException -import com.sksamuel.hoplite.ConfigLoader -import common.TestConstants -import config.AgentRole -import config.Config -import io.cucumber.java.AfterAll -import io.cucumber.java.BeforeAll -import io.iohk.atala.prism.models.CreateWalletRequest -import io.iohk.atala.prism.models.CreateWebhookNotification -import io.restassured.RestAssured -import io.restassured.builder.RequestSpecBuilder -import net.serenitybdd.screenplay.Actor -import net.serenitybdd.screenplay.actors.Cast -import net.serenitybdd.screenplay.actors.OnStage -import net.serenitybdd.screenplay.rest.abilities.CallAnApi -import org.apache.http.HttpStatus -import java.util.* - -val config = ConfigLoader().loadConfigOrThrow(TestConstants.TESTS_CONFIG) - -/** - * This function starts all services and actors before all tests. - */ -fun initServices() { - config.services?.keycloak?.start(config.roles) - config.services?.prismNode?.start() - config.services?.vault?.start() - config.agents?.forEach { agent -> - agent.start() - } -} - -/** - * This function initializes all actors and sets the stage. - */ -fun initActors() { - /** - * This function initializes a wallet for an actor when Keycloak is used. - * - * @param actor The actor for which the wallet should be initialized. - */ - fun initializeWallet(actor: Actor) { - RestAssured - .given() - .baseUri(actor.usingAbilityTo(CallAnApi::class.java).resolve("/")) - .auth().oauth2(actor.recall("BEARER_TOKEN")) - .body( - CreateWalletRequest( - name = UUID.randomUUID().toString(), - ), - ) - .post("/wallets") - .then().statusCode(HttpStatus.SC_CREATED) - } - - /** - * This function registers a webhook for an actor. - * - * @param actor The actor for which the webhook should be registered. - * @param webhookUrl The url of the webhook. - */ - fun registerWebhook(actor: Actor, webhookUrl: String) { - val spec = RequestSpecBuilder() - .setBaseUri(actor.usingAbilityTo(CallAnApi::class.java).resolve("/")) - if (actor.recall("AUTH_KEY") != null) { - spec.addHeader(actor.recall("AUTH_HEADER"), actor.recall("AUTH_KEY")) - } - if (actor.recall("BEARER_TOKEN") != null) { - spec.addHeader("Authorization", "Bearer ${actor.recall("BEARER_TOKEN")}") - } - val response = RestAssured - .given().spec(spec.build()) - .body(CreateWebhookNotification(url = webhookUrl)) - .post("/events/webhooks") - .thenReturn() - response.then().statusCode(HttpStatus.SC_OK) - actor.remember("WEBHOOK_ID", response.body.jsonPath().getString("id")) - } - - val cast = Cast() - config.roles.forEach { role -> - cast.actorNamed( - role.name, - CallAnApi.at(role.url.toExternalForm()), - ) - } - if (config.services?.keycloak != null) { - config.roles.forEach { role -> - val actor = cast.actorNamed(role.name) - try { - actor.remember("BEARER_TOKEN", config.services.keycloak.getKeycloakAuthToken(actor.name, actor.name)) - } catch (e: NullPointerException) { - throw ConfigException("Keycloak is configured, but no token found for user ${actor.name}!") - } - if (role.agentRole != AgentRole.Admin) { - initializeWallet(actor) - } - } - } - config.roles.forEach { role -> - val actor = cast.actorNamed(role.name) - if (role.apikey != null) { - actor.remember("AUTH_KEY", role.apikey) - actor.remember("AUTH_HEADER", role.authHeader) - } - if (role.token != null) { - actor.remember("BEARER_TOKEN", role.token) - } - if (role.webhook != null) { - actor.whoCan(ListenToEvents.at(role.webhook.url, role.webhook.localPort)) - if (role.webhook.initRequired) { - registerWebhook(actor, role.webhook.url.toExternalForm()) - } - } - } - OnStage.setTheStage(cast) -} - -/** - * This function destroys all actors and clears the stage. - */ -fun destroyActors() { - /** - * This function deletes a webhook for an actor. - * - * @param actor The actor for which the webhook should be deleted. - */ - fun deleteWebhook(actor: Actor) { - val spec = RequestSpecBuilder() - .setBaseUri(actor.usingAbilityTo(CallAnApi::class.java).resolve("/")) - if (actor.recall("AUTH_KEY") != null) { - spec.addHeader(actor.recall("AUTH_HEADER"), actor.recall("AUTH_KEY")) - } - if (actor.recall("BEARER_TOKEN") != null) { - spec.addHeader("Authorization", "Bearer ${actor.recall("BEARER_TOKEN")}") - } - RestAssured - .given().spec(spec.build()) - .delete("/events/webhooks/${actor.recall("WEBHOOK_ID")}") - .then().statusCode(HttpStatus.SC_OK) - } - - // Delete webhooks - config.roles.forEach { role -> - val actor = OnStage.theActorCalled(role.name) - if (role.webhook != null && role.webhook.initRequired) { - deleteWebhook(actor) - } - } - OnStage.drawTheCurtain() -} - -@BeforeAll -fun init() { - initServices() - initActors() -} - -@AfterAll -fun clearStage() { - destroyActors() - config.agents?.forEach { agent -> - agent.stop() - } - config.services?.keycloak?.stop() - config.services?.prismNode?.stop() - config.services?.vault?.stop() -} diff --git a/tests/integration-tests/src/main/kotlin/models/AnoncredsSchema.kt b/tests/integration-tests/src/test/kotlin/models/AnoncredsSchema.kt similarity index 89% rename from tests/integration-tests/src/main/kotlin/models/AnoncredsSchema.kt rename to tests/integration-tests/src/test/kotlin/models/AnoncredsSchema.kt index ae79a5d118..e581597583 100644 --- a/tests/integration-tests/src/main/kotlin/models/AnoncredsSchema.kt +++ b/tests/integration-tests/src/test/kotlin/models/AnoncredsSchema.kt @@ -13,5 +13,5 @@ class AnoncredsSchema( val issuerId: String, @SerializedName("attrNames") - val attrNames: List + val attrNames: List, ) diff --git a/tests/integration-tests/src/main/kotlin/models/Events.kt b/tests/integration-tests/src/test/kotlin/models/Events.kt similarity index 83% rename from tests/integration-tests/src/main/kotlin/models/Events.kt rename to tests/integration-tests/src/test/kotlin/models/Events.kt index 0295c3a713..92c87074b9 100644 --- a/tests/integration-tests/src/main/kotlin/models/Events.kt +++ b/tests/integration-tests/src/test/kotlin/models/Events.kt @@ -12,7 +12,7 @@ data class Event( @SerializedName("id") var id: String, @SerializedName("ts") var ts: String, @SerializedName("data") var data: JsonElement, - @SerializedName("walletId") var walletId: String + @SerializedName("walletId") var walletId: String, ) data class ConnectionEvent( @@ -20,7 +20,7 @@ data class ConnectionEvent( @SerializedName("id") var id: String, @SerializedName("ts") var ts: String, @SerializedName("data") var data: Connection, - @SerializedName("walletId") var walletId: String + @SerializedName("walletId") var walletId: String, ) data class CredentialEvent( @@ -28,7 +28,7 @@ data class CredentialEvent( @SerializedName("id") var id: String, @SerializedName("ts") var ts: String, @SerializedName("data") var data: IssueCredentialRecord, - @SerializedName("walletId") var walletId: String + @SerializedName("walletId") var walletId: String, ) data class PresentationEvent( @@ -36,7 +36,7 @@ data class PresentationEvent( @SerializedName("id") var id: String, @SerializedName("ts") var ts: String, @SerializedName("data") var data: PresentationStatus, - @SerializedName("walletId") var walletId: String + @SerializedName("walletId") var walletId: String, ) data class DidEvent( @@ -44,5 +44,5 @@ data class DidEvent( @SerializedName("id") var id: String, @SerializedName("ts") var ts: String, @SerializedName("data") var data: ManagedDID, - @SerializedName("walletId") var walletId: String + @SerializedName("walletId") var walletId: String, ) diff --git a/tests/integration-tests/src/main/kotlin/models/JsonSchema.kt b/tests/integration-tests/src/test/kotlin/models/JsonSchema.kt similarity index 97% rename from tests/integration-tests/src/main/kotlin/models/JsonSchema.kt rename to tests/integration-tests/src/test/kotlin/models/JsonSchema.kt index a7e1654e0b..2599b949f5 100644 --- a/tests/integration-tests/src/main/kotlin/models/JsonSchema.kt +++ b/tests/integration-tests/src/test/kotlin/models/JsonSchema.kt @@ -16,5 +16,5 @@ data class JsonSchema( var type: String = "", @SerializedName("properties") - val properties: MutableMap = mutableMapOf() + val properties: MutableMap = mutableMapOf(), ) diff --git a/tests/integration-tests/src/main/kotlin/models/JsonSchemaProperty.kt b/tests/integration-tests/src/test/kotlin/models/JsonSchemaProperty.kt similarity index 82% rename from tests/integration-tests/src/main/kotlin/models/JsonSchemaProperty.kt rename to tests/integration-tests/src/test/kotlin/models/JsonSchemaProperty.kt index dc118bf7b0..7d8cceb892 100644 --- a/tests/integration-tests/src/main/kotlin/models/JsonSchemaProperty.kt +++ b/tests/integration-tests/src/test/kotlin/models/JsonSchemaProperty.kt @@ -4,5 +4,5 @@ import com.google.gson.annotations.SerializedName data class JsonSchemaProperty( @SerializedName("type") - var type: String = "" + var type: String = "", ) diff --git a/tests/integration-tests/src/test/kotlin/steps/Setup.kt b/tests/integration-tests/src/test/kotlin/steps/Setup.kt new file mode 100644 index 0000000000..25e021ba56 --- /dev/null +++ b/tests/integration-tests/src/test/kotlin/steps/Setup.kt @@ -0,0 +1,179 @@ +package steps + +import abilities.ListenToEvents +import com.sksamuel.hoplite.ConfigException +import com.sksamuel.hoplite.ConfigLoader +import common.TestConstants +import config.AgentRole +import config.Config +import io.cucumber.java.AfterAll +import io.cucumber.java.BeforeAll +import io.iohk.atala.prism.models.CreateWalletRequest +import io.iohk.atala.prism.models.CreateWebhookNotification +import io.restassured.RestAssured +import io.restassured.builder.RequestSpecBuilder +import net.serenitybdd.screenplay.Actor +import net.serenitybdd.screenplay.actors.Cast +import net.serenitybdd.screenplay.actors.OnStage +import net.serenitybdd.screenplay.rest.abilities.CallAnApi +import org.apache.http.HttpStatus +import java.util.UUID + +object Setup { + val config = ConfigLoader().loadConfigOrThrow(TestConstants.TESTS_CONFIG) + + /** + * This function starts all services and actors before all tests. + */ + fun initServices() { + config.services?.keycloak?.setUsers(config.roles)?.start() + config.services?.prismNode?.start() + config.services?.vault?.start() + config.agents?.forEach { + it.start() + } + } + + /** + * This function initializes all actors and sets the stage. + */ + fun initActors() { + /** + * This function initializes a wallet for an actor when Keycloak is used. + * + * @param actor The actor for which the wallet should be initialized. + */ + fun initializeWallet(actor: Actor) { + RestAssured + .given() + .baseUri(actor.usingAbilityTo(CallAnApi::class.java).resolve("/")) + .auth().oauth2(actor.recall("BEARER_TOKEN")) + .body( + CreateWalletRequest( + name = UUID.randomUUID().toString(), + ), + ) + .post("/wallets") + .then().statusCode(HttpStatus.SC_CREATED) + } + + /** + * This function registers a webhook for an actor. + * + * @param actor The actor for which the webhook should be registered. + * @param webhookUrl The url of the webhook. + */ + fun registerWebhook(actor: Actor, webhookUrl: String) { + val spec = RequestSpecBuilder() + .setBaseUri(actor.usingAbilityTo(CallAnApi::class.java).resolve("/")) + if (actor.recall("AUTH_KEY") != null) { + spec.addHeader(actor.recall("AUTH_HEADER"), actor.recall("AUTH_KEY")) + } + if (actor.recall("BEARER_TOKEN") != null) { + spec.addHeader("Authorization", "Bearer ${actor.recall("BEARER_TOKEN")}") + } + val response = RestAssured + .given().spec(spec.build()) + .body(CreateWebhookNotification(url = webhookUrl)) + .post("/events/webhooks") + .thenReturn() + response.then().statusCode(HttpStatus.SC_OK) + actor.remember("WEBHOOK_ID", response.body.jsonPath().getString("id")) + } + + val cast = Cast() + config.roles.forEach { role -> + cast.actorNamed( + role.name, + CallAnApi.at(role.url.toExternalForm()), + ) + } + if (config.services?.keycloak != null) { + config.roles.forEach { role -> + val actor = cast.actorNamed(role.name) + try { + actor.remember("BEARER_TOKEN", config.services.keycloak.getKeycloakAuthToken(actor.name, actor.name)) + } catch (e: NullPointerException) { + throw ConfigException("Keycloak is configured, but no token found for user ${actor.name}!") + } + if (role.agentRole != AgentRole.Admin) { + initializeWallet(actor) + } + } + } + config.roles.forEach { role -> + val actor = cast.actorNamed(role.name) + if (role.apikey != null) { + actor.remember("AUTH_KEY", role.apikey) + actor.remember("AUTH_HEADER", role.authHeader) + } + if (role.token != null) { + actor.remember("BEARER_TOKEN", role.token) + } + if (role.webhook != null) { + actor.whoCan(ListenToEvents.at(role.webhook.url, role.webhook.localPort)) + if (role.webhook.initRequired) { + registerWebhook(actor, role.webhook.url.toExternalForm()) + } + } + } + OnStage.setTheStage(cast) + } + + /** + * This function destroys all actors and clears the stage. + */ + fun stopActors() { + /** + * This function deletes a webhook for an actor. + * + * @param actor The actor for which the webhook should be deleted. + */ + fun deleteWebhook(actor: Actor) { + val spec = RequestSpecBuilder() + .setBaseUri(actor.usingAbilityTo(CallAnApi::class.java).resolve("/")) + if (actor.recall("AUTH_KEY") != null) { + spec.addHeader(actor.recall("AUTH_HEADER"), actor.recall("AUTH_KEY")) + } + if (actor.recall("BEARER_TOKEN") != null) { + spec.addHeader("Authorization", "Bearer ${actor.recall("BEARER_TOKEN")}") + } + RestAssured + .given().spec(spec.build()) + .delete("/events/webhooks/${actor.recall("WEBHOOK_ID")}") + .then().statusCode(HttpStatus.SC_OK) + } + + // Delete webhooks + config.roles.forEach { role -> + val actor = OnStage.theActorCalled(role.name) + if (role.webhook != null && role.webhook.initRequired) { + deleteWebhook(actor) + } + } + OnStage.drawTheCurtain() + } + + /** + * Stop all the created services, unless they are set to keep running. + */ + fun stopServices() { + config.agents?.forEach { agent -> + agent.stop() + } + config.services?.keycloak?.stop() + config.services?.prismNode?.stop() + config.services?.vault?.stop() + } +} +@BeforeAll +fun init() { + Setup.initServices() + Setup.initActors() +} + +@AfterAll +fun clearStage() { + Setup.stopActors() + Setup.stopServices() +} diff --git a/tests/integration-tests/src/test/kotlin/features/common/CommonSteps.kt b/tests/integration-tests/src/test/kotlin/steps/common/CommonSteps.kt similarity index 91% rename from tests/integration-tests/src/test/kotlin/features/common/CommonSteps.kt rename to tests/integration-tests/src/test/kotlin/steps/common/CommonSteps.kt index 147fe2f179..45e738ae5a 100644 --- a/tests/integration-tests/src/test/kotlin/features/common/CommonSteps.kt +++ b/tests/integration-tests/src/test/kotlin/steps/common/CommonSteps.kt @@ -1,8 +1,8 @@ -package features.common +package steps.common -import features.connection.ConnectionSteps -import features.credentials.IssueCredentialsSteps -import features.did.PublishDidSteps +import steps.connection.ConnectionSteps +import steps.credentials.IssueCredentialsSteps +import steps.did.PublishDidSteps import interactions.Get import io.cucumber.java.ParameterType import io.cucumber.java.en.Given @@ -26,10 +26,10 @@ class CommonSteps { @Given("{actor} has an issued credential from {actor}") fun holderHasIssuedCredentialFromIssuer(holder: Actor, issuer: Actor) { holder.attemptsTo( - Get.resource("/issue-credentials/records") + Get.resource("/issue-credentials/records"), ) holder.attemptsTo( - Ensure.thatTheLastResponse().statusCode().isEqualTo(HttpStatus.SC_OK) + Ensure.thatTheLastResponse().statusCode().isEqualTo(HttpStatus.SC_OK), ) val receivedCredential = SerenityRest.lastResponse().get().contents!!.findLast { credential -> credential.protocolState == IssueCredentialRecord.ProtocolState.CREDENTIAL_RECEIVED && @@ -56,10 +56,10 @@ class CommonSteps { @Given("{actor} and {actor} have an existing connection") fun actorsHaveExistingConnection(inviter: Actor, invitee: Actor) { inviter.attemptsTo( - Get.resource("/connections") + Get.resource("/connections"), ) inviter.attemptsTo( - Ensure.thatTheLastResponse().statusCode().isEqualTo(HttpStatus.SC_OK) + Ensure.thatTheLastResponse().statusCode().isEqualTo(HttpStatus.SC_OK), ) val inviterConnection = SerenityRest.lastResponse().get().contents!!.firstOrNull { it.label == "Connection with ${invitee.name}" && it.state == Connection.State.CONNECTION_RESPONSE_SENT @@ -68,10 +68,10 @@ class CommonSteps { var inviteeConnection: Connection? = null if (inviterConnection != null) { invitee.attemptsTo( - Get.resource("/connections") + Get.resource("/connections"), ) invitee.attemptsTo( - Ensure.thatTheLastResponse().statusCode().isEqualTo(HttpStatus.SC_OK) + Ensure.thatTheLastResponse().statusCode().isEqualTo(HttpStatus.SC_OK), ) inviteeConnection = SerenityRest.lastResponse().get().contents!!.firstOrNull { it.theirDid == inviterConnection.myDid && it.state == Connection.State.CONNECTION_RESPONSE_RECEIVED diff --git a/tests/integration-tests/src/test/kotlin/features/connection/ConnectionSteps.kt b/tests/integration-tests/src/test/kotlin/steps/connection/ConnectionSteps.kt similarity index 94% rename from tests/integration-tests/src/test/kotlin/features/connection/ConnectionSteps.kt rename to tests/integration-tests/src/test/kotlin/steps/connection/ConnectionSteps.kt index 7430f78663..052e3ef80d 100644 --- a/tests/integration-tests/src/test/kotlin/features/connection/ConnectionSteps.kt +++ b/tests/integration-tests/src/test/kotlin/steps/connection/ConnectionSteps.kt @@ -1,4 +1,4 @@ -package features.connection +package steps.connection import abilities.ListenToEvents import common.Utils.wait @@ -28,9 +28,9 @@ class ConnectionSteps { Post.to("/connections") .with { it.body( - CreateConnectionRequest(label = connectionLabel) + CreateConnectionRequest(label = connectionLabel), ) - } + }, ) val connection = SerenityRest.lastResponse().get() @@ -39,7 +39,7 @@ class ConnectionSteps { Ensure.thatTheLastResponse().statusCode().isEqualTo(SC_CREATED), Ensure.that(connection.label!!).isEqualTo(connectionLabel), Ensure.that(connection.state).isEqualTo(Connection.State.INVITATION_GENERATED), - Ensure.that(connection.role).isEqualTo(Connection.Role.INVITER) + Ensure.that(connection.role).isEqualTo(Connection.Role.INVITER), ) // Acme remembers connection to send it out of band to Bob @@ -55,10 +55,10 @@ class ConnectionSteps { .with { it.body( AcceptConnectionInvitationRequest( - inviterConnection.invitation.invitationUrl.split("=")[1] - ) + inviterConnection.invitation.invitationUrl.split("=")[1], + ), ) - } + }, ) val inviteeConnection = SerenityRest.lastResponse().get() @@ -69,7 +69,7 @@ class ConnectionSteps { Ensure.that(inviteeConnection.invitation.invitationUrl).isEqualTo(inviterConnection.invitation.invitationUrl), Ensure.that(inviteeConnection.invitation.type).isEqualTo(inviterConnection.invitation.type), Ensure.that(inviteeConnection.state).isEqualTo(Connection.State.CONNECTION_REQUEST_PENDING), - Ensure.that(inviteeConnection.role).isEqualTo(Connection.Role.INVITEE) + Ensure.that(inviteeConnection.role).isEqualTo(Connection.Role.INVITEE), ) invitee.remember("connection", inviteeConnection) @@ -85,7 +85,7 @@ class ConnectionSteps { lastEvent != null && lastEvent.data.state == Connection.State.CONNECTION_RESPONSE_SENT }, - "Inviter connection didn't reach ${Connection.State.CONNECTION_RESPONSE_SENT} state" + "Inviter connection didn't reach ${Connection.State.CONNECTION_RESPONSE_SENT} state", ) } @@ -100,7 +100,7 @@ class ConnectionSteps { lastEvent != null && lastEvent.data.state == Connection.State.CONNECTION_RESPONSE_RECEIVED }, - "Invitee connection didn't reach ${Connection.State.CONNECTION_RESPONSE_RECEIVED} state." + "Invitee connection didn't reach ${Connection.State.CONNECTION_RESPONSE_RECEIVED} state.", ) } @@ -108,18 +108,18 @@ class ConnectionSteps { fun inviterAndInviteeHaveAConnection(inviter: Actor, invitee: Actor) { // Connection established. Both parties exchanged their DIDs with each other inviter.attemptsTo( - Get.resource("/connections/${inviter.recall("connection").connectionId}") + Get.resource("/connections/${inviter.recall("connection").connectionId}"), ) inviter.attemptsTo( - Ensure.thatTheLastResponse().statusCode().isEqualTo(SC_OK) + Ensure.thatTheLastResponse().statusCode().isEqualTo(SC_OK), ) inviter.remember("connection-with-${invitee.name}", SerenityRest.lastResponse().get()) invitee.attemptsTo( - Get.resource("/connections/${invitee.recall("connection").connectionId}") + Get.resource("/connections/${invitee.recall("connection").connectionId}"), ) invitee.attemptsTo( - Ensure.thatTheLastResponse().statusCode().isEqualTo(SC_OK) + Ensure.thatTheLastResponse().statusCode().isEqualTo(SC_OK), ) invitee.remember("connection-with-${inviter.name}", SerenityRest.lastResponse().get()) diff --git a/tests/integration-tests/src/test/kotlin/features/credentials/IssueCredentialsSteps.kt b/tests/integration-tests/src/test/kotlin/steps/credentials/IssueCredentialsSteps.kt similarity index 92% rename from tests/integration-tests/src/test/kotlin/features/credentials/IssueCredentialsSteps.kt rename to tests/integration-tests/src/test/kotlin/steps/credentials/IssueCredentialsSteps.kt index e504943c5a..deeee3e201 100644 --- a/tests/integration-tests/src/test/kotlin/features/credentials/IssueCredentialsSteps.kt +++ b/tests/integration-tests/src/test/kotlin/steps/credentials/IssueCredentialsSteps.kt @@ -1,4 +1,4 @@ -package features.credentials +package steps.credentials import abilities.ListenToEvents import common.Utils.wait @@ -33,26 +33,26 @@ class IssueCredentialsSteps { schemaId = null, claims = linkedMapOf( "firstName" to "FirstName", - "lastName" to "LastName" + "lastName" to "LastName", ), issuingDID = did, connectionId = issuer.recall("connection-with-${holder.name}").connectionId, validityPeriod = 3600.0, credentialFormat = "JWT", - automaticIssuance = false + automaticIssuance = false, ) issuer.attemptsTo( Post.to("/issue-credentials/credential-offers") .with { it.body(credentialOfferRequest) - } + }, ) val credentialRecord = SerenityRest.lastResponse().get() issuer.attemptsTo( - Ensure.thatTheLastResponse().statusCode().isEqualTo(SC_CREATED) + Ensure.thatTheLastResponse().statusCode().isEqualTo(SC_CREATED), ) issuer.remember("thid", credentialRecord.thid) @@ -74,16 +74,16 @@ class IssueCredentialsSteps { name = "StudentCredential", version = "1.0", issuerId = issuer.recall("shortFormDid"), - attrNames = listOf("name", "age") + attrNames = listOf("name", "age"), ), tags = listOf("school", "students"), - version = "1.0.0" - ) + version = "1.0.0", + ), ) - } + }, ) issuer.attemptsTo( - Ensure.thatTheLastResponse().statusCode().isEqualTo(SC_CREATED) + Ensure.thatTheLastResponse().statusCode().isEqualTo(SC_CREATED), ) val schema = SerenityRest.lastResponse().get() issuer.remember("anoncredsSchema", schema) @@ -105,13 +105,13 @@ class IssueCredentialsSteps { author = issuer.recall("shortFormDid"), signatureType = "CL", tag = "student", - supportRevocation = false - ) + supportRevocation = false, + ), ) - } + }, ) issuer.attemptsTo( - Ensure.thatTheLastResponse().statusCode().isEqualTo(SC_CREATED) + Ensure.thatTheLastResponse().statusCode().isEqualTo(SC_CREATED), ) val credentialDefinition = SerenityRest.lastResponse().get() issuer.remember("anoncredsCredentialDefinition", credentialDefinition) @@ -123,26 +123,26 @@ class IssueCredentialsSteps { credentialDefinitionId = issuer.recall("anoncredsCredentialDefinition").guid, claims = linkedMapOf( "name" to "Bob", - "age" to "21" + "age" to "21", ), issuingDID = issuer.recall("shortFormDid"), connectionId = issuer.recall("connection-with-${holder.name}").connectionId, validityPeriod = 3600.0, credentialFormat = "AnonCreds", - automaticIssuance = false + automaticIssuance = false, ) issuer.attemptsTo( Post.to("/issue-credentials/credential-offers") .with { it.body(credentialOfferRequest) - } + }, ) val credentialRecord = SerenityRest.lastResponse().get() issuer.attemptsTo( - Ensure.thatTheLastResponse().statusCode().isEqualTo(SC_CREATED) + Ensure.thatTheLastResponse().statusCode().isEqualTo(SC_CREATED), ) issuer.remember("thid", credentialRecord.thid) @@ -160,7 +160,7 @@ class IssueCredentialsSteps { credentialEvent!!.data.protocolState == IssueCredentialRecord.ProtocolState.OFFER_RECEIVED }, "Holder was unable to receive the credential offer from Issuer! " + - "Protocol state did not achieve ${IssueCredentialRecord.ProtocolState.OFFER_RECEIVED} state." + "Protocol state did not achieve ${IssueCredentialRecord.ProtocolState.OFFER_RECEIVED} state.", ) val recordId = ListenToEvents.`as`(holder).credentialEvents.last().data.recordId @@ -173,12 +173,12 @@ class IssueCredentialsSteps { Post.to("/issue-credentials/records/${holder.recall("recordId")}/accept-offer") .with { it.body( - AcceptCredentialOfferRequest(holder.recall("longFormDid")) + AcceptCredentialOfferRequest(holder.recall("longFormDid")), ) - } + }, ) holder.attemptsTo( - Ensure.thatTheLastResponse().statusCode().isEqualTo(SC_OK) + Ensure.thatTheLastResponse().statusCode().isEqualTo(SC_OK), ) } @@ -188,12 +188,12 @@ class IssueCredentialsSteps { Post.to("/issue-credentials/records/${holder.recall("recordId")}/accept-offer") .with { it.body( - "{}" + "{}", ) - } + }, ) holder.attemptsTo( - Ensure.thatTheLastResponse().statusCode().isEqualTo(SC_OK) + Ensure.thatTheLastResponse().statusCode().isEqualTo(SC_OK), ) } @@ -207,14 +207,14 @@ class IssueCredentialsSteps { credentialEvent != null && credentialEvent!!.data.protocolState == IssueCredentialRecord.ProtocolState.REQUEST_RECEIVED }, - "Issuer was unable to receive the credential request from Holder! Protocol state did not achieve RequestReceived state." + "Issuer was unable to receive the credential request from Holder! Protocol state did not achieve RequestReceived state.", ) val recordId = credentialEvent!!.data.recordId issuer.attemptsTo( - Post.to("/issue-credentials/records/$recordId/issue-credential") + Post.to("/issue-credentials/records/$recordId/issue-credential"), ) issuer.attemptsTo( - Ensure.thatTheLastResponse().statusCode().isEqualTo(SC_OK) + Ensure.thatTheLastResponse().statusCode().isEqualTo(SC_OK), ) wait( @@ -226,7 +226,7 @@ class IssueCredentialsSteps { credentialEvent!!.data.protocolState == IssueCredentialRecord.ProtocolState.CREDENTIAL_SENT }, "Issuer was unable to issue the credential! " + - "Protocol state did not achieve ${IssueCredentialRecord.ProtocolState.CREDENTIAL_SENT} state." + "Protocol state did not achieve ${IssueCredentialRecord.ProtocolState.CREDENTIAL_SENT} state.", ) } @@ -241,7 +241,7 @@ class IssueCredentialsSteps { credentialEvent!!.data.protocolState == IssueCredentialRecord.ProtocolState.CREDENTIAL_RECEIVED }, "Holder was unable to receive the credential from Issuer! " + - "Protocol state did not achieve ${IssueCredentialRecord.ProtocolState.CREDENTIAL_RECEIVED} state." + "Protocol state did not achieve ${IssueCredentialRecord.ProtocolState.CREDENTIAL_RECEIVED} state.", ) holder.remember("issuedCredential", ListenToEvents.`as`(holder).credentialEvents.last().data) } diff --git a/tests/integration-tests/src/test/kotlin/features/did/DeactivateDidSteps.kt b/tests/integration-tests/src/test/kotlin/steps/did/DeactivateDidSteps.kt similarity index 94% rename from tests/integration-tests/src/test/kotlin/features/did/DeactivateDidSteps.kt rename to tests/integration-tests/src/test/kotlin/steps/did/DeactivateDidSteps.kt index 29e7a7434e..34f484e608 100644 --- a/tests/integration-tests/src/test/kotlin/features/did/DeactivateDidSteps.kt +++ b/tests/integration-tests/src/test/kotlin/steps/did/DeactivateDidSteps.kt @@ -1,4 +1,4 @@ -package features.did +package steps.did import common.TestConstants import common.Utils.wait @@ -19,7 +19,7 @@ class DeactivateDidSteps { @When("{actor} deactivates PRISM DID") fun actorIssuesDeactivateDidOperation(actor: Actor) { actor.attemptsTo( - Post.to("/did-registrar/dids/${actor.recall("shortFormDid")}/deactivations") + Post.to("/did-registrar/dids/${actor.recall("shortFormDid")}/deactivations"), ) val didOperationResponse = SerenityRest.lastResponse().get() @@ -27,7 +27,7 @@ class DeactivateDidSteps { actor.attemptsTo( Ensure.thatTheLastResponse().statusCode().isEqualTo(HttpStatus.SC_ACCEPTED), Ensure.that(didOperationResponse.scheduledOperation.didRef).isNotEmpty(), - Ensure.that(didOperationResponse.scheduledOperation.id).isNotEmpty() + Ensure.that(didOperationResponse.scheduledOperation.id).isNotEmpty(), ) } @@ -36,12 +36,12 @@ class DeactivateDidSteps { wait( { actor.attemptsTo( - Get.resource("/dids/${actor.recall("shortFormDid")}") + Get.resource("/dids/${actor.recall("shortFormDid")}"), ) SerenityRest.lastResponse().get().didDocumentMetadata.deactivated!! }, "ERROR: DID deactivate operation did not succeed on the ledger!", - timeout = TestConstants.DID_UPDATE_PUBLISH_MAX_WAIT_5_MIN + timeout = TestConstants.DID_UPDATE_PUBLISH_MAX_WAIT_5_MIN, ) } } diff --git a/tests/integration-tests/src/test/kotlin/features/did/ManageDidSteps.kt b/tests/integration-tests/src/test/kotlin/steps/did/ManageDidSteps.kt similarity index 80% rename from tests/integration-tests/src/test/kotlin/features/did/ManageDidSteps.kt rename to tests/integration-tests/src/test/kotlin/steps/did/ManageDidSteps.kt index d041db6987..084d72e527 100644 --- a/tests/integration-tests/src/test/kotlin/features/did/ManageDidSteps.kt +++ b/tests/integration-tests/src/test/kotlin/steps/did/ManageDidSteps.kt @@ -1,4 +1,4 @@ -package features.did +package steps.did import interactions.Get import interactions.Post @@ -7,7 +7,14 @@ import io.cucumber.java.en.Then import io.cucumber.java.en.When import io.iohk.atala.automation.extensions.get import io.iohk.atala.automation.serenity.ensure.Ensure -import io.iohk.atala.prism.models.* +import io.iohk.atala.prism.models.CreateManagedDidRequest +import io.iohk.atala.prism.models.CreateManagedDidRequestDocumentTemplate +import io.iohk.atala.prism.models.Json +import io.iohk.atala.prism.models.ManagedDID +import io.iohk.atala.prism.models.ManagedDIDKeyTemplate +import io.iohk.atala.prism.models.ManagedDIDPage +import io.iohk.atala.prism.models.Purpose +import io.iohk.atala.prism.models.Service import net.serenitybdd.rest.SerenityRest import net.serenitybdd.screenplay.Actor import org.apache.http.HttpStatus.SC_CREATED @@ -30,11 +37,11 @@ class ManageDidSteps { Post.to("/did-registrar/dids") .with { it.body(createDidRequest) - } + }, ) actor.attemptsTo( - Ensure.thatTheLastResponse().statusCode().isEqualTo(SC_CREATED) + Ensure.thatTheLastResponse().statusCode().isEqualTo(SC_CREATED), ) var createdDids = actor.recall>("createdDids") @@ -51,7 +58,7 @@ class ManageDidSteps { @When("{actor} lists all PRISM DIDs") fun iListManagedDids(actor: Actor) { actor.attemptsTo( - Get.resource("/did-registrar/dids") + Get.resource("/did-registrar/dids"), ) } @@ -60,7 +67,7 @@ class ManageDidSteps { val managedDid = SerenityRest.lastResponse().get() actor.attemptsTo( Ensure.thatTheLastResponse().statusCode().isEqualTo(SC_CREATED), - Ensure.that(managedDid.longFormDid!!).isNotEmpty() + Ensure.that(managedDid.longFormDid!!).isNotEmpty(), ) } @@ -70,7 +77,7 @@ class ManageDidSteps { val managedDidList = SerenityRest.lastResponse().get().contents!! .filter { it.status == "CREATED" }.map { it.longFormDid!! } actor.attemptsTo( - Ensure.that(managedDidList).containsElementsFrom(expectedDids) + Ensure.that(managedDidList).containsElementsFrom(expectedDids), ) } @@ -78,8 +85,8 @@ class ManageDidSteps { CreateManagedDidRequestDocumentTemplate( publicKeys = listOf(ManagedDIDKeyTemplate("auth-1", Purpose.AUTHENTICATION)), services = listOf( - Service("https://foo.bar.com", listOf("LinkedDomains"), Json("https://foo.bar.com/")) - ) - ) + Service("https://foo.bar.com", listOf("LinkedDomains"), Json("https://foo.bar.com/")), + ), + ), ) } diff --git a/tests/integration-tests/src/test/kotlin/features/did/PublishDidSteps.kt b/tests/integration-tests/src/test/kotlin/steps/did/PublishDidSteps.kt similarity index 91% rename from tests/integration-tests/src/test/kotlin/features/did/PublishDidSteps.kt rename to tests/integration-tests/src/test/kotlin/steps/did/PublishDidSteps.kt index 48f55057fa..d91b5ddf13 100644 --- a/tests/integration-tests/src/test/kotlin/features/did/PublishDidSteps.kt +++ b/tests/integration-tests/src/test/kotlin/steps/did/PublishDidSteps.kt @@ -1,4 +1,4 @@ -package features.did +package steps.did import abilities.ListenToEvents import common.TestConstants @@ -22,10 +22,10 @@ class PublishDidSteps { @Given("{actor} have published PRISM DID") fun actorHavePublishedPrismDid(actor: Actor) { actor.attemptsTo( - Get.resource("/did-registrar/dids") + Get.resource("/did-registrar/dids"), ) actor.attemptsTo( - Ensure.thatTheLastResponse().statusCode().isEqualTo(SC_OK) + Ensure.thatTheLastResponse().statusCode().isEqualTo(SC_OK), ) val publishedDids = SerenityRest.lastResponse().get().contents!!.filter { // TODO: fix openapi spec to have statuses as enum @@ -33,7 +33,7 @@ class PublishDidSteps { } val did = publishedDids.firstOrNull { actor.attemptsTo( - Get.resource("/dids/${it.did}") + Get.resource("/dids/${it.did}"), ) !SerenityRest.lastResponse().get().didDocumentMetadata.deactivated!! } @@ -51,54 +51,54 @@ class PublishDidSteps { CreateManagedDidRequestDocumentTemplate( publicKeys = listOf( ManagedDIDKeyTemplate("auth-1", Purpose.AUTHENTICATION), - ManagedDIDKeyTemplate("assertion-1", Purpose.ASSERTION_METHOD) + ManagedDIDKeyTemplate("assertion-1", Purpose.ASSERTION_METHOD), ), services = listOf( Service("https://foo.bar.com", listOf("LinkedDomains"), Json("https://foo.bar.com/")), Service("https://update.com", listOf("LinkedDomains"), Json("https://update.com/")), - Service("https://remove.com", listOf("LinkedDomains"), Json("https://remove.com/")) - ) - ) + Service("https://remove.com", listOf("LinkedDomains"), Json("https://remove.com/")), + ), + ), ) actor.attemptsTo( Post.to("/did-registrar/dids") .with { it.body(createDidRequest) - } + }, ) val managedDid = SerenityRest.lastResponse().get() actor.attemptsTo( Ensure.thatTheLastResponse().statusCode().isEqualTo(SC_CREATED), - Ensure.that(managedDid.longFormDid!!).isNotEmpty() + Ensure.that(managedDid.longFormDid!!).isNotEmpty(), ) actor.remember("longFormDid", managedDid.longFormDid) actor.attemptsTo( - Get.resource("/did-registrar/dids/${managedDid.longFormDid}") + Get.resource("/did-registrar/dids/${managedDid.longFormDid}"), ) actor.attemptsTo( - Ensure.thatTheLastResponse().statusCode().isEqualTo(SC_OK) + Ensure.thatTheLastResponse().statusCode().isEqualTo(SC_OK), ) val did = SerenityRest.lastResponse().get() actor.remember( "shortFormDid", - did.did + did.did, ) } @When("{actor} publishes DID to ledger") fun hePublishesDidToLedger(actor: Actor) { actor.attemptsTo( - Post.to("/did-registrar/dids/${actor.recall("shortFormDid")}/publications") + Post.to("/did-registrar/dids/${actor.recall("shortFormDid")}/publications"), ) val didOperationResponse = SerenityRest.lastResponse().get() actor.attemptsTo( Ensure.thatTheLastResponse().statusCode().isEqualTo(HttpStatus.SC_ACCEPTED), Ensure.that(didOperationResponse.scheduledOperation.didRef).isNotEmpty(), - Ensure.that(didOperationResponse.scheduledOperation.id).isNotEmpty() + Ensure.that(didOperationResponse.scheduledOperation.id).isNotEmpty(), ) wait( @@ -110,17 +110,17 @@ class PublishDidSteps { didEvent != null && didEvent.data.status == "PUBLISHED" }, "ERROR: DID was not published to ledger!", - timeout = TestConstants.DID_UPDATE_PUBLISH_MAX_WAIT_5_MIN + timeout = TestConstants.DID_UPDATE_PUBLISH_MAX_WAIT_5_MIN, ) actor.attemptsTo( - Get.resource("/dids/${actor.recall("shortFormDid")}") + Get.resource("/dids/${actor.recall("shortFormDid")}"), ) val didDocument = SerenityRest.lastResponse().get().didDocument!! actor.attemptsTo( Ensure.thatTheLastResponse().statusCode().isEqualTo(SC_OK), - Ensure.that(didDocument.id).isEqualTo(actor.recall("shortFormDid")) + Ensure.that(didDocument.id).isEqualTo(actor.recall("shortFormDid")), ) } @@ -134,7 +134,7 @@ class PublishDidSteps { Ensure.that(didDocument.authentication!![0]) .isEqualTo("$shortFormDid#${TestConstants.PRISM_DID_AUTH_KEY.id}"), Ensure.that(didDocument.verificationMethod!![0].controller).isEqualTo(shortFormDid), - Ensure.that(didResolutionResult.didDocumentMetadata.deactivated!!).isFalse() + Ensure.that(didResolutionResult.didDocumentMetadata.deactivated!!).isFalse(), ) } } diff --git a/tests/integration-tests/src/test/kotlin/features/did/UpdateDidSteps.kt b/tests/integration-tests/src/test/kotlin/steps/did/UpdateDidSteps.kt similarity index 82% rename from tests/integration-tests/src/test/kotlin/features/did/UpdateDidSteps.kt rename to tests/integration-tests/src/test/kotlin/steps/did/UpdateDidSteps.kt index dad0341d95..2bfd644db2 100644 --- a/tests/integration-tests/src/test/kotlin/features/did/UpdateDidSteps.kt +++ b/tests/integration-tests/src/test/kotlin/steps/did/UpdateDidSteps.kt @@ -1,4 +1,4 @@ -package features.did +package steps.did import common.TestConstants import common.Utils.wait @@ -8,7 +8,17 @@ import io.cucumber.java.en.Then import io.cucumber.java.en.When import io.iohk.atala.automation.extensions.get import io.iohk.atala.automation.serenity.ensure.Ensure -import io.iohk.atala.prism.models.* +import io.iohk.atala.prism.models.ActionType +import io.iohk.atala.prism.models.DIDOperationResponse +import io.iohk.atala.prism.models.DIDResolutionResult +import io.iohk.atala.prism.models.Json +import io.iohk.atala.prism.models.ManagedDIDKeyTemplate +import io.iohk.atala.prism.models.Purpose +import io.iohk.atala.prism.models.RemoveEntryById +import io.iohk.atala.prism.models.Service +import io.iohk.atala.prism.models.UpdateManagedDIDRequest +import io.iohk.atala.prism.models.UpdateManagedDIDRequestAction +import io.iohk.atala.prism.models.UpdateManagedDIDServiceAction import net.serenitybdd.rest.SerenityRest import net.serenitybdd.screenplay.Actor import org.apache.http.HttpStatus @@ -19,7 +29,7 @@ class UpdateDidSteps { fun actorUpdatesPrismDidByAddingNewKeys(actor: Actor) { val updatePrismDidAction = UpdateManagedDIDRequestAction( actionType = ActionType.ADD_KEY, - ManagedDIDKeyTemplate("auth-2", Purpose.AUTHENTICATION) + ManagedDIDKeyTemplate("auth-2", Purpose.AUTHENTICATION), ) actor.remember("updatePrismDidAction", updatePrismDidAction) } @@ -28,7 +38,7 @@ class UpdateDidSteps { fun actorUpdatesPrismDidByRemovingKeys(actor: Actor) { val updatePrismDidAction = UpdateManagedDIDRequestAction( actionType = ActionType.REMOVE_KEY, - removeKey = RemoveEntryById("auth-1") + removeKey = RemoveEntryById("auth-1"), ) actor.remember("updatePrismDidAction", updatePrismDidAction) } @@ -40,8 +50,8 @@ class UpdateDidSteps { addService = Service( "https://new.service.com", listOf("LinkedDomains"), - Json("https://new.service.com/") - ) + Json("https://new.service.com/"), + ), ) actor.remember("updatePrismDidAction", updatePrismDidAction) } @@ -50,7 +60,7 @@ class UpdateDidSteps { fun actorUpdatesPrismDidByRemovingServices(actor: Actor) { val updatePrismDidAction = UpdateManagedDIDRequestAction( actionType = ActionType.REMOVE_SERVICE, - removeService = RemoveEntryById("https://new.service.com") + removeService = RemoveEntryById("https://new.service.com"), ) actor.remember("updatePrismDidAction", updatePrismDidAction) } @@ -61,12 +71,12 @@ class UpdateDidSteps { id = TestConstants.PRISM_DID_SERVICE_FOR_UPDATE.id, type = TestConstants.PRISM_DID_SERVICE_FOR_UPDATE.type, serviceEndpoint = Json( - TestConstants.PRISM_DID_UPDATE_NEW_SERVICE_URL - ) + TestConstants.PRISM_DID_UPDATE_NEW_SERVICE_URL, + ), ) val updatePrismDidAction = UpdateManagedDIDRequestAction( actionType = ActionType.UPDATE_SERVICE, - updateService = newService + updateService = newService, ) actor.remember("updatePrismDidAction", updatePrismDidAction) } @@ -77,13 +87,13 @@ class UpdateDidSteps { Post.to("/did-registrar/dids/${actor.recall("shortFormDid")}/updates") .with { it.body(UpdateManagedDIDRequest(listOf(actor.recall("updatePrismDidAction")))) - } + }, ) val didOperationResponse = SerenityRest.lastResponse().get() actor.attemptsTo( Ensure.thatTheLastResponse().statusCode().isEqualTo(HttpStatus.SC_ACCEPTED), Ensure.that(didOperationResponse.scheduledOperation.didRef).isNotEmpty(), - Ensure.that(didOperationResponse.scheduledOperation.id).isNotEmpty() + Ensure.that(didOperationResponse.scheduledOperation.id).isNotEmpty(), ) } @@ -92,10 +102,11 @@ class UpdateDidSteps { wait( { actor.attemptsTo( - Get.resource("/dids/${actor.recall("shortFormDid")}") + Get.resource("/dids/${actor.recall("shortFormDid")}"), ) val authUris = SerenityRest.lastResponse().get().didDocument!!.authentication!! - val verificationMethods = SerenityRest.lastResponse().get().didDocument!!.verificationMethod!!.map { it.id } + val verificationMethods = SerenityRest.lastResponse() + .get().didDocument!!.verificationMethod!!.map { it.id } authUris.any { it == "${actor.recall("shortFormDid")}#${TestConstants.PRISM_DID_UPDATE_NEW_AUTH_KEY.id}" } && verificationMethods.any { @@ -103,7 +114,7 @@ class UpdateDidSteps { } }, "ERROR: DID UPDATE operation did not succeed on the ledger!", - timeout = TestConstants.DID_UPDATE_PUBLISH_MAX_WAIT_5_MIN + timeout = TestConstants.DID_UPDATE_PUBLISH_MAX_WAIT_5_MIN, ) } @@ -112,10 +123,11 @@ class UpdateDidSteps { wait( { actor.attemptsTo( - Get.resource("/dids/${actor.recall("shortFormDid")}") + Get.resource("/dids/${actor.recall("shortFormDid")}"), ) val authUris = SerenityRest.lastResponse().get().didDocument!!.authentication!! - val verificationMethods = SerenityRest.lastResponse().get().didDocument!!.verificationMethod!!.map { it.id } + val verificationMethods = SerenityRest.lastResponse() + .get().didDocument!!.verificationMethod!!.map { it.id } authUris.none { it == "${actor.recall("shortFormDid")}#${TestConstants.PRISM_DID_AUTH_KEY.id}" } && verificationMethods.none { @@ -123,7 +135,7 @@ class UpdateDidSteps { } }, "ERROR: DID UPDATE operation did not succeed on the ledger!", - timeout = TestConstants.DID_UPDATE_PUBLISH_MAX_WAIT_5_MIN + timeout = TestConstants.DID_UPDATE_PUBLISH_MAX_WAIT_5_MIN, ) } @@ -132,15 +144,16 @@ class UpdateDidSteps { wait( { actor.attemptsTo( - Get.resource("/dids/${actor.recall("shortFormDid")}") + Get.resource("/dids/${actor.recall("shortFormDid")}"), ) - val serviceIds = SerenityRest.lastResponse().get().didDocument!!.service!!.map { it.id } + val serviceIds = + SerenityRest.lastResponse().get().didDocument!!.service!!.map { it.id } serviceIds.any { it == "${actor.recall("shortFormDid")}#${TestConstants.PRISM_DID_UPDATE_NEW_SERVICE.id}" } }, "ERROR: DID UPDATE operation did not succeed on the ledger!", - timeout = TestConstants.DID_UPDATE_PUBLISH_MAX_WAIT_5_MIN + timeout = TestConstants.DID_UPDATE_PUBLISH_MAX_WAIT_5_MIN, ) } @@ -149,15 +162,16 @@ class UpdateDidSteps { wait( { actor.attemptsTo( - Get.resource("/dids/${actor.recall("shortFormDid")}") + Get.resource("/dids/${actor.recall("shortFormDid")}"), ) - val serviceIds = SerenityRest.lastResponse().get().didDocument!!.service!!.map { it.id } + val serviceIds = + SerenityRest.lastResponse().get().didDocument!!.service!!.map { it.id } serviceIds.none { it == "${actor.recall("shortFormDid")}#${TestConstants.PRISM_DID_UPDATE_NEW_SERVICE.id}" } }, "ERROR: DID UPDATE operation did not succeed on the ledger!", - timeout = TestConstants.DID_UPDATE_PUBLISH_MAX_WAIT_5_MIN + timeout = TestConstants.DID_UPDATE_PUBLISH_MAX_WAIT_5_MIN, ) } @@ -166,13 +180,13 @@ class UpdateDidSteps { wait( { actor.attemptsTo( - Get.resource("/dids/${actor.recall("shortFormDid")}") + Get.resource("/dids/${actor.recall("shortFormDid")}"), ) val service = SerenityRest.lastResponse().get().didDocument!!.service!! service.any { it.serviceEndpoint.value.contains(TestConstants.PRISM_DID_UPDATE_NEW_SERVICE_URL) } }, "ERROR: DID UPDATE operation did not succeed on the ledger!", - timeout = TestConstants.DID_UPDATE_PUBLISH_MAX_WAIT_5_MIN + timeout = TestConstants.DID_UPDATE_PUBLISH_MAX_WAIT_5_MIN, ) } } diff --git a/tests/integration-tests/src/test/kotlin/features/multitenancy/EntitySteps.kt b/tests/integration-tests/src/test/kotlin/steps/multitenancy/EntitySteps.kt similarity index 84% rename from tests/integration-tests/src/test/kotlin/features/multitenancy/EntitySteps.kt rename to tests/integration-tests/src/test/kotlin/steps/multitenancy/EntitySteps.kt index 4a0c88e297..8f22ab828d 100644 --- a/tests/integration-tests/src/test/kotlin/features/multitenancy/EntitySteps.kt +++ b/tests/integration-tests/src/test/kotlin/steps/multitenancy/EntitySteps.kt @@ -1,4 +1,4 @@ -package features.multitenancy +package steps.multitenancy import interactions.Post import io.iohk.atala.automation.extensions.get @@ -17,7 +17,7 @@ class EntitySteps { actor: Actor, walletId: UUID, name: String = "", - id: UUID = UUID.randomUUID() + id: UUID = UUID.randomUUID(), ): EntityResponse { actor.attemptsTo( Post.to("/iam/entities") @@ -26,13 +26,13 @@ class EntitySteps { CreateEntityRequest( walletId = walletId, name = name, - id = id - ) + id = id, + ), ) - } + }, ) actor.attemptsTo( - Ensure.thatTheLastResponse().statusCode().isEqualTo(SC_CREATED) + Ensure.thatTheLastResponse().statusCode().isEqualTo(SC_CREATED), ) return SerenityRest.lastResponse().get() } @@ -44,13 +44,13 @@ class EntitySteps { it.body( ApiKeyAuthenticationRequest( entityId = entityId, - apiKey = apiKey - ) + apiKey = apiKey, + ), ) - } + }, ) actor.attemptsTo( - Ensure.thatTheLastResponse().statusCode().isEqualTo(SC_CREATED) + Ensure.thatTheLastResponse().statusCode().isEqualTo(SC_CREATED), ) } } diff --git a/tests/integration-tests/src/test/kotlin/features/multitenancy/EventsSteps.kt b/tests/integration-tests/src/test/kotlin/steps/multitenancy/EventsSteps.kt similarity index 89% rename from tests/integration-tests/src/test/kotlin/features/multitenancy/EventsSteps.kt rename to tests/integration-tests/src/test/kotlin/steps/multitenancy/EventsSteps.kt index 0348674dc4..da4e3b59d4 100644 --- a/tests/integration-tests/src/test/kotlin/features/multitenancy/EventsSteps.kt +++ b/tests/integration-tests/src/test/kotlin/steps/multitenancy/EventsSteps.kt @@ -1,4 +1,4 @@ -package features.multitenancy +package steps.multitenancy import interactions.Post import io.iohk.atala.automation.serenity.ensure.Ensure @@ -12,13 +12,13 @@ class EventsSteps { Post.to("/events/webhooks") .with { it.body( - CreateWebhookNotification(url = webhookUrl) + CreateWebhookNotification(url = webhookUrl), ) - } + }, ) actor.attemptsTo( - Ensure.thatTheLastResponse().statusCode().isEqualTo(HttpStatus.SC_CREATED) + Ensure.thatTheLastResponse().statusCode().isEqualTo(HttpStatus.SC_CREATED), ) } } diff --git a/tests/integration-tests/src/test/kotlin/features/multitenancy/WalletsSteps.kt b/tests/integration-tests/src/test/kotlin/steps/multitenancy/WalletsSteps.kt similarity index 90% rename from tests/integration-tests/src/test/kotlin/features/multitenancy/WalletsSteps.kt rename to tests/integration-tests/src/test/kotlin/steps/multitenancy/WalletsSteps.kt index d3f3d47d71..28154b508c 100644 --- a/tests/integration-tests/src/test/kotlin/features/multitenancy/WalletsSteps.kt +++ b/tests/integration-tests/src/test/kotlin/steps/multitenancy/WalletsSteps.kt @@ -1,4 +1,4 @@ -package features.multitenancy +package steps.multitenancy import common.TestConstants import interactions.Get @@ -24,7 +24,7 @@ class WalletsSteps { actor: Actor, name: String = "test-wallet", seed: String = Random.nextBytes(64).toHexString(), - id: UUID = UUID.randomUUID() + id: UUID = UUID.randomUUID(), ): WalletDetail { actor.attemptsTo( Post.to("/wallets") @@ -33,10 +33,10 @@ class WalletsSteps { CreateWalletRequest( name = name, seed = seed, - id = id - ) + id = id, + ), ) - } + }, ) return SerenityRest.lastResponse().get() } @@ -45,7 +45,7 @@ class WalletsSteps { fun iCreateNewWalletWithName(acme: Actor, name: String) { val wallet = createNewWallet(acme, name) acme.attemptsTo( - Ensure.thatTheLastResponse().statusCode().isEqualTo(SC_CREATED) + Ensure.thatTheLastResponse().statusCode().isEqualTo(SC_CREATED), ) acme.remember("walletId", wallet.id) } @@ -58,7 +58,7 @@ class WalletsSteps { acme.attemptsTo( Ensure.thatTheLastResponse().statusCode().isEqualTo(SC_CREATED), Ensure.that(wallet.id).isEqualTo(uniqueId) - .withReportedError("Wallet id is not correct!") + .withReportedError("Wallet id is not correct!"), ) } @@ -66,7 +66,7 @@ class WalletsSteps { fun acmeCreateNewWalletWithTheSameId(acme: Actor) { createNewWallet(acme, id = acme.recall("uniqueId")) acme.attemptsTo( - Ensure.thatTheLastResponse().statusCode().isEqualTo(SC_BAD_REQUEST) + Ensure.thatTheLastResponse().statusCode().isEqualTo(SC_BAD_REQUEST), ) } @@ -76,7 +76,7 @@ class WalletsSteps { acme.remember("uniqueName", name) createNewWallet(acme, name = name) acme.attemptsTo( - Ensure.thatTheLastResponse().statusCode().isEqualTo(SC_CREATED) + Ensure.thatTheLastResponse().statusCode().isEqualTo(SC_CREATED), ) } @@ -84,7 +84,7 @@ class WalletsSteps { fun acmeCreatesNewWalletWithTheSameUniqueName(acme: Actor) { createNewWallet(acme, name = acme.recall("uniqueName")) acme.attemptsTo( - Ensure.thatTheLastResponse().statusCode().isEqualTo(SC_CREATED) + Ensure.thatTheLastResponse().statusCode().isEqualTo(SC_CREATED), ) } @@ -94,7 +94,7 @@ class WalletsSteps { Get.resource("/wallets/${acme.recall("walletId")}") .with { it.queryParam("name", name) - } + }, ) val wallet = SerenityRest.lastResponse().get() @@ -102,40 +102,40 @@ class WalletsSteps { Ensure.that(wallet.name).isEqualTo(name) .withReportedError("Wallet name is not correct!"), Ensure.that(wallet.id).isEqualTo(acme.recall("walletId")) - .withReportedError("Wallet id is not correct!") + .withReportedError("Wallet id is not correct!"), ) } @Then("{actor} should have two wallets with unique name but different ids") fun acmeShouldHaveTwoWalletsWithNameButDifferentIds(acme: Actor) { acme.attemptsTo( - Get.resource("/wallets") + Get.resource("/wallets"), ) acme.attemptsTo( - Ensure.thatTheLastResponse().statusCode().isEqualTo(SC_OK) + Ensure.thatTheLastResponse().statusCode().isEqualTo(SC_OK), ) val wallets = SerenityRest.lastResponse().get().contents!!.filter { it.name == acme.recall("uniqueName") } acme.attemptsTo( Ensure.that(wallets.size).isEqualTo(2) - .withReportedError("Two wallets with the same name were not created!") + .withReportedError("Two wallets with the same name were not created!"), ) } @Then("{actor} should have only one wallet and second operation should fail") fun acmeShouldHaveOnlyOneWalletAndSecondOperationShouldFail(acme: Actor) { acme.attemptsTo( - Ensure.thatTheLastResponse().statusCode().isEqualTo(SC_BAD_REQUEST) + Ensure.thatTheLastResponse().statusCode().isEqualTo(SC_BAD_REQUEST), ) acme.attemptsTo( - Get.resource("/wallets") + Get.resource("/wallets"), ) acme.attemptsTo( - Ensure.thatTheLastResponse().statusCode().isEqualTo(SC_OK) + Ensure.thatTheLastResponse().statusCode().isEqualTo(SC_OK), ) val wallets = SerenityRest.lastResponse().get().contents!!.filter { it.id == acme.recall("uniqueId") } acme.attemptsTo( Ensure.that(wallets.size).isEqualTo(1) - .withReportedError("Only one wallet should be created with the same id!") + .withReportedError("Only one wallet should be created with the same id!"), ) } @@ -147,7 +147,7 @@ class WalletsSteps { @Then("{actor} should see the error and wallet should not be created") fun acmeShouldSeeTheErrorAndWalletShouldNotBeCreated(acme: Actor) { acme.attemptsTo( - Ensure.thatTheLastResponse().statusCode().isEqualTo(SC_BAD_REQUEST) + Ensure.thatTheLastResponse().statusCode().isEqualTo(SC_BAD_REQUEST), ) } } diff --git a/tests/integration-tests/src/test/kotlin/features/proofs/PresentProofSteps.kt b/tests/integration-tests/src/test/kotlin/steps/proofs/PresentProofSteps.kt similarity index 91% rename from tests/integration-tests/src/test/kotlin/features/proofs/PresentProofSteps.kt rename to tests/integration-tests/src/test/kotlin/steps/proofs/PresentProofSteps.kt index 0e2c3ebc4f..f519ecb0a5 100644 --- a/tests/integration-tests/src/test/kotlin/features/proofs/PresentProofSteps.kt +++ b/tests/integration-tests/src/test/kotlin/steps/proofs/PresentProofSteps.kt @@ -1,4 +1,4 @@ -package features.proofs +package steps.proofs import abilities.ListenToEvents import common.Utils.wait @@ -24,25 +24,25 @@ class PresentProofSteps { connectionId = faber.recall("connection-with-${bob.name}").connectionId, options = Options( challenge = "11c91493-01b3-4c4d-ac36-b336bab5bddf", - domain = "https://example-verifier.com" + domain = "https://example-verifier.com", ), proofs = listOf( ProofRequestAux( schemaId = "https://schema.org/Person", - trustIssuers = listOf("did:web:atalaprism.io/users/testUser") - ) - ) + trustIssuers = listOf("did:web:atalaprism.io/users/testUser"), + ), + ), ) faber.attemptsTo( Post.to("/present-proof/presentations") .with { it.body( - presentationRequest + presentationRequest, ) - } + }, ) faber.attemptsTo( - Ensure.thatTheLastResponse().statusCode().isEqualTo(SC_CREATED) + Ensure.thatTheLastResponse().statusCode().isEqualTo(SC_CREATED), ) val presentationStatus = SerenityRest.lastResponse().get() faber.remember("thid", presentationStatus.thid) @@ -59,7 +59,7 @@ class PresentProofSteps { proofEvent != null && proofEvent!!.data.status == PresentationStatus.Status.REQUEST_RECEIVED }, - "ERROR: Bob did not achieve any presentation request!" + "ERROR: Bob did not achieve any presentation request!", ) bob.remember("presentationId", proofEvent!!.data.presentationId) } @@ -68,15 +68,15 @@ class PresentProofSteps { fun bobMakesThePresentationOfTheProof(bob: Actor, faber: Actor) { val requestPresentationAction = RequestPresentationAction( proofId = listOf(bob.recall("issuedCredential").recordId), - action = RequestPresentationAction.Action.REQUEST_MINUS_ACCEPT + action = RequestPresentationAction.Action.REQUEST_MINUS_ACCEPT, ) bob.attemptsTo( Patch.to("/present-proof/presentations/${bob.recall("presentationId")}").with { it.body( - requestPresentationAction + requestPresentationAction, ) - } + }, ) } @@ -86,10 +86,10 @@ class PresentProofSteps { Patch.to("/present-proof/presentations/${bob.recall("presentationId")}").with { it.body( RequestPresentationAction( - action = RequestPresentationAction.Action.REQUEST_MINUS_REJECT - ) + action = RequestPresentationAction.Action.REQUEST_MINUS_REJECT, + ), ) - } + }, ) } @@ -103,7 +103,7 @@ class PresentProofSteps { proofEvent != null && proofEvent!!.data.status == PresentationStatus.Status.REQUEST_REJECTED }, - "ERROR: Faber did not receive presentation from Bob!" + "ERROR: Faber did not receive presentation from Bob!", ) } @@ -118,7 +118,7 @@ class PresentProofSteps { proofEvent != null && proofEvent!!.data.status == PresentationStatus.Status.PRESENTATION_VERIFIED }, - "ERROR: presentation did not achieve PresentationVerified state!" + "ERROR: presentation did not achieve PresentationVerified state!", ) } } diff --git a/tests/integration-tests/src/test/kotlin/features/schemas/CredentialSchemasSteps.kt b/tests/integration-tests/src/test/kotlin/steps/schemas/CredentialSchemasSteps.kt similarity index 81% rename from tests/integration-tests/src/test/kotlin/features/schemas/CredentialSchemasSteps.kt rename to tests/integration-tests/src/test/kotlin/steps/schemas/CredentialSchemasSteps.kt index b1aad0e237..da902aa7ef 100644 --- a/tests/integration-tests/src/test/kotlin/features/schemas/CredentialSchemasSteps.kt +++ b/tests/integration-tests/src/test/kotlin/steps/schemas/CredentialSchemasSteps.kt @@ -1,4 +1,4 @@ -package features +package steps import common.TestConstants import interactions.Get @@ -7,12 +7,14 @@ import io.cucumber.java.en.Then import io.cucumber.java.en.When import io.iohk.atala.automation.extensions.get import io.iohk.atala.automation.serenity.ensure.Ensure +import io.iohk.atala.prism.models.CredentialSchemaInput import io.iohk.atala.prism.models.CredentialSchemaResponse import models.JsonSchema import net.serenitybdd.rest.SerenityRest import net.serenitybdd.screenplay.Actor import org.apache.http.HttpStatus.SC_CREATED import org.apache.http.HttpStatus.SC_OK +import java.util.UUID class CredentialSchemasSteps { @@ -21,9 +23,9 @@ class CredentialSchemasSteps { actor.attemptsTo( Post.to("/schema-registry/schemas").with { it.body( - TestConstants.STUDENT_SCHEMA.copy(author = actor.recall("shortFormDid")) + TestConstants.STUDENT_SCHEMA.copy(author = actor.recall("shortFormDid")), ) - } + }, ) } @@ -44,7 +46,7 @@ class CredentialSchemasSteps { Ensure.that(credentialSchema.version).contains(TestConstants.STUDENT_SCHEMA.version), Ensure.that(credentialSchema.type).isEqualTo(TestConstants.CREDENTIAL_SCHEMA_TYPE), Ensure.that(credentialSchema.tags!!).containsExactlyInAnyOrderElementsFrom(TestConstants.STUDENT_SCHEMA.tags!!), - Ensure.that(jsonSchema.toString()).isEqualTo(TestConstants.jsonSchema.toString()) + Ensure.that(jsonSchema.toString()).isEqualTo(TestConstants.jsonSchema.toString()), ) } @@ -55,15 +57,20 @@ class CredentialSchemasSteps { actor.attemptsTo( Post.to("/schema-registry/schemas").with { it.body( - TestConstants.generate_with_name_suffix_and_author( - i.toString(), - actor.recall("shortFormDid") - ) + CredentialSchemaInput( + author = actor.recall("shortFormDid"), + name = "${UUID.randomUUID()} $i", + description = "Simple student credentials schema", + type = TestConstants.CREDENTIAL_SCHEMA_TYPE, + schema = TestConstants.jsonSchema, + tags = listOf("school", "students"), + version = "1.0.0", + ), ) - } + }, ) actor.attemptsTo( - Ensure.thatTheLastResponse().statusCode().isEqualTo(SC_CREATED) + Ensure.thatTheLastResponse().statusCode().isEqualTo(SC_CREATED), ) createdSchemas.add(SerenityRest.lastResponse().get()) } @@ -74,10 +81,10 @@ class CredentialSchemasSteps { fun theyCanBeAccessedWithPagination(actor: Actor) { actor.recall>("createdSchemas").forEach { schema -> actor.attemptsTo( - Get.resource("/schema-registry/schemas/${schema.guid}") + Get.resource("/schema-registry/schemas/${schema.guid}"), ) actor.attemptsTo( - Ensure.thatTheLastResponse().statusCode().isEqualTo(SC_OK) + Ensure.thatTheLastResponse().statusCode().isEqualTo(SC_OK), ) } } diff --git a/tests/integration-tests/src/test/kotlin/features/system/SystemSteps.kt b/tests/integration-tests/src/test/kotlin/steps/system/SystemSteps.kt similarity index 86% rename from tests/integration-tests/src/test/kotlin/features/system/SystemSteps.kt rename to tests/integration-tests/src/test/kotlin/steps/system/SystemSteps.kt index c9db4a12c2..d54c2c0f03 100644 --- a/tests/integration-tests/src/test/kotlin/features/system/SystemSteps.kt +++ b/tests/integration-tests/src/test/kotlin/steps/system/SystemSteps.kt @@ -1,4 +1,4 @@ -package features.system +package steps.system import interactions.Get import io.cucumber.java.en.Then @@ -14,10 +14,10 @@ class SystemSteps { @When("{actor} makes a request to the health endpoint") fun actorRequestsHealthEndpoint(actor: Actor) { actor.attemptsTo( - Get.resource("/_system/health") + Get.resource("/_system/health"), ) actor.attemptsTo( - Ensure.thatTheLastResponse().statusCode().isEqualTo(HttpStatus.SC_OK) + Ensure.thatTheLastResponse().statusCode().isEqualTo(HttpStatus.SC_OK), ) } @@ -25,17 +25,17 @@ class SystemSteps { fun actorUnderstandsVersion(actor: Actor) { val healthResponse = SerenityRest.lastResponse().get() actor.attemptsTo( - Ensure.that(healthResponse.version).isNotBlank() + Ensure.that(healthResponse.version).isNotBlank(), ) } @When("{actor} makes a request to the metrics endpoint") fun actorRequestsMetricEndpoint(actor: Actor) { actor.attemptsTo( - Get.resource("/_system/metrics") + Get.resource("/_system/metrics"), ) actor.attemptsTo( - Ensure.thatTheLastResponse().statusCode().isEqualTo(HttpStatus.SC_OK) + Ensure.thatTheLastResponse().statusCode().isEqualTo(HttpStatus.SC_OK), ) } @@ -45,8 +45,7 @@ class SystemSteps { actor.attemptsTo( Ensure.that(metricsResponse.body.asString()).contains("present_proof_flow_did_com_exchange_job_ms_gauge"), Ensure.that(metricsResponse.body.asString()).contains("connection_flow_did_com_exchange_job_ms_gauge"), - Ensure.that(metricsResponse.body.asString()).contains("issuance_flow_did_com_exchange_job_ms_gauge") + Ensure.that(metricsResponse.body.asString()).contains("issuance_flow_did_com_exchange_job_ms_gauge"), ) } - } diff --git a/tests/integration-tests/src/test/kotlin/features/verificationpolicies/VerificationPoliciesSteps.kt b/tests/integration-tests/src/test/kotlin/steps/verificationpolicies/VerificationPoliciesSteps.kt similarity index 90% rename from tests/integration-tests/src/test/kotlin/features/verificationpolicies/VerificationPoliciesSteps.kt rename to tests/integration-tests/src/test/kotlin/steps/verificationpolicies/VerificationPoliciesSteps.kt index 9695b79846..0b0fe7c663 100644 --- a/tests/integration-tests/src/test/kotlin/features/verificationpolicies/VerificationPoliciesSteps.kt +++ b/tests/integration-tests/src/test/kotlin/steps/verificationpolicies/VerificationPoliciesSteps.kt @@ -22,12 +22,12 @@ class VerificationPoliciesSteps { actor.attemptsTo( Post.to("/verification/policies").with { it.body( - TestConstants.TEST_VERIFICATION_POLICY + TestConstants.TEST_VERIFICATION_POLICY, ) - } + }, ) actor.attemptsTo( - Ensure.thatTheLastResponse().statusCode().isEqualTo(HttpStatus.SC_CREATED) + Ensure.thatTheLastResponse().statusCode().isEqualTo(HttpStatus.SC_CREATED), ) } @@ -39,7 +39,7 @@ class VerificationPoliciesSteps { Ensure.that(policy.nonce).isNotNull(), Ensure.that(policy.kind).contains("VerificationPolicy"), Ensure.that(policy.name).contains(TestConstants.TEST_VERIFICATION_POLICY.name), - Ensure.that(policy.description).contains(TestConstants.TEST_VERIFICATION_POLICY.description) + Ensure.that(policy.description).contains(TestConstants.TEST_VERIFICATION_POLICY.description), ) policy.constraints!!.forEach { @@ -47,8 +47,8 @@ class VerificationPoliciesSteps { Ensure.that(it.schemaId).isEqualTo(TestConstants.TEST_VERIFICATION_POLICY.constraints!!.first().schemaId), Ensure.that(it.trustedIssuers!!) .containsExactlyInAnyOrderElementsFrom( - TestConstants.TEST_VERIFICATION_POLICY.constraints!!.first().trustedIssuers!! - ) + TestConstants.TEST_VERIFICATION_POLICY.constraints!!.first().trustedIssuers!!, + ), ) } actor.remember("policy", policy) @@ -60,12 +60,12 @@ class VerificationPoliciesSteps { val updatePolicyInput = VerificationPolicyInput( name = policy.name, description = "updated description + ${UUID.randomUUID()}", - constraints = policy.constraints + constraints = policy.constraints, ) actor.attemptsTo( Put.to("/verification/policies/${policy.id}?nonce=${policy.nonce}").with { it.body(updatePolicyInput) - } + }, ) actor.remember("updatedPolicyInput", updatePolicyInput) } @@ -75,7 +75,7 @@ class VerificationPoliciesSteps { val updatePolicyInput = actor.forget("updatedPolicyInput") actor.attemptsTo( - Get.resource("/verification/policies/${actor.recall("policy").id}") + Get.resource("/verification/policies/${actor.recall("policy").id}"), ) val policy = SerenityRest.lastResponse().get() @@ -84,7 +84,7 @@ class VerificationPoliciesSteps { Ensure.that(policy.nonce).isNotNull(), Ensure.that(policy.kind).contains("VerificationPolicy"), Ensure.that(policy.name).contains(updatePolicyInput.name), - Ensure.that(policy.description).contains(updatePolicyInput.description) + Ensure.that(policy.description).contains(updatePolicyInput.description), ) policy.constraints!!.forEach { @@ -92,8 +92,8 @@ class VerificationPoliciesSteps { Ensure.that(it.schemaId).isEqualTo(updatePolicyInput.constraints!!.first().schemaId), Ensure.that(it.trustedIssuers!!) .containsExactlyInAnyOrderElementsFrom( - updatePolicyInput.constraints!!.first().trustedIssuers!! - ) + updatePolicyInput.constraints!!.first().trustedIssuers!!, + ), ) } } diff --git a/tests/integration-tests/src/test/resources/configs/mt_keycloak_agent_role.conf b/tests/integration-tests/src/test/resources/configs/mt_keycloak_agent_role.conf index 480ffaad69..66cc1b4aea 100644 --- a/tests/integration-tests/src/test/resources/configs/mt_keycloak_agent_role.conf +++ b/tests/integration-tests/src/test/resources/configs/mt_keycloak_agent_role.conf @@ -26,7 +26,7 @@ roles = [ name = "Admin" url = "${ADMIN_AGENT_URL:-http://localhost:8080}" agent_role = "Admin" - } + }, { name = "Issuer" url = "${ISSUER_AGENT_URL:-http://localhost:8080}" diff --git a/tests/integration-tests/src/test/resources/configs/mt_keycloak_vault.conf b/tests/integration-tests/src/test/resources/configs/mt_keycloak_vault.conf index f5dff9f9ab..20ac254707 100644 --- a/tests/integration-tests/src/test/resources/configs/mt_keycloak_vault.conf +++ b/tests/integration-tests/src/test/resources/configs/mt_keycloak_vault.conf @@ -9,6 +9,7 @@ services = { } vault = { http_port = 8200 + auth_type = "APP_ROLE" } } diff --git a/tests/integration-tests/src/test/resources/configs/mt_vault_approle.conf b/tests/integration-tests/src/test/resources/configs/mt_vault_approle.conf new file mode 100644 index 0000000000..bc557c7877 --- /dev/null +++ b/tests/integration-tests/src/test/resources/configs/mt_vault_approle.conf @@ -0,0 +1,59 @@ +# Specify shared services that are used by all agents (if any) +services = { + prism_node = { + http_port = 50053 + version = "${PRISM_NODE_VERSION}" + } + vault = { + http_port = 8200, + vault_auth_type = "APP_ROLE" + } +} + +# Specify agents that are required to be created before running tests +agents = [ + { + version = "${OPEN_ENTERPRISE_AGENT_VERSION}" + http_port = 8080 + didcomm_port = 7080 + auth_enabled = true + prism_node = ${services.prism_node} + vault = ${services.vault} + } +] + +roles = [ + { + name = "Admin" + url = "${ADMIN_AGENT_URL:-http://localhost:8080}" + apikey = "${ADMIN_API_KEY:-admin}" + auth_header = "x-admin-api-key" + } + { + name = "Issuer" + url = "${ISSUER_AGENT_URL:-http://localhost:8080}" + apikey = "${ISSUER_API_KEY:-${random.string(16)}}" + webhook = { + url = "${ISSUER_WEBHOOK_URL:-http://host.docker.internal:9955}" + init_required = true + } + }, + { + name = "Holder" + url = "${HOLDER_AGENT_URL:-http://localhost:8080}" + apikey = "${HOLDER_API_KEY:-${random.string(16)}}" + webhook = { + url = "${HOLDER_WEBHOOK_URL:-http://host.docker.internal:9956}" + init_required = true + } + }, + { + name = "Verifier" + url = "${VERIFIER_AGENT_URL:-http://localhost:8080}" + apikey = "${VERIFIER_API_KEY:-${random.string(16)}}" + webhook = { + url = "${VERIFIER_WEBHOOK_URL:-http://host.docker.internal:9957}" + init_required = true + } + } +] diff --git a/tests/integration-tests/src/test/resources/configs/mt_vault.conf b/tests/integration-tests/src/test/resources/configs/mt_vault_token.conf similarity index 96% rename from tests/integration-tests/src/test/resources/configs/mt_vault.conf rename to tests/integration-tests/src/test/resources/configs/mt_vault_token.conf index 4b40259412..b49baaab85 100644 --- a/tests/integration-tests/src/test/resources/configs/mt_vault.conf +++ b/tests/integration-tests/src/test/resources/configs/mt_vault_token.conf @@ -5,7 +5,8 @@ services = { version = "${PRISM_NODE_VERSION}" } vault = { - http_port = 8200 + http_port = 8200, + vault_auth_type = TOKEN } } diff --git a/tests/integration-tests/src/test/resources/containers/agent.yml b/tests/integration-tests/src/test/resources/containers/agent.yml index 050699c4d4..033601a9d1 100644 --- a/tests/integration-tests/src/test/resources/containers/agent.yml +++ b/tests/integration-tests/src/test/resources/containers/agent.yml @@ -31,8 +31,9 @@ services: POLLUX_DB_HOST: postgres CONNECT_DB_HOST: postgres AGENT_DB_HOST: postgres - VAULT_APPROLE_ROLE_ID: agent - VAULT_APPROLE_SECRET_ID: agent-secret + VAULT_TOKEN: null + VAULT_APPROLE_ROLE_ID: null + VAULT_APPROLE_SECRET_ID: null AUTH_HEADER: apikey ADMIN_AUTH_HEADER: "x-admin-api-key" # Configuration parameters diff --git a/tests/integration-tests/src/test/resources/cucumber.properties b/tests/integration-tests/src/test/resources/cucumber.properties deleted file mode 100644 index 170a3740bb..0000000000 --- a/tests/integration-tests/src/test/resources/cucumber.properties +++ /dev/null @@ -1,2 +0,0 @@ -cucumber.publish.quiet=true -cucumber.object-factory=io.iohk.atala.automation.serenity.objectfactory.AtalaObjectFactory \ No newline at end of file diff --git a/tests/integration-tests/src/test/resources/log4j.properties b/tests/integration-tests/src/test/resources/log4j.properties deleted file mode 100644 index 0ee9259273..0000000000 --- a/tests/integration-tests/src/test/resources/log4j.properties +++ /dev/null @@ -1,7 +0,0 @@ -# Root logger option -log4j.rootLogger=INFO, stdout -# Direct log messages to stdout -log4j.appender.stdout=org.apache.log4j.ConsoleAppender -log4j.appender.stdout.Target=System.out -log4j.appender.stdout.layout=org.apache.log4j.PatternLayout -log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n diff --git a/tests/integration-tests/src/test/resources/logback-test.xml b/tests/integration-tests/src/test/resources/logback-test.xml new file mode 100644 index 0000000000..357d669652 --- /dev/null +++ b/tests/integration-tests/src/test/resources/logback-test.xml @@ -0,0 +1,16 @@ + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n + + + + + + + + + + + +