From 8343075384e8b0b45c67f8fc89152c045490cca9 Mon Sep 17 00:00:00 2001 From: Jim Wild Date: Tue, 17 Jun 2025 14:24:51 +0000 Subject: [PATCH 01/20] Do some work defining the new artillery load test for notify. Also copy and edit the psu load test definition. Needs some more config. --- .devcontainer/Dockerfile | 2 +- .github/workflows/run_notify_load.yml | 129 ++++++++++++++++++++++++++ artillery/helper/allowed_odscodes.mjs | 4 + artillery/helper/psu.mjs | 12 ++- artillery/notify_entrypoint.mjs | 88 ++++++++++++++++++ artillery/notify_load_test.yml | 51 ++++++++++ 6 files changed, 280 insertions(+), 6 deletions(-) create mode 100644 .github/workflows/run_notify_load.yml create mode 100644 artillery/helper/allowed_odscodes.mjs create mode 100644 artillery/notify_entrypoint.mjs create mode 100644 artillery/notify_load_test.yml diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index f11ba757..7274b289 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -9,7 +9,7 @@ RUN apt-get update \ jq apt-transport-https ca-certificates gnupg-agent \ software-properties-common bash-completion python3-pip make libbz2-dev \ libreadline-dev libsqlite3-dev wget llvm libncurses5-dev libncursesw5-dev \ - xz-utils tk-dev liblzma-dev netcat libyaml-dev + xz-utils tk-dev liblzma-dev netcat-traditional libyaml-dev # install aws stuff RUN wget -O /tmp/awscliv2.zip "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" && \ diff --git a/.github/workflows/run_notify_load.yml b/.github/workflows/run_notify_load.yml new file mode 100644 index 00000000..95bc9a11 --- /dev/null +++ b/.github/workflows/run_notify_load.yml @@ -0,0 +1,129 @@ +name: Run psu notify load test + +on: + workflow_dispatch: + inputs: + environment: + description: 'Environment to run tests against. Allowed values are dev or ref' + required: true + default: 'ref' + arrivalRate: + description: 'The number of new users to add every second' + required: true + default: '275' + duration: + description: 'The duration of the main test' + required: true + default: '900' + rampUpDuration: + description: 'The duration to ramp up to the arrival rate' + required: true + default: '900' + maxVusers: + description: 'Maximum number of vusers to create' + required: true + default: '500' + +jobs: + run_artillery: + name: Run Artillery + runs-on: ubuntu-latest + permissions: + id-token: write + contents: read + steps: + - name: Show input params + shell: bash + run: | + echo "## environment : ${{ github.event.inputs.environment }}" >> "$GITHUB_STEP_SUMMARY" + echo "## arrivalRate : ${{ github.event.inputs.arrivalRate }}" >> "$GITHUB_STEP_SUMMARY" + echo "## duration : ${{ github.event.inputs.duration }}" >> "$GITHUB_STEP_SUMMARY" + echo "## rampUpDuration : ${{ github.event.inputs.rampUpDuration }}" >> "$GITHUB_STEP_SUMMARY" + echo "## maxVusers : ${{ github.event.inputs.maxVusers }}" >> "$GITHUB_STEP_SUMMARY" + + - name: Checkout repo + uses: actions/checkout@v4 + with: + ref: ${{ env.BRANCH_NAME }} + + - name: Install asdf + uses: asdf-vm/actions/setup@05e0d2ed97b598bfce82fd30daf324ae0c4570e6 + with: + asdf_branch: v0.14.0 + + - name: Cache asdf + uses: actions/cache@v4 + with: + path: | + ~/.asdf + key: ${{ runner.os }}-asdf-${{ hashFiles('**/.tool-versions') }} + restore-keys: | + ${{ runner.os }}-asdf- + + - name: Install asdf dependencies in .tool-versions + uses: asdf-vm/actions/install@05e0d2ed97b598bfce82fd30daf324ae0c4570e6 + with: + asdf_branch: v0.14.0 + env: + PYTHON_CONFIGURE_OPTS: --enable-shared + + - name: Install Dependencies + run: make install + + - name: Assume dev artillery runner role + if: github.event.inputs.environment == 'dev' + uses: aws-actions/configure-aws-credentials@v4 + with: + aws-region: eu-west-2 + role-to-assume: ${{ secrets.DEV_ARTILLERY_RUNNER_ROLE }} + role-session-name: github-actions-artillery + + - name: Assume ref artillery runner role + if: github.event.inputs.environment == 'ref' + uses: aws-actions/configure-aws-credentials@v4 + with: + aws-region: eu-west-2 + role-to-assume: ${{ secrets.REF_ARTILLERY_RUNNER_ROLE }} + role-session-name: github-actions-artillery + + - name: Run load tests + shell: bash + env: + environment: ${{ github.event.inputs.environment }} + arrivalRate: ${{ github.event.inputs.arrivalRate }} + duration: ${{ github.event.inputs.duration }} + rampUpDuration: ${{ github.event.inputs.rampUpDuration }} + maxVusers: ${{ github.event.inputs.maxVusers }} + run: | + ./scripts/run_psu_load_test.sh + + - uses: actions/upload-artifact@v4 + if: always() + name: Upload test_report + with: + name: test_report + path: | + psu_load_test.json + psu_load_test.json.html + + - name: Upload artifacts to S3, if we are using REF environment + if: github.event.inputs.environment == 'ref' + continue-on-error: true + env: + AWS_REGION: eu-west-2 + BUCKET_NAME: artilleryio-test-data-${{ secrets.AWS_ACCOUNT_ID }} + RUN_ID: ${{ github.run_id }} + run: | + aws s3 cp psu_load_test.json s3://$BUCKET_NAME/reports/$RUN_ID/psu_load_test.json --acl public-read + aws s3 cp psu_load_test.json.html s3://$BUCKET_NAME/reports/$RUN_ID/psu_load_test.json.html --acl public-read + + - name: Output link to HTML report + if: github.event.inputs.environment == 'ref' + env: + AWS_REGION: eu-west-2 + BUCKET_NAME: artilleryio-test-data-${{ secrets.AWS_ACCOUNT_ID }} + RUN_ID: ${{ github.run_id }} + run: | + REPORT_URL="https://$BUCKET_NAME.s3.${AWS_REGION}.amazonaws.com/reports/$RUN_ID/psu_load_test.json.html" + echo "Test report is hosted at: $REPORT_URL" >> "$GITHUB_STEP_SUMMARY" + echo "::set-output name=report_url::$REPORT_URL" \ No newline at end of file diff --git a/artillery/helper/allowed_odscodes.mjs b/artillery/helper/allowed_odscodes.mjs new file mode 100644 index 00000000..4c74917a --- /dev/null +++ b/artillery/helper/allowed_odscodes.mjs @@ -0,0 +1,4 @@ +// These ODS codes should match the values in AWS parameter store. They're assumed to be enabled in the whitelist +export const allowedOdsCodes = [ + // TODO: I need to define these +] \ No newline at end of file diff --git a/artillery/helper/psu.mjs b/artillery/helper/psu.mjs index fbaae91f..160b4978 100644 --- a/artillery/helper/psu.mjs +++ b/artillery/helper/psu.mjs @@ -6,17 +6,19 @@ const logger = pino() let oauthToken let tokenExpiryTime -export function getBody(isValid = true) { +export function getBody( + isValid = true, + status = "in-progress", + odsCode = "C9Z10", + nhsNumber = "9449304130", + businessStatus = "With Pharmacy", +) { // If this is intended to be a failed request, mangle the prescription ID. const prescriptionID = isValid ? shortPrescId() : invalidShortPrescId(); const task_identifier = uuidv4() const prescriptionOrderItemNumber = uuidv4() - const nhsNumber = "9449304130" const currentTimestamp = new Date().toISOString() - const odsCode = "C9Z1O" - const status = "in-progress" - const businessStatus = "With Pharmacy" const body = { resourceType: "Bundle", type: "transaction", diff --git a/artillery/notify_entrypoint.mjs b/artillery/notify_entrypoint.mjs new file mode 100644 index 00000000..89d99150 --- /dev/null +++ b/artillery/notify_entrypoint.mjs @@ -0,0 +1,88 @@ +import {v4 as uuidv4} from "uuid" +import pino from "pino" +import {getSharedAuthToken, getBody} from "./psu_entrypoint.mjs" +import {allowedOdsCodes} from "./allowed_odsCodes.js" + +const logger = pino() + +const NUM_ODS_CODES = 10000 + +// make a list of 10k ODS codes, using the known-valid ones as a seed +const fullOdsCodes = (() => { + const set = new Set(allowedOdsCodes) + const codes = [...allowedOdsCodes] + + const letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + const numbers = "0123456789" + + while (codes.length < NUM_ODS_CODES) { + let c = "" + for (let i = 0; i < 2; i++) { + c += letters.charAt(Math.floor(Math.random() * letters.length)) + } + for (let i = 0; i < 3; i++) { + c += numbers.charAt(Math.floor(Math.random() * numbers.length)) + } + + if (!set.has(c)) { + set.add(c) + codes.push(c) + } + } + + return codes +})() + +function computeCheckDigit(nhsNumber) { + const factors = [10,9,8,7,6,5,4,3,2] + let total = 0 + + for (let i = 0; i < 9; i++) { + total += parseInt(nhsNumber.charAt(i),10) * factors[i] + } + + const rem = total % 11 + let d = 11 - rem + if (d === 11) d = 0 + + return d +} + +function generateValidNhsNumber() { + while (true) { + const partial = Array.from({length:9},() => Math.floor(Math.random()*10)).join("") + const cd = computeCheckDigit(partial) + if (cd < 10) return partial + cd + } +} + +export function initUser(_, vuContext) { + // Generate data for a patient + vuContext.vars.odsCode = fullOdsCodes[Math.floor(Math.random()*fullOdsCodes.length)] + vuContext.vars.nhsNumber = generateValidNhsNumber() + + let prescriptionCount = Math.round(Math.sampleNormal(3,1)) + if (prescriptionCount < 1) prescriptionCount = 1 // just truncate at 1. + vuContext.vars.prescriptionCount = prescriptionCount +} + +// beforeEach request +export function generatePrescData(requestParams, vuContext) { + const body = getBody( + true, /* isValid */ + "ready to collect", /* status */ + vuContext.vars.odsCode, /* odsCode */ + vuContext.vars.nhsNumber /* nhsNumber */ + ) + + requestParams.json = body + vuContext.vars.x_request_id = uuidv4() + vuContext.vars.x_correlation_id = uuidv4() + + // Wait this long between requests + let delay = Math.sampleNormal(150, 60) + if (delay < 0) delay = 0 + vuContext.vars.nextDelay = delay +} + +export { getSharedAuthToken } diff --git a/artillery/notify_load_test.yml b/artillery/notify_load_test.yml new file mode 100644 index 00000000..2fc20a68 --- /dev/null +++ b/artillery/notify_load_test.yml @@ -0,0 +1,51 @@ +config: + processor: "./notify_entrypoint.mjs" + phases: + - name: ramp-up + duration: "10m" + arrivalRate: 20 + rampTo: 100 + maxVusers: 1000 + - name: steady-state + duration: "30m" + arrivalRate: 100 + maxVusers: 1000 + environments: + dev: + target: https://internal-dev.api.service.nhs.uk + ref: + target: https://ref.api.service.nhs.uk + int: + target: https://int.api.service.nhs.uk + pr: + target: https://psu-pr-{{ prNumber }}.dev.eps.national.nhs.uk + defaults: + headers: + Authorization: "Bearer {{ authToken }}" + +scenarios: + - name: dynamic PSU calls for each patient + + # grab token once per VU + before: + - function: getSharedAuthToken + + flow: + - function: initUser + + # for each prescription (number of prescriptions is variable) + - loop: + count: "{{ prescriptionCount }}" + steps: + - beforeRequest: generatePrescData + + - post: + url: "/prescription-status-update/" + headers: + x-request-id: "{{ x_request_id }}" + x-correlation-id: "{{ x_correlation_id }}" + expect: + - statusCode: 201 + + # wait before next prescription + - think: "{{ nextDelay }}" From 022359a24df2a7ced747bda3340acc8b944c8435 Mon Sep 17 00:00:00 2001 From: Jim Wild Date: Tue, 17 Jun 2025 14:46:17 +0000 Subject: [PATCH 02/20] Add missing newline characters --- .github/workflows/run_notify_load.yml | 2 +- artillery/helper/allowed_odscodes.mjs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/run_notify_load.yml b/.github/workflows/run_notify_load.yml index 95bc9a11..3749cd96 100644 --- a/.github/workflows/run_notify_load.yml +++ b/.github/workflows/run_notify_load.yml @@ -126,4 +126,4 @@ jobs: run: | REPORT_URL="https://$BUCKET_NAME.s3.${AWS_REGION}.amazonaws.com/reports/$RUN_ID/psu_load_test.json.html" echo "Test report is hosted at: $REPORT_URL" >> "$GITHUB_STEP_SUMMARY" - echo "::set-output name=report_url::$REPORT_URL" \ No newline at end of file + echo "::set-output name=report_url::$REPORT_URL" diff --git a/artillery/helper/allowed_odscodes.mjs b/artillery/helper/allowed_odscodes.mjs index 4c74917a..aff41a63 100644 --- a/artillery/helper/allowed_odscodes.mjs +++ b/artillery/helper/allowed_odscodes.mjs @@ -1,4 +1,4 @@ // These ODS codes should match the values in AWS parameter store. They're assumed to be enabled in the whitelist export const allowedOdsCodes = [ // TODO: I need to define these -] \ No newline at end of file +] From 2e4df68cfdc2bb7081e2ff11a0bb755c0e50199d Mon Sep 17 00:00:00 2001 From: Jim Wild Date: Tue, 17 Jun 2025 14:47:36 +0000 Subject: [PATCH 03/20] Changed my mind. Use env var config for ramp up and load values --- artillery/notify_load_test.yml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/artillery/notify_load_test.yml b/artillery/notify_load_test.yml index 2fc20a68..58f484f2 100644 --- a/artillery/notify_load_test.yml +++ b/artillery/notify_load_test.yml @@ -1,15 +1,15 @@ config: processor: "./notify_entrypoint.mjs" phases: - - name: ramp-up - duration: "10m" - arrivalRate: 20 - rampTo: 100 - maxVusers: 1000 - - name: steady-state - duration: "30m" - arrivalRate: 100 - maxVusers: 1000 + - name: ramp up phase + duration: "{{ $env.rampUpDuration }}" + arrivalRate: 1 + rampTo: "{{ $env.arrivalRate }}" + maxVusers: "{{ $env.maxVusers }}" + - name: run phase + duration: "{{ $env.duration }}" + arrivalRate: "{{ $env.arrivalRate }}" + maxVusers: "{{ $env.maxVusers }}" environments: dev: target: https://internal-dev.api.service.nhs.uk From 07fc66259d9fec39026b6f2eb5ff4f5af48b079c Mon Sep 17 00:00:00 2001 From: Jim Wild Date: Tue, 17 Jun 2025 14:56:29 +0000 Subject: [PATCH 04/20] Forgot to update the runner script --- .github/workflows/run_notify_load.yml | 14 +++--- scripts/run_notify_load_test.sh | 66 +++++++++++++++++++++++++++ 2 files changed, 73 insertions(+), 7 deletions(-) create mode 100755 scripts/run_notify_load_test.sh diff --git a/.github/workflows/run_notify_load.yml b/.github/workflows/run_notify_load.yml index 3749cd96..d6ea151e 100644 --- a/.github/workflows/run_notify_load.yml +++ b/.github/workflows/run_notify_load.yml @@ -22,7 +22,7 @@ on: maxVusers: description: 'Maximum number of vusers to create' required: true - default: '500' + default: '10000' jobs: run_artillery: @@ -95,7 +95,7 @@ jobs: rampUpDuration: ${{ github.event.inputs.rampUpDuration }} maxVusers: ${{ github.event.inputs.maxVusers }} run: | - ./scripts/run_psu_load_test.sh + ./scripts/run_notify_load_test.sh - uses: actions/upload-artifact@v4 if: always() @@ -103,8 +103,8 @@ jobs: with: name: test_report path: | - psu_load_test.json - psu_load_test.json.html + notify_load_test.json + notify_load_test.json.html - name: Upload artifacts to S3, if we are using REF environment if: github.event.inputs.environment == 'ref' @@ -114,8 +114,8 @@ jobs: BUCKET_NAME: artilleryio-test-data-${{ secrets.AWS_ACCOUNT_ID }} RUN_ID: ${{ github.run_id }} run: | - aws s3 cp psu_load_test.json s3://$BUCKET_NAME/reports/$RUN_ID/psu_load_test.json --acl public-read - aws s3 cp psu_load_test.json.html s3://$BUCKET_NAME/reports/$RUN_ID/psu_load_test.json.html --acl public-read + aws s3 cp notify_load_test.json s3://$BUCKET_NAME/reports/$RUN_ID/notify_load_test.json --acl public-read + aws s3 cp notify_load_test.json.html s3://$BUCKET_NAME/reports/$RUN_ID/notify_load_test.json.html --acl public-read - name: Output link to HTML report if: github.event.inputs.environment == 'ref' @@ -124,6 +124,6 @@ jobs: BUCKET_NAME: artilleryio-test-data-${{ secrets.AWS_ACCOUNT_ID }} RUN_ID: ${{ github.run_id }} run: | - REPORT_URL="https://$BUCKET_NAME.s3.${AWS_REGION}.amazonaws.com/reports/$RUN_ID/psu_load_test.json.html" + REPORT_URL="https://$BUCKET_NAME.s3.${AWS_REGION}.amazonaws.com/reports/$RUN_ID/notify_load_test.json.html" echo "Test report is hosted at: $REPORT_URL" >> "$GITHUB_STEP_SUMMARY" echo "::set-output name=report_url::$REPORT_URL" diff --git a/scripts/run_notify_load_test.sh b/scripts/run_notify_load_test.sh new file mode 100755 index 00000000..4cde2a90 --- /dev/null +++ b/scripts/run_notify_load_test.sh @@ -0,0 +1,66 @@ +#!/usr/bin/env bash + +if [ -z "${maxVusers}" ]; then + echo "maxVusers is unset or set to the empty string" + exit 1 +fi + +if [ -z "${duration}" ]; then + echo "duration is unset or set to the empty string" + exit 1 +fi + +if [ -z "${environment}" ]; then + echo "environment is unset or set to the empty string" + exit 1 +fi + +if [ -z "${arrivalRate}" ]; then + echo "arrivalRate is unset or set to the empty string" + exit 1 +fi + +if [ -z "${rampUpDuration}" ]; then + echo "rampUpDuration is unset or set to the empty string" + exit 1 +fi + +if ! [[ "${environment}" =~ ^(dev|ref)$ ]] +then + echo "environment must be dev or ref" + exit 1 +fi + +security_group=$(aws cloudformation list-exports --output json | jq -r '.Exports[] | select(.Name == "artillery-resources:ArtillerySecurityGroupId") | .Value' | grep -o '[^:]*$') +export security_group +vpc_subnets=$(aws cloudformation list-exports --output json | jq -r '.Exports[] | select(.Name == "vpc-resources:PrivateSubnets") | .Value' | grep -o '[^:]*$') +export vpc_subnets + +artillery_worker_role_name=$(aws cloudformation list-exports --output json | jq -r '.Exports[] | select(.Name == "artillery-resources:ArtilleryWorkerRoleName") | .Value' | grep -o '[^:]*$') +export artillery_worker_role_name + +cat < runtimeenv.env +maxVusers=$maxVusers +duration=$duration +arrivalRate=$arrivalRate +rampUpDuration=$rampUpDuration +EOF + +echo ${launch_config} + +# shellcheck disable=SC2090,SC2086 +npx artillery run-fargate \ + --environment "${environment}" \ + --secret psu_api_key \ + --secret psu_private_key \ + --secret psu_kid \ + --region eu-west-2 \ + --cluster artilleryio-cluster \ + --security-group-ids "${security_group}" \ + --subnet-ids "${vpc_subnets}" \ + --task-role-name "${artillery_worker_role_name}" \ + --dotenv runtimeenv.env \ + --output notify_load_test.json \ + artillery/notify_load_test.yml + +npx artillery report notify_load_test.json From 4d74d03d9726a968bc07433b1daf0edddf62a017 Mon Sep 17 00:00:00 2001 From: Jim Wild Date: Tue, 17 Jun 2025 15:02:54 +0000 Subject: [PATCH 05/20] Refactor ODS code generation to be more JS, and less python --- artillery/notify_entrypoint.mjs | 46 +++++++++++++++------------------ 1 file changed, 21 insertions(+), 25 deletions(-) diff --git a/artillery/notify_entrypoint.mjs b/artillery/notify_entrypoint.mjs index 89d99150..bb4a9c76 100644 --- a/artillery/notify_entrypoint.mjs +++ b/artillery/notify_entrypoint.mjs @@ -7,31 +7,27 @@ const logger = pino() const NUM_ODS_CODES = 10000 -// make a list of 10k ODS codes, using the known-valid ones as a seed -const fullOdsCodes = (() => { - const set = new Set(allowedOdsCodes) - const codes = [...allowedOdsCodes] - - const letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - const numbers = "0123456789" - - while (codes.length < NUM_ODS_CODES) { - let c = "" - for (let i = 0; i < 2; i++) { - c += letters.charAt(Math.floor(Math.random() * letters.length)) - } - for (let i = 0; i < 3; i++) { - c += numbers.charAt(Math.floor(Math.random() * numbers.length)) - } - - if (!set.has(c)) { - set.add(c) - codes.push(c) - } - } - - return codes -})() +const LETTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; +const DIGITS = "0123456789"; + +const randomChar = (chars) => chars[Math.floor(Math.random() * chars.length)]; + +/** Generate one two-letter, three-digit ODS code, e.g. "AB123" */ +const generateOdsCode = () => + `${randomChar(LETTERS)}${randomChar(LETTERS)}${randomChar(DIGITS)}${randomChar(DIGITS)}${randomChar(DIGITS)}`; + +function buildFullOdsCodes(targetCount, seedCodes) { + const codes = new Set(seedCodes); + + while (codes.size < targetCount) { + codes.add(generateOdsCode()); + } + + return Array.from(codes); +} + +/** The complete list of 10k ODS codes */ +const fullOdsCodes = buildFullOdsCodes(NUM_ODS_CODES, allowedOdsCodes); function computeCheckDigit(nhsNumber) { const factors = [10,9,8,7,6,5,4,3,2] From b0809f73ba7ebf61a7fe85043111d62e50eabd28 Mon Sep 17 00:00:00 2001 From: Jim Wild Date: Wed, 18 Jun 2025 11:14:40 +0000 Subject: [PATCH 06/20] loop has to be an array --- artillery/notify_load_test.yml | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/artillery/notify_load_test.yml b/artillery/notify_load_test.yml index 58f484f2..3d4ddbb2 100644 --- a/artillery/notify_load_test.yml +++ b/artillery/notify_load_test.yml @@ -35,17 +35,15 @@ scenarios: # for each prescription (number of prescriptions is variable) - loop: - count: "{{ prescriptionCount }}" - steps: - - beforeRequest: generatePrescData - - - post: - url: "/prescription-status-update/" - headers: - x-request-id: "{{ x_request_id }}" - x-correlation-id: "{{ x_correlation_id }}" - expect: - - statusCode: 201 - - # wait before next prescription - - think: "{{ nextDelay }}" + - count: "{{ prescriptionCount }}" + steps: + - beforeRequest: generatePrescData + - post: + url: "/prescription-status-update/" + headers: + x-request-id: "{{ x_request_id }}" + x-correlation-id: "{{ x_correlation_id }}" + expect: + - statusCode: 201 + # wait before next prescription + - think: "{{ nextDelay }}" From 1d7b9a3ab42cad1311e19bb973ea549b2514a1ec Mon Sep 17 00:00:00 2001 From: Jim Wild Date: Wed, 18 Jun 2025 11:33:30 +0000 Subject: [PATCH 07/20] Add allowed ODS codes FA501 - FA999 --- artillery/helper/allowed_odscodes.mjs | 28 +++++++++++++++++++++++++-- artillery/notify_entrypoint.mjs | 2 +- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/artillery/helper/allowed_odscodes.mjs b/artillery/helper/allowed_odscodes.mjs index aff41a63..4d086887 100644 --- a/artillery/helper/allowed_odscodes.mjs +++ b/artillery/helper/allowed_odscodes.mjs @@ -1,4 +1,28 @@ // These ODS codes should match the values in AWS parameter store. They're assumed to be enabled in the whitelist -export const allowedOdsCodes = [ - // TODO: I need to define these +export const allowedOdsCodes = [ // FAXXX for XXX in {501..999} + "FA501","FA502","FA503","FA504","FA505","FA506","FA507","FA508","FA509","FA510","FA511","FA512","FA513","FA514","FA515","FA516","FA517","FA518","FA519","FA520", + "FA521","FA522","FA523","FA524","FA525","FA526","FA527","FA528","FA529","FA530","FA531","FA532","FA533","FA534","FA535","FA536","FA537","FA538","FA539","FA540", + "FA541","FA542","FA543","FA544","FA545","FA546","FA547","FA548","FA549","FA550","FA551","FA552","FA553","FA554","FA555","FA556","FA557","FA558","FA559","FA560", + "FA561","FA562","FA563","FA564","FA565","FA566","FA567","FA568","FA569","FA570","FA571","FA572","FA573","FA574","FA575","FA576","FA577","FA578","FA579","FA580", + "FA581","FA582","FA583","FA584","FA585","FA586","FA587","FA588","FA589","FA590","FA591","FA592","FA593","FA594","FA595","FA596","FA597","FA598","FA599","FA600", + "FA601","FA602","FA603","FA604","FA605","FA606","FA607","FA608","FA609","FA610","FA611","FA612","FA613","FA614","FA615","FA616","FA617","FA618","FA619","FA620", + "FA621","FA622","FA623","FA624","FA625","FA626","FA627","FA628","FA629","FA630","FA631","FA632","FA633","FA634","FA635","FA636","FA637","FA638","FA639","FA640", + "FA641","FA642","FA643","FA644","FA645","FA646","FA647","FA648","FA649","FA650","FA651","FA652","FA653","FA654","FA655","FA656","FA657","FA658","FA659","FA660", + "FA661","FA662","FA663","FA664","FA665","FA666","FA667","FA668","FA669","FA670","FA671","FA672","FA673","FA674","FA675","FA676","FA677","FA678","FA679","FA680", + "FA681","FA682","FA683","FA684","FA685","FA686","FA687","FA688","FA689","FA690","FA691","FA692","FA693","FA694","FA695","FA696","FA697","FA698","FA699","FA700", + "FA701","FA702","FA703","FA704","FA705","FA706","FA707","FA708","FA709","FA710","FA711","FA712","FA713","FA714","FA715","FA716","FA717","FA718","FA719","FA720", + "FA721","FA722","FA723","FA724","FA725","FA726","FA727","FA728","FA729","FA730","FA731","FA732","FA733","FA734","FA735","FA736","FA737","FA738","FA739","FA740", + "FA741","FA742","FA743","FA744","FA745","FA746","FA747","FA748","FA749","FA750","FA751","FA752","FA753","FA754","FA755","FA756","FA757","FA758","FA759","FA760", + "FA761","FA762","FA763","FA764","FA765","FA766","FA767","FA768","FA769","FA770","FA771","FA772","FA773","FA774","FA775","FA776","FA777","FA778","FA779","FA780", + "FA781","FA782","FA783","FA784","FA785","FA786","FA787","FA788","FA789","FA790","FA791","FA792","FA793","FA794","FA795","FA796","FA797","FA798","FA799","FA800", + "FA801","FA802","FA803","FA804","FA805","FA806","FA807","FA808","FA809","FA810","FA811","FA812","FA813","FA814","FA815","FA816","FA817","FA818","FA819","FA820", + "FA821","FA822","FA823","FA824","FA825","FA826","FA827","FA828","FA829","FA830","FA831","FA832","FA833","FA834","FA835","FA836","FA837","FA838","FA839","FA840", + "FA841","FA842","FA843","FA844","FA845","FA846","FA847","FA848","FA849","FA850","FA851","FA852","FA853","FA854","FA855","FA856","FA857","FA858","FA859","FA860", + "FA861","FA862","FA863","FA864","FA865","FA866","FA867","FA868","FA869","FA870","FA871","FA872","FA873","FA874","FA875","FA876","FA877","FA878","FA879","FA880", + "FA881","FA882","FA883","FA884","FA885","FA886","FA887","FA888","FA889","FA890","FA891","FA892","FA893","FA894","FA895","FA896","FA897","FA898","FA899","FA900", + "FA901","FA902","FA903","FA904","FA905","FA906","FA907","FA908","FA909","FA910","FA911","FA912","FA913","FA914","FA915","FA916","FA917","FA918","FA919","FA920", + "FA921","FA922","FA923","FA924","FA925","FA926","FA927","FA928","FA929","FA930","FA931","FA932","FA933","FA934","FA935","FA936","FA937","FA938","FA939","FA940", + "FA941","FA942","FA943","FA944","FA945","FA946","FA947","FA948","FA949","FA950","FA951","FA952","FA953","FA954","FA955","FA956","FA957","FA958","FA959","FA960", + "FA961","FA962","FA963","FA964","FA965","FA966","FA967","FA968","FA969","FA970","FA971","FA972","FA973","FA974","FA975","FA976","FA977","FA978","FA979","FA980", + "FA981","FA982","FA983","FA984","FA985","FA986","FA987","FA988","FA989","FA990","FA991","FA992","FA993","FA994","FA995","FA996","FA997","FA998","FA999" ] diff --git a/artillery/notify_entrypoint.mjs b/artillery/notify_entrypoint.mjs index bb4a9c76..5f89f3d4 100644 --- a/artillery/notify_entrypoint.mjs +++ b/artillery/notify_entrypoint.mjs @@ -5,7 +5,7 @@ import {allowedOdsCodes} from "./allowed_odsCodes.js" const logger = pino() -const NUM_ODS_CODES = 10000 +const NUM_ODS_CODES = 1000 const LETTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; const DIGITS = "0123456789"; From 9cde7654566d4fc55504e17f1603da0c43e3606e Mon Sep 17 00:00:00 2001 From: Jim Wild Date: Wed, 18 Jun 2025 13:01:32 +0000 Subject: [PATCH 08/20] Fix broken import --- Makefile | 5 ++- artillery/notify_entrypoint.mjs | 4 +- scripts/test_notify_load_test.sh | 74 ++++++++++++++++++++++++++++++++ 3 files changed, 80 insertions(+), 3 deletions(-) create mode 100755 scripts/test_notify_load_test.sh diff --git a/Makefile b/Makefile index 88a4bcbd..ac6a846a 100644 --- a/Makefile +++ b/Makefile @@ -36,4 +36,7 @@ local-cpsu: ./scripts/test_cpsu_load_test.sh local-psu: - ./scripts/test_psu_load_test.sh \ No newline at end of file + ./scripts/test_psu_load_test.sh + +local-notify: + ./scripts/test_notify_load_test.sh \ No newline at end of file diff --git a/artillery/notify_entrypoint.mjs b/artillery/notify_entrypoint.mjs index 5f89f3d4..51ebb09f 100644 --- a/artillery/notify_entrypoint.mjs +++ b/artillery/notify_entrypoint.mjs @@ -1,7 +1,7 @@ import {v4 as uuidv4} from "uuid" import pino from "pino" -import {getSharedAuthToken, getBody} from "./psu_entrypoint.mjs" -import {allowedOdsCodes} from "./allowed_odsCodes.js" +import {getSharedAuthToken, getBody} from "./helper/psu.mjs" +import {allowedOdsCodes} from "./helper/allowed_odscodes.mjs" const logger = pino() diff --git a/scripts/test_notify_load_test.sh b/scripts/test_notify_load_test.sh new file mode 100755 index 00000000..80ae66ec --- /dev/null +++ b/scripts/test_notify_load_test.sh @@ -0,0 +1,74 @@ +#!/usr/bin/env bash + + +if [ -z "${environment}" ]; then + echo "environment is unset or set to the empty string" + exit 1 +fi + +if [ -z "${maxVusers}" ]; then + echo "maxVusers is unset or set to the empty string" + exit 1 +fi + +if [ -z "${duration}" ]; then + echo "duration is unset or set to the empty string" + exit 1 +fi + +if [ -z "${arrivalRate}" ]; then + echo "arrivalRate is unset or set to the empty string" + exit 1 +fi + +if [ -z "${rampUpDuration}" ]; then + echo "rampUpDuration is unset or set to the empty string" + exit 1 +fi + +if [ -z "${psu_api_key}" ]; then + echo "psu_api_key is unset or set to the empty string" + exit 1 +fi + +if [ -z "${psu_private_key}" ]; then + echo "psu_private_key is unset or set to the empty string" + exit 1 +fi + +if [ -z "${psu_kid}" ]; then + echo "psu_kid is unset or set to the empty string" + exit 1 +fi + + +# Create a dotenv file with the variables for Artillery +cat < runtimeenv.env +maxVusers=${maxVusers} +duration=${duration} +arrivalRate=${arrivalRate} +rampUpDuration=${rampUpDuration} +psu_api_key="${psu_api_key}" +psu_private_key="${psu_private_key}" +psu_kid="${psu_kid}" +EOF + +echo "Running Artillery test locally..." +echo "" +echo "Max Virtual Users: ${maxVusers}" +echo "Phase Duration: ${duration}" +echo "Arrival Rate: ${arrivalRate}" +echo "Ramp Up Duration: ${rampUpDuration}" +echo "" + +set -e + +# Run the Artillery test locally +npx artillery run \ + -e "${environment}" \ + --dotenv /workspaces/eps-load-test/runtimeenv.env \ + --output /workspaces/eps-load-test/notify_load_test.json \ + /workspaces/eps-load-test/artillery/notify_load_test.yml + +# Generate a report from the test results +npx /workspaces/eps-load-test/artillery report notify_load_test.json From 01b565398b6e1dfbeeb227aa2d51c73953f5e0b9 Mon Sep 17 00:00:00 2001 From: Jim Wild Date: Wed, 18 Jun 2025 13:39:34 +0000 Subject: [PATCH 09/20] fix context issue --- artillery/notify_entrypoint.mjs | 42 ++++++++++++++++++++++----------- 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/artillery/notify_entrypoint.mjs b/artillery/notify_entrypoint.mjs index 51ebb09f..e267da2e 100644 --- a/artillery/notify_entrypoint.mjs +++ b/artillery/notify_entrypoint.mjs @@ -52,33 +52,47 @@ function generateValidNhsNumber() { } } -export function initUser(_, vuContext) { +// Apparently Math.sampleNormal isn't a function? Do a quick Box-Muller transform instead +function sampleNormal(mean = 0, sd = 1) { + let u = 0, v = 0; + // avoid zeros because of log(0) + while (u === 0) u = Math.random(); + while (v === 0) v = Math.random(); + const z = Math.sqrt(-2 * Math.log(u)) * Math.cos(2 * Math.PI * v); + return z * sd + mean; +} + +export function initUser(context, events, done) { // Generate data for a patient - vuContext.vars.odsCode = fullOdsCodes[Math.floor(Math.random()*fullOdsCodes.length)] - vuContext.vars.nhsNumber = generateValidNhsNumber() + context.vars.odsCode = fullOdsCodes[Math.floor(Math.random()*fullOdsCodes.length)] + context.vars.nhsNumber = generateValidNhsNumber() - let prescriptionCount = Math.round(Math.sampleNormal(3,1)) + let prescriptionCount = Math.round(sampleNormal(3,1)) if (prescriptionCount < 1) prescriptionCount = 1 // just truncate at 1. - vuContext.vars.prescriptionCount = prescriptionCount + context.vars.prescriptionCount = prescriptionCount + + done() } // beforeEach request -export function generatePrescData(requestParams, vuContext) { +export function generatePrescData(requestParams, context, ee, next) { const body = getBody( - true, /* isValid */ - "ready to collect", /* status */ - vuContext.vars.odsCode, /* odsCode */ - vuContext.vars.nhsNumber /* nhsNumber */ + true, /* isValid */ + "ready to collect", /* status */ + context.vars.odsCode, /* odsCode */ + context.vars.nhsNumber /* nhsNumber */ ) requestParams.json = body - vuContext.vars.x_request_id = uuidv4() - vuContext.vars.x_correlation_id = uuidv4() + context.vars.x_request_id = uuidv4() + context.vars.x_correlation_id = uuidv4() // Wait this long between requests - let delay = Math.sampleNormal(150, 60) + let delay = sampleNormal(150, 60) if (delay < 0) delay = 0 - vuContext.vars.nextDelay = delay + context.vars.nextDelay = delay + + next() } export { getSharedAuthToken } From b0aa7c12dc057c5863b10f1fa5ea1e98258a1985 Mon Sep 17 00:00:00 2001 From: Jim Wild Date: Wed, 18 Jun 2025 14:45:07 +0000 Subject: [PATCH 10/20] Running the flow, but getting 401 errors --- artillery/helper/allowed_odscodes.mjs | 28 ----------------------- artillery/helper/odscodes.mjs | 8 +++++++ artillery/notify_entrypoint.mjs | 16 ++++++++------ artillery/notify_load_test.yml | 32 +++++++++++++-------------- scripts/test_notify_load_test.sh | 1 + 5 files changed, 33 insertions(+), 52 deletions(-) delete mode 100644 artillery/helper/allowed_odscodes.mjs create mode 100644 artillery/helper/odscodes.mjs diff --git a/artillery/helper/allowed_odscodes.mjs b/artillery/helper/allowed_odscodes.mjs deleted file mode 100644 index 4d086887..00000000 --- a/artillery/helper/allowed_odscodes.mjs +++ /dev/null @@ -1,28 +0,0 @@ -// These ODS codes should match the values in AWS parameter store. They're assumed to be enabled in the whitelist -export const allowedOdsCodes = [ // FAXXX for XXX in {501..999} - "FA501","FA502","FA503","FA504","FA505","FA506","FA507","FA508","FA509","FA510","FA511","FA512","FA513","FA514","FA515","FA516","FA517","FA518","FA519","FA520", - "FA521","FA522","FA523","FA524","FA525","FA526","FA527","FA528","FA529","FA530","FA531","FA532","FA533","FA534","FA535","FA536","FA537","FA538","FA539","FA540", - "FA541","FA542","FA543","FA544","FA545","FA546","FA547","FA548","FA549","FA550","FA551","FA552","FA553","FA554","FA555","FA556","FA557","FA558","FA559","FA560", - "FA561","FA562","FA563","FA564","FA565","FA566","FA567","FA568","FA569","FA570","FA571","FA572","FA573","FA574","FA575","FA576","FA577","FA578","FA579","FA580", - "FA581","FA582","FA583","FA584","FA585","FA586","FA587","FA588","FA589","FA590","FA591","FA592","FA593","FA594","FA595","FA596","FA597","FA598","FA599","FA600", - "FA601","FA602","FA603","FA604","FA605","FA606","FA607","FA608","FA609","FA610","FA611","FA612","FA613","FA614","FA615","FA616","FA617","FA618","FA619","FA620", - "FA621","FA622","FA623","FA624","FA625","FA626","FA627","FA628","FA629","FA630","FA631","FA632","FA633","FA634","FA635","FA636","FA637","FA638","FA639","FA640", - "FA641","FA642","FA643","FA644","FA645","FA646","FA647","FA648","FA649","FA650","FA651","FA652","FA653","FA654","FA655","FA656","FA657","FA658","FA659","FA660", - "FA661","FA662","FA663","FA664","FA665","FA666","FA667","FA668","FA669","FA670","FA671","FA672","FA673","FA674","FA675","FA676","FA677","FA678","FA679","FA680", - "FA681","FA682","FA683","FA684","FA685","FA686","FA687","FA688","FA689","FA690","FA691","FA692","FA693","FA694","FA695","FA696","FA697","FA698","FA699","FA700", - "FA701","FA702","FA703","FA704","FA705","FA706","FA707","FA708","FA709","FA710","FA711","FA712","FA713","FA714","FA715","FA716","FA717","FA718","FA719","FA720", - "FA721","FA722","FA723","FA724","FA725","FA726","FA727","FA728","FA729","FA730","FA731","FA732","FA733","FA734","FA735","FA736","FA737","FA738","FA739","FA740", - "FA741","FA742","FA743","FA744","FA745","FA746","FA747","FA748","FA749","FA750","FA751","FA752","FA753","FA754","FA755","FA756","FA757","FA758","FA759","FA760", - "FA761","FA762","FA763","FA764","FA765","FA766","FA767","FA768","FA769","FA770","FA771","FA772","FA773","FA774","FA775","FA776","FA777","FA778","FA779","FA780", - "FA781","FA782","FA783","FA784","FA785","FA786","FA787","FA788","FA789","FA790","FA791","FA792","FA793","FA794","FA795","FA796","FA797","FA798","FA799","FA800", - "FA801","FA802","FA803","FA804","FA805","FA806","FA807","FA808","FA809","FA810","FA811","FA812","FA813","FA814","FA815","FA816","FA817","FA818","FA819","FA820", - "FA821","FA822","FA823","FA824","FA825","FA826","FA827","FA828","FA829","FA830","FA831","FA832","FA833","FA834","FA835","FA836","FA837","FA838","FA839","FA840", - "FA841","FA842","FA843","FA844","FA845","FA846","FA847","FA848","FA849","FA850","FA851","FA852","FA853","FA854","FA855","FA856","FA857","FA858","FA859","FA860", - "FA861","FA862","FA863","FA864","FA865","FA866","FA867","FA868","FA869","FA870","FA871","FA872","FA873","FA874","FA875","FA876","FA877","FA878","FA879","FA880", - "FA881","FA882","FA883","FA884","FA885","FA886","FA887","FA888","FA889","FA890","FA891","FA892","FA893","FA894","FA895","FA896","FA897","FA898","FA899","FA900", - "FA901","FA902","FA903","FA904","FA905","FA906","FA907","FA908","FA909","FA910","FA911","FA912","FA913","FA914","FA915","FA916","FA917","FA918","FA919","FA920", - "FA921","FA922","FA923","FA924","FA925","FA926","FA927","FA928","FA929","FA930","FA931","FA932","FA933","FA934","FA935","FA936","FA937","FA938","FA939","FA940", - "FA941","FA942","FA943","FA944","FA945","FA946","FA947","FA948","FA949","FA950","FA951","FA952","FA953","FA954","FA955","FA956","FA957","FA958","FA959","FA960", - "FA961","FA962","FA963","FA964","FA965","FA966","FA967","FA968","FA969","FA970","FA971","FA972","FA973","FA974","FA975","FA976","FA977","FA978","FA979","FA980", - "FA981","FA982","FA983","FA984","FA985","FA986","FA987","FA988","FA989","FA990","FA991","FA992","FA993","FA994","FA995","FA996","FA997","FA998","FA999" -] diff --git a/artillery/helper/odscodes.mjs b/artillery/helper/odscodes.mjs new file mode 100644 index 00000000..af4f9015 --- /dev/null +++ b/artillery/helper/odscodes.mjs @@ -0,0 +1,8 @@ +// These ODS codes should match the values in AWS parameter store. They're assumed to be enabled in the whitelist +export const allowedOdsCodes = [ + "FA565" +] + +export const blockedOdsCodes = [ + "B3J1Z" +] \ No newline at end of file diff --git a/artillery/notify_entrypoint.mjs b/artillery/notify_entrypoint.mjs index e267da2e..d6f517dc 100644 --- a/artillery/notify_entrypoint.mjs +++ b/artillery/notify_entrypoint.mjs @@ -1,11 +1,11 @@ import {v4 as uuidv4} from "uuid" import pino from "pino" import {getSharedAuthToken, getBody} from "./helper/psu.mjs" -import {allowedOdsCodes} from "./helper/allowed_odscodes.mjs" +import {allowedOdsCodes, blockedOdsCodes} from "./helper/odscodes.mjs" -const logger = pino() +export { getSharedAuthToken } -const NUM_ODS_CODES = 1000 +const logger = pino() const LETTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; const DIGITS = "0123456789"; @@ -26,8 +26,8 @@ function buildFullOdsCodes(targetCount, seedCodes) { return Array.from(codes); } -/** The complete list of 10k ODS codes */ -const fullOdsCodes = buildFullOdsCodes(NUM_ODS_CODES, allowedOdsCodes); +// The complete list of ODS codes +const fullOdsCodes = allowedOdsCodes.concat(blockedOdsCodes) function computeCheckDigit(nhsNumber) { const factors = [10,9,8,7,6,5,4,3,2] @@ -71,11 +71,13 @@ export function initUser(context, events, done) { if (prescriptionCount < 1) prescriptionCount = 1 // just truncate at 1. context.vars.prescriptionCount = prescriptionCount + logger.info(`Patient ${context.vars.nhsNumber}, ODS ${context.vars.odsCode} has ${context.vars.prescriptionCount} prescriptions`) + done() } -// beforeEach request export function generatePrescData(requestParams, context, ee, next) { + logger.info(`Generating a prescription for patient ${context.vars.nhsNumber}`) const body = getBody( true, /* isValid */ "ready to collect", /* status */ @@ -91,8 +93,8 @@ export function generatePrescData(requestParams, context, ee, next) { let delay = sampleNormal(150, 60) if (delay < 0) delay = 0 context.vars.nextDelay = delay + logger.info(`Patient ${context.vars.nhsNumber} will think for ${context.vars.nextDelay} seconds`) next() } -export { getSharedAuthToken } diff --git a/artillery/notify_load_test.yml b/artillery/notify_load_test.yml index 3d4ddbb2..e1e51f17 100644 --- a/artillery/notify_load_test.yml +++ b/artillery/notify_load_test.yml @@ -12,13 +12,13 @@ config: maxVusers: "{{ $env.maxVusers }}" environments: dev: - target: https://internal-dev.api.service.nhs.uk + target: https://internal-dev.api.service.nhs.uk/ ref: - target: https://ref.api.service.nhs.uk + target: https://ref.api.service.nhs.uk/ int: - target: https://int.api.service.nhs.uk + target: https://int.api.service.nhs.uk/ pr: - target: https://psu-pr-{{ prNumber }}.dev.eps.national.nhs.uk + target: https://psu-pr-{{ prNumber }}.dev.eps.national.nhs.uk/ defaults: headers: Authorization: "Bearer {{ authToken }}" @@ -29,21 +29,19 @@ scenarios: # grab token once per VU before: - function: getSharedAuthToken - + flow: - function: initUser # for each prescription (number of prescriptions is variable) - loop: - - count: "{{ prescriptionCount }}" - steps: - - beforeRequest: generatePrescData - - post: - url: "/prescription-status-update/" - headers: - x-request-id: "{{ x_request_id }}" - x-correlation-id: "{{ x_correlation_id }}" - expect: - - statusCode: 201 - # wait before next prescription - - think: "{{ nextDelay }}" + - post: + url: "/prescription-status-update/" + beforeRequest: "generatePrescData" + headers: + x-request-id: "{{ x_request_id }}" + x-correlation-id: "{{ x_correlation_id }}" + expect: + - statusCode: 201 + - think: "{{ nextDelay }}" + count: "{{ prescriptionCount }}" diff --git a/scripts/test_notify_load_test.sh b/scripts/test_notify_load_test.sh index 80ae66ec..cccd57f9 100755 --- a/scripts/test_notify_load_test.sh +++ b/scripts/test_notify_load_test.sh @@ -55,6 +55,7 @@ EOF echo "Running Artillery test locally..." echo "" +echo "Environment: ${environment}" echo "Max Virtual Users: ${maxVusers}" echo "Phase Duration: ${duration}" echo "Arrival Rate: ${arrivalRate}" From 8565140538eb6646d7e06ba649fa39ca3314133b Mon Sep 17 00:00:00 2001 From: Jim Wild Date: Wed, 18 Jun 2025 15:09:14 +0000 Subject: [PATCH 11/20] Get the requests going. Seems to work now --- artillery/helper/odscodes.mjs | 2 +- artillery/helper/psu.mjs | 1 - artillery/notify_entrypoint.mjs | 10 ++++++---- artillery/notify_load_test.yml | 22 +++++++++------------- scripts/test_notify_load_test.sh | 9 +-------- 5 files changed, 17 insertions(+), 27 deletions(-) diff --git a/artillery/helper/odscodes.mjs b/artillery/helper/odscodes.mjs index af4f9015..42869b24 100644 --- a/artillery/helper/odscodes.mjs +++ b/artillery/helper/odscodes.mjs @@ -4,5 +4,5 @@ export const allowedOdsCodes = [ ] export const blockedOdsCodes = [ - "B3J1Z" + // "B3J1Z" ] \ No newline at end of file diff --git a/artillery/helper/psu.mjs b/artillery/helper/psu.mjs index 160b4978..025fe7bf 100644 --- a/artillery/helper/psu.mjs +++ b/artillery/helper/psu.mjs @@ -132,7 +132,6 @@ export async function getPSUParams(requestParams, vuContext) { const isValid = vuContext.scenario.tags.isValid const body = getBody(isValid) - requestParams.json = body // This sets the body of the request and some variables so headers are unique requestParams.json = body vuContext.vars.x_request_id = uuidv4() diff --git a/artillery/notify_entrypoint.mjs b/artillery/notify_entrypoint.mjs index d6f517dc..51d52370 100644 --- a/artillery/notify_entrypoint.mjs +++ b/artillery/notify_entrypoint.mjs @@ -80,18 +80,20 @@ export function generatePrescData(requestParams, context, ee, next) { logger.info(`Generating a prescription for patient ${context.vars.nhsNumber}`) const body = getBody( true, /* isValid */ - "ready to collect", /* status */ + "completed", /* status */ context.vars.odsCode, /* odsCode */ - context.vars.nhsNumber /* nhsNumber */ + context.vars.nhsNumber, /* nhsNumber */ + "ready to collect" /* Item status */ ) + // The body is fine - it works when I put it in postman requestParams.json = body context.vars.x_request_id = uuidv4() context.vars.x_correlation_id = uuidv4() // Wait this long between requests - let delay = sampleNormal(150, 60) - if (delay < 0) delay = 0 + let delay = sampleNormal(60, 60) + while (delay < 0) delay = sampleNormal(60, 60) context.vars.nextDelay = delay logger.info(`Patient ${context.vars.nhsNumber} will think for ${context.vars.nextDelay} seconds`) diff --git a/artillery/notify_load_test.yml b/artillery/notify_load_test.yml index e1e51f17..aa50a854 100644 --- a/artillery/notify_load_test.yml +++ b/artillery/notify_load_test.yml @@ -1,11 +1,9 @@ config: processor: "./notify_entrypoint.mjs" + plugins: + expect: {} + apdex: {} phases: - - name: ramp up phase - duration: "{{ $env.rampUpDuration }}" - arrivalRate: 1 - rampTo: "{{ $env.arrivalRate }}" - maxVusers: "{{ $env.maxVusers }}" - name: run phase duration: "{{ $env.duration }}" arrivalRate: "{{ $env.arrivalRate }}" @@ -19,17 +17,14 @@ config: target: https://int.api.service.nhs.uk/ pr: target: https://psu-pr-{{ prNumber }}.dev.eps.national.nhs.uk/ - defaults: - headers: - Authorization: "Bearer {{ authToken }}" + +before: + flow: + - function: getSharedAuthToken scenarios: - name: dynamic PSU calls for each patient - # grab token once per VU - before: - - function: getSharedAuthToken - flow: - function: initUser @@ -39,9 +34,10 @@ scenarios: url: "/prescription-status-update/" beforeRequest: "generatePrescData" headers: + Authorization: "Bearer {{ authToken }}" x-request-id: "{{ x_request_id }}" x-correlation-id: "{{ x_correlation_id }}" expect: - statusCode: 201 - - think: "{{ nextDelay }}" + - think: "{{ nextDelay }}seconds" count: "{{ prescriptionCount }}" diff --git a/scripts/test_notify_load_test.sh b/scripts/test_notify_load_test.sh index cccd57f9..de2ef682 100755 --- a/scripts/test_notify_load_test.sh +++ b/scripts/test_notify_load_test.sh @@ -21,11 +21,6 @@ if [ -z "${arrivalRate}" ]; then exit 1 fi -if [ -z "${rampUpDuration}" ]; then - echo "rampUpDuration is unset or set to the empty string" - exit 1 -fi - if [ -z "${psu_api_key}" ]; then echo "psu_api_key is unset or set to the empty string" exit 1 @@ -47,7 +42,6 @@ cat < runtimeenv.env maxVusers=${maxVusers} duration=${duration} arrivalRate=${arrivalRate} -rampUpDuration=${rampUpDuration} psu_api_key="${psu_api_key}" psu_private_key="${psu_private_key}" psu_kid="${psu_kid}" @@ -57,9 +51,8 @@ echo "Running Artillery test locally..." echo "" echo "Environment: ${environment}" echo "Max Virtual Users: ${maxVusers}" -echo "Phase Duration: ${duration}" +echo "Run Phase Duration: ${duration}" echo "Arrival Rate: ${arrivalRate}" -echo "Ramp Up Duration: ${rampUpDuration}" echo "" set -e From 3dcbafa57b9e6de5ea539b9bd2e455bcd086da46 Mon Sep 17 00:00:00 2001 From: Jim Wild Date: Wed, 18 Jun 2025 15:21:33 +0000 Subject: [PATCH 12/20] Dont wait after making the last request --- .github/workflows/run_notify_load.yml | 7 +------ artillery/notify_entrypoint.mjs | 15 ++++++++++++--- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/.github/workflows/run_notify_load.yml b/.github/workflows/run_notify_load.yml index d6ea151e..8ffb5440 100644 --- a/.github/workflows/run_notify_load.yml +++ b/.github/workflows/run_notify_load.yml @@ -15,14 +15,10 @@ on: description: 'The duration of the main test' required: true default: '900' - rampUpDuration: - description: 'The duration to ramp up to the arrival rate' - required: true - default: '900' maxVusers: description: 'Maximum number of vusers to create' required: true - default: '10000' + default: '1000' jobs: run_artillery: @@ -38,7 +34,6 @@ jobs: echo "## environment : ${{ github.event.inputs.environment }}" >> "$GITHUB_STEP_SUMMARY" echo "## arrivalRate : ${{ github.event.inputs.arrivalRate }}" >> "$GITHUB_STEP_SUMMARY" echo "## duration : ${{ github.event.inputs.duration }}" >> "$GITHUB_STEP_SUMMARY" - echo "## rampUpDuration : ${{ github.event.inputs.rampUpDuration }}" >> "$GITHUB_STEP_SUMMARY" echo "## maxVusers : ${{ github.event.inputs.maxVusers }}" >> "$GITHUB_STEP_SUMMARY" - name: Checkout repo diff --git a/artillery/notify_entrypoint.mjs b/artillery/notify_entrypoint.mjs index 51d52370..faf2f154 100644 --- a/artillery/notify_entrypoint.mjs +++ b/artillery/notify_entrypoint.mjs @@ -70,6 +70,7 @@ export function initUser(context, events, done) { let prescriptionCount = Math.round(sampleNormal(3,1)) if (prescriptionCount < 1) prescriptionCount = 1 // just truncate at 1. context.vars.prescriptionCount = prescriptionCount + context.vars.loopcount = 0 logger.info(`Patient ${context.vars.nhsNumber}, ODS ${context.vars.odsCode} has ${context.vars.prescriptionCount} prescriptions`) @@ -90,12 +91,20 @@ export function generatePrescData(requestParams, context, ee, next) { requestParams.json = body context.vars.x_request_id = uuidv4() context.vars.x_correlation_id = uuidv4() + + context.vars.loopcount += 1 // Wait this long between requests - let delay = sampleNormal(60, 60) - while (delay < 0) delay = sampleNormal(60, 60) + let meanDelay = 10 // seconds + let stdDevDelay = 10 // seconds + let delay = 0 + if (context.vars.loopcount < context.vars.prescriptionCount) { + delay = sampleNormal(meanDelay, stdDevDelay) + while (delay < 0) delay = sampleNormal(meanDelay, stdDevDelay) + } + context.vars.nextDelay = delay - logger.info(`Patient ${context.vars.nhsNumber} will think for ${context.vars.nextDelay} seconds`) + logger.info(`Patient ${context.vars.nhsNumber} (on loop ${context.vars.loopcount}) will think for ${context.vars.nextDelay} seconds`) next() } From e56a5edc0943bb9d20c1fac77525dea5cd13f0fc Mon Sep 17 00:00:00 2001 From: Jim Wild Date: Wed, 18 Jun 2025 15:29:04 +0000 Subject: [PATCH 13/20] fix scripts --- scripts/run_notify_load_test.sh | 6 ------ scripts/test_cpsu_load_test.sh | 2 +- scripts/test_notify_load_test.sh | 2 +- scripts/test_psu_load_test.sh | 2 +- 4 files changed, 3 insertions(+), 9 deletions(-) diff --git a/scripts/run_notify_load_test.sh b/scripts/run_notify_load_test.sh index 4cde2a90..69e9ba08 100755 --- a/scripts/run_notify_load_test.sh +++ b/scripts/run_notify_load_test.sh @@ -20,11 +20,6 @@ if [ -z "${arrivalRate}" ]; then exit 1 fi -if [ -z "${rampUpDuration}" ]; then - echo "rampUpDuration is unset or set to the empty string" - exit 1 -fi - if ! [[ "${environment}" =~ ^(dev|ref)$ ]] then echo "environment must be dev or ref" @@ -43,7 +38,6 @@ cat < runtimeenv.env maxVusers=$maxVusers duration=$duration arrivalRate=$arrivalRate -rampUpDuration=$rampUpDuration EOF echo ${launch_config} diff --git a/scripts/test_cpsu_load_test.sh b/scripts/test_cpsu_load_test.sh index e85d0972..525a41bc 100755 --- a/scripts/test_cpsu_load_test.sh +++ b/scripts/test_cpsu_load_test.sh @@ -59,4 +59,4 @@ npx artillery run \ /workspaces/eps-load-test/artillery/cpsu_load_test.yml # Generate a report from the test results -npx /workspaces/eps-load-test/artillery report cpsu_load_test.json +npx artillery report cpsu_load_test.json diff --git a/scripts/test_notify_load_test.sh b/scripts/test_notify_load_test.sh index de2ef682..defd1baf 100755 --- a/scripts/test_notify_load_test.sh +++ b/scripts/test_notify_load_test.sh @@ -65,4 +65,4 @@ npx artillery run \ /workspaces/eps-load-test/artillery/notify_load_test.yml # Generate a report from the test results -npx /workspaces/eps-load-test/artillery report notify_load_test.json +npx artillery report notify_load_test.json diff --git a/scripts/test_psu_load_test.sh b/scripts/test_psu_load_test.sh index 50a5f1c6..dc167869 100755 --- a/scripts/test_psu_load_test.sh +++ b/scripts/test_psu_load_test.sh @@ -71,4 +71,4 @@ npx artillery run \ /workspaces/eps-load-test/artillery/psu_load_test.yml # Generate a report from the test results -npx /workspaces/eps-load-test/artillery report psu_load_test.json +npx artillery report psu_load_test.json From 5b74681df884e56f37e527eb2156a24349c77646 Mon Sep 17 00:00:00 2001 From: Jim Wild Date: Thu, 19 Jun 2025 09:40:05 +0000 Subject: [PATCH 14/20] Reduce logging output --- artillery/notify_entrypoint.mjs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/artillery/notify_entrypoint.mjs b/artillery/notify_entrypoint.mjs index faf2f154..ffd671d6 100644 --- a/artillery/notify_entrypoint.mjs +++ b/artillery/notify_entrypoint.mjs @@ -78,7 +78,7 @@ export function initUser(context, events, done) { } export function generatePrescData(requestParams, context, ee, next) { - logger.info(`Generating a prescription for patient ${context.vars.nhsNumber}`) + logger.debug(`Generating a prescription for patient ${context.vars.nhsNumber}`) const body = getBody( true, /* isValid */ "completed", /* status */ @@ -104,7 +104,7 @@ export function generatePrescData(requestParams, context, ee, next) { } context.vars.nextDelay = delay - logger.info(`Patient ${context.vars.nhsNumber} (on loop ${context.vars.loopcount}) will think for ${context.vars.nextDelay} seconds`) + logger.debug(`Patient ${context.vars.nhsNumber} (on prescription update ${context.vars.loopcount}/${context.vars.prescriptionCount}) will think for ${context.vars.nextDelay} seconds`) next() } From 972e7a17301f21fc021b689e7f412611d06b1b96 Mon Sep 17 00:00:00 2001 From: Jim Wild Date: Thu, 19 Jun 2025 14:49:06 +0000 Subject: [PATCH 15/20] dotenv flag is deprecated since v2.0.21 --- runtimeenv.env.dev | 57 ++++++++++++++++++++++++++++++++ scripts/run_cpsu_load_test.sh | 2 +- scripts/run_notify_load_test.sh | 18 ++++++---- scripts/run_psu_load_test.sh | 2 +- scripts/test_cpsu_load_test.sh | 2 +- scripts/test_notify_load_test.sh | 12 ++++--- scripts/test_psu_load_test.sh | 2 +- 7 files changed, 81 insertions(+), 14 deletions(-) create mode 100644 runtimeenv.env.dev diff --git a/runtimeenv.env.dev b/runtimeenv.env.dev new file mode 100644 index 00000000..e98d8e30 --- /dev/null +++ b/runtimeenv.env.dev @@ -0,0 +1,57 @@ +maxVusers=200 +duration=900 +arrivalRate=200 +psu_api_key="wFW9WPe2ZVkEsgu9G0R5C6As0MDxb74p" +psu_private_key="-----BEGIN PRIVATE KEY----- +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 +TgDTGVbNkvTyaA2TP6ChuCGhAIMlSA== +-----END PRIVATE KEY-----" +psu_kid="PSU-DEV-JIM-WILD" diff --git a/scripts/run_cpsu_load_test.sh b/scripts/run_cpsu_load_test.sh index 321c9cd3..448950e3 100755 --- a/scripts/run_cpsu_load_test.sh +++ b/scripts/run_cpsu_load_test.sh @@ -57,7 +57,7 @@ npx artillery run-fargate \ --security-group-ids "${security_group}" \ --subnet-ids "${vpc_subnets}" \ --task-role-name "${artillery_worker_role_name}" \ - --dotenv runtimeenv.env \ + --env-file runtimeenv.env \ --output cpsu_load_test.json \ artillery/cpsu_load_test.yml diff --git a/scripts/run_notify_load_test.sh b/scripts/run_notify_load_test.sh index 69e9ba08..3c7f0571 100755 --- a/scripts/run_notify_load_test.sh +++ b/scripts/run_notify_load_test.sh @@ -1,5 +1,16 @@ #!/usr/bin/env bash + +if [ -z "${artillery_key}" ]; then + echo "artillery_key is unset or set to the empty string" + exit 1 +fi + +if [ -z "${environment}" ]; then + echo "environment is unset or set to the empty string" + exit 1 +fi + if [ -z "${maxVusers}" ]; then echo "maxVusers is unset or set to the empty string" exit 1 @@ -10,11 +21,6 @@ if [ -z "${duration}" ]; then exit 1 fi -if [ -z "${environment}" ]; then - echo "environment is unset or set to the empty string" - exit 1 -fi - if [ -z "${arrivalRate}" ]; then echo "arrivalRate is unset or set to the empty string" exit 1 @@ -53,7 +59,7 @@ npx artillery run-fargate \ --security-group-ids "${security_group}" \ --subnet-ids "${vpc_subnets}" \ --task-role-name "${artillery_worker_role_name}" \ - --dotenv runtimeenv.env \ + --env-file runtimeenv.env \ --output notify_load_test.json \ artillery/notify_load_test.yml diff --git a/scripts/run_psu_load_test.sh b/scripts/run_psu_load_test.sh index 4e1e563a..2bc409ad 100755 --- a/scripts/run_psu_load_test.sh +++ b/scripts/run_psu_load_test.sh @@ -59,7 +59,7 @@ npx artillery run-fargate \ --security-group-ids "${security_group}" \ --subnet-ids "${vpc_subnets}" \ --task-role-name "${artillery_worker_role_name}" \ - --dotenv runtimeenv.env \ + --env-file runtimeenv.env \ --output psu_load_test.json \ artillery/psu_load_test.yml diff --git a/scripts/test_cpsu_load_test.sh b/scripts/test_cpsu_load_test.sh index 525a41bc..91fb7eb4 100755 --- a/scripts/test_cpsu_load_test.sh +++ b/scripts/test_cpsu_load_test.sh @@ -54,7 +54,7 @@ set -e # Run the Artillery test locally npx artillery run \ -e "${environment}" \ - --dotenv /workspaces/eps-load-test/runtimeenv.env \ + --env-file /workspaces/eps-load-test/runtimeenv.env \ --output /workspaces/eps-load-test/cpsu_load_test.json \ /workspaces/eps-load-test/artillery/cpsu_load_test.yml diff --git a/scripts/test_notify_load_test.sh b/scripts/test_notify_load_test.sh index defd1baf..8cae9e4c 100755 --- a/scripts/test_notify_load_test.sh +++ b/scripts/test_notify_load_test.sh @@ -1,6 +1,11 @@ #!/usr/bin/env bash +if [ -z "${artillery_key}" ]; then + echo "artillery_key is unset or set to the empty string" + exit 1 +fi + if [ -z "${environment}" ]; then echo "environment is unset or set to the empty string" exit 1 @@ -45,6 +50,7 @@ arrivalRate=${arrivalRate} psu_api_key="${psu_api_key}" psu_private_key="${psu_private_key}" psu_kid="${psu_kid}" +artillery_key="${artillery_key}" EOF echo "Running Artillery test locally..." @@ -60,9 +66,7 @@ set -e # Run the Artillery test locally npx artillery run \ -e "${environment}" \ - --dotenv /workspaces/eps-load-test/runtimeenv.env \ + --env-file /workspaces/eps-load-test/runtimeenv.env \ --output /workspaces/eps-load-test/notify_load_test.json \ + --record --key ${artillery_key} \ /workspaces/eps-load-test/artillery/notify_load_test.yml - -# Generate a report from the test results -npx artillery report notify_load_test.json diff --git a/scripts/test_psu_load_test.sh b/scripts/test_psu_load_test.sh index dc167869..dd6902ca 100755 --- a/scripts/test_psu_load_test.sh +++ b/scripts/test_psu_load_test.sh @@ -66,7 +66,7 @@ set -e # Run the Artillery test locally npx artillery run \ -e "${environment}" \ - --dotenv /workspaces/eps-load-test/runtimeenv.env \ + --env-file /workspaces/eps-load-test/runtimeenv.env \ --output /workspaces/eps-load-test/psu_load_test.json \ /workspaces/eps-load-test/artillery/psu_load_test.yml From 4f1cbfb581af3dc5063db612b0461043e68a0913 Mon Sep 17 00:00:00 2001 From: Jim Wild Date: Thu, 19 Jun 2025 14:53:01 +0000 Subject: [PATCH 16/20] remove mistaken change --- scripts/run_notify_load_test.sh | 6 ------ 1 file changed, 6 deletions(-) diff --git a/scripts/run_notify_load_test.sh b/scripts/run_notify_load_test.sh index 3c7f0571..ca80c212 100755 --- a/scripts/run_notify_load_test.sh +++ b/scripts/run_notify_load_test.sh @@ -1,11 +1,5 @@ #!/usr/bin/env bash - -if [ -z "${artillery_key}" ]; then - echo "artillery_key is unset or set to the empty string" - exit 1 -fi - if [ -z "${environment}" ]; then echo "environment is unset or set to the empty string" exit 1 From aaabcdc5aea2310bd45ce9d575c8b61700f0847e Mon Sep 17 00:00:00 2001 From: Jim Wild Date: Thu, 19 Jun 2025 15:18:04 +0000 Subject: [PATCH 17/20] Possible path error? --- scripts/run_notify_load_test.sh | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/scripts/run_notify_load_test.sh b/scripts/run_notify_load_test.sh index ca80c212..94b959da 100755 --- a/scripts/run_notify_load_test.sh +++ b/scripts/run_notify_load_test.sh @@ -45,9 +45,7 @@ echo ${launch_config} # shellcheck disable=SC2090,SC2086 npx artillery run-fargate \ --environment "${environment}" \ - --secret psu_api_key \ - --secret psu_private_key \ - --secret psu_kid \ + --secret psu_api_key psu_private_key psu_kid \ --region eu-west-2 \ --cluster artilleryio-cluster \ --security-group-ids "${security_group}" \ @@ -55,6 +53,6 @@ npx artillery run-fargate \ --task-role-name "${artillery_worker_role_name}" \ --env-file runtimeenv.env \ --output notify_load_test.json \ - artillery/notify_load_test.yml + notify_load_test.yml npx artillery report notify_load_test.json From 768a27f017699bf72fe14051c818ac6b57168268 Mon Sep 17 00:00:00 2001 From: Jim Wild Date: Thu, 19 Jun 2025 15:19:40 +0000 Subject: [PATCH 18/20] Revert previous commit --- scripts/run_notify_load_test.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/run_notify_load_test.sh b/scripts/run_notify_load_test.sh index 94b959da..a06c0e95 100755 --- a/scripts/run_notify_load_test.sh +++ b/scripts/run_notify_load_test.sh @@ -53,6 +53,6 @@ npx artillery run-fargate \ --task-role-name "${artillery_worker_role_name}" \ --env-file runtimeenv.env \ --output notify_load_test.json \ - notify_load_test.yml + artillery/notify_load_test.yml npx artillery report notify_load_test.json From e928f93f12d9b1edaae9a3bc6ce1ad6ce9148456 Mon Sep 17 00:00:00 2001 From: Jim Wild Date: Tue, 24 Jun 2025 12:59:39 +0000 Subject: [PATCH 19/20] Always check the auth token on each loop of the load test --- artillery/notify_load_test.yml | 1 + runtimeenv.env.dev | 57 ---------------------------------- 2 files changed, 1 insertion(+), 57 deletions(-) delete mode 100644 runtimeenv.env.dev diff --git a/artillery/notify_load_test.yml b/artillery/notify_load_test.yml index aa50a854..4173eb1e 100644 --- a/artillery/notify_load_test.yml +++ b/artillery/notify_load_test.yml @@ -30,6 +30,7 @@ scenarios: # for each prescription (number of prescriptions is variable) - loop: + - function: getSharedAuthToken - post: url: "/prescription-status-update/" beforeRequest: "generatePrescData" diff --git a/runtimeenv.env.dev b/runtimeenv.env.dev deleted file mode 100644 index e98d8e30..00000000 --- a/runtimeenv.env.dev +++ /dev/null @@ -1,57 +0,0 @@ -maxVusers=200 -duration=900 -arrivalRate=200 -psu_api_key="wFW9WPe2ZVkEsgu9G0R5C6As0MDxb74p" -psu_private_key="-----BEGIN PRIVATE KEY----- -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 -TgDTGVbNkvTyaA2TP6ChuCGhAIMlSA== ------END PRIVATE KEY-----" -psu_kid="PSU-DEV-JIM-WILD" From d252aa6ee4f20b8a2fcd56844bd0bef6128ed6df Mon Sep 17 00:00:00 2001 From: Jim Wild Date: Thu, 19 Jun 2025 14:49:06 +0000 Subject: [PATCH 20/20] dotenv flag is deprecated since v2.0.21 --- scripts/run_notify_load_test.sh | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/scripts/run_notify_load_test.sh b/scripts/run_notify_load_test.sh index a06c0e95..2657438d 100755 --- a/scripts/run_notify_load_test.sh +++ b/scripts/run_notify_load_test.sh @@ -1,5 +1,11 @@ #!/usr/bin/env bash + +if [ -z "${artillery_key}" ]; then + echo "artillery_key is unset or set to the empty string" + exit 1 +fi + if [ -z "${environment}" ]; then echo "environment is unset or set to the empty string" exit 1