Refactor(utils): Consolidate response handling into utility functions 02/141102/3
authorsourabh_sourabh <sourabh.sourabh@est.tech>
Wed, 4 Jun 2025 17:22:42 +0000 (18:22 +0100)
committersourabh_sourabh <sourabh.sourabh@est.tech>
Fri, 6 Jun 2025 15:03:28 +0000 (16:03 +0100)
Abstracted common HTTP response validation and metric recording logic into reusable utility functions:

- Added `validateAndRecordMetric` for generalized response checks and metric recording.
- Introduced `processHttpResponseWithOverheadMetrics` to handle response times adjusted for known delays.
- Created `validateResponseAndRecordMetric` to validate JSON response lengths and record metrics.

These changes promote code reuse and maintain consistency across test scenarios.

Issue-ID: CPS-2690
Change-Id: Ie4dd1075c65e87c1b48225e096b2b781b098da0f
Signed-off-by: sourabh_sourabh <sourabh.sourabh@est.tech>
k6-tests/ncmp/common/utils.js
k6-tests/ncmp/ncmp-test-runner.js
k6-tests/ncmp/run-all-tests.sh

index ca3a651..0c24912 100644 (file)
@@ -146,25 +146,45 @@ function makeSummaryCsvLine(testCase, testName, unit, measurementName, currentEx
 }
 
 /**
- * Handles the response by performing a check, logging errors if any, and recording overhead.
+ * Validates a response against an expected HTTP status and an optional additional check.
+ * If successful, records a metric value to a trend. Logs detailed error output on failure.
+ *
+ * @param {Object} response - The HTTP response object from a K6 request.
+ * @param {number} expectedStatus - The expected HTTP status code (e.g., 200, 202).
+ * @param {string} checkLabel - The label to use in the K6 `check()` for reporting.
+ * @param {Trend} trendMetric - A K6 `Trend` metric to which the extracted value will be added on success.
+ * @param {function(Object): number} metricExtractor - A function that takes the response and returns a numeric value to record (e.g., `res.timings.duration`).
+ * @param {function(Object): boolean} [additionalCheckValidator=() => true] - Optional function for any additional custom validation on the response.
  *
- * @param {Object} response - The HTTP response object.
- * @param {number} expectedStatus - The expected HTTP status code.
- * @param {string} checkLabel - A descriptive label for the check.
- * @param {number} delayMs - The predefined delay in milliseconds.
- * @param {Trend} trendMetric - The Trend metric to record overhead.
  */
-export function handleHttpResponse(response, expectedStatus, checkLabel, delayMs, trendMetric) {
+export function validateAndRecordMetric(response, expectedStatus, checkLabel, trendMetric, metricExtractor, additionalCheckValidator = () => true) {
+
+    const isExpectedStatus = response.status === expectedStatus;
+    const isAdditionalCheckValid = additionalCheckValidator(response);
     const isSuccess = check(response, {
-        [checkLabel]: (responseObj) => responseObj.status === expectedStatus,
+        [checkLabel]: () => isExpectedStatus && isAdditionalCheckValid,
     });
 
     if (isSuccess) {
-        const overhead = response.timings.duration - delayMs;
-        trendMetric.add(overhead);
+        trendMetric.add(metricExtractor(response));
     } else {
-        let responseBody = JSON.parse(response.body);
-        console.error(`${checkLabel} failed: Error response status: ${response.status}, message: ${responseBody.message}, details: ${responseBody.details}`);
+        console.error(`${checkLabel} failed. Status: ${response.status}`);
+        if (response.body) {
+            try {
+                const responseBody = JSON.parse(response.body);
+                console.error(`❌ ${checkLabel} failed: Error response status: ${response.status}, message: ${responseBody.message}, details: ${responseBody.details}`);
+            } catch (e) {
+                console.error(`❌ ${checkLabel} failed: Unable to parse response body.`);
+            }
+        }
     }
 }
 
+export function processHttpResponseWithOverheadMetrics(response, expectedStatus, checkLabel, delayMs, trendMetric) {
+    validateAndRecordMetric(response, expectedStatus, checkLabel, trendMetric, (res) => res.timings.duration - delayMs);
+}
+
+export function validateResponseAndRecordMetric(response, expectedStatus, expectedJsonLength, trendMetric, checkLabel) {
+    validateAndRecordMetric(response, expectedStatus, checkLabel, trendMetric, (res) => res.timings.duration, (res) => res.json('#') === expectedJsonLength);
+}
+
index a3c30d8..dae4df5 100644 (file)
@@ -25,7 +25,8 @@ import {
     TOTAL_CM_HANDLES, READ_DATA_FOR_CM_HANDLE_DELAY_MS, WRITE_DATA_FOR_CM_HANDLE_DELAY_MS,
     makeCustomSummaryReport, makeBatchOfCmHandleIds, makeRandomBatchOfAlternateIds,
     LEGACY_BATCH_THROUGHPUT_TEST_BATCH_SIZE, REGISTRATION_BATCH_SIZE,
-    KAFKA_BOOTSTRAP_SERVERS, LEGACY_BATCH_TOPIC_NAME, CONTAINER_UP_TIME_IN_SECONDS, testConfig, handleHttpResponse
+    KAFKA_BOOTSTRAP_SERVERS, LEGACY_BATCH_TOPIC_NAME, CONTAINER_UP_TIME_IN_SECONDS, testConfig, processHttpResponseWithOverheadMetrics,
+    validateResponseAndRecordMetric
 } from './common/utils.js';
 import { createCmHandles, deleteCmHandles, waitForAllCmHandlesToBeReady } from './common/cmhandle-crud.js';
 import { executeCmHandleSearch, executeCmHandleIdSearch } from './common/search-base.js';
@@ -91,94 +92,62 @@ export function teardown() {
 
 export function passthroughReadAltIdScenario() {
     const response = passthroughRead();
-    handleHttpResponse(response, 200, 'passthrough read with alternate Id status equals 200',
-        READ_DATA_FOR_CM_HANDLE_DELAY_MS, ncmpReadOverheadTrend);
+    processHttpResponseWithOverheadMetrics(response, 200, 'passthrough read with alternate Id status equals 200', READ_DATA_FOR_CM_HANDLE_DELAY_MS, ncmpReadOverheadTrend);
 }
 
 export function passthroughWriteAltIdScenario() {
     const response = passthroughWrite();
-    handleHttpResponse(response, 201, 'passthrough write with alternate Id status equals 201',
-        WRITE_DATA_FOR_CM_HANDLE_DELAY_MS, ncmpWriteOverheadTrend);
+    processHttpResponseWithOverheadMetrics(response, 201, 'passthrough write with alternate Id status equals 201', WRITE_DATA_FOR_CM_HANDLE_DELAY_MS, ncmpWriteOverheadTrend);
 }
 
 export function cmHandleIdSearchNoFilterScenario() {
     const response = executeCmHandleIdSearch('no-filter');
-    if (check(response, { 'CM handle ID no-filter search status equals 200': (response) => response.status === 200 })
-     && check(response, { 'CM handle ID no-filter search returned the correct number of ids': (response) => response.json('#') === TOTAL_CM_HANDLES })) {
-        cmHandleIdSearchNoFilterTrend.add(response.timings.duration);
-    }
+    validateResponseAndRecordMetric(response, 200, TOTAL_CM_HANDLES, cmHandleIdSearchNoFilterTrend, 'CM handle ID no-filter search');
 }
 
 export function cmHandleSearchNoFilterScenario() {
     const response = executeCmHandleSearch('no-filter');
-    if (check(response, { 'CM handle no-filter search status equals 200': (response) => response.status === 200 })
-     && check(response, { 'CM handle no-filter search returned expected CM-handles': (response) => response.json('#') === TOTAL_CM_HANDLES })) {
-        cmHandleSearchNoFilterTrend.add(response.timings.duration);
-    }
+    validateResponseAndRecordMetric(response, 200, TOTAL_CM_HANDLES, cmHandleSearchNoFilterTrend, 'CM handle no-filter search');
 }
 
 export function cmHandleIdSearchModuleScenario() {
     const response = executeCmHandleIdSearch('module');
-    if (check(response, { 'CM handle ID module search status equals 200': (response) => response.status === 200 })
-     && check(response, { 'CM handle ID module search returned the correct number of ids': (response) => response.json('#') === TOTAL_CM_HANDLES })) {
-        cmHandleIdSearchModuleFilterTrend.add(response.timings.duration);
-    }
+    validateResponseAndRecordMetric(response, 200, TOTAL_CM_HANDLES, cmHandleIdSearchModuleFilterTrend, 'CM handle ID module search');
 }
 
 export function cmHandleSearchModuleScenario() {
     const response = executeCmHandleSearch('module');
-    if (check(response, { 'CM handle module search status equals 200': (response) => response.status === 200 })
-     && check(response, { 'CM handle module search returned expected CM-handles': (response) => response.json('#') === TOTAL_CM_HANDLES })) {
-        cmHandleSearchModuleFilterTrend.add(response.timings.duration);
-    }
+    validateResponseAndRecordMetric(response, 200, TOTAL_CM_HANDLES, cmHandleSearchModuleFilterTrend, 'CM handle module search');
 }
 
 export function cmHandleIdSearchPropertyScenario() {
     const response = executeCmHandleIdSearch('property');
-    if (check(response, { 'CM handle ID property search status equals 200': (response) => response.status === 200 })
-     && check(response, { 'CM handle ID property search returned the correct number of ids': (response) => response.json('#') === TOTAL_CM_HANDLES })) {
-        cmHandleIdSearchPropertyFilterTrend.add(response.timings.duration);
-    }
+    validateResponseAndRecordMetric(response, 200, TOTAL_CM_HANDLES, cmHandleIdSearchPropertyFilterTrend, 'CM handle ID property search');
 }
 
 export function cmHandleSearchPropertyScenario() {
     const response = executeCmHandleSearch('property');
-    if (check(response, { 'CM handle property search status equals 200': (response) => response.status === 200 })
-     && check(response, { 'CM handle property search returned expected CM-handles': (response) => response.json('#') === TOTAL_CM_HANDLES })) {
-        cmHandleSearchPropertyFilterTrend.add(response.timings.duration);
-    }
+    validateResponseAndRecordMetric(response, 200, TOTAL_CM_HANDLES, cmHandleSearchPropertyFilterTrend, 'CM handle property search');
 }
 
 export function cmHandleIdSearchCpsPathScenario() {
     const response = executeCmHandleIdSearch('cps-path-for-ready-cm-handles');
-    if (check(response, { 'CM handle ID cps path search status equals 200': (response) => response.status === 200 })
-     && check(response, { 'CM handle ID cps path search returned the correct number of ids': (response) => response.json('#') === TOTAL_CM_HANDLES })) {
-        cmHandleIdSearchCpsPathFilterTrend.add(response.timings.duration);
-    }
+    validateResponseAndRecordMetric(response, 200, TOTAL_CM_HANDLES, cmHandleIdSearchCpsPathFilterTrend, 'CM handle ID cps path search');
 }
 
 export function cmHandleSearchCpsPathScenario() {
     const response = executeCmHandleSearch('cps-path-for-ready-cm-handles');
-    if (check(response, { 'CM handle cps path search status equals 200': (response) => response.status === 200 })
-     && check(response, { 'CM handle cps path search returned expected CM-handles': (response) => response.json('#') === TOTAL_CM_HANDLES })) {
-        cmHandleSearchCpsPathFilterTrend.add(response.timings.duration);
-    }
+    validateResponseAndRecordMetric(response, 200, TOTAL_CM_HANDLES, cmHandleSearchCpsPathFilterTrend, 'CM handle cps path search');
 }
 
 export function cmHandleIdSearchTrustLevelScenario() {
     const response = executeCmHandleIdSearch('trust-level');
-    if (check(response, { 'CM handle ID trust level search status equals 200': (response) => response.status === 200 })
-     && check(response, { 'CM handle ID trust level search returned the correct number of cm handle references': (response) => response.json('#') === TOTAL_CM_HANDLES })) {
-        cmHandleIdSearchTrustLevelFilterTrend.add(response.timings.duration);
-    }
+    validateResponseAndRecordMetric(response, 200, TOTAL_CM_HANDLES, cmHandleIdSearchTrustLevelFilterTrend, 'CM handle ID trust level search');
 }
 
 export function cmHandleSearchTrustLevelScenario() {
     const response = executeCmHandleSearch('trust-level');
-    if (check(response, { 'CM handle trust level search status equals 200': (response) => response.status === 200 })
-     && check(response, { 'CM handle trust level search returned expected CM-handles': (response) => response.json('#') === TOTAL_CM_HANDLES })) {
-        cmHandleSearchTrustLevelFilterTrend.add(response.timings.duration);
-    }
+    validateResponseAndRecordMetric(response, 200, TOTAL_CM_HANDLES, cmHandleSearchTrustLevelFilterTrend, 'CM handle trust level search');
 }
 
 export function legacyBatchProduceScenario() {
@@ -189,18 +158,12 @@ export function legacyBatchProduceScenario() {
 
 export function writeDataJobLargeScenario() {
     const response = executeWriteDataJob(100000);
-    if (check(response, {'large  writeDataJob response status is 200': (response) => response.status === 200})
-        && check(response, {'large  writeDataJob received expected number of responses': (response) => response.json('#') === EXPECTED_WRITE_RESPONSE_COUNT})) {
-        dcmWriteDataJobLargeTrend.add(response.timings.duration);
-    }
+    validateResponseAndRecordMetric(response, 200, EXPECTED_WRITE_RESPONSE_COUNT, dcmWriteDataJobLargeTrend, 'Large writeDataJob');
 }
 
 export function writeDataJobSmallScenario() {
     const response = executeWriteDataJob(100);
-    if (check(response, {'small writeDataJob response status is 200': (response) => response.status === 200})
-        && check(response, {'small writeDataJob received expected number of responses': (response) => response.json('#') === EXPECTED_WRITE_RESPONSE_COUNT})) {
-        dcmWriteDataJobSmallTrend.add(response.timings.duration);
-    }
+    validateResponseAndRecordMetric(response, 200, EXPECTED_WRITE_RESPONSE_COUNT, dcmWriteDataJobSmallTrend, 'Small writeDataJob');
 }
 
 export function produceAvcEventsScenario() {
index c7197b6..872bd0c 100755 (executable)
@@ -87,7 +87,7 @@ awk -v trends="$trend_declarations" '
   }
 ' "$NCMP_RUNNER_FILE" > "$TMP_FILE"
 mv "$TMP_FILE" "$NCMP_RUNNER_FILE"
-echo "✅ Trend declarations inserted."
+echo "✅ Trend declarations inserted into [$NCMP_RUNNER_FILE]"
 
 # Update thresholds in KPI config
 TMP_FILE=$(mktemp)