diff --git a/.github/workflows/performance-tests.yml b/.github/workflows/performance-tests.yml index 2b9190c222..a92646eac4 100644 --- a/.github/workflows/performance-tests.yml +++ b/.github/workflows/performance-tests.yml @@ -6,9 +6,6 @@ concurrency: on: pull_request: - paths: - - ".github/workflows/performance-tests.yml" - - "tests/performance-tests/**" push: branches: - "main" @@ -41,11 +38,10 @@ jobs: username: ${{ secrets.ATALA_GITHUB_ACTOR }} password: ${{ secrets.ATALA_GITHUB_TOKEN }} - - name: Install Compose - uses: ndeloof/install-compose-action@v0.0.1 + - uses: KengoTODA/actions-setup-docker-compose@v1 + name: Install `docker-compose` with: - version: v2.12.2 - legacy: true # will also install in PATH as `docker-compose` + version: '2.14.2' - name: Build local version of PRISM Agent env: @@ -61,8 +57,13 @@ jobs: - name: Start services for issuer env: PORT: 8080 - DEFAULT_WALLET_ENABLED: "true" + ADMIN_TOKEN: admin + DEFAULT_WALLET_ENABLED: true DEFAULT_WALLET_AUTH_API_KEY: default + API_KEY_AUTO_PROVISIONING: false + API_KEY_ENABLED: true + DOCKERHOST: "host.docker.internal" + PG_PORT: 5432 uses: isbang/compose-action@v1.4.1 with: compose-file: "./infrastructure/shared/docker-compose.yml" @@ -73,8 +74,13 @@ jobs: - name: Start services for holder env: PORT: 8090 - DEFAULT_WALLET_ENABLED: "true" + ADMIN_TOKEN: admin + DEFAULT_WALLET_ENABLED: true DEFAULT_WALLET_AUTH_API_KEY: default + API_KEY_AUTO_PROVISIONING: false + API_KEY_ENABLED: true + DOCKERHOST: "host.docker.internal" + PG_PORT: 5433 uses: isbang/compose-action@v1.4.1 with: compose-file: "./infrastructure/shared/docker-compose.yml" @@ -82,6 +88,23 @@ jobs: up-flags: "--wait" down-flags: "--volumes" + - name: Start services for verifier + env: + PORT: 8100 + ADMIN_TOKEN: admin + DEFAULT_WALLET_ENABLED: true + DEFAULT_WALLET_AUTH_API_KEY: default + API_KEY_AUTO_PROVISIONING: false + API_KEY_ENABLED: true + DOCKERHOST: "host.docker.internal" + PG_PORT: 5434 + uses: isbang/compose-action@v1.4.1 + with: + compose-file: "./infrastructure/shared/docker-compose.yml" + compose-flags: "--env-file ./infrastructure/local/.env -p verifier" + up-flags: "--wait" + down-flags: "--volumes" + - name: Setup Node.js uses: actions/setup-node@v3 with: @@ -90,23 +113,30 @@ jobs: scope: 'input-output-hk' - name: Install dependencies - uses: borales/actions-yarn@v4 + uses: borales/actions-yarn@v4.2.0 with: cmd: install dir: ${{ env.BENCHMARKING_DIR }} - name: Compile tests to JS - uses: borales/actions-yarn@v4 + uses: borales/actions-yarn@v4.2.0 with: cmd: webpack dir: ${{ env.BENCHMARKING_DIR }} - - name: Connection Flow Smoke Test + - name: All Smoke Tests env: ISSUER_AGENT_API_KEY: default HOLDER_AGENT_API_KEY: default + VERIFIER_AGENT_API_KEY: default run: | # Have to use manual download because GitHub action doesnt support localhost execution curl https://github.com/grafana/k6/releases/download/v0.45.0/k6-v0.45.0-linux-amd64.tar.gz -L | tar xvz --strip-components 1 ls -la - ./k6 run ${{ env.BENCHMARKING_DIR }}/dist/connection-flow-test.js + ./k6 run -e SCENARIO_LABEL=create-prism-did-smoke ${{ env.BENCHMARKING_DIR }}/dist/create-prism-did-test.js + ./k6 run -e SCENARIO_LABEL=credential-offer-smoke ${{ env.BENCHMARKING_DIR }}/dist/credential-offer-test.js + ./k6 run -e SCENARIO_LABEL=credential-schema-smoke ${{ env.BENCHMARKING_DIR }}/dist/credential-schema-test.js + ./k6 run -e SCENARIO_LABEL=did-publishing-smoke ${{ env.BENCHMARKING_DIR }}/dist/did-publishing-test.js + ./k6 run -e SCENARIO_LABEL=connection-flow-smoke ${{ env.BENCHMARKING_DIR }}/dist/connection-flow-test.js + ./k6 run -e SCENARIO_LABEL=issuance-flow-smoke ${{ env.BENCHMARKING_DIR }}/dist/issuance-flow-test.js + ./k6 run -e SCENARIO_LABEL=present-proof-flow-smoke ${{ env.BENCHMARKING_DIR }}/dist/present-proof-flow-test.js diff --git a/infrastructure/shared/docker-compose.yml b/infrastructure/shared/docker-compose.yml index 20888a3229..8b626ea58c 100644 --- a/infrastructure/shared/docker-compose.yml +++ b/infrastructure/shared/docker-compose.yml @@ -92,8 +92,8 @@ services: AGENT_DB_NAME: agent AGENT_DB_USER: postgres AGENT_DB_PASSWORD: postgres - DIDCOMM_SERVICE_URL: ${DIDCOMM_SERVICE_URL:-http://${DOCKERHOST}:${PORT}/didcomm} - REST_SERVICE_URL: ${REST_SERVICE_URL:-http://${DOCKERHOST}:${PORT}/prism-agent} + DIDCOMM_SERVICE_URL: http://${DOCKERHOST}:${PORT}/didcomm + REST_SERVICE_URL: http://${DOCKERHOST}:${PORT}/prism-agent PRISM_NODE_HOST: prism-node PRISM_NODE_PORT: 50053 VAULT_ADDR: ${VAULT_ADDR:-http://vault-server:8200} diff --git a/infrastructure/single-tenant-testing-stack/.env b/infrastructure/single-tenant-testing-stack/.env new file mode 100644 index 0000000000..a40f9fe1cb --- /dev/null +++ b/infrastructure/single-tenant-testing-stack/.env @@ -0,0 +1,3 @@ +PRISM_AGENT_VERSION=1.17.0 +PRISM_NODE_VERSION=2.2.1 +VAULT_DEV_ROOT_TOKEN_ID=root diff --git a/infrastructure/single-tenant-testing-stack/apisix/conf/apisix.yaml b/infrastructure/single-tenant-testing-stack/apisix/conf/apisix.yaml new file mode 100644 index 0000000000..92a710f380 --- /dev/null +++ b/infrastructure/single-tenant-testing-stack/apisix/conf/apisix.yaml @@ -0,0 +1,84 @@ +plugins: + - name: proxy-rewrite + +routes: + - uri: /issuer/prism-agent/* + upstream_id: 1 + plugins: + proxy-rewrite: + regex_uri: ["^/issuer/prism-agent/(.*)", "/$1"] + - uri: /issuer/didcomm* + upstream_id: 2 + plugins: + proxy-rewrite: + regex_uri: ["^/issuer/didcomm(.*)", "/$1"] + - uri: /verifier/prism-agent/* + upstream_id: 3 + plugins: + proxy-rewrite: + regex_uri: ["^/verifier/prism-agent/(.*)", "/$1"] + - uri: /verifier/didcomm* + upstream_id: 4 + plugins: + proxy-rewrite: + regex_uri: ["^/verifier/didcomm(.*)", "/$1"] + - uri: /holder/prism-agent/* + upstream_id: 5 + plugins: + proxy-rewrite: + regex_uri: ["^/holder/prism-agent/(.*)", "/$1"] + - uri: /holder/didcomm* + upstream_id: 6 + plugins: + proxy-rewrite: + regex_uri: ["^/holder/didcomm(.*)", "/$1"] +upstreams: + - id: 1 + nodes: + "issuer-oea:8085": 1 # tapir + type: roundrobin + timeout: + connect: 900 + send: 900 + read: 900 + - id: 2 + nodes: + "issuer-oea:8090": 1 # didcom + type: roundrobin + timeout: + connect: 900 + send: 900 + read: 900 + - id: 3 + nodes: + "verifier-oea:8085": 1 # tapir + type: roundrobin + timeout: + connect: 900 + send: 900 + read: 900 + - id: 4 + nodes: + "verifier-oea:8090": 1 # didcom + type: roundrobin + timeout: + connect: 900 + send: 900 + read: 900 + - id: 5 + nodes: + "holder-oea:8085": 1 # tapir + type: roundrobin + timeout: + connect: 900 + send: 900 + read: 900 + - id: 6 + nodes: + "holder-oea:8090": 1 # didcom + type: roundrobin + timeout: + connect: 900 + send: 900 + read: 900 +#END diff --git a/infrastructure/single-tenant-testing-stack/apisix/conf/config.yaml b/infrastructure/single-tenant-testing-stack/apisix/conf/config.yaml new file mode 100644 index 0000000000..7385594d84 --- /dev/null +++ b/infrastructure/single-tenant-testing-stack/apisix/conf/config.yaml @@ -0,0 +1,27 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apisix: + node_listen: 9080 # APISIX listening port + enable_ipv6: false + enable_admin: false + config_center: yaml + +deployment: + role: data_plane + role_data_plane: + config_provider: yaml diff --git a/infrastructure/single-tenant-testing-stack/docker-compose.yml b/infrastructure/single-tenant-testing-stack/docker-compose.yml new file mode 100644 index 0000000000..b5ca092a70 --- /dev/null +++ b/infrastructure/single-tenant-testing-stack/docker-compose.yml @@ -0,0 +1,304 @@ +--- +version: "3.8" + +services: + issuer-db: + image: postgres:13 + environment: + POSTGRES_MULTIPLE_DATABASES: "castor,pollux,connect,agent" + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgres + volumes: + - issuer_pg_data_db:/var/lib/postgresql/data + - ./postgres/init-script.sh:/docker-entrypoint-initdb.d/init-script.sh + - ./postgres/max_conns.sql:/docker-entrypoint-initdb.d/max_conns.sql + ports: + - 5432:5432 + healthcheck: + test: ["CMD", "pg_isready", "-U", "postgres", "-d", "castor"] + interval: 10s + timeout: 5s + retries: 5 + + verifier-db: + image: postgres:13 + environment: + POSTGRES_MULTIPLE_DATABASES: "castor,pollux,connect,agent" + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgres + volumes: + - verifier_pg_data_db:/var/lib/postgresql/data + - ./postgres/init-script.sh:/docker-entrypoint-initdb.d/init-script.sh + - ./postgres/max_conns.sql:/docker-entrypoint-initdb.d/max_conns.sql + ports: + - 5433:5432 + healthcheck: + test: ["CMD", "pg_isready", "-U", "postgres", "-d", "castor"] + interval: 10s + timeout: 5s + retries: 5 + + holder-db: + image: postgres:13 + environment: + POSTGRES_MULTIPLE_DATABASES: "castor,pollux,connect,agent" + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgres + volumes: + - holder_pg_data_db:/var/lib/postgresql/data + - ./postgres/init-script.sh:/docker-entrypoint-initdb.d/init-script.sh + - ./postgres/max_conns.sql:/docker-entrypoint-initdb.d/max_conns.sql + ports: + - 5434:5432 + healthcheck: + test: ["CMD", "pg_isready", "-U", "postgres", "-d", "castor"] + interval: 10s + timeout: 5s + retries: 5 + + node-db: + image: postgres:13 + environment: + POSTGRES_MULTIPLE_DATABASES: "node_db" + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgres + volumes: + - node_pg_data_db:/var/lib/postgresql/data + - ./postgres/init-script.sh:/docker-entrypoint-initdb.d/init-script.sh + - ./postgres/max_conns.sql:/docker-entrypoint-initdb.d/max_conns.sql + healthcheck: + test: ["CMD", "pg_isready", "-U", "postgres", "-d", "node_db"] + interval: 10s + timeout: 5s + retries: 5 + + prism-node: + image: ghcr.io/input-output-hk/prism-node:${PRISM_NODE_VERSION} + environment: + NODE_PSQL_HOST: node-db:5432 + NODE_LEDGER: in-memory + NODE_REFRESH_AND_SUBMIT_PERIOD: 1s + NODE_MOVE_SCHEDULED_TO_PENDING_PERIOD: 1s + NODE_WALLET_MAX_TPS: 1000 + depends_on: + node-db: + condition: service_healthy + + issuer-oea: + image: ghcr.io/input-output-hk/prism-agent:${PRISM_AGENT_VERSION} + environment: + IRIS_HOST: iris + IRIS_PORT: 8081 + CASTOR_DB_HOST: issuer-db + CASTOR_DB_PORT: 5432 + CASTOR_DB_NAME: castor + CASTOR_DB_USER: postgres + CASTOR_DB_PASSWORD: postgres + POLLUX_DB_HOST: issuer-db + POLLUX_DB_PORT: 5432 + POLLUX_DB_NAME: pollux + POLLUX_DB_USER: postgres + POLLUX_DB_PASSWORD: postgres + CONNECT_DB_HOST: issuer-db + CONNECT_DB_PORT: 5432 + CONNECT_DB_NAME: connect + CONNECT_DB_USER: postgres + CONNECT_DB_PASSWORD: postgres + AGENT_DB_HOST: issuer-db + AGENT_DB_PORT: 5432 + AGENT_DB_NAME: agent + AGENT_DB_USER: postgres + AGENT_DB_PASSWORD: postgres + DIDCOMM_SERVICE_URL: http://host.docker.internal:${PORT}/issuer/didcomm + PRISM_NODE_HOST: prism-node + PRISM_NODE_PORT: 50053 + SECRET_STORAGE_BACKEND: postgres + DEV_MODE: true + DEFAULT_WALLET_ENABLED: + DEFAULT_WALLET_SEED: + # DEFAULT_WALLET_WEBHOOK_URL: + # DEFAULT_WALLET_WEBHOOK_API_KEY: + # DEFAULT_WALLET_AUTH_API_KEY: + # GLOBAL_WEBHOOK_URL: + # GLOBAL_WEBHOOK_API_KEY: + # WEBHOOK_PARALLELISM: + ADMIN_TOKEN: + API_KEY_SALT: + API_KEY_ENABLED: + API_KEY_AUTHENTICATE_AS_DEFAULT_USER: + API_KEY_AUTO_PROVISIONING: + ISSUE_BG_JOB_RECORDS_LIMIT: 25 + ISSUE_BG_JOB_RECURRENCE_DELAY: 2 seconds + ISSUE_BG_JOB_PROCESSING_PARALLELISM: 5 + PRESENTATION_BG_JOB_RECORDS_LIMIT: 25 + PRESENTATION_BG_JOB_RECURRENCE_DELAY: 2 seconds + PRESENTATION_BG_JOB_PROCESSING_PARALLELISM: 5 + CONNECT_BG_JOB_RECORDS_LIMIT: 25 + CONNECT_BG_JOB_RECURRENCE_DELAY: 2 seconds + CONNECT_BG_JOB_PROCESSING_PARALLELISM: 5 + depends_on: + issuer-db: + condition: service_healthy + prism-node: + condition: service_started + healthcheck: + test: ["CMD", "curl", "-f", "http://issuer-oea:8085/_system/health"] + interval: 30s + timeout: 10s + retries: 5 + extra_hosts: + - "host.docker.internal:host-gateway" + + verifier-oea: + image: ghcr.io/input-output-hk/prism-agent:${PRISM_AGENT_VERSION} + environment: + IRIS_HOST: iris + IRIS_PORT: 8081 + CASTOR_DB_HOST: verifier-db + CASTOR_DB_PORT: 5432 + CASTOR_DB_NAME: castor + CASTOR_DB_USER: postgres + CASTOR_DB_PASSWORD: postgres + POLLUX_DB_HOST: verifier-db + POLLUX_DB_PORT: 5432 + POLLUX_DB_NAME: pollux + POLLUX_DB_USER: postgres + POLLUX_DB_PASSWORD: postgres + CONNECT_DB_HOST: verifier-db + CONNECT_DB_PORT: 5432 + CONNECT_DB_NAME: connect + CONNECT_DB_USER: postgres + CONNECT_DB_PASSWORD: postgres + AGENT_DB_HOST: verifier-db + AGENT_DB_PORT: 5432 + AGENT_DB_NAME: agent + AGENT_DB_USER: postgres + AGENT_DB_PASSWORD: postgres + DIDCOMM_SERVICE_URL: http://host.docker.internal:${PORT}/verifier/didcomm + PRISM_NODE_HOST: prism-node + PRISM_NODE_PORT: 50053 + SECRET_STORAGE_BACKEND: postgres + DEV_MODE: true + DEFAULT_WALLET_ENABLED: + DEFAULT_WALLET_SEED: + # DEFAULT_WALLET_WEBHOOK_URL: + # DEFAULT_WALLET_WEBHOOK_API_KEY: + # DEFAULT_WALLET_AUTH_API_KEY: + # GLOBAL_WEBHOOK_URL: + # GLOBAL_WEBHOOK_API_KEY: + # WEBHOOK_PARALLELISM: + ADMIN_TOKEN: + API_KEY_SALT: + API_KEY_ENABLED: + API_KEY_AUTHENTICATE_AS_DEFAULT_USER: + API_KEY_AUTO_PROVISIONING: + ISSUE_BG_JOB_RECORDS_LIMIT: 25 + ISSUE_BG_JOB_RECURRENCE_DELAY: 2 seconds + ISSUE_BG_JOB_PROCESSING_PARALLELISM: 5 + PRESENTATION_BG_JOB_RECORDS_LIMIT: 25 + PRESENTATION_BG_JOB_RECURRENCE_DELAY: 2 seconds + PRESENTATION_BG_JOB_PROCESSING_PARALLELISM: 5 + CONNECT_BG_JOB_RECORDS_LIMIT: 25 + CONNECT_BG_JOB_RECURRENCE_DELAY: 2 seconds + CONNECT_BG_JOB_PROCESSING_PARALLELISM: 5 + depends_on: + verifier-db: + condition: service_healthy + prism-node: + condition: service_started + healthcheck: + test: ["CMD", "curl", "-f", "http://verifier-oea:8085/_system/health"] + interval: 30s + timeout: 10s + retries: 5 + extra_hosts: + - "host.docker.internal:host-gateway" + + holder-oea: + image: ghcr.io/input-output-hk/prism-agent:${PRISM_AGENT_VERSION} + environment: + IRIS_HOST: iris + IRIS_PORT: 8081 + CASTOR_DB_HOST: holder-db + CASTOR_DB_PORT: 5432 + CASTOR_DB_NAME: castor + CASTOR_DB_USER: postgres + CASTOR_DB_PASSWORD: postgres + POLLUX_DB_HOST: holder-db + POLLUX_DB_PORT: 5432 + POLLUX_DB_NAME: pollux + POLLUX_DB_USER: postgres + POLLUX_DB_PASSWORD: postgres + CONNECT_DB_HOST: holder-db + CONNECT_DB_PORT: 5432 + CONNECT_DB_NAME: connect + CONNECT_DB_USER: postgres + CONNECT_DB_PASSWORD: postgres + AGENT_DB_HOST: holder-db + AGENT_DB_PORT: 5432 + AGENT_DB_NAME: agent + AGENT_DB_USER: postgres + AGENT_DB_PASSWORD: postgres + DIDCOMM_SERVICE_URL: http://host.docker.internal:${PORT}/holder/didcomm + PRISM_NODE_HOST: prism-node + PRISM_NODE_PORT: 50053 + SECRET_STORAGE_BACKEND: postgres + DEV_MODE: true + DEFAULT_WALLET_ENABLED: + DEFAULT_WALLET_SEED: + # DEFAULT_WALLET_WEBHOOK_URL: + # DEFAULT_WALLET_WEBHOOK_API_KEY: + # DEFAULT_WALLET_AUTH_API_KEY: + # GLOBAL_WEBHOOK_URL: + # GLOBAL_WEBHOOK_API_KEY: + # WEBHOOK_PARALLELISM: + ISSUE_BG_JOB_RECORDS_LIMIT: 25 + ISSUE_BG_JOB_RECURRENCE_DELAY: 2 seconds + ISSUE_BG_JOB_PROCESSING_PARALLELISM: 5 + PRESENTATION_BG_JOB_RECORDS_LIMIT: 25 + PRESENTATION_BG_JOB_RECURRENCE_DELAY: 2 seconds + PRESENTATION_BG_JOB_PROCESSING_PARALLELISM: 5 + CONNECT_BG_JOB_RECORDS_LIMIT: 25 + CONNECT_BG_JOB_RECURRENCE_DELAY: 2 seconds + CONNECT_BG_JOB_PROCESSING_PARALLELISM: 5 + ADMIN_TOKEN: + API_KEY_SALT: + API_KEY_ENABLED: + API_KEY_AUTHENTICATE_AS_DEFAULT_USER: + API_KEY_AUTO_PROVISIONING: + depends_on: + holder-db: + condition: service_healthy + prism-node: + condition: service_started + healthcheck: + test: ["CMD", "curl", "-f", "http://holder-oea:8085/_system/health"] + interval: 30s + timeout: 10s + retries: 5 + extra_hosts: + - "host.docker.internal:host-gateway" + + apisix: + image: apache/apisix:2.15.0-alpine + volumes: + - ./apisix/conf/apisix.yaml:/usr/local/apisix/conf/apisix.yaml:ro + - ./apisix/conf/config.yaml:/usr/local/apisix/conf/config.yaml:ro + ports: + - "${PORT}:9080/tcp" + depends_on: + - issuer-oea + - verifier-oea + - holder-oea + +volumes: + issuer_pg_data_db: + verifier_pg_data_db: + holder_pg_data_db: + node_pg_data_db: + pgadmin: +# Temporary commit network setting due to e2e CI bug +# to be enabled later after debugging +#networks: +# default: +# name: ${NETWORK} diff --git a/infrastructure/single-tenant-testing-stack/postgres/init-script.sh b/infrastructure/single-tenant-testing-stack/postgres/init-script.sh new file mode 100755 index 0000000000..408264cf1e --- /dev/null +++ b/infrastructure/single-tenant-testing-stack/postgres/init-script.sh @@ -0,0 +1,24 @@ +#!/bin/bash + +set -e +set -u + +function create_user_and_database() { + local database=$1 + local app_user=${database}-application-user + echo " Creating user and database '$database'" + psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" <<-EOSQL + CREATE USER "$app_user" WITH PASSWORD 'password'; + CREATE DATABASE $database; + \c $database + ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO "$app_user"; + EOSQL +} + +if [ -n "$POSTGRES_MULTIPLE_DATABASES" ]; then + echo "Multiple database creation requested: $POSTGRES_MULTIPLE_DATABASES" + for db in $(echo $POSTGRES_MULTIPLE_DATABASES | tr ',' ' '); do + create_user_and_database $db + done + echo "Multiple databases created" +fi diff --git a/infrastructure/single-tenant-testing-stack/postgres/max_conns.sql b/infrastructure/single-tenant-testing-stack/postgres/max_conns.sql new file mode 100644 index 0000000000..f2a343e505 --- /dev/null +++ b/infrastructure/single-tenant-testing-stack/postgres/max_conns.sql @@ -0,0 +1 @@ +ALTER SYSTEM SET max_connections = 500; diff --git a/infrastructure/single-tenant-testing-stack/run-e2e-tests-local.sh b/infrastructure/single-tenant-testing-stack/run-e2e-tests-local.sh new file mode 100755 index 0000000000..98dcd604ee --- /dev/null +++ b/infrastructure/single-tenant-testing-stack/run-e2e-tests-local.sh @@ -0,0 +1,32 @@ +#!/usr/bin/env bash + +set -e + +SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd) + +ENV_FILE="${ENV_FILE:=${SCRIPT_DIR}/.env}" +PORT="${PORT:=9500}" + +echo "ENV_FILE = ${ENV_FILE}" + +echo "--------------------------------------" +echo "Starting stack using docker compose" +echo "--------------------------------------" + +PORT=${PORT} docker compose -f ${SCRIPT_DIR}/docker-compose.yml \ + --env-file ${ENV_FILE} up -d --wait + +export AGENT_AUTH_REQUIRED=true +export ACME_AGENT_URL=http://localhost:${PORT}/issuer/prism-agent +export ACME_AUTH_KEY=default +export BOB_AGENT_URL=http://localhost:${PORT}/verifier/prism-agent +export BOB_AUTH_KEY=default +export MALLORY_AGENT_URL=http://localhost:${PORT}/holder/prism-agent +export MALLORY_AUTH_KEY=default +export FABER_AGENT_URL=http://localhost:${PORT}/holder/prism-agent +export FABER_AUTH_KEY=default + +( + cd ${SCRIPT_DIR}/../../tests/e2e-tests/ + ./gradlew test reports +) diff --git a/infrastructure/single-tenant-testing-stack/run-performance-tests-local.sh b/infrastructure/single-tenant-testing-stack/run-performance-tests-local.sh new file mode 100755 index 0000000000..57166c29e9 --- /dev/null +++ b/infrastructure/single-tenant-testing-stack/run-performance-tests-local.sh @@ -0,0 +1,43 @@ +#!/usr/bin/env bash + +set -e + +SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd) + +ENV_FILE="${ENV_FILE:=${SCRIPT_DIR}/.env}" +PORT="${PORT:=9500}" + +echo "ENV_FILE = ${ENV_FILE}" + +echo "--------------------------------------" +echo "Starting stack using docker compose" +echo "--------------------------------------" + +PORT=${PORT} docker compose -f ${SCRIPT_DIR}/docker-compose.yml \ + --env-file ${ENV_FILE} up -d --wait + +export ISSUER_AGENT_URL=http://localhost:${PORT}/issuer/prism-agent +export ISSUER_AGENT_API_KEY=default +export HOLDER_AGENT_URL=http://localhost:${PORT}/holder/prism-agent +export HOLDER_AGENT_API_KEY=default +export VERIFIER_AGENT_URL=http://localhost:${PORT}/verifier/prism-agent +export VERIFIER_AGENT_API_KEY=default + +echo "--------------------------------------" +echo "Run perf tests" +echo "--------------------------------------" + +( + export K6_PROMETHEUS_RW_SERVER_URL=http://localhost:9090/api/v1/write + export K6_PROMETHEUS_RW_TREND_AS_NATIVE_HISTOGRAM=true + cd ${SCRIPT_DIR}/../../tests/performance-tests/atala-performance-tests-k6 + yarn install + yarn webpack + k6 run -e SCENARIO_LABEL=create-prism-did-smoke dist/create-prism-did-test.js -o experimental-prometheus-rw + k6 run -e SCENARIO_LABEL=credential-offer-smoke dist/credential-offer-test.js -o experimental-prometheus-rw + k6 run -e SCENARIO_LABEL=credential-schema-smoke dist/credential-schema-test.js -o experimental-prometheus-rw + k6 run -e SCENARIO_LABEL=did-publishing-smoke dist/did-publishing-test.js -o experimental-prometheus-rw + k6 run -e SCENARIO_LABEL=connection-flow-smoke dist/connection-flow-test.js -o experimental-prometheus-rw + k6 run -e SCENARIO_LABEL=issuance-flow-smoke dist/issuance-flow-test.js -o experimental-prometheus-rw + k6 run -e SCENARIO_LABEL=present-proof-flow-smoke dist/present-proof-flow-test.js -o experimental-prometheus-rw +) diff --git a/tests/performance-tests/atala-performance-tests-k6/package.json b/tests/performance-tests/atala-performance-tests-k6/package.json index 8b8e498d7e..c7d543ed60 100644 --- a/tests/performance-tests/atala-performance-tests-k6/package.json +++ b/tests/performance-tests/atala-performance-tests-k6/package.json @@ -19,7 +19,8 @@ "typescript": "5.2.2", "webpack": "5.88.2", "webpack-cli": "5.1.4", - "webpack-glob-entries": "^1.0.1" + "webpack-glob-entries": "^1.0.1", + "ts-deepmerge": "6.2.0" }, "scripts": { "start": "webpack" diff --git a/tests/performance-tests/atala-performance-tests-k6/src/common/ConnectionService.ts b/tests/performance-tests/atala-performance-tests-k6/src/common/ConnectionService.ts index 59e1cea9a6..d43acd6678 100644 --- a/tests/performance-tests/atala-performance-tests-k6/src/common/ConnectionService.ts +++ b/tests/performance-tests/atala-performance-tests-k6/src/common/ConnectionService.ts @@ -1,3 +1,5 @@ +/*global __ENV*/ + import { Connection, ConnectionInvitation, ConnectionStateEnum } from "@input-output-hk/prism-typescript-client"; import { WAITING_LOOP_MAX_ITERATIONS, WAITING_LOOP_PAUSE_INTERVAL } from "./Config"; import { HttpService } from "./HttpService"; @@ -26,7 +28,7 @@ export class ConnectionService extends HttpService { */ getConnection(connectionId: string): Connection { const res = this.get(`connections/${connectionId}`); - const connection = res.json() as unknown as Connection; + const connection = this.toJson(res) as unknown as Connection; return connection; } @@ -36,7 +38,7 @@ export class ConnectionService extends HttpService { */ createConnection(): Connection { const payload = { label: "test" }; - const connection = this.post("connections", payload).json() as unknown as Connection; + const connection = this.toJson(this.post("connections", payload)) as unknown as Connection; return connection; } @@ -48,7 +50,7 @@ export class ConnectionService extends HttpService { acceptConnectionInvitation(invitation: ConnectionInvitation): Connection { const payload = { invitation: this.invitationFromUrl(invitation.invitationUrl) }; const res = this.post("connection-invitations", payload, 200); - return res.json() as unknown as Connection; + return this.toJson(res) as unknown as Connection; } /** @@ -66,7 +68,7 @@ export class ConnectionService extends HttpService { sleep(WAITING_LOOP_PAUSE_INTERVAL); iterations++; } while (state !== requiredState && iterations < WAITING_LOOP_MAX_ITERATIONS); - if (state != requiredState) { + if (state !== requiredState) { throw new Error(`Connection state is ${state}, required ${requiredState}`); } } diff --git a/tests/performance-tests/atala-performance-tests-k6/src/common/CredentialsService.ts b/tests/performance-tests/atala-performance-tests-k6/src/common/CredentialsService.ts index 8961b64b9a..c55a37ca1d 100644 --- a/tests/performance-tests/atala-performance-tests-k6/src/common/CredentialsService.ts +++ b/tests/performance-tests/atala-performance-tests-k6/src/common/CredentialsService.ts @@ -2,7 +2,8 @@ import { sleep } from "k6"; import { HttpService } from "./HttpService"; import { ISSUER_AGENT_URL, WAITING_LOOP_MAX_ITERATIONS, WAITING_LOOP_PAUSE_INTERVAL } from "./Config"; import { IssueCredentialRecord, Connection, CredentialSchemaResponse } from "@input-output-hk/prism-typescript-client"; -import {v4 as uuidv4} from 'uuid'; +import { crypto } from "k6/experimental/webcrypto"; + /** * A service class for managing credentials in the application. @@ -18,8 +19,8 @@ export class CredentialsService extends HttpService { */ createCredentialOffer(issuingDid: string, connection: Connection, schema: CredentialSchemaResponse): IssueCredentialRecord { const payload = `{ - "claims": { - "emailAddress": "${uuidv4()}-@atala.io", + "claims": { + "emailAddress": "${crypto.randomUUID()}-@atala.io", "familyName": "Test", "dateOfIssuance": "${new Date()}", "drivingLicenseID": "Test", @@ -31,13 +32,13 @@ export class CredentialsService extends HttpService { "automaticIssuance": false }`; const res = this.post("issue-credentials/credential-offers", payload); - return res.json() as unknown as IssueCredentialRecord; + return this.toJson(res) as unknown as IssueCredentialRecord; } createCredentialSchema(issuingDid: string): CredentialSchemaResponse { const payload = ` { - "name": "${uuidv4()}}", + "name": "${crypto.randomUUID()}}", "version": "1.0.0", "description": "Simple credential schema for the driving licence verifiable credential.", "type": "https://w3c-ccg.github.io/vc-json-schemas/schema/2.0/schema.json", @@ -85,7 +86,7 @@ export class CredentialsService extends HttpService { } ` const res = this.post("schema-registry/schemas", payload); - return res.json() as unknown as CredentialSchemaResponse; + return this.toJson(res) as unknown as CredentialSchemaResponse; } /** @@ -95,7 +96,7 @@ export class CredentialsService extends HttpService { */ getCredentialRecord(record: IssueCredentialRecord): IssueCredentialRecord { const res = this.get(`issue-credentials/records/${record.recordId}`); - return res.json() as unknown as IssueCredentialRecord; + return this.toJson(res) as unknown as IssueCredentialRecord; } /** @@ -104,7 +105,7 @@ export class CredentialsService extends HttpService { */ getCredentialRecords(thid: string): IssueCredentialRecord[] { const res = this.get(`issue-credentials/records?thid=${thid}`); - return res.json("contents") as unknown as IssueCredentialRecord[]; + return this.toJson(res).contents as unknown as IssueCredentialRecord[]; } /** @@ -116,7 +117,7 @@ export class CredentialsService extends HttpService { acceptCredentialOffer(record: IssueCredentialRecord, subjectDid: string): IssueCredentialRecord { const payload = { subjectId: subjectDid }; const res = this.post(`issue-credentials/records/${record.recordId}/accept-offer`, payload, 200); - return res.json() as unknown as IssueCredentialRecord; + return this.toJson(res) as unknown as IssueCredentialRecord; } /** @@ -126,7 +127,7 @@ export class CredentialsService extends HttpService { */ issueCredential(record: IssueCredentialRecord): IssueCredentialRecord { const res = this.post(`issue-credentials/records/${record.recordId}/issue-credential`, null, 200); - return res.json() as unknown as IssueCredentialRecord; + return this.toJson(res) as unknown as IssueCredentialRecord; } /** diff --git a/tests/performance-tests/atala-performance-tests-k6/src/common/DidService.ts b/tests/performance-tests/atala-performance-tests-k6/src/common/DidService.ts index c203bf0af6..95f1cc60c8 100644 --- a/tests/performance-tests/atala-performance-tests-k6/src/common/DidService.ts +++ b/tests/performance-tests/atala-performance-tests-k6/src/common/DidService.ts @@ -1,7 +1,10 @@ +/*global __ENV*/ + import { HttpService } from "./HttpService"; import { WAITING_LOOP_MAX_ITERATIONS, WAITING_LOOP_PAUSE_INTERVAL } from "./Config"; import { CreateManagedDIDResponse, DIDDocument, DidOperationSubmission, ManagedDID } from "@input-output-hk/prism-typescript-client"; -import { sleep } from "k6"; +import {sleep} from "k6"; + /** * A service class for managing decentralized identifiers (DIDs) in the application. @@ -16,7 +19,7 @@ export class DidService extends HttpService { */ getDid(did: string): ManagedDID { const res = this.get(`did-registrar/dids/${did}`); - return res.json() as unknown as ManagedDID; + return this.toJson(res) as unknown as ManagedDID; } /** @@ -26,7 +29,7 @@ export class DidService extends HttpService { */ resolveDid(did: string): DIDDocument { const res = this.get(`dids/${did}`); - return res.json() as unknown as DIDDocument; + return this.toJson(res) as unknown as DIDDocument; } /** @@ -36,7 +39,7 @@ export class DidService extends HttpService { */ publishDid(did: string): DidOperationSubmission { const res = this.post(`did-registrar/dids/${did}/publications`, null, 202); - return res.json("scheduledOperation") as unknown as DidOperationSubmission; + return this.toJson(res).scheduledOperation as unknown as DidOperationSubmission; } /** @@ -46,7 +49,7 @@ export class DidService extends HttpService { */ createUnpublishedDid(documentTemplate: string): CreateManagedDIDResponse { const res = this.post("did-registrar/dids", documentTemplate); - return res.json() as unknown as CreateManagedDIDResponse; + return this.toJson(res) as unknown as CreateManagedDIDResponse; } /** diff --git a/tests/performance-tests/atala-performance-tests-k6/src/common/HttpService.ts b/tests/performance-tests/atala-performance-tests-k6/src/common/HttpService.ts index 422652c11a..4b98159cc1 100644 --- a/tests/performance-tests/atala-performance-tests-k6/src/common/HttpService.ts +++ b/tests/performance-tests/atala-performance-tests-k6/src/common/HttpService.ts @@ -34,6 +34,10 @@ export class HttpService { }; } + public toJson(response: RefinedResponse): any { + return JSON.parse(response.body as string) + } + /** * Performs an HTTP POST request to the specified endpoint with the provided payload. * @param endpoint The API endpoint to post to. diff --git a/tests/performance-tests/atala-performance-tests-k6/src/common/ProofsService.ts b/tests/performance-tests/atala-performance-tests-k6/src/common/ProofsService.ts index b52c723a25..f274b8f0de 100644 --- a/tests/performance-tests/atala-performance-tests-k6/src/common/ProofsService.ts +++ b/tests/performance-tests/atala-performance-tests-k6/src/common/ProofsService.ts @@ -34,7 +34,7 @@ export class ProofsService extends HttpService { ] }` const res = this.post("present-proof/presentations", payload); - return res.json("presentationId") as string; + return this.toJson(res).presentationId as string; } /** @@ -51,7 +51,7 @@ export class ProofsService extends HttpService { ] }` const res = this.patch(`present-proof/presentations/${presentation.presentationId}`, payload); - return res.json("presentationId") as string; + return this.toJson(res).presentationId as string; } /** @@ -61,7 +61,7 @@ export class ProofsService extends HttpService { */ getPresentation(presentationId: string): PresentationStatus { const res = this.get(`present-proof/presentations/${presentationId}`); - return res.json() as unknown as PresentationStatus; + return this.toJson(res) as unknown as PresentationStatus; } /** @@ -70,7 +70,7 @@ export class ProofsService extends HttpService { */ getPresentations(thid: string): PresentationStatus[] { const res = this.get(`present-proof/presentations?thid=${thid}`); - return res.json("contents") as unknown as PresentationStatus[]; + return this.toJson(res).contents as unknown as PresentationStatus[]; } /** diff --git a/tests/performance-tests/atala-performance-tests-k6/src/scenarios/default.ts b/tests/performance-tests/atala-performance-tests-k6/src/scenarios/default.ts new file mode 100644 index 0000000000..81c1973063 --- /dev/null +++ b/tests/performance-tests/atala-performance-tests-k6/src/scenarios/default.ts @@ -0,0 +1,31 @@ +/*global __ENV*/ + +import { Options } from "k6/options"; + +export const defaultOptions: Options = { + setupTimeout: '120s', + scenarios: { + smoke: { + // a simple test to ensure performance tests work and requests don't fail + executor: "shared-iterations", + vus: 1, + iterations: 1, + tags: { scenario_label: __ENV.SCENARIO_LABEL || "defaultScenarioLabel" }, // add label for filtering in observability platform + }, + }, + thresholds: { + http_req_failed: [ + // fail if any requests fail during smoke test + { + threshold: "rate==0", + abortOnFail: true, + }, + ], + http_req_duration: [ + { threshold: "p(95)<2000", abortOnFail: false }, // 95% of requests should complete within 2 seconds, but don't fail tests + { threshold: "p(99)<5000", abortOnFail: false }, // 99% of requests should complete within 5 seconds, but don't fail tests + ], + checks: [{ threshold: "rate==1", abortOnFail: true }], // fail if any checks fail (the checks are defined in test code which is executed) + + } +} diff --git a/tests/performance-tests/atala-performance-tests-k6/src/tests/credentials/credential-offer-test.ts b/tests/performance-tests/atala-performance-tests-k6/src/tests/credentials/credential-offer-test.ts index 27b86d07ff..ac6ae5727d 100644 --- a/tests/performance-tests/atala-performance-tests-k6/src/tests/credentials/credential-offer-test.ts +++ b/tests/performance-tests/atala-performance-tests-k6/src/tests/credentials/credential-offer-test.ts @@ -1,37 +1,16 @@ import { group } from 'k6'; import { Options } from 'k6/options'; import { Issuer, Holder } from '../../actors'; -import { Connection } from '@input-output-hk/prism-typescript-client'; +import { Connection, CredentialSchemaResponse } from '@input-output-hk/prism-typescript-client'; +import { defaultOptions } from "../../scenarios/default"; +import merge from "ts-deepmerge"; -// export let options: Options = { -// stages: [ -// { duration: '1m', target: 5 }, -// ], -// thresholds: { -// http_req_failed: [{ -// threshold: 'rate<=0.05', -// abortOnFail: true, -// }], -// http_req_duration: ['p(95)<=100'], -// checks: ['rate>=0.99'], -// }, -// }; - -export let options: Options = { - scenarios: { - smoke: { - executor: 'constant-vus', - vus: 3, - duration: "1s", - }, - }, +export const localOptions: Options = { thresholds: { - 'http_req_duration{group:::Issuer creates credential offer}': ['max >= 0'], - 'http_reqs{group:::Issuer creates credential offer}': ['count >= 0'], - 'group_duration{group:::Issuer creates credential offer}': ['max >= 0'], - }, -}; - + 'group_duration{group:::Issuer creates credential offer}': ['avg < 30000'] + } +} +export let options: Options = merge(localOptions, defaultOptions) export const issuer = new Issuer(); export const holder = new Holder(); @@ -52,22 +31,29 @@ export function setup() { holder.finalizeConnectionWithIssuer(); }); - return { + group("Issuer creates credential schema", function () { + issuer.createCredentialSchema(); + }); + + return { issuerDid: issuer.did, holderDid: holder.did, + issuerSchema: issuer.schema, connectionWithHolder: issuer.connectionWithHolder!, connectionWithIssuer: holder.connectionWithIssuer! }; } -export default (data: { issuerDid: string; holderDid: string; connectionWithHolder: Connection, connectionWithIssuer: Connection }) => { +export default (data: { issuerDid: string; holderDid: string; issuerSchema: CredentialSchemaResponse, connectionWithHolder: Connection, connectionWithIssuer: Connection }) => { // This is the only way to pass data from setup to default issuer.did = data.issuerDid; + issuer.schema = data.issuerSchema holder.did = data.holderDid; issuer.connectionWithHolder = data.connectionWithHolder; holder.connectionWithIssuer = data.connectionWithIssuer; + group('Issuer creates credential offer', function () { issuer.createCredentialOffer(); }); diff --git a/tests/performance-tests/atala-performance-tests-k6/src/tests/credentials/credential-schema-test.ts b/tests/performance-tests/atala-performance-tests-k6/src/tests/credentials/credential-schema-test.ts index 095eee806f..1b4b2cda16 100644 --- a/tests/performance-tests/atala-performance-tests-k6/src/tests/credentials/credential-schema-test.ts +++ b/tests/performance-tests/atala-performance-tests-k6/src/tests/credentials/credential-schema-test.ts @@ -1,43 +1,33 @@ -import { group } from 'k6'; -import { Options } from 'k6/options'; -import { Issuer } from '../../actors'; +import { group } from "k6"; +import { Options } from "k6/options"; +import { Issuer } from "../../actors"; +import { defaultOptions } from "../../scenarios/default"; +import merge from "ts-deepmerge"; -export let options: Options = { - scenarios: { - smoke: { - executor: 'constant-vus', - vus: 3, - duration: "1s", - }, - }, +export const localOptions: Options = { thresholds: { - 'http_req_duration{group:::Issuer creates credential schema}': ['max >= 0'], - 'http_reqs{group:::Issuer creates credential schema}': ['count >= 0'], - 'group_duration{group:::Issuer creates credential schema}': ['max >= 0'], - checks: ['rate==1'], - http_req_duration: ['p(95)<=100'], - }, -}; - + 'group_duration{group:::Issuer creates credential schema}': ['avg < 30000'] + } +} +export let options: Options = merge(localOptions, defaultOptions) export const issuer = new Issuer(); export function setup() { - group('Issuer publishes DID', function () { + group("Issuer publishes DID", function () { issuer.createUnpublishedDid(); issuer.publishDid(); }); - return { + return { issuerDid: issuer.did, }; } -export default (data: { issuerDid: string; }) => { - +export default (data: { issuerDid: string }) => { // This is the only way to pass data from setup to default issuer.did = data.issuerDid; - group('Issuer creates credential schema', function () { + group("Issuer creates credential schema", function () { issuer.createCredentialSchema(); }); }; diff --git a/tests/performance-tests/atala-performance-tests-k6/src/tests/dids/create-prism-did-test.ts b/tests/performance-tests/atala-performance-tests-k6/src/tests/dids/create-prism-did-test.ts index dfd9350ca4..00f9c7d9ba 100644 --- a/tests/performance-tests/atala-performance-tests-k6/src/tests/dids/create-prism-did-test.ts +++ b/tests/performance-tests/atala-performance-tests-k6/src/tests/dids/create-prism-did-test.ts @@ -1,26 +1,20 @@ import { Options } from 'k6/options'; import { Issuer } from '../../actors'; +import { defaultOptions } from "../../scenarios/default"; +import { group } from "k6"; +import merge from "ts-deepmerge"; -export let options: Options = { - scenarios: { - smoke: { - executor: 'constant-vus', - vus: 3, - duration: "1m", - }, - }, +export const localOptions: Options = { thresholds: { - http_req_failed: [{ - threshold: 'rate==0', - abortOnFail: true, - }], - http_req_duration: ['p(95)<=500'], - checks: ['rate==1'], - }, -}; + 'group_duration{group:::Issuer create unpublished DID}': ['avg < 30000'] + } +} +export let options: Options = merge(localOptions, defaultOptions) const issuer = new Issuer(); export default () => { + group("Issuer create unpublished DID", function () { issuer.createUnpublishedDid(); + }); }; diff --git a/tests/performance-tests/atala-performance-tests-k6/src/tests/dids/did-publishing-test.ts b/tests/performance-tests/atala-performance-tests-k6/src/tests/dids/did-publishing-test.ts index 0fd5eb4644..73ce438618 100644 --- a/tests/performance-tests/atala-performance-tests-k6/src/tests/dids/did-publishing-test.ts +++ b/tests/performance-tests/atala-performance-tests-k6/src/tests/dids/did-publishing-test.ts @@ -1,23 +1,21 @@ -import { Options } from 'k6/options'; -import { Issuer } from '../../actors'; +import { Options } from "k6/options"; +import { Issuer } from "../../actors"; +import merge from "ts-deepmerge"; +import { group } from "k6"; +import { defaultOptions } from "../../scenarios/default"; -export let options: Options = { - stages: [ - { duration: '1m', target: 5 }, - ], - thresholds: { - http_req_failed: [{ - threshold: 'rate<=0.05', - abortOnFail: true, - }], - http_req_duration: ['p(95)<=100'], - checks: ['rate>=0.99'], - }, - }; +export const localOptions: Options = { + thresholds: { + "group_duration{group:::Issuer create published DID}": ["avg < 30000"], + }, +}; +export let options: Options = merge(localOptions, defaultOptions); const issuer = new Issuer(); export default () => { + group("Issuer create published DID", function () { issuer.createUnpublishedDid(); issuer.publishDid(); + }); }; diff --git a/tests/performance-tests/atala-performance-tests-k6/src/tests/flows/connection-flow-test.ts b/tests/performance-tests/atala-performance-tests-k6/src/tests/flows/connection-flow-test.ts index 563a36bf11..2102eff94c 100644 --- a/tests/performance-tests/atala-performance-tests-k6/src/tests/flows/connection-flow-test.ts +++ b/tests/performance-tests/atala-performance-tests-k6/src/tests/flows/connection-flow-test.ts @@ -1,24 +1,25 @@ -import { Options } from 'k6/options'; -import { connectionFlow } from '../common'; - -export let options: Options = { - scenarios: { - smoke: { - executor: 'constant-vus', - vus: 3, - duration: "1m", - }, - }, +import { Options } from "k6/options"; +import { connectionFlow } from "../common"; +import { defaultOptions } from "../../scenarios/default"; +import merge from "ts-deepmerge"; +export const localOptions: Options = { thresholds: { - http_req_failed: [{ - threshold: 'rate==0', - abortOnFail: true, - }], - http_req_duration: ['p(95)<=500'], - checks: ['rate==1'], + "group_duration{group:::Issuer initiates connection with Holder}": [ + "avg < 15000", + ], + "group_duration{group:::Holder accepts connection with Issuer}": [ + "avg < 15000", + ], + "group_duration{group:::Issuer finalizes connection with Holder}": [ + "avg < 30000", + ], + "group_duration{group:::Holder finalizes connection with Issuer}": [ + "avg < 15000", + ], }, }; +export let options: Options = merge(localOptions, defaultOptions); -export default() => { +export default () => { connectionFlow(); -} +}; diff --git a/tests/performance-tests/atala-performance-tests-k6/src/tests/flows/full-flow-test.ts b/tests/performance-tests/atala-performance-tests-k6/src/tests/flows/full-flow-test.ts deleted file mode 100644 index fe17b7148e..0000000000 --- a/tests/performance-tests/atala-performance-tests-k6/src/tests/flows/full-flow-test.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { group } from 'k6'; -import { Options } from 'k6/options'; -import { Issuer, Holder, Verifier } from '../../actors'; - -export let options: Options = { - vus: 1, - iterations: 1 -}; - -const issuer = new Issuer(); -const holder = new Holder(); -const verifier = new Verifier(); - -export function setup() { - group('Issuer publishes DID', function () { - issuer.createUnpublishedDid(); - issuer.publishDid(); - }); - - group('Holder creates unpublished DID', function () { - holder.createUnpublishedDid(); - }); - - return { issuerDid: issuer.did, holderDid: holder.did }; -} - -export default (data: { issuerDid: string; holderDid: string; }) => { - - issuer.did = data.issuerDid; - holder.did = data.holderDid; - - group('Holder connects with Issuer', function () { - issuer.createHolderConnection(); - holder.acceptIssuerConnection(issuer.connectionWithHolder!.invitation); - issuer.finalizeConnectionWithHolder(); - holder.finalizeConnectionWithIssuer(); - }); - - group('Issuer creates credential offer for Holder', function () { - issuer.createCredentialOffer(); - issuer.waitForCredentialOfferToBeSent(); - holder.waitAndAcceptCredentialOffer(); - issuer.receiveCredentialRequest(); - issuer.issueCredential(); - issuer.waitForCredentialToBeSent(); - holder.receiveCredential(); - }); - - group('Holder connects with Verifier', function () { - verifier.createHolderConnection(); - holder.acceptVerifierConnection(verifier.connectionWithHolder!.invitation); - verifier.finalizeConnectionWithHolder(); - holder.finalizeConnectionWithVerifier(); - }); - - group('Verifier requests proof from Holder', function () { - verifier.requestProof(); - holder.waitAndAcceptProofRequest(); - verifier.acknowledgeProof(); - }); - -}; diff --git a/tests/performance-tests/atala-performance-tests-k6/src/tests/flows/issuance-flow-test.ts b/tests/performance-tests/atala-performance-tests-k6/src/tests/flows/issuance-flow-test.ts index c4f52a0094..89e144524c 100644 --- a/tests/performance-tests/atala-performance-tests-k6/src/tests/flows/issuance-flow-test.ts +++ b/tests/performance-tests/atala-performance-tests-k6/src/tests/flows/issuance-flow-test.ts @@ -1,76 +1,84 @@ -import { group } from 'k6'; -import { Options } from 'k6/options'; -import {issuer, holder} from '../common'; -import { CredentialSchemaResponse } from '@input-output-hk/prism-typescript-client'; +import { group } from "k6"; +import { Options } from "k6/options"; +import { issuer, holder } from "../common"; +import { CredentialSchemaResponse } from "@input-output-hk/prism-typescript-client"; +import { defaultOptions } from "../../scenarios/default"; +import merge from "ts-deepmerge"; -export let options: Options = { - scenarios: { - smoke: { - executor: 'constant-vus', - vus: 5, - duration: "3m", - }, - }, +export const localOptions: Options = { thresholds: { - http_req_failed: [{ - threshold: 'rate==0', - abortOnFail: true, - }], - http_req_duration: ['p(95)<=500'], - checks: ['rate==1'], + "group_duration{group:::Issuer connects with Holder}": ["avg < 30000"], + "group_duration{group:::Issuer creates credential offer for Holder}": [ + "avg < 60000", + ], + "group_duration{group:::Issuer issues credential to Holder}": [ + "avg < 60000", + ], + "group_duration{group:::Holder receives credential from Issuer}": [ + "avg < 30000", + ], }, }; +export let options: Options = merge(localOptions, defaultOptions); // This is setup code. It runs once at the beginning of the test, regardless of the number of VUs. export function setup() { - - group('Issuer publishes DID', function () { + group("Issuer publishes DID", function () { issuer.createUnpublishedDid(); issuer.publishDid(); }); - group('Issuer creates credential schema', function () { + group("Issuer creates credential schema", function () { issuer.createCredentialSchema(); }); - group('Holder creates unpublished DID', function () { + group("Holder creates unpublished DID", function () { holder.createUnpublishedDid(); }); - return { issuerDid: issuer.did, holderDid: holder.did, issuerSchema: issuer.schema }; + return { + issuerDid: issuer.did, + holderDid: holder.did, + issuerSchema: issuer.schema, + }; } -export default (data: { issuerDid: string; holderDid: string; issuerSchema: CredentialSchemaResponse}) => { - +export default (data: { + issuerDid: string; + holderDid: string; + issuerSchema: CredentialSchemaResponse; +}) => { // This is the only way to pass data from setup to default issuer.did = data.issuerDid; issuer.schema = data.issuerSchema; holder.did = data.holderDid; - group('Issuer connects with Holder', function () { + group("Issuer connects with Holder", function () { issuer.createHolderConnection(); holder.acceptIssuerConnection(issuer.connectionWithHolder!.invitation); issuer.finalizeConnectionWithHolder(); holder.finalizeConnectionWithIssuer(); }); - group('Issuer creates credential offer for Holder', function () { + group("Issuer creates credential offer for Holder", function () { issuer.createCredentialOffer(); issuer.waitForCredentialOfferToBeSent(); }); - - group('Holder achieves and accepts credential offer from Issuer', function () { - holder.waitAndAcceptCredentialOffer(issuer.credential!.thid); - }); - group('Issuer issues credential to Holder', function () { + group( + "Holder achieves and accepts credential offer from Issuer", + function () { + holder.waitAndAcceptCredentialOffer(issuer.credential!.thid); + } + ); + + group("Issuer issues credential to Holder", function () { issuer.receiveCredentialRequest(); issuer.issueCredential(); issuer.waitForCredentialToBeSent(); }); - group('Holder receives credential from Issuer', function () { + group("Holder receives credential from Issuer", function () { holder.receiveCredential(); }); - }; diff --git a/tests/performance-tests/atala-performance-tests-k6/src/tests/flows/present-proof-flow-test.ts b/tests/performance-tests/atala-performance-tests-k6/src/tests/flows/present-proof-flow-test.ts index 667c0d4c90..83b5a8860a 100644 --- a/tests/performance-tests/atala-performance-tests-k6/src/tests/flows/present-proof-flow-test.ts +++ b/tests/performance-tests/atala-performance-tests-k6/src/tests/flows/present-proof-flow-test.ts @@ -1,61 +1,66 @@ -import { group } from 'k6'; -import { Options } from 'k6/options'; -import { Issuer, Holder, Verifier } from '../../actors'; -import { CredentialSchemaResponse } from '@input-output-hk/prism-typescript-client'; +import { group } from "k6"; +import { Options } from "k6/options"; +import { Issuer, Holder, Verifier } from "../../actors"; +import { CredentialSchemaResponse } from "@input-output-hk/prism-typescript-client"; +import { defaultOptions } from "../../scenarios/default"; +import merge from "ts-deepmerge"; -export let options: Options = { - scenarios: { - smoke: { - executor: 'constant-vus', - vus: 5, - duration: "3m", - }, - }, +export const localOptions: Options = { thresholds: { - http_req_failed: [{ - threshold: 'rate==0', - abortOnFail: true, - }], - http_req_duration: ['p(95)<=500'], - checks: ['rate==1'], + "group_duration{group:::Holder connects with Issuer}": ["avg < 30000"], + "group_duration{group:::Issuer creates credential offer for Holder}": [ + "avg < 120000", + ], + "group_duration{group:::Holder connects with Verifier}": ["avg < 30000"], + "group_duration{group:::Verifier requests proof from Holder}": [ + "avg < 30000", + ], }, }; +export let options: Options = merge(localOptions, defaultOptions); const issuer = new Issuer(); const holder = new Holder(); const verifier = new Verifier(); export function setup() { - group('Issuer publishes DID', function () { + group("Issuer publishes DID", function () { issuer.createUnpublishedDid(); issuer.publishDid(); }); - group('Issuer creates credential schema', function () { + group("Issuer creates credential schema", function () { issuer.createCredentialSchema(); }); - group('Holder creates unpublished DID', function () { + group("Holder creates unpublished DID", function () { holder.createUnpublishedDid(); }); - return { issuerDid: issuer.did, holderDid: holder.did, issuerSchema: issuer.schema }; + return { + issuerDid: issuer.did, + holderDid: holder.did, + issuerSchema: issuer.schema, + }; } -export default (data: { issuerDid: string; holderDid: string; issuerSchema: CredentialSchemaResponse; }) => { - +export default (data: { + issuerDid: string; + holderDid: string; + issuerSchema: CredentialSchemaResponse; +}) => { issuer.did = data.issuerDid; issuer.schema = data.issuerSchema; holder.did = data.holderDid; - group('Holder connects with Issuer', function () { + group("Holder connects with Issuer", function () { issuer.createHolderConnection(); holder.acceptIssuerConnection(issuer.connectionWithHolder!.invitation); issuer.finalizeConnectionWithHolder(); holder.finalizeConnectionWithIssuer(); }); - group('Issuer creates credential offer for Holder', function () { + group("Issuer creates credential offer for Holder", function () { issuer.createCredentialOffer(); issuer.waitForCredentialOfferToBeSent(); holder.waitAndAcceptCredentialOffer(issuer.credential!.thid); @@ -65,17 +70,16 @@ export default (data: { issuerDid: string; holderDid: string; issuerSchema: Cred holder.receiveCredential(); }); - group('Holder connects with Verifier', function () { + group("Holder connects with Verifier", function () { verifier.createHolderConnection(); holder.acceptVerifierConnection(verifier.connectionWithHolder!.invitation); verifier.finalizeConnectionWithHolder(); holder.finalizeConnectionWithVerifier(); }); - group('Verifier requests proof from Holder', function () { + group("Verifier requests proof from Holder", function () { verifier.requestProof(); holder.waitAndAcceptProofRequest(verifier.presentation!.thid); verifier.acknowledgeProof(); }); - }; diff --git a/tests/performance-tests/atala-performance-tests-k6/yarn.lock b/tests/performance-tests/atala-performance-tests-k6/yarn.lock index 81ef0af7bf..2568095e62 100644 --- a/tests/performance-tests/atala-performance-tests-k6/yarn.lock +++ b/tests/performance-tests/atala-performance-tests-k6/yarn.lock @@ -2388,6 +2388,11 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" +ts-deepmerge@6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/ts-deepmerge/-/ts-deepmerge-6.2.0.tgz#77554381a4884d66cab799470bc2620a1c9d84e8" + integrity sha512-2qxI/FZVDPbzh63GwWIZYE7daWKtwXZYuyc8YNq0iTmMUwn4mL0jRLsp6hfFlgbdRSR4x2ppe+E86FnvEpN7Nw== + typescript@5.2.2: version "5.2.2" resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.2.2.tgz#5ebb5e5a5b75f085f22bc3f8460fba308310fa78"