From b1d6eafa159f35659bbd4d878028e8fb364e2666 Mon Sep 17 00:00:00 2001 From: Dagfinn Olsen Date: Tue, 12 Nov 2024 12:53:59 +0100 Subject: [PATCH] feat(performance): Expands search for serviceowners, improved tracing and logging (#1439) Implements more GET testcalls to serviceowner apis and improves tracing and logging ## Description ## Related Issue(s) - #1326 ## Verification - [ ] **Your** code builds clean without any errors or warnings - [ ] Manual testing done (required) - [ ] Relevant automated test added (if you find this hard, leave it and we'll help out) ## Documentation - [ ] Documentation is updated (either in `docs`-directory, Altinnpedia or a separate linked PR in [altinn-studio-docs.](https://github.com/Altinn/altinn-studio-docs), if applicable) ## Summary by CodeRabbit ## Release Notes - **New Features** - Introduced a `sentinelPerformanceValue` for enhanced performance tracking. - Added flexibility to performance tests by implementing `isSingleUserMode` for better user scenario handling. - **Improvements** - Enhanced logging and traceability in dialog creation and purging processes. - Expanded performance thresholds for more detailed monitoring. - Updated functions to accept dynamic GET request functions, improving modularity. - **Bug Fixes** - Improved error handling and conditions for single vs. multi-user modes in various performance test scripts. - **Documentation** - Updated comments for clarity regarding options and parameters in several scripts. --------- Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- tests/k6/common/config.js | 1 + .../enduser/performance/enduser-search.js | 7 +++- .../graphql/performance/graphql-search.js | 6 ++- .../performancetest_common/createDialog.js | 16 +++++--- .../performancetest_common/simpleSearch.js | 40 +++++++++++-------- .../performancetest_data/01-create-dialog.js | 6 ++- .../serviceowners-staging.csv | 2 +- .../performance/create-dialog-and-search.js | 2 +- .../serviceowner/performance/create-dialog.js | 5 ++- .../performance/create-remove-dialog.js | 6 ++- .../serviceowner/performance/purge-dialogs.js | 8 +++- .../performance/serviceowner-search.js | 11 ++++- 12 files changed, 73 insertions(+), 37 deletions(-) diff --git a/tests/k6/common/config.js b/tests/k6/common/config.js index 0478cff55..102ec02c6 100644 --- a/tests/k6/common/config.js +++ b/tests/k6/common/config.js @@ -65,3 +65,4 @@ export const tokenGeneratorEnv = __ENV.API_ENVIRONMENT == "yt01" ? "yt01" : "tt0 export const baseUrlGraphql = baseUrls[__ENV.API_VERSION]["graphql"][__ENV.API_ENVIRONMENT]; export const sentinelValue = "dialogporten-e2e-sentinel"; +export const sentinelPerformanceValue = "dialogporten-e2e-sentinel-performance"; diff --git a/tests/k6/tests/enduser/performance/enduser-search.js b/tests/k6/tests/enduser/performance/enduser-search.js index d32d2ba1d..5c6fd1906 100644 --- a/tests/k6/tests/enduser/performance/enduser-search.js +++ b/tests/k6/tests/enduser/performance/enduser-search.js @@ -1,6 +1,7 @@ import { enduserSearch } from '../../performancetest_common/simpleSearch.js' import { getDefaultThresholds } from '../../performancetest_common/getDefaultThresholds.js'; import { endUsersWithTokens } from '../../performancetest_common/readTestdata.js'; +import { randomItem } from 'https://jslib.k6.io/k6-utils/1.4.0/index.js'; export let options = { summaryTrendStats: ['avg', 'min', 'med', 'max', 'p(95)', 'p(99)', 'p(99.5)', 'p(99.9)', 'count'], @@ -19,8 +20,10 @@ export let options = { export default function() { if (!endUsersWithTokens || endUsersWithTokens.length === 0) { throw new Error('No end users loaded for testing'); - } - if ((options.vus === undefined || options.vus === 1) && (options.iterations === undefined || options.iterations === 1)) { + } + + const isSingleUserMode = (options.vus ?? 1) === 1 && (options.iterations ?? 1) === 1 && (options.duration ?? 0) === 0; + if (isSingleUserMode) { enduserSearch(endUsersWithTokens[0]); } else { diff --git a/tests/k6/tests/graphql/performance/graphql-search.js b/tests/k6/tests/graphql/performance/graphql-search.js index de5a9c04d..98b4ccbbb 100644 --- a/tests/k6/tests/graphql/performance/graphql-search.js +++ b/tests/k6/tests/graphql/performance/graphql-search.js @@ -3,11 +3,12 @@ * Run: k6 run tests/k6/tests/graphql/performance/graphql-search.js --vus 1 --iterations 1 -e env=yt01 */ -import { randomItem } from 'https://jslib.k6.io/k6-utils/1.2.0/index.js'; +import { randomItem } from 'https://jslib.k6.io/k6-utils/1.4.0/index.js'; import { getDefaultThresholds } from '../../performancetest_common/getDefaultThresholds.js'; import { endUsersWithTokens as endUsers } from '../../performancetest_common/readTestdata.js'; import { graphqlSearch } from "../../performancetest_common/simpleSearch.js"; + /** * The options object for configuring the performance test for GraphQL search. * @@ -26,7 +27,8 @@ export default function() { if (!endUsers || endUsers.length === 0) { throw new Error('No end users loaded for testing'); } - if ((options.vus === undefined || options.vus === 1) && (options.iterations === undefined || options.iterations === 1)) { + const isSingleUserMode = (options.vus ?? 1) === 1 && (options.iterations ?? 1) === 1 && (options.duration ?? 0) === 0; + if (isSingleUserMode) { graphqlSearch(endUsers[0]); } else { diff --git a/tests/k6/tests/performancetest_common/createDialog.js b/tests/k6/tests/performancetest_common/createDialog.js index ecd7591cb..bfcf96acd 100644 --- a/tests/k6/tests/performancetest_common/createDialog.js +++ b/tests/k6/tests/performancetest_common/createDialog.js @@ -14,12 +14,13 @@ import dialogToInsert from "../performancetest_data/01-create-dialog.js"; * @param {Object} endUser - The end user object. */ export function createDialog(serviceOwner, endUser) { + var traceparent = uuidv4(); var paramsWithToken = { headers: { Authorization: "Bearer " + serviceOwner.token, - traceparent: uuidv4() + traceparent: traceparent }, - tags: { name: 'create dialog' } + tags: { name: 'create dialog', traceparent: traceparent, enduser: endUser.ssn } }; describe('create dialog', () => { @@ -35,12 +36,14 @@ export function createDialog(serviceOwner, endUser) { * @param {Object} serviceOwner - The service owner object. * @param {Object} endUser - The end user object. */ -export function createAndRemoveDialog(serviceOwner, endUser) { +export function createAndRemoveDialog(serviceOwner, endUser) { + var traceparent = uuidv4(); var paramsWithToken = { headers: { - Authorization: "Bearer " + serviceOwner.token + Authorization: "Bearer " + serviceOwner.token, + traceparent: traceparent }, - tags: { name: 'create dialog' } + tags: { name: 'create dialog', traceparent: traceparent, enduser: endUser.ssn } } let dialogId = 0; @@ -52,7 +55,10 @@ export function createAndRemoveDialog(serviceOwner, endUser) { }); describe('remove dialog', () => { + traceparent = uuidv4(); paramsWithToken.tags.name = 'remove dialog'; + paramsWithToken.tags.traceparent = traceparent; + paramsWithToken.headers.traceparent = traceparent if (dialogId) { let r = purgeSO('dialogs/' + dialogId, paramsWithToken); expect(r.status, 'response status').to.equal(204); diff --git a/tests/k6/tests/performancetest_common/simpleSearch.js b/tests/k6/tests/performancetest_common/simpleSearch.js index 7a41e5b0e..4c42d3a01 100644 --- a/tests/k6/tests/performancetest_common/simpleSearch.js +++ b/tests/k6/tests/performancetest_common/simpleSearch.js @@ -16,18 +16,20 @@ import { getGraphqlParty } from '../performancetest_data/graphql-search.js'; * @param {Object} paramsWithToken - The parameters with token. * @returns {void} */ -function retrieveDialogContent(response, paramsWithToken) { +function retrieveDialogContent(response, paramsWithToken, getFunction = getEU) { const items = response.json().items; if (!items?.length) return; const dialogId = items[0].id; if (!dialogId) return; - getContent(dialogId, paramsWithToken, 'get dialog'); - getContentChain(dialogId, paramsWithToken, 'get dialog activities', 'get dialog activity', '/activities/') - getContentChain(dialogId, paramsWithToken, 'get seenlogs', 'get seenlog', '/seenlog/') - getContent(dialogId, paramsWithToken, 'get labellog', '/labellog'); - getContentChain(dialogId, paramsWithToken, 'get transmissions', 'get transmission', '/transmissions/') + getContent(dialogId, paramsWithToken, 'get dialog', '', getFunction); + getContentChain(dialogId, paramsWithToken, 'get dialog activities', 'get dialog activity', '/activities/', getFunction); + getContentChain(dialogId, paramsWithToken, 'get seenlogs', 'get seenlog', '/seenlog/', getFunction); + if (getFunction == getEU) { + getContent(dialogId, paramsWithToken, 'get labellog', '/labellog', getFunction); + } + getContentChain(dialogId, paramsWithToken, 'get transmissions', 'get transmission', '/transmissions/', getFunction); } /** @@ -61,12 +63,12 @@ export function enduserSearch(enduser) { * @param {string} path - The path to append to the URL. Can be empty or /labellog. * @returns {void} */ -export function getContent(dialogId, paramsWithToken, tag, path = '') { +export function getContent(dialogId, paramsWithToken, tag, path = '', getFunction = getEU) { const listParams = { ...paramsWithToken, tags: { ...paramsWithToken.tags, name: tag } }; - getUrl('dialogs/' + dialogId + path, listParams); + getUrl('dialogs/' + dialogId + path, listParams, getFunction); } /** @@ -78,19 +80,19 @@ export function getContent(dialogId, paramsWithToken, tag, path = '') { * @param {string} endpoint - The endpoint to append to the URL. * @returns {void} */ -export function getContentChain(dialogId, paramsWithToken, tag, subtag, endpoint) { +export function getContentChain(dialogId, paramsWithToken, tag, subtag, endpoint, getFunction = getEU) { const listParams = { ...paramsWithToken, tags: { ...paramsWithToken.tags, name: tag } }; - let d = getUrl('dialogs/' + dialogId + endpoint, listParams); + let d = getUrl('dialogs/' + dialogId + endpoint, listParams, getFunction); let json = d.json(); if (json.length > 0) { const detailParams = { ...paramsWithToken, tags: { ...paramsWithToken.tags, name: subtag } }; - getUrl('dialogs/' + dialogId + endpoint + randomItem(json).id, detailParams); + getUrl('dialogs/' + dialogId + endpoint + randomItem(json).id, detailParams, getFunction); } } @@ -100,8 +102,8 @@ export function getContentChain(dialogId, paramsWithToken, tag, subtag, endpoint * @param {Object} paramsWithToken - The parameters with token. * @returns {Object} The response object. */ -export function getUrl(url, paramsWithToken) { - let r = getEU(url, paramsWithToken); +export function getUrl(url, paramsWithToken, getFunction = getEU) { + let r = getFunction(url, paramsWithToken); expectStatusFor(r).to.equal(200); expect(r, 'response').to.have.validJsonBody(); return r; @@ -114,12 +116,14 @@ export function getUrl(url, paramsWithToken) { * @returns {void} */ export function graphqlSearch(enduser) { + let traceparent = uuidv4(); let paramsWithToken = { headers: { Authorization: "Bearer " + enduser.token, - traceparent: uuidv4() + traceparent: traceparent, + 'User-Agent': 'dialogporten-k6-graphql-search' }, - tags: { name: 'graphql search' } + tags: { name: 'graphql search', traceparent: traceparent } }; describe('Perform graphql dialog list', () => { let r = postGQ(getGraphqlParty(enduser.ssn), paramsWithToken); @@ -134,12 +138,13 @@ export function graphqlSearch(enduser) { * @param {*} enduser */ export function serviceownerSearch(serviceowner, enduser, tag_name) { + let traceparent = uuidv4(); let paramsWithToken = { headers: { Authorization: "Bearer " + serviceowner.token, - traceparent: uuidv4() + traceparent: traceparent }, - tags: { name: tag_name } + tags: { name: tag_name, traceparent: traceparent, enduser: enduser.ssn } } let enduserid = encodeURIComponent(`urn:altinn:person:identifier-no:${enduser.ssn}`); @@ -149,5 +154,6 @@ export function serviceownerSearch(serviceowner, enduser, tag_name) { let r = getSO('dialogs' + defaultFilter, paramsWithToken); expectStatusFor(r).to.equal(200); expect(r, 'response').to.have.validJsonBody(); + retrieveDialogContent(r, paramsWithToken, getSO); }); } diff --git a/tests/k6/tests/performancetest_data/01-create-dialog.js b/tests/k6/tests/performancetest_data/01-create-dialog.js index a5e994cb8..551d324cc 100644 --- a/tests/k6/tests/performancetest_data/01-create-dialog.js +++ b/tests/k6/tests/performancetest_data/01-create-dialog.js @@ -1,4 +1,5 @@ import {default as createDialogPayload} from "../serviceowner/testdata/01-create-dialog.js" +import { sentinelPerformanceValue } from "../../common/config.js"; const ACTIVITY_TYPE_INFORMATION = 'Information'; @@ -7,7 +8,10 @@ function cleanUp(originalPayload) { throw new Error('Invalid payload'); } - const payload = { ...originalPayload }; + const payload = { + ...originalPayload, + searchTags: [...(originalPayload.searchTags || []), { "value": sentinelPerformanceValue }] + }; const { visibleFrom, ...payloadWithoutVisibleFrom } = payload; const activities = payload.activities?.map(activity => { diff --git a/tests/k6/tests/performancetest_data/serviceowners-staging.csv b/tests/k6/tests/performancetest_data/serviceowners-staging.csv index 449a5e7c6..c0e5e421f 100644 --- a/tests/k6/tests/performancetest_data/serviceowners-staging.csv +++ b/tests/k6/tests/performancetest_data/serviceowners-staging.csv @@ -1,2 +1,2 @@ org,orgno,scopes,resource -digdir,991825827,digdir:dialogporten.serviceprovider,super-simple-service +digdir,991825827,digdir:dialogporten.serviceprovider digdir:dialogporten.serviceprovider.search,super-simple-service diff --git a/tests/k6/tests/scenarios/performance/create-dialog-and-search.js b/tests/k6/tests/scenarios/performance/create-dialog-and-search.js index 5caf31f9c..6b6f6d8c2 100644 --- a/tests/k6/tests/scenarios/performance/create-dialog-and-search.js +++ b/tests/k6/tests/scenarios/performance/create-dialog-and-search.js @@ -2,7 +2,7 @@ * Performance test for creating dialogs and searching dialogs. * Run: k6 run tests/k6/tests/scenarios/performance/create-dialog-and-search.js -e env=yt01 -e svus=1 -e evus=1 -e duration=1m */ -import { randomItem } from 'https://jslib.k6.io/k6-utils/1.2.0/index.js'; +import { randomItem } from 'https://jslib.k6.io/k6-utils/1.4.0/index.js'; import { enduserSearch } from '../../performancetest_common/simpleSearch.js'; import { createDialog } from '../../performancetest_common/createDialog.js'; import { getDefaultThresholds } from '../../performancetest_common/getDefaultThresholds.js'; diff --git a/tests/k6/tests/serviceowner/performance/create-dialog.js b/tests/k6/tests/serviceowner/performance/create-dialog.js index 03cbadbc5..5429aed45 100644 --- a/tests/k6/tests/serviceowner/performance/create-dialog.js +++ b/tests/k6/tests/serviceowner/performance/create-dialog.js @@ -2,7 +2,7 @@ * Performance test for creating a dialog * Run: k6 run tests/k6/tests/serviceowner/performance/create-dialog.js --vus 1 --iterations 1 */ -import { randomItem } from 'https://jslib.k6.io/k6-utils/1.2.0/index.js'; +import { randomItem } from 'https://jslib.k6.io/k6-utils/1.4.0/index.js'; import { getDefaultThresholds } from '../../performancetest_common/getDefaultThresholds.js'; import { createDialog } from '../../performancetest_common/createDialog.js'; import { serviceOwners, endUsers } from '../../performancetest_common/readTestdata.js'; @@ -19,7 +19,8 @@ export default function() { if (!serviceOwners || serviceOwners.length === 0) { throw new Error('No service owners loaded for testing'); } - if ((options.vus === undefined || options.vus === 1) && (options.iterations === undefined || options.iterations === 1)) { + const isSingleUserMode = (options.vus ?? 1) === 1 && (options.iterations ?? 1) === 1 && (options.duration ?? 0) === 0; + if (isSingleUserMode) { createDialog(serviceOwners[0], endUsers[0]); } else { diff --git a/tests/k6/tests/serviceowner/performance/create-remove-dialog.js b/tests/k6/tests/serviceowner/performance/create-remove-dialog.js index e46134a8a..26e90099d 100644 --- a/tests/k6/tests/serviceowner/performance/create-remove-dialog.js +++ b/tests/k6/tests/serviceowner/performance/create-remove-dialog.js @@ -2,7 +2,7 @@ * Performance test for creating and removing a dialog * Run: k6 run tests/k6/tests/serviceowner/performance/create-remove-dialog.js --vus 1 --iterations 1 */ -import { randomItem } from 'https://jslib.k6.io/k6-utils/1.2.0/index.js'; +import { randomItem } from 'https://jslib.k6.io/k6-utils/1.4.0/index.js'; import { getDefaultThresholds } from '../../performancetest_common/getDefaultThresholds.js'; import { serviceOwners, endUsers } from "../../performancetest_common/readTestdata.js"; import { createAndRemoveDialog } from '../../performancetest_common/createDialog.js'; @@ -22,7 +22,9 @@ export default function() { if (!serviceOwners || serviceOwners.length === 0) { throw new Error('No service owners loaded for testing'); } - if ((options.vus === undefined || options.vus === 1) && (options.iterations === undefined || options.iterations === 1)) { + + const isSingleUserMode = (options.vus ?? 1) === 1 && (options.iterations ?? 1) === 1 && (options.duration ?? 0) === 0; + if (isSingleUserMode) { createAndRemoveDialog(serviceOwners[0], endUsers[0]); } else { diff --git a/tests/k6/tests/serviceowner/performance/purge-dialogs.js b/tests/k6/tests/serviceowner/performance/purge-dialogs.js index 932488890..d883c7601 100644 --- a/tests/k6/tests/serviceowner/performance/purge-dialogs.js +++ b/tests/k6/tests/serviceowner/performance/purge-dialogs.js @@ -7,12 +7,13 @@ * * Run: k6 run tests/k6/tests/serviceowner/performance/purge-dialogs.js -e env=yt01 */ +import { uuidv4 } from 'https://jslib.k6.io/k6-utils/1.4.0/index.js'; import { getSO, purgeSO } from '../../../common/request.js'; import { serviceOwners } from '../../performancetest_common/readTestdata.js'; import { expect, expectStatusFor } from "../../../common/testimports.js"; import { getDefaultThresholds } from '../../performancetest_common/getDefaultThresholds.js'; import { describe } from '../../../common/describe.js'; -import { sentinelValue } from '../../../common/config.js'; +import { sentinelPerformanceValue as sentinelValue } from '../../../common/config.js'; /** * Retrieves the dialog ids to purge. @@ -21,9 +22,12 @@ import { sentinelValue } from '../../../common/config.js'; * @returns {Array} - The dialog ids to purge. */ function getDialogs(serviceOwner) { + var traceparent = uuidv4(); + console.log("Searching for dialogs to purge, tracevalue: " + traceparent); var paramsWithToken = { headers: { - Authorization: "Bearer " + serviceOwner.token + Authorization: "Bearer " + serviceOwner.token, + traceparent: traceparent } } let hasNextPage = false; diff --git a/tests/k6/tests/serviceowner/performance/serviceowner-search.js b/tests/k6/tests/serviceowner/performance/serviceowner-search.js index 89ef62697..345f2dfa5 100644 --- a/tests/k6/tests/serviceowner/performance/serviceowner-search.js +++ b/tests/k6/tests/serviceowner/performance/serviceowner-search.js @@ -1,4 +1,4 @@ -import { randomItem } from 'https://jslib.k6.io/k6-utils/1.1.0/index.js'; +import { randomItem } from 'https://jslib.k6.io/k6-utils/1.4.0/index.js'; import { serviceownerSearch } from '../../performancetest_common/simpleSearch.js' import { getDefaultThresholds } from '../../performancetest_common/getDefaultThresholds.js'; import { serviceOwners ,endUsersWithTokens } from '../../performancetest_common/readTestdata.js'; @@ -7,7 +7,14 @@ const tag_name = 'serviceowner search'; export let options = { summaryTrendStats: ['avg', 'min', 'med', 'max', 'p(95)', 'p(99)', 'p(99.5)', 'p(99.9)', 'count'], - thresholds: getDefaultThresholds(['http_req_duration', 'http_reqs'],[tag_name]) + thresholds: getDefaultThresholds(['http_req_duration', 'http_reqs'],[tag_name, + 'get dialog', + 'get dialog activities', + 'get dialog activity', + 'get seenlogs', + 'get seenlog', + 'get transmissions', + 'get transmission']) }; /**