UNPKG

@apistudio/apim-cli

Version:

CLI for API Management Products

121 lines (120 loc) 5.28 kB
/** * Copyright IBM Corp. 2024, 2025 */ import { LogWrapper } from '../../service/log-wrapper.js'; import { filterSensitiveData, generateCSV, generatePDF, } from '../../helpers/helper.js'; export class TestExecutionReport { constructor() { this.getResponse = (execution) => { if (execution.response.stream) { return execution.response.stream.toString(); } else if (execution.response.data) { return execution.response.data; } else { return 'Response unavailable'; } }; this.formatExecution = (execution) => { try { return { id: execution.id, name: execution.itemName, url: execution.request?.endpoint, method: execution.request?.method, header: execution.request?.headers, time: execution.completedAt - execution.startedAt || 0, responseCode: { code: execution.response?.status || 408, name: execution.response?.statusText || 'Request Timed out', time: execution.response?.responseTime || 0, size: execution.response?.responseSize || 0, }, response: this.getResponse(execution), responseHeaders: execution.response.headers || null, allTests: execution.assertions?.map(({ error, actualValue, expectedValue, assertion, action, key, metadata, skipped, }) => ({ [assertion]: error ? { status: false, skipped, error, actualValue, expectedValue, action, key, metadata, } : { status: true, skipped, actualValue, expectedValue, action, key, metadata, }, })), }; } catch (error) { LogWrapper.logError('0013', `formatting execution with id ${execution.id}`, error.message); return null; } }; } getExecutionResults(executions) { try { LogWrapper.logDebug('0003', 'Formatting execution results.'); return executions .map(this.formatExecution) .filter((result) => result !== null); } catch (e) { LogWrapper.logError('0013', 'processing executions', e.message); return []; } } createFilteredSummary(collectionId, collectionName, assertionSummary, results, startedAt, completedAt, metadata) { LogWrapper.logDebug('0003', 'Creating filtered summary from execution results.'); const totalAssertions = assertionSummary.reduce((count, item) => count + (item.assertions?.length || 0), 0); const totalFailedAssertions = assertionSummary.reduce((count, item) => { return (count + item.assertions.reduce((innerCount, assertion) => { return innerCount + (assertion.error ? 1 : 0); }, 0)); }, 0); const summary = { id: collectionId, name: `${collectionName} Collection`, timestamp: completedAt, envMetadata: metadata ?? undefined, totalPass: totalAssertions - totalFailedAssertions, status: 'finished', // TODO Need to confirm the criteria for marking test as failed startedAt: startedAt, totalFail: totalFailedAssertions, totalTime: completedAt - startedAt, results: results, }; return summary; } collectReport(collectionId, collectionName, assertionSummary, executions, startedAt, completedAt, metadata) { LogWrapper.logInfo('0215', `${collectionName}`); const results = this.getExecutionResults(executions); const filteredSummary = this.createFilteredSummary(collectionId, collectionName, assertionSummary, results, startedAt, completedAt, metadata); return filteredSummary; } getReport(summary, format) { // Any environment variables, tokens, or request/response fields marked // as secret, sensitive, or under a specified key pattern (e.g., {}SECRET{}, password, token, etc.) // shall not be included in any exported report. const cleanSummary = summary.map((item) => filterSensitiveData(item)); // The report shall be exportable in PDF and CSV formats. if (format === 'CSV') { return generateCSV(cleanSummary); } else { return generatePDF(cleanSummary); } } }