diff --git a/tests/integration-tests/src/test/kotlin/steps/connectionless/ConnectionLessSteps.kt b/tests/integration-tests/src/test/kotlin/steps/connectionless/ConnectionLessSteps.kt new file mode 100644 index 0000000000..627281d15a --- /dev/null +++ b/tests/integration-tests/src/test/kotlin/steps/connectionless/ConnectionLessSteps.kt @@ -0,0 +1,81 @@ +package steps.connectionless + +import interactions.Post +import interactions.body +import io.cucumber.java.en.* +import io.iohk.atala.automation.extensions.get +import io.iohk.atala.automation.serenity.ensure.Ensure +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.hyperledger.identus.client.models.* + +class ConnectionLessSteps { + + @When("{actor} creates a {string} credential offer invitation with {string} form DID") + fun inviterGeneratesACredentialOfferInvitation(issuer: Actor, credentialFormat: String, didForm: String) { + val claims = linkedMapOf( + "firstName" to "Automation", + "lastName" to "Execution", + "email" to "email@example.com", + ) + val did: String = if (didForm == "short") { + issuer.recall("shortFormDid") + } else { + issuer.recall("longFormDid") + } + val credentialOfferRequest = CreateIssueCredentialRecordRequest( + claims = claims, + issuingDID = did, + validityPeriod = 3600.0, + credentialFormat = credentialFormat, + automaticIssuance = false, + goalCode = "issue-vc", + goal = "To issue a Faber College Graduate credential", + ) + + issuer.attemptsTo( + Post.to("/issue-credentials/credential-offers/invitation").body(credentialOfferRequest), + ) + + val credentialRecord = SerenityRest.lastResponse().get() + + issuer.attemptsTo( + Ensure.thatTheLastResponse().statusCode().isEqualTo(SC_CREATED), + Ensure.that(credentialRecord.goalCode!!).isEqualTo("issue-vc"), + Ensure.that(credentialRecord.protocolState).isEqualTo(IssueCredentialRecord.ProtocolState.INVITATION_GENERATED), + Ensure.that(credentialRecord.role).isEqualTo(IssueCredentialRecord.Role.ISSUER), + ) + + // Acme remembers connection to send it out of band to Bob + issuer.remember("credentialRecord", credentialRecord) + issuer.remember("thid", credentialRecord.thid) + } + + @And("{actor} accepts the credential offer invitation from {actor}") + fun holderAcceptsCredentialOfferInvitation(holder: Actor, issuer: Actor) { + // Bob accepts connection using achieved out-of-band invitation + val credentialOfferInvitationRecord = issuer.recall("credentialRecord") + holder.attemptsTo( + Post.to("/issue-credentials/credential-offers/accept-invitation") + .with { + it.body( + AcceptCredentialOfferInvitation( + credentialOfferInvitationRecord.invitation?.invitationUrl?.split("=")?.getOrNull(1) + ?: throw IllegalStateException("Invalid invitation URL format"), + ), + ) + }, + ) + val holderIssueCredentialRecord = SerenityRest.lastResponse().get() + + holder.attemptsTo( + Ensure.thatTheLastResponse().statusCode().isEqualTo(SC_OK), + Ensure.that(holderIssueCredentialRecord.protocolState).isEqualTo(IssueCredentialRecord.ProtocolState.OFFER_RECEIVED), + Ensure.that(holderIssueCredentialRecord.role).isEqualTo(IssueCredentialRecord.Role.HOLDER), + ) + holder.remember("recordId", holderIssueCredentialRecord.recordId) + holder.remember("thid", holderIssueCredentialRecord.thid) + } +} diff --git a/tests/integration-tests/src/test/resources/features/credential/jwt/issuance.feature b/tests/integration-tests/src/test/resources/features/credential/jwt/issuance.feature index 2e9689f772..79b541c1f6 100644 --- a/tests/integration-tests/src/test/resources/features/credential/jwt/issuance.feature +++ b/tests/integration-tests/src/test/resources/features/credential/jwt/issuance.feature @@ -1,6 +1,7 @@ @jwt @issuance Feature: Issue JWT credential + Scenario: Issuing jwt credential with published PRISM DID Given Issuer and Holder have an existing connection And Issuer has a published DID for JWT @@ -39,3 +40,12 @@ Feature: Issue JWT credential And Holder accepts jwt credential offer And Issuer issues the credential Then Holder receives the issued credential + + Scenario: Connectionless issuance of JWT credential using OOB invitation + Given Issuer has a published DID for JWT + And Holder has an unpublished DID for JWT + When Issuer creates a "JWT" credential offer invitation with "short" form DID + And Holder accepts the credential offer invitation from Issuer + And Holder accepts jwt credential offer + And Issuer issues the credential + Then Holder receives the issued credential \ No newline at end of file diff --git a/tests/integration-tests/src/test/resources/features/credential/sdjwt/issuance.feature b/tests/integration-tests/src/test/resources/features/credential/sdjwt/issuance.feature index 29af552150..a96c24f04f 100644 --- a/tests/integration-tests/src/test/resources/features/credential/sdjwt/issuance.feature +++ b/tests/integration-tests/src/test/resources/features/credential/sdjwt/issuance.feature @@ -23,6 +23,17 @@ Feature: Issue SD-JWT credential Then Holder receives the issued credential Then Holder checks the sd-jwt credential contents with holder binding + Scenario: Connectionless issuance of sd-jwt credential with holder binding + And Issuer has a published DID for SD_JWT + And Holder has an unpublished DID for SD_JWT + When Issuer creates a "SDJWT" credential offer invitation with "short" form DID + And Holder accepts the credential offer invitation from Issuer + And Holder accepts credential offer for sd-jwt with 'auth-1' key binding + And Issuer issues the credential + Then Holder receives the issued credential + Then Holder checks the sd-jwt credential contents with holder binding + + # Scenario: Issuing sd-jwt with wrong algorithm # Given Issuer and Holder have an existing connection # When Issuer prepares a custom PRISM DID