@apistudio/apim-cli
Version:
CLI for API Management Products
121 lines (120 loc) • 5.28 kB
JavaScript
/**
* 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);
}
}
}