From 684b378446c771123dbcedfe7d0b1f5a05872ab2 Mon Sep 17 00:00:00 2001 From: Mike Vesprini Date: Thu, 19 Dec 2024 23:38:13 -0800 Subject: [PATCH] Cleanup auth and readme --- performance/README.md | 44 ++++++++++++++++++-------- performance/backend_script.js | 36 ++++++++++----------- performance/common/auth.js | 17 ++-------- performance/common/params.js | 2 +- performance/frontend_script.js | 28 ++++++++++++++-- performance/tests/frontend/protocol.js | 21 ++++++++++-- 6 files changed, 96 insertions(+), 52 deletions(-) diff --git a/performance/README.md b/performance/README.md index ff586be9d..1bc50f0a2 100644 --- a/performance/README.md +++ b/performance/README.md @@ -5,6 +5,13 @@ Performance testing of this app was done using k6 by Grafana (https://k6.io/). Frontend performance tests made use of the k6 browser testing tools (https://grafana.com/docs/k6/latest/using-k6-browser/). +## Running the Tests + +Backend and frontend have thier own test scripts, and their own make commands to run. +For the most accurate results, it is generally best to run one scenario at a time. Note that if running multiple test +scenarios at once, such as hitting several endpoints continuously, each test will have its own set of the number of +users specified in the stages, so change the numbers as needed. + ## Setup In order to configure the tests, the following settings are available: @@ -15,32 +22,43 @@ In order to configure the tests, the following settings are available: - HOST: set in the `Makefile`, this points to the server being tested. Front- and back-end each have a value. - REQUESTS_PER_SECOND: set i the `Makefile`, this dictates the rate that the requests will be made at. ** Important: if setting above 50rps, alert platform services first ** -- Credentials and request headers: set in `common/auth.js` these are the credentials used to run the tests. User - credentials will need to be added for the browser test. A valid auth token is needed for the protocol level tets. +- Credentials and tokens: set in `common/auth.js` these are the credentials used to run the tests. User + credentials will need to be added for the browser test. A valid auth token & refresh token are needed for the + protocol level tets. - Tests: determining which tests will run in a given suite is done by simply commenting out tests that you want skipped - in the respective `frontend_script.js` or `backend_script.js` file. Comment out the tests entry in both `options.scenario` - and the corresponding if statement in the default function. + in the respective `frontend_script.js` or `backend_script.js` file. Comment out the tests entry in both + `options.scenario` and the corresponding if statement in the default function. -## Running the Tests +### Token and Credentials -Backend and frontend have thier own test scripts, and their own make commands to run. +Valid user credentials with the appropriate roles are required for the browser tests, along with that users officer ID +from the officer table. For the protocol level tests a token and refresh token that are valid at the time of running +the test will be needed, and the tests will refresh the values as they run. The logic for this can be found in +`backend_script.js` and `frontend_script.js`. The initial values for the token and refresh token can be found be loggin +into the environment that is being used for the load test and copying the values out of the response of the network +call titled `token` from browser tools network tab. If you leave an instance open in your browser these network +requests will continue to be made with the latest token, which can save time between tests. Once the rest of the setup +is done and you are ready to run the tests, copy the latest values into `common/auth.js` and don't forget to save. -### Locally +### Running from Local 1. Install k6 - https://k6.io/docs/get-started/installation/ -2. If results files already exist in `/performance/k6_results` consider dating them or moving them, or else they will be +2. If results files already exist in `/performance/k6_results` rename or move them, or else they will be overwritten. 3. Configure the tests (see "Setup" above) to be pointed at the correct servers with the desired load and scenarios. + The load is set for the front- and backend separately in the Makefile, be sure to set the correct one. 4. Ensure that the servers being used for testing are running and ready with the correct test data, and setup any - monitoring needed during the testing. It is a good idea to watch resource usage on the machine running the tests as - well just to ensure that its own resource constraints are not affecting results. + cluster / resource monitoring needed during the testing. It is a good idea to watch resource usage on the machine + running the tests as well just to ensure that its own resource constraints are not affecting results. -5. From the `/performance` directory, run either `make bakend_perf_test` or `make frontend_perf_test` depending on - which suite you are running. (If running one of the more intensive tests such as `spike` or `stress`, it is - recommended that a `test_run` is done first to ensure that everything is working as intended.) +5. From the `/performance` directory, run `make bakend_perf_test` or `make frontend_perf_test` depending on + which suite you are running. To run the frontend browser test with a regular browser, instead run + `K6_BROWSER_HEADLESS=false make frontend_perf_test` however this is very resource intesive to do with more than one + virtual user so adjust the number of vus. If running one of the more intensive tests such as `spike` or + `stress`, it is recommended that a `test_run` is done first to ensure that everything is working as intended. 6. When the tests finish, k6 will present you with a summary of the test in the terminal that ran the tests, it is recommended to copy the summary into a text file. The detailed results can be found in `/performance/k6_results/`. diff --git a/performance/backend_script.js b/performance/backend_script.js index 421890bc9..0766c70a9 100644 --- a/performance/backend_script.js +++ b/performance/backend_script.js @@ -14,7 +14,7 @@ import { mapSearchWithCMFilter, } from "./tests/backend/mapSearch.js"; import { getComplaintDetails, addAndRemoveComplaintOutcome } from "./tests/backend/complaint_details.js"; -import { INITIAL_COS_TOKEN, INITIAL_COS_REFRESH_TOKEN, generateRequestConfig } from "./common/auth.js"; +import { INITIAL_TOKEN, INITIAL_REFRESH_TOKEN, generateRequestConfig } from "./common/auth.js"; const defaultOptions = { executor: "ramping-vus", @@ -52,9 +52,9 @@ export const options = { */ const TOKEN_REFRESH_TIME = 60; -var cosToken = INITIAL_COS_TOKEN; -var cosRefreshToken = INITIAL_COS_REFRESH_TOKEN; -var cosRequestConfig = generateRequestConfig(cosToken); +var token = INITIAL_TOKEN; +var refreshToken = INITIAL_REFRESH_TOKEN; +var requestConfig = generateRequestConfig(token); export default function () { const HOST = __ENV.SERVER_HOST; @@ -64,46 +64,46 @@ export default function () { "https://dev.loginproxy.gov.bc.ca/auth/realms/standard/protocol/openid-connect/token", { grant_type: "refresh_token", - refresh_token: cosRefreshToken, + refresh_token: refreshToken, client_id: "compliance-and-enforcement-digital-services-web-4794", }, ); - cosToken = JSON.parse(refreshRes.body).access_token; - cosRefreshToken = JSON.parse(refreshRes.body).refresh_token; - cosRequestConfig = generateRequestConfig(cosToken); + token = JSON.parse(refreshRes.body).access_token; + refreshToken = JSON.parse(refreshRes.body).refresh_token; + requestConfig = generateRequestConfig(token); } // search if (exec.scenario.name === "searchWithDefaultFilters") { - searchWithDefaultFilters(HOST, cosRequestConfig); + searchWithDefaultFilters(HOST, requestConfig); } if (exec.scenario.name === "searchWithoutFilters") { - searchWithoutFilters(HOST, cosRequestConfig); + searchWithoutFilters(HOST, requestConfig); } if (exec.scenario.name === "openSearchWithoutFilters") { - openSearchWithoutFilters(HOST, cosRequestConfig); + openSearchWithoutFilters(HOST, requestConfig); } if (exec.scenario.name === "searchWithCMFilter") { - searchWithCMFilter(HOST, cosRequestConfig); + searchWithCMFilter(HOST, requestConfig); } // map search if (exec.scenario.name === "mapSearchDefaultFilters") { - mapSearchDefaultFilters(HOST, cosRequestConfig); + mapSearchDefaultFilters(HOST, requestConfig); } if (exec.scenario.name === "mapSearchAllOpenComplaints") { - mapSearchAllOpenComplaints(HOST, cosRequestConfig); + mapSearchAllOpenComplaints(HOST, requestConfig); } if (exec.scenario.name === "mapSearchAllComplaints") { - mapSearchAllComplaints(HOST, cosRequestConfig); + mapSearchAllComplaints(HOST, requestConfig); } if (exec.scenario.name === "mapSearchWithCMFilter") { - mapSearchWithCMFilter(HOST, cosRequestConfig); + mapSearchWithCMFilter(HOST, requestConfig); } // complaint details if (exec.scenario.name === "getComplaintDetails") { - getComplaintDetails(HOST, cosRequestConfig); + getComplaintDetails(HOST, requestConfig); } if (exec.scenario.name === "addAndRemoveComplaintOutcome") { - addAndRemoveComplaintOutcome(HOST, cosRequestConfig); + addAndRemoveComplaintOutcome(HOST, requestConfig); } } diff --git a/performance/common/auth.js b/performance/common/auth.js index 0bb576e32..0531fd16d 100644 --- a/performance/common/auth.js +++ b/performance/common/auth.js @@ -1,5 +1,5 @@ -export const INITIAL_COS_TOKEN = ""; -export const INITIAL_COS_REFRESH_TOKEN = ""; +export const INITIAL_TOKEN = ""; +export const INITIAL_REFRESH_TOKEN = ""; export const COS_USER_CREDS = { username: "", @@ -15,16 +15,3 @@ export const generateRequestConfig = (token) => { }, }; }; - -const CEEB_USER_TOKEN = "xxxxxxx"; -export const CEEB_USER_CREDS = { - username: "", - password: "", -}; - -export const CEEB_USER_HEADERS = { - headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${CEEB_USER_TOKEN}`, - }, -}; diff --git a/performance/common/params.js b/performance/common/params.js index 8bac9dc6c..7cd4256ea 100644 --- a/performance/common/params.js +++ b/performance/common/params.js @@ -14,7 +14,7 @@ export const STAGES = { // Test run stages to make sure all scenarios are working test_run: [ { duration: "5s", target: TEST_RUN_USERS }, - { duration: "5s", target: TEST_RUN_USERS }, + { duration: "10s", target: TEST_RUN_USERS }, { duration: "5s", target: 0 }, ], diff --git a/performance/frontend_script.js b/performance/frontend_script.js index 231696a22..a89fc6c5e 100644 --- a/performance/frontend_script.js +++ b/performance/frontend_script.js @@ -1,6 +1,8 @@ import exec from "k6/execution"; +import http from "k6/http"; import { browserTest } from "./tests/frontend/browser.js"; import { protocolTest } from "./tests/frontend/protocol.js"; +import { INITIAL_TOKEN, INITIAL_REFRESH_TOKEN, generateRequestConfig } from "./common/auth.js"; /** * To run with an open browser, prepend the make command with K6_BROWSER_HEADLESS=false @@ -30,19 +32,39 @@ export const options = { }, protocolTest: { executor: "constant-vus", - vus: 5, - duration: "30s", + vus: 1, + duration: "20s", }, }, }; +const TOKEN_REFRESH_TIME = 60; +var token = INITIAL_TOKEN; +var refreshToken = INITIAL_REFRESH_TOKEN; +var requestConfig = generateRequestConfig(token); + export default function () { const HOST = __ENV.APP_HOST; + // Refresh the token if necessary based on iteration number, refresh time and rate of requests + if (__ITER === 0 || __ITER % (__ENV.RPS * TOKEN_REFRESH_TIME) === 0) { + const refreshRes = http.post( + "https://dev.loginproxy.gov.bc.ca/auth/realms/standard/protocol/openid-connect/token", + { + grant_type: "refresh_token", + refresh_token: refreshToken, + client_id: "compliance-and-enforcement-digital-services-web-4794", + }, + ); + + token = JSON.parse(refreshRes.body).access_token; + refreshToken = JSON.parse(refreshRes.body).refresh_token; + requestConfig = generateRequestConfig(token); + } if (exec.scenario.name === "browserTest") { browserTest(HOST); } if (exec.scenario.name === "protocolTest") { - protocolTest(HOST); + protocolTest(HOST, requestConfig); } } diff --git a/performance/tests/frontend/protocol.js b/performance/tests/frontend/protocol.js index 4e233f6f6..78e03d6ad 100644 --- a/performance/tests/frontend/protocol.js +++ b/performance/tests/frontend/protocol.js @@ -1,8 +1,25 @@ import http from "k6/http"; import { check } from "k6"; -export function protocolTest() { - const res = http.get("http://localhost:3001/"); +export function protocolTest(host, requestConfig) { + const res = http.get(host + "/static/js/bundle.js", { + headers: { + ...requestConfig.headers, + Accept: + "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7", + "Accept-Encoding": "gzip, deflate, br, zstd", + "Accept-Language": "en-US,en;q=0.9", + "Cache-Control": "no-cache", + Connection: "keep-alive", + Host: "localhost:3001", + Pragma: "no-cache", + "Sec-Fetch-Dest": "document", + "Sec-Fetch-Mode": "navigate", + "Sec-Fetch-Site": "same-origin", + "Sec-Fetch-User": 1, + "Upgrade-Insecure-Requests": 1, + }, + }); check(res, { "status is 200": (res) => res.status === 200, });