Skip to content

Commit

Permalink
PHAIN-123: Add PHA import notifications updates performance tests (#6)
Browse files Browse the repository at this point in the history
  • Loading branch information
tmc-ee authored Feb 11, 2025
1 parent 6b6632b commit fe39591
Show file tree
Hide file tree
Showing 14 changed files with 241 additions and 40 deletions.
4 changes: 1 addition & 3 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,9 @@ USER k6

WORKDIR /opt/perftest

COPY scenarios/ ./scenarios/
COPY src/ ./src/
COPY entrypoint.sh .
COPY k6-reporter-2.4.0.js .

ENV S3_ENDPOINT=https://s3.eu-west-2.amazonaws.com
ENV TEST_SCENARIO=test

ENTRYPOINT [ "./entrypoint.sh" ]
31 changes: 26 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,29 @@
# pha-import-notifications-perf

Performance tests for PHA import notifications.
Performance tests for PHA import notifications service.

## Tests
## Pre-requisites

### Environment variables

Environment variables must be set as follows:

- `K6_TARGET_URL` is the URL of the service.
- `K6_WORKLOAD` is the target workload. Available values are smoke (default), load, stress, spike.
- `K6_THRESHOLD` is the target threshold. Available values are low (default), medium or high.
- `TEST_CLIENT_LOGIN_URL` is the authentication URL.
- `TEST_CLIENT_APP_ID` is the allocated client id.
- `TEST_CLIENT_SECRET` is the allocated client secret.

## Usage

### Local

Run as follows:

```bash
k6 run src/tests/updates.js --summary-export=summary.json
```

### Build

Expand All @@ -12,8 +33,6 @@ Build as follows:
docker build . -t my-performance-tests
```

### Run

Run as follows:

```bash
Expand All @@ -24,7 +43,9 @@ docker run \
-e AWS_SECRET_ACCESS_KEY='test' \
-e AWS_SECRET_KEY='test' \
-e AWS_REGION='eu-west-2' \
-e BASE_URL='http://host.docker.internal:8080' \
-e TEST_CLIENT_LOGIN_URL='<value>' \
-e TEST_CLIENT_APP_ID='<value>' \
-e TEST_CLIENT_SECRET='<value>' \
my-performance-tests
```

Expand Down
8 changes: 6 additions & 2 deletions entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,15 @@
echo "run_id: $RUN_ID in $ENVIRONMENT"

K6_HOME=/opt/perftest
K6_SCENARIOS=${K6_HOME}/scenarios
K6_REPORT=index.html
K6_SUMMARY=summary.json

k6 run -e BASE_URL=https://pha-import-notifications.perf-test.cdp-int.defra.cloud ${K6_SCENARIOS}/${TEST_SCENARIO}.js --summary-export=${K6_SUMMARY}
k6 run \
-e K6_TARGET_URL=https://pha-import-notifications.perf-test.cdp-int.defra.cloud \
-e K6_WORKLOAD=smoke \
-e K6_THRESHOLD=low \
${K6_HOME}/src/tests/updates.js \
--summary-export=${K6_SUMMARY}
test_exit_code=$?

if [ -n "$RESULTS_OUTPUT_S3_PATH" ]; then
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 1 addition & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
{
"name": "pha-import-notifications-perf",
"version": "0.2.0",
"version": "0.6.0",
"description": "Performance tests for PHA import notifications.",
"scripts": {
"test": "k6 run -e BASE_URL=http://localhost:8080 scenarios/test.js --summary-export=summary.json"
},
"author": "Defra DDTS",
"license": "OGL-UK-3.0",
"devDependencies": {
Expand Down
24 changes: 0 additions & 24 deletions scenarios/test.js

This file was deleted.

16 changes: 16 additions & 0 deletions src/config/thresholds.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
const config = {
low: {
http_req_duration: ['p(90)<2500'],
http_req_failed: ['rate<0.01'],
},
medium: {
http_req_duration: ['p(90)<400', 'p(95)<800', 'p(99.9)<2000'],
http_req_failed: ['rate<0.01'],
},
high: {
http_req_duration: ['p(90)<250', 'p(95)<500', 'p(99.9)<1500'],
http_req_failed: ['rate<0.01'],
},
};

export const threshold = config[__ENV.K6_THRESHOLD] || config['low'];
34 changes: 34 additions & 0 deletions src/config/workloads.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
const config = {
// 12 iterations of the user journey over 15 mins (average volume of traffic)
load: {
executor: 'constant-arrival-rate',
duration: '15m',
preAllocatedVUs: 1,
rate: 12,
timeUnit: '15m',
},
// 1200 iterations of the user journey over 15 mins (100x average volume of traffic)
stress: {
executor: 'constant-arrival-rate',
duration: '15m',
preAllocatedVUs: 12,
rate: 1200,
timeUnit: '15m',
},
// Ramp up to 300 virtual users in 1 min with each virtual user completing as many iterations of the user journey as possible
spike: {
executor: 'ramping-vus',
stages: [
{duration: '1m', target: 300},
{duration: '30s', target: 0},
],
},
// 1 iteration of the user journey for validation purposes
smoke: {
executor: 'per-vu-iterations',
vus: 1,
iterations: 1,
},
};

export const workload = config[__ENV.K6_WORKLOAD] || config['smoke'];
24 changes: 24 additions & 0 deletions src/data/import-notifications.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"importNotifications": [
{
"scenario": "Example CHED-A.",
"referenceNumber": "CHEDA.GB.2024.4792831"
},
{
"scenario": "Example CHED-D with linked movements.",
"referenceNumber": "CHEDD.GB.2024.5019877"
},
{
"scenario": "Example CHED-P.",
"referenceNumber": "CHEDP.GB.2024.4144842"
},
{
"scenario": "Example CHED-PP.",
"referenceNumber": "CHEDPP.GB.2024.3726460"
},
{
"scenario": "Example CHED-A with linked movements.",
"referenceNumber": "CHEDA.GB.2024.5129502"
}
]
}
File renamed without changes.
1 change: 1 addition & 0 deletions src/libs/k6-url-1.0.0.js

Large diffs are not rendered by default.

34 changes: 34 additions & 0 deletions src/requests/import-notifications.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import http from 'k6/http';
import {URLSearchParams} from '../libs/k6-url-1.0.0.js';

const targetUrl = `${__ENV.K6_TARGET_URL}/import-notifications`;

export function getUpdates(accessToken, bcp, from, to) {
const searchParams = new URLSearchParams([
['bcp', bcp],
['from', from],
['to', to],
]);

const url = `${targetUrl}?${searchParams.toString()}`;

const params = {
headers: {
Authorization: `Bearer ${accessToken}`,
},
};

return http.get(url, params);
}

export function getImportNotification(accessToken, referenceNumber) {
const url = `${targetUrl}/${referenceNumber}`;

const params = {
headers: {
Authorization: `Bearer ${accessToken}`,
},
};

return http.get(url, params);
}
30 changes: 30 additions & 0 deletions src/requests/oauth.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import {check} from 'k6';
import encoding from 'k6/encoding';
import http from 'k6/http';

export function getAccessToken(tokenUrl, clientId, clientSecret) {
const url = `${tokenUrl}/oauth2/token`;

const body = {
grant_type: 'client_credentials',
client_id: clientId,
client_secret: clientSecret,
};

const encodedCredentials = encoding.b64encode(`${clientId}:${clientSecret}`);

const params = {
headers: {
Authorization: `Basic ${encodedCredentials}`,
},
};

const response = http.post(url, body, params);

check(response, {
'is status 200': (r) => r.status === 200,
'has access token': (r) => r.json().hasOwnProperty('access_token'),
});

return response.json().access_token;
}
66 changes: 66 additions & 0 deletions src/tests/updates.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import {check, group} from 'k6';
import {SharedArray} from 'k6/data';
import {threshold} from '../config/thresholds.js';
import {workload} from '../config/workloads.js';
import {htmlReport} from '../libs/k6-reporter-2.3.0.js';
import * as service from '../requests/import-notifications.js';
import * as oauth from '../requests/oauth.js';

export const options = {
scenarios: {
workload,
},
thresholds: threshold,
};

const testData = new SharedArray('import notifications', function () {
return JSON.parse(
open('../data/import-notifications.json')
).importNotifications;
});

export function setup() {
const tokenUrl = __ENV.TEST_CLIENT_LOGIN_URL;
const clientId = __ENV.TEST_CLIENT_APP_ID;
const clientSecret = __ENV.TEST_CLIENT_SECRET;

return oauth.getAccessToken(tokenUrl, clientId, clientSecret);
}

export default function (accessToken) {
group('PHA import notifications updates', function () {
const bcp = 'GBTEEP1';
const from = '2025-01-07T10:21:28.3970000Z';
const to = '2025-01-07T10:21:38.3970000Z';

const response = service.getUpdates(accessToken, bcp, from, to);

check(response, {
'is status 200': (r) => r.status === 200,
'has import notifications': (r) =>
r.json().hasOwnProperty('importNotifications'),
});

// TODO: Remove loop when test data is updated, results in 15 requests, the target (average) is 13.175859517
for (let i = 0; i < 3; i++) {
for (const importNotification of testData) {
const response = service.getImportNotification(
accessToken,
importNotification.referenceNumber
);

check(response, {
'is status 200': (r) => r.status === 200,
'verify reference number': (r) =>
r.json().referenceNumber === importNotification.referenceNumber,
});
}
}
});
}

export function handleSummary(data) {
return {
'index.html': htmlReport(data),
};
}

0 comments on commit fe39591

Please sign in to comment.