diff --git a/.devops/performance-test-pipelines.yaml b/.devops/performance-test-pipelines.yaml deleted file mode 100644 index d1732b5..0000000 --- a/.devops/performance-test-pipelines.yaml +++ /dev/null @@ -1,113 +0,0 @@ -# azure-pipelines.yml -trigger: none - -parameters: - - name: "ENVIRONMENT" - displayName: "Environment" - type: string - values: - - "dev" - - "uat" - - "prod" - default: "uat" - - name: "TEST_TYPE" - displayName: "Test type" - type: string - values: - - "load" - - "spike" - - "stress" - - "constant" - - "smoke" - - "ramp" - default: "constant" - - name: "SCRIPT" - displayName: "Script name" - type: string - values: - - "notice_single_generation" - - "notice_massive_generation" - - "notice_single_generation_on_massive" - default: "notice_single_generation" - - name: "DB_NAME" - displayName: "DB name" - type: string - values: - - printnoticek6 - - name: "TEMPLATE_ID" - displayName: "Template to use" - type: string - values: - - "TemplateSingleInstalment" - - "TemplateSingleInstalmentPoste" - - "TemplateInstalments" - - "TemplateInstalmentsPoste" - default: "TemplateSingleInstalment" - - name: "PROCESS_TIME" - displayName: "Process time for a massive request (in seconds)" - type: number - default: 5 - - name: "NUMBER_OF_MASSIVE_ELEMENTS" - displayName: "Number of notices in a massive request" - type: number - default: 10 -variables: - ${{ if eq(parameters['ENVIRONMENT'], 'dev') }}: - poolImage: 'pagopa-dev-loadtest-linux' - API_SUBSCRIPTION_KEY: "$(DEV_API_SUBSCRIPTION_KEY)" - blobInstitutionsConnectionString: "$(DEV_BLOB_INSTITUTIONS_CONNECTION_STRING)" - blobNoticesConnectionString: "$(DEV_BLOB_NOTICES_CONNECTION_STRING)" - mongoNoticesConnectionString: "$(DEV_MONGO_NOTICES_CONNECTION_STRING)" - - ${{ if eq(parameters['ENVIRONMENT'], 'uat') }}: - poolImage: 'pagopa-uat-loadtest-linux' - API_SUBSCRIPTION_KEY: "$(UAT_API_SUBSCRIPTION_KEY)" - blobInstitutionsConnectionString: "$(UAT_BLOB_INSTITUTIONS_CONNECTION_STRING)" - blobNoticesConnectionString: "$(UAT_BLOB_NOTICES_CONNECTION_STRING)" - mongoNoticesConnectionString: "$(UAT_MONGO_NOTICES_CONNECTION_STRING)" - - ${{ if eq(parameters['ENVIRONMENT'], 'prod') }}: - poolImage: 'pagopa-uat-loadtest-linux' - API_SUBSCRIPTION_KEY: "$(PROD_API_SUBSCRIPTION_KEY)" - blobInstitutionsConnectionString: "$(PROD_BLOB_INSTITUTIONS_CONNECTION_STRING)" - blobNoticesConnectionString: "$(PROD_BLOB_NOTICES_CONNECTION_STRING)" - mongoNoticesConnectionString: "$(PROD_MONGO_NOTICES_CONNECTION_STRING)" - -pool: - name: $(poolImage) - -steps: - - script: | - cd ./performance-test/src - docker pull grafana/k6 - displayName: Pull k6 image - - script: | - cd ./performance-test/src - docker build -f ./DockerfilePre -t exec-node . - docker run --rm --name initToRunk6 -e BLOB_INSTITUTIONS_CONN_STRING=${BLOB_INSTITUTIONS_CONN_STRING} -e BLOB_NOTICES_CONN_STRING=${BLOB_NOTICES_CONN_STRING} -e NOTICES_MONGO_CONN_STRING=${NOTICES_MONGO_CONN_STRING} -e ENVIRONMENT_STRING="${ENVIRONMENT_STRING}" exec-node - displayName: Precondition run perf test - env: - BLOB_INSTITUTIONS_CONN_STRING: ${{ variables.blobInstitutionsConnectionString }} - BLOB_NOTICES_CONN_STRING: ${{ variables.blobNoticesConnectionString }} - NOTICES_MONGO_CONN_STRING: ${{ variables.mongoNoticesConnectionString }} - NUMBER_OF_MASSIVE_ELEMENTS: ${{ parameters.NUMBER_OF_MASSIVE_ELEMENTS }} - ENVIRONMENT_STRING: ${{ parameters.ENVIRONMENT }} - - - script: | - cd ./performance-test - sh ./run_performance_test.sh ${{ parameters.ENVIRONMENT }} ${{ parameters.TEST_TYPE }} ${{ parameters.SCRIPT }} ${{ parameters.DB_NAME}} $OCP_APIM_SUBSCRIPTION_KEY ${{ parameters.TEMPLATE_ID }} ${{ parameters.PROCESS_TIME }} ${{ parameters.NUMBER_OF_MASSIVE_ELEMENTS }} - displayName: Run k6 ${{ parameters.SCRIPT }} on ${{ parameters.ENVIRONMENT }} - env: - OCP_APIM_SUBSCRIPTION_KEY: ${{ variables.API_SUBSCRIPTION_KEY }} - - - script: | - cd ./performance-test/src - docker build -f ./DockerfilePost -t exec-node . - docker run --rm --name initToRunk6 -e BLOB_INSTITUTIONS_CONN_STRING=${BLOB_INSTITUTIONS_CONN_STRING} -e BLOB_NOTICES_CONN_STRING=${BLOB_NOTICES_CONN_STRING} -e NOTICES_MONGO_CONN_STRING=${NOTICES_MONGO_CONN_STRING} -e ENVIRONMENT_STRING="${ENVIRONMENT_STRING}" exec-node - displayName: Teardown run perf test - env: - BLOB_INSTITUTIONS_CONN_STRING: ${{ variables.blobInstitutionsConnectionString }} - BLOB_NOTICES_CONN_STRING: ${{ variables.blobNoticesConnectionString }} - NOTICES_MONGO_CONN_STRING: ${{ variables.mongoNoticesConnectionString }} - ENVIRONMENT_STRING: ${{ parameters.ENVIRONMENT }} - diff --git a/.devops/performance-test-pipelines.yml b/.devops/performance-test-pipelines.yml new file mode 100644 index 0000000..3fc53be --- /dev/null +++ b/.devops/performance-test-pipelines.yml @@ -0,0 +1,59 @@ +# azure-pipelines.yml +trigger: none + +parameters: + - name: "ENVIRONMENT" + displayName: "Environment" + type: string + values: + - "dev" + - "uat" + default: "uat" + - name: "TEST_TYPE" + displayName: "Test type" + type: string + values: + - "load" + - "spike" + - "stress" + - "constant" + default: "constant" + - name: "NOTICE_TYPE" + displayName: "Notice type" + type: string + values: + - "single_opt" + - "single_and_many_opt" + - "single_and_multy_opt" + - "single_and_co_opt" + - "all" + default: "all" + - name: "DB_NAME" + displayName: "DB name" + type: string + values: + - pagopa_payment_options_servicek6 +variables: + ${{ if eq(parameters['ENVIRONMENT'], 'dev') }}: + poolImage: 'pagopa-dev-loadtest-linux' + API_SUBSCRIPTION_KEY: "$(DEV_API_SUBSCRIPTION_KEY)" + ${{ if eq(parameters['ENVIRONMENT'], 'uat') }}: + poolImage: 'pagopa-uat-loadtest-linux' + API_SUBSCRIPTION_KEY: "$(UAT_API_SUBSCRIPTION_KEY)" + +pool: + name: $(poolImage) + +steps: + - script: | + cd ./performance-test/src + docker pull grafana/k6 + displayName: Pull k6 image + - script: | + cd ./performance-test + sh ./run_performance_test.sh ${{ parameters.ENVIRONMENT }} ${{ parameters.TEST_TYPE }} payment_options_service_test ${{ parameters.DB_NAME}} $OCP_APIM_SUBSCRIPTION_KEY + displayName: Run k6 payment_options_service_test with notice type ${{ parameters.NOTICE_TYPE}} on ${{ parameters.ENVIRONMENT }} + env: + OCP_APIM_SUBSCRIPTION_KEY: ${{ variables.API_SUBSCRIPTION_KEY }} + NOTICE_TYPE: ${{ parameters.NOTICE_TYPE }} + diff --git a/perfomance-test/README.md b/perfomance-test/README.md deleted file mode 100644 index a7638d5..0000000 --- a/perfomance-test/README.md +++ /dev/null @@ -1 +0,0 @@ -Put here the performance tests with K6 diff --git a/performance-test/README.md b/performance-test/README.md new file mode 100644 index 0000000..880ead4 --- /dev/null +++ b/performance-test/README.md @@ -0,0 +1,14 @@ +# K6 tests + +This is a set of [k6](https://k6.io) tests. + +To invoke k6 tests use `run_performance_test.sh` script. + + +## How to run 🚀 + +Use this command to launch the tests: + +``` shell +sh run_performance_test.sh +``` \ No newline at end of file diff --git a/performance-test/docker-compose.yaml b/performance-test/docker-compose.yaml new file mode 100644 index 0000000..c07d21f --- /dev/null +++ b/performance-test/docker-compose.yaml @@ -0,0 +1,27 @@ +version: '3.3' +services: + k6: + image: grafana/k6 + container_name: k6 + volumes: + - '${PWD}/src:/scripts' + environment: + - OCP_APIM_SUBSCRIPTION_KEY=${ocp_apim_subscription_key} + - VARS=${env}.environment.json + - TEST_TYPE=/scripts/test-types/${type}.json + - K6_OUT=influxdb=http://nginx:8086/${db_name} + command: run /scripts/${script}.js + depends_on: + - nginx + + nginx: + image: nginx + container_name: nginx + volumes: + - '${PWD}/nginx/nginx.conf:/etc/nginx/nginx.conf' + environment: + - ENVIRONMENT=${env} + ports: + - "8086:8086" + - "80:80" + diff --git a/performance-test/nginx/nginx.conf b/performance-test/nginx/nginx.conf new file mode 100644 index 0000000..a9cc127 --- /dev/null +++ b/performance-test/nginx/nginx.conf @@ -0,0 +1,16 @@ +events { + worker_connections 1024; +} + +http { + server { + listen 8086; + location / { + proxy_pass https://api.uat.platform.pagopa.it/shared/influxdb/v1/; + proxy_http_version 1.1; + proxy_set_header Host api.uat.platform.pagopa.it; + proxy_pass_request_headers on; + } + } +} + diff --git a/performance-test/run_performance_test.sh b/performance-test/run_performance_test.sh new file mode 100644 index 0000000..3c831b1 --- /dev/null +++ b/performance-test/run_performance_test.sh @@ -0,0 +1,35 @@ +# sh run_performance_test.sh + +ENVIRONMENT=$1 +TYPE=$2 +SCRIPT=$3 +DB_NAME=$4 +OCP_APIM_SUBSCRIPTION_KEY=$5 + +if [ -z "$ENVIRONMENT" ] +then + echo "No env specified: sh run_performance_test.sh " + exit 1 +fi + +if [ -z "$TYPE" ] +then + echo "No test type specified: sh run_performance_test.sh " + exit 1 +fi +if [ -z "$SCRIPT" ] +then + echo "No script name specified: sh run_performance_test.sh " + exit 1 +fi + +export env=${ENVIRONMENT} +export type=${TYPE} +export script=${SCRIPT} +export db_name=${DB_NAME} +export ocp_apim_subscription_key=${OCP_APIM_SUBSCRIPTION_KEY} + +stack_name=$(cd .. && basename "$PWD") +docker compose -p "${stack_name}-k6" up -d --remove-orphans --force-recreate --build +docker logs -f k6 +docker stop nginx \ No newline at end of file diff --git a/performance-test/src/dev.environment.json b/performance-test/src/dev.environment.json new file mode 100644 index 0000000..ba2b627 --- /dev/null +++ b/performance-test/src/dev.environment.json @@ -0,0 +1,8 @@ +{ + "environment": [ + { + "env": "dev", + "paymentOptionsServiceURIBasePath": "https://api.dev.platform.pagopa.it/payment-options/service/v1" + } + ] +} diff --git a/performance-test/src/local.environment.json b/performance-test/src/local.environment.json new file mode 100644 index 0000000..6f50451 --- /dev/null +++ b/performance-test/src/local.environment.json @@ -0,0 +1,8 @@ +{ + "environment": [ + { + "env": "local", + "paymentOptionsServiceURIBasePath": "http://localhost:8080" + } + ] +} diff --git a/performance-test/src/modules/payment_options_client.js b/performance-test/src/modules/payment_options_client.js new file mode 100644 index 0000000..5fa31ac --- /dev/null +++ b/performance-test/src/modules/payment_options_client.js @@ -0,0 +1,19 @@ +import http from 'k6/http'; +const subKey = `${__ENV.OCP_APIM_SUBSCRIPTION_KEY}`; + +export function getToService(endpoint, params) { + let url = endpoint; + let headers = { + 'Ocp-Apim-Subscription-Key': subKey, + "Content-Type": "application/json" + }; + + const queryParams = params ? Object.entries(params) : []; + if (queryParams && queryParams.length) { + queryParams.forEach((el, index) => { + url = url.concat(index === 0 ? "?" : "&", el[0], "=", el[1]); + }); + } + + return http.get(url, { headers, responseType: "text", params }); +} \ No newline at end of file diff --git a/performance-test/src/package.json b/performance-test/src/package.json new file mode 100644 index 0000000..864ce35 --- /dev/null +++ b/performance-test/src/package.json @@ -0,0 +1,8 @@ +{ + "type": "module", + "dependencies": { + "axios": "^0.27.2" + }, + "scripts" : { + } +} diff --git a/performance-test/src/payment_options_service_test.js b/performance-test/src/payment_options_service_test.js new file mode 100644 index 0000000..135e31a --- /dev/null +++ b/performance-test/src/payment_options_service_test.js @@ -0,0 +1,57 @@ +import { getToService } from "./modules/payment_options_client.js"; +import { SharedArray } from 'k6/data'; +import { check } from 'k6'; + +const varsArray = new SharedArray('vars', function () { + return JSON.parse(open(`./${__ENV.VARS}`)).environment; +}); +export const ENV_VARS = varsArray[0]; + +const paymentOptionsServiceURIBasePath = `${ENV_VARS.paymentOptionsServiceURIBasePath}`; +const noticeType = `${__ENV.NOTICE_TYPE}`; + +const ORGANIZATIONAL_FISCAL_CODE = "77777777777"; + +const SINGLE_OPT_NOTICE_NUMBER = "311111111111111111"; +const SINGLE_AND_MANY_OPT_NOTICE_NUMBER = "311111111111111112"; +const SINGLE_AND_MULTI_OPT_NOTICE_NUMBER = "311111111111111116"; +const SINGLE_AND_CO_OPT_NOTICE_NUMBER = "311111111112222225"; + +const VALID_PSP = "99999000001"; + +const getSelectedNoticeNumbers = () => { + const noticeTypeAll = noticeType === "undefined" || noticeType === "all"; + + const selectedNotices = []; + if (noticeType === "single_opt" || noticeTypeAll) { + selectedNotices.push(SINGLE_OPT_NOTICE_NUMBER); + } + if (noticeType === "single_and_many_opt" || noticeTypeAll) { + selectedNotices.push(SINGLE_AND_MANY_OPT_NOTICE_NUMBER); + } + if (noticeType === "single_and_multy_opt" || noticeTypeAll) { + selectedNotices.push(SINGLE_AND_MULTI_OPT_NOTICE_NUMBER); + } + if (noticeType === "single_and_co_opt" || noticeTypeAll) { + selectedNotices.push(SINGLE_AND_CO_OPT_NOTICE_NUMBER); + } + console.log(`Selected the following notices: ${JSON.stringify(selectedNotices)}`); + return selectedNotices; +} + +export default function () { + const selectedNotices = getSelectedNoticeNumbers(); + for (let i = 0; i < selectedNotices.length; i++) { + const el = selectedNotices[i]; + let response = getToService(`${paymentOptionsServiceURIBasePath}/payment-options/organizations/${ORGANIZATIONAL_FISCAL_CODE}/notices/${el}`, { idPsp: VALID_PSP }); + console.info(`Payment Options Service getPaymentOptions with notice number ${el} call, Status ${response.status}`); + + let responseBody = JSON.parse(response.body); + + check(response, { + 'Payment Options Service getPaymentOptions status is 200': () => response.status === 200, + 'Payment Options Service getPaymentOptions body has list of payment options': () => + Boolean(responseBody && responseBody.paymentOptions && responseBody.paymentOptions.length) + }); + } +} \ No newline at end of file diff --git a/performance-test/src/test-types/constant.json b/performance-test/src/test-types/constant.json new file mode 100644 index 0000000..242fafb --- /dev/null +++ b/performance-test/src/test-types/constant.json @@ -0,0 +1,50 @@ +{ + "discardResponseBodies": true, + "summaryTrendStats": [ + "avg", + "min", + "med", + "max", + "p(95)", + "p(99)", + "p(99.99)", + "count" + ], + "scenarios": { + "contacts": { + "executor": "constant-arrival-rate", + "duration": "5m", + "rate": 75, + "timeUnit": "1s", + "preAllocatedVUs": 100, + "maxVUs": 200 + } + }, + "thresholds": { + "http_req_failed": [ + "rate<0.1" + ], + "http_req_duration": [ + "p(99)<2000" + ], + "http_req_duration{group:::setup}": [ + "max>=0" + ], + "http_req_duration{scenario:contacts}": [ + "max>=0" + ], + "iteration_duration{scenario:contacts}": [ + "max>=0" + ], + "iteration_duration{group:::setup}": [ + "max>=0" + ], + "iterations{group:::setup}": [ + "rate>=0" + ], + "iterations{scenario:contacts}": [ + "rate>=0" + ] + }, + "setupTimeout": "60m" + } \ No newline at end of file diff --git a/performance-test/src/test-types/load.json b/performance-test/src/test-types/load.json new file mode 100644 index 0000000..1525a1a --- /dev/null +++ b/performance-test/src/test-types/load.json @@ -0,0 +1,34 @@ +{ + "summaryTrendStats": [ + "avg", + "min", + "med", + "max", + "p(95)", + "p(99)", + "p(99.99)", + "count" + ], + "stages": [ + { + "duration": "1m", + "target": 20 + }, + { + "duration": "2m", + "target": 20 + }, + { + "duration": "2m", + "target": 0 + } + ], + "thresholds": { + "http_req_failed": [ + "rate<0.001" + ], + "http_req_duration": [ + "p(99)<1500" + ] + } +} diff --git a/performance-test/src/test-types/spike.json b/performance-test/src/test-types/spike.json new file mode 100644 index 0000000..efbf1d4 --- /dev/null +++ b/performance-test/src/test-types/spike.json @@ -0,0 +1,38 @@ +{ + "summaryTrendStats": [ + "avg", + "min", + "med", + "max", + "p(95)", + "p(99)", + "p(99.99)", + "count" + ], + "stages": [ + { + "duration": "10s", + "target": 1 + }, + { + "duration": "20s", + "target": 5 + }, + { + "duration": "10s", + "target": 1 + }, + { + "duration": "10s", + "target": 0 + } + ], + "thresholds": { + "http_req_failed": [ + "rate<0.001" + ], + "http_req_duration": [ + "p(99)<100" + ] + } +} diff --git a/performance-test/src/test-types/stress.json b/performance-test/src/test-types/stress.json new file mode 100644 index 0000000..f9cf5d5 --- /dev/null +++ b/performance-test/src/test-types/stress.json @@ -0,0 +1,61 @@ +{ + "summaryTrendStats": [ + "avg", + "min", + "med", + "max", + "p(95)", + "p(99)", + "p(99.99)", + "count" + ], + "stages": [ + { + "duration": "2m", + "target": 100 + }, + { + "duration": "5m", + "target": 100 + }, + { + "duration": "2m", + "target": 200 + }, + { + "duration": "5m", + "target": 200 + }, + { + "duration": "2m", + "target": 300 + }, + { + "duration": "5m", + "target": 300 + }, + { + "duration": "2m", + "target": 400 + }, + { + "duration": "5m", + "target": 400 + }, + { + "duration": "10m", + "target": 0 + } + ], + "thresholds": { + "http_req_failed": [ + "rate<0.001" + ], + "http_req_duration": [ + "p(99)<5000" + ], + "http_req_duration{bizEventMethod:GetOrganizationReceipt}": [ + "p(95)<1500" + ] + } +} diff --git a/performance-test/src/uat.environment.json b/performance-test/src/uat.environment.json new file mode 100644 index 0000000..2173e50 --- /dev/null +++ b/performance-test/src/uat.environment.json @@ -0,0 +1,8 @@ +{ + "environment": [ + { + "env": "uat", + "paymentOptionsServiceURIBasePath": "https://api.uat.platform.pagopa.it/payment-options/service/v1" + } + ] +}