diff --git a/.github/workflows/e2e-tests.yml b/.github/workflows/integration-tests.yml similarity index 92% rename from .github/workflows/e2e-tests.yml rename to .github/workflows/integration-tests.yml index ff4f0829ea..ce047ef1a8 100644 --- a/.github/workflows/e2e-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -1,7 +1,7 @@ name: Integration tests concurrency: - group: ${{ github.head_ref }}${{ github.ref }}-e2e-tests + group: ${{ github.head_ref }}${{ github.ref }}-integration-tests cancel-in-progress: true on: @@ -16,14 +16,14 @@ on: defaults: run: shell: bash - working-directory: "tests/e2e-tests" + working-directory: "tests/integration-tests" jobs: - run-e2e-tests: + run-integration-tests: name: "Run e2e tests" runs-on: ubuntu-latest env: - REPORTS_DIR: "tests/e2e-tests/target/site/serenity" + REPORTS_DIR: "tests/integration-tests/target/site/serenity" steps: - name: Checkout uses: actions/checkout@v3 @@ -99,10 +99,13 @@ jobs: distribution: 'zulu' java-version: '19' - - name: Run e2e tests + - name: Run integration tests + env: + ATALA_GITHUB_ACTOR: ${{ secrets.ATALA_GITHUB_ACTOR }} + ATALA_GITHUB_TOKEN: ${{ secrets.ATALA_GITHUB_TOKEN }} continue-on-error: true run: | - ./gradlew test --tests "E2eTestsRunner" || true + ./gradlew test --tests "IntegrationTestsRunner" || true ./gradlew reports - name: Extract test results @@ -148,7 +151,7 @@ jobs: if: github.ref_name == 'main' || steps.analyze_test_results.outputs.conclusion == 'failure' uses: actions/upload-artifact@v2 with: - name: e2e-tests-result + name: integration-tests-result path: ${{ env.REPORTS_DIR }} - name: Slack Notification diff --git a/infrastructure/shared/docker-compose-demo.yml b/infrastructure/shared/docker-compose-demo.yml index 2021606da1..14a42d9b33 100644 --- a/infrastructure/shared/docker-compose-demo.yml +++ b/infrastructure/shared/docker-compose-demo.yml @@ -23,6 +23,8 @@ services: image: ghcr.io/input-output-hk/prism-node:${PRISM_NODE_VERSION} environment: NODE_PSQL_HOST: db:5432 + NODE_REFRESH_AND_SUBMIT_PERIOD: 1s + NODE_MOVE_SCHEDULED_TO_PENDING_PERIOD: 1s depends_on: db: condition: service_healthy diff --git a/tests/e2e-tests/build.gradle.kts b/tests/e2e-tests/build.gradle.kts deleted file mode 100644 index 4e9d088ca1..0000000000 --- a/tests/e2e-tests/build.gradle.kts +++ /dev/null @@ -1,55 +0,0 @@ -plugins { - id("org.jetbrains.kotlin.jvm") version "1.9.0" - idea - jacoco - id("net.serenity-bdd.serenity-gradle-plugin") version "4.0.1" - kotlin("plugin.serialization") version "1.9.0" -} - -repositories { - mavenCentral() -} - -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.1") - implementation("net.serenity-bdd:serenity-cucumber:4.0.1") - implementation("net.serenity-bdd:serenity-screenplay-rest:4.0.1") - testImplementation("net.serenity-bdd:serenity-ensure:4.0.1") - // Beautify exceptions handling assertions - testImplementation("org.assertj:assertj-core:3.23.1") - // Navigate through Json with xpath - testImplementation("com.jayway.jsonpath:json-path:2.7.0") - // HTTP listener - implementation("io.ktor:ktor-server-netty:2.3.0") - implementation("io.ktor:ktor-client-apache:2.3.0") - implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.0") -} - -buildscript { - dependencies { - classpath("net.serenity-bdd:serenity-single-page-report:4.0.1") - classpath("net.serenity-bdd:serenity-json-summary-report:4.0.1") - } -} - -/** - * Add HTML one-pager and JSON summary report to be produced - */ -serenity { - reports = listOf("single-page-html", "json-summary") -} - -tasks.test { - testLogging.showStandardStreams = true - systemProperty("cucumber.filter.tags", System.getProperty("cucumber.filter.tags")) -} - -kotlin { - jvmToolchain(19) -} diff --git a/tests/e2e-tests/settings.gradle.kts b/tests/e2e-tests/settings.gradle.kts deleted file mode 100644 index 40b97a0ff6..0000000000 --- a/tests/e2e-tests/settings.gradle.kts +++ /dev/null @@ -1,2 +0,0 @@ - -rootProject.name = "e2e-tests" diff --git a/tests/e2e-tests/src/main/kotlin/api_models/Connection.kt b/tests/e2e-tests/src/main/kotlin/api_models/Connection.kt deleted file mode 100644 index 37096a7609..0000000000 --- a/tests/e2e-tests/src/main/kotlin/api_models/Connection.kt +++ /dev/null @@ -1,27 +0,0 @@ -package api_models - -import kotlinx.serialization.Serializable - -@Serializable -data class Connection( - var connectionId: String = "", - var thid: String = "", - var createdAt: String = "", - var updatedAt: String = "", - var invitation: Invitation = Invitation(), - var kind: String = "", - var self: String = "", - var state: String = "", - var label: String = "", - var myDid: String = "", - var theirDid: String = "", - var role: String = "", - var metaRetries: Int = 0, -): JsonEncoded - -object ConnectionState { - const val INVITATION_GENERATED = "InvitationGenerated" - const val CONNECTION_REQUEST_PENDING = "ConnectionRequestPending" - const val CONNECTION_RESPONSE_SENT = "ConnectionResponseSent" - const val CONNECTION_RESPONSE_RECEIVED = "ConnectionResponseReceived" -} diff --git a/tests/e2e-tests/src/main/kotlin/api_models/CreatePrismDidRequest.kt b/tests/e2e-tests/src/main/kotlin/api_models/CreatePrismDidRequest.kt deleted file mode 100644 index 4c90ac84c4..0000000000 --- a/tests/e2e-tests/src/main/kotlin/api_models/CreatePrismDidRequest.kt +++ /dev/null @@ -1,5 +0,0 @@ -package api_models - -data class CreatePrismDidRequest( - val documentTemplate: DocumentTemplate, -) diff --git a/tests/e2e-tests/src/main/kotlin/api_models/Credential.kt b/tests/e2e-tests/src/main/kotlin/api_models/Credential.kt deleted file mode 100644 index eea423b762..0000000000 --- a/tests/e2e-tests/src/main/kotlin/api_models/Credential.kt +++ /dev/null @@ -1,31 +0,0 @@ -package api_models - -import kotlinx.serialization.Serializable - -@Serializable -data class Credential( - var automaticIssuance: Boolean = false, - var awaitConfirmation: Boolean = false, - var createdAt: String = "", - var protocolState: String = "", - var recordId: String = "", - var thid: String = "", - var role: String = "", - var schemaId: String? = "", - var subjectId: String = "", - var updatedAt: String = "", - var validityPeriod: Double = 0.0, - var claims: LinkedHashMap = LinkedHashMap(), - var credential: String = "", - var issuingDID: String = "", - var connectionId: String = "", - var credentialFormat: String = "JWT", - var metaRetries: Int = 0, -): JsonEncoded - -object CredentialState { - const val OFFER_RECEIVED = "OfferReceived" - const val REQUEST_RECEIVED = "RequestReceived" - const val CREDENTIAL_SENT = "CredentialSent" - const val CREDENTIAL_RECEIVED = "CredentialReceived" -} diff --git a/tests/e2e-tests/src/main/kotlin/api_models/CredentialSchema.kt b/tests/e2e-tests/src/main/kotlin/api_models/CredentialSchema.kt deleted file mode 100644 index be0c860cc1..0000000000 --- a/tests/e2e-tests/src/main/kotlin/api_models/CredentialSchema.kt +++ /dev/null @@ -1,19 +0,0 @@ -package api_models - -import com.fasterxml.jackson.databind.JsonNode - -data class CredentialSchema( - var name: String? = null, - var version: String? = null, - var tags: List? = listOf(""), - var description: String? = null, - var type: String? = null, - var author: String? = null, - var authored: String? = null, - var schema: JsonNode? = null, - var guid: String? = null, - var longId: String? = null, - var id: String? = null, - var kind: String? = null, - var self: String? = null, -) diff --git a/tests/e2e-tests/src/main/kotlin/api_models/DidResolutionResult.kt b/tests/e2e-tests/src/main/kotlin/api_models/DidResolutionResult.kt deleted file mode 100644 index cedf10631c..0000000000 --- a/tests/e2e-tests/src/main/kotlin/api_models/DidResolutionResult.kt +++ /dev/null @@ -1,64 +0,0 @@ -package api_models - -import kotlinx.serialization.Serializable - -@Serializable -data class DidResolutionResult( - var `@context`: String? = null, - var didDocument: DidDocument? = null, - var didDocumentMetadata: DidDocumentMetadata? = null, - var didResolutionMetadata: DidResolutionMetadata? = null, -): JsonEncoded - -@Serializable -data class DidDocument( - var `@context`: List? = null, - var assertionMethod: List? = null, - var authentication: List? = null, - var capabilityInvocation: List? = null, - var capabilityDelegation: List? = null, - var controller: String? = null, - var id: String? = null, - var keyAgreement: List? = null, - var service: List? = null, - var verificationMethod: List? = null, -): JsonEncoded - -@Serializable -data class VerificationMethod( - var controller: String? = null, - var id: String? = null, - var publicKeyJwk: PublicKeyJwk? = null, - var type: String? = null, -): JsonEncoded - -typealias VerificationMethodRef = String - -@Serializable -data class PublicKeyJwk( - var crv: String? = null, - var kty: String? = null, - var x: String? = null, - var y: String? = null, -): JsonEncoded - -@Serializable -data class DidDocumentMetadata( - var canonicalId: String? = null, - var versionId: String? = null, - var deactivated: Boolean? = null, - var created: String? = null, - var updated: String? = null, -): JsonEncoded - -@Serializable -data class DidDocumentService( - var id: String? = null, - var serviceEndpoint: List? = null, - var type: String? = null, -): JsonEncoded - -@Serializable -data class DidResolutionMetadata( - var contentType: String? = null, -): JsonEncoded diff --git a/tests/e2e-tests/src/main/kotlin/api_models/DocumentTemplate.kt b/tests/e2e-tests/src/main/kotlin/api_models/DocumentTemplate.kt deleted file mode 100644 index fb52838955..0000000000 --- a/tests/e2e-tests/src/main/kotlin/api_models/DocumentTemplate.kt +++ /dev/null @@ -1,6 +0,0 @@ -package api_models - -data class DocumentTemplate( - val publicKeys: List, - val services: List, -) diff --git a/tests/e2e-tests/src/main/kotlin/api_models/Entity.kt b/tests/e2e-tests/src/main/kotlin/api_models/Entity.kt deleted file mode 100644 index 6035d98f50..0000000000 --- a/tests/e2e-tests/src/main/kotlin/api_models/Entity.kt +++ /dev/null @@ -1,16 +0,0 @@ -package api_models - -import kotlinx.serialization.Serializable - -@Serializable -data class CreateEntityRequest( - val walletId: String, - val name: String, - val id: String, -): JsonEncoded - -@Serializable -data class AddApiKeyRequest( - val entityId: String, - val apiKey: String, -): JsonEncoded diff --git a/tests/e2e-tests/src/main/kotlin/api_models/EventRegistration.kt b/tests/e2e-tests/src/main/kotlin/api_models/EventRegistration.kt deleted file mode 100644 index 6c152b45bd..0000000000 --- a/tests/e2e-tests/src/main/kotlin/api_models/EventRegistration.kt +++ /dev/null @@ -1,8 +0,0 @@ -package api_models - -import kotlinx.serialization.Serializable - -@Serializable -data class RegisterWebhookRequest( - val url: String, -) :JsonEncoded diff --git a/tests/e2e-tests/src/main/kotlin/api_models/Events.kt b/tests/e2e-tests/src/main/kotlin/api_models/Events.kt deleted file mode 100644 index 817274ffe4..0000000000 --- a/tests/e2e-tests/src/main/kotlin/api_models/Events.kt +++ /dev/null @@ -1,49 +0,0 @@ -package api_models - -import kotlinx.serialization.Serializable -import kotlinx.serialization.json.JsonElement - -@Serializable -data class Event( - var type: String, - var id: String, - var ts: String, - var data: JsonElement, - var walletId: String, -) : JsonEncoded - -@Serializable -data class ConnectionEvent( - var type: String, - var id: String, - var ts: String, - var data: Connection, - var walletId: String, -) : JsonEncoded - -@Serializable -data class CredentialEvent( - var type: String, - var id: String, - var ts: String, - var data: Credential, - var walletId: String, -) : JsonEncoded - -@Serializable -data class PresentationEvent( - var type: String, - var id: String, - var ts: String, - var data: PresentationProof, - var walletId: String, -) : JsonEncoded - -@Serializable -data class DidEvent( - var type: String, - var id: String, - var ts: String, - var data: ManagedDid, - var walletId: String, -) : JsonEncoded diff --git a/tests/e2e-tests/src/main/kotlin/api_models/HealthInfo.kt b/tests/e2e-tests/src/main/kotlin/api_models/HealthInfo.kt deleted file mode 100644 index 896c228f42..0000000000 --- a/tests/e2e-tests/src/main/kotlin/api_models/HealthInfo.kt +++ /dev/null @@ -1,4 +0,0 @@ -package api_models -data class HealthInfo( - var version: String = "", -) diff --git a/tests/e2e-tests/src/main/kotlin/api_models/Invitation.kt b/tests/e2e-tests/src/main/kotlin/api_models/Invitation.kt deleted file mode 100644 index 93cb6f4fe8..0000000000 --- a/tests/e2e-tests/src/main/kotlin/api_models/Invitation.kt +++ /dev/null @@ -1,11 +0,0 @@ -package api_models - -import kotlinx.serialization.Serializable - -@Serializable -data class Invitation( - var id: String = "", - var from: String = "", - var invitationUrl: String = "", - var type: String = "", -): JsonEncoded diff --git a/tests/e2e-tests/src/main/kotlin/api_models/JsonEncoded.kt b/tests/e2e-tests/src/main/kotlin/api_models/JsonEncoded.kt deleted file mode 100644 index 560d6132f1..0000000000 --- a/tests/e2e-tests/src/main/kotlin/api_models/JsonEncoded.kt +++ /dev/null @@ -1,12 +0,0 @@ -package api_models - -import kotlinx.serialization.Serializable -import kotlinx.serialization.encodeToString -import kotlinx.serialization.json.Json - -@Serializable -sealed interface JsonEncoded { - fun toJsonString(): String { - return Json.encodeToString(this) - } -} diff --git a/tests/e2e-tests/src/main/kotlin/api_models/ManagedDid.kt b/tests/e2e-tests/src/main/kotlin/api_models/ManagedDid.kt deleted file mode 100644 index e5063ffc30..0000000000 --- a/tests/e2e-tests/src/main/kotlin/api_models/ManagedDid.kt +++ /dev/null @@ -1,15 +0,0 @@ -package api_models - -import kotlinx.serialization.Serializable - -@Serializable -data class ManagedDid( - var did: String = "", - var longFormDid: String = "", - var status: String = "", -): JsonEncoded - -object ManagedDidStatuses { - val PUBLISHED = "PUBLISHED" - val CREATED = "CREATED" -} diff --git a/tests/e2e-tests/src/main/kotlin/api_models/PresentationProof.kt b/tests/e2e-tests/src/main/kotlin/api_models/PresentationProof.kt deleted file mode 100644 index 2f617a3571..0000000000 --- a/tests/e2e-tests/src/main/kotlin/api_models/PresentationProof.kt +++ /dev/null @@ -1,21 +0,0 @@ -package api_models - -import kotlinx.serialization.Serializable - -@Serializable -data class PresentationProof( - var presentationId: String? = null, - var thid: String? = null, - var status: String? = null, - var connectionId: String? = null, - var proofs: List? = null, - var data: List? = null, - var role: String? = null, - var metaRetries: Int = 0, -): JsonEncoded - -object PresentationProofStatus { - const val REQUEST_RECEIVED = "RequestReceived" - const val REQUEST_REJECTED = "RequestRejected" - const val PRESENTATION_VERIFIED = "PresentationVerified" -} diff --git a/tests/e2e-tests/src/main/kotlin/api_models/PublicKey.kt b/tests/e2e-tests/src/main/kotlin/api_models/PublicKey.kt deleted file mode 100644 index 9e53427ea4..0000000000 --- a/tests/e2e-tests/src/main/kotlin/api_models/PublicKey.kt +++ /dev/null @@ -1,16 +0,0 @@ -package api_models - -import com.fasterxml.jackson.annotation.JsonValue - -data class PublicKey( - val id: String, - val purpose: Purpose, -) - -enum class Purpose(@JsonValue val value: String) { - AUTHENTICATION("authentication"), - ASSERTION_METHOD("assertionMethod"), - KEY_AGREEMENT("keyAgreement"), - CAPABILITY_INVOCATION("capabilityInvocation"), - CAPABILITY_DELEGATION("capabilityDelegation"), -} diff --git a/tests/e2e-tests/src/main/kotlin/api_models/Service.kt b/tests/e2e-tests/src/main/kotlin/api_models/Service.kt deleted file mode 100644 index 1fb68ee4f4..0000000000 --- a/tests/e2e-tests/src/main/kotlin/api_models/Service.kt +++ /dev/null @@ -1,7 +0,0 @@ -package api_models - -data class Service( - var id: String = "", - var serviceEndpoint: List = listOf(""), - var type: String = "", -) diff --git a/tests/e2e-tests/src/main/kotlin/api_models/UpdatePrismDidRequest.kt b/tests/e2e-tests/src/main/kotlin/api_models/UpdatePrismDidRequest.kt deleted file mode 100644 index d9681335e0..0000000000 --- a/tests/e2e-tests/src/main/kotlin/api_models/UpdatePrismDidRequest.kt +++ /dev/null @@ -1,14 +0,0 @@ -package api_models - -data class UpdatePrismDidRequest( - val actions: List, -) - -data class UpdatePrismDidAction( - val actionType: String? = null, - val addKey: PublicKey? = null, - val removeKey: PublicKey? = null, - val addService: Service? = null, - val removeService: Service? = null, - val updateService: Service? = null, -) diff --git a/tests/e2e-tests/src/main/kotlin/api_models/VerificationPolicy.kt b/tests/e2e-tests/src/main/kotlin/api_models/VerificationPolicy.kt deleted file mode 100644 index c3eb0caf21..0000000000 --- a/tests/e2e-tests/src/main/kotlin/api_models/VerificationPolicy.kt +++ /dev/null @@ -1,24 +0,0 @@ -package api_models - -data class VerificationPolicy( - var id: String? = null, - var nonce: String? = null, - var name: String? = null, - var description: String? = null, - var constraints: List? = null, - var createdAt: String? = null, - var updatedAt: String? = null, - var kind: String? = null, - var self: String? = null, -) - -data class VerificationPolicyInput( - var name: String? = null, - var description: String? = null, - var constraints: List? = null, -) - -data class Constraint( - var schemaId: String? = null, - var trustedIssuers: List? = null, -) diff --git a/tests/e2e-tests/src/main/kotlin/api_models/Wallet.kt b/tests/e2e-tests/src/main/kotlin/api_models/Wallet.kt deleted file mode 100644 index 4bb7283bc8..0000000000 --- a/tests/e2e-tests/src/main/kotlin/api_models/Wallet.kt +++ /dev/null @@ -1,10 +0,0 @@ -package api_models - -import kotlinx.serialization.Serializable - -@Serializable -data class CreateWalletRequest( - val name: String, - val seed: String, - val id: String, -): JsonEncoded diff --git a/tests/e2e-tests/src/test/kotlin/common/CredentialSchemas.kt b/tests/e2e-tests/src/test/kotlin/common/CredentialSchemas.kt deleted file mode 100644 index 70338cc29f..0000000000 --- a/tests/e2e-tests/src/test/kotlin/common/CredentialSchemas.kt +++ /dev/null @@ -1,51 +0,0 @@ -package common - -import api_models.CredentialSchema -import com.fasterxml.jackson.databind.ObjectMapper -import java.util.* - -object CredentialSchemas { - - val CREDENTIAL_SCHEMA_TYPE = "https://w3c-ccg.github.io/vc-json-schemas/schema/2.0/schema.json" - - val SCHEMA_TYPE = "https://json-schema.org/draft/2020-12/schema" - - val JSON_SCHEMA = """ - { - "${"$"}id": "https://example.com/student-schema-1.0", - "${"$"}schema": "$SCHEMA_TYPE", - "description": "Student schema", - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "age": { - "type": "integer" - } - } - } - """.trimIndent() - - fun generate_with_name_suffix_and_author(suffix: String, author: String): CredentialSchema { - return CredentialSchema( - author = author, - name = "${UUID.randomUUID()} $suffix", - description = "Simple student credentials schema", - type = CREDENTIAL_SCHEMA_TYPE, - schema = ObjectMapper().readTree(JSON_SCHEMA), - tags = listOf("school", "students"), - version = "1.0.0", - ) - } - - val STUDENT_SCHEMA = CredentialSchema( - author = "did:prism:agent", - name = UUID.randomUUID().toString(), - description = "Simple student credentials schema", - type = CREDENTIAL_SCHEMA_TYPE, - schema = ObjectMapper().readTree(JSON_SCHEMA), - tags = listOf("school", "students"), - version = "1.0.0", - ) -} diff --git a/tests/e2e-tests/src/test/kotlin/common/Ensure.kt b/tests/e2e-tests/src/test/kotlin/common/Ensure.kt deleted file mode 100644 index 491437276d..0000000000 --- a/tests/e2e-tests/src/test/kotlin/common/Ensure.kt +++ /dev/null @@ -1,52 +0,0 @@ -package common - -import net.serenitybdd.screenplay.Question -import org.openqa.selenium.By -import java.time.LocalDate -import java.time.LocalTime -import net.serenitybdd.screenplay.ensure.enableSoftAssertions as EnableSoftAssertions -import net.serenitybdd.screenplay.ensure.reportSoftAssertions as ReportSoftAssertions -import net.serenitybdd.screenplay.ensure.that as That -import net.serenitybdd.screenplay.ensure.thatAmongst as ThatAmongst -import net.serenitybdd.screenplay.ensure.thatTheCurrentPage as ThatTheCurrentPage -import net.serenitybdd.screenplay.ensure.thatTheListOf as ThatTheListOf -import net.serenitybdd.screenplay.targets.Target as SerenityTarget - -object Ensure { - fun that(value: String?) = That(value) - fun that(value: LocalDate) = That(value) - fun that(value: LocalTime) = That(value) - fun that(value: Boolean) = That(value) - fun that(value: Float) = That(value) - fun that(value: Double) = That(value) - - fun that(value: Comparable) = That(value) - fun that(value: Collection) = That(value) - - fun that(question: Question, predicate: (actual: A) -> Boolean) = That(question, predicate) - fun that(description: String, question: Question, predicate: (actual: A) -> Boolean) = - That(description, question, predicate) - - fun > that(description: String, question: Question) = That(description, question) - fun > that(question: Question) = That(question) - - fun that(description: String, question: Question>) = That(description, question) - fun that(question: Question>) = That(question) - - fun thatTheListOf(description: String, question: Question>) = ThatTheListOf(description, question) - fun thatTheListOf(question: Question>) = ThatTheListOf(question) - - fun thatTheCurrentPage() = ThatTheCurrentPage() - fun that(value: SerenityTarget) = That(value) - fun that(value: By) = net.serenitybdd.screenplay.ensure.that(value) - - // Collection matchers - fun thatTheListOf(value: SerenityTarget) = ThatTheListOf(value) - fun thatTheListOf(value: By) = ThatTheListOf(value) - - fun thatAmongst(value: SerenityTarget) = ThatAmongst(value) - fun thatAmongst(value: By) = ThatAmongst(value) - - fun enableSoftAssertions() = EnableSoftAssertions() - fun reportSoftAssertions() = ReportSoftAssertions() -} diff --git a/tests/e2e-tests/src/test/kotlin/common/Environments.kt b/tests/e2e-tests/src/test/kotlin/common/Environments.kt deleted file mode 100644 index eebbe6db36..0000000000 --- a/tests/e2e-tests/src/test/kotlin/common/Environments.kt +++ /dev/null @@ -1,24 +0,0 @@ -package common - -object Environments { - val AGENT_AUTH_REQUIRED: Boolean = (System.getenv("AGENT_AUTH_REQUIRED") ?: "true").toBoolean() - val AGENT_AUTH_HEADER = System.getenv("AGENT_AUTH_HEADER") ?: "apikey" - val ACME_AUTH_KEY = System.getenv("ACME_AUTH_KEY") ?: "SECURE_ACME_AUTH_KEY_GREATER_16_SYMBOLS" - val ACME_AGENT_URL = System.getenv("ACME_AGENT_URL") ?: "http://localhost:8080/prism-agent" - val ACME_AGENT_WEBHOOK_HOST = System.getenv("ACME_AGENT_WEBHOOK_HOST") ?: "host.docker.internal" - val ACME_AGENT_WEBHOOK_PORT = (System.getenv("ACME_AGENT_WEBHOOK_PORT") ?: "9955").toInt() - val ACME_AGENT_WEBHOOK_URL = "http://$ACME_AGENT_WEBHOOK_HOST:$ACME_AGENT_WEBHOOK_PORT" - val BOB_AGENT_URL = System.getenv("BOB_AGENT_URL") ?: "http://localhost:8090/prism-agent" - val BOB_AUTH_KEY = System.getenv("BOB_AUTH_KEY") ?: "default" - val BOB_AGENT_WEBHOOK_HOST = System.getenv("BOB_AGENT_WEBHOOK_HOST") ?: "host.docker.internal" - val BOB_AGENT_WEBHOOK_PORT = (System.getenv("BOB_AGENT_WEBHOOK_PORT") ?: "9956").toInt() - val BOB_AGENT_WEBHOOK_URL = "http://$BOB_AGENT_WEBHOOK_HOST:$BOB_AGENT_WEBHOOK_PORT" - val FABER_AGENT_URL = System.getenv("FABER_AGENT_URL") ?: "http://localhost:8080/prism-agent" - val FABER_AUTH_KEY = System.getenv("FABER_AUTH_KEY") ?: "SECURE_FABER_AUTH_KEY_GREATER_16_SYMBOLS" - val FABER_AGENT_WEBHOOK_HOST = System.getenv("FABER_AGENT_WEBHOOK_HOST") ?: "host.docker.internal" - val FABER_AGENT_WEBHOOK_PORT = (System.getenv("FABER_AGENT_WEBHOOK_PORT") ?: "9957").toInt() - val FABER_AGENT_WEBHOOK_URL = "http://$FABER_AGENT_WEBHOOK_HOST:$FABER_AGENT_WEBHOOK_PORT" - val ADMIN_AGENT_URL = ACME_AGENT_URL - val ADMIN_AUTH_HEADER = System.getenv("ADMIN_AUTH_HEADER") ?: "x-admin-api-key" - val ADMIN_AUTH_TOKEN = System.getenv("ADMIN_AUTH_TOKEN") ?: "admin" -} diff --git a/tests/e2e-tests/src/test/kotlin/common/TestConstants.kt b/tests/e2e-tests/src/test/kotlin/common/TestConstants.kt deleted file mode 100644 index 21bf3c67e9..0000000000 --- a/tests/e2e-tests/src/test/kotlin/common/TestConstants.kt +++ /dev/null @@ -1,43 +0,0 @@ -package common - -import api_models.PublicKey -import api_models.Purpose -import api_models.Service -import java.time.Duration -import java.util.* - -object TestConstants { - val VERIFICATION_POLICIES = VerificationPolicies - val CREDENTIAL_SCHEMAS = CredentialSchemas - val RANDOM_CONSTAND_UUID = UUID.randomUUID().toString() - val DID_UPDATE_PUBLISH_MAX_WAIT_5_MIN = Duration.ofSeconds(60L) - val PRISM_DID_AUTH_KEY = PublicKey("auth-1", Purpose.AUTHENTICATION) - val PRISM_DID_ASSERTION_KEY = PublicKey("assertion-1", Purpose.ASSERTION_METHOD) - val PRISM_DID_UPDATE_NEW_AUTH_KEY = PublicKey("auth-2", Purpose.AUTHENTICATION) - val PRISM_DID_SERVICE = Service( - "https://foo.bar.com", - listOf("https://foo.bar.com/"), - "LinkedDomains", - ) - val PRISM_DID_SERVICE_FOR_UPDATE = Service( - "https://update.com", - listOf("https://update.com/"), - "LinkedDomains", - ) - val PRISM_DID_SERVICE_TO_REMOVE = Service( - "https://remove.com", - listOf("https://remove.com/"), - "LinkedDomains", - ) - val PRISM_DID_UPDATE_NEW_SERVICE_URL = "https://bar.foo.com/" - val PRISM_DID_UPDATE_NEW_SERVICE = Service( - "https://new.service.com", - listOf("https://new.service.com/"), - "LinkedDomains", - ) - val EVENT_TYPE_CONNECTION_UPDATED = "ConnectionUpdated" - val EVENT_TYPE_ISSUE_CREDENTIAL_RECORD_UPDATED = "IssueCredentialRecordUpdated" - val EVENT_TYPE_PRESENTATION_UPDATED = "PresentationUpdated" - val EVENT_TYPE_DID_STATUS_UPDATED = "DIDStatusUpdated" - val WRONG_SEED = "wrong seed" -} diff --git a/tests/e2e-tests/src/test/kotlin/common/Utils.kt b/tests/e2e-tests/src/test/kotlin/common/Utils.kt deleted file mode 100644 index b4d3fa53fd..0000000000 --- a/tests/e2e-tests/src/test/kotlin/common/Utils.kt +++ /dev/null @@ -1,52 +0,0 @@ -package common - -import com.fasterxml.jackson.databind.ObjectMapper -import com.jayway.jsonpath.DocumentContext -import com.jayway.jsonpath.JsonPath -import net.serenitybdd.rest.SerenityRest -import org.awaitility.Awaitility -import org.awaitility.core.ConditionTimeoutException -import org.awaitility.kotlin.withPollInterval -import org.awaitility.pollinterval.FixedPollInterval -import java.time.Duration -import kotlin.reflect.KClass - -object Utils { - - fun lastResponseObject(path: String, clazz: KClass): T { - return SerenityRest.lastResponse().jsonPath().getObject(path, clazz.java) - } - - fun lastResponseList(path: String, clazz: KClass): List { - return SerenityRest.lastResponse().jsonPath().getList(path, clazz.java) - } - - fun lastResponseMap(path: String, keyType: KClass, valueType: KClass): Map { - return SerenityRest.lastResponse().jsonPath().getMap(path, keyType.java, valueType.java) - } - - fun toJsonPath(any: Any): DocumentContext { - val json = ObjectMapper().writeValueAsString(any) - return JsonPath.parse(json) - } - - fun wait( - blockToWait: () -> Boolean, - errorMessage: String, - poolInterval: FixedPollInterval = FixedPollInterval(Duration.ofMillis(500L)), - timeout: Duration = Duration.ofSeconds(120L), - ) { - try { - Awaitility.await().withPollInterval(poolInterval) - .pollInSameThread() - .atMost(timeout) - .until { - blockToWait() - } - } catch (err: ConditionTimeoutException) { - throw ConditionTimeoutException( - errorMessage, - ) - } - } -} diff --git a/tests/e2e-tests/src/test/kotlin/common/VerificationPolicies.kt b/tests/e2e-tests/src/test/kotlin/common/VerificationPolicies.kt deleted file mode 100644 index a79fff24ff..0000000000 --- a/tests/e2e-tests/src/test/kotlin/common/VerificationPolicies.kt +++ /dev/null @@ -1,25 +0,0 @@ -package common - -import api_models.Constraint -import api_models.VerificationPolicy - -object VerificationPolicies { - - val schemaId = "http://atalaprism.io/schemas/1.0/StudentCredential" - val trustedIssuer1 = "did:example:123456789abcdefghi" - val trustedIssuer2 = "did:example:123456789abcdefghj" - - val VERIFICATION_POLICY = VerificationPolicy( - name = "Trusted Issuer and SchemaID", - description = "Verification Policy with trusted issuer and schemaId", - constraints = listOf( - Constraint( - schemaId = schemaId, - trustedIssuers = listOf( - trustedIssuer1, - trustedIssuer2 - ) - ) - ) - ) -} diff --git a/tests/e2e-tests/src/test/kotlin/features/CommonSteps.kt b/tests/e2e-tests/src/test/kotlin/features/CommonSteps.kt deleted file mode 100644 index 718e1f565d..0000000000 --- a/tests/e2e-tests/src/test/kotlin/features/CommonSteps.kt +++ /dev/null @@ -1,153 +0,0 @@ -package features - -import api_models.Connection -import api_models.ConnectionState -import api_models.Credential -import common.Environments -import common.ListenToEvents -import common.Utils -import common.Utils.lastResponseList -import features.connection.ConnectionSteps -import features.did.PublishDidSteps -import features.issue_credentials.IssueCredentialsSteps -import features.multitenancy.EntitySteps -import features.multitenancy.EventsSteps -import features.multitenancy.WalletsSteps -import interactions.Get -import io.cucumber.java.AfterAll -import io.cucumber.java.BeforeAll -import io.cucumber.java.ParameterType -import io.cucumber.java.en.Given -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 net.serenitybdd.screenplay.rest.questions.ResponseConsequence -import org.apache.http.HttpStatus.SC_OK - -@BeforeAll -fun initializeIssuerVerifierMultitenantAgent() { - val eventSteps = EventsSteps() - val cast = Cast() - cast.actorNamed("Admin", CallAnApi.at(Environments.ADMIN_AGENT_URL)) - cast.actorNamed("Acme", CallAnApi.at(Environments.ACME_AGENT_URL), ListenToEvents.at(Environments.ACME_AGENT_WEBHOOK_HOST, Environments.ACME_AGENT_WEBHOOK_PORT)) - cast.actorNamed("Bob", CallAnApi.at(Environments.BOB_AGENT_URL), ListenToEvents.at(Environments.BOB_AGENT_WEBHOOK_HOST, Environments.BOB_AGENT_WEBHOOK_PORT)) - cast.actorNamed("Faber", CallAnApi.at(Environments.FABER_AGENT_URL), ListenToEvents.at(Environments.FABER_AGENT_WEBHOOK_HOST, Environments.FABER_AGENT_WEBHOOK_PORT)) - OnStage.setTheStage(cast) - val walletSteps = WalletsSteps() - val entitySteps = EntitySteps() - - // Create issuer wallet and tenant - walletSteps.createNewWallet(cast.actorNamed("Admin"), "issuerWallet") - val issuerEntityId = entitySteps.createNewEntity( - cast.actorNamed("Admin"), walletId = Utils.lastResponseObject("id", String::class), name = "issuer" - ) - entitySteps.addNewApiKeyToEntity(cast.actorNamed("Admin"), issuerEntityId, Environments.ACME_AUTH_KEY) - - // Create verifier wallet - walletSteps.createNewWallet(cast.actorNamed("Admin"), "verifierWallet") - val verifierEntityId = entitySteps.createNewEntity( - cast.actorNamed("Admin"), walletId = Utils.lastResponseObject("id", String::class), name = "verifier" - ) - entitySteps.addNewApiKeyToEntity(cast.actorNamed("Admin"), verifierEntityId, Environments.FABER_AUTH_KEY) - cast.actors.forEach { actor -> - when(actor.name) { - "Acme" -> { - actor.remember("AUTH_KEY", Environments.ACME_AUTH_KEY) - } - "Bob" -> { - actor.remember("AUTH_KEY", Environments.BOB_AUTH_KEY) - } - "Faber" -> { - actor.remember("AUTH_KEY", Environments.FABER_AUTH_KEY) - } - } - } - eventSteps.registerNewWebhook(cast.actorNamed("Acme"), Environments.ACME_AGENT_WEBHOOK_URL) - eventSteps.registerNewWebhook(cast.actorNamed("Faber"), Environments.FABER_AGENT_WEBHOOK_URL) -} - -@AfterAll -fun clearStage() { - OnStage.drawTheCurtain() -} - -class CommonSteps { - @ParameterType(".*") - fun actor(actorName: String): Actor { - return OnStage.theActorCalled(actorName) - } - - @Given("{actor} has an issued credential from {actor}") - fun holderHasIssuedCredentialFromIssuer(holder: Actor, issuer: Actor) { - holder.attemptsTo( - Get.resource("/issue-credentials/records"), - ) - holder.should( - ResponseConsequence.seeThatResponse { - it.statusCode(SC_OK) - }, - ) - val receivedCredential = lastResponseList("contents", Credential::class).findLast { credential -> - credential.protocolState == "CredentialReceived" - } - - if (receivedCredential != null) { - holder.remember("issuedCredential", receivedCredential) - } else { - val publishDidSteps = PublishDidSteps() - val issueSteps = IssueCredentialsSteps() - actorsHaveExistingConnection(issuer, holder) - publishDidSteps.createsUnpublishedDid(holder) - publishDidSteps.createsUnpublishedDid(issuer) - publishDidSteps.hePublishesDidToLedger(issuer) - issueSteps.acmeOffersACredential(issuer, holder, "short") - issueSteps.bobRequestsTheCredential(holder) - issueSteps.acmeIssuesTheCredential(issuer) - issueSteps.bobHasTheCredentialIssued(holder) - } - } - - @Given("{actor} and {actor} have an existing connection") - fun actorsHaveExistingConnection(inviter: Actor, invitee: Actor) { - inviter.attemptsTo( - Get.resource("/connections"), - ) - inviter.should( - ResponseConsequence.seeThatResponse { - it.statusCode(SC_OK) - }, - ) - val inviterConnection = lastResponseList("contents", Connection::class).firstOrNull { - it.label == "Connection with ${invitee.name}" && it.state == ConnectionState.CONNECTION_RESPONSE_SENT - } - - var inviteeConnection: Connection? = null - if (inviterConnection != null) { - invitee.attemptsTo( - Get.resource("/connections"), - ) - invitee.should( - ResponseConsequence.seeThatResponse { - it.statusCode(SC_OK) - }, - ) - inviteeConnection = lastResponseList("contents", Connection::class).firstOrNull { - it.theirDid == inviterConnection.myDid && it.state == ConnectionState.CONNECTION_RESPONSE_RECEIVED - } - } - - if (inviterConnection != null && inviteeConnection != null) { - inviter.remember("connection-with-${invitee.name}", inviterConnection) - invitee.remember("connection-with-${inviter.name}", inviteeConnection) - } else { - val connectionSteps = ConnectionSteps() - connectionSteps.inviterGeneratesAConnectionInvitation(inviter, invitee) - connectionSteps.inviteeReceivesTheConnectionInvitation(invitee, inviter) - connectionSteps.inviteeSendsAConnectionRequestToInviter(invitee, inviter) - connectionSteps.inviterReceivesTheConnectionRequest(inviter) - connectionSteps.inviteeReceivesTheConnectionResponse(invitee) - connectionSteps.inviterAndInviteeHaveAConnection(inviter, invitee) - } - } -} diff --git a/tests/e2e-tests/src/test/kotlin/features/connection/ConnectionSteps.kt b/tests/e2e-tests/src/test/kotlin/features/connection/ConnectionSteps.kt deleted file mode 100644 index 3d1d945762..0000000000 --- a/tests/e2e-tests/src/test/kotlin/features/connection/ConnectionSteps.kt +++ /dev/null @@ -1,161 +0,0 @@ -package features.connection - -import api_models.* -import common.ListenToEvents -import common.Utils.lastResponseObject -import common.Utils.wait -import interactions.Get -import interactions.Post -import io.cucumber.java.en.Then -import io.cucumber.java.en.When -import net.serenitybdd.screenplay.Actor -import net.serenitybdd.screenplay.rest.questions.ResponseConsequence -import org.apache.http.HttpStatus.SC_CREATED -import org.apache.http.HttpStatus.SC_OK -import org.assertj.core.api.Assertions.assertThat -import org.hamcrest.CoreMatchers.* - -class ConnectionSteps { - - @When("{actor} generates a connection invitation to {actor}") - fun inviterGeneratesAConnectionInvitation(inviter: Actor, invitee: Actor) { - // Acme(Issuer) initiates a connection - // and sends it to Bob(Holder) out-of-band, e.g. using QR-code - val connectionLabel = "Connection with ${invitee.name}" - inviter.attemptsTo( - Post.to("/connections") - .with { - it.body("""{"label": "$connectionLabel"}""") - }, - ) - inviter.should( - ResponseConsequence.seeThatResponse { response -> - response.statusCode(SC_CREATED) - response.body("connectionId", notNullValue()) - response.body("createdAt", notNullValue()) - response.body("invitation", notNullValue()) - response.body("label", containsString(connectionLabel)) - response.body("state", containsString(ConnectionState.INVITATION_GENERATED)) - response.body("role", containsString("Inviter")) - }, - ) - // Acme remembers invitation URL to send it out of band to Bob - inviter.remember( - "invitationUrl", - lastResponseObject("", Connection::class) - .invitation.invitationUrl.split("=")[1], - ) - inviter.remember( - "invitation", - lastResponseObject("invitation", Invitation::class), - ) - - // Acme remembers its connection ID for further use - inviter.remember( - "connectionId", - lastResponseObject("", Connection::class) - .connectionId, - ) - inviter.remember("thid", lastResponseObject("", Connection::class).thid) - } - - @When("{actor} receives the connection invitation from {actor}") - fun inviteeReceivesTheConnectionInvitation(invitee: Actor, inviter: Actor) { - // Here out of band transfer of connection QR code is happening - // and Bob (Holder) gets an invitation URL - // they're accepting connection invitation by POST request specifying achieved invitation - // we demonstrate it by Bob remembering invitationUrl that Acme recalls - invitee.remember("invitationUrl", inviter.recall("invitationUrl")) - } - - @When("{actor} sends a connection request to {actor}") - fun inviteeSendsAConnectionRequestToInviter(invitee: Actor, inviter: Actor) { - // Bob accepts connection using achieved out-of-band invitation - invitee.attemptsTo( - Post.to("/connection-invitations") - .with { - it.body("""{"invitation": "${invitee.recall("invitationUrl")}"}""") - }, - ) - val acmeInvitation = inviter.recall("invitation") - invitee.should( - ResponseConsequence.seeThatResponse { response -> - response.statusCode(SC_OK) - response.body("connectionId", notNullValue()) - response.body("createdAt", notNullValue()) - response.body("myDid", notNullValue()) - response.body("theirDid", notNullValue()) - response.body("invitation.from", containsString(acmeInvitation.from)) - response.body("invitation.id", containsString(acmeInvitation.id)) - response.body("invitation.invitationUrl", containsString(acmeInvitation.invitationUrl)) - response.body("invitation.type", containsString(acmeInvitation.type)) - response.body("state", containsString(ConnectionState.CONNECTION_REQUEST_PENDING)) - response.body("role", containsString("Invitee")) - }, - ) - invitee.remember("connectionId", lastResponseObject("", Connection::class).connectionId) - invitee.remember("thid", lastResponseObject("", Connection::class).thid) - } - - @When("{actor} receives the connection request and sends back the response") - fun inviterReceivesTheConnectionRequest(inviter: Actor) { - wait( - { - val lastEvent = ListenToEvents.`as`(inviter).connectionEvents.lastOrNull { - it.data.thid == inviter.recall("thid") - } - lastEvent != null && - lastEvent.data.state == ConnectionState.CONNECTION_RESPONSE_SENT - }, - "Inviter connection didn't reach ${ConnectionState.CONNECTION_RESPONSE_SENT} state", - ) - } - - @When("{actor} receives the connection response") - fun inviteeReceivesTheConnectionResponse(invitee: Actor) { - // Bob (Holder) receives final connection response - wait( - { - val lastEvent = ListenToEvents.`as`(invitee).connectionEvents.lastOrNull { - it.data.thid == invitee.recall("thid") - } - lastEvent != null && - lastEvent.data.state == ConnectionState.CONNECTION_RESPONSE_RECEIVED - }, - "Invitee connection didn't reach ${ConnectionState.CONNECTION_RESPONSE_RECEIVED} state.", - ) - } - - @Then("{actor} and {actor} have a connection") - fun inviterAndInviteeHaveAConnection(inviter: Actor, invitee: Actor) { - // Connection established. Both parties exchanged their DIDs with each other - inviter.attemptsTo( - Get.resource("/connections/${inviter.recall("connectionId")}"), - ) - inviter.should( - ResponseConsequence.seeThatResponse { - it.statusCode(SC_OK) - }, - ) - inviter.remember("connection-with-${invitee.name}", lastResponseObject("", Connection::class)) - - invitee.attemptsTo( - Get.resource("/connections/${invitee.recall("connectionId")}"), - ) - invitee.should( - ResponseConsequence.seeThatResponse { - it.statusCode(SC_OK) - }, - ) - invitee.remember("connection-with-${inviter.name}", lastResponseObject("", Connection::class)) - - assertThat(inviter.recall("connection-with-${invitee.name}").myDid) - .isEqualTo(invitee.recall("connection-with-${inviter.name}").theirDid) - assertThat(inviter.recall("connection-with-${invitee.name}").theirDid) - .isEqualTo(invitee.recall("connection-with-${inviter.name}").myDid) - assertThat(inviter.recall("connection-with-${invitee.name}").state) - .isEqualTo(ConnectionState.CONNECTION_RESPONSE_SENT) - assertThat(invitee.recall("connection-with-${inviter.name}").state) - .isEqualTo(ConnectionState.CONNECTION_RESPONSE_RECEIVED) - } -} diff --git a/tests/e2e-tests/src/test/kotlin/features/credential_schemas/CredentialSchemasSteps.kt b/tests/e2e-tests/src/test/kotlin/features/credential_schemas/CredentialSchemasSteps.kt deleted file mode 100644 index 147b8d9a12..0000000000 --- a/tests/e2e-tests/src/test/kotlin/features/credential_schemas/CredentialSchemasSteps.kt +++ /dev/null @@ -1,176 +0,0 @@ -package features.credential_schemas - -import api_models.CredentialSchema -import com.fasterxml.jackson.databind.ObjectMapper -import common.TestConstants -import common.Utils.lastResponseObject -import common.Utils.toJsonPath -import io.cucumber.java.PendingException -import io.cucumber.java.en.Then -import io.cucumber.java.en.When -import io.restassured.path.json.JsonPath -import net.serenitybdd.screenplay.Actor -import interactions.Get -import interactions.Post -import net.serenitybdd.screenplay.rest.questions.ResponseConsequence -import org.apache.http.HttpStatus.* -import org.hamcrest.CoreMatchers.* -import org.hamcrest.Matchers.containsString -import org.hamcrest.Matchers.emptyString -import java.util.* - -class CredentialSchemasSteps { - - @When("{actor} creates a new credential schema") - fun acmeCreatesANewCredentialSchema(actor: Actor) { - actor.attemptsTo( - Post.to("/schema-registry/schemas").with { - it.body(TestConstants.CREDENTIAL_SCHEMAS.STUDENT_SCHEMA.copy(author = actor.recall("shortFormDid"))) - }, - ) - } - - @Then("{actor} sees new credential schema is available") - fun newCredentialSchemaIsAvailable(actor: Actor) { - actor.should(ResponseConsequence.seeThatResponse("New schema created") { - it.statusCode(SC_CREATED) - it.body("guid", not(emptyString())) - it.body("id", not(emptyString())) - it.body("longId", not(emptyString())) - it.body("authored", not(emptyString())) - it.body("kind", containsString("CredentialSchema")) - it.body("name", containsString(TestConstants.CREDENTIAL_SCHEMAS.STUDENT_SCHEMA.name)) - it.body("description", containsString(TestConstants.CREDENTIAL_SCHEMAS.STUDENT_SCHEMA.description)) - it.body("version", containsString(TestConstants.CREDENTIAL_SCHEMAS.STUDENT_SCHEMA.version)) - it.body("type", equalTo(TestConstants.CREDENTIAL_SCHEMAS.CREDENTIAL_SCHEMA_TYPE)) - TestConstants.CREDENTIAL_SCHEMAS.STUDENT_SCHEMA.tags!!.forEach { tag -> - it.body("tags", hasItem(tag)) - } - it.body( - "schema.\$id", - equalTo(TestConstants.CREDENTIAL_SCHEMAS.STUDENT_SCHEMA.schema!!.get("\$id").asText()) - ) - - it.body( - "schema", equalTo>( - JsonPath( - ObjectMapper().writeValueAsString( - TestConstants.CREDENTIAL_SCHEMAS.STUDENT_SCHEMA.schema - ) - ).getMap("") - ) - ) - }) - } - - @When("{actor} creates {int} new schemas") - fun acmeCreatesMultipleSchemas(actor: Actor, numberOfSchemas: Int) { - val createdSchemas: MutableList = mutableListOf() - repeat(numberOfSchemas) { i: Int -> - actor.attemptsTo( - Post.to("/schema-registry/schemas").with { - it.body( - TestConstants.CREDENTIAL_SCHEMAS.generate_with_name_suffix_and_author( - i.toString(), - actor.recall("shortFormDid") - ) - ) - }, - ) - actor.should( - ResponseConsequence.seeThatResponse("New schema created") { - it.statusCode(SC_CREATED) - }, - ) - createdSchemas.add(lastResponseObject("", CredentialSchema::class)) - } - actor.remember("createdSchemas", createdSchemas) - } - - @Then("{actor} can access all of them one by one") - fun theyCanBeAccessedWithPagination(actor: Actor) { - actor.recall>("createdSchemas").forEach { schema -> - actor.attemptsTo( - Get.resource("/schema-registry/schemas/${schema.guid}"), - ) - actor.should( - ResponseConsequence.seeThatResponse("Schema achieved") { - it.statusCode(SC_OK) - }, - ) - } - } - - @When("{actor} creates a new schema with some id") - fun acmeCreatesANewSchemaWithFixedId(actor: Actor) { - val wrongSchema = TestConstants.CREDENTIAL_SCHEMAS.STUDENT_SCHEMA - wrongSchema.guid = TestConstants.RANDOM_CONSTAND_UUID - actor.attemptsTo( - Post.to("/schema-registry/schemas").with { - it.body(wrongSchema) - }, - ) - actor.should( - ResponseConsequence.seeThatResponse("New schema created") { - it.statusCode(SC_CREATED) - }, - ) - } - - @When("{actor} tries to create a new schema with identical id") - fun acmeTriesToCreateANewSchemaWithSameId(actor: Actor) { - val wrongSchema = TestConstants.CREDENTIAL_SCHEMAS.STUDENT_SCHEMA - wrongSchema.guid = TestConstants.RANDOM_CONSTAND_UUID - actor.attemptsTo( - Post.to("/schema-registry/schemas").with { - it.body(wrongSchema) - }, - ) - } - - @Then("{actor} sees the request failure with identical id error") - fun idDuplicateErrorIsThrown(actor: Actor) { - try { - actor.should( - ResponseConsequence.seeThatResponse("New schema creation error: same UUID") { - it.statusCode(SC_BAD_REQUEST) - }, - ) - } catch (err: AssertionError) { - println(err.message) - throw PendingException("BUG: New credential schema CAN be created with same UUID.") - } - } - - @When("{actor} tries to create a new schema with {word} in field {word}") - fun acmeTriesToCreateANewSchemaWithField(actor: Actor, value: String, field: String) { - actor.attemptsTo( - Post.to("/schema-registry/schemas").with { - it.body( - toJsonPath(TestConstants.CREDENTIAL_SCHEMAS.STUDENT_SCHEMA).set(field, value).jsonString(), - ) - }, - ) - } - - @When("{actor} tries to get schemas with {int} in parameter {word}") - fun acmeTriesToCreateANewSchemaWithParameter(actor: Actor, value: Int, parameter: String) { - actor.attemptsTo( - Get.resource("/schema-registry/schemas?$parameter=$value"), - ) - } - - @Then("{actor} sees the request with status {int}") - fun heSeesTheRequestFailureWithErrorStatus(actor: Actor, errorStatusCode: Int) { - try { - actor.should( - ResponseConsequence.seeThatResponse { - it.statusCode(errorStatusCode) - }, - ) - } catch (err: AssertionError) { - println(err.message) - throw PendingException("BUG: credential schemas CAN be accessed with negative limit and offset.") - } - } -} diff --git a/tests/e2e-tests/src/test/kotlin/features/did/ManageDidSteps.kt b/tests/e2e-tests/src/test/kotlin/features/did/ManageDidSteps.kt deleted file mode 100644 index 84fd532c8c..0000000000 --- a/tests/e2e-tests/src/test/kotlin/features/did/ManageDidSteps.kt +++ /dev/null @@ -1,114 +0,0 @@ -package features.did - -import api_models.* -import common.Ensure -import common.TestConstants -import common.Utils.lastResponseList -import common.Utils.lastResponseObject -import common.Utils.toJsonPath -import io.cucumber.java.en.Given -import io.cucumber.java.en.Then -import io.cucumber.java.en.When -import net.serenitybdd.rest.SerenityRest.lastResponse -import net.serenitybdd.screenplay.Actor -import interactions.Get -import interactions.Post -import net.serenitybdd.screenplay.rest.questions.ResponseConsequence -import org.apache.http.HttpStatus.SC_CREATED -import org.assertj.core.api.Assertions -import org.hamcrest.Matchers.* - -class ManageDidSteps { - - @Given("{actor} creates {int} PRISM DIDs") - fun createsMultipleManagedDids(actor: Actor, number: Int) { - repeat(number) { - createManageDid(actor) - } - actor.remember("number", number) - } - - @When("{actor} creates PRISM DID") - fun createManageDid(actor: Actor) { - val createDidRequest = createPrismDidRequest() - - actor.attemptsTo( - Post.to("/did-registrar/dids") - .with { - it.body(createDidRequest) - }, - ) - var createdDids = actor.recall>("createdDids") - if (createdDids == null) { - createdDids = mutableListOf() - } - createdDids.add(lastResponseObject("longFormDid", String::class)) - actor.remember("createdDids", createdDids) - } - - @When("{actor} tries to create PRISM DID with missing {word}") - fun triesToCreateManagedDidWithMissingField(actor: Actor, missingFieldPath: String) { - val createDidRequest = createPrismDidRequest() - val requestBody = toJsonPath(createDidRequest).delete(missingFieldPath).jsonString() - actor.attemptsTo( - Post.to("/did-registrar/dids") - .with { - it.body(requestBody) - }, - ) - } - - @When("{actor} tries to create a managed DID with value {word} in {word}") - fun trisToCreateManagedDidWithValueInField(actor: Actor, value: String, fieldPath: String) { - val createDidRequest = createPrismDidRequest() - val requestBody = toJsonPath(createDidRequest).set(fieldPath, value).jsonString() - actor.attemptsTo( - Post.to("/did-registrar/dids") - .with { - it.body(requestBody) - }, - ) - } - - @When("{actor} lists all PRISM DIDs") - fun iListManagedDids(actor: Actor) { - actor.attemptsTo( - Get.resource("/did-registrar/dids"), - ) - } - - @Then("{actor} sees PRISM DID was created successfully") - fun theDidShouldBeRegisteredSuccessfully(actor: Actor) { - actor.should( - ResponseConsequence.seeThatResponse { - it.statusCode(SC_CREATED) - it.body("longFormDid", not(emptyString())) - }, - ) - } - - @Then("{actor} sees the request has failed with error status {int}") - fun seesTheRequestHasFailedWithErrorStatus(actor: Actor, errorStatusCode: Int) { - Assertions.assertThat(lastResponse().statusCode).isEqualTo(errorStatusCode) - } - - @Then("{actor} sees the list contains all created DIDs") - fun seeTheListContainsAllCreatedDids(actor: Actor) { - val expectedDids = actor.recall>("createdDids") - val managedDidList = lastResponseList("contents.longFormDid", String::class) - actor.attemptsTo( - Ensure.that(managedDidList).containsElementsFrom(expectedDids) - ) - } - - private fun createPrismDidRequest(): CreatePrismDidRequest { - val publicKeys = listOf( - TestConstants.PRISM_DID_AUTH_KEY, - ) - val services = listOf( - TestConstants.PRISM_DID_SERVICE, - ) - val documentTemplate = DocumentTemplate(publicKeys, services) - return CreatePrismDidRequest(documentTemplate) - } -} diff --git a/tests/e2e-tests/src/test/kotlin/features/did/PublishDidSteps.kt b/tests/e2e-tests/src/test/kotlin/features/did/PublishDidSteps.kt deleted file mode 100644 index f95cf7f6da..0000000000 --- a/tests/e2e-tests/src/test/kotlin/features/did/PublishDidSteps.kt +++ /dev/null @@ -1,153 +0,0 @@ -package features.did - -import api_models.* -import common.ListenToEvents -import common.TestConstants -import common.Utils.lastResponseList -import common.Utils.lastResponseObject -import common.Utils.wait -import io.cucumber.java.en.Given -import io.cucumber.java.en.Then -import io.cucumber.java.en.When -import net.serenitybdd.screenplay.Actor -import interactions.Get -import interactions.Post -import net.serenitybdd.rest.SerenityRest -import net.serenitybdd.screenplay.rest.questions.ResponseConsequence -import org.apache.http.HttpStatus.* -import org.assertj.core.api.Assertions.assertThat -import org.hamcrest.Matchers.* - -class PublishDidSteps { - - @Given("{actor} have published PRISM DID") - fun actorHavePublishedPrismDid(actor: Actor) { - actor.attemptsTo( - Get.resource("/did-registrar/dids"), - ) - actor.should( - ResponseConsequence.seeThatResponse { - it.statusCode(SC_OK) - }, - ) - val publishedDids = lastResponseList("contents", ManagedDid::class).filter { - it.status == ManagedDidStatuses.PUBLISHED - } - val did = publishedDids.firstOrNull { - actor.attemptsTo( - Get.resource("/dids/${it.did}"), - ) - lastResponseObject("didDocumentMetadata.deactivated", String::class) == "false" - } - if (did == null) { - createsUnpublishedDid(actor) - hePublishesDidToLedger(actor) - } else { - actor.remember("shortFormDid", did.did) - } - } - - @Given("{actor} creates unpublished DID") - fun createsUnpublishedDid(actor: Actor) { - val publicKeys = listOf( - TestConstants.PRISM_DID_AUTH_KEY, - TestConstants.PRISM_DID_ASSERTION_KEY, - ) - val services = listOf( - TestConstants.PRISM_DID_SERVICE, - TestConstants.PRISM_DID_SERVICE_FOR_UPDATE, - TestConstants.PRISM_DID_SERVICE_TO_REMOVE, - ) - val documentTemplate = DocumentTemplate(publicKeys, services) - actor.attemptsTo( - Post.to("/did-registrar/dids") - .with { - it.body(CreatePrismDidRequest(documentTemplate)) - }, - ) - actor.should( - ResponseConsequence.seeThatResponse { - it.statusCode(SC_CREATED) - it.body("longFormDid", not(emptyString())) - }, - ) - val longFormDid = lastResponseObject("longFormDid", String::class) - actor.remember("longFormDid", longFormDid) - - actor.attemptsTo( - Get.resource("/did-registrar/dids/$longFormDid"), - ) - actor.should( - ResponseConsequence.seeThatResponse { - it.statusCode(SC_OK) - }, - ) - actor.remember( - "shortFormDid", - lastResponseObject("", ManagedDid::class).did, - ) - } - - @When("{actor} publishes DID to ledger") - fun hePublishesDidToLedger(actor: Actor) { - actor.attemptsTo( - Post.to("/did-registrar/dids/${actor.recall("shortFormDid")}/publications"), - ) - actor.should( - ResponseConsequence.seeThatResponse { - it.statusCode(SC_ACCEPTED) - it.body("scheduledOperation.didRef", not(emptyString())) - it.body("scheduledOperation.id", not(emptyString())) - }, - ) - wait( - { - val didEvent = - ListenToEvents.`as`(actor).didEvents.lastOrNull { - it.data.did == actor.recall("shortFormDid") - } - didEvent != null && didEvent.data.status == ManagedDidStatuses.PUBLISHED - }, - "ERROR: DID was not published to ledger!", - timeout = TestConstants.DID_UPDATE_PUBLISH_MAX_WAIT_5_MIN, - ) - actor.attemptsTo( - Get.resource("/dids/${actor.recall("shortFormDid")}"), - ) - actor.should( - ResponseConsequence.seeThatResponse { - it.statusCode(SC_OK) - it.body("didDocument.id", equalTo(actor.recall("shortFormDid"))) - }, - ) - } - - @Then("{actor} resolves DID document corresponds to W3C standard") - fun heSeesDidDocumentCorrespondsToW3cStandard(actor: Actor) { - val didDocument = lastResponseObject("", DidResolutionResult::class).didDocument!! - assertThat(didDocument) - .hasFieldOrProperty("assertionMethod") - .hasFieldOrProperty("authentication") - .hasFieldOrProperty("capabilityInvocation") - .hasFieldOrProperty("controller") - .hasFieldOrProperty("id") - .hasFieldOrProperty("keyAgreement") - .hasFieldOrProperty("service") - .hasFieldOrProperty("verificationMethod") - - val shortFormDid = actor.recall("shortFormDid") - - assertThat(didDocument.id == shortFormDid) - - assertThat(didDocument.authentication!![0]) - .isEqualTo("$shortFormDid#${TestConstants.PRISM_DID_AUTH_KEY.id}") - - assertThat(didDocument.verificationMethod!![0]) - .hasFieldOrPropertyWithValue("controller", shortFormDid) - .hasFieldOrProperty("publicKeyJwk") - - assertThat(lastResponseObject("", DidResolutionResult::class).didDocumentMetadata!!) - .hasFieldOrPropertyWithValue("deactivated", false) - .hasFieldOrProperty("canonicalId") - } -} diff --git a/tests/e2e-tests/src/test/kotlin/features/multitenancy/EventsSteps.kt b/tests/e2e-tests/src/test/kotlin/features/multitenancy/EventsSteps.kt deleted file mode 100644 index 81acb9a978..0000000000 --- a/tests/e2e-tests/src/test/kotlin/features/multitenancy/EventsSteps.kt +++ /dev/null @@ -1,19 +0,0 @@ -package features.multitenancy - -import api_models.RegisterWebhookRequest -import interactions.Post -import net.serenitybdd.rest.SerenityRest -import net.serenitybdd.screenplay.Actor - -class EventsSteps { - fun registerNewWebhook(actor: Actor, webhookUrl: String) { - actor.attemptsTo( - Post.to("/events/webhooks") - .with { - it.body( - RegisterWebhookRequest(url = webhookUrl) - ) - }, - ) - } -} diff --git a/tests/e2e-tests/src/test/kotlin/features/present_proof/PresentProofSteps.kt b/tests/e2e-tests/src/test/kotlin/features/present_proof/PresentProofSteps.kt deleted file mode 100644 index 3641b9de23..0000000000 --- a/tests/e2e-tests/src/test/kotlin/features/present_proof/PresentProofSteps.kt +++ /dev/null @@ -1,133 +0,0 @@ -package features.present_proof - -import api_models.* -import common.ListenToEvents -import common.Utils.lastResponseObject -import common.Utils.wait -import interactions.Get -import io.cucumber.java.en.Then -import io.cucumber.java.en.When -import net.serenitybdd.screenplay.Actor -import interactions.Post -import interactions.Patch -import net.serenitybdd.screenplay.rest.questions.ResponseConsequence -import org.apache.http.HttpStatus.SC_CREATED -import org.apache.http.HttpStatus.SC_OK - -class PresentProofSteps { - - var proofEvent: PresentationEvent? = null - - @When("{actor} sends a request for proof presentation to {actor}") - fun faberSendsARequestForProofPresentationToBob(faber: Actor, bob: Actor) { - faber.attemptsTo( - Post.to("/present-proof/presentations") - .with { - it.body( - """ - { - "description":"Request presentation of credential", - "connectionId": "${faber.recall("connection-with-${bob.name}").connectionId}", - "options":{ - "challenge": "11c91493-01b3-4c4d-ac36-b336bab5bddf", - "domain": "https://example-verifier.com" - }, - "proofs":[ - { - "schemaId": "https://schema.org/Person", - "trustIssuers": [ - "did:web:atalaprism.io/users/testUser" - ] - } - ] - } - """.trimIndent(), - ) - }, - ) - faber.should( - ResponseConsequence.seeThatResponse { - it.statusCode(SC_CREATED) - }, - ) - - val presentationId = lastResponseObject("", PresentationProof::class).presentationId - faber.remember("presentationId", presentationId) - faber.attemptsTo( - Get.resource("/present-proof/presentations/${presentationId}"), - ) - faber.should( - ResponseConsequence.seeThatResponse("Get presentations") { - it.statusCode(SC_OK) - }, - ) - faber.remember("thid", lastResponseObject("", PresentationProof::class).thid) - bob.remember("thid", lastResponseObject("", PresentationProof::class).thid) - } - - @When("{actor} receives the request") - fun bobReceivesTheRequest(bob: Actor) { - wait( - { - proofEvent = ListenToEvents.`as`(bob).presentationEvents.lastOrNull { - it.data.thid == bob.recall("thid") - } - proofEvent != null && - proofEvent!!.data.status == PresentationProofStatus.REQUEST_RECEIVED - }, - "ERROR: Bob did not achieve any presentation request!", - ) - bob.remember("presentationId", proofEvent!!.data.presentationId) - } - - @When("{actor} makes the presentation of the proof to {actor}") - fun bobMakesThePresentationOfTheProof(bob: Actor, faber: Actor) { - bob.attemptsTo( - Patch.to("/present-proof/presentations/${bob.recall("presentationId")}").with { - it.body( - """ - { "action": "request-accept", "proofId": ["${bob.recall("issuedCredential").recordId}"] } - """.trimIndent(), - ) - }, - ) - } - - @When("{actor} rejects the proof") - fun bobRejectsProof(bob: Actor) { - bob.attemptsTo( - Patch.to("/present-proof/presentations/${bob.recall("presentationId")}").with { - it.body("""{ "action": "request-reject" }""") - }, - ) - } - - @Then("{actor} sees the proof is rejected") - fun bobSeesProofIsRejected(bob: Actor) { - wait( - { - proofEvent = ListenToEvents.`as`(bob).presentationEvents.lastOrNull { - it.data.thid == bob.recall("thid") - } - proofEvent != null && - proofEvent!!.data.status == PresentationProofStatus.REQUEST_REJECTED - }, - "ERROR: Faber did not receive presentation from Bob!", - ) - } - - @Then("{actor} has the proof verified") - fun faberHasTheProofVerified(faber: Actor) { - wait( - { - proofEvent = ListenToEvents.`as`(faber).presentationEvents.lastOrNull { - it.data.thid == faber.recall("thid") - } - - proofEvent != null && - proofEvent!!.data.status == PresentationProofStatus.PRESENTATION_VERIFIED - }, - "ERROR: presentation did not achieve PresentationVerified state!", - ) - } -} diff --git a/tests/e2e-tests/src/test/kotlin/features/system/SystemSteps.kt b/tests/e2e-tests/src/test/kotlin/features/system/SystemSteps.kt deleted file mode 100644 index 9835c7246b..0000000000 --- a/tests/e2e-tests/src/test/kotlin/features/system/SystemSteps.kt +++ /dev/null @@ -1,36 +0,0 @@ -package features.system - -import api_models.HealthInfo -import common.Utils.lastResponseObject -import io.cucumber.java.en.Then -import io.cucumber.java.en.When -import net.serenitybdd.screenplay.Actor -import interactions.Get -import net.serenitybdd.screenplay.rest.questions.ResponseConsequence -import org.apache.http.HttpStatus.SC_OK -import org.assertj.core.api.Assertions.assertThat - -class SystemSteps { - @When("{actor} makes a request to the health endpoint") - fun actorRequestsHealthEndpoint(actor: Actor) { - actor.attemptsTo( - Get.resource("/_system/health"), - ) - actor.should( - ResponseConsequence.seeThatResponse { - it.statusCode(SC_OK) - }, - ) - val healthResponse = lastResponseObject("", HealthInfo::class) - assertThat(healthResponse) - .hasFieldOrProperty("version") - .hasNoNullFieldsOrProperties() - actor.remember("version", healthResponse.version) - } - - @Then("{actor} knows what version of the service is running") - fun actorUnderstandsVersion(actor: Actor) { - assertThat(actor.recall("version")) - .isNotBlank() - } -} diff --git a/tests/e2e-tests/src/test/kotlin/features/verification_policies/VerificationPoliciesSteps.kt b/tests/e2e-tests/src/test/kotlin/features/verification_policies/VerificationPoliciesSteps.kt deleted file mode 100644 index 6f5d95280a..0000000000 --- a/tests/e2e-tests/src/test/kotlin/features/verification_policies/VerificationPoliciesSteps.kt +++ /dev/null @@ -1,96 +0,0 @@ -package features.verification_policies - -import api_models.VerificationPolicy -import api_models.VerificationPolicyInput -import common.TestConstants -import io.cucumber.java.en.Then -import io.cucumber.java.en.When -import net.serenitybdd.rest.SerenityRest -import net.serenitybdd.screenplay.Actor -import interactions.Put -import interactions.Post -import net.serenitybdd.screenplay.rest.questions.ResponseConsequence -import org.apache.http.HttpStatus -import org.hamcrest.CoreMatchers -import org.hamcrest.Matchers -import java.util.* - - -class VerificationPoliciesSteps { - - @When("{actor} creates a new verification policy") - fun acmeCreatesANewVerificationPolicy(actor: Actor) { - actor.attemptsTo( - Post.to("/verification/policies").with { - it.body(TestConstants.VERIFICATION_POLICIES.VERIFICATION_POLICY) - }, - ) - } - - @Then("{actor} sees new verification policy is available") - fun newVerificationPolicyIsAvailable(actor: Actor) { - actor.should(ResponseConsequence.seeThatResponse("New policy created") { - it.statusCode(HttpStatus.SC_CREATED) - //it.body("", CoreMatchers.`is`(Matchers.emptyString())) - it.body("id", CoreMatchers.not(Matchers.emptyString())) - it.body("nonce", CoreMatchers.not(Matchers.emptyString())) - it.body("kind", Matchers.containsString("VerificationPolicy")) - it.body( - "name", - Matchers.containsString(TestConstants.VERIFICATION_POLICIES.VERIFICATION_POLICY.name) - ) - it.body( - "description", - Matchers.containsString(TestConstants.VERIFICATION_POLICIES.VERIFICATION_POLICY.description) - ) - TestConstants.VERIFICATION_POLICIES.VERIFICATION_POLICY.constraints!!.forEach { constraint -> - it.body("constraints.schemaId", CoreMatchers.hasItem(constraint.schemaId)) - it.body("constraints.trustedIssuers", CoreMatchers.hasItems(constraint.trustedIssuers!!)) - } - }) - val policy = SerenityRest.lastResponse().`as`(VerificationPolicy::class.java) - actor.remember("policy", policy) - } - - @When("{actor} updates a new verification policy") - fun acmeUpdatesAVerificationPolicy(actor: Actor) { - val policy = actor.recall("policy") - val updatePolicyInput = VerificationPolicyInput( - name = policy.name, - description = "updated description + ${UUID.randomUUID()}", - constraints = policy.constraints - ) - actor.attemptsTo( - Put.to("/verification/policies/${policy.id}?nonce=${policy.nonce}").with { - it.body(updatePolicyInput) - }, - ) - actor.remember("updatedPolicyInput", updatePolicyInput) - } - - @Then("{actor} sees the updated verification policy is available") - fun updatedVerificationPolicyIsAvailable(actor: Actor) { - val updatedPolicy = actor.forget("policy") - val updatePolicyInput = actor.forget("updatedPolicyInput") - actor.should(ResponseConsequence.seeThatResponse("Verification policy is updated") { - it.statusCode(HttpStatus.SC_OK) - it.body("id", CoreMatchers.`is`(Matchers.equalTo(updatedPolicy.id))) - it.body("nonce", CoreMatchers.not(Matchers.emptyString())) - it.body("kind", Matchers.containsString("VerificationPolicy")) - it.body( - "name", - Matchers.containsString(updatePolicyInput.name) - ) - it.body( - "description", - Matchers.containsString(updatePolicyInput.description) - ) - updatePolicyInput.constraints!!.forEach { constraint -> - it.body("constraints.schemaId", CoreMatchers.hasItem(constraint.schemaId)) - it.body("constraints.trustedIssuers", CoreMatchers.hasItems(constraint.trustedIssuers!!)) - } - }) - val policy = SerenityRest.lastResponse().`as`(VerificationPolicy::class.java) - actor.remember("policy", policy) - } -} diff --git a/tests/e2e-tests/src/test/resources/cucumber.properties b/tests/e2e-tests/src/test/resources/cucumber.properties deleted file mode 100644 index b48dd63bf1..0000000000 --- a/tests/e2e-tests/src/test/resources/cucumber.properties +++ /dev/null @@ -1 +0,0 @@ -cucumber.publish.quiet=true diff --git a/tests/e2e-tests/src/test/resources/features/did_registrar/create_did.feature b/tests/e2e-tests/src/test/resources/features/did_registrar/create_did.feature deleted file mode 100644 index 90d8b3b23a..0000000000 --- a/tests/e2e-tests/src/test/resources/features/did_registrar/create_did.feature +++ /dev/null @@ -1,39 +0,0 @@ -Feature: Create and publish DID - -@TEST_ATL-3838 -Scenario: Create PRISM DID - When Acme creates PRISM DID - Then He sees PRISM DID was created successfully - -@TEST_ATL-3839 -Scenario Outline: PRISM DID creation fails when required request fields are missing - Given Acme tries to create PRISM DID with missing - Then He sees the request has failed with error status -Examples: - | field | error | - | documentTemplate | 400 | - | documentTemplate.publicKeys | 400 | - | documentTemplate.publicKeys[0].id | 400 | - | documentTemplate.publicKeys[0].purpose | 400 | - | documentTemplate.services | 400 | - | documentTemplate.services[0].id | 400 | - | documentTemplate.services[0].type | 400 | - | documentTemplate.services[0].serviceEndpoint | 400 | - -@TEST_ATL-3840 -Scenario Outline: PRISM DID creation fails with wrong formatted fields - Given Acme tries to create a managed DID with value in - Then He sees the request has failed with error status -Examples: - | field | value | error | - | documentTemplate.publicKeys[0].id | # | 422 | - | documentTemplate.publicKeys[0].purpose | potato | 400 | - | documentTemplate.services[0].id | # | 422 | - | documentTemplate.services[0].type | pot@to | 422 | - | documentTemplate.services[0].serviceEndpoint[0] | potato | 422 | - -@TEST_ATL-3842 -Scenario: Successfully publish DID to ledger - When Acme creates unpublished DID - And He publishes DID to ledger - Then He resolves DID document corresponds to W3C standard diff --git a/tests/e2e-tests/.gitignore b/tests/integration-tests/.gitignore similarity index 100% rename from tests/e2e-tests/.gitignore rename to tests/integration-tests/.gitignore diff --git a/tests/integration-tests/build.gradle.kts b/tests/integration-tests/build.gradle.kts new file mode 100644 index 0000000000..ea7b9a924f --- /dev/null +++ b/tests/integration-tests/build.gradle.kts @@ -0,0 +1,74 @@ +plugins { + 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" +} + +repositories { + 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 { + username = System.getenv("ATALA_GITHUB_ACTOR") + password = System.getenv("ATALA_GITHUB_TOKEN") + } + } +} + +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") + // RestAPI client + implementation("io.iohk.atala.prism:prism-kotlin-client:1.18.0") + // Test helpers library + testImplementation("io.iohk.atala:atala-automation:0.3.0") + // Hoplite for configuration + implementation("com.sksamuel.hoplite:hoplite-core:2.7.5") + implementation("com.sksamuel.hoplite:hoplite-hocon:2.7.5") +} + +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.test { + testLogging.showStandardStreams = true + systemProperty("cucumber.filter.tags", System.getProperty("cucumber.filter.tags")) +} + +kotlin { + jvmToolchain(19) +} + +ktlint { + disabledRules.set(setOf("no-wildcard-imports")) +} diff --git a/tests/e2e-tests/gradle.properties b/tests/integration-tests/gradle.properties similarity index 100% rename from tests/e2e-tests/gradle.properties rename to tests/integration-tests/gradle.properties diff --git a/tests/e2e-tests/gradle/wrapper/gradle-wrapper.jar b/tests/integration-tests/gradle/wrapper/gradle-wrapper.jar similarity index 100% rename from tests/e2e-tests/gradle/wrapper/gradle-wrapper.jar rename to tests/integration-tests/gradle/wrapper/gradle-wrapper.jar diff --git a/tests/e2e-tests/gradle/wrapper/gradle-wrapper.properties b/tests/integration-tests/gradle/wrapper/gradle-wrapper.properties similarity index 100% rename from tests/e2e-tests/gradle/wrapper/gradle-wrapper.properties rename to tests/integration-tests/gradle/wrapper/gradle-wrapper.properties diff --git a/tests/e2e-tests/gradlew b/tests/integration-tests/gradlew similarity index 100% rename from tests/e2e-tests/gradlew rename to tests/integration-tests/gradlew diff --git a/tests/e2e-tests/gradlew.bat b/tests/integration-tests/gradlew.bat similarity index 100% rename from tests/e2e-tests/gradlew.bat rename to tests/integration-tests/gradlew.bat diff --git a/tests/e2e-tests/serenity.properties b/tests/integration-tests/serenity.properties similarity index 77% rename from tests/e2e-tests/serenity.properties rename to tests/integration-tests/serenity.properties index a4b2f36eb0..52ed622b2f 100644 --- a/tests/e2e-tests/serenity.properties +++ b/tests/integration-tests/serenity.properties @@ -1,4 +1,4 @@ -serenity.project.name=PRISM agent e2e tests +serenity.project.name=Open Enterprise Agent Integration tests serenity.reports.show.step.details=true serenity.console.colors=true simplified.stack.traces=false diff --git a/tests/integration-tests/settings.gradle.kts b/tests/integration-tests/settings.gradle.kts new file mode 100644 index 0000000000..e57032400b --- /dev/null +++ b/tests/integration-tests/settings.gradle.kts @@ -0,0 +1 @@ +rootProject.name = "integration-tests" diff --git a/tests/integration-tests/src/main/kotlin/models/Events.kt b/tests/integration-tests/src/main/kotlin/models/Events.kt new file mode 100644 index 0000000000..0295c3a713 --- /dev/null +++ b/tests/integration-tests/src/main/kotlin/models/Events.kt @@ -0,0 +1,48 @@ +package models + +import com.google.gson.JsonElement +import com.google.gson.annotations.SerializedName +import io.iohk.atala.prism.models.Connection +import io.iohk.atala.prism.models.IssueCredentialRecord +import io.iohk.atala.prism.models.ManagedDID +import io.iohk.atala.prism.models.PresentationStatus + +data class Event( + @SerializedName("type") var type: String, + @SerializedName("id") var id: String, + @SerializedName("ts") var ts: String, + @SerializedName("data") var data: JsonElement, + @SerializedName("walletId") var walletId: String +) + +data class ConnectionEvent( + @SerializedName("type") var type: String, + @SerializedName("id") var id: String, + @SerializedName("ts") var ts: String, + @SerializedName("data") var data: Connection, + @SerializedName("walletId") var walletId: String +) + +data class CredentialEvent( + @SerializedName("type") var type: String, + @SerializedName("id") var id: String, + @SerializedName("ts") var ts: String, + @SerializedName("data") var data: IssueCredentialRecord, + @SerializedName("walletId") var walletId: String +) + +data class PresentationEvent( + @SerializedName("type") var type: String, + @SerializedName("id") var id: String, + @SerializedName("ts") var ts: String, + @SerializedName("data") var data: PresentationStatus, + @SerializedName("walletId") var walletId: String +) + +data class DidEvent( + @SerializedName("type") var type: String, + @SerializedName("id") var id: String, + @SerializedName("ts") var ts: String, + @SerializedName("data") var data: ManagedDID, + @SerializedName("walletId") var walletId: String +) diff --git a/tests/integration-tests/src/main/kotlin/models/Schema.kt b/tests/integration-tests/src/main/kotlin/models/Schema.kt new file mode 100644 index 0000000000..59db1c85a7 --- /dev/null +++ b/tests/integration-tests/src/main/kotlin/models/Schema.kt @@ -0,0 +1,20 @@ +package models + +import com.google.gson.annotations.SerializedName + +data class Schema( + @SerializedName("\$id") + var id: String = "", + + @SerializedName("\$schema") + var schema: String = "", + + @SerializedName("\$description") + var description: String = "", + + @SerializedName("type") + var type: String = "", + + @SerializedName("properties") + val properties: MutableMap = mutableMapOf() +) diff --git a/tests/integration-tests/src/main/kotlin/models/SchemaProperty.kt b/tests/integration-tests/src/main/kotlin/models/SchemaProperty.kt new file mode 100644 index 0000000000..676d47151b --- /dev/null +++ b/tests/integration-tests/src/main/kotlin/models/SchemaProperty.kt @@ -0,0 +1,8 @@ +package models + +import com.google.gson.annotations.SerializedName + +data class SchemaProperty( + @SerializedName("type") + var type: String = "" +) diff --git a/tests/e2e-tests/src/test/kotlin/common/ListenToEvents.kt b/tests/integration-tests/src/test/kotlin/common/ListenToEvents.kt similarity index 61% rename from tests/e2e-tests/src/test/kotlin/common/ListenToEvents.kt rename to tests/integration-tests/src/test/kotlin/common/ListenToEvents.kt index b221e3ce12..3efae51c49 100644 --- a/tests/e2e-tests/src/test/kotlin/common/ListenToEvents.kt +++ b/tests/integration-tests/src/test/kotlin/common/ListenToEvents.kt @@ -1,6 +1,7 @@ package common -import api_models.* +import com.google.gson.GsonBuilder +import io.iohk.atala.automation.restassured.CustomGsonObjectMapperFactory import io.ktor.http.* import io.ktor.server.application.* import io.ktor.server.engine.* @@ -8,19 +9,21 @@ import io.ktor.server.netty.* import io.ktor.server.request.* import io.ktor.server.response.* import io.ktor.server.routing.* -import kotlinx.serialization.decodeFromString -import kotlinx.serialization.json.Json +import models.* import net.serenitybdd.screenplay.Ability import net.serenitybdd.screenplay.Actor import net.serenitybdd.screenplay.HasTeardown -import java.lang.IllegalArgumentException +import java.net.URL +import java.time.OffsetDateTime open class ListenToEvents( - private val host: String, - private val port: Int, -): Ability, HasTeardown { + private val url: URL +) : Ability, HasTeardown { private val server: ApplicationEngine + private val gson = GsonBuilder() + .registerTypeAdapter(OffsetDateTime::class.java, CustomGsonObjectMapperFactory.OffsetDateTimeDeserializer()) + .create() var connectionEvents: MutableList = mutableListOf() var credentialEvents: MutableList = mutableListOf() @@ -31,13 +34,13 @@ open class ListenToEvents( application.routing { post("/") { val eventString = call.receiveText() - val event = Json.decodeFromString(eventString) + val event = gson.fromJson(eventString, Event::class.java) when (event.type) { - TestConstants.EVENT_TYPE_CONNECTION_UPDATED -> connectionEvents.add(Json.decodeFromString(eventString)) - TestConstants.EVENT_TYPE_ISSUE_CREDENTIAL_RECORD_UPDATED -> credentialEvents.add(Json.decodeFromString(eventString)) - TestConstants.EVENT_TYPE_PRESENTATION_UPDATED -> presentationEvents.add(Json.decodeFromString(eventString)) + TestConstants.EVENT_TYPE_CONNECTION_UPDATED -> connectionEvents.add(gson.fromJson(eventString, ConnectionEvent::class.java)) + TestConstants.EVENT_TYPE_ISSUE_CREDENTIAL_RECORD_UPDATED -> credentialEvents.add(gson.fromJson(eventString, CredentialEvent::class.java)) + TestConstants.EVENT_TYPE_PRESENTATION_UPDATED -> presentationEvents.add(gson.fromJson(eventString, PresentationEvent::class.java)) TestConstants.EVENT_TYPE_DID_STATUS_UPDATED -> { - didEvents.add(Json.decodeFromString(eventString)) + didEvents.add(gson.fromJson(eventString, DidEvent::class.java)) } else -> { throw IllegalArgumentException("ERROR: unknown event type ${event.type}") @@ -49,8 +52,8 @@ open class ListenToEvents( } companion object { - fun at(host: String, port: Int): ListenToEvents { - return ListenToEvents(host, port) + fun at(url: URL): ListenToEvents { + return ListenToEvents(url) } fun `as`(actor: Actor): ListenToEvents { @@ -61,14 +64,15 @@ open class ListenToEvents( init { server = embeddedServer( Netty, - port = port, - host = if (host == "host.docker.internal") "0.0.0.0" else host, - module = {route(this)}) + port = url.port, + host = if (url.host == "host.docker.internal") "0.0.0.0" else url.host, + module = { route(this) } + ) .start(wait = false) } override fun toString(): String { - return "Listen HTTP port at ${host}:${port}" + return "Listen HTTP port at $url" } override fun tearDown() { diff --git a/tests/integration-tests/src/test/kotlin/common/TestConstants.kt b/tests/integration-tests/src/test/kotlin/common/TestConstants.kt new file mode 100644 index 0000000000..f3b10b8432 --- /dev/null +++ b/tests/integration-tests/src/test/kotlin/common/TestConstants.kt @@ -0,0 +1,90 @@ +package common + +import io.iohk.atala.prism.models.* +import models.Schema +import models.SchemaProperty +import java.time.Duration +import java.util.* + +object TestConstants { + val TEST_VERIFICATION_POLICY = VerificationPolicyInput( + name = "Trusted Issuer and SchemaID", + description = "Verification Policy with trusted issuer and schemaId", + constraints = listOf( + VerificationPolicyConstraint( + schemaId = "http://atalaprism.io/schemas/1.0/StudentCredential", + trustedIssuers = listOf( + "did:example:123456789abcdefghi", + "did:example:123456789abcdefghj" + ) + ) + ) + ) + val CREDENTIAL_SCHEMA_TYPE = "https://w3c-ccg.github.io/vc-json-schemas/schema/2.0/schema.json" + + val SCHEMA_TYPE = "https://json-schema.org/draft/2020-12/schema" + + val jsonSchema = Schema( + id = "https://example.com/student-schema-1.0", + schema = SCHEMA_TYPE, + description = "Student schema", + type = "object", + properties = mutableMapOf( + "name" to SchemaProperty(type = "string"), + "age" to SchemaProperty(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(), + description = "Simple student credentials schema", + type = CREDENTIAL_SCHEMA_TYPE, + schema = jsonSchema, + tags = listOf("school", "students"), + version = "1.0.0" + ) + val RANDOM_CONSTAND_UUID = UUID.randomUUID().toString() + val DID_UPDATE_PUBLISH_MAX_WAIT_5_MIN = Duration.ofSeconds(60L) + val PRISM_DID_AUTH_KEY = ManagedDIDKeyTemplate("auth-1", Purpose.AUTHENTICATION) + val PRISM_DID_ASSERTION_KEY = ManagedDIDKeyTemplate("assertion-1", Purpose.ASSERTION_METHOD) + val PRISM_DID_UPDATE_NEW_AUTH_KEY = ManagedDIDKeyTemplate("auth-2", Purpose.AUTHENTICATION) + val PRISM_DID_SERVICE = Service( + "https://foo.bar.com", + listOf("LinkedDomains"), + Json("https://foo.bar.com/") + ) + val PRISM_DID_SERVICE_FOR_UPDATE = Service( + "https://update.com", + listOf("LinkedDomains"), + Json("https://update.com/") + ) + val PRISM_DID_SERVICE_TO_REMOVE = Service( + "https://remove.com", + listOf("LinkedDomains"), + Json("https://remove.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/") + ) + val EVENT_TYPE_CONNECTION_UPDATED = "ConnectionUpdated" + val EVENT_TYPE_ISSUE_CREDENTIAL_RECORD_UPDATED = "IssueCredentialRecordUpdated" + val EVENT_TYPE_PRESENTATION_UPDATED = "PresentationUpdated" + val EVENT_TYPE_DID_STATUS_UPDATED = "DIDStatusUpdated" + val WRONG_SEED = "wrong seed" +} diff --git a/tests/integration-tests/src/test/kotlin/common/Utils.kt b/tests/integration-tests/src/test/kotlin/common/Utils.kt new file mode 100644 index 0000000000..2ccbd5b5c1 --- /dev/null +++ b/tests/integration-tests/src/test/kotlin/common/Utils.kt @@ -0,0 +1,29 @@ +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 + +object Utils { + fun wait( + blockToWait: () -> Boolean, + errorMessage: String, + poolInterval: FixedPollInterval = FixedPollInterval(Duration.ofMillis(500L)), + timeout: Duration = Duration.ofSeconds(120L) + ) { + try { + Awaitility.await().withPollInterval(poolInterval) + .pollInSameThread() + .atMost(timeout) + .until { + blockToWait() + } + } catch (err: ConditionTimeoutException) { + throw ConditionTimeoutException( + errorMessage + ) + } + } +} diff --git a/tests/integration-tests/src/test/kotlin/config/AgentConf.kt b/tests/integration-tests/src/test/kotlin/config/AgentConf.kt new file mode 100644 index 0000000000..3918ef0960 --- /dev/null +++ b/tests/integration-tests/src/test/kotlin/config/AgentConf.kt @@ -0,0 +1,11 @@ +package config + +import com.sksamuel.hoplite.ConfigAlias +import java.net.URL + +data class AgentConf( + val url: URL, + @ConfigAlias("webhook_url") val webhookUrl: URL?, + var apikey: String?, + @ConfigAlias("multi-tenant") val multiTenant: Boolean?, +) diff --git a/tests/integration-tests/src/test/kotlin/config/Config.kt b/tests/integration-tests/src/test/kotlin/config/Config.kt new file mode 100644 index 0000000000..c4abe8b62d --- /dev/null +++ b/tests/integration-tests/src/test/kotlin/config/Config.kt @@ -0,0 +1,9 @@ +package config + +data class Config( + val global: GlobalConf, + val issuer: AgentConf, + val holder: AgentConf, + val verifier: AgentConf, + val admin: AgentConf +) diff --git a/tests/integration-tests/src/test/kotlin/config/GlobalConf.kt b/tests/integration-tests/src/test/kotlin/config/GlobalConf.kt new file mode 100644 index 0000000000..e3e6d89a34 --- /dev/null +++ b/tests/integration-tests/src/test/kotlin/config/GlobalConf.kt @@ -0,0 +1,10 @@ +package config + +import com.sksamuel.hoplite.ConfigAlias + +data class GlobalConf( + @ConfigAlias("auth_required") val authRequired: Boolean, + @ConfigAlias("auth_header") val authHeader: String, + @ConfigAlias("admin_auth_header") val adminAuthHeader: String, + @ConfigAlias("admin_apikey") val adminApiKey: String +) diff --git a/tests/integration-tests/src/test/kotlin/features/CommonSteps.kt b/tests/integration-tests/src/test/kotlin/features/CommonSteps.kt new file mode 100644 index 0000000000..1713f2ad20 --- /dev/null +++ b/tests/integration-tests/src/test/kotlin/features/CommonSteps.kt @@ -0,0 +1,208 @@ +package features + +import com.sksamuel.hoplite.ConfigLoader +import common.ListenToEvents +import config.AgentConf +import config.Config +import features.connection.ConnectionSteps +import features.credentials.IssueCredentialsSteps +import features.did.PublishDidSteps +import features.multitenancy.EventsSteps +import interactions.Get +import io.cucumber.java.AfterAll +import io.cucumber.java.BeforeAll +import io.cucumber.java.ParameterType +import io.cucumber.java.en.Given +import io.iohk.atala.automation.extensions.get +import io.iohk.atala.automation.serenity.ensure.Ensure +import io.iohk.atala.prism.models.* +import io.restassured.RestAssured +import net.serenitybdd.rest.SerenityRest +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 org.apache.http.HttpStatus.SC_OK +import java.util.* +import kotlin.random.Random + +@OptIn(ExperimentalStdlibApi::class) +fun createWalletAndEntity(agentConf: AgentConf) { + val config = ConfigLoader().loadConfigOrThrow("/tests.conf") + val createWalletResponse = RestAssured + .given().body( + CreateWalletRequest( + name = UUID.randomUUID().toString(), + seed = Random.nextBytes(64).toHexString(), + id = UUID.randomUUID() + ) + ) + .header(config.global.adminAuthHeader, config.global.adminApiKey) + .post("${agentConf.url}/wallets") + .thenReturn() + Ensure.that(createWalletResponse.statusCode).isEqualTo(HttpStatus.SC_CREATED) + val wallet = createWalletResponse.body.jsonPath().getObject("", WalletDetail::class.java) + val tenantResponse = RestAssured + .given().body( + CreateEntityRequest( + name = UUID.randomUUID().toString(), + walletId = wallet.id + ) + ) + .header(config.global.adminAuthHeader, config.global.adminApiKey) + .post("${agentConf.url}/iam/entities") + .thenReturn() + Ensure.that(tenantResponse.statusCode).isEqualTo(HttpStatus.SC_CREATED) + val entity = tenantResponse.body.jsonPath().getObject("", EntityResponse::class.java) + val addApiKeyResponse = + RestAssured + .given().body( + ApiKeyAuthenticationRequest( + entityId = entity.id, + apiKey = agentConf.apikey!! + ) + ) + .header(config.global.adminAuthHeader, config.global.adminApiKey) + .post("${agentConf.url}/iam/apikey-authentication") + .thenReturn() + Ensure.that(addApiKeyResponse.statusCode).isEqualTo(HttpStatus.SC_CREATED) + val registerIssuerWebhookResponse = + RestAssured + .given().body( + CreateWebhookNotification( + url = agentConf.webhookUrl!!.toExternalForm() + ) + ) + .header(config.global.authHeader, agentConf.apikey) + .post("${agentConf.url}/events/webhooks") + .thenReturn() + Ensure.that(registerIssuerWebhookResponse.statusCode).isEqualTo(HttpStatus.SC_CREATED) +} + +@BeforeAll +fun initAgents() { + val cast = Cast() + val config = ConfigLoader().loadConfigOrThrow("/tests.conf") + cast.actorNamed( + "Admin", + CallAnApi.at(config.admin.url.toExternalForm()) + ) + cast.actorNamed( + "Acme", + CallAnApi.at(config.issuer.url.toExternalForm()), + ListenToEvents.at(config.issuer.webhookUrl!!) + ) + cast.actorNamed( + "Bob", + CallAnApi.at(config.holder.url.toExternalForm()), + ListenToEvents.at(config.holder.webhookUrl!!) + ) + cast.actorNamed( + "Faber", + CallAnApi.at(config.verifier.url.toExternalForm()), + ListenToEvents.at(config.verifier.webhookUrl!!) + ) + OnStage.setTheStage(cast) + + // Create issuer wallet and tenant + if (config.issuer.multiTenant!!) { + createWalletAndEntity(config.issuer) + } + // Create verifier wallet + if (config.verifier.multiTenant!!) { + createWalletAndEntity(config.verifier) + } + + cast.actors.forEach { actor -> + when (actor.name) { + "Acme" -> { + actor.remember("AUTH_KEY", config.issuer.apikey) + } + "Bob" -> { + actor.remember("AUTH_KEY", config.holder.apikey) + } + "Faber" -> { + actor.remember("AUTH_KEY", config.verifier.apikey) + } + } + } +} + +@AfterAll +fun clearStage() { + OnStage.drawTheCurtain() +} + +class CommonSteps { + @ParameterType(".*") + fun actor(actorName: String): Actor { + return OnStage.theActorCalled(actorName) + } + + @Given("{actor} has an issued credential from {actor}") + fun holderHasIssuedCredentialFromIssuer(holder: Actor, issuer: Actor) { + holder.attemptsTo( + Get.resource("/issue-credentials/records") + ) + holder.attemptsTo( + Ensure.thatTheLastResponse().statusCode().isEqualTo(SC_OK) + ) + val receivedCredential = SerenityRest.lastResponse().get().contents!!.findLast { credential -> + credential.protocolState == IssueCredentialRecord.ProtocolState.CREDENTIAL_RECEIVED + } + + if (receivedCredential != null) { + holder.remember("issuedCredential", receivedCredential) + } else { + val publishDidSteps = PublishDidSteps() + val issueSteps = IssueCredentialsSteps() + actorsHaveExistingConnection(issuer, holder) + publishDidSteps.createsUnpublishedDid(holder) + publishDidSteps.createsUnpublishedDid(issuer) + publishDidSteps.hePublishesDidToLedger(issuer) + issueSteps.acmeOffersACredential(issuer, holder, "short") + issueSteps.bobRequestsTheCredential(holder) + issueSteps.acmeIssuesTheCredential(issuer) + issueSteps.bobHasTheCredentialIssued(holder) + } + } + + @Given("{actor} and {actor} have an existing connection") + fun actorsHaveExistingConnection(inviter: Actor, invitee: Actor) { + inviter.attemptsTo( + Get.resource("/connections") + ) + inviter.attemptsTo( + Ensure.thatTheLastResponse().statusCode().isEqualTo(SC_OK) + ) + val inviterConnection = SerenityRest.lastResponse().get().contents!!.firstOrNull { + it.label == "Connection with ${invitee.name}" && it.state == Connection.State.CONNECTION_RESPONSE_SENT + } + + var inviteeConnection: Connection? = null + if (inviterConnection != null) { + invitee.attemptsTo( + Get.resource("/connections") + ) + invitee.attemptsTo( + Ensure.thatTheLastResponse().statusCode().isEqualTo(SC_OK) + ) + inviteeConnection = SerenityRest.lastResponse().get().contents!!.firstOrNull { + it.theirDid == inviterConnection.myDid && it.state == Connection.State.CONNECTION_RESPONSE_RECEIVED + } + } + + if (inviterConnection != null && inviteeConnection != null) { + inviter.remember("connection-with-${invitee.name}", inviterConnection) + invitee.remember("connection-with-${inviter.name}", inviteeConnection) + } else { + val connectionSteps = ConnectionSteps() + connectionSteps.inviterGeneratesAConnectionInvitation(inviter, invitee) + connectionSteps.inviteeSendsAConnectionRequestToInviter(invitee, inviter) + connectionSteps.inviterReceivesTheConnectionRequest(inviter) + connectionSteps.inviteeReceivesTheConnectionResponse(invitee) + connectionSteps.inviterAndInviteeHaveAConnection(inviter, invitee) + } + } +} diff --git a/tests/integration-tests/src/test/kotlin/features/connection/ConnectionSteps.kt b/tests/integration-tests/src/test/kotlin/features/connection/ConnectionSteps.kt new file mode 100644 index 0000000000..301ff5064e --- /dev/null +++ b/tests/integration-tests/src/test/kotlin/features/connection/ConnectionSteps.kt @@ -0,0 +1,135 @@ +package features.connection + +import common.ListenToEvents +import common.Utils.wait +import interactions.Get +import interactions.Post +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.AcceptConnectionInvitationRequest +import io.iohk.atala.prism.models.Connection +import io.iohk.atala.prism.models.CreateConnectionRequest +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 org.assertj.core.api.Assertions.assertThat + +class ConnectionSteps { + + @When("{actor} generates a connection invitation to {actor}") + fun inviterGeneratesAConnectionInvitation(inviter: Actor, invitee: Actor) { + // Acme(Issuer) initiates a connection + // and sends it to Bob(Holder) out-of-band, e.g. using QR-code + val connectionLabel = "Connection with ${invitee.name}" + inviter.attemptsTo( + Post.to("/connections") + .with { + it.body( + CreateConnectionRequest(label = connectionLabel) + ) + } + ) + + val connection = SerenityRest.lastResponse().get() + + inviter.attemptsTo( + 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) + ) + + // Acme remembers connection to send it out of band to Bob + inviter.remember("connection", connection) + } + + @When("{actor} sends a connection request to {actor}") + fun inviteeSendsAConnectionRequestToInviter(invitee: Actor, inviter: Actor) { + // Bob accepts connection using achieved out-of-band invitation + val inviterConnection = inviter.recall("connection") + invitee.attemptsTo( + Post.to("/connection-invitations") + .with { + it.body( + AcceptConnectionInvitationRequest( + inviterConnection.invitation.invitationUrl.split("=")[1] + ) + ) + } + ) + val inviteeConnection = SerenityRest.lastResponse().get() + + invitee.attemptsTo( + Ensure.thatTheLastResponse().statusCode().isEqualTo(SC_OK), + Ensure.that(inviteeConnection.invitation.from).isEqualTo(inviterConnection.invitation.from), + Ensure.that(inviteeConnection.invitation.id).isEqualTo(inviterConnection.invitation.id), + 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) + ) + + invitee.remember("connection", inviteeConnection) + } + + @When("{actor} receives the connection request and sends back the response") + fun inviterReceivesTheConnectionRequest(inviter: Actor) { + wait( + { + val lastEvent = ListenToEvents.`as`(inviter).connectionEvents.lastOrNull { + it.data.thid == inviter.recall("connection").thid + } + lastEvent != null && + lastEvent.data.state == Connection.State.CONNECTION_RESPONSE_SENT + }, + "Inviter connection didn't reach ${Connection.State.CONNECTION_RESPONSE_SENT} state" + ) + } + + @When("{actor} receives the connection response") + fun inviteeReceivesTheConnectionResponse(invitee: Actor) { + // Bob (Holder) receives final connection response + wait( + { + val lastEvent = ListenToEvents.`as`(invitee).connectionEvents.lastOrNull { + it.data.thid == invitee.recall("connection").thid + } + lastEvent != null && + lastEvent.data.state == Connection.State.CONNECTION_RESPONSE_RECEIVED + }, + "Invitee connection didn't reach ${Connection.State.CONNECTION_RESPONSE_RECEIVED} state." + ) + } + + @Then("{actor} and {actor} have a connection") + 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}") + ) + inviter.attemptsTo( + Ensure.thatTheLastResponse().statusCode().isEqualTo(SC_OK) + ) + inviter.remember("connection-with-${invitee.name}", SerenityRest.lastResponse().get()) + + invitee.attemptsTo( + Get.resource("/connections/${invitee.recall("connection").connectionId}") + ) + invitee.attemptsTo( + Ensure.thatTheLastResponse().statusCode().isEqualTo(SC_OK) + ) + invitee.remember("connection-with-${inviter.name}", SerenityRest.lastResponse().get()) + + assertThat(inviter.recall("connection-with-${invitee.name}").myDid) + .isEqualTo(invitee.recall("connection-with-${inviter.name}").theirDid) + assertThat(inviter.recall("connection-with-${invitee.name}").theirDid) + .isEqualTo(invitee.recall("connection-with-${inviter.name}").myDid) + assertThat(inviter.recall("connection-with-${invitee.name}").state) + .isEqualTo(Connection.State.CONNECTION_RESPONSE_SENT) + assertThat(invitee.recall("connection-with-${inviter.name}").state) + .isEqualTo(Connection.State.CONNECTION_RESPONSE_RECEIVED) + } +} diff --git a/tests/e2e-tests/src/test/kotlin/features/issue_credentials/IssueCredentialsSteps.kt b/tests/integration-tests/src/test/kotlin/features/credentials/IssueCredentialsSteps.kt similarity index 58% rename from tests/e2e-tests/src/test/kotlin/features/issue_credentials/IssueCredentialsSteps.kt rename to tests/integration-tests/src/test/kotlin/features/credentials/IssueCredentialsSteps.kt index f57355234c..a187fb0f5a 100644 --- a/tests/e2e-tests/src/test/kotlin/features/issue_credentials/IssueCredentialsSteps.kt +++ b/tests/integration-tests/src/test/kotlin/features/credentials/IssueCredentialsSteps.kt @@ -1,14 +1,19 @@ -package features.issue_credentials +package features.credentials -import api_models.* import common.ListenToEvents -import common.Utils.lastResponseObject import common.Utils.wait +import interactions.Post 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.AcceptCredentialOfferRequest +import io.iohk.atala.prism.models.Connection +import io.iohk.atala.prism.models.CreateIssueCredentialRecordRequest +import io.iohk.atala.prism.models.IssueCredentialRecord +import models.CredentialEvent +import net.serenitybdd.rest.SerenityRest import net.serenitybdd.screenplay.Actor -import interactions.Post -import net.serenitybdd.screenplay.rest.questions.ResponseConsequence import org.apache.http.HttpStatus.SC_CREATED import org.apache.http.HttpStatus.SC_OK @@ -18,35 +23,39 @@ class IssueCredentialsSteps { @When("{actor} offers a credential to {actor} with {string} form DID") fun acmeOffersACredential(issuer: Actor, holder: Actor, didForm: String) { + val did: String = if (didForm == "short") { + issuer.recall("shortFormDid") + } else { + issuer.recall("longFormDid") + } - val did: String = if (didForm == "short") - issuer.recall("shortFormDid") else issuer.recall("longFormDid") - - val newCredential = Credential( + val credentialOfferRequest = CreateIssueCredentialRecordRequest( schemaId = null, - validityPeriod = 3600.0, - automaticIssuance = false, - awaitConfirmation = false, claims = linkedMapOf( "firstName" to "FirstName", - "lastName" to "LastName", + "lastName" to "LastName" ), issuingDID = did, connectionId = issuer.recall("connection-with-${holder.name}").connectionId, + validityPeriod = 3600.0, + automaticIssuance = false ) + issuer.attemptsTo( Post.to("/issue-credentials/credential-offers") .with { - it.body(newCredential) - }, + it.body(credentialOfferRequest) + } ) - issuer.should( - ResponseConsequence.seeThatResponse { - it.statusCode(SC_CREATED) - }, + + val credentialRecord = SerenityRest.lastResponse().get() + + issuer.attemptsTo( + Ensure.thatTheLastResponse().statusCode().isEqualTo(SC_CREATED) ) - issuer.remember("thid", lastResponseObject("", Credential::class).thid) - holder.remember("thid", lastResponseObject("", Credential::class).thid) + + issuer.remember("thid", credentialRecord.thid) + holder.remember("thid", credentialRecord.thid) } @When("{actor} receives the credential offer and accepts") @@ -57,9 +66,10 @@ class IssueCredentialsSteps { it.data.thid == holder.recall("thid") } credentialEvent != null && - credentialEvent!!.data.protocolState == CredentialState.OFFER_RECEIVED + credentialEvent!!.data.protocolState == IssueCredentialRecord.ProtocolState.OFFER_RECEIVED }, - "Holder was unable to receive the credential offer from Issuer! Protocol state did not achieve OfferReceived state.", + "Holder was unable to receive the credential offer from Issuer! " + + "Protocol state did not achieve ${IssueCredentialRecord.ProtocolState.OFFER_RECEIVED} state." ) val recordId = ListenToEvents.`as`(holder).credentialEvents.last().data.recordId @@ -68,15 +78,13 @@ class IssueCredentialsSteps { holder.attemptsTo( Post.to("/issue-credentials/records/$recordId/accept-offer") .with { - it.body(""" - { "subjectId": "${holder.recall("longFormDid")}" } - """.trimIndent()) - }, + it.body( + AcceptCredentialOfferRequest(holder.recall("longFormDid")) + ) + } ) - holder.should( - ResponseConsequence.seeThatResponse("Accept offer") { - it.statusCode(SC_OK) - }, + holder.attemptsTo( + Ensure.thatTheLastResponse().statusCode().isEqualTo(SC_OK) ) } @@ -88,18 +96,16 @@ class IssueCredentialsSteps { it.data.thid == issuer.recall("thid") } credentialEvent != null && - credentialEvent!!.data.protocolState == CredentialState.REQUEST_RECEIVED + 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.should( - ResponseConsequence.seeThatResponse("Issue credential") { - it.statusCode(SC_OK) - }, + issuer.attemptsTo( + Ensure.thatTheLastResponse().statusCode().isEqualTo(SC_OK) ) wait( @@ -108,10 +114,10 @@ class IssueCredentialsSteps { it.data.thid == issuer.recall("thid") } credentialEvent != null && - credentialEvent!!.data.protocolState == CredentialState.CREDENTIAL_SENT + credentialEvent!!.data.protocolState == IssueCredentialRecord.ProtocolState.CREDENTIAL_SENT }, "Issuer was unable to issue the credential! " + - "Protocol state did not achieve ${CredentialState.CREDENTIAL_SENT} state.", + "Protocol state did not achieve ${IssueCredentialRecord.ProtocolState.CREDENTIAL_SENT} state." ) } @@ -123,10 +129,10 @@ class IssueCredentialsSteps { it.data.thid == holder.recall("thid") } credentialEvent != null && - credentialEvent!!.data.protocolState == CredentialState.CREDENTIAL_RECEIVED + credentialEvent!!.data.protocolState == IssueCredentialRecord.ProtocolState.CREDENTIAL_RECEIVED }, "Holder was unable to receive the credential from Issuer! " + - "Protocol state did not achieve ${CredentialState.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/e2e-tests/src/test/kotlin/features/did/DeactivateDidSteps.kt b/tests/integration-tests/src/test/kotlin/features/did/DeactivateDidSteps.kt similarity index 55% rename from tests/e2e-tests/src/test/kotlin/features/did/DeactivateDidSteps.kt rename to tests/integration-tests/src/test/kotlin/features/did/DeactivateDidSteps.kt index d5c56a029f..29e7a7434e 100644 --- a/tests/e2e-tests/src/test/kotlin/features/did/DeactivateDidSteps.kt +++ b/tests/integration-tests/src/test/kotlin/features/did/DeactivateDidSteps.kt @@ -1,31 +1,33 @@ package features.did -import common.ListenToEvents import common.TestConstants -import common.Utils.lastResponseObject import common.Utils.wait +import interactions.Get +import interactions.Post 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.DIDOperationResponse +import io.iohk.atala.prism.models.DIDResolutionResult +import net.serenitybdd.rest.SerenityRest import net.serenitybdd.screenplay.Actor -import interactions.Get -import interactions.Post -import net.serenitybdd.screenplay.rest.questions.ResponseConsequence import org.apache.http.HttpStatus -import org.hamcrest.Matchers 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") ) - actor.should( - ResponseConsequence.seeThatResponse { - it.statusCode(HttpStatus.SC_ACCEPTED) - it.body("scheduledOperation.didRef", Matchers.not(Matchers.emptyString())) - it.body("scheduledOperation.id", Matchers.not(Matchers.emptyString())) - }, + + 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() ) } @@ -34,12 +36,12 @@ class DeactivateDidSteps { wait( { actor.attemptsTo( - Get.resource("/dids/${actor.recall("shortFormDid")}"), + Get.resource("/dids/${actor.recall("shortFormDid")}") ) - lastResponseObject("didDocumentMetadata.deactivated", String::class) == "true" + 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/features/did/ManageDidSteps.kt new file mode 100644 index 0000000000..d041db6987 --- /dev/null +++ b/tests/integration-tests/src/test/kotlin/features/did/ManageDidSteps.kt @@ -0,0 +1,85 @@ +package features.did + +import interactions.Get +import interactions.Post +import io.cucumber.java.en.Given +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 net.serenitybdd.rest.SerenityRest +import net.serenitybdd.screenplay.Actor +import org.apache.http.HttpStatus.SC_CREATED + +class ManageDidSteps { + + @Given("{actor} creates {int} PRISM DIDs") + fun createsMultipleManagedDids(actor: Actor, number: Int) { + repeat(number) { + createManageDid(actor) + } + actor.remember("number", number) + } + + @When("{actor} creates PRISM DID") + fun createManageDid(actor: Actor) { + val createDidRequest = createPrismDidRequest() + + actor.attemptsTo( + Post.to("/did-registrar/dids") + .with { + it.body(createDidRequest) + } + ) + + actor.attemptsTo( + Ensure.thatTheLastResponse().statusCode().isEqualTo(SC_CREATED) + ) + + var createdDids = actor.recall>("createdDids") + if (createdDids == null) { + createdDids = mutableListOf() + } + + val managedDid = SerenityRest.lastResponse().get() + + createdDids.add(managedDid.longFormDid!!) + actor.remember("createdDids", createdDids) + } + + @When("{actor} lists all PRISM DIDs") + fun iListManagedDids(actor: Actor) { + actor.attemptsTo( + Get.resource("/did-registrar/dids") + ) + } + + @Then("{actor} sees PRISM DID was created successfully") + fun theDidShouldBeRegisteredSuccessfully(actor: Actor) { + val managedDid = SerenityRest.lastResponse().get() + actor.attemptsTo( + Ensure.thatTheLastResponse().statusCode().isEqualTo(SC_CREATED), + Ensure.that(managedDid.longFormDid!!).isNotEmpty() + ) + } + + @Then("{actor} sees the list contains all created DIDs") + fun seeTheListContainsAllCreatedDids(actor: Actor) { + val expectedDids = actor.recall>("createdDids") + val managedDidList = SerenityRest.lastResponse().get().contents!! + .filter { it.status == "CREATED" }.map { it.longFormDid!! } + actor.attemptsTo( + Ensure.that(managedDidList).containsElementsFrom(expectedDids) + ) + } + + private fun createPrismDidRequest(): CreateManagedDidRequest = CreateManagedDidRequest( + CreateManagedDidRequestDocumentTemplate( + publicKeys = listOf(ManagedDIDKeyTemplate("auth-1", Purpose.AUTHENTICATION)), + services = listOf( + 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/features/did/PublishDidSteps.kt new file mode 100644 index 0000000000..76167565e3 --- /dev/null +++ b/tests/integration-tests/src/test/kotlin/features/did/PublishDidSteps.kt @@ -0,0 +1,140 @@ +package features.did + +import common.ListenToEvents +import common.TestConstants +import common.Utils.wait +import interactions.Get +import interactions.Post +import io.cucumber.java.en.Given +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 net.serenitybdd.rest.SerenityRest +import net.serenitybdd.screenplay.Actor +import org.apache.http.HttpStatus +import org.apache.http.HttpStatus.SC_CREATED +import org.apache.http.HttpStatus.SC_OK + +class PublishDidSteps { + + @Given("{actor} have published PRISM DID") + fun actorHavePublishedPrismDid(actor: Actor) { + actor.attemptsTo( + Get.resource("/did-registrar/dids") + ) + actor.attemptsTo( + Ensure.thatTheLastResponse().statusCode().isEqualTo(SC_OK) + ) + val publishedDids = SerenityRest.lastResponse().get().contents!!.filter { + // TODO: fix openapi spec to have statuses as enum + it.status == "PUBLISHED" + } + val did = publishedDids.firstOrNull { + actor.attemptsTo( + Get.resource("/dids/${it.did}") + ) + !SerenityRest.lastResponse().get().didDocumentMetadata.deactivated!! + } + if (did == null) { + createsUnpublishedDid(actor) + hePublishesDidToLedger(actor) + } else { + actor.remember("shortFormDid", did.did) + } + } + + @Given("{actor} creates unpublished DID") + fun createsUnpublishedDid(actor: Actor) { + val createDidRequest = CreateManagedDidRequest( + CreateManagedDidRequestDocumentTemplate( + publicKeys = listOf( + ManagedDIDKeyTemplate("auth-1", Purpose.AUTHENTICATION), + 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/")) + ) + ) + ) + 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() + ) + + actor.remember("longFormDid", managedDid.longFormDid) + + actor.attemptsTo( + Get.resource("/did-registrar/dids/${managedDid.longFormDid}") + ) + actor.attemptsTo( + Ensure.thatTheLastResponse().statusCode().isEqualTo(SC_OK) + ) + val did = SerenityRest.lastResponse().get() + actor.remember( + "shortFormDid", + did.did + ) + } + + @When("{actor} publishes DID to ledger") + fun hePublishesDidToLedger(actor: Actor) { + actor.attemptsTo( + 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() + ) + + wait( + { + val didEvent = + ListenToEvents.`as`(actor).didEvents.lastOrNull { + it.data.did == actor.recall("shortFormDid") + } + didEvent != null && didEvent.data.status == "PUBLISHED" + }, + "ERROR: DID was not published to ledger!", + timeout = TestConstants.DID_UPDATE_PUBLISH_MAX_WAIT_5_MIN + ) + actor.attemptsTo( + 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")) + ) + } + + @Then("{actor} resolves DID document corresponds to W3C standard") + fun heSeesDidDocumentCorrespondsToW3cStandard(actor: Actor) { + val didResolutionResult = SerenityRest.lastResponse().get() + val didDocument = didResolutionResult.didDocument!! + val shortFormDid = actor.recall("shortFormDid") + actor.attemptsTo( + Ensure.that(didDocument.id).isEqualTo(shortFormDid), + 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() + ) + } +} diff --git a/tests/e2e-tests/src/test/kotlin/features/did/UpdateDidSteps.kt b/tests/integration-tests/src/test/kotlin/features/did/UpdateDidSteps.kt similarity index 63% rename from tests/e2e-tests/src/test/kotlin/features/did/UpdateDidSteps.kt rename to tests/integration-tests/src/test/kotlin/features/did/UpdateDidSteps.kt index 3d566291de..dad0341d95 100644 --- a/tests/e2e-tests/src/test/kotlin/features/did/UpdateDidSteps.kt +++ b/tests/integration-tests/src/test/kotlin/features/did/UpdateDidSteps.kt @@ -1,69 +1,72 @@ package features.did -import api_models.* import common.TestConstants -import common.Utils.lastResponseList import common.Utils.wait +import interactions.Get +import interactions.Post 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 net.serenitybdd.rest.SerenityRest import net.serenitybdd.screenplay.Actor -import interactions.Get -import interactions.Post -import net.serenitybdd.screenplay.rest.questions.ResponseConsequence import org.apache.http.HttpStatus -import org.hamcrest.Matchers.emptyString -import org.hamcrest.Matchers.not class UpdateDidSteps { @When("{actor} updates PRISM DID by adding new keys") fun actorUpdatesPrismDidByAddingNewKeys(actor: Actor) { - val updatePrismDidAction = UpdatePrismDidAction( - actionType = "ADD_KEY", - addKey = TestConstants.PRISM_DID_UPDATE_NEW_AUTH_KEY, + val updatePrismDidAction = UpdateManagedDIDRequestAction( + actionType = ActionType.ADD_KEY, + ManagedDIDKeyTemplate("auth-2", Purpose.AUTHENTICATION) ) actor.remember("updatePrismDidAction", updatePrismDidAction) } @When("{actor} updates PRISM DID by removing keys") fun actorUpdatesPrismDidByRemovingKeys(actor: Actor) { - val updatePrismDidAction = UpdatePrismDidAction( - actionType = "REMOVE_KEY", - removeKey = TestConstants.PRISM_DID_AUTH_KEY, + val updatePrismDidAction = UpdateManagedDIDRequestAction( + actionType = ActionType.REMOVE_KEY, + removeKey = RemoveEntryById("auth-1") ) actor.remember("updatePrismDidAction", updatePrismDidAction) } @When("{actor} updates PRISM DID with new services") fun actorUpdatesPrismDidWithNewServices(actor: Actor) { - val updatePrismDidAction = UpdatePrismDidAction( - actionType = "ADD_SERVICE", - addService = TestConstants.PRISM_DID_UPDATE_NEW_SERVICE, + val updatePrismDidAction = UpdateManagedDIDRequestAction( + actionType = ActionType.ADD_SERVICE, + addService = Service( + "https://new.service.com", + listOf("LinkedDomains"), + Json("https://new.service.com/") + ) ) actor.remember("updatePrismDidAction", updatePrismDidAction) } @When("{actor} updates PRISM DID by removing services") fun actorUpdatesPrismDidByRemovingServices(actor: Actor) { - val updatePrismDidAction = UpdatePrismDidAction( - actionType = "REMOVE_SERVICE", - removeService = TestConstants.PRISM_DID_UPDATE_NEW_SERVICE, + val updatePrismDidAction = UpdateManagedDIDRequestAction( + actionType = ActionType.REMOVE_SERVICE, + removeService = RemoveEntryById("https://new.service.com") ) actor.remember("updatePrismDidAction", updatePrismDidAction) } @When("{actor} updates PRISM DID by updating services") fun actorUpdatesPrismDidByUpdatingServices(actor: Actor) { - val newService = Service( + val newService = UpdateManagedDIDServiceAction( id = TestConstants.PRISM_DID_SERVICE_FOR_UPDATE.id, type = TestConstants.PRISM_DID_SERVICE_FOR_UPDATE.type, - serviceEndpoint = listOf( - TestConstants.PRISM_DID_UPDATE_NEW_SERVICE_URL, - ), + serviceEndpoint = Json( + TestConstants.PRISM_DID_UPDATE_NEW_SERVICE_URL + ) ) - val updatePrismDidAction = UpdatePrismDidAction( - actionType = "UPDATE_SERVICE", - updateService = newService, + val updatePrismDidAction = UpdateManagedDIDRequestAction( + actionType = ActionType.UPDATE_SERVICE, + updateService = newService ) actor.remember("updatePrismDidAction", updatePrismDidAction) } @@ -73,15 +76,14 @@ class UpdateDidSteps { actor.attemptsTo( Post.to("/did-registrar/dids/${actor.recall("shortFormDid")}/updates") .with { - it.body(UpdatePrismDidRequest(listOf(actor.recall("updatePrismDidAction")))) - }, + it.body(UpdateManagedDIDRequest(listOf(actor.recall("updatePrismDidAction")))) + } ) - actor.should( - ResponseConsequence.seeThatResponse { - it.statusCode(HttpStatus.SC_ACCEPTED) - it.body("scheduledOperation.didRef", not(emptyString())) - it.body("scheduledOperation.id", not(emptyString())) - }, + 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() ) } @@ -90,10 +92,10 @@ class UpdateDidSteps { wait( { actor.attemptsTo( - Get.resource("/dids/${actor.recall("shortFormDid")}"), + Get.resource("/dids/${actor.recall("shortFormDid")}") ) - val authUris = lastResponseList("didDocument.authentication", String::class) - val verificationMethods = lastResponseList("didDocument.verificationMethod.id", String::class) + val authUris = SerenityRest.lastResponse().get().didDocument!!.authentication!! + 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 { @@ -101,7 +103,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 ) } @@ -110,10 +112,10 @@ class UpdateDidSteps { wait( { actor.attemptsTo( - Get.resource("/dids/${actor.recall("shortFormDid")}"), + Get.resource("/dids/${actor.recall("shortFormDid")}") ) - val authUris = lastResponseList("didDocument.authentication", String::class) - val verificationMethods = lastResponseList("didDocument.verificationMethod.id", String::class) + val authUris = SerenityRest.lastResponse().get().didDocument!!.authentication!! + val verificationMethods = SerenityRest.lastResponse().get().didDocument!!.verificationMethod!!.map { it.id } authUris.none { it == "${actor.recall("shortFormDid")}#${TestConstants.PRISM_DID_AUTH_KEY.id}" } && verificationMethods.none { @@ -121,7 +123,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 ) } @@ -130,15 +132,15 @@ class UpdateDidSteps { wait( { actor.attemptsTo( - Get.resource("/dids/${actor.recall("shortFormDid")}"), + Get.resource("/dids/${actor.recall("shortFormDid")}") ) - val serviceIds = lastResponseList("didDocument.service.id", String::class) + 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 ) } @@ -147,15 +149,15 @@ class UpdateDidSteps { wait( { actor.attemptsTo( - Get.resource("/dids/${actor.recall("shortFormDid")}"), + Get.resource("/dids/${actor.recall("shortFormDid")}") ) - val serviceIds = lastResponseList("didDocument.service.id", String::class) + 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 ) } @@ -164,13 +166,13 @@ class UpdateDidSteps { wait( { actor.attemptsTo( - Get.resource("/dids/${actor.recall("shortFormDid")}"), + Get.resource("/dids/${actor.recall("shortFormDid")}") ) - val service = lastResponseList("didDocument.service", Service::class) - service.any { it.serviceEndpoint.contains(TestConstants.PRISM_DID_UPDATE_NEW_SERVICE_URL) } + 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/e2e-tests/src/test/kotlin/features/multitenancy/EntitySteps.kt b/tests/integration-tests/src/test/kotlin/features/multitenancy/EntitySteps.kt similarity index 53% rename from tests/e2e-tests/src/test/kotlin/features/multitenancy/EntitySteps.kt rename to tests/integration-tests/src/test/kotlin/features/multitenancy/EntitySteps.kt index 3e45e7d4f3..4a0c88e297 100644 --- a/tests/e2e-tests/src/test/kotlin/features/multitenancy/EntitySteps.kt +++ b/tests/integration-tests/src/test/kotlin/features/multitenancy/EntitySteps.kt @@ -1,10 +1,11 @@ package features.multitenancy -import api_models.CreateEntityRequest -import api_models.AddApiKeyRequest -import common.Ensure -import common.Utils import interactions.Post +import io.iohk.atala.automation.extensions.get +import io.iohk.atala.automation.serenity.ensure.Ensure +import io.iohk.atala.prism.models.ApiKeyAuthenticationRequest +import io.iohk.atala.prism.models.CreateEntityRequest +import io.iohk.atala.prism.models.EntityResponse import net.serenitybdd.rest.SerenityRest import net.serenitybdd.screenplay.Actor import org.apache.http.HttpStatus.SC_CREATED @@ -14,9 +15,10 @@ class EntitySteps { fun createNewEntity( actor: Actor, - walletId: String, + walletId: UUID, name: String = "", - id: String = UUID.randomUUID().toString()): String { + id: UUID = UUID.randomUUID() + ): EntityResponse { actor.attemptsTo( Post.to("/iam/entities") .with { @@ -24,31 +26,31 @@ class EntitySteps { CreateEntityRequest( walletId = walletId, name = name, - id = id, + id = id ) ) - }, + } ) actor.attemptsTo( - Ensure.that(SerenityRest.lastResponse().statusCode).isEqualTo(SC_CREATED) + Ensure.thatTheLastResponse().statusCode().isEqualTo(SC_CREATED) ) - return Utils.lastResponseObject("id", String::class) + return SerenityRest.lastResponse().get() } - fun addNewApiKeyToEntity(actor: Actor, entityId: String, apiKey: String) { + fun addNewApiKeyToEntity(actor: Actor, entityId: UUID, apiKey: String) { actor.attemptsTo( Post.to("/iam/apikey-authentication") .with { it.body( - AddApiKeyRequest( + ApiKeyAuthenticationRequest( entityId = entityId, - apiKey = apiKey, + apiKey = apiKey ) ) - }, + } ) actor.attemptsTo( - Ensure.that(SerenityRest.lastResponse().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/features/multitenancy/EventsSteps.kt new file mode 100644 index 0000000000..0348674dc4 --- /dev/null +++ b/tests/integration-tests/src/test/kotlin/features/multitenancy/EventsSteps.kt @@ -0,0 +1,24 @@ +package features.multitenancy + +import interactions.Post +import io.iohk.atala.automation.serenity.ensure.Ensure +import io.iohk.atala.prism.models.CreateWebhookNotification +import net.serenitybdd.screenplay.Actor +import org.apache.http.HttpStatus + +class EventsSteps { + fun registerNewWebhook(actor: Actor, webhookUrl: String) { + actor.attemptsTo( + Post.to("/events/webhooks") + .with { + it.body( + CreateWebhookNotification(url = webhookUrl) + ) + } + ) + + actor.attemptsTo( + Ensure.thatTheLastResponse().statusCode().isEqualTo(HttpStatus.SC_CREATED) + ) + } +} diff --git a/tests/e2e-tests/src/test/kotlin/features/multitenancy/WalletsSteps.kt b/tests/integration-tests/src/test/kotlin/features/multitenancy/WalletsSteps.kt similarity index 58% rename from tests/e2e-tests/src/test/kotlin/features/multitenancy/WalletsSteps.kt rename to tests/integration-tests/src/test/kotlin/features/multitenancy/WalletsSteps.kt index b6815edb9e..d3f3d47d71 100644 --- a/tests/e2e-tests/src/test/kotlin/features/multitenancy/WalletsSteps.kt +++ b/tests/integration-tests/src/test/kotlin/features/multitenancy/WalletsSteps.kt @@ -1,21 +1,22 @@ package features.multitenancy -import api_models.CreateWalletRequest -import common.Ensure import common.TestConstants -import common.Utils import interactions.Get import interactions.Post import io.cucumber.java.en.Given 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.CreateWalletRequest +import io.iohk.atala.prism.models.WalletDetail +import io.iohk.atala.prism.models.WalletDetailPage import net.serenitybdd.rest.SerenityRest import net.serenitybdd.screenplay.Actor import org.apache.http.HttpStatus.* import java.util.* import kotlin.random.Random - class WalletsSteps { @OptIn(ExperimentalStdlibApi::class) @@ -23,7 +24,8 @@ class WalletsSteps { actor: Actor, name: String = "test-wallet", seed: String = Random.nextBytes(64).toHexString(), - id: String = UUID.randomUUID().toString()) { + id: UUID = UUID.randomUUID() + ): WalletDetail { actor.attemptsTo( Post.to("/wallets") .with { @@ -31,33 +33,32 @@ class WalletsSteps { CreateWalletRequest( name = name, seed = seed, - id = id, + id = id ) ) - }, + } ) + return SerenityRest.lastResponse().get() } @When("{actor} creates new wallet with name {string}") fun iCreateNewWalletWithName(acme: Actor, name: String) { - createNewWallet(acme, name) + val wallet = createNewWallet(acme, name) acme.attemptsTo( - Ensure.that(SerenityRest.lastResponse().statusCode).isEqualTo(SC_CREATED) - .withReportedError("Response status code is not correct!"), + Ensure.thatTheLastResponse().statusCode().isEqualTo(SC_CREATED) ) - acme.remember("walletId", Utils.lastResponseObject("id", String::class)) + acme.remember("walletId", wallet.id) } @When("{actor} creates new wallet with unique id") fun acmeCreateNewWalletWithId(acme: Actor) { - val uniqueId = UUID.randomUUID().toString() + val uniqueId = UUID.randomUUID() acme.remember("uniqueId", uniqueId) - createNewWallet(acme, id = uniqueId) + val wallet = createNewWallet(acme, id = uniqueId) acme.attemptsTo( - Ensure.that(SerenityRest.lastResponse().statusCode).isEqualTo(SC_CREATED) - .withReportedError("Response status code is not correct!"), - Ensure.that(Utils.lastResponseObject("id", String::class)).isEqualTo(uniqueId) - .withReportedError("Wallet id is not correct!"), + Ensure.thatTheLastResponse().statusCode().isEqualTo(SC_CREATED), + Ensure.that(wallet.id).isEqualTo(uniqueId) + .withReportedError("Wallet id is not correct!") ) } @@ -65,8 +66,7 @@ class WalletsSteps { fun acmeCreateNewWalletWithTheSameId(acme: Actor) { createNewWallet(acme, id = acme.recall("uniqueId")) acme.attemptsTo( - Ensure.that(SerenityRest.lastResponse().statusCode).isEqualTo(SC_BAD_REQUEST) - .withReportedError("Response status code is not correct!"), + Ensure.thatTheLastResponse().statusCode().isEqualTo(SC_BAD_REQUEST) ) } @@ -76,8 +76,7 @@ class WalletsSteps { acme.remember("uniqueName", name) createNewWallet(acme, name = name) acme.attemptsTo( - Ensure.that(SerenityRest.lastResponse().statusCode).isEqualTo(SC_CREATED) - .withReportedError("Response status code is not correct!"), + Ensure.thatTheLastResponse().statusCode().isEqualTo(SC_CREATED) ) } @@ -85,8 +84,7 @@ class WalletsSteps { fun acmeCreatesNewWalletWithTheSameUniqueName(acme: Actor) { createNewWallet(acme, name = acme.recall("uniqueName")) acme.attemptsTo( - Ensure.that(SerenityRest.lastResponse().statusCode).isEqualTo(SC_CREATED) - .withReportedError("Response status code is not correct!"), + Ensure.thatTheLastResponse().statusCode().isEqualTo(SC_CREATED) ) } @@ -96,13 +94,15 @@ class WalletsSteps { Get.resource("/wallets/${acme.recall("walletId")}") .with { it.queryParam("name", name) - }, + } ) + val wallet = SerenityRest.lastResponse().get() + acme.attemptsTo( - Ensure.that(Utils.lastResponseObject("name", String::class)).isEqualTo(name) + Ensure.that(wallet.name).isEqualTo(name) .withReportedError("Wallet name is not correct!"), - Ensure.that(Utils.lastResponseObject("id", String::class)).isEqualTo(acme.recall("walletId")) - .withReportedError("Wallet id is not correct!"), + Ensure.that(wallet.id).isEqualTo(acme.recall("walletId")) + .withReportedError("Wallet id is not correct!") ) } @@ -112,33 +112,30 @@ class WalletsSteps { Get.resource("/wallets") ) acme.attemptsTo( - Ensure.that(SerenityRest.lastResponse().statusCode).isEqualTo(SC_OK) - .withReportedError("Response status code is not correct!"), + Ensure.thatTheLastResponse().statusCode().isEqualTo(SC_OK) ) - val wallets = Utils.lastResponseList("contents.name", String::class).filter { it == acme.recall("uniqueName") } + 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.that(SerenityRest.lastResponse().statusCode).isEqualTo(SC_BAD_REQUEST) - .withReportedError("Response status code is not correct!") + Ensure.thatTheLastResponse().statusCode().isEqualTo(SC_BAD_REQUEST) ) acme.attemptsTo( Get.resource("/wallets") ) acme.attemptsTo( - Ensure.that(SerenityRest.lastResponse().statusCode).isEqualTo(SC_OK) - .withReportedError("Response status code is not correct!"), + Ensure.thatTheLastResponse().statusCode().isEqualTo(SC_OK) ) - val wallets = Utils.lastResponseList("contents.id", String::class).filter { it == acme.recall("uniqueId") } + 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!") ) } @@ -150,8 +147,7 @@ class WalletsSteps { @Then("{actor} should see the error and wallet should not be created") fun acmeShouldSeeTheErrorAndWalletShouldNotBeCreated(acme: Actor) { acme.attemptsTo( - Ensure.that(SerenityRest.lastResponse().statusCode).isEqualTo(SC_BAD_REQUEST) - .withReportedError("Response status code is not correct!"), + 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/features/proofs/PresentProofSteps.kt new file mode 100644 index 0000000000..c1fa5976e2 --- /dev/null +++ b/tests/integration-tests/src/test/kotlin/features/proofs/PresentProofSteps.kt @@ -0,0 +1,124 @@ +package features.proofs + +import common.ListenToEvents +import common.Utils.wait +import interactions.Patch +import interactions.Post +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 models.PresentationEvent +import net.serenitybdd.rest.SerenityRest +import net.serenitybdd.screenplay.Actor +import org.apache.http.HttpStatus.SC_CREATED + +class PresentProofSteps { + + private var proofEvent: PresentationEvent? = null + + @When("{actor} sends a request for proof presentation to {actor}") + fun faberSendsARequestForProofPresentationToBob(faber: Actor, bob: Actor) { + val presentationRequest = RequestPresentationInput( + connectionId = faber.recall("connection-with-${bob.name}").connectionId, + options = Options( + challenge = "11c91493-01b3-4c4d-ac36-b336bab5bddf", + domain = "https://example-verifier.com" + ), + proofs = listOf( + ProofRequestAux( + schemaId = "https://schema.org/Person", + trustIssuers = listOf("did:web:atalaprism.io/users/testUser") + ) + ) + ) + faber.attemptsTo( + Post.to("/present-proof/presentations") + .with { + it.body( + presentationRequest + ) + } + ) + faber.attemptsTo( + Ensure.thatTheLastResponse().statusCode().isEqualTo(SC_CREATED) + ) + val presentationStatus = SerenityRest.lastResponse().get() + faber.remember("thid", presentationStatus.thid) + bob.remember("thid", presentationStatus.thid) + } + + @When("{actor} receives the request") + fun bobReceivesTheRequest(bob: Actor) { + wait( + { + proofEvent = ListenToEvents.`as`(bob).presentationEvents.lastOrNull { + it.data.thid == bob.recall("thid") + } + proofEvent != null && + proofEvent!!.data.status == PresentationStatus.Status.REQUEST_RECEIVED + }, + "ERROR: Bob did not achieve any presentation request!" + ) + bob.remember("presentationId", proofEvent!!.data.presentationId) + } + + @When("{actor} makes the presentation of the proof to {actor}") + fun bobMakesThePresentationOfTheProof(bob: Actor, faber: Actor) { + val requestPresentationAction = RequestPresentationAction( + proofId = listOf(bob.recall("issuedCredential").recordId), + action = RequestPresentationAction.Action.REQUEST_MINUS_ACCEPT + ) + + bob.attemptsTo( + Patch.to("/present-proof/presentations/${bob.recall("presentationId")}").with { + it.body( + requestPresentationAction + ) + } + ) + } + + @When("{actor} rejects the proof") + fun bobRejectsProof(bob: Actor) { + bob.attemptsTo( + Patch.to("/present-proof/presentations/${bob.recall("presentationId")}").with { + it.body( + RequestPresentationAction( + action = RequestPresentationAction.Action.REQUEST_MINUS_REJECT + ) + ) + } + ) + } + + @Then("{actor} sees the proof is rejected") + fun bobSeesProofIsRejected(bob: Actor) { + wait( + { + proofEvent = ListenToEvents.`as`(bob).presentationEvents.lastOrNull { + it.data.thid == bob.recall("thid") + } + proofEvent != null && + proofEvent!!.data.status == PresentationStatus.Status.REQUEST_REJECTED + }, + "ERROR: Faber did not receive presentation from Bob!" + ) + } + + @Then("{actor} has the proof verified") + fun faberHasTheProofVerified(faber: Actor) { + wait( + { + proofEvent = ListenToEvents.`as`(faber).presentationEvents.lastOrNull { + it.data.thid == faber.recall("thid") + } + + proofEvent != null && + proofEvent!!.data.status == PresentationStatus.Status.PRESENTATION_VERIFIED + }, + "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/features/schemas/CredentialSchemasSteps.kt new file mode 100644 index 0000000000..c34af47855 --- /dev/null +++ b/tests/integration-tests/src/test/kotlin/features/schemas/CredentialSchemasSteps.kt @@ -0,0 +1,84 @@ +package features + +import common.TestConstants +import interactions.Get +import interactions.Post +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.CredentialSchemaResponse +import models.Schema +import net.serenitybdd.rest.SerenityRest +import net.serenitybdd.screenplay.Actor +import org.apache.http.HttpStatus.SC_CREATED +import org.apache.http.HttpStatus.SC_OK + +class CredentialSchemasSteps { + + @When("{actor} creates a new credential schema") + fun acmeCreatesANewCredentialSchema(actor: Actor) { + actor.attemptsTo( + Post.to("/schema-registry/schemas").with { + it.body( + TestConstants.STUDENT_SCHEMA.copy(author = actor.recall("shortFormDid")) + ) + } + ) + } + + @Then("{actor} sees new credential schema is available") + fun newCredentialSchemaIsAvailable(actor: Actor) { + val credentialSchema = SerenityRest.lastResponse().get() + val schema = SerenityRest.lastResponse().get("schema") + + actor.attemptsTo( + Ensure.thatTheLastResponse().statusCode().isEqualTo(SC_CREATED), + Ensure.that(credentialSchema.guid).isNotNull(), + Ensure.that(credentialSchema.id).isNotNull(), + Ensure.that(credentialSchema.longId!!).isNotNull(), + Ensure.that(credentialSchema.authored).isNotNull(), + Ensure.that(credentialSchema.kind).isEqualTo("CredentialSchema"), + Ensure.that(credentialSchema.name).contains(TestConstants.STUDENT_SCHEMA.name), + Ensure.that(credentialSchema.description).contains(TestConstants.STUDENT_SCHEMA.description!!), + 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(schema.toString()).isEqualTo(TestConstants.jsonSchema.toString()) + ) + } + + @When("{actor} creates {int} new schemas") + fun acmeCreatesMultipleSchemas(actor: Actor, numberOfSchemas: Int) { + val createdSchemas: MutableList = mutableListOf() + repeat(numberOfSchemas) { i: Int -> + actor.attemptsTo( + Post.to("/schema-registry/schemas").with { + it.body( + TestConstants.generate_with_name_suffix_and_author( + i.toString(), + actor.recall("shortFormDid") + ) + ) + } + ) + actor.attemptsTo( + Ensure.thatTheLastResponse().statusCode().isEqualTo(SC_CREATED) + ) + createdSchemas.add(SerenityRest.lastResponse().get()) + } + actor.remember("createdSchemas", createdSchemas) + } + + @Then("{actor} can access all of them one by one") + fun theyCanBeAccessedWithPagination(actor: Actor) { + actor.recall>("createdSchemas").forEach { schema -> + actor.attemptsTo( + Get.resource("/schema-registry/schemas/${schema.guid}") + ) + actor.attemptsTo( + 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/features/system/SystemSteps.kt new file mode 100644 index 0000000000..aaee61d9ec --- /dev/null +++ b/tests/integration-tests/src/test/kotlin/features/system/SystemSteps.kt @@ -0,0 +1,31 @@ +package features.system + +import interactions.Get +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.HealthInfo +import net.serenitybdd.rest.SerenityRest +import net.serenitybdd.screenplay.Actor +import org.apache.http.HttpStatus + +class SystemSteps { + @When("{actor} makes a request to the health endpoint") + fun actorRequestsHealthEndpoint(actor: Actor) { + actor.attemptsTo( + Get.resource("/_system/health") + ) + actor.attemptsTo( + Ensure.thatTheLastResponse().statusCode().isEqualTo(HttpStatus.SC_OK) + ) + } + + @Then("{actor} knows what version of the service is running") + fun actorUnderstandsVersion(actor: Actor) { + val healthResponse = SerenityRest.lastResponse().get() + actor.attemptsTo( + Ensure.that(healthResponse.version).isNotBlank() + ) + } +} diff --git a/tests/integration-tests/src/test/kotlin/features/verificationpolicies/VerificationPoliciesSteps.kt b/tests/integration-tests/src/test/kotlin/features/verificationpolicies/VerificationPoliciesSteps.kt new file mode 100644 index 0000000000..0b1ad616d9 --- /dev/null +++ b/tests/integration-tests/src/test/kotlin/features/verificationpolicies/VerificationPoliciesSteps.kt @@ -0,0 +1,100 @@ +package features.verificationpolicies + +import common.TestConstants +import interactions.Get +import interactions.Post +import interactions.Put +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.VerificationPolicy +import io.iohk.atala.prism.models.VerificationPolicyInput +import net.serenitybdd.rest.SerenityRest +import net.serenitybdd.screenplay.Actor +import org.apache.http.HttpStatus +import java.util.* + +class VerificationPoliciesSteps { + + @When("{actor} creates a new verification policy") + fun acmeCreatesANewVerificationPolicy(actor: Actor) { + actor.attemptsTo( + Post.to("/verification/policies").with { + it.body( + TestConstants.TEST_VERIFICATION_POLICY + ) + } + ) + actor.attemptsTo( + Ensure.thatTheLastResponse().statusCode().isEqualTo(HttpStatus.SC_CREATED) + ) + } + + @Then("{actor} sees new verification policy is available") + fun newVerificationPolicyIsAvailable(actor: Actor) { + val policy = SerenityRest.lastResponse().get() + actor.attemptsTo( + Ensure.that(policy.id).isNotNull(), + 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) + ) + + policy.constraints!!.forEach { + actor.attemptsTo( + Ensure.that(it.schemaId).isEqualTo(TestConstants.TEST_VERIFICATION_POLICY.constraints!!.first().schemaId), + Ensure.that(it.trustedIssuers!!) + .containsExactlyInAnyOrderElementsFrom( + TestConstants.TEST_VERIFICATION_POLICY.constraints!!.first().trustedIssuers!! + ) + ) + } + actor.remember("policy", policy) + } + + @When("{actor} updates a new verification policy") + fun acmeUpdatesAVerificationPolicy(actor: Actor) { + val policy = actor.recall("policy") + val updatePolicyInput = VerificationPolicyInput( + name = policy.name, + description = "updated description + ${UUID.randomUUID()}", + constraints = policy.constraints + ) + actor.attemptsTo( + Put.to("/verification/policies/${policy.id}?nonce=${policy.nonce}").with { + it.body(updatePolicyInput) + } + ) + actor.remember("updatedPolicyInput", updatePolicyInput) + } + + @Then("{actor} sees the updated verification policy is available") + fun updatedVerificationPolicyIsAvailable(actor: Actor) { + val updatePolicyInput = actor.forget("updatedPolicyInput") + + actor.attemptsTo( + Get.resource("/verification/policies/${actor.recall("policy").id}") + ) + val policy = SerenityRest.lastResponse().get() + + actor.attemptsTo( + Ensure.that(policy.id).isNotNull(), + 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) + ) + + policy.constraints!!.forEach { + actor.attemptsTo( + Ensure.that(it.schemaId).isEqualTo(updatePolicyInput.constraints!!.first().schemaId), + Ensure.that(it.trustedIssuers!!) + .containsExactlyInAnyOrderElementsFrom( + updatePolicyInput.constraints!!.first().trustedIssuers!! + ) + ) + } + } +} diff --git a/tests/integration-tests/src/test/kotlin/interactions/AuthRestInteraction.kt b/tests/integration-tests/src/test/kotlin/interactions/AuthRestInteraction.kt new file mode 100644 index 0000000000..881c193dae --- /dev/null +++ b/tests/integration-tests/src/test/kotlin/interactions/AuthRestInteraction.kt @@ -0,0 +1,25 @@ +package interactions + +import com.sksamuel.hoplite.ConfigLoader +import config.Config +import io.ktor.util.* +import io.restassured.specification.RequestSpecification +import net.serenitybdd.screenplay.Actor +import net.serenitybdd.screenplay.rest.interactions.RestInteraction + +abstract class AuthRestInteraction : RestInteraction() { + + private val config = ConfigLoader().loadConfigOrThrow("/tests.conf") + + fun specWithAuthHeaders(actor: T): RequestSpecification { + val spec = rest() + if (actor!!.name.toLowerCasePreservingASCIIRules().contains("admin")) { + spec.header(config.global.adminAuthHeader, config.global.adminApiKey) + } else { + if (config.global.authRequired) { + spec.header(config.global.authHeader, actor.recall("AUTH_KEY")) + } + } + return spec + } +} diff --git a/tests/e2e-tests/src/test/kotlin/interactions/Delete.kt b/tests/integration-tests/src/test/kotlin/interactions/Delete.kt similarity index 50% rename from tests/e2e-tests/src/test/kotlin/interactions/Delete.kt rename to tests/integration-tests/src/test/kotlin/interactions/Delete.kt index b70dd3da7b..4727abaad3 100644 --- a/tests/e2e-tests/src/test/kotlin/interactions/Delete.kt +++ b/tests/integration-tests/src/test/kotlin/interactions/Delete.kt @@ -1,30 +1,18 @@ package interactions -import common.Environments -import io.ktor.util.* import net.serenitybdd.annotations.Step import net.serenitybdd.screenplay.Actor import net.serenitybdd.screenplay.Tasks import net.serenitybdd.screenplay.rest.abilities.CallAnApi -import net.serenitybdd.screenplay.rest.interactions.RestInteraction - /** * This class is a copy of the class Delete from serenity rest interactions * to add a custom authentication header to the request on-the-fly. */ -open class Delete(private val resource: String) : RestInteraction() { +open class Delete(private val resource: String) : AuthRestInteraction() { @Step("{0} executes a DELETE on the resource #resource") override fun performAs(actor: T) { - val spec = rest() - if (actor!!.name.toLowerCasePreservingASCIIRules().contains("admin")) { - spec.header(Environments.ADMIN_AUTH_HEADER, Environments.ADMIN_AUTH_TOKEN) - } else { - if (Environments.AGENT_AUTH_REQUIRED) { - spec.header(Environments.AGENT_AUTH_HEADER, actor.recall("AUTH_KEY")) - } - } - spec.delete(CallAnApi.`as`(actor).resolve(resource)) + specWithAuthHeaders(actor).delete(CallAnApi.`as`(actor).resolve(resource)) } companion object { diff --git a/tests/e2e-tests/src/test/kotlin/interactions/Get.kt b/tests/integration-tests/src/test/kotlin/interactions/Get.kt similarity index 50% rename from tests/e2e-tests/src/test/kotlin/interactions/Get.kt rename to tests/integration-tests/src/test/kotlin/interactions/Get.kt index f049f85d1f..c62b416cd8 100644 --- a/tests/e2e-tests/src/test/kotlin/interactions/Get.kt +++ b/tests/integration-tests/src/test/kotlin/interactions/Get.kt @@ -1,29 +1,18 @@ package interactions -import common.Environments -import io.ktor.util.* import net.serenitybdd.annotations.Step import net.serenitybdd.screenplay.Actor import net.serenitybdd.screenplay.Tasks import net.serenitybdd.screenplay.rest.abilities.CallAnApi -import net.serenitybdd.screenplay.rest.interactions.RestInteraction /** * This class is a copy of the class Get from serenity rest interactions * to add a custom authentication header to the request on-the-fly. */ -open class Get(private val resource: String) : RestInteraction() { +open class Get(private val resource: String) : AuthRestInteraction() { @Step("{0} executes a GET on the resource #resource") override fun performAs(actor: T) { - val spec = rest() - if (actor!!.name.toLowerCasePreservingASCIIRules().contains("admin")) { - spec.header(Environments.ADMIN_AUTH_HEADER, Environments.ADMIN_AUTH_TOKEN) - } else { - if (Environments.AGENT_AUTH_REQUIRED) { - spec.header(Environments.AGENT_AUTH_HEADER, actor.recall("AUTH_KEY")) - } - } - spec.get(CallAnApi.`as`(actor).resolve(resource)) + specWithAuthHeaders(actor).get(CallAnApi.`as`(actor).resolve(resource)) } companion object { diff --git a/tests/e2e-tests/src/test/kotlin/interactions/Patch.kt b/tests/integration-tests/src/test/kotlin/interactions/Patch.kt similarity index 50% rename from tests/e2e-tests/src/test/kotlin/interactions/Patch.kt rename to tests/integration-tests/src/test/kotlin/interactions/Patch.kt index a48f463301..fbf874bbf5 100644 --- a/tests/e2e-tests/src/test/kotlin/interactions/Patch.kt +++ b/tests/integration-tests/src/test/kotlin/interactions/Patch.kt @@ -1,29 +1,18 @@ package interactions -import common.Environments -import io.ktor.util.* import net.serenitybdd.annotations.Step import net.serenitybdd.screenplay.Actor import net.serenitybdd.screenplay.Tasks import net.serenitybdd.screenplay.rest.abilities.CallAnApi -import net.serenitybdd.screenplay.rest.interactions.RestInteraction /** * This class is a copy of the class Patch from serenity rest interactions * to add a custom authentication header to the request on-the-fly. */ -open class Patch(private val resource: String) : RestInteraction() { +open class Patch(private val resource: String) : AuthRestInteraction() { @Step("{0} executes a PATCH on the resource #resource") override fun performAs(actor: T) { - val spec = rest() - if (actor!!.name.toLowerCasePreservingASCIIRules().contains("admin")) { - spec.header(Environments.ADMIN_AUTH_HEADER, Environments.ADMIN_AUTH_TOKEN) - } else { - if (Environments.AGENT_AUTH_REQUIRED) { - spec.header(Environments.AGENT_AUTH_HEADER, actor.recall("AUTH_KEY")) - } - } - spec.patch(CallAnApi.`as`(actor).resolve(resource)) + specWithAuthHeaders(actor).patch(CallAnApi.`as`(actor).resolve(resource)) } companion object { diff --git a/tests/e2e-tests/src/test/kotlin/interactions/Post.kt b/tests/integration-tests/src/test/kotlin/interactions/Post.kt similarity index 50% rename from tests/e2e-tests/src/test/kotlin/interactions/Post.kt rename to tests/integration-tests/src/test/kotlin/interactions/Post.kt index 2104827f94..9b692867db 100644 --- a/tests/e2e-tests/src/test/kotlin/interactions/Post.kt +++ b/tests/integration-tests/src/test/kotlin/interactions/Post.kt @@ -1,29 +1,18 @@ package interactions -import common.Environments -import io.ktor.util.* import net.serenitybdd.annotations.Step import net.serenitybdd.screenplay.Actor import net.serenitybdd.screenplay.Tasks import net.serenitybdd.screenplay.rest.abilities.CallAnApi -import net.serenitybdd.screenplay.rest.interactions.RestInteraction /** * This class is a copy of the class Post from serenity rest interactions * to add a custom authentication header to the request on-the-fly. */ -open class Post(private val resource: String) : RestInteraction() { +open class Post(private val resource: String) : AuthRestInteraction() { @Step("{0} executes a POST on the resource #resource") override fun performAs(actor: T) { - val spec = rest() - if (actor!!.name.toLowerCasePreservingASCIIRules().contains("admin")) { - spec.header(Environments.ADMIN_AUTH_HEADER, Environments.ADMIN_AUTH_TOKEN) - } else { - if (Environments.AGENT_AUTH_REQUIRED) { - spec.header(Environments.AGENT_AUTH_HEADER, actor.recall("AUTH_KEY")) - } - } - spec.post(CallAnApi.`as`(actor).resolve(resource)) + specWithAuthHeaders(actor).post(CallAnApi.`as`(actor).resolve(resource)) } companion object { diff --git a/tests/e2e-tests/src/test/kotlin/interactions/Put.kt b/tests/integration-tests/src/test/kotlin/interactions/Put.kt similarity index 50% rename from tests/e2e-tests/src/test/kotlin/interactions/Put.kt rename to tests/integration-tests/src/test/kotlin/interactions/Put.kt index 9326f9b0e5..4c2eeeeaa9 100644 --- a/tests/e2e-tests/src/test/kotlin/interactions/Put.kt +++ b/tests/integration-tests/src/test/kotlin/interactions/Put.kt @@ -1,30 +1,18 @@ package interactions -import common.Environments -import io.ktor.util.* import net.serenitybdd.annotations.Step import net.serenitybdd.screenplay.Actor import net.serenitybdd.screenplay.Tasks import net.serenitybdd.screenplay.rest.abilities.CallAnApi -import net.serenitybdd.screenplay.rest.interactions.RestInteraction - /** * This class is a copy of the class Put from serenity rest interactions * to add a custom authentication header to the request on-the-fly. */ -open class Put(private val resource: String) : RestInteraction() { +open class Put(private val resource: String) : AuthRestInteraction() { @Step("{0} executes a PUT on the resource #resource") override fun performAs(actor: T) { - val spec = rest() - if (actor!!.name.toLowerCasePreservingASCIIRules().contains("admin")) { - spec.header(Environments.ADMIN_AUTH_HEADER, Environments.ADMIN_AUTH_TOKEN) - } else { - if (Environments.AGENT_AUTH_REQUIRED) { - spec.header(Environments.AGENT_AUTH_HEADER, actor.recall("AUTH_KEY")) - } - } - spec.put(CallAnApi.`as`(actor).resolve(resource)) + specWithAuthHeaders(actor).put(CallAnApi.`as`(actor).resolve(resource)) } companion object { diff --git a/tests/e2e-tests/src/test/kotlin/runners/E2eTestsRunner.kt b/tests/integration-tests/src/test/kotlin/runners/IntegrationTestsRunner.kt similarity index 71% rename from tests/e2e-tests/src/test/kotlin/runners/E2eTestsRunner.kt rename to tests/integration-tests/src/test/kotlin/runners/IntegrationTestsRunner.kt index daab43ba4f..4613f9d205 100644 --- a/tests/e2e-tests/src/test/kotlin/runners/E2eTestsRunner.kt +++ b/tests/integration-tests/src/test/kotlin/runners/IntegrationTestsRunner.kt @@ -6,14 +6,14 @@ import org.junit.runner.RunWith @CucumberOptions( features = [ - "src/test/resources/features", + "src/test/resources/features" ], glue = ["features"], snippets = CucumberOptions.SnippetType.CAMELCASE, plugin = [ "pretty", - "json:target/serenity-reports/cucumber_report.json", - ], + "json:target/serenity-reports/cucumber_report.json" + ] ) @RunWith(CucumberWithSerenity::class) -class E2eTestsRunner +class IntegrationTestsRunner diff --git a/tests/integration-tests/src/test/resources/cucumber.properties b/tests/integration-tests/src/test/resources/cucumber.properties new file mode 100644 index 0000000000..170a3740bb --- /dev/null +++ b/tests/integration-tests/src/test/resources/cucumber.properties @@ -0,0 +1,2 @@ +cucumber.publish.quiet=true +cucumber.object-factory=io.iohk.atala.automation.serenity.objectfactory.AtalaObjectFactory \ No newline at end of file diff --git a/tests/e2e-tests/src/test/resources/features/connection/connection.feature b/tests/integration-tests/src/test/resources/features/connection/connection.feature similarity index 82% rename from tests/e2e-tests/src/test/resources/features/connection/connection.feature rename to tests/integration-tests/src/test/resources/features/connection/connection.feature index 6254ba419f..360a135220 100644 --- a/tests/e2e-tests/src/test/resources/features/connection/connection.feature +++ b/tests/integration-tests/src/test/resources/features/connection/connection.feature @@ -1,9 +1,7 @@ Feature: Agents connection -@TEST_ATL-3834 Scenario: Establish a connection between two agents When Acme generates a connection invitation to Bob - And Bob receives the connection invitation from Acme And Bob sends a connection request to Acme And Acme receives the connection request and sends back the response And Bob receives the connection response diff --git a/tests/e2e-tests/src/test/resources/features/issue_credentials/issue_credentials.feature b/tests/integration-tests/src/test/resources/features/credentials/issue_credentials.feature similarity index 96% rename from tests/e2e-tests/src/test/resources/features/issue_credentials/issue_credentials.feature rename to tests/integration-tests/src/test/resources/features/credentials/issue_credentials.feature index 527e4fa667..94a1609753 100644 --- a/tests/e2e-tests/src/test/resources/features/issue_credentials/issue_credentials.feature +++ b/tests/integration-tests/src/test/resources/features/credentials/issue_credentials.feature @@ -1,7 +1,6 @@ @RFC0453 @AIP20 Feature: Issue Credentials Protocol -@TEST_ATL-3849 Scenario: Issuing credential with published PRISM DID to unpublished PRISM DID Given Acme and Bob have an existing connection When Acme creates unpublished DID @@ -12,7 +11,6 @@ Scenario: Issuing credential with published PRISM DID to unpublished PRISM DID And Acme issues the credential Then Bob receives the issued credential -@TEST_ATL-3894 Scenario: Issuing credential with unpublished PRISM DID to unpublished PRISM DID Given Acme and Bob have an existing connection When Acme creates unpublished DID diff --git a/tests/integration-tests/src/test/resources/features/did/create_did.feature b/tests/integration-tests/src/test/resources/features/did/create_did.feature new file mode 100644 index 0000000000..f987c46390 --- /dev/null +++ b/tests/integration-tests/src/test/resources/features/did/create_did.feature @@ -0,0 +1,10 @@ +Feature: Create and publish DID + +Scenario: Create PRISM DID + When Acme creates PRISM DID + Then He sees PRISM DID was created successfully + +Scenario: Successfully publish DID to ledger + When Acme creates unpublished DID + And He publishes DID to ledger + Then He resolves DID document corresponds to W3C standard diff --git a/tests/e2e-tests/src/test/resources/features/did_registrar/deactivate_did.feature b/tests/integration-tests/src/test/resources/features/did/deactivate_did.feature similarity index 92% rename from tests/e2e-tests/src/test/resources/features/did_registrar/deactivate_did.feature rename to tests/integration-tests/src/test/resources/features/did/deactivate_did.feature index 3c80b55c14..9f98dfa7a3 100644 --- a/tests/e2e-tests/src/test/resources/features/did_registrar/deactivate_did.feature +++ b/tests/integration-tests/src/test/resources/features/did/deactivate_did.feature @@ -1,7 +1,6 @@ @DLT Feature: Deactivate DID -@TEST_ATL-3837 Scenario: Deactivate DID Given Acme have published PRISM DID When Acme deactivates PRISM DID diff --git a/tests/e2e-tests/src/test/resources/features/did_registrar/listing_did.feature b/tests/integration-tests/src/test/resources/features/did/listing_did.feature similarity index 92% rename from tests/e2e-tests/src/test/resources/features/did_registrar/listing_did.feature rename to tests/integration-tests/src/test/resources/features/did/listing_did.feature index 14bffb3eab..cddac4ecd2 100644 --- a/tests/e2e-tests/src/test/resources/features/did_registrar/listing_did.feature +++ b/tests/integration-tests/src/test/resources/features/did/listing_did.feature @@ -1,6 +1,5 @@ Feature: DID listing -@TEST_ATL-3841 Scenario: Listing multiple PRISM DIDs Given Acme creates 5 PRISM DIDs When He lists all PRISM DIDs diff --git a/tests/e2e-tests/src/test/resources/features/did_registrar/update_did.feature b/tests/integration-tests/src/test/resources/features/did/update_did.feature similarity index 92% rename from tests/e2e-tests/src/test/resources/features/did_registrar/update_did.feature rename to tests/integration-tests/src/test/resources/features/did/update_did.feature index 906d6b1472..349ae0628a 100644 --- a/tests/e2e-tests/src/test/resources/features/did_registrar/update_did.feature +++ b/tests/integration-tests/src/test/resources/features/did/update_did.feature @@ -2,34 +2,28 @@ Feature: Update DID Background: Published DID is created - #@PRECOND_ATL-3843 Given Acme have published PRISM DID -@TEST_ATL-3844 Scenario: Update PRISM DID by adding new services When Acme updates PRISM DID with new services And He submits PRISM DID update operation Then He sees PRISM DID was successfully updated with new services -@TEST_ATL-3845 Scenario: Update PRISM DID by removing services When Acme updates PRISM DID by removing services And He submits PRISM DID update operation Then He sees PRISM DID was successfully updated by removing services -@TEST_ATL-3846 Scenario: Update PRISM DID by updating services When Acme updates PRISM DID by updating services And He submits PRISM DID update operation Then He sees PRISM DID was successfully updated by updating services -@TEST_ATL-3847 Scenario: Update PRISM DID by adding new keys When Acme updates PRISM DID by adding new keys And He submits PRISM DID update operation Then He sees PRISM DID was successfully updated with new keys -@TEST_ATL-3848 Scenario: Update PRISM DID by removing keys When Acme updates PRISM DID by removing keys And He submits PRISM DID update operation diff --git a/tests/e2e-tests/src/test/resources/features/multitenancy/wallets.feature b/tests/integration-tests/src/test/resources/features/multitenancy/wallets.feature similarity index 100% rename from tests/e2e-tests/src/test/resources/features/multitenancy/wallets.feature rename to tests/integration-tests/src/test/resources/features/multitenancy/wallets.feature diff --git a/tests/e2e-tests/src/test/resources/features/present_proof/present_proof.feature b/tests/integration-tests/src/test/resources/features/proofs/present_proof.feature similarity index 95% rename from tests/e2e-tests/src/test/resources/features/present_proof/present_proof.feature rename to tests/integration-tests/src/test/resources/features/proofs/present_proof.feature index dcb0865754..406908d490 100644 --- a/tests/e2e-tests/src/test/resources/features/present_proof/present_proof.feature +++ b/tests/integration-tests/src/test/resources/features/proofs/present_proof.feature @@ -1,6 +1,5 @@ Feature: Present Proof Protocol -@TEST_ATL-3850 Scenario: Holder presents credential proof to verifier Given Faber and Bob have an existing connection And Bob has an issued credential from Acme @@ -9,7 +8,6 @@ Scenario: Holder presents credential proof to verifier And Bob makes the presentation of the proof to Faber Then Faber has the proof verified -@TEST_ATL-3881 Scenario: Verifier rejects holder proof Given Faber and Bob have an existing connection And Bob has an issued credential from Acme @@ -18,7 +16,6 @@ Scenario: Verifier rejects holder proof And Bob rejects the proof Then Bob sees the proof is rejected -@TEST_ATL-4968 Scenario: Holder presents proof to verifier which is the issuer itself Given Acme and Bob have an existing connection And Bob has an issued credential from Acme diff --git a/tests/e2e-tests/src/test/resources/features/credential_schemas/credential_schemas.feature b/tests/integration-tests/src/test/resources/features/schemas/credential_schemas.feature similarity index 92% rename from tests/e2e-tests/src/test/resources/features/credential_schemas/credential_schemas.feature rename to tests/integration-tests/src/test/resources/features/schemas/credential_schemas.feature index 11a73b6600..1cd58174fc 100644 --- a/tests/e2e-tests/src/test/resources/features/credential_schemas/credential_schemas.feature +++ b/tests/integration-tests/src/test/resources/features/schemas/credential_schemas.feature @@ -1,12 +1,10 @@ Feature: Credential schemas -@TEST_ATL-3835 Scenario: Successful schema creation When Acme creates unpublished DID And Acme creates a new credential schema Then He sees new credential schema is available -@TEST_ATL-3836 Scenario Outline: Multiple schema creation When Acme creates unpublished DID And Acme creates new schemas diff --git a/tests/e2e-tests/src/test/resources/features/system/health_endpoint.feature b/tests/integration-tests/src/test/resources/features/system/health_endpoint.feature similarity index 93% rename from tests/e2e-tests/src/test/resources/features/system/health_endpoint.feature rename to tests/integration-tests/src/test/resources/features/system/health_endpoint.feature index ba03a7903c..ea5229aee6 100644 --- a/tests/e2e-tests/src/test/resources/features/system/health_endpoint.feature +++ b/tests/integration-tests/src/test/resources/features/system/health_endpoint.feature @@ -1,7 +1,6 @@ @system @smoke Feature: Agent Health Endpoint -@TEST_ATL-3833 Scenario: The runtime version can be retrieved from the Health Endpoint When Acme makes a request to the health endpoint Then Acme knows what version of the service is running diff --git a/tests/e2e-tests/src/test/resources/features/verification_policies/verification_policies.feature b/tests/integration-tests/src/test/resources/features/verificationpolicies/verification_policies.feature similarity index 70% rename from tests/e2e-tests/src/test/resources/features/verification_policies/verification_policies.feature rename to tests/integration-tests/src/test/resources/features/verificationpolicies/verification_policies.feature index 0b14be8b8a..e72204109f 100644 --- a/tests/e2e-tests/src/test/resources/features/verification_policies/verification_policies.feature +++ b/tests/integration-tests/src/test/resources/features/verificationpolicies/verification_policies.feature @@ -1,4 +1,3 @@ -@TEST_ATL-2487 Feature: Verification Policies Scenario: Successful verification policy creation @@ -6,5 +5,3 @@ Scenario: Successful verification policy creation Then He sees new verification policy is available When He updates a new verification policy Then He sees the updated verification policy is available - When He updates a new verification policy - Then He sees the updated verification policy is available diff --git a/tests/e2e-tests/src/test/resources/log4j.properties b/tests/integration-tests/src/test/resources/log4j.properties similarity index 100% rename from tests/e2e-tests/src/test/resources/log4j.properties rename to tests/integration-tests/src/test/resources/log4j.properties diff --git a/tests/e2e-tests/src/test/resources/scripts/add_test_execution.py b/tests/integration-tests/src/test/resources/scripts/add_test_execution.py similarity index 100% rename from tests/e2e-tests/src/test/resources/scripts/add_test_execution.py rename to tests/integration-tests/src/test/resources/scripts/add_test_execution.py diff --git a/tests/e2e-tests/src/test/resources/scripts/requirements.txt b/tests/integration-tests/src/test/resources/scripts/requirements.txt similarity index 100% rename from tests/e2e-tests/src/test/resources/scripts/requirements.txt rename to tests/integration-tests/src/test/resources/scripts/requirements.txt diff --git a/tests/integration-tests/src/test/resources/tests.conf b/tests/integration-tests/src/test/resources/tests.conf new file mode 100644 index 0000000000..bd1065b8e6 --- /dev/null +++ b/tests/integration-tests/src/test/resources/tests.conf @@ -0,0 +1,32 @@ +global { + auth_required = true + auth_header = "${AUTH_HEADER:-apikey}" + admin_auth_header = "${ADMIN_AUTH_HEADER:-x-admin-api-key}" + admin_apikey = "${ADMIN_API_KEY:-admin}" +} + +admin { + url = "${ADMIN_AGENT_URL:-http://localhost:8080/prism-agent}" + apikey = "${ISSUER_API_KEY:-${random.string(16)}}" +} + +issuer { + url = "${ISSUER_AGENT_URL:-http://localhost:8080/prism-agent}" + webhook_url = "${ISSUER_WEBHOOK_URL:-http://host.docker.internal:9955}" + apikey = "${ISSUER_API_KEY:-${random.string(16)}}" + multi-tenant = true +} + +verifier { + url = "${VERIFIER_AGENT_URL:-http://localhost:8080/prism-agent}" + webhook_url = "${VERIFIER_WEBHOOK_URL:-http://host.docker.internal:9957}" + apikey = "${VERIFIER_API_KEY:-${random.string(16)}}" + multi-tenant = true +} + +holder { + url = "${HOLDER_AGENT_URL:-http://localhost:8090/prism-agent}" + webhook_url = "${HOLDER_WEBHOOK_URL:-http://host.docker.internal:9956}" + apikey = "${HOLDER_API_KEY:-default}" + multi-tenant = false +}