From fe395915e62c501d64334b7e5330ab1a84244b3a Mon Sep 17 00:00:00 2001 From: Tom McArdle <79710668+tmc-ee@users.noreply.github.com> Date: Tue, 11 Feb 2025 17:59:33 +0000 Subject: [PATCH] PHAIN-123: Add PHA import notifications updates performance tests (#6) --- Dockerfile | 4 +- README.md | 31 +++++++-- entrypoint.sh | 8 ++- package-lock.json | 4 +- package.json | 5 +- scenarios/test.js | 24 ------- src/config/thresholds.js | 16 +++++ src/config/workloads.js | 34 ++++++++++ src/data/import-notifications.json | 24 +++++++ .../libs/k6-reporter-2.3.0.js | 0 src/libs/k6-url-1.0.0.js | 1 + src/requests/import-notifications.js | 34 ++++++++++ src/requests/oauth.js | 30 +++++++++ src/tests/updates.js | 66 +++++++++++++++++++ 14 files changed, 241 insertions(+), 40 deletions(-) delete mode 100644 scenarios/test.js create mode 100644 src/config/thresholds.js create mode 100644 src/config/workloads.js create mode 100644 src/data/import-notifications.json rename k6-reporter-2.4.0.js => src/libs/k6-reporter-2.3.0.js (100%) create mode 100644 src/libs/k6-url-1.0.0.js create mode 100644 src/requests/import-notifications.js create mode 100644 src/requests/oauth.js create mode 100644 src/tests/updates.js diff --git a/Dockerfile b/Dockerfile index 70f1d13..c25fd08 100644 --- a/Dockerfile +++ b/Dockerfile @@ -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" ] diff --git a/README.md b/README.md index 4067a66..c84a25e 100644 --- a/README.md +++ b/README.md @@ -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 @@ -12,8 +33,6 @@ Build as follows: docker build . -t my-performance-tests ``` -### Run - Run as follows: ```bash @@ -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='' \ + -e TEST_CLIENT_APP_ID='' \ + -e TEST_CLIENT_SECRET='' \ my-performance-tests ``` diff --git a/entrypoint.sh b/entrypoint.sh index 2587700..28f054e 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -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 diff --git a/package-lock.json b/package-lock.json index 3f29847..02d9ba2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "pha-import-notifications-perf", - "version": "0.2.0", + "version": "0.6.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "pha-import-notifications-perf", - "version": "0.2.0", + "version": "0.6.0", "license": "OGL-UK-3.0", "devDependencies": { "@types/k6": "^0.54.2" diff --git a/package.json b/package.json index 341dd36..5f91e77 100644 --- a/package.json +++ b/package.json @@ -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": { diff --git a/scenarios/test.js b/scenarios/test.js deleted file mode 100644 index 237fc0d..0000000 --- a/scenarios/test.js +++ /dev/null @@ -1,24 +0,0 @@ -import http from 'k6/http'; -import {check} from 'k6'; -import {htmlReport} from '../k6-reporter-2.4.0.js'; - -export const options = { - thresholds: { - http_req_duration: ['p(90)<2500'], - }, -}; - -export default function () { - const res = http.get(`${__ENV.BASE_URL}/hello/world`); - - check(res, { - 'is status 200': (r) => r.status === 200, - 'verify response text': (r) => r.json(['response']) === 'Hello World', - }); -} - -export function handleSummary(data) { - return { - 'index.html': htmlReport(data), - }; -} diff --git a/src/config/thresholds.js b/src/config/thresholds.js new file mode 100644 index 0000000..8102959 --- /dev/null +++ b/src/config/thresholds.js @@ -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']; diff --git a/src/config/workloads.js b/src/config/workloads.js new file mode 100644 index 0000000..a381dd9 --- /dev/null +++ b/src/config/workloads.js @@ -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']; diff --git a/src/data/import-notifications.json b/src/data/import-notifications.json new file mode 100644 index 0000000..5076027 --- /dev/null +++ b/src/data/import-notifications.json @@ -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" + } + ] +} diff --git a/k6-reporter-2.4.0.js b/src/libs/k6-reporter-2.3.0.js similarity index 100% rename from k6-reporter-2.4.0.js rename to src/libs/k6-reporter-2.3.0.js diff --git a/src/libs/k6-url-1.0.0.js b/src/libs/k6-url-1.0.0.js new file mode 100644 index 0000000..5fd1e82 --- /dev/null +++ b/src/libs/k6-url-1.0.0.js @@ -0,0 +1 @@ +!function(t,e){for(var r in e)t[r]=e[r];e.__esModule&&Object.defineProperty(t,"__esModule",{value:!0})}(exports,(()=>{var t={3020:(t,e,r)=>{t.exports={URL:r(8149),URLSearchParams:r(8492)}},3099:t=>{t.exports=function(t){if("function"!=typeof t)throw TypeError(String(t)+" is not a function");return t}},6077:(t,e,r)=>{var n=r(111);t.exports=function(t){if(!n(t)&&null!==t)throw TypeError("Can't set "+String(t)+" as a prototype");return t}},1223:(t,e,r)=>{var n=r(5112),o=r(30),a=r(3070),i=n("unscopables"),u=Array.prototype;null==u[i]&&a.f(u,i,{configurable:!0,value:o(null)}),t.exports=function(t){u[i][t]=!0}},5787:t=>{t.exports=function(t,e,r){if(!(t instanceof e))throw TypeError("Incorrect "+(r?r+" ":"")+"invocation");return t}},9670:(t,e,r)=>{var n=r(111);t.exports=function(t){if(!n(t))throw TypeError(String(t)+" is not an object");return t}},8457:(t,e,r)=>{"use strict";var n=r(9974),o=r(7908),a=r(3411),i=r(7659),u=r(7466),s=r(6135),c=r(1246);t.exports=function(t){var e,r,f,l,p,h,v=o(t),g="function"==typeof this?this:Array,y=arguments.length,d=y>1?arguments[1]:void 0,m=void 0!==d,b=c(v),x=0;if(m&&(d=n(d,y>2?arguments[2]:void 0,2)),null==b||g==Array&&i(b))for(r=new g(e=u(v.length));e>x;x++)h=m?d(v[x],x):v[x],s(r,x,h);else for(p=(l=b.call(v)).next,r=new g;!(f=p.call(l)).done;x++)h=m?a(l,d,[f.value,x],!0):f.value,s(r,x,h);return r.length=x,r}},1318:(t,e,r)=>{var n=r(5656),o=r(7466),a=r(1400),i=function(t){return function(e,r,i){var u,s=n(e),c=o(s.length),f=a(i,c);if(t&&r!=r){for(;c>f;)if((u=s[f++])!=u)return!0}else for(;c>f;f++)if((t||f in s)&&s[f]===r)return t||f||0;return!t&&-1}};t.exports={includes:i(!0),indexOf:i(!1)}},3411:(t,e,r)=>{var n=r(9670);t.exports=function(t,e,r,o){try{return o?e(n(r)[0],r[1]):e(r)}catch(e){var a=t.return;throw void 0!==a&&n(a.call(t)),e}}},4326:t=>{var e={}.toString;t.exports=function(t){return e.call(t).slice(8,-1)}},648:(t,e,r)=>{var n=r(1694),o=r(4326),a=r(5112)("toStringTag"),i="Arguments"==o(function(){return arguments}());t.exports=n?o:function(t){var e,r,n;return void 0===t?"Undefined":null===t?"Null":"string"==typeof(r=function(t,e){try{return t[e]}catch(t){}}(e=Object(t),a))?r:i?o(e):"Object"==(n=o(e))&&"function"==typeof e.callee?"Arguments":n}},9920:(t,e,r)=>{var n=r(6656),o=r(3887),a=r(1236),i=r(3070);t.exports=function(t,e){for(var r=o(e),u=i.f,s=a.f,c=0;c{var n=r(7293);t.exports=!n((function(){function t(){}return t.prototype.constructor=null,Object.getPrototypeOf(new t)!==t.prototype}))},4994:(t,e,r)=>{"use strict";var n=r(3383).IteratorPrototype,o=r(30),a=r(9114),i=r(8003),u=r(7497),s=function(){return this};t.exports=function(t,e,r){var c=e+" Iterator";return t.prototype=o(n,{next:a(1,r)}),i(t,c,!1,!0),u[c]=s,t}},8880:(t,e,r)=>{var n=r(9781),o=r(3070),a=r(9114);t.exports=n?function(t,e,r){return o.f(t,e,a(1,r))}:function(t,e,r){return t[e]=r,t}},9114:t=>{t.exports=function(t,e){return{enumerable:!(1&t),configurable:!(2&t),writable:!(4&t),value:e}}},6135:(t,e,r)=>{"use strict";var n=r(7593),o=r(3070),a=r(9114);t.exports=function(t,e,r){var i=n(e);i in t?o.f(t,i,a(0,r)):t[i]=r}},654:(t,e,r)=>{"use strict";var n=r(2109),o=r(4994),a=r(9518),i=r(7674),u=r(8003),s=r(8880),c=r(1320),f=r(5112),l=r(1913),p=r(7497),h=r(3383),v=h.IteratorPrototype,g=h.BUGGY_SAFARI_ITERATORS,y=f("iterator"),d="keys",m="values",b="entries",x=function(){return this};t.exports=function(t,e,r,f,h,w,S){o(r,e,f);var A,O,R,j=function(t){if(t===h&&E)return E;if(!g&&t in U)return U[t];switch(t){case d:case m:case b:return function(){return new r(this,t)}}return function(){return new r(this)}},k=e+" Iterator",L=!1,U=t.prototype,P=U[y]||U["@@iterator"]||h&&U[h],E=!g&&P||j(h),I="Array"==e&&U.entries||P;if(I&&(A=a(I.call(new t)),v!==Object.prototype&&A.next&&(l||a(A)===v||(i?i(A,v):"function"!=typeof A[y]&&s(A,y,x)),u(A,k,!0,!0),l&&(p[k]=x))),h==m&&P&&P.name!==m&&(L=!0,E=function(){return P.call(this)}),l&&!S||U[y]===E||s(U,y,E),p[e]=E,h)if(O={values:j(m),keys:w?E:j(d),entries:j(b)},S)for(R in O)(g||L||!(R in U))&&c(U,R,O[R]);else n({target:e,proto:!0,forced:g||L},O);return O}},9781:(t,e,r)=>{var n=r(7293);t.exports=!n((function(){return 7!=Object.defineProperty({},1,{get:function(){return 7}})[1]}))},317:(t,e,r)=>{var n=r(7854),o=r(111),a=n.document,i=o(a)&&o(a.createElement);t.exports=function(t){return i?a.createElement(t):{}}},748:t=>{t.exports=["constructor","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","toLocaleString","toString","valueOf"]},2109:(t,e,r)=>{var n=r(7854),o=r(1236).f,a=r(8880),i=r(1320),u=r(3505),s=r(9920),c=r(4705);t.exports=function(t,e){var r,f,l,p,h,v=t.target,g=t.global,y=t.stat;if(r=g?n:y?n[v]||u(v,{}):(n[v]||{}).prototype)for(f in e){if(p=e[f],l=t.noTargetGet?(h=o(r,f))&&h.value:r[f],!c(g?f:v+(y?".":"#")+f,t.forced)&&void 0!==l){if(typeof p==typeof l)continue;s(p,l)}(t.sham||l&&l.sham)&&a(p,"sham",!0),i(r,f,p,t)}}},7293:t=>{t.exports=function(t){try{return!!t()}catch(t){return!0}}},9974:(t,e,r)=>{var n=r(3099);t.exports=function(t,e,r){if(n(t),void 0===e)return t;switch(r){case 0:return function(){return t.call(e)};case 1:return function(r){return t.call(e,r)};case 2:return function(r,n){return t.call(e,r,n)};case 3:return function(r,n,o){return t.call(e,r,n,o)}}return function(){return t.apply(e,arguments)}}},5005:(t,e,r)=>{var n=r(857),o=r(7854),a=function(t){return"function"==typeof t?t:void 0};t.exports=function(t,e){return arguments.length<2?a(n[t])||a(o[t]):n[t]&&n[t][e]||o[t]&&o[t][e]}},1246:(t,e,r)=>{var n=r(648),o=r(7497),a=r(5112)("iterator");t.exports=function(t){if(null!=t)return t[a]||t["@@iterator"]||o[n(t)]}},8554:(t,e,r)=>{var n=r(9670),o=r(1246);t.exports=function(t){var e=o(t);if("function"!=typeof e)throw TypeError(String(t)+" is not iterable");return n(e.call(t))}},7854:(t,e,r)=>{var n=function(t){return t&&t.Math==Math&&t};t.exports=n("object"==typeof globalThis&&globalThis)||n("object"==typeof window&&window)||n("object"==typeof self&&self)||n("object"==typeof r.g&&r.g)||Function("return this")()},6656:t=>{var e={}.hasOwnProperty;t.exports=function(t,r){return e.call(t,r)}},3501:t=>{t.exports={}},490:(t,e,r)=>{var n=r(5005);t.exports=n("document","documentElement")},4664:(t,e,r)=>{var n=r(9781),o=r(7293),a=r(317);t.exports=!n&&!o((function(){return 7!=Object.defineProperty(a("div"),"a",{get:function(){return 7}}).a}))},8361:(t,e,r)=>{var n=r(7293),o=r(4326),a="".split;t.exports=n((function(){return!Object("z").propertyIsEnumerable(0)}))?function(t){return"String"==o(t)?a.call(t,""):Object(t)}:Object},2788:(t,e,r)=>{var n=r(5465),o=Function.toString;"function"!=typeof n.inspectSource&&(n.inspectSource=function(t){return o.call(t)}),t.exports=n.inspectSource},9909:(t,e,r)=>{var n,o,a,i=r(8536),u=r(7854),s=r(111),c=r(8880),f=r(6656),l=r(6200),p=r(3501),h=u.WeakMap;if(i){var v=new h,g=v.get,y=v.has,d=v.set;n=function(t,e){return d.call(v,t,e),e},o=function(t){return g.call(v,t)||{}},a=function(t){return y.call(v,t)}}else{var m=l("state");p[m]=!0,n=function(t,e){return c(t,m,e),e},o=function(t){return f(t,m)?t[m]:{}},a=function(t){return f(t,m)}}t.exports={set:n,get:o,has:a,enforce:function(t){return a(t)?o(t):n(t,{})},getterFor:function(t){return function(e){var r;if(!s(e)||(r=o(e)).type!==t)throw TypeError("Incompatible receiver, "+t+" required");return r}}}},7659:(t,e,r)=>{var n=r(5112),o=r(7497),a=n("iterator"),i=Array.prototype;t.exports=function(t){return void 0!==t&&(o.Array===t||i[a]===t)}},4705:(t,e,r)=>{var n=r(7293),o=/#|\.prototype\./,a=function(t,e){var r=u[i(t)];return r==c||r!=s&&("function"==typeof e?n(e):!!e)},i=a.normalize=function(t){return String(t).replace(o,".").toLowerCase()},u=a.data={},s=a.NATIVE="N",c=a.POLYFILL="P";t.exports=a},111:t=>{t.exports=function(t){return"object"==typeof t?null!==t:"function"==typeof t}},1913:t=>{t.exports=!1},3383:(t,e,r)=>{"use strict";var n,o,a,i=r(9518),u=r(8880),s=r(6656),c=r(5112),f=r(1913),l=c("iterator"),p=!1;[].keys&&("next"in(a=[].keys())?(o=i(i(a)))!==Object.prototype&&(n=o):p=!0),null==n&&(n={}),f||s(n,l)||u(n,l,(function(){return this})),t.exports={IteratorPrototype:n,BUGGY_SAFARI_ITERATORS:p}},7497:t=>{t.exports={}},133:(t,e,r)=>{var n=r(7293);t.exports=!!Object.getOwnPropertySymbols&&!n((function(){return!String(Symbol())}))},590:(t,e,r)=>{var n=r(7293),o=r(5112),a=r(1913),i=o("iterator");t.exports=!n((function(){var t=new URL("b?a=1&b=2&c=3","http://a"),e=t.searchParams,r="";return t.pathname="c%20d",e.forEach((function(t,n){e.delete("b"),r+=n+t})),a&&!t.toJSON||!e.sort||"http://a/c%20d?a=1&c=3"!==t.href||"3"!==e.get("c")||"a=1"!==String(new URLSearchParams("?a=1"))||!e[i]||"a"!==new URL("https://a@b").username||"b"!==new URLSearchParams(new URLSearchParams("a=b")).get("a")||"xn--e1aybc"!==new URL("http://тест").host||"#%D0%B1"!==new URL("http://a#б").hash||"a1c3"!==r||"x"!==new URL("http://x",void 0).host}))},8536:(t,e,r)=>{var n=r(7854),o=r(2788),a=n.WeakMap;t.exports="function"==typeof a&&/native code/.test(o(a))},1574:(t,e,r)=>{"use strict";var n=r(9781),o=r(7293),a=r(1956),i=r(5181),u=r(5296),s=r(7908),c=r(8361),f=Object.assign,l=Object.defineProperty;t.exports=!f||o((function(){if(n&&1!==f({b:1},f(l({},"a",{enumerable:!0,get:function(){l(this,"b",{value:3,enumerable:!1})}}),{b:2})).b)return!0;var t={},e={},r=Symbol(),o="abcdefghijklmnopqrst";return t[r]=7,o.split("").forEach((function(t){e[t]=t})),7!=f({},t)[r]||a(f({},e)).join("")!=o}))?function(t,e){for(var r=s(t),o=arguments.length,f=1,l=i.f,p=u.f;o>f;)for(var h,v=c(arguments[f++]),g=l?a(v).concat(l(v)):a(v),y=g.length,d=0;y>d;)h=g[d++],n&&!p.call(v,h)||(r[h]=v[h]);return r}:f},30:(t,e,r)=>{var n,o=r(9670),a=r(6048),i=r(748),u=r(3501),s=r(490),c=r(317),f=r(6200)("IE_PROTO"),l=function(){},p=function(t){return"