diff --git a/.github/workflows/integrationtests.yml b/.github/workflows/bdd-integration-tests.yml similarity index 93% rename from .github/workflows/integrationtests.yml rename to .github/workflows/bdd-integration-tests.yml index 0fc4159460..ed466ba5ff 100644 --- a/.github/workflows/integrationtests.yml +++ b/.github/workflows/bdd-integration-tests.yml @@ -1,4 +1,4 @@ -name: acapy-integration-tests +name: acapy-bdd-integration-tests on: schedule: @@ -20,7 +20,7 @@ defaults: jobs: test: runs-on: ubuntu-latest - if: (github.event_name == 'pull_request' && github.event.pull_request.draft == false && github.repository == 'hyperledger/aries-cloudagent-python') || (github.event_name != 'pull_request') + if: (github.event_name == 'pull_request' && github.event.pull_request.draft == false && github.repository == 'jamshale/aries-cloudagent-python') || (github.event_name != 'pull_request') outputs: is_release: ${{ steps.check_if_release.outputs.is_release }} steps: diff --git a/.github/workflows/bdd-interop-tests.yml b/.github/workflows/bdd-interop-tests.yml new file mode 100644 index 0000000000..ce86eb257a --- /dev/null +++ b/.github/workflows/bdd-interop-tests.yml @@ -0,0 +1,47 @@ +name: acapy-bdd-interop-tests + +on: + pull_request: + branches: + - main + types: [opened, synchronize, reopened, ready_for_review] + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +defaults: + run: + shell: bash + +jobs: + test: + runs-on: ubuntu-latest + if: (github.event_name == 'pull_request' && github.event.pull_request.draft == false && github.repository == 'jamshale/aries-cloudagent-python') || (github.event_name != 'pull_request') + outputs: + is_release: ${{ steps.check_if_release.outputs.is_release }} + steps: + - name: checkout-acapy + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Request GitHub API for PR data + uses: octokit/request-action@v2.x + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + id: get_pr_data + with: + route: GET /repos/${{ github.event.repository.full_name }}/pulls/${{ github.event.number }} + - name: Run BDD Interop Tests + run: | + # Get AATH + git clone https://github.com/hyperledger/aries-agent-test-harness.git + echo ${{ fromJson(steps.get_pr_data.outputs.data).head.repo.html_url }} + echo ${{ fromJson(steps.get_pr_data.outputs.data).head.ref }} + ls -a + sed -i 's|@git+https://github.com/hyperledger/aries-cloudagent-python@main|@git+${{ fromJson(steps.get_pr_data.outputs.data).head.repo.html_url }}@${{ fromJson(steps.get_pr_data.outputs.data).head.ref }}|g' ./aries-agent-test-harness/aries-backchannels/acapy/requirements-main.txt + cat aries-agent-test-harness/aries-backchannels/acapy/requirements-main.txt + cd aries-agent-test-harness + ./manage build -a acapy-main + NO_TTY=1 LEDGER_URL_CONFIG=http://test.bcovrin.vonx.io TAILS_SERVER_URL_CONFIG=https://tails.vonx.io ./manage run -d acapy-main -t @AcceptanceTest -t ~@wip -t ~@T004-RFC0211 -t ~@DidMethod_orb -t ~@Transport_NoHttpOutbound + diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml new file mode 100644 index 0000000000..0d6cacc54f --- /dev/null +++ b/.github/workflows/integration-tests.yml @@ -0,0 +1,39 @@ +name: acapy-integration-tests + +on: + pull_request: + branches: + - main + types: [opened, synchronize, reopened, ready_for_review] + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +defaults: + run: + shell: bash + +jobs: + test: + runs-on: ubuntu-latest + if: (github.event_name == 'pull_request' && github.event.pull_request.draft == false && github.repository == 'jamshale/aries-cloudagent-python') || (github.event_name != 'pull_request') + steps: + - name: checkout-acapy + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Install poetry + run: pipx install poetry + id: setup-poetry + - uses: actions/setup-python@v5 + with: + python-version: "3.12" + cache: "poetry" + - name: Run Integration tests + run: | + # Build the docker image for testing + docker build -t acapy-test -f docker/Dockerfile.run . + cd integration-tests + poetry install --no-root + poetry run pytest -m examples diff --git a/demo/features/0160-connection.feature b/demo/features/0160-connection.feature index a68e651711..7d2df7c2d2 100644 --- a/demo/features/0160-connection.feature +++ b/demo/features/0160-connection.feature @@ -12,7 +12,7 @@ Feature: RFC 0160 Aries agent connection functions Then "Acme" has an active connection And "Bob" has an active connection - @PR @Release @UnqualifiedDids + @UnqualifiedDids Examples: | Acme_capabilities | Acme_extra | Bob_capabilities | Bob_extra | | --public-did --did-exchange --emit-did-peer-2 | | --did-exchange --emit-did-peer-2 | | @@ -40,7 +40,7 @@ Feature: RFC 0160 Aries agent connection functions | --did-exchange --emit-did-peer-4 | | --emit-did-peer-4 | | | --did-exchange --reuse-connections --emit-did-peer-4 | | --reuse-connections --emit-did-peer-4 | | - @PR @Release @MultiUseConnectionReuse + @MultiUseConnectionReuse Examples: | Acme_capabilities | Acme_extra | Bob_capabilities | Bob_extra | | --did-exchange --multi-use-invitations --emit-did-peer-2 | | --emit-did-peer-2 | | @@ -56,7 +56,7 @@ Feature: RFC 0160 Aries agent connection functions | --public-did --did-exchange --multi-use-invitations --emit-did-peer-4 | | --did-exchange --emit-did-peer-2 | | | --public-did --did-exchange --multi-use-invitations --reuse-connections --emit-did-peer-2 | | --did-exchange --reuse-connections --emit-did-peer-4 | | - @PR @Release @WalletType_Askar_AnonCreds + @WalletType_Askar_AnonCreds Examples: | Acme_capabilities | Acme_extra | Bob_capabilities | Bob_extra | | --public-did --did-exchange --wallet-type askar-anoncreds --emit-did-peer-2 | | --did-exchange --wallet-type askar-anoncreds --emit-did-peer-2 | | diff --git a/demo/features/0453-issue-credential.feature b/demo/features/0453-issue-credential.feature index 9a6aae587b..9a19ca025a 100644 --- a/demo/features/0453-issue-credential.feature +++ b/demo/features/0453-issue-credential.feature @@ -12,37 +12,37 @@ Feature: RFC 0453 Aries agent issue credential When "Acme" offers a credential with data Then "Bob" has the credential issued - @Release @WalletType_Askar @BasicTest + @WalletType_Askar @BasicTest Examples: | Acme_capabilities | Bob_capabilities | Schema_name | Credential_data | Acme_extra | Bob_extra | | --public-did --did-exchange | --did-exchange | driverslicense | Data_DL_NormalizedValues | | | - @Release @WalletType_Askar @AltTests + @WalletType_Askar @AltTests Examples: | Acme_capabilities | Bob_capabilities | Schema_name | Credential_data | Acme_extra | Bob_extra | | --public-did | | driverslicense | Data_DL_NormalizedValues | | | | --public-did --mediation | --mediation | driverslicense | Data_DL_NormalizedValues | | | | --public-did --multitenant | --multitenant --log-file | driverslicense | Data_DL_NormalizedValues | | | - @Release @WalletType_Askar_AnonCreds @BasicTest @cred_type_vc_di + @WalletType_Askar_AnonCreds @BasicTest @cred_type_vc_di Examples: | Acme_capabilities | Bob_capabilities | Schema_name | Credential_data | Acme_extra | Bob_extra | | --public-did --wallet-type askar-anoncreds | --wallet-type askar-anoncreds | driverslicense | Data_DL_NormalizedValues | | | | --public-did --wallet-type askar-anoncreds --cred-type vc_di | --wallet-type askar-anoncreds | driverslicense | Data_DL_NormalizedValues | | | - @Release @WalletType_Askar_AnonCreds @AltTests + @WalletType_Askar_AnonCreds @AltTests Examples: | Acme_capabilities | Bob_capabilities | Schema_name | Credential_data | Acme_extra | Bob_extra | | --public-did --wallet-type askar-anoncreds | | driverslicense | Data_DL_NormalizedValues | | | | --public-did | --wallet-type askar-anoncreds | driverslicense | Data_DL_NormalizedValues | | | - @PR @Release @WalletType_Askar @ConnectionTests + @WalletType_Askar @ConnectionTests Examples: | Acme_capabilities | Bob_capabilities | Schema_name | Credential_data | Acme_extra | Bob_extra | | --did-exchange --emit-did-peer-4 | --did-exchange --emit-did-peer-4 | driverslicense | Data_DL_NormalizedValues | | | | --did-exchange --reuse-connections --emit-did-peer-4 | --did-exchange --reuse-connections --emit-did-peer-4 | driverslicense | Data_DL_NormalizedValues | | | - @PR @Release @WalletType_Askar_AnonCreds @ConnectionTests + @WalletType_Askar_AnonCreds @ConnectionTests Examples: | Acme_capabilities | Bob_capabilities | Schema_name | Credential_data | Acme_extra | Bob_extra | | --did-exchange --wallet-type askar-anoncreds --emit-did-peer-4 | --did-exchange --wallet-type askar-anoncreds --emit-did-peer-4 | driverslicense | Data_DL_NormalizedValues | | | @@ -82,7 +82,7 @@ Feature: RFC 0453 Aries agent issue credential And "Acme" offers and deletes a credential with data Then "Bob" has the exchange abandoned - @PR @Release @WalletType_Askar + @WalletType_Askar Examples: | Acme_capabilities | Bob_capabilities | Schema_name | Credential_data | | --public-did | | driverslicense | Data_DL_NormalizedValues | @@ -110,7 +110,7 @@ Feature: RFC 0453 Aries agent issue credential And "Acme" is ready to issue a credential for When "Bob" requests a credential with data from "Acme" it fails - @PR @Release @WalletType_Askar + @WalletType_Askar Examples: | Acme_capabilities | Bob_capabilities | Schema_name | Credential_data | | --public-did | | driverslicense | Data_DL_NormalizedValues | @@ -141,13 +141,13 @@ Feature: RFC 0453 Aries agent issue credential Then "Bob" has the json-ld credential issued And "Acme" has the exchange completed - @PR @Release @WalletType_Askar + @WalletType_Askar Examples: | Acme_capabilities | Bob_capabilities | Schema_name | Credential_data | Key_type | Sig_type | | --public-did --cred-type json-ld | | driverslicense | Data_DL_NormalizedValues | ed25519 | Ed25519Signature2018 | | --public-did --cred-type json-ld | | driverslicense | Data_DL_NormalizedValues | ed25519 | Ed25519Signature2020 | - @PR @Release @WalletType_Askar @BBS + @WalletType_Askar @BBS Examples: | Acme_capabilities | Bob_capabilities | Schema_name | Credential_data | Key_type | Sig_type | | --public-did --cred-type json-ld | | driverslicense | Data_DL_NormalizedValues | bls12381g2 | BbsBlsSignature2020 | @@ -193,18 +193,18 @@ Feature: RFC 0453 Aries agent issue credential When "Acme" offers "Bob" a json-ld credential with data and Then "Bob" has the json-ld credential issued - @PR @Release @WalletType_Askar + @WalletType_Askar Examples: | Acme_capabilities | Bob_capabilities | Schema_name | Credential_data | Key_type | Sig_type | | --public-did --cred-type json-ld | | driverslicense | Data_DL_NormalizedValues | ed25519 | Ed25519Signature2018 | | --public-did --cred-type json-ld | | driverslicense | Data_DL_NormalizedValues | ed25519 | Ed25519Signature2020 | - @PR @Release @WalletType_Askar @BBS + @WalletType_Askar @BBS Examples: | Acme_capabilities | Bob_capabilities | Schema_name | Credential_data | Key_type | Sig_type | | --public-did --cred-type json-ld | | driverslicense | Data_DL_NormalizedValues | bls12381g2 | BbsBlsSignature2020 | - @Release @WalletType_Askar + @WalletType_Askar Examples: | Acme_capabilities | Bob_capabilities | Schema_name | Credential_data | Key_type | Sig_type | | --public-did --cred-type json-ld --did-exchange | --did-exchange | driverslicense | Data_DL_NormalizedValues | ed25519 | Ed25519Signature2018 | @@ -214,14 +214,14 @@ Feature: RFC 0453 Aries agent issue credential | --public-did --cred-type json-ld --mediation | --mediation | driverslicense | Data_DL_NormalizedValues | ed25519 | Ed25519Signature2020 | | --public-did --cred-type json-ld --multitenant --log-file | --multitenant | driverslicense | Data_DL_NormalizedValues | ed25519 | Ed25519Signature2020 | - @Release @WalletType_Askar @BBS + @WalletType_Askar @BBS Examples: | Acme_capabilities | Bob_capabilities | Schema_name | Credential_data | Key_type | Sig_type | | --public-did --cred-type json-ld --did-exchange | --did-exchange | driverslicense | Data_DL_NormalizedValues | bls12381g2 | BbsBlsSignature2020 | | --public-did --cred-type json-ld --mediation | --mediation | driverslicense | Data_DL_NormalizedValues | bls12381g2 | BbsBlsSignature2020 | | --public-did --cred-type json-ld --multitenant --log-file | --multitenant | driverslicense | Data_DL_NormalizedValues | bls12381g2 | BbsBlsSignature2020 | - @Release @WalletType_Askar_AnonCreds + @WalletType_Askar_AnonCreds Examples: | Acme_capabilities | Bob_capabilities | Schema_name | Credential_data | Key_type | Sig_type | | --public-did --cred-type json-ld --wallet-type askar-anoncreds | --wallet-type askar-anoncreds | driverslicense | Data_DL_NormalizedValues | ed25519 | Ed25519Signature2018 | @@ -229,7 +229,7 @@ Feature: RFC 0453 Aries agent issue credential | --public-did --cred-type json-ld --wallet-type askar-anoncreds | --wallet-type askar-anoncreds | driverslicense | Data_DL_NormalizedValues | ed25519 | Ed25519Signature2020 | | --public-did --cred-type json-ld --did-exchange --wallet-type askar-anoncreds | --did-exchange --wallet-type askar-anoncreds | driverslicense | Data_DL_NormalizedValues | ed25519 | Ed25519Signature2020 | - @Release @WalletType_Askar_AnonCreds @BBS + @WalletType_Askar_AnonCreds @BBS Examples: | Acme_capabilities | Bob_capabilities | Schema_name | Credential_data | Key_type | Sig_type | | --public-did --cred-type json-ld --wallet-type askar-anoncreds | --wallet-type askar-anoncreds | driverslicense | Data_DL_NormalizedValues | bls12381g2 | BbsBlsSignature2020 | @@ -262,18 +262,18 @@ Feature: RFC 0453 Aries agent issue credential When "Bob" requests a json-ld credential with data from "Acme" with Then "Bob" has the json-ld credential issued - @PR @Release @WalletType_Askar + @WalletType_Askar Examples: | Acme_capabilities | Bob_capabilities | Schema_name | Credential_data | Key_type | Sig_type | | --public-did --cred-type json-ld | | driverslicense | Data_DL_NormalizedValues | ed25519 | Ed25519Signature2018 | | --public-did --cred-type json-ld | | driverslicense | Data_DL_NormalizedValues | ed25519 | Ed25519Signature2020 | - @PR @Release @WalletType_Askar @BBS + @WalletType_Askar @BBS Examples: | Acme_capabilities | Bob_capabilities | Schema_name | Credential_data | Key_type | Sig_type | | --public-did --cred-type json-ld | | driverslicense | Data_DL_NormalizedValues | bls12381g2 | BbsBlsSignature2020 | - @Release @WalletType_Askar + @WalletType_Askar Examples: | Acme_capabilities | Bob_capabilities | Schema_name | Credential_data | Key_type | Sig_type | | --public-did --cred-type json-ld --did-exchange | --did-exchange | driverslicense | Data_DL_NormalizedValues | ed25519 | Ed25519Signature2018 | @@ -283,20 +283,20 @@ Feature: RFC 0453 Aries agent issue credential | --public-did --cred-type json-ld --mediation | --mediation | driverslicense | Data_DL_NormalizedValues | ed25519 | Ed25519Signature2020 | | --public-did --cred-type json-ld --multitenant | --multitenant | driverslicense | Data_DL_NormalizedValues | ed25519 | Ed25519Signature2020 | - @Release @WalletType_Askar @BBS + @WalletType_Askar @BBS Examples: | Acme_capabilities | Bob_capabilities | Schema_name | Credential_data | Key_type | Sig_type | | --public-did --cred-type json-ld --did-exchange | --did-exchange | driverslicense | Data_DL_NormalizedValues | bls12381g2 | BbsBlsSignature2020 | | --public-did --cred-type json-ld --mediation | --mediation | driverslicense | Data_DL_NormalizedValues | bls12381g2 | BbsBlsSignature2020 | | --public-did --cred-type json-ld --multitenant | --multitenant | driverslicense | Data_DL_NormalizedValues | bls12381g2 | BbsBlsSignature2020 | - @PR @Release @WalletType_Askar_AnonCreds + @WalletType_Askar_AnonCreds Examples: | Acme_capabilities | Bob_capabilities | Schema_name | Credential_data | Key_type | Sig_type | | --public-did --cred-type json-ld --wallet-type askar-anoncreds | --wallet-type askar-anoncreds | driverslicense | Data_DL_NormalizedValues | ed25519 | Ed25519Signature2018 | | --public-did --cred-type json-ld --wallet-type askar-anoncreds | --wallet-type askar-anoncreds | driverslicense | Data_DL_NormalizedValues | ed25519 | Ed25519Signature2020 | - @PR @Release @WalletType_Askar_AnonCreds @BBS + @WalletType_Askar_AnonCreds @BBS Examples: | Acme_capabilities | Bob_capabilities | Schema_name | Credential_data | Key_type | Sig_type | | --public-did --cred-type json-ld --wallet-type askar-anoncreds | --wallet-type askar-anoncreds | driverslicense | Data_DL_NormalizedValues | bls12381g2 | BbsBlsSignature2020 | @@ -313,14 +313,14 @@ Feature: RFC 0453 Aries agent issue credential Then "Acme" revokes the credential And "Bob" has the credential issued - @Release @WalletType_Askar + @WalletType_Askar Examples: | Acme_capabilities | Bob_capabilities | Schema_name | Credential_data | | --revocation --public-did | | driverslicense | Data_DL_NormalizedValues | | --revocation --public-did --did-exchange | --did-exchange | driverslicense | Data_DL_NormalizedValues | | --revocation --public-did --multitenant | --multitenant | driverslicense | Data_DL_NormalizedValues | - @Release @WalletType_Askar_AnonCreds + @WalletType_Askar_AnonCreds Examples: | Acme_capabilities | Bob_capabilities | Schema_name | Credential_data | | --revocation --public-did --wallet-type askar-anoncreds | --wallet-type askar-anoncreds | driverslicense | Data_DL_NormalizedValues | diff --git a/demo/features/0454-present-proof.feature b/demo/features/0454-present-proof.feature index 36973d828f..dffc1f598c 100644 --- a/demo/features/0454-present-proof.feature +++ b/demo/features/0454-present-proof.feature @@ -12,27 +12,27 @@ Feature: RFC 0454 Aries agent present proof When "Faber" sends a request for proof presentation to "Bob" Then "Faber" has the proof verified - @PR @Release @WalletType_Askar + @WalletType_Askar Examples: | issuer | Acme_capabilities | Bob_capabilities | Schema_name | Credential_data | Proof_request | | Faber | --public-did | | driverslicense_v2 | Data_DL_MaxValues | DL_age_over_19_v2 | - @Release @WalletType_Askar + @WalletType_Askar Examples: | issuer | Acme_capabilities | Bob_capabilities | Schema_name | Credential_data | Proof_request | | Faber | --public-did --did-exchange | --did-exchange | driverslicense_v2 | Data_DL_MaxValues | DL_age_over_19_v2 | - @PR @Release @WalletType_Askar_AnonCreds + @WalletType_Askar_AnonCreds Examples: | issuer | Acme_capabilities | Bob_capabilities | Schema_name | Credential_data | Proof_request | | Faber | --public-did --wallet-type askar-anoncreds | --wallet-type askar-anoncreds | driverslicense_v2 | Data_DL_MaxValues | DL_age_over_19_v2 | - @Release @WalletType_Askar_AnonCreds @cred_type_vc_di + @WalletType_Askar_AnonCreds @cred_type_vc_di Examples: | issuer | Acme_capabilities | Bob_capabilities | Schema_name | Credential_data | Proof_request | | Faber | --public-did --wallet-type askar-anoncreds --cred-type vc_di | --wallet-type askar-anoncreds | driverslicense_v2 | Data_DL_MaxValues | DL_age_over_19_v2 | - @Release @WalletType_Askar_AnonCreds + @WalletType_Askar_AnonCreds Examples: | issuer | Acme_capabilities | Bob_capabilities | Schema_name | Credential_data | Proof_request | | Faber | --public-did --wallet-type askar-anoncreds | | driverslicense_v2 | Data_DL_MaxValues | DL_age_over_19_v2 | @@ -51,12 +51,12 @@ Feature: RFC 0454 Aries agent present proof When "Faber" sends a request for proof presentation to "Bob" Then "Faber" has the proof verified - @PR @Release @WalletType_Askar + @WalletType_Askar Examples: | issuer | Acme_capabilities | Acme_extra | Bob_capabilities | Bob_extra | Schema_name | Credential_data | Proof_request | | Faber | --public-did --did-exchange --emit-did-peer-2 | | --did-exchange --emit-did-peer-2 | | driverslicense_v2 | Data_DL_MaxValues | DL_age_over_19_v2 | - @PR @Release @WalletType_Askar_AnonCreds + @WalletType_Askar_AnonCreds Examples: | issuer | Acme_capabilities | Acme_extra | Bob_capabilities | Bob_extra | Schema_name | Credential_data | Proof_request | | Faber | --public-did --wallet-type askar-anoncreds --emit-did-peer-2 | | --wallet-type askar-anoncreds --emit-did-peer-2 | | driverslicense_v2 | Data_DL_MaxValues | DL_age_over_19_v2 | @@ -103,17 +103,17 @@ Feature: RFC 0454 Aries agent present proof When "Faber" sends a request for json-ld proof presentation to "Bob" with Then "Faber" has the proof verified - @PR @Release @WalletType_Askar @BBS + @WalletType_Askar @BBS Examples: | issuer | Acme_capabilities | Bob_capabilities | Schema_name | Credential_data | Proof_request | Key_type | Sig_type | | Acme | --public-did --cred-type json-ld | | driverslicense_v2 | Data_DL_MaxValues | DL_age_over_19_v2 | bls12381g2 | BbsBlsSignature2020 | - @Release @WalletType_Askar @BBS + @WalletType_Askar @BBS Examples: | issuer | Acme_capabilities | Bob_capabilities | Schema_name | Credential_data | Proof_request | Key_type | Sig_type | | Faber | --public-did --cred-type json-ld --did-exchange | --did-exchange | driverslicense_v2 | Data_DL_MaxValues | DL_age_over_19_v2 | bls12381g2 | BbsBlsSignature2020 | - @PR @Release @WalletType_Askar_AnonCreds @BBS + @WalletType_Askar_AnonCreds @BBS Examples: | issuer | Acme_capabilities | Bob_capabilities | Schema_name | Credential_data | Proof_request | Key_type | Sig_type | | Faber | --public-did --cred-type json-ld --wallet-type askar-anoncreds | --wallet-type askar-anoncreds | driverslicense_v2 | Data_DL_MaxValues | DL_age_over_19_v2 | bls12381g2 | BbsBlsSignature2020 | @@ -132,22 +132,22 @@ Feature: RFC 0454 Aries agent present proof When "Faber" sends a request for proof presentation to "Bob" Then "Faber" has the proof verification fail - @PR @Release @WalletType_Askar + @WalletType_Askar Examples: | issuer | Acme_capabilities | Bob_capabilities | Schema_name | Credential_data | Proof_request | | Faber | --revocation --public-did | | driverslicense_v2 | Data_DL_MaxValues | DL_age_over_19_v2 | - @Release @WalletType_Askar + @WalletType_Askar Examples: | issuer | Acme_capabilities | Bob_capabilities | Schema_name | Credential_data | Proof_request | | Faber | --revocation --public-did --did-exchange | --did-exchange | driverslicense_v2 | Data_DL_MaxValues | DL_age_over_19_v2 | - @PR @Release @WalletType_Askar_AnonCreds + @WalletType_Askar_AnonCreds Examples: | issuer | Acme_capabilities | Bob_capabilities | Schema_name | Credential_data | Proof_request | | Faber | --revocation --public-did --wallet-type askar-anoncreds | --wallet-type askar-anoncreds | driverslicense_v2 | Data_DL_MaxValues | DL_age_over_19_v2 | - @Release @WalletType_Askar_AnonCreds @cred_type_vc_di + @WalletType_Askar_AnonCreds @cred_type_vc_di Examples: | issuer | Acme_capabilities | Bob_capabilities | Schema_name | Credential_data | Proof_request | | Faber | --revocation --public-did --wallet-type askar-anoncreds --cred-type vc_di | --wallet-type askar-anoncreds | driverslicense_v2 | Data_DL_MaxValues | DL_age_over_19_v2 | @@ -198,17 +198,17 @@ Feature: RFC 0454 Aries agent present proof When "Faber" sends a request for proof presentation to "Bob" Then "Faber" has the proof verified - @PR @Release @WalletType_Askar + @WalletType_Askar Examples: | issuer1 | Acme1_capabilities | issuer2 | Acme2_capabilities | Bob_cap | Schema_name_1 | Credential_data_1 | Schema_name_2 | Credential_data_2 | Proof_request | | Acme1 | --revocation --public-did | Acme2 | --public-did | | driverslicense_v2 | Data_DL_MaxValues | health_id | Data_DL_MaxValues | DL_age_over_19_v2_with_health_id | - @PR @Release @WalletType_Askar_AnonCreds + @WalletType_Askar_AnonCreds Examples: | issuer1 | Acme1_capabilities | issuer2 | Acme2_capabilities | Bob_cap | Schema_name_1 | Credential_data_1 | Schema_name_2 | Credential_data_2 | Proof_request | | Acme1 | --revocation --public-did --wallet-type askar-anoncreds | Acme2 | --public-did --wallet-type askar-anoncreds | --wallet-type askar-anoncreds | driverslicense_v2 | Data_DL_MaxValues | health_id | Data_DL_MaxValues | DL_age_over_19_v2_with_health_id | - @Release @WalletType_Askar_AnonCreds @cred_type_vc_di + @WalletType_Askar_AnonCreds @cred_type_vc_di Examples: | issuer1 | Acme1_capabilities | issuer2 | Acme2_capabilities | Bob_cap | Schema_name_1 | Credential_data_1 | Schema_name_2 | Credential_data_2 | Proof_request | | Acme1 | --revocation --public-did --wallet-type askar-anoncreds --cred-type vc_di | Acme2 | --public-did --wallet-type askar-anoncreds | --wallet-type askar-anoncreds | driverslicense_v2 | Data_DL_MaxValues | health_id | Data_DL_MaxValues | DL_age_over_19_v2_with_health_id | @@ -229,7 +229,7 @@ Feature: RFC 0454 Aries agent present proof When "Faber" sends a request for proof presentation to "Bob" Then "Faber" has the proof verification fail - @PR @Release @WalletType_Askar + @WalletType_Askar Examples: | issuer1 | Acme1_capabilities | issuer2 | Acme2_capabilities | Bob_cap | Schema_name_1 | Credential_data_1 | Schema_name_2 | Credential_data_2 | Proof_request | | Acme1 | --revocation --public-did | Acme2 | --public-did | | driverslicense_v2 | Data_DL_MaxValues | health_id | Data_DL_MaxValues | DL_age_over_19_v2_with_health_id_r2 | @@ -256,13 +256,13 @@ Feature: RFC 0454 Aries agent present proof When "Faber" sends a request for proof presentation to "Bob" Then "Faber" has the proof verification fail - @PR @Release @WalletType_Askar + @WalletType_Askar Examples: | issuer1 | Acme1_capabilities | issuer2 | Acme2_capabilities | Bob_cap | Schema_name_1 | Credential_data_1 | Schema_name_2 | Credential_data_2 | Proof_request | | Acme1 | --revocation --public-did | Acme2 | --public-did | | driverslicense_v2 | Data_DL_MaxValues | health_id | Data_DL_MaxValues | DL_age_over_19_v2_with_health_id | | Acme1 | --revocation --public-did | Acme2 | --public-did | | driverslicense_v2 | Data_DL_MaxValues | health_id | Data_DL_MaxValues | DL_age_over_19_v2_with_health_id_r2 | - @PR @Release @WalletType_Askar_AnonCreds + @WalletType_Askar_AnonCreds Examples: | issuer1 | Acme1_capabilities | issuer2 | Acme2_capabilities | Bob_cap | Schema_name_1 | Credential_data_1 | Schema_name_2 | Credential_data_2 | Proof_request | | Acme1 | --revocation --public-did --wallet-type askar-anoncreds | Acme2 | --public-did --wallet-type askar-anoncreds | --wallet-type askar-anoncreds | driverslicense_v2 | Data_DL_MaxValues | health_id | Data_DL_MaxValues | DL_age_over_19_v2_with_health_id | @@ -284,17 +284,17 @@ Feature: RFC 0454 Aries agent present proof When "Faber" sends a request with explicit revocation status for proof presentation to "Bob" Then "Faber" has the proof verified - @PR @Release @WalletType_Askar + @WalletType_Askar Examples: | issuer1 | Acme1_capabilities | issuer2 | Acme2_capabilities | Bob_cap | Schema_name_1 | Credential_data_1 | Schema_name_2 | Credential_data_2 | Proof_request | | Acme1 | --revocation --public-did | Acme2 | --public-did | | driverslicense_v2 | Data_DL_MaxValues | health_id | Data_DL_MaxValues | DL_age_over_19_v2_with_health_id_no_revoc | - @PR @Release @WalletType_Askar_AnonCreds + @WalletType_Askar_AnonCreds Examples: | issuer1 | Acme1_capabilities | issuer2 | Acme2_capabilities | Bob_cap | Schema_name_1 | Credential_data_1 | Schema_name_2 | Credential_data_2 | Proof_request | | Acme1 | --revocation --public-did --wallet-type askar-anoncreds | Acme2 | --public-did --wallet-type askar-anoncreds | --wallet-type askar-anoncreds | driverslicense_v2 | Data_DL_MaxValues | health_id | Data_DL_MaxValues | DL_age_over_19_v2_with_health_id_no_revoc | - @PR @Release @WalletType_Askar_AnonCreds @cred_type_vc_di + @WalletType_Askar_AnonCreds @cred_type_vc_di Examples: | issuer1 | Acme1_capabilities | issuer2 | Acme2_capabilities | Bob_cap | Schema_name_1 | Credential_data_1 | Schema_name_2 | Credential_data_2 | Proof_request | | Acme1 | --revocation --public-did --wallet-type askar-anoncreds --cred-type vc_di | Acme2 | --public-did --wallet-type askar-anoncreds | --wallet-type askar-anoncreds | driverslicense_v2 | Data_DL_MaxValues | health_id | Data_DL_MaxValues | DL_age_over_19_v2_with_health_id_no_revoc | diff --git a/demo/features/revocation-api.feature b/demo/features/revocation-api.feature index ebfdb120de..a1c7fa2f81 100644 --- a/demo/features/revocation-api.feature +++ b/demo/features/revocation-api.feature @@ -1,6 +1,6 @@ Feature: ACA-Py Revocation API - @Revoc-api @PR @Release + @Revoc-api Scenario Outline: Using revocation api, issue and revoke credentials Given we have "3" agents | name | role | capabilities | @@ -19,7 +19,7 @@ Feature: ACA-Py Revocation API #| Acme | --revocation --public-did | | driverslicense_v2 | Data_DL_MaxValues | DL_age_over_19_v2 | | Acme | --revocation --public-did --wallet-type askar-anoncreds | --wallet-type askar-anoncreds | driverslicense_v2 | Data_DL_MaxValues | DL_age_over_19_v2 | - @Revoc-api @PR @Release + @Revoc-api Scenario Outline: Using revocation api, issue, revoke credentials and publish Given we have "3" agents | name | role | capabilities | @@ -76,7 +76,7 @@ Feature: ACA-Py Revocation API | Acme | --revocation --public-did --did-exchange --multitenant --wallet-type askar | --wallet-type askar-anoncreds | driverslicense_v2 | Data_DL_MaxValues | DL_age_over_19_v2 | | Acme | --revocation --public-did --did-exchange --multitenant --wallet-type askar-anoncreds | --wallet-type askar-anoncreds | driverslicense_v2 | Data_DL_MaxValues | DL_age_over_19_v2 | - @Revoc-api @PR @Release + @Revoc-api Scenario Outline: Using revocation api, rotate revocation Given we have "3" agents | name | role | capabilities | @@ -93,7 +93,7 @@ Feature: ACA-Py Revocation API #| Acme | --revocation --public-did | | driverslicense_v2 | Data_DL_MaxValues | DL_age_over_19_v2 | | Acme | --revocation --public-did --wallet-type askar-anoncreds | --wallet-type askar-anoncreds | driverslicense_v2 | Data_DL_MaxValues | DL_age_over_19_v2 | - @Revoc-api @PR @Release + @Revoc-api Scenario Outline: Using revocation api, fill registry (need to run with "TAILS_FILE_COUNT": "4" env var) Given we have "2" agents | name | role | capabilities | diff --git a/docs/deploying/BBSSignatures.md b/docs/deploying/BBSSignatures.md index b03be5e2a2..76579b76fb 100644 --- a/docs/deploying/BBSSignatures.md +++ b/docs/deploying/BBSSignatures.md @@ -24,4 +24,4 @@ WARNNG: if you do NOT have `bbs` installed you should exclude the BBS specific i ./run_bdd -t ~@BBS ``` -See the [Unit](../testing/UnitTests.md) and [Integration](../testing/INTEGRATION-TESTS.md) testing docs for more information on how to run tests. +See the [Unit](../testing/UnitTests.md) and [Integration](../testing/BDDTests.md) testing docs for more information on how to run tests. diff --git a/docs/deploying/ContainerImagesAndGithubActions.md b/docs/deploying/ContainerImagesAndGithubActions.md index 6e891fcddb..91e96c8d37 100644 --- a/docs/deploying/ContainerImagesAndGithubActions.md +++ b/docs/deploying/ContainerImagesAndGithubActions.md @@ -90,7 +90,7 @@ variants and between the BC Gov ACA-Py images. - Publish (`.github/workflows/publish.yml`) - Run on new release published or when manually triggered; builds and pushes the Standard ACA-Py variant to the Github Container Registry. -- Integration Tests (`.github/workflows/integrationtests.yml`) - Run on pull +- BDD Integration Tests (`.github/workflows/BDDTests.yml`) - Run on pull requests (to the hyperledger fork only); runs BDD integration tests. - Format (`.github/workflows/format.yml`) - Run on pull requests; checks formatting of files modified by the PR. diff --git a/docs/testing/INTEGRATION-TESTS.md b/docs/testing/BDDTests.md similarity index 99% rename from docs/testing/INTEGRATION-TESTS.md rename to docs/testing/BDDTests.md index dad1955b6c..8ed9b76472 100644 --- a/docs/testing/INTEGRATION-TESTS.md +++ b/docs/testing/BDDTests.md @@ -28,7 +28,7 @@ cd aries-cloudagent-python/demo Note that an Indy ledger and tails server are both required (these can also be specified using environment variables). -Note also that some tests require a ledger with TAA enabled, how to run these tests will be described later. +Note also that some tests require a ledger with TAA enabled, how to run tEhese tests will be described later. By default the test suite runs using a default (SQLite) wallet, to run the tests using postgres run the following: diff --git a/docs/testing/IntegrationTests.md b/docs/testing/IntegrationTests.md new file mode 100644 index 0000000000..5356e5eac1 --- /dev/null +++ b/docs/testing/IntegrationTests.md @@ -0,0 +1,24 @@ +# Integration Test Plan + +Integration testing in ACA-Py consists of 3 different levels or types. +1. Interop profile (AATH) BDD tests. +2. ACA-Py specific BDD tests. +3. End to End tests. + +## Interop profile (AATH) BDD tests + +Interoperability is extremely important in the aries community. When implementing or changing features that are included in the [aries interop profile](https://github.com/hyperledger/aries-rfcs/blob/main/concepts/0302-aries-interop-profile/README.md) the developer should try to add tests to this test suite. The tests will then be ran for PR's and scheduled workflows for acapy <--> acapy agents. + +These tests are contained in a separate repo [AATH](https://github.com/hyperledger/aries-agent-test-harness). They use the gherkin syntax and a http back channel. + + +## ACA-Py specific BDD tests + +These tests leverage the demo agent and also use behave syntax and a back channel. See [README](./BDDTests.md) + +These tests are another tool for leveraging the demo agent and the gherkin syntax. They should not be used to test features that involve the interop profile, as they can not be used to test against other frameworks. None of the tests that are covered by the AATH tests will be ran automatically. They are here because some developers may prefer the testing strategy and can be useful for explicit testing steps and protocols not included in the interop profile. + +## Scenario testing + +These tests utilize the minimal example [agent](https://github.com/Indicio-tech/acapy-minimal-example) produced by Indicio. They exist in the `integration-tests` directory. They are very useful for running specific test plans and checking webhooks. + diff --git a/integration-tests/Dockerfile b/integration-tests/Dockerfile new file mode 100644 index 0000000000..e08aee4d2b --- /dev/null +++ b/integration-tests/Dockerfile @@ -0,0 +1,17 @@ +FROM python:3.10 +WORKDIR /usr/src/app/ + +ENV POETRY_VERSION=1.8.3 +ENV POETRY_HOME=/opt/poetry +RUN curl -sSL https://install.python-poetry.org | python - + +ENV PATH="/opt/poetry/bin:$PATH" +RUN poetry config virtualenvs.in-project true + +# Setup project +COPY pyproject.toml poetry.lock README.md ./ +RUN poetry install + +COPY examples/ examples/ + +ENTRYPOINT ["poetry", "run"] \ No newline at end of file diff --git a/integration-tests/README.md b/integration-tests/README.md new file mode 100644 index 0000000000..85819a0d65 --- /dev/null +++ b/integration-tests/README.md @@ -0,0 +1,3 @@ +This project is used to write integration tests leveraging the [acapy-minimal-example](https://github.com/Indicio-tech/acapy-minimal-example) library provided by the contributors from Indicio + +Every test example will have a docker-compose.yml file for all the agents and services used in the test scenario. \ No newline at end of file diff --git a/integration-tests/conftest.py b/integration-tests/conftest.py new file mode 100644 index 0000000000..fcfe03ec86 --- /dev/null +++ b/integration-tests/conftest.py @@ -0,0 +1,126 @@ +"""Pytest fixtures and configuration.""" + +import subprocess +from pathlib import Path + +import pytest +from pytest import Session + +EXAMPLES_DIR = Path(__file__).parent / "examples" + + +class ExampleFailedException(Exception): + """Raised when an example fails.""" + + def __init__(self, message: str, exit_status: int): + """Initialize ExampleFailedException.""" + + super().__init__(message) + self.exit_status = exit_status + + +class ExampleRunner: + """Run the docker compose of a given example.""" + + def __init__(self, compose_file: str): + """Initialize ExampleRunner.""" + + self.compose_file = compose_file + + def compose(self, *command: str) -> int: + """Runs docker compose using subprocess with the given command. + + Returns exit status and output. + """ + try: + subprocess.run( + ["docker", "compose", "-f", self.compose_file, *command], + check=True, + ) + return 0 + except subprocess.CalledProcessError as e: + return e.returncode + + def cleanup(self): + """Runs docker compose down -v for cleanup.""" + exit_status = self.compose("down", "-v") + if exit_status != 0: + raise ExampleFailedException( + f"Cleanup failed with exit status {exit_status}", exit_status + ) + + def handle_run(self, *command: str): + """Handles the run of docker compose/. + + raises exception if exit status is non-zero. + """ + try: + exit_status = self.compose(*command) + if exit_status != 0: + raise ExampleFailedException( + f"Command failed with exit status: {exit_status}", + exit_status=exit_status, + ) + finally: + self.cleanup() + + +def pytest_collect_file(parent: Session, file_path: Path): + """Pytest collection hook. + + This will collect the docker compose.yml files from the examples and create + pytest items to run them. + """ + file = Path(str(file_path)) + + # Skip certain examples + if (file.parent / "__skip__").exists(): + return + + if file.suffix == ".yml" and file.parent.parent == EXAMPLES_DIR: + return ExampleFile.from_parent(parent, path=file.parent) + + +class ExampleFile(pytest.File): + """Pytest file for example.""" + + def collect(self): + """Collect tests from example file.""" + path = Path(self.fspath) + item = ExampleItem.from_parent( + self, name=path.name, compose_file=str(path / "docker-compose.yml") + ) + item.add_marker(pytest.mark.examples) + yield item + + +class ExampleItem(pytest.Item): + """Example item. + + Runs the docker-compose.yml file of the example and reports failure if the + exit status is non-zero. + """ + + def __init__(self, name: str, parent: pytest.File, compose_file: str): + """Initialize ExampleItem.""" + super().__init__(name, parent) + self.compose_file = compose_file + + def runtest(self) -> None: + """Run the test.""" + ExampleRunner(self.compose_file).handle_run("run", "example") + + def repr_failure(self, excinfo): + """Called when self.runtest() raises an exception.""" + if isinstance(excinfo.value, ExampleFailedException): + return "\n".join( + [ + "Example failed!", + f" {excinfo.value}", + ] + ) + return f"Some other exception happened: {excinfo.value}" + + def reportinfo(self): + """Report info about the example.""" + return self.fspath, 0, f"example: {self.name}" diff --git a/integration-tests/docker-compose.yml b/integration-tests/docker-compose.yml new file mode 100644 index 0000000000..5573cff1c2 --- /dev/null +++ b/integration-tests/docker-compose.yml @@ -0,0 +1,91 @@ +# This docker-compose file is used for tests only + +services: + alice: + image: acapy-test + ports: + - "3001:3001" + command: > + start + --label Alice + --inbound-transport http 0.0.0.0 3000 + --outbound-transport http + --endpoint http://alice:3000 + --admin 0.0.0.0 3001 + --admin-insecure-mode + --tails-server-base-url http://tails:6543 + --genesis-url https://raw.githubusercontent.com/Indicio-tech/indicio-network/main/genesis_files/pool_transactions_testnet_genesis + --wallet-type askar + --wallet-name alice + --wallet-key insecure + --auto-provision + --log-level debug + --debug-webhooks + healthcheck: + test: curl -s -o /dev/null -w '%{http_code}' "http://localhost:3001/status/live" | grep "200" > /dev/null + start_period: 30s + interval: 7s + timeout: 5s + retries: 5 + depends_on: + tails: + condition: service_started + + bob: + image: acapy-test + ports: + - "3002:3001" + command: > + start + --label Bob + --inbound-transport http 0.0.0.0 3000 + --outbound-transport http + --endpoint http://bob:3000 + --admin 0.0.0.0 3001 + --admin-insecure-mode + --tails-server-base-url http://tails:6543 + --genesis-url https://raw.githubusercontent.com/Indicio-tech/indicio-network/main/genesis_files/pool_transactions_testnet_genesis + --wallet-type askar + --wallet-name bob + --wallet-key insecure + --auto-provision + --log-level debug + --debug-webhooks + --monitor-revocation-notification + healthcheck: + test: curl -s -o /dev/null -w '%{http_code}' "http://localhost:3001/status/live" | grep "200" > /dev/null + start_period: 30s + interval: 7s + timeout: 5s + retries: 5 + + tails: + image: ghcr.io/bcgov/tails-server:latest + ports: + - 6543:6543 + environment: + - GENESIS_URL=https://raw.githubusercontent.com/Indicio-tech/indicio-network/main/genesis_files/pool_transactions_testnet_genesis + command: > + tails-server + --host 0.0.0.0 + --port 6543 + --storage-path /tmp/tails-files + --log-level INFO + + tests: + container_name: juggernaut + build: + context: . + args: + install_flags: "" + environment: + - ALICE=http://alice:3001 + - BOB=http://bob:3001 + volumes: + - ./tests:/usr/src/app/tests:z + entrypoint: "poetry run pytest" + depends_on: + alice: + condition: service_healthy + bob: + condition: service_healthy \ No newline at end of file diff --git a/integration-tests/examples/connectionless/docker-compose.yml b/integration-tests/examples/connectionless/docker-compose.yml new file mode 100644 index 0000000000..95702ed09c --- /dev/null +++ b/integration-tests/examples/connectionless/docker-compose.yml @@ -0,0 +1,91 @@ + services: + alice: + image: acapy-test + ports: + - "3001:3001" + environment: + RUST_LOG: 'aries-askar::log::target=error' + command: > + start + --label Alice + --inbound-transport http 0.0.0.0 3000 + --outbound-transport http + --endpoint http://alice:3000 + --admin 0.0.0.0 3001 + --admin-insecure-mode + --tails-server-base-url http://tails:6543 + --genesis-url https://raw.githubusercontent.com/Indicio-tech/indicio-network/main/genesis_files/pool_transactions_testnet_genesis + --wallet-type askar + --wallet-name alice + --wallet-key insecure + --auto-provision + --log-level debug + --debug-webhooks + healthcheck: + test: curl -s -o /dev/null -w '%{http_code}' "http://localhost:3001/status/live" | grep "200" > /dev/null + start_period: 30s + interval: 7s + timeout: 5s + retries: 5 + depends_on: + tails: + condition: service_started + + bob: + image: acapy-test + ports: + - "3002:3001" + environment: + RUST_LOG: 'aries-askar::log::target=error' + command: > + start + --label Bob + --inbound-transport http 0.0.0.0 3000 + --outbound-transport http + --endpoint http://bob:3000 + --admin 0.0.0.0 3001 + --admin-insecure-mode + --tails-server-base-url http://tails:6543 + --genesis-url https://raw.githubusercontent.com/Indicio-tech/indicio-network/main/genesis_files/pool_transactions_testnet_genesis + --wallet-type askar + --wallet-name bob + --wallet-key insecure + --auto-provision + --log-level debug + --debug-webhooks + --monitor-revocation-notification + healthcheck: + test: curl -s -o /dev/null -w '%{http_code}' "http://localhost:3001/status/live" | grep "200" > /dev/null + start_period: 30s + interval: 7s + timeout: 5s + retries: 5 + + tails: + image: ghcr.io/bcgov/tails-server:latest + ports: + - 6543:6543 + environment: + - GENESIS_URL=https://raw.githubusercontent.com/Indicio-tech/indicio-network/main/genesis_files/pool_transactions_testnet_genesis + command: > + tails-server + --host 0.0.0.0 + --port 6543 + --storage-path /tmp/tails-files + --log-level INFO + + example: + container_name: controller + build: + context: ../.. + environment: + - ALICE=http://alice:3001 + - BOB=http://bob:3001 + volumes: + - ./example.py:/usr/src/app/example.py:ro,z + command: python -m example + depends_on: + alice: + condition: service_healthy + bob: + condition: service_healthy diff --git a/integration-tests/examples/connectionless/example.py b/integration-tests/examples/connectionless/example.py new file mode 100644 index 0000000000..a0dcc78895 --- /dev/null +++ b/integration-tests/examples/connectionless/example.py @@ -0,0 +1,241 @@ +"""Minimal reproducible example script. + +This script is for you to use to reproduce a bug or demonstrate a feature. +""" + +import asyncio +from dataclasses import dataclass +from os import getenv + +from acapy_controller import Controller +from acapy_controller.controller import Minimal +from acapy_controller.logging import logging_to_stdout +from acapy_controller.protocols import ( + InvitationRecord, + V20CredExRecordDetail, + V20CredExRecordIndy, + indy_anoncred_onboard, + indy_anoncred_credential_artifacts, +) + +ALICE = getenv("ALICE", "http://alice:3001") +BOB = getenv("BOB", "http://bob:3001") + + +@dataclass +class ConnectionlessV20CredExRecord(Minimal): + """Minimal record for connectionless v2 cred ex record.""" + + cred_ex_id: str + + +async def icv2(): + """Test Controller protocols.""" + async with Controller(base_url=ALICE) as alice, Controller(base_url=BOB) as bob: + await indy_anoncred_onboard(alice) + _, cred_def = await indy_anoncred_credential_artifacts( + alice, ["firstname", "lastname"] + ) + + attributes = {"firstname": "Bob", "lastname": "Builder"} + offer = await alice.post( + "/issue-credential-2.0/create-offer", + json={ + "auto_issue": False, + "auto_remove": False, + "comment": "Credential from minimal example", + "trace": False, + "filter": {"indy": {"cred_def_id": cred_def.credential_definition_id}}, + "credential_preview": { + "type": "issue-credential-2.0/2.0/credential-preview", # pyright: ignore + "attributes": [ + { + "mime_type": None, + "name": name, + "value": value, + } + for name, value in attributes.items() + ], + }, + }, + response=ConnectionlessV20CredExRecord, + ) + invite = await alice.post( + "/out-of-band/create-invitation", + json={ + "attachments": [{"id": offer.cred_ex_id, "type": "credential-offer"}] + }, + response=InvitationRecord, + ) + bob.event_queue.flush() + await bob.post("/out-of-band/receive-invitation", json=invite.invitation) + bob_cred_ex = await bob.event_with_values( + topic="issue_credential_v2_0", + state="offer-received", + event_type=ConnectionlessV20CredExRecord, + ) + bob_cred_ex_id = bob_cred_ex.cred_ex_id + + alice.event_queue.flush() + bob_cred_ex = await bob.post( + f"/issue-credential-2.0/records/{bob_cred_ex_id}/send-request", + response=ConnectionlessV20CredExRecord, + ) + + alice_cred_ex = await alice.event_with_values( + topic="issue_credential_v2_0", + state="request-received", + event_type=ConnectionlessV20CredExRecord, + ) + alice_cred_ex_id = alice_cred_ex.cred_ex_id + + alice_cred_ex = await alice.post( + f"/issue-credential-2.0/records/{alice_cred_ex_id}/issue", + json={}, + response=V20CredExRecordDetail, + ) + + await bob.event_with_values( + topic="issue_credential_v2_0", + cred_ex_id=bob_cred_ex_id, + state="credential-received", + ) + + bob_cred_ex = await bob.post( + f"/issue-credential-2.0/records/{bob_cred_ex_id}/store", + json={}, + response=V20CredExRecordDetail, + ) + alice_cred_ex = await alice.event_with_values( + topic="issue_credential_v2_0", + event_type=ConnectionlessV20CredExRecord, + cred_ex_id=alice_cred_ex_id, + state="done", + ) + await alice.event_with_values( + topic="issue_credential_v2_0_indy", + event_type=V20CredExRecordIndy, + ) + + bob_cred_ex = await bob.event_with_values( + topic="issue_credential_v2_0", + event_type=ConnectionlessV20CredExRecord, + cred_ex_id=bob_cred_ex_id, + state="done", + ) + await bob.event_with_values( + topic="issue_credential_v2_0_indy", + event_type=V20CredExRecordIndy, + ) + + +@dataclass +class ConnectionlessV10CredExRecord(Minimal): + """Minimal record for v1 cred ex record.""" + + credential_exchange_id: str + + +async def icv1(): + """Issue credential v1.""" + async with Controller(base_url=ALICE) as alice, Controller(base_url=BOB) as bob: + await indy_anoncred_onboard(alice) + _, cred_def = await indy_anoncred_credential_artifacts( + alice, ["firstname", "lastname"] + ) + + attributes = {"firstname": "Bob", "lastname": "Builder"} + offer = await alice.post( + "/issue-credential/create-offer", + json={ + "auto_issue": False, + "auto_remove": False, + "comment": "Credential from minimal example", + "trace": False, + "cred_def_id": cred_def.credential_definition_id, + "credential_preview": { + "@type": "issue-credential/1.0/credential-preview", + "attributes": [ + { + "mime_type": None, + "name": name, + "value": value, + } + for name, value in attributes.items() + ], + }, + }, + response=ConnectionlessV10CredExRecord, + ) + invite = await alice.post( + "/out-of-band/create-invitation", + json={ + "attachments": [ + {"id": offer.credential_exchange_id, "type": "credential-offer"} + ] + }, + response=InvitationRecord, + ) + bob.event_queue.flush() + await bob.post("/out-of-band/receive-invitation", json=invite.invitation) + bob_cred_ex = await bob.event_with_values( + topic="issue_credential", + state="offer_received", + event_type=ConnectionlessV10CredExRecord, + ) + bob_cred_ex_id = bob_cred_ex.credential_exchange_id + + alice.event_queue.flush() + bob_cred_ex = await bob.post( + f"/issue-credential/records/{bob_cred_ex_id}/send-request", + response=ConnectionlessV10CredExRecord, + ) + + alice_cred_ex = await alice.event_with_values( + topic="issue_credential", + state="request_received", + event_type=ConnectionlessV10CredExRecord, + ) + alice_cred_ex_id = alice_cred_ex.credential_exchange_id + + alice_cred_ex = await alice.post( + f"/issue-credential/records/{alice_cred_ex_id}/issue", + json={}, + response=ConnectionlessV10CredExRecord, + ) + + await bob.event_with_values( + topic="issue_credential", + credential_exchange_id=bob_cred_ex_id, + state="credential_received", + ) + + bob_cred_ex = await bob.post( + f"/issue-credential/records/{bob_cred_ex_id}/store", + json={}, + response=ConnectionlessV10CredExRecord, + ) + alice_cred_ex = await alice.event_with_values( + topic="issue_credential", + event_type=ConnectionlessV10CredExRecord, + credential_exchange_id=alice_cred_ex_id, + state="credential_acked", + ) + + bob_cred_ex = await bob.event_with_values( + topic="issue_credential", + event_type=ConnectionlessV10CredExRecord, + credential_exchange_id=bob_cred_ex_id, + state="credential_acked", + ) + + +async def main(): + """Run.""" + await icv1() + await icv2() + + +if __name__ == "__main__": + logging_to_stdout() + asyncio.run(main()) diff --git a/integration-tests/examples/json_ld/docker-compose.yml b/integration-tests/examples/json_ld/docker-compose.yml new file mode 100644 index 0000000000..3a3ae3fe52 --- /dev/null +++ b/integration-tests/examples/json_ld/docker-compose.yml @@ -0,0 +1,65 @@ + services: + alice: + image: acapy-test + ports: + - "3001:3001" + command: > + start -it http 0.0.0.0 3000 + --label Alice + -ot http + -e http://alice:3000 + --admin 0.0.0.0 3001 --admin-insecure-mode + --log-level debug + --genesis-url https://raw.githubusercontent.com/Indicio-tech/indicio-network/main/genesis_files/pool_transactions_demonet_genesis + --wallet-type askar + --wallet-name alice + --wallet-key insecure + --auto-provision + --debug-webhooks + healthcheck: + test: curl -s -o /dev/null -w '%{http_code}' "http://localhost:3001/status/live" | grep "200" > /dev/null + start_period: 30s + interval: 3s + timeout: 5s + retries: 5 + + + bob: + image: acapy-test + ports: + - "3002:3001" + command: > + start -it http 0.0.0.0 3000 + --label Bob + -ot http + -e http://bob:3000 + --admin 0.0.0.0 3001 --admin-insecure-mode + --log-level debug + --genesis-url https://raw.githubusercontent.com/Indicio-tech/indicio-network/main/genesis_files/pool_transactions_demonet_genesis + --wallet-type askar + --wallet-name bob + --wallet-key insecure + --auto-provision + --debug-webhooks + healthcheck: + test: curl -s -o /dev/null -w '%{http_code}' "http://localhost:3001/status/live" | grep "200" > /dev/null + start_period: 30s + interval: 3s + timeout: 5s + retries: 5 + + example: + container_name: controller + build: + context: ../.. + environment: + - ALICE=http://alice:3001 + - BOB=http://bob:3001 + volumes: + - ./example.py:/usr/src/app/example.py:ro,z + command: python -m example + depends_on: + alice: + condition: service_healthy + bob: + condition: service_healthy diff --git a/integration-tests/examples/json_ld/example.py b/integration-tests/examples/json_ld/example.py new file mode 100644 index 0000000000..21282bc495 --- /dev/null +++ b/integration-tests/examples/json_ld/example.py @@ -0,0 +1,319 @@ +"""Minimal reproducible example script. + +This script is for you to use to reproduce a bug or demonstrate a feature. +""" + +import asyncio +from datetime import date +import json +from os import getenv +from uuid import uuid4 + +from acapy_controller import Controller +from acapy_controller.logging import logging_to_stdout, pause_for_input, section +from acapy_controller.models import DIDResult, V20PresExRecord +from acapy_controller.protocols import ( + didexchange, + indy_anoncred_onboard, + jsonld_issue_credential, + jsonld_present_proof, +) + +ALICE = getenv("ALICE", "http://alice:3001") +BOB = getenv("BOB", "http://bob:3001") + + +def presentation_summary(pres_ex: V20PresExRecord): + """Summarize a presentation.""" + pres_ex_dict = pres_ex.dict(exclude_none=True, exclude_unset=True) + return json.dumps( + { + key: pres_ex_dict.get(key) + for key in ( + "verified", + "state", + "role", + "connection_id", + "pres_request", + "pres", + ) + }, + indent=2, + ) + + +async def main(): + """Test Controller protocols.""" + async with Controller(base_url=ALICE) as alice, Controller(base_url=BOB) as bob: + with section("Establish connection"): + alice_conn, bob_conn = await didexchange(alice, bob) + + with section("Prepare for issuance"): + with section("Issuer prepares issuing DIDs", character="-"): + public_did = await indy_anoncred_onboard(alice) + bls_alice_did_res = ( + await alice.post( + "/wallet/did/create", + json={"method": "key", "options": {"key_type": "bls12381g2"}}, + ) + )["result"] + assert bls_alice_did_res + bls_alice_did = bls_alice_did_res["did"] + + with section("Recipient prepares subject DIDs", character="-"): + bob_did = ( + await bob.post( + "/wallet/did/create", + json={"method": "key", "options": {"key_type": "ed25519"}}, + response=DIDResult, + ) + ).result + assert bob_did + bls_bob_did_res = ( + await bob.post( + "/wallet/did/create", + json={"method": "key", "options": {"key_type": "bls12381g2"}}, + ) + )["result"] + assert bls_bob_did_res + bls_bob_did = bls_bob_did_res["did"] + + pause_for_input() + + with section("Issue example credential using ED25519 Signature"): + issuer_cred_ex, holder_cred_ex = await jsonld_issue_credential( + alice, + bob, + alice_conn.connection_id, + bob_conn.connection_id, + credential={ + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://w3id.org/citizenship/v1", + ], + "type": ["VerifiableCredential", "PermanentResident"], + "issuer": "did:sov:" + public_did.did, + "issuanceDate": str(date.today()), + "credentialSubject": { + "type": ["PermanentResident"], + "id": bob_did.did, + "givenName": "Bob", + "familyName": "Builder", + "gender": "Male", + "birthCountry": "Bahamas", + "birthDate": "1958-07-17", + }, + }, + options={"proofType": "Ed25519Signature2018"}, + ) + + pause_for_input() + + with section("Present example credential"): + alice_pres_ex, bob_pres_ex = await jsonld_present_proof( + alice, + bob, + alice_conn.connection_id, + bob_conn.connection_id, + presentation_definition={ + "input_descriptors": [ + { + "id": "citizenship_input_1", + "name": "EU Driver's License", + "schema": [ + { + "uri": "https://www.w3.org/2018/credentials#VerifiableCredential" # noqa: E501 + }, + { + "uri": "https://w3id.org/citizenship#PermanentResident" # noqa: E501 + }, + ], + "constraints": { + "is_holder": [ + { + "directive": "required", + "field_id": [ + "1f44d55f-f161-4938-a659-f8026467f126" + ], + } + ], + "fields": [ + { + "id": "1f44d55f-f161-4938-a659-f8026467f126", + "path": ["$.credentialSubject.familyName"], + "purpose": "The claim must be from one of the specified issuers", # noqa: E501 + "filter": {"const": "Builder"}, + }, + { + "path": ["$.credentialSubject.givenName"], + "purpose": "The claim must be from one of the specified issuers", # noqa: E501 + }, + ], + }, + } + ], + "id": str(uuid4()), + "format": {"ldp_vp": {"proof_type": ["Ed25519Signature2018"]}}, + }, + domain="test-degree", + ) + with section("Presentation summary", character="-"): + print(presentation_summary(alice_pres_ex.into(V20PresExRecord))) + + pause_for_input() + + with section("Issue Credential with quick context"): + issuer_cred_ex, holder_cred_ex = await jsonld_issue_credential( + alice, + bob, + alice_conn.connection_id, + bob_conn.connection_id, + credential={ + "@context": [ + "https://www.w3.org/2018/credentials/v1", + { + "ex": "https://example.com/examples#", + "TableTennisTournamentWin": "ex:TableTennisTournamentWin", + "dateWon": "ex:dateWon", + }, + ], + "type": ["VerifiableCredential", "TableTennisTournamentWin"], + "issuer": "did:sov:" + public_did.did, + "issuanceDate": str(date.today()), + "credentialSubject": { + "id": bob_did.did, + "dateWon": str(date.today()), + }, + }, + options={"proofType": "Ed25519Signature2018"}, + ) + + pause_for_input() + + with section("Present quick context credential"): + alice_pres_ex, bob_pres_ex = await jsonld_present_proof( + alice, + bob, + alice_conn.connection_id, + bob_conn.connection_id, + presentation_definition={ + "input_descriptors": [ + { + "id": "ttt_win_input_1", + "name": "TableTennisTournamentWin", + "schema": [ + { + "uri": "https://www.w3.org/2018/credentials#VerifiableCredential" # noqa: E501 + }, + { + "uri": "https://example.com/examples#TableTennisTournamentWin" # noqa: E501 + }, + ], + "constraints": { + "is_holder": [ + { + "directive": "required", + "field_id": [ + "1f44d55f-f161-4938-a659-f8026467f126" + ], + } + ], + "fields": [ + { + "id": "1f44d55f-f161-4938-a659-f8026467f126", + "path": ["$.credentialSubject.dateWon"], + "purpose": "Get proof of win on date", # noqa: E501 + }, + ], + }, + } + ], + "id": str(uuid4()), + "format": {"ldp_vp": {"proof_type": ["Ed25519Signature2018"]}}, + }, + domain="test-degree", + ) + with section("Presentation summary", character="-"): + print(presentation_summary(alice_pres_ex.into(V20PresExRecord))) + + pause_for_input() + + with section("Issue BBS+ Credential"): + issuer_cred_ex, holder_cred_ex = await jsonld_issue_credential( + alice, + bob, + alice_conn.connection_id, + bob_conn.connection_id, + credential={ + "@context": [ + "https://www.w3.org/2018/credentials/v1", + { + "ex": "https://example.com/examples#", + "Employment": "ex:Employment", + "dateHired": "ex:dateHired", + "clearance": "ex:clearance", + }, + ], + "type": ["VerifiableCredential", "Employment"], + "issuer": bls_alice_did, + "issuanceDate": str(date.today()), + "credentialSubject": { + "id": bls_bob_did, + "dateHired": str(date.today()), + "clearance": 1, + }, + }, + options={"proofType": "BbsBlsSignature2020"}, + ) + + pause_for_input() + + with section("Present BBS+ Credential with SD"): + alice_pres_ex, bob_pres_ex = await jsonld_present_proof( + alice, + bob, + alice_conn.connection_id, + bob_conn.connection_id, + presentation_definition={ + "input_descriptors": [ + { + "id": "building_access_1", + "name": "BuildingAccess", + "schema": [ + { + "uri": "https://www.w3.org/2018/credentials#VerifiableCredential" # noqa: E501 + }, + {"uri": "https://example.com/examples#Employment"}, + ], + "constraints": { + "limit_disclosure": "required", + "is_holder": [ + { + "directive": "required", + "field_id": [ + "1f44d55f-f161-4938-a659-f8026467f126" + ], + } + ], + "fields": [ + { + "id": "1f44d55f-f161-4938-a659-f8026467f126", + "path": ["$.credentialSubject.clearance"], + "purpose": "Get clearance", # noqa: E501 + }, + ], + }, + } + ], + "id": str(uuid4()), + "format": {"ldp_vp": {"proof_type": ["BbsBlsSignature2020"]}}, + }, + domain="building-access", + ) + with section("Presentation summary", character="-"): + print(presentation_summary(alice_pres_ex.into(V20PresExRecord))) + + +if __name__ == "__main__": + logging_to_stdout() + asyncio.run(main()) diff --git a/integration-tests/examples/mediation/docker-compose.yml b/integration-tests/examples/mediation/docker-compose.yml new file mode 100644 index 0000000000..b6e9dbebbb --- /dev/null +++ b/integration-tests/examples/mediation/docker-compose.yml @@ -0,0 +1,105 @@ + services: + alice: + image: acapy-test + ports: + - "3001:3001" + environment: + RUST_LOG: warn + command: > + start + --label Alice + --inbound-transport http 0.0.0.0 3000 + --outbound-transport http + --endpoint http://alice:3000 + --admin 0.0.0.0 3001 + --admin-insecure-mode + --no-ledger + --wallet-type askar + --wallet-name alice + --wallet-key insecure + --auto-provision + --log-level debug + --debug-webhooks + healthcheck: + test: curl -s -o /dev/null -w '%{http_code}' "http://localhost:3001/status/live" | grep "200" > /dev/null + start_period: 30s + interval: 7s + timeout: 5s + retries: 5 + + bob: + image: acapy-test + ports: + - "3002:3001" + environment: + RUST_LOG: warn + command: > + start + --label Bob + --inbound-transport http 0.0.0.0 3000 + --outbound-transport http + --endpoint http://bob:3000 + --admin 0.0.0.0 3001 + --admin-insecure-mode + --no-ledger + --wallet-type askar + --wallet-name bob + --wallet-key insecure + --auto-provision + --log-level debug + --debug-webhooks + --monitor-revocation-notification + healthcheck: + test: curl -s -o /dev/null -w '%{http_code}' "http://localhost:3001/status/live" | grep "200" > /dev/null + start_period: 30s + interval: 7s + timeout: 5s + retries: 5 + + mediator: + image: acapy-test + ports: + - "3003:3001" + environment: + RUST_LOG: warn + command: > + start + --label Mediator + --inbound-transport http 0.0.0.0 3000 + --outbound-transport http + --endpoint http://mediator:3000 + --admin 0.0.0.0 3001 + --admin-insecure-mode + --no-ledger + --wallet-type askar + --wallet-name mediator + --wallet-key insecure + --auto-provision + --log-level debug + --debug-webhooks + --enable-undelivered-queue + healthcheck: + test: curl -s -o /dev/null -w '%{http_code}' "http://localhost:3001/status/live" | grep "200" > /dev/null + start_period: 30s + interval: 7s + timeout: 5s + retries: 5 + + example: + container_name: controller + build: + context: ../.. + environment: + - ALICE=http://alice:3001 + - BOB=http://bob:3001 + - MEDIATOR=http://mediator:3001 + volumes: + - ./example.py:/usr/src/app/example.py:ro,z + command: python -m example + depends_on: + alice: + condition: service_healthy + bob: + condition: service_healthy + mediator: + condition: service_healthy diff --git a/integration-tests/examples/mediation/example.py b/integration-tests/examples/mediation/example.py new file mode 100644 index 0000000000..9a7851c04f --- /dev/null +++ b/integration-tests/examples/mediation/example.py @@ -0,0 +1,44 @@ +"""Minimal reproducible example script. + +This script is for you to use to reproduce a bug or demonstrate a feature. +""" + +import asyncio +from os import getenv + +from acapy_controller import Controller +from acapy_controller.logging import logging_to_stdout +from acapy_controller.protocols import ( + connection, + didexchange, + request_mediation_v1, + trustping, +) + +ALICE = getenv("ALICE", "http://alice:3001") +BOB = getenv("BOB", "http://bob:3001") +MEDIATOR = getenv("MEDIATOR", "http://mediator:3001") + + +async def main(): + """Test Controller protocols.""" + alice = Controller(base_url=ALICE) + bob = Controller(base_url=BOB) + mediator = Controller(base_url=MEDIATOR) + + async with alice, bob, mediator: + ma, am = await didexchange(mediator, alice) + mam, amm = await request_mediation_v1( + mediator, alice, ma.connection_id, am.connection_id + ) + await alice.put(f"/mediation/{amm.mediation_id}/default-mediator") + ab, ba = await didexchange(alice, bob) + await trustping(alice, ab) + + ab, ba = await connection(alice, bob) + await trustping(alice, ab) + + +if __name__ == "__main__": + logging_to_stdout() + asyncio.run(main()) diff --git a/integration-tests/examples/multitenancy/docker-compose.yml b/integration-tests/examples/multitenancy/docker-compose.yml new file mode 100644 index 0000000000..0b5726971b --- /dev/null +++ b/integration-tests/examples/multitenancy/docker-compose.yml @@ -0,0 +1,61 @@ + services: + agency: + image: ghcr.io/hyperledger/aries-cloudagent-python:py3.9-0.11.0 + ports: + - "3001:3001" + command: > + start + --label Agency + --inbound-transport http 0.0.0.0 3000 + --outbound-transport http + --endpoint http://agency:3000 + --admin 0.0.0.0 3001 + --admin-insecure-mode + --tails-server-base-url http://tails:6543 + --genesis-url https://raw.githubusercontent.com/Indicio-tech/indicio-network/main/genesis_files/pool_transactions_testnet_genesis + --wallet-type askar + --wallet-name agency + --wallet-key insecure + --auto-provision + --multitenant + --multitenant-admin + --jwt-secret insecure + --multitenancy-config wallet_type=askar-profile key_derivation_method=RAW + --log-level debug + --debug-webhooks + healthcheck: + test: curl -s -o /dev/null -w '%{http_code}' "http://localhost:3001/status/live" | grep "200" > /dev/null + start_period: 30s + interval: 7s + timeout: 5s + retries: 5 + depends_on: + tails: + condition: service_started + + example: + container_name: controller + build: + context: ../.. + environment: + - AGENCY=http://agency:3001 + volumes: + - ./example.py:/usr/src/app/example.py:ro,z + command: python -m example + depends_on: + agency: + condition: service_healthy + + tails: + image: ghcr.io/bcgov/tails-server:latest + ports: + - 6543:6543 + environment: + - GENESIS_URL=https://raw.githubusercontent.com/Indicio-tech/indicio-network/main/genesis_files/pool_transactions_testnet_genesis + command: > + tails-server + --host 0.0.0.0 + --port 6543 + --storage-path /tmp/tails-files + --log-level INFO + diff --git a/integration-tests/examples/multitenancy/example.py b/integration-tests/examples/multitenancy/example.py new file mode 100644 index 0000000000..9432018c13 --- /dev/null +++ b/integration-tests/examples/multitenancy/example.py @@ -0,0 +1,81 @@ +"""Minimal reproducible example script. + +This script is for you to use to reproduce a bug or demonstrate a feature. +""" + +import asyncio +from os import getenv + +from acapy_controller import Controller +from acapy_controller.logging import logging_to_stdout +from acapy_controller.models import CreateWalletResponse +from acapy_controller.protocols import ( + didexchange, + indy_anoncred_credential_artifacts, + indy_anoncred_onboard, + indy_issue_credential_v2, + indy_present_proof_v2, +) + +AGENCY = getenv("AGENCY", "http://agency:3001") + + +async def main(): + """Test Controller protocols.""" + async with Controller(base_url=AGENCY) as agency: + alice = await agency.post( + "/multitenancy/wallet", + json={ + "label": "Alice", + "wallet_type": "askar", + }, + response=CreateWalletResponse, + ) + bob = await agency.post( + "/multitenancy/wallet", + json={ + "label": "Bob", + "wallet_type": "askar", + }, + response=CreateWalletResponse, + ) + + async with Controller( + base_url=AGENCY, wallet_id=alice.wallet_id, subwallet_token=alice.token + ) as alice, Controller( + base_url=AGENCY, wallet_id=bob.wallet_id, subwallet_token=bob.token + ) as bob: + # Issuance prep + await indy_anoncred_onboard(alice) + _, cred_def = await indy_anoncred_credential_artifacts( + alice, + ["firstname", "lastname"], + support_revocation=True, + ) + + # Connecting + alice_conn, bob_conn = await didexchange(alice, bob) + + # Issue a credential + await indy_issue_credential_v2( + alice, + bob, + alice_conn.connection_id, + bob_conn.connection_id, + cred_def.credential_definition_id, + {"firstname": "Bob", "lastname": "Builder"}, + ) + + # Present the the credential's attributes + await indy_present_proof_v2( + bob, + alice, + bob_conn.connection_id, + alice_conn.connection_id, + requested_attributes=[{"name": "firstname"}], + ) + + +if __name__ == "__main__": + logging_to_stdout() + asyncio.run(main()) diff --git a/integration-tests/examples/presenting_revoked_credential/docker-compose.yml b/integration-tests/examples/presenting_revoked_credential/docker-compose.yml new file mode 100644 index 0000000000..97e78bae82 --- /dev/null +++ b/integration-tests/examples/presenting_revoked_credential/docker-compose.yml @@ -0,0 +1,103 @@ + services: + alice: + image: acapy-test + # image: acapy-test-image + # build: + # context: . + # dockerfile: Dockerfile.acapy + # args: + # acapy_url: https://github.com/Indicio-tech/aries-cloudagent-python@c1fed3c13d33e65979b08dd1eaf79dc84e3ce504 + ports: + - "3001:3001" + command: > + start + --label Alice + --inbound-transport http 0.0.0.0 3000 + --outbound-transport http + --endpoint http://alice:3000 + --admin 0.0.0.0 3001 + --admin-insecure-mode + --tails-server-base-url http://tails:6543 + --genesis-url https://raw.githubusercontent.com/Indicio-tech/indicio-network/main/genesis_files/pool_transactions_testnet_genesis + --wallet-type askar + --wallet-name alice + --wallet-key insecure + --auto-provision + --log-level debug + --debug-webhooks + --notify-revocation + healthcheck: + test: curl -s -o /dev/null -w '%{http_code}' "http://localhost:3001/status/live" | grep "200" > /dev/null + start_period: 30s + interval: 7s + timeout: 5s + retries: 5 + depends_on: + tails: + condition: service_started + + bob: + image: acapy-test + #image: bcgovimages/aries-cloudagent:py36-1.16-1_0.7.5 + #image: bcgovimages/aries-cloudagent:py36-1.16-1_1.0.0-rc0 + # image: acapy-test-image + # build: + # context: . + # dockerfile: Dockerfile.acapy + # args: + # acapy_url: https://github.com/Indicio-tech/aries-cloudagent-python@c1fed3c13d33e65979b08dd1eaf79dc84e3ce504 + ports: + - "3002:3001" + command: > + start + --label Bob + --inbound-transport http 0.0.0.0 3000 + --outbound-transport http + --endpoint http://bob:3000 + --admin 0.0.0.0 3001 + --admin-insecure-mode + --tails-server-base-url http://tails:6543 + --genesis-url https://raw.githubusercontent.com/Indicio-tech/indicio-network/main/genesis_files/pool_transactions_testnet_genesis + --wallet-type askar + --wallet-name bob + --wallet-key insecure + --auto-provision + --log-level debug + --debug-webhooks + --monitor-revocation-notification + healthcheck: + test: curl -s -o /dev/null -w '%{http_code}' "http://localhost:3001/status/live" | grep "200" > /dev/null + start_period: 30s + interval: 7s + timeout: 5s + retries: 5 + + example: + container_name: controller + build: + context: ../.. + environment: + - ALICE=http://alice:3001 + - BOB=http://bob:3001 + volumes: + - ./example.py:/usr/src/app/example.py:ro,z + command: python -m example + depends_on: + alice: + condition: service_healthy + bob: + condition: service_healthy + + tails: + image: ghcr.io/bcgov/tails-server:latest + ports: + - 6543:6543 + environment: + - GENESIS_URL=https://raw.githubusercontent.com/Indicio-tech/indicio-network/main/genesis_files/pool_transactions_testnet_genesis + command: > + tails-server + --host 0.0.0.0 + --port 6543 + --storage-path /tmp/tails-files + --log-level INFO + diff --git a/integration-tests/examples/presenting_revoked_credential/example.py b/integration-tests/examples/presenting_revoked_credential/example.py new file mode 100644 index 0000000000..7ee79b6c65 --- /dev/null +++ b/integration-tests/examples/presenting_revoked_credential/example.py @@ -0,0 +1,178 @@ +"""Minimal reproducible example script. + +This script is for you to use to reproduce a bug or demonstrate a feature. +""" + +import asyncio +import json +from os import getenv +import time + +from acapy_controller import Controller +from acapy_controller.logging import logging_to_stdout +from acapy_controller.models import V20PresExRecord, V20PresExRecordList +from acapy_controller.protocols import ( + didexchange, + indy_anoncred_credential_artifacts, + indy_anoncred_onboard, + indy_anoncreds_publish_revocation, + indy_anoncreds_revoke, + indy_issue_credential_v2, + indy_present_proof_v2, +) + +ALICE = getenv("ALICE", "http://alice:3001") +BOB = getenv("BOB", "http://bob:3001") + + +def summary(presentation: V20PresExRecord) -> str: + """Summarize a presentation exchange record.""" + request = presentation.pres_request + return "Summary: " + json.dumps( + { + "state": presentation.state, + "verified": presentation.verified, + "presentation_request": request.dict(by_alias=True) if request else None, + }, + indent=2, + sort_keys=True, + ) + + +async def main(): + """Test Controller protocols.""" + async with Controller(base_url=ALICE) as alice, Controller(base_url=BOB) as bob: + # Connecting + alice_conn, bob_conn = await didexchange(alice, bob) + + # Issuance prep + await indy_anoncred_onboard(alice) + schema, cred_def = await indy_anoncred_credential_artifacts( + alice, + ["firstname", "lastname"], + support_revocation=True, + ) + + # Issue a credential + alice_cred_ex, _ = await indy_issue_credential_v2( + alice, + bob, + alice_conn.connection_id, + bob_conn.connection_id, + cred_def.credential_definition_id, + {"firstname": "Bob", "lastname": "Builder"}, + ) + issued_time = int(time.time()) + + # Present the the credential's attributes + await indy_present_proof_v2( + bob, + alice, + bob_conn.connection_id, + alice_conn.connection_id, + requested_attributes=[{"name": "firstname"}], + ) + + # Revoke credential + await indy_anoncreds_revoke( + alice, + cred_ex=alice_cred_ex, + holder_connection_id=alice_conn.connection_id, + notify=True, + ) + await indy_anoncreds_publish_revocation(alice, cred_ex=alice_cred_ex) + # TODO: Make this into a helper in protocols.py? + await bob.record(topic="revocation-notification") + revoked_time = int(time.time()) + + # Request proof from holder again after revoking, + # using the interval before cred revoked + await indy_present_proof_v2( + bob, + alice, + bob_conn.connection_id, + alice_conn.connection_id, + requested_attributes=[ + { + "name": "firstname", + "restrictions": [ + {"cred_def_id": cred_def.credential_definition_id} + ], + } + ], + non_revoked={"from": issued_time, "to": issued_time}, + ) + + # Request proof, no interval + await indy_present_proof_v2( + bob, + alice, + bob_conn.connection_id, + alice_conn.connection_id, + requested_attributes=[ + { + "name": "firstname", + "restrictions": [ + {"cred_def_id": cred_def.credential_definition_id} + ], + } + ], + ) + + # Request proof, using invalid/revoked interval but using + # local non_revoked override (in requsted attrs) + # ("LOCAL"-->requested attrs) + await indy_present_proof_v2( + bob, + alice, + bob_conn.connection_id, + alice_conn.connection_id, + requested_attributes=[ + { + "name": "firstname", + "restrictions": [ + {"cred_def_id": cred_def.credential_definition_id} + ], + "non_revoked": { + "from": issued_time, + "to": issued_time, + }, + } + ], + non_revoked={"from": revoked_time - 1, "to": revoked_time}, + ) + + # Request proof, just local invalid interval + await indy_present_proof_v2( + bob, + alice, + bob_conn.connection_id, + alice_conn.connection_id, + requested_attributes=[ + { + "name": "firstname", + "restrictions": [ + {"cred_def_id": cred_def.credential_definition_id} + ], + "non_revoked": { + "from": revoked_time, + "to": revoked_time, + }, + } + ], + ) + + # Query presentations + presentations = await alice.get( + "/present-proof-2.0/records", + response=V20PresExRecordList, + ) + + # Presentation summary + for i, pres in enumerate(presentations.results): + print(summary(pres)) + + +if __name__ == "__main__": + logging_to_stdout() + asyncio.run(main()) diff --git a/integration-tests/examples/self_attested/docker-compose.yml b/integration-tests/examples/self_attested/docker-compose.yml new file mode 100644 index 0000000000..b261fb76ac --- /dev/null +++ b/integration-tests/examples/self_attested/docker-compose.yml @@ -0,0 +1,102 @@ + services: + alice: + image: acapy-test + # image: acapy-test-image + # build: + # context: . + # dockerfile: Dockerfile.acapy + # args: + # acapy_url: https://github.com/Indicio-tech/aries-cloudagent-python@c1fed3c13d33e65979b08dd1eaf79dc84e3ce504 + ports: + - "3001:3001" + command: > + start + --label Alice + --inbound-transport http 0.0.0.0 3000 + --outbound-transport http + --endpoint http://alice:3000 + --admin 0.0.0.0 3001 + --admin-insecure-mode + --tails-server-base-url http://tails:6543 + --genesis-url https://raw.githubusercontent.com/Indicio-tech/indicio-network/main/genesis_files/pool_transactions_testnet_genesis + --wallet-type askar + --wallet-name alice + --wallet-key insecure + --auto-provision + --log-level debug + --debug-webhooks + healthcheck: + test: curl -s -o /dev/null -w '%{http_code}' "http://localhost:3001/status/live" | grep "200" > /dev/null + start_period: 30s + interval: 7s + timeout: 5s + retries: 5 + depends_on: + tails: + condition: service_started + + bob: + image: acapy-test + #image: bcgovimages/aries-cloudagent:py36-1.16-1_0.7.5 + #image: bcgovimages/aries-cloudagent:py36-1.16-1_1.0.0-rc0 + # image: acapy-test-image + # build: + # context: . + # dockerfile: Dockerfile.acapy + # args: + # acapy_url: https://github.com/Indicio-tech/aries-cloudagent-python@c1fed3c13d33e65979b08dd1eaf79dc84e3ce504 + ports: + - "3002:3001" + command: > + start + --label Bob + --inbound-transport http 0.0.0.0 3000 + --outbound-transport http + --endpoint http://bob:3000 + --admin 0.0.0.0 3001 + --admin-insecure-mode + --tails-server-base-url http://tails:6543 + --genesis-url https://raw.githubusercontent.com/Indicio-tech/indicio-network/main/genesis_files/pool_transactions_testnet_genesis + --wallet-type askar + --wallet-name bob + --wallet-key insecure + --auto-provision + --log-level debug + --debug-webhooks + --monitor-revocation-notification + healthcheck: + test: curl -s -o /dev/null -w '%{http_code}' "http://localhost:3001/status/live" | grep "200" > /dev/null + start_period: 30s + interval: 7s + timeout: 5s + retries: 5 + + example: + container_name: controller + build: + context: ../.. + environment: + - ALICE=http://alice:3001 + - BOB=http://bob:3001 + volumes: + - ./example.py:/usr/src/app/example.py:ro,z + command: python -m example + depends_on: + alice: + condition: service_healthy + bob: + condition: service_healthy + + tails: + image: ghcr.io/bcgov/tails-server:latest + ports: + - 6543:6543 + environment: + - GENESIS_URL=https://raw.githubusercontent.com/Indicio-tech/indicio-network/main/genesis_files/pool_transactions_testnet_genesis + command: > + tails-server + --host 0.0.0.0 + --port 6543 + --storage-path /tmp/tails-files + --log-level INFO + diff --git a/integration-tests/examples/self_attested/example.py b/integration-tests/examples/self_attested/example.py new file mode 100644 index 0000000000..1cfa7e641c --- /dev/null +++ b/integration-tests/examples/self_attested/example.py @@ -0,0 +1,152 @@ +"""Minimal reproducible example script. + +This script is for you to use to reproduce a bug or demonstrate a feature. +""" + +import asyncio +from os import getenv +from secrets import randbelow +from typing import List +from uuid import uuid4 + +from acapy_controller import Controller +from acapy_controller.logging import logging_to_stdout +from acapy_controller.models import V10PresentationExchange +from acapy_controller.protocols import ( + IndyCredPrecis, + didexchange, + indy_anoncred_credential_artifacts, + indy_anoncred_onboard, + indy_auto_select_credentials_for_presentation_request, + indy_issue_credential_v2, +) + +ALICE = getenv("ALICE", "http://alice:3001") +BOB = getenv("BOB", "http://bob:3001") + + +async def main(): + """Test Controller protocols.""" + async with Controller(base_url=ALICE) as alice, Controller(base_url=BOB) as bob: + # Connecting + alice_conn, bob_conn = await didexchange(alice, bob) + + # Issuance prep + await indy_anoncred_onboard(alice) + schema, cred_def = await indy_anoncred_credential_artifacts( + alice, + ["firstname", "lastname"], + support_revocation=True, + ) + schema, cred_def_age = await indy_anoncred_credential_artifacts( + alice, + ["age"], + support_revocation=True, + ) + + # Issue a credential + await indy_issue_credential_v2( + alice, + bob, + alice_conn.connection_id, + bob_conn.connection_id, + cred_def.credential_definition_id, + {"firstname": "Bob", "lastname": "Builder"}, + ) + await indy_issue_credential_v2( + alice, + bob, + alice_conn.connection_id, + bob_conn.connection_id, + cred_def_age.credential_definition_id, + {"age": "42"}, + ) + + # Present the thing + self_uuid = str(uuid4()) + alice_pres_ex = await alice.post( + "/present-proof/send-request", + json={ + "auto_verify": False, + "comment": "Presentation request from minimal", + "connection_id": alice_conn.connection_id, + "proof_request": { + "name": "proof", + "version": "0.1.0", + "nonce": str(randbelow(10**10)), + "requested_attributes": { + str(uuid4()): { + "name": "firstname", + "restrictions": [ + {"cred_def_id": cred_def.credential_definition_id} + ], + }, + str(uuid4()): { + "name": "age", + "restrictions": [ + {"cred_def_id": cred_def_age.credential_definition_id} + ], + }, + self_uuid: {"name": "self-attested"}, + }, + "requested_predicates": {}, + "non_revoked": None, + }, + "trace": False, + }, + response=V10PresentationExchange, + ) + alice_pres_ex_id = alice_pres_ex.presentation_exchange_id + + bob_pres_ex = await bob.record_with_values( + topic="present_proof", + record_type=V10PresentationExchange, + connection_id=bob_conn.connection_id, + state="request_received", + ) + assert bob_pres_ex.presentation_request + bob_pres_ex_id = bob_pres_ex.presentation_exchange_id + + relevant_creds = await bob.get( + f"/present-proof/records/{bob_pres_ex_id}/credentials", + response=List[IndyCredPrecis], + ) + pres_spec = indy_auto_select_credentials_for_presentation_request( + bob_pres_ex.presentation_request.serialize(), relevant_creds + ) + pres_spec.self_attested_attributes = {self_uuid: "self-attested data goes here"} + bob_pres_ex = await bob.post( + f"/present-proof/records/{bob_pres_ex_id}/send-presentation", + json=pres_spec, + response=V10PresentationExchange, + ) + + await alice.record_with_values( + topic="present_proof", + record_type=V10PresentationExchange, + presentation_exchange_id=alice_pres_ex_id, + state="presentation_received", + ) + alice_pres_ex = await alice.post( + f"/present-proof/records/{alice_pres_ex_id}/verify-presentation", + json={}, + response=V10PresentationExchange, + ) + alice_pres_ex = await alice.record_with_values( + topic="present_proof", + record_type=V10PresentationExchange, + presentation_exchange_id=alice_pres_ex_id, + state="verified", + ) + + bob_pres_ex = await bob.record_with_values( + topic="present_proof", + record_type=V10PresentationExchange, + presentation_exchange_id=bob_pres_ex_id, + state="presentation_acked", + ) + + +if __name__ == "__main__": + logging_to_stdout() + asyncio.run(main()) diff --git a/integration-tests/examples/simple/docker-compose.yml b/integration-tests/examples/simple/docker-compose.yml new file mode 100644 index 0000000000..95702ed09c --- /dev/null +++ b/integration-tests/examples/simple/docker-compose.yml @@ -0,0 +1,91 @@ + services: + alice: + image: acapy-test + ports: + - "3001:3001" + environment: + RUST_LOG: 'aries-askar::log::target=error' + command: > + start + --label Alice + --inbound-transport http 0.0.0.0 3000 + --outbound-transport http + --endpoint http://alice:3000 + --admin 0.0.0.0 3001 + --admin-insecure-mode + --tails-server-base-url http://tails:6543 + --genesis-url https://raw.githubusercontent.com/Indicio-tech/indicio-network/main/genesis_files/pool_transactions_testnet_genesis + --wallet-type askar + --wallet-name alice + --wallet-key insecure + --auto-provision + --log-level debug + --debug-webhooks + healthcheck: + test: curl -s -o /dev/null -w '%{http_code}' "http://localhost:3001/status/live" | grep "200" > /dev/null + start_period: 30s + interval: 7s + timeout: 5s + retries: 5 + depends_on: + tails: + condition: service_started + + bob: + image: acapy-test + ports: + - "3002:3001" + environment: + RUST_LOG: 'aries-askar::log::target=error' + command: > + start + --label Bob + --inbound-transport http 0.0.0.0 3000 + --outbound-transport http + --endpoint http://bob:3000 + --admin 0.0.0.0 3001 + --admin-insecure-mode + --tails-server-base-url http://tails:6543 + --genesis-url https://raw.githubusercontent.com/Indicio-tech/indicio-network/main/genesis_files/pool_transactions_testnet_genesis + --wallet-type askar + --wallet-name bob + --wallet-key insecure + --auto-provision + --log-level debug + --debug-webhooks + --monitor-revocation-notification + healthcheck: + test: curl -s -o /dev/null -w '%{http_code}' "http://localhost:3001/status/live" | grep "200" > /dev/null + start_period: 30s + interval: 7s + timeout: 5s + retries: 5 + + tails: + image: ghcr.io/bcgov/tails-server:latest + ports: + - 6543:6543 + environment: + - GENESIS_URL=https://raw.githubusercontent.com/Indicio-tech/indicio-network/main/genesis_files/pool_transactions_testnet_genesis + command: > + tails-server + --host 0.0.0.0 + --port 6543 + --storage-path /tmp/tails-files + --log-level INFO + + example: + container_name: controller + build: + context: ../.. + environment: + - ALICE=http://alice:3001 + - BOB=http://bob:3001 + volumes: + - ./example.py:/usr/src/app/example.py:ro,z + command: python -m example + depends_on: + alice: + condition: service_healthy + bob: + condition: service_healthy diff --git a/integration-tests/examples/simple/example.py b/integration-tests/examples/simple/example.py new file mode 100644 index 0000000000..20830dfcc9 --- /dev/null +++ b/integration-tests/examples/simple/example.py @@ -0,0 +1,26 @@ +"""Minimal reproducible example script. + +This script is for you to use to reproduce a bug or demonstrate a feature. +""" + +import asyncio +from os import getenv + +from acapy_controller import Controller +from acapy_controller.logging import logging_to_stdout +from acapy_controller.protocols import connection, didexchange + +ALICE = getenv("ALICE", "http://alice:3001") +BOB = getenv("BOB", "http://bob:3001") + + +async def main(): + """Test Controller protocols.""" + async with Controller(base_url=ALICE) as alice, Controller(base_url=BOB) as bob: + await connection(alice, bob) + await didexchange(alice, bob) + + +if __name__ == "__main__": + logging_to_stdout() + asyncio.run(main()) diff --git a/integration-tests/examples/tunnels/__skip__ b/integration-tests/examples/tunnels/__skip__ new file mode 100644 index 0000000000..e69de29bb2 diff --git a/integration-tests/examples/tunnels/docker-compose.yml b/integration-tests/examples/tunnels/docker-compose.yml new file mode 100644 index 0000000000..72d9da2308 --- /dev/null +++ b/integration-tests/examples/tunnels/docker-compose.yml @@ -0,0 +1,126 @@ + services: + alice: + image: acapy-test + ports: + - "3001:3001" + volumes: + - ./tunnel_endpoint.sh:/tunnel_endpoint.sh:ro,z + environment: + AGENT_TUNNEL_ENDPOINT: http://tunnel-alice:4040 + TAILS_TUNNEL_ENDPOINT: http://tunnel-tails:4040 + entrypoint: > + /bin/sh -c '/tunnel_endpoint.sh aca-py "$$@"' -- + command: > + start + --label Alice + --inbound-transport http 0.0.0.0 3000 + --outbound-transport http + --admin 0.0.0.0 3001 + --admin-insecure-mode + --tails-server-base-url http://tails:6543 + --genesis-url https://raw.githubusercontent.com/Indicio-tech/indicio-network/main/genesis_files/pool_transactions_testnet_genesis + --wallet-type askar + --wallet-name alice + --wallet-key insecure + --auto-provision + --log-level debug + --debug-webhooks + healthcheck: + test: curl -s -o /dev/null -w '%{http_code}' "http://localhost:3001/status/live" | grep "200" > /dev/null + start_period: 30s + interval: 7s + timeout: 5s + retries: 5 + depends_on: + tails: + condition: service_started + tunnel-alice: + condition: service_started + + bob: + image: acapy-test + ports: + - "3002:3001" + volumes: + - ./tunnel_endpoint.sh:/tunnel_endpoint.sh:ro,z + environment: + AGENT_TUNNEL_ENDPOINT: http://tunnel-bob:4040 + TAILS_TUNNEL_ENDPOINT: http://tunnel-tails:4040 + entrypoint: > + /bin/sh -c '/tunnel_endpoint.sh aca-py "$$@"' -- + command: > + start + --label Bob + --inbound-transport http 0.0.0.0 3000 + --outbound-transport http + --admin 0.0.0.0 3001 + --admin-insecure-mode + --tails-server-base-url http://tails:6543 + --genesis-url https://raw.githubusercontent.com/Indicio-tech/indicio-network/main/genesis_files/pool_transactions_testnet_genesis + --wallet-type askar + --wallet-name bob + --wallet-key insecure + --auto-provision + --log-level debug + --debug-webhooks + --monitor-revocation-notification + healthcheck: + test: curl -s -o /dev/null -w '%{http_code}' "http://localhost:3001/status/live" | grep "200" > /dev/null + start_period: 30s + interval: 7s + timeout: 5s + retries: 5 + depends_on: + tails: + condition: service_started + tunnel-bob: + condition: service_started + + tunnel-alice: + image: dbluhm/agent-tunnel + command: -s alice:3000 -p 4040 -h ${AGENT_TUNNEL_HOST} + + tunnel-bob: + image: dbluhm/agent-tunnel + command: -s bob:3000 -p 4040 -h ${AGENT_TUNNEL_HOST} + + example: + container_name: controller + build: + context: ../.. + environment: + - ALICE=http://alice:3001 + - BOB=http://bob:3001 + volumes: + - ../../controller:/usr/src/app/controller:ro,z + - ./example.py:/usr/src/app/example.py:ro,z + command: python -m example + depends_on: + alice: + condition: service_healthy + bob: + condition: service_healthy + + tunnel-tails: + image: ngrok/ngrok + command: start --all + environment: + - NGROK_CONFIG=/etc/ngrok.yml + volumes: + - ./ngrok.yml:/etc/ngrok.yml + + tails: + image: ghcr.io/bcgov/tails-server:latest + ports: + - 6543:6543 + environment: + - GENESIS_URL=https://raw.githubusercontent.com/Indicio-tech/indicio-network/main/genesis_files/pool_transactions_testnet_genesis + command: > + tails + --host 0.0.0.0 + --port 6543 + --storage-path /tmp/tails-files + --log-level INFO + depends_on: + tunnel-tails: + condition: service_started diff --git a/integration-tests/examples/tunnels/example.py b/integration-tests/examples/tunnels/example.py new file mode 100644 index 0000000000..7ee79b6c65 --- /dev/null +++ b/integration-tests/examples/tunnels/example.py @@ -0,0 +1,178 @@ +"""Minimal reproducible example script. + +This script is for you to use to reproduce a bug or demonstrate a feature. +""" + +import asyncio +import json +from os import getenv +import time + +from acapy_controller import Controller +from acapy_controller.logging import logging_to_stdout +from acapy_controller.models import V20PresExRecord, V20PresExRecordList +from acapy_controller.protocols import ( + didexchange, + indy_anoncred_credential_artifacts, + indy_anoncred_onboard, + indy_anoncreds_publish_revocation, + indy_anoncreds_revoke, + indy_issue_credential_v2, + indy_present_proof_v2, +) + +ALICE = getenv("ALICE", "http://alice:3001") +BOB = getenv("BOB", "http://bob:3001") + + +def summary(presentation: V20PresExRecord) -> str: + """Summarize a presentation exchange record.""" + request = presentation.pres_request + return "Summary: " + json.dumps( + { + "state": presentation.state, + "verified": presentation.verified, + "presentation_request": request.dict(by_alias=True) if request else None, + }, + indent=2, + sort_keys=True, + ) + + +async def main(): + """Test Controller protocols.""" + async with Controller(base_url=ALICE) as alice, Controller(base_url=BOB) as bob: + # Connecting + alice_conn, bob_conn = await didexchange(alice, bob) + + # Issuance prep + await indy_anoncred_onboard(alice) + schema, cred_def = await indy_anoncred_credential_artifacts( + alice, + ["firstname", "lastname"], + support_revocation=True, + ) + + # Issue a credential + alice_cred_ex, _ = await indy_issue_credential_v2( + alice, + bob, + alice_conn.connection_id, + bob_conn.connection_id, + cred_def.credential_definition_id, + {"firstname": "Bob", "lastname": "Builder"}, + ) + issued_time = int(time.time()) + + # Present the the credential's attributes + await indy_present_proof_v2( + bob, + alice, + bob_conn.connection_id, + alice_conn.connection_id, + requested_attributes=[{"name": "firstname"}], + ) + + # Revoke credential + await indy_anoncreds_revoke( + alice, + cred_ex=alice_cred_ex, + holder_connection_id=alice_conn.connection_id, + notify=True, + ) + await indy_anoncreds_publish_revocation(alice, cred_ex=alice_cred_ex) + # TODO: Make this into a helper in protocols.py? + await bob.record(topic="revocation-notification") + revoked_time = int(time.time()) + + # Request proof from holder again after revoking, + # using the interval before cred revoked + await indy_present_proof_v2( + bob, + alice, + bob_conn.connection_id, + alice_conn.connection_id, + requested_attributes=[ + { + "name": "firstname", + "restrictions": [ + {"cred_def_id": cred_def.credential_definition_id} + ], + } + ], + non_revoked={"from": issued_time, "to": issued_time}, + ) + + # Request proof, no interval + await indy_present_proof_v2( + bob, + alice, + bob_conn.connection_id, + alice_conn.connection_id, + requested_attributes=[ + { + "name": "firstname", + "restrictions": [ + {"cred_def_id": cred_def.credential_definition_id} + ], + } + ], + ) + + # Request proof, using invalid/revoked interval but using + # local non_revoked override (in requsted attrs) + # ("LOCAL"-->requested attrs) + await indy_present_proof_v2( + bob, + alice, + bob_conn.connection_id, + alice_conn.connection_id, + requested_attributes=[ + { + "name": "firstname", + "restrictions": [ + {"cred_def_id": cred_def.credential_definition_id} + ], + "non_revoked": { + "from": issued_time, + "to": issued_time, + }, + } + ], + non_revoked={"from": revoked_time - 1, "to": revoked_time}, + ) + + # Request proof, just local invalid interval + await indy_present_proof_v2( + bob, + alice, + bob_conn.connection_id, + alice_conn.connection_id, + requested_attributes=[ + { + "name": "firstname", + "restrictions": [ + {"cred_def_id": cred_def.credential_definition_id} + ], + "non_revoked": { + "from": revoked_time, + "to": revoked_time, + }, + } + ], + ) + + # Query presentations + presentations = await alice.get( + "/present-proof-2.0/records", + response=V20PresExRecordList, + ) + + # Presentation summary + for i, pres in enumerate(presentations.results): + print(summary(pres)) + + +if __name__ == "__main__": + logging_to_stdout() + asyncio.run(main()) diff --git a/integration-tests/examples/tunnels/ngrok.yml b/integration-tests/examples/tunnels/ngrok.yml new file mode 100644 index 0000000000..a318dc2b4d --- /dev/null +++ b/integration-tests/examples/tunnels/ngrok.yml @@ -0,0 +1,8 @@ +log: stdout +tunnels: + tails: + addr: tails:6543 + inspect: false + proto: http +version: 2 +web_addr: 0.0.0.0:4040 diff --git a/integration-tests/examples/tunnels/tunnel_endpoint.sh b/integration-tests/examples/tunnels/tunnel_endpoint.sh new file mode 100755 index 0000000000..ff415fa974 --- /dev/null +++ b/integration-tests/examples/tunnels/tunnel_endpoint.sh @@ -0,0 +1,36 @@ +#!/bin/bash + +AGENT_TUNNEL_ENDPOINT=${AGENT_TUNNEL_ENDPOINT:-http://localhost:4040} +TAILS_TUNNEL_ENDPOINT=${TAILS_TUNNEL_ENDPOINT:-http://localhost:4040} + +WAIT_INTERVAL=${WAIT_INTERVAL:-3} +WAIT_ATTEMPTS=${WAIT_ATTEMPTS:-10} + +liveliness_check () { + for CURRENT_ATTEMPT in $(seq 1 "$WAIT_ATTEMPTS"); do + if ! curl -s -o /dev/null -w '%{http_code}' "${1}/status" | grep "200" > /dev/null; then + if [[ $CURRENT_ATTEMPT -gt $WAIT_ATTEMPTS ]] + then + echo "Failed while waiting for 200 status from ${1}" + exit 1 + fi + + echo "Waiting for tunnel..." 1>&2 + sleep "$WAIT_INTERVAL" & + wait $! + else + break + fi + done +} + + +liveliness_check ${AGENT_TUNNEL_ENDPOINT} +liveliness_check ${TAILS_TUNNEL_ENDPOINT} + +ACAPY_ENDPOINT=$(curl --silent "${AGENT_TUNNEL_ENDPOINT}/url" | python -c "import sys, json; print(json.load(sys.stdin)['url'])") +ACAPY_TAILS_SERVER_BASE_URL=$(curl --silent "${TAILS_TUNNEL_ENDPOINT}/api/tunnels" | python -c "import sys, json; print(json.load(sys.stdin)['tunnels'][0]['public_url'])") +export ACAPY_ENDPOINT=${ACAPY_ENDPOINT} +export ACAPY_TAILS_SERVER_BASE_URL=${ACAPY_TAILS_SERVER_BASE_URL} +exec "$@" + diff --git a/integration-tests/pyproject.toml b/integration-tests/pyproject.toml new file mode 100644 index 0000000000..c7e645ab3a --- /dev/null +++ b/integration-tests/pyproject.toml @@ -0,0 +1,20 @@ +[tool.poetry] +name = "integration-tests" +version = "0.1.0" +description = "" +authors = [] +readme = "README.md" + +[tool.poetry.dependencies] +python = "^3.10" +acapy-controller = {git = "https://github.com/indicio-tech/acapy-minimal-example.git"} +pytest = "^8.3.2" +pytest-asyncio = "^0.23.8" +pydantic = "^2.8.2" + +[tool.pytest.ini_options] +markers = "examples: test the examples" + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" diff --git a/mkdocs.yml b/mkdocs.yml index e25fd638a4..aea28af754 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -145,7 +145,7 @@ nav: - Testing/Troubleshooting: - Running and Creating Unit Tests: testing/UnitTests.md - Managing Logging: testing/Logging.md - - ACA-Py Integration Tests: testing/INTEGRATION-TESTS.md + - ACA-Py Integration BDD Tests: testing/BDDTests.md - Protocol Tracing: testing/AgentTracing.md - Troubleshooting: testing/Troubleshooting.md - Contributing: