feat: support saving run collection result report (#9360)

This commit is contained in:
Bingbing
2025-11-12 11:04:18 +08:00
committed by GitHub
parent 511e052dcd
commit 90265ffb51
8 changed files with 1437 additions and 7 deletions

View File

@@ -1,5 +1,7 @@
import type { ExecException } from 'node:child_process';
import { exec } from 'node:child_process';
import fs from 'node:fs';
import { tmpdir } from 'node:os';
import path from 'node:path';
import { beforeAll, describe, expect, it } from 'vitest';
@@ -144,8 +146,7 @@ describe('inso dev bundle', () => {
});
it('send request with client cert and key', async () => {
const input =
`$PWD/packages/insomnia-inso/bin/inso run collection -w packages/insomnia-inso/src/db/fixtures/nedb --requestNamePattern "withCertAndCA" --verbose "Insomnia Designer" wrk_0b96eff -f $PWD/packages`;
const input = `$PWD/packages/insomnia-inso/bin/inso run collection -w packages/insomnia-inso/src/db/fixtures/nedb --requestNamePattern "withCertAndCA" --verbose "Insomnia Designer" wrk_0b96eff -f $PWD/packages`;
const result = await runCliFromRoot(input);
if (result.code !== 0) {
console.log(result);
@@ -186,6 +187,69 @@ describe('inso dev bundle', () => {
expect(result.stdout).toContain('updated value from folder: 666');
});
});
describe('run collection report generation', () => {
it.each([
{
name: 'default report',
input:
'$PWD/packages/insomnia-inso/bin/inso run collection -w packages/insomnia-inso/src/examples/run-collection-result-report.yml wrk_c5d5b5 -e env_1072af',
expectedReportFile: './fixtures/run-collection-report/default-report.json',
},
{
name: 'redact report',
input:
'$PWD/packages/insomnia-inso/bin/inso run collection -w packages/insomnia-inso/src/examples/run-collection-result-report.yml wrk_c5d5b5 -e env_1072af --includeFullData=redact --acceptRisk',
expectedReportFile: './fixtures/run-collection-report/redact-report.json',
},
{
name: 'plaintext report',
input:
'$PWD/packages/insomnia-inso/bin/inso run collection -w packages/insomnia-inso/src/examples/run-collection-result-report.yml wrk_c5d5b5 -e env_1072af --includeFullData=plaintext --acceptRisk',
expectedReportFile: './fixtures/run-collection-report/plaintext-report.json',
},
])('generate report: $name', async ({ input, expectedReportFile }) => {
const root = path.join(tmpdir(), 'insomnia-cli-test-output');
const outputFilePath = path.resolve(root, 'run-collection-report-output.json');
const result = await runCliFromRoot(`${input} --output ${outputFilePath}`);
expect(result.code).toBe(0);
const expectedReport = JSON.parse(fs.readFileSync(path.resolve(__dirname, expectedReportFile), 'utf8'));
expect(fs.existsSync(outputFilePath)).toBe(true);
const report = JSON.parse(fs.readFileSync(outputFilePath, 'utf8'));
// Some fields are dynamic so we use expect.any to validate their types/ existence
expect(report).toEqual({
...expectedReport,
executions: expectedReport.executions.map((exec: any) => ({
...exec,
response: {
...exec.response,
// executionTime can vary so just check it's a number
responseTime: expect.any(Number),
headers: exec.response.headers
? {
...exec.response.headers,
date: expect.any(String),
}
: undefined,
},
tests: exec.tests.map((test: any) => ({
...test,
executionTime: expect.any(Number),
})),
})),
timing: {
started: expect.any(Number),
completed: expect.any(Number),
responseAverage: expect.any(Number),
responseMin: expect.any(Number),
responseMax: expect.any(Number),
},
});
});
});
});
const packagedSuccessCodes = shouldReturnSuccessCode.map(x =>

View File

@@ -1,12 +1,15 @@
import fs from 'node:fs';
import { readFile } from 'node:fs/promises';
import { homedir } from 'node:os';
import path from 'node:path';
import path, { dirname } from 'node:path';
import * as commander from 'commander';
import type { logType } from 'consola';
import consola, { BasicReporter, FancyReporter, LogLevel } from 'consola';
import { cosmiconfig } from 'cosmiconfig';
// @ts-expect-error the enquirer types are incomplete https://github.com/enquirer/enquirer/pull/307
import { Confirm } from 'enquirer';
import { pick } from 'es-toolkit';
import { isDevelopment, JSON_ORDER_PREFIX, JSON_ORDER_SEPARATOR } from 'insomnia/src/common/constants';
import { getSendRequestCallbackMemDb } from 'insomnia/src/common/send-request';
import type { Environment, UserUploadEnvironment } from 'insomnia/src/models/environment';
@@ -19,10 +22,13 @@ import orderedJSON from 'json-order';
import { parseArgsStringToArgv } from 'string-argv';
import { v4 as uuidv4 } from 'uuid';
import type { Workspace } from '~/models/workspace';
import { type RequestTestResult } from '../../insomnia-scripting-environment/src/objects';
import packageJson from '../package.json';
import { exportSpecification, writeFileWithCliOptions } from './commands/export-specification';
import { getRuleSetFileFromFolderByFilename, lintSpecification } from './commands/lint-specification';
import { RunCollectionResultReport } from './commands/run-collection/result-report';
import type { Database } from './db';
import { isFile, loadDb } from './db';
import { insomniaExportAdapter } from './db/adapters/insomnia-adapter';
@@ -140,7 +146,18 @@ export const getDefaultProductName = (): string => {
const localAppDir = getAppDataDir(getDefaultProductName());
export const getAbsoluteFilePath = ({ workingDir, file }: { workingDir?: string; file: string }) => {
return file && path.resolve(workingDir || process.cwd(), file);
if (!file) {
return '';
}
if (workingDir) {
if (fs.existsSync(workingDir) && !fs.statSync(workingDir).isDirectory()) {
return path.resolve(dirname(workingDir), file);
}
return path.resolve(workingDir, file);
}
return path.resolve(process.cwd(), file);
};
export const logErrorAndExit = (err?: Error) => {
if (err instanceof InsoError) {
@@ -533,6 +550,18 @@ export const go = (args?: string[]) => {
'This allows you to control what folders Insomnia (and scripts within Insomnia) can read/write to.',
[],
)
.option('--output <file>', 'Output the results to a file in JSON format.')
.addOption(
new commander.Option(
'--includeFullData <type>',
'Include full data in the output file, including request, response, environment and etc.',
).choices(['redact', 'plaintext']),
)
.option(
'--acceptRisk',
'Accept the security warning when outputting to a file, please make sure you understand the risks.',
false,
)
.action(
async (
identifier,
@@ -552,12 +581,57 @@ export const go = (args?: string[]) => {
noProxy?: string;
reporter: TestReporter;
dataFolders: string[];
output?: string;
includeFullData?: 'redact' | 'plaintext';
acceptRisk: boolean;
},
) => {
const options = await mergeOptionsAndInit(cmd);
const pathToSearch = getWorkingDir(options);
let outputFilePath = '';
// Check if the output file is a writable file if it exists
if (options.output) {
outputFilePath = getAbsoluteFilePath({ workingDir: options.workingDir, file: options.output });
if (fs.existsSync(outputFilePath)) {
const stats = fs.statSync(outputFilePath);
if (!stats.isFile()) {
logger.fatal(`Output path "${outputFilePath}" is not a file.`);
return process.exit(1);
}
try {
fs.accessSync(outputFilePath, fs.constants.W_OK);
} catch (err) {
logger.fatal(`Output file "${outputFilePath}" is not writable.`);
return process.exit(1);
}
}
// Show security disclaimer when outputting to a file with data
if (options.includeFullData && !options.acceptRisk) {
const disclaimerMessage = [
'SECURITY WARNING',
'Outputting to a file could contain sensitive data like API tokens or secrets. Make sure you understand this, and the contents of your collection, before proceeding.',
'Are you sure you want to continue?',
].join('\n');
const acceptDisclaimer = await new Confirm({ message: disclaimerMessage, initial: false }).run();
if (!acceptDisclaimer) {
logger.fatal('User did not accept the disclaimer, aborting.');
return process.exit(1);
}
}
}
const report = new RunCollectionResultReport(
{
outputFilePath,
includeFullData: options.includeFullData,
},
logger,
);
const pathToSearch = getWorkingDir(options);
const db = await loadDb({
pathToSearch,
filterTypes: [],
@@ -569,6 +643,8 @@ export const go = (args?: string[]) => {
return process.exit(1);
}
report.update({ collection: workspace as Workspace });
// Find environment
const workspaceId = workspace._id;
// get global env by id from nedb or gitstore, or first element from file
@@ -635,6 +711,8 @@ export const go = (args?: string[]) => {
return process.exit(1);
}
report.update({ environment: environment as Environment });
let requestsToRun = getRequestsToRunFromListOrWorkspace(db, workspaceId, options.item);
if (options.requestNamePattern) {
requestsToRun = requestsToRun.filter(req => req.name.match(new RegExp(options.requestNamePattern)));
@@ -735,6 +813,13 @@ export const go = (args?: string[]) => {
noProxy: options.noProxy,
};
report.update({
proxy: proxyOptions,
iterationCount,
iterationData,
startedAt: Date.now(),
});
const sendRequest = await getSendRequestCallbackMemDb(
environment._id,
db,
@@ -762,6 +847,21 @@ export const go = (args?: string[]) => {
continue;
}
report.addExecution({
request: req,
response: {
status: res.statusMessage,
code: res.status,
headers: res.headers,
data: res.data,
responseTime: res.responseTime,
},
// TODO: Remove the category field from test results since it is not needed in the report and is always incorrect as unknown.
tests: res.testResults.map(t => pick(t, ['testCase', 'status', 'executionTime', 'errorMessage'])),
iteration: i,
success,
});
const timelineString = await readFile(res.timelinePath, 'utf8');
const appendNewLineIfNeeded = (str: string) => (str.endsWith('\n') ? str : str + '\n');
const timeline = deserializeNDJSON(timelineString)
@@ -796,8 +896,12 @@ export const go = (args?: string[]) => {
logTestResultSummary(testResultsQueue);
await report.saveReport();
return process.exit(success ? 0 : 1);
} catch (error) {
report.update({ error: (error instanceof Error ? error.message : String(error)) || 'Unknown error' });
await report.saveReport();
logErrorAndExit(error);
}
return process.exit(1);

View File

@@ -0,0 +1,282 @@
import fs from 'node:fs';
import { dirname } from 'node:path';
import type { Consola } from 'consola';
import { pick } from 'es-toolkit';
import type { Environment, UserUploadEnvironment } from '~/models/environment';
import type { Request, RequestAuthentication, RequestHeader } from '~/models/request';
import type { Workspace } from '~/models/workspace';
import { typedKeys } from '~/utils';
import type { RequestTestResult } from '../../../../insomnia-scripting-environment/src/objects';
interface RunReportExecution {
request: Request;
response: {
status?: string;
code?: number;
headers?: Record<string, string>;
data?: string;
responseTime: number;
};
tests: Omit<RequestTestResult, 'category'>[];
iteration: number;
success: boolean;
}
type ReportData = Pick<
RunCollectionResultReport,
'collection' | 'environment' | 'proxy' | 'iterationCount' | 'iterationData' | 'executions' | 'startedAt' | 'error'
>;
const insensitiveBaseModelKeys = ['_id', 'type', 'parentId', 'created', 'modified', 'name'] as const;
export class RunCollectionResultReport {
// The collection (workspace) that was run
collection: Workspace | null = null;
// The environment used during the run
environment: Environment | null = null;
// The proxy settings used during the run, if set
proxy: {
proxyEnabled: boolean;
httpProxy?: string;
httpsProxy?: string;
noProxy?: string;
} | null = null;
// The number of iterations that were run
iterationCount = 0;
// The iteration data used during the run
iterationData: UserUploadEnvironment[] = [];
// The executions that occurred during the run
executions: RunReportExecution[] = [];
// The start time of the run
startedAt: number = Date.now();
// The error that occurred during the run, if any
error: string | null = null;
constructor(
private options: {
outputFilePath: string;
includeFullData?: 'redact' | 'plaintext';
},
private logger: Consola,
init?: Partial<ReportData>,
) {
Object.assign(this, init);
}
update(partial: Partial<ReportData>) {
Object.assign(this, partial);
}
addExecution(execution: RunReportExecution) {
this.executions.push(execution);
}
private getTiming() {
const responseTimes = this.executions.map(e => e.response.responseTime);
return {
started: this.startedAt,
completed: Date.now(),
responseAverage: responseTimes.length ? responseTimes.reduce((a, b) => a + b, 0) / responseTimes.length : 0,
responseMin: Math.min(...responseTimes),
responseMax: Math.max(...responseTimes),
};
}
private getStats() {
const iterationStatusArray = new Array(this.iterationCount).fill(true);
let failedRequests = 0;
let totalTests = 0;
let failedTests = 0;
for (const exec of this.executions) {
if (exec.success === false) {
iterationStatusArray[exec.iteration] = false;
failedRequests += 1;
}
totalTests += exec.tests.length;
failedTests += exec.tests.filter(test => test.status === 'failed').length;
}
return {
iterations: {
// The total number of iterations
total: this.iterationCount,
// The number of failed iterations
failed: iterationStatusArray.filter(status => status === false).length,
},
requests: {
// The total number of requests
total: this.executions.length,
// The number of failed requests
failed: failedRequests,
},
tests: {
// The total number of tests
total: totalTests,
// The number of failed tests
failed: failedTests,
},
};
}
private getFullData() {
return {
collection: this.collection,
environment: this.environment,
proxy: this.proxy,
// Don't expose the success field
executions: this.executions.map(exec => pick(exec, ['request', 'response', 'tests', 'iteration'])),
timing: this.getTiming(),
stats: this.getStats(),
error: this.error,
};
}
private getSafeData() {
return {
collection: this.collection,
environment: this.environment ? pick(this.environment, [...insensitiveBaseModelKeys, 'isPrivate']) : null,
proxy: this.proxy,
executions: this.executions.map(exec => ({
request: pick(exec.request, [...insensitiveBaseModelKeys, 'description']),
response: pick(exec.response, ['status', 'code', 'responseTime']),
tests: exec.tests,
iteration: exec.iteration,
})),
timing: this.getTiming(),
stats: this.getStats(),
error: this.error,
};
}
private getRedactedData() {
const REDACTED_VALUE = '<Redacted by Insomnia>';
// Known sensitive header names (case-insensitive)
const sensitiveHeaders = new Set([
'cookie',
'set-cookie',
'authorization',
'auth',
'x-auth-token',
'x-api-key',
'api-key',
'x-csrf-token',
'x-xsrf-token',
'x-access-token',
'x-refresh-token',
'bearer',
'basic',
'x-forwarded-for',
'x-real-ip',
'x-client-ip',
'proxy-authorization',
]);
const redactObject = <T extends Record<string, any>>(obj: T, keysToRedact?: Set<keyof T>, ignoreCase?: boolean) => {
const redactedObject: Partial<T> = {};
for (const [key, value] of Object.entries(obj)) {
let needsRedaction = true;
if (keysToRedact) {
if (ignoreCase) {
const keysToRedactInLowerCase = new Set(Array.from(keysToRedact).map(k => k.toString().toLowerCase()));
needsRedaction = keysToRedactInLowerCase.has(key.toLowerCase());
} else {
needsRedaction = keysToRedact.has(key);
}
}
redactedObject[key as keyof T] = needsRedaction ? REDACTED_VALUE : value;
}
return redactedObject;
};
const redactRequestHeaders = (headers?: RequestHeader[]) => {
if (!headers) return headers;
return headers.map(header => ({
...header,
value: sensitiveHeaders.has(header.name?.toLowerCase()) ? REDACTED_VALUE : header.value,
}));
};
const redactResponseHeaders = (headers?: Record<string, string>) => {
if (!headers) return headers;
return redactObject(headers, sensitiveHeaders, true);
};
const redactAuth = (auth?: RequestAuthentication | {}) => {
const isValidAuth = (auth?: RequestAuthentication | {}): auth is RequestAuthentication => {
return !!auth && Object.keys(auth).length > 0 && 'type' in auth;
};
if (!isValidAuth(auth)) return auth;
const authWhitelist = new Set<string>(['type', 'disabled', 'grantType']);
return redactObject(auth, new Set(typedKeys(auth).filter(k => !authWhitelist.has(k))));
};
const redactEnvironment = (env?: Environment | null) => {
if (!env) {
return env;
}
return {
...env,
data: redactObject(env.data),
...(env.kvPairData
? {
kvPairData: env.kvPairData.map(pair => ({
...pair,
value: REDACTED_VALUE,
})),
}
: {}),
};
};
return {
collection: this.collection,
environment: redactEnvironment(this.environment),
proxy: this.proxy,
executions: this.executions.map(exec => ({
request: {
...exec.request,
headers: redactRequestHeaders(exec.request.headers),
authentication: redactAuth(exec.request.authentication),
},
response: {
...exec.response,
headers: redactResponseHeaders(exec.response.headers),
},
tests: exec.tests,
iteration: exec.iteration,
})),
timing: this.getTiming(),
stats: this.getStats(),
error: this.error,
};
}
private generateJSONReport() {
if (this.options.includeFullData === 'plaintext') {
return this.getFullData();
} else if (this.options.includeFullData === 'redact') {
return this.getRedactedData();
}
return this.getSafeData();
}
saveReport = async () => {
if (!this.options.outputFilePath) {
return;
}
const jsonReport = this.generateJSONReport();
await fs.promises.mkdir(dirname(this.options.outputFilePath), { recursive: true });
await fs.promises.writeFile(this.options.outputFilePath, JSON.stringify(jsonReport, null, 2), 'utf8');
this.logger.log('Result report saved to:', this.options.outputFilePath);
};
}

View File

@@ -0,0 +1,207 @@
type: collection.insomnia.rest/5.0
name: CLI Run Collection Report Generation
meta:
id: wrk_c5d5b5167b9049af8b53c5b6f076349b
created: 1762508027498
modified: 1762508027498
description: ''
collection:
- url: http://localhost:4010/echo
name: Request With Sensitive Headers
meta:
id: req_a7eea21417aa4af2a1c906d5e562a325
created: 1754293004564
modified: 1762509152390
isPrivate: false
description: ''
sortKey: -1754293004564
method: POST
body:
mimeType: application/json
text: |-
{
"foo": "bar"
}
parameters:
- name: queryname
value: queryvalue
disabled: false
id: pair_927facef133b4df59bb4661c9473111a
headers:
- name: Content-Type
value: application/json
id: pair_152fb2b378bc46559796642f3dd5c0d0
- name: User-Agent
value: insomnia/11.4.0
id: pair_8e55f5504c924f798bf31f1db38412e3
- name: X-Header
value: foo
id: pair_714ac3cf4ca645b38755a8b318c28b91
- id: pair_aa7cdfd5c82d4f4cb7737ad41e357220
name: Authorization
value: <sensitive>
- id: pair_32ad47361f6c40bcb3f84273b1b4ce50
name: Auth
value: <sensitive>
- id: pair_5dd2022670e04b6ea959ac75ba5ead74
name: Cookie
value: <sensitive>
- id: pair_245205652e154b30a0ab241c13e61b99
name: x-auth-token
value: <sensitive>
- id: pair_3e019947ced7433f90dde5c4f69af8d5
name: x-api-key
value: <sensitive>
authentication:
type: digest
disabled: false
username: ''
password: ''
scripts:
preRequest: |
const track = +(new Date());
insomnia.environment.set("track", track);
insomnia.test('Check if track exists', () => {
insomnia.expect(insomnia.environment.get("track")).to.eql(track);
});
afterResponse: |
insomnia.test('Check if status is 200', () => {
insomnia.expect(insomnia.response.code).to.eql(200);
});
insomnia.test('Check if track exists', () => {
insomnia.expect(insomnia.environment.get("track")).to.not.null;
});
settings:
renderRequestBody: true
encodeUrl: true
followRedirects: global
cookies:
send: true
store: true
rebuildPath: true
- url: http://localhost:4010/echo
name: Request With Auth - Basic
meta:
id: req_bb9e621055204a13bc9e301fe376748f
created: 1754297299654
modified: 1762509360358
isPrivate: false
description: ''
sortKey: -1754297299654
method: POST
body:
mimeType: multipart/form-data
params:
- id: pair_e6f1d8dad6f1413497c3b2fc4a1c7fc5
name: key
value: value
description: ''
disabled: false
parameters:
- name: from-r1
value: "{% response 'body', 'req_a7eea21417aa4af2a1c906d5e562a325',
'b64::JC5kYXRh::46b', 'no-history', 60 %}"
disabled: false
id: pair_c5d5d7ca8e89414ab14cff81370b45d7
headers:
- name: Content-Type
value: multipart/form-data
- name: User-Agent
value: insomnia/11.4.0
authentication:
type: basic
useISO88591: false
disabled: false
username: username
password: password
scripts:
preRequest: |
insomnia.test('Check if track exists', () => {
insomnia.expect(insomnia.environment.get("track")).to.not.null;
});
settings:
renderRequestBody: true
encodeUrl: true
followRedirects: global
cookies:
send: true
store: true
rebuildPath: true
- url: http://localhost:4010/echo
name: Request With Auth - API Key
meta:
id: req_ef809d54e6aa4d72a54593fdeaa14b93
created: 1754468596942
modified: 1762509338246
isPrivate: false
description: ''
sortKey: -1754468596942
method: GET
body:
mimeType: multipart/form-data
parameters:
- id: pair_cd383e4bd88a42389b0f8531a1b699ae
name: ''
value: ''
description: ''
disabled: false
headers:
- name: Content-Type
value: multipart/form-data
- name: User-Agent
value: insomnia/11.4.0
authentication:
type: apikey
disabled: false
key: api-key
value: api-key-value
addTo: header
scripts:
preRequest: |
insomnia.expect(200).to.eql(200);
afterResponse: |+
insomnia.test('Check if status is 200', () => {
insomnia.expect(insomnia.response.code).to.eql(200);
});
settings:
renderRequestBody: true
encodeUrl: true
followRedirects: global
cookies:
send: true
store: true
rebuildPath: true
cookieJar:
name: Default Jar
meta:
id: jar_64efeac28c0b43c79fa15bf19a98e7a6
created: 1754292952909
modified: 1762509361179
environments:
name: Base Environment
meta:
id: env_6aec164bc7c64ffc96c62e06517c5ba6
created: 1754292952907
modified: 1762509361181
isPrivate: false
data:
env-name: env-value
subEnvironments:
- name: New Environment
meta:
id: env_1072af0c15764cd8875f645b0f2e5ad4
created: 1754293170062
modified: 1762509361180
isPrivate: true
sortKey: 1754293170062
data:
pri-env-name: env-value
track: 1762509003311
track-3: 1762508357019

View File

@@ -0,0 +1,135 @@
{
"collection": {
"_id": "wrk_c5d5b5167b9049af8b53c5b6f076349b",
"created": 1762508027498,
"modified": 1762508027498,
"isPrivate": false,
"description": "",
"metaSortKey": 0,
"type": "Workspace",
"name": "CLI Run Collection Report Generation",
"parentId": "",
"scope": "collection"
},
"environment": {
"_id": "env_1072af0c15764cd8875f645b0f2e5ad4",
"type": "Environment",
"parentId": "env_6aec164bc7c64ffc96c62e06517c5ba6",
"created": 1754293170062,
"modified": 1762509361180,
"name": "New Environment",
"isPrivate": true
},
"proxy": {
"proxyEnabled": false,
"httpProxy": "",
"httpsProxy": "",
"noProxy": ""
},
"executions": [
{
"request": {
"_id": "req_ef809d54e6aa4d72a54593fdeaa14b93",
"type": "Request",
"parentId": "wrk_c5d5b5167b9049af8b53c5b6f076349b",
"created": 1754468596942,
"modified": 1762509338246,
"name": "Request With Auth - API Key",
"description": ""
},
"response": {
"status": "OK",
"code": 200,
"responseTime": 4.039
},
"tests": [
{
"testCase": "Check if status is 200",
"status": "passed",
"executionTime": 0.11487499999998363
}
],
"iteration": 0
},
{
"request": {
"_id": "req_bb9e621055204a13bc9e301fe376748f",
"type": "Request",
"parentId": "wrk_c5d5b5167b9049af8b53c5b6f076349b",
"created": 1754297299654,
"modified": 1762509360358,
"name": "Request With Auth - Basic",
"description": ""
},
"response": {
"status": "OK",
"code": 200,
"responseTime": 0.596
},
"tests": [
{
"testCase": "Check if track exists",
"status": "passed",
"executionTime": 0.03712500000006003
}
],
"iteration": 0
},
{
"request": {
"_id": "req_a7eea21417aa4af2a1c906d5e562a325",
"type": "Request",
"parentId": "wrk_c5d5b5167b9049af8b53c5b6f076349b",
"created": 1754293004564,
"modified": 1762509152390,
"name": "Request With Sensitive Headers",
"description": ""
},
"response": {
"status": "OK",
"code": 200,
"responseTime": 0.404
},
"tests": [
{
"testCase": "Check if track exists",
"status": "passed",
"executionTime": 0.020999999999958163
},
{
"testCase": "Check if status is 200",
"status": "passed",
"executionTime": 0.04466699999989032
},
{
"testCase": "Check if track exists",
"status": "passed",
"executionTime": 0.0225830000000542
}
],
"iteration": 0
}
],
"timing": {
"started": 1762755820632,
"completed": 1762755820961,
"responseAverage": 1.6796666666666666,
"responseMin": 0.404,
"responseMax": 4.039
},
"stats": {
"iterations": {
"total": 1,
"failed": 0
},
"requests": {
"total": 3,
"failed": 0
},
"tests": {
"total": 5,
"failed": 0
}
},
"error": null
}

View File

@@ -0,0 +1,319 @@
{
"collection": {
"_id": "wrk_c5d5b5167b9049af8b53c5b6f076349b",
"created": 1762508027498,
"modified": 1762508027498,
"isPrivate": false,
"description": "",
"metaSortKey": 0,
"type": "Workspace",
"name": "CLI Run Collection Report Generation",
"parentId": "",
"scope": "collection"
},
"environment": {
"_id": "env_1072af0c15764cd8875f645b0f2e5ad4",
"created": 1754293170062,
"modified": 1762509361180,
"isPrivate": true,
"description": "",
"metaSortKey": 1754293170062,
"type": "Environment",
"color": null,
"data": {
"pri-env-name": "env-value",
"track": 1762509003311,
"track-3": 1762508357019
},
"name": "New Environment",
"parentId": "env_6aec164bc7c64ffc96c62e06517c5ba6"
},
"proxy": {
"proxyEnabled": false,
"httpProxy": "",
"httpsProxy": "",
"noProxy": ""
},
"executions": [
{
"request": {
"_id": "req_ef809d54e6aa4d72a54593fdeaa14b93",
"created": 1754468596942,
"modified": 1762509338246,
"isPrivate": false,
"description": "",
"metaSortKey": -1754468596942,
"type": "Request",
"name": "Request With Auth - API Key",
"parentId": "wrk_c5d5b5167b9049af8b53c5b6f076349b",
"url": "http://localhost:4010/echo",
"method": "GET",
"body": {
"mimeType": "multipart/form-data"
},
"parameters": [],
"headers": [
{
"name": "Content-Type",
"value": "multipart/form-data"
},
{
"name": "User-Agent",
"value": "insomnia/11.4.0"
}
],
"authentication": {
"type": "apikey",
"key": "api-key",
"value": "api-key-value",
"disabled": false,
"addTo": "header"
},
"preRequestScript": "\ninsomnia.expect(200).to.eql(200);\n",
"settingDisableRenderRequestBody": false,
"settingEncodeUrl": true,
"settingFollowRedirects": "global",
"settingSendCookies": true,
"settingStoreCookies": true,
"settingRebuildPath": true,
"afterResponseScript": "\ninsomnia.test('Check if status is 200', () => {\n insomnia.expect(insomnia.response.code).to.eql(200);\n});\n\n",
"pathParameters": []
},
"response": {
"status": "OK",
"code": 200,
"headers": {
"x-powered-by": "Express",
"content-type": "application/json; charset=utf-8",
"content-length": "263",
"etag": "W/\"107-4RvANbiPgVs3QIOYml7CD9b1VQY\"",
"date": "Mon, 10 Nov 2025 06:23:42 GMT",
"connection": "keep-alive",
"keep-alive": "timeout=5"
},
"data": "{\"method\":\"GET\",\"headers\":{\"host\":\"localhost:4010\",\"content-type\":\"multipart/form-data; boundary=X-INSOMNIA-BOUNDARY\",\"user-agent\":\"insomnia/11.4.0\",\"api-key\":\"api-key-value\",\"accept\":\"*/*\",\"content-length\":\"25\"},\"data\":\"--X-INSOMNIA-BOUNDARY--\\r\\n\",\"cookies\":{}}",
"responseTime": 3.766
},
"tests": [
{
"testCase": "Check if status is 200",
"status": "passed",
"executionTime": 0.08066600000006474
}
],
"iteration": 0
},
{
"request": {
"_id": "req_bb9e621055204a13bc9e301fe376748f",
"created": 1754297299654,
"modified": 1762509360358,
"isPrivate": false,
"description": "",
"metaSortKey": -1754297299654,
"type": "Request",
"name": "Request With Auth - Basic",
"parentId": "wrk_c5d5b5167b9049af8b53c5b6f076349b",
"url": "http://localhost:4010/echo",
"method": "POST",
"body": {
"mimeType": "multipart/form-data",
"params": [
{
"name": "key",
"value": "value",
"description": "",
"disabled": false
}
]
},
"parameters": [
{
"name": "from-r1",
"value": "{% response 'body', 'req_a7eea21417aa4af2a1c906d5e562a325', 'b64::JC5kYXRh::46b', 'no-history', 60 %}",
"disabled": false
}
],
"headers": [
{
"name": "Content-Type",
"value": "multipart/form-data"
},
{
"name": "User-Agent",
"value": "insomnia/11.4.0"
}
],
"authentication": {
"type": "basic",
"useISO88591": false,
"username": "username",
"password": "password",
"disabled": false
},
"preRequestScript": "insomnia.test('Check if track exists', () => {\n insomnia.expect(insomnia.environment.get(\"track\")).to.not.null;\n});\n",
"settingDisableRenderRequestBody": false,
"settingEncodeUrl": true,
"settingFollowRedirects": "global",
"settingSendCookies": true,
"settingStoreCookies": true,
"settingRebuildPath": true,
"afterResponseScript": "",
"pathParameters": []
},
"response": {
"status": "OK",
"code": 200,
"headers": {
"x-powered-by": "Express",
"content-type": "application/json; charset=utf-8",
"content-length": "374",
"etag": "W/\"176-/xZqOqpzEjCbbk72Hk4zWm66rXM\"",
"date": "Mon, 10 Nov 2025 06:23:42 GMT",
"connection": "keep-alive",
"keep-alive": "timeout=5"
},
"data": "{\"method\":\"POST\",\"headers\":{\"host\":\"localhost:4010\",\"content-type\":\"multipart/form-data; boundary=X-INSOMNIA-BOUNDARY\",\"user-agent\":\"insomnia/11.4.0\",\"authorization\":\"Basic dXNlcm5hbWU6cGFzc3dvcmQ=\",\"accept\":\"*/*\",\"content-length\":\"101\"},\"data\":\"--X-INSOMNIA-BOUNDARY\\r\\nContent-Disposition: form-data; name=\\\"key\\\"\\r\\n\\r\\nvalue\\r\\n--X-INSOMNIA-BOUNDARY--\\r\\n\",\"cookies\":{}}",
"responseTime": 0.426
},
"tests": [
{
"testCase": "Check if track exists",
"status": "passed",
"executionTime": 0.03374999999994088
}
],
"iteration": 0
},
{
"request": {
"_id": "req_a7eea21417aa4af2a1c906d5e562a325",
"created": 1754293004564,
"modified": 1762509152390,
"isPrivate": false,
"description": "",
"metaSortKey": -1754293004564,
"type": "Request",
"name": "Request With Sensitive Headers",
"parentId": "wrk_c5d5b5167b9049af8b53c5b6f076349b",
"url": "http://localhost:4010/echo",
"method": "POST",
"body": {
"mimeType": "application/json",
"text": "{\n\t\"foo\": \"bar\"\n}"
},
"parameters": [
{
"name": "queryname",
"value": "queryvalue",
"disabled": false
}
],
"headers": [
{
"name": "Content-Type",
"value": "application/json"
},
{
"name": "User-Agent",
"value": "insomnia/11.4.0"
},
{
"name": "X-Header",
"value": "foo"
},
{
"name": "Authorization",
"value": "<sensitive>"
},
{
"name": "Auth",
"value": "<sensitive>"
},
{
"name": "Cookie",
"value": "<sensitive>"
},
{
"name": "x-auth-token",
"value": "<sensitive>"
},
{
"name": "x-api-key",
"value": "<sensitive>"
}
],
"authentication": {
"type": "digest",
"disabled": false,
"username": "",
"password": ""
},
"preRequestScript": "const track = +(new Date());\n\ninsomnia.environment.set(\"track\", track);\n\ninsomnia.test('Check if track exists', () => {\n insomnia.expect(insomnia.environment.get(\"track\")).to.eql(track);\n});\n",
"settingDisableRenderRequestBody": false,
"settingEncodeUrl": true,
"settingFollowRedirects": "global",
"settingSendCookies": true,
"settingStoreCookies": true,
"settingRebuildPath": true,
"afterResponseScript": "\ninsomnia.test('Check if status is 200', () => {\n insomnia.expect(insomnia.response.code).to.eql(200);\n});\n\ninsomnia.test('Check if track exists', () => {\n insomnia.expect(insomnia.environment.get(\"track\")).to.not.null;\n});\n",
"pathParameters": []
},
"response": {
"status": "OK",
"code": 200,
"headers": {
"x-powered-by": "Express",
"content-type": "application/json; charset=utf-8",
"content-length": "348",
"etag": "W/\"15c-nysNeZeBrFfZqNEw3jU0n6zGw2w\"",
"date": "Mon, 10 Nov 2025 06:23:42 GMT",
"connection": "keep-alive",
"keep-alive": "timeout=5"
},
"data": "{\"method\":\"POST\",\"headers\":{\"host\":\"localhost:4010\",\"auth\":\"<sensitive>\",\"authorization\":\"<sensitive>\",\"content-type\":\"application/json\",\"cookie\":\"<sensitive>\",\"user-agent\":\"insomnia/11.4.0\",\"x-api-key\":\"<sensitive>\",\"x-auth-token\":\"<sensitive>\",\"x-header\":\"foo\",\"accept\":\"*/*\",\"content-length\":\"17\"},\"data\":\"{\\n\\t\\\"foo\\\": \\\"bar\\\"\\n}\",\"cookies\":{}}",
"responseTime": 0.382
},
"tests": [
{
"testCase": "Check if track exists",
"status": "passed",
"executionTime": 0.023083999999926164
},
{
"testCase": "Check if status is 200",
"status": "passed",
"executionTime": 0.03708299999993869
},
{
"testCase": "Check if track exists",
"status": "passed",
"executionTime": 0.01999999999998181
}
],
"iteration": 0
}
],
"timing": {
"started": 1762755822497,
"completed": 1762755822776,
"responseAverage": 1.5246666666666666,
"responseMin": 0.382,
"responseMax": 3.766
},
"stats": {
"iterations": {
"total": 1,
"failed": 0
},
"requests": {
"total": 3,
"failed": 0
},
"tests": {
"total": 5,
"failed": 0
}
},
"error": null
}

View File

@@ -0,0 +1,319 @@
{
"collection": {
"_id": "wrk_c5d5b5167b9049af8b53c5b6f076349b",
"created": 1762508027498,
"modified": 1762508027498,
"isPrivate": false,
"description": "",
"metaSortKey": 0,
"type": "Workspace",
"name": "CLI Run Collection Report Generation",
"parentId": "",
"scope": "collection"
},
"environment": {
"_id": "env_1072af0c15764cd8875f645b0f2e5ad4",
"created": 1754293170062,
"modified": 1762509361180,
"isPrivate": true,
"description": "",
"metaSortKey": 1754293170062,
"type": "Environment",
"color": null,
"name": "New Environment",
"parentId": "env_6aec164bc7c64ffc96c62e06517c5ba6",
"data": {
"pri-env-name": "<Redacted by Insomnia>",
"track": "<Redacted by Insomnia>",
"track-3": "<Redacted by Insomnia>"
}
},
"proxy": {
"proxyEnabled": false,
"httpProxy": "",
"httpsProxy": "",
"noProxy": ""
},
"executions": [
{
"request": {
"_id": "req_ef809d54e6aa4d72a54593fdeaa14b93",
"created": 1754468596942,
"modified": 1762509338246,
"isPrivate": false,
"description": "",
"metaSortKey": -1754468596942,
"type": "Request",
"name": "Request With Auth - API Key",
"parentId": "wrk_c5d5b5167b9049af8b53c5b6f076349b",
"url": "http://localhost:4010/echo",
"method": "GET",
"body": {
"mimeType": "multipart/form-data"
},
"parameters": [],
"headers": [
{
"name": "Content-Type",
"value": "multipart/form-data"
},
{
"name": "User-Agent",
"value": "insomnia/11.4.0"
}
],
"authentication": {
"type": "apikey",
"key": "<Redacted by Insomnia>",
"value": "<Redacted by Insomnia>",
"disabled": false,
"addTo": "<Redacted by Insomnia>"
},
"preRequestScript": "\ninsomnia.expect(200).to.eql(200);\n",
"settingDisableRenderRequestBody": false,
"settingEncodeUrl": true,
"settingFollowRedirects": "global",
"settingSendCookies": true,
"settingStoreCookies": true,
"settingRebuildPath": true,
"afterResponseScript": "\ninsomnia.test('Check if status is 200', () => {\n insomnia.expect(insomnia.response.code).to.eql(200);\n});\n\n",
"pathParameters": []
},
"response": {
"status": "OK",
"code": 200,
"headers": {
"x-powered-by": "Express",
"content-type": "application/json; charset=utf-8",
"content-length": "263",
"etag": "W/\"107-4RvANbiPgVs3QIOYml7CD9b1VQY\"",
"date": "Mon, 10 Nov 2025 06:22:52 GMT",
"connection": "keep-alive",
"keep-alive": "timeout=5"
},
"data": "{\"method\":\"GET\",\"headers\":{\"host\":\"localhost:4010\",\"content-type\":\"multipart/form-data; boundary=X-INSOMNIA-BOUNDARY\",\"user-agent\":\"insomnia/11.4.0\",\"api-key\":\"api-key-value\",\"accept\":\"*/*\",\"content-length\":\"25\"},\"data\":\"--X-INSOMNIA-BOUNDARY--\\r\\n\",\"cookies\":{}}",
"responseTime": 6.6080000000000005
},
"tests": [
{
"testCase": "Check if status is 200",
"status": "passed",
"executionTime": 0.10637500000007094
}
],
"iteration": 0
},
{
"request": {
"_id": "req_bb9e621055204a13bc9e301fe376748f",
"created": 1754297299654,
"modified": 1762509360358,
"isPrivate": false,
"description": "",
"metaSortKey": -1754297299654,
"type": "Request",
"name": "Request With Auth - Basic",
"parentId": "wrk_c5d5b5167b9049af8b53c5b6f076349b",
"url": "http://localhost:4010/echo",
"method": "POST",
"body": {
"mimeType": "multipart/form-data",
"params": [
{
"name": "key",
"value": "value",
"description": "",
"disabled": false
}
]
},
"parameters": [
{
"name": "from-r1",
"value": "{% response 'body', 'req_a7eea21417aa4af2a1c906d5e562a325', 'b64::JC5kYXRh::46b', 'no-history', 60 %}",
"disabled": false
}
],
"headers": [
{
"name": "Content-Type",
"value": "multipart/form-data"
},
{
"name": "User-Agent",
"value": "insomnia/11.4.0"
}
],
"authentication": {
"type": "basic",
"useISO88591": "<Redacted by Insomnia>",
"username": "<Redacted by Insomnia>",
"password": "<Redacted by Insomnia>",
"disabled": false
},
"preRequestScript": "insomnia.test('Check if track exists', () => {\n insomnia.expect(insomnia.environment.get(\"track\")).to.not.null;\n});\n",
"settingDisableRenderRequestBody": false,
"settingEncodeUrl": true,
"settingFollowRedirects": "global",
"settingSendCookies": true,
"settingStoreCookies": true,
"settingRebuildPath": true,
"afterResponseScript": "",
"pathParameters": []
},
"response": {
"status": "OK",
"code": 200,
"headers": {
"x-powered-by": "Express",
"content-type": "application/json; charset=utf-8",
"content-length": "374",
"etag": "W/\"176-/xZqOqpzEjCbbk72Hk4zWm66rXM\"",
"date": "Mon, 10 Nov 2025 06:22:52 GMT",
"connection": "keep-alive",
"keep-alive": "timeout=5"
},
"data": "{\"method\":\"POST\",\"headers\":{\"host\":\"localhost:4010\",\"content-type\":\"multipart/form-data; boundary=X-INSOMNIA-BOUNDARY\",\"user-agent\":\"insomnia/11.4.0\",\"authorization\":\"Basic dXNlcm5hbWU6cGFzc3dvcmQ=\",\"accept\":\"*/*\",\"content-length\":\"101\"},\"data\":\"--X-INSOMNIA-BOUNDARY\\r\\nContent-Disposition: form-data; name=\\\"key\\\"\\r\\n\\r\\nvalue\\r\\n--X-INSOMNIA-BOUNDARY--\\r\\n\",\"cookies\":{}}",
"responseTime": 0.88
},
"tests": [
{
"testCase": "Check if track exists",
"status": "passed",
"executionTime": 0.035499999999956344
}
],
"iteration": 0
},
{
"request": {
"_id": "req_a7eea21417aa4af2a1c906d5e562a325",
"created": 1754293004564,
"modified": 1762509152390,
"isPrivate": false,
"description": "",
"metaSortKey": -1754293004564,
"type": "Request",
"name": "Request With Sensitive Headers",
"parentId": "wrk_c5d5b5167b9049af8b53c5b6f076349b",
"url": "http://localhost:4010/echo",
"method": "POST",
"body": {
"mimeType": "application/json",
"text": "{\n\t\"foo\": \"bar\"\n}"
},
"parameters": [
{
"name": "queryname",
"value": "queryvalue",
"disabled": false
}
],
"headers": [
{
"name": "Content-Type",
"value": "application/json"
},
{
"name": "User-Agent",
"value": "insomnia/11.4.0"
},
{
"name": "X-Header",
"value": "foo"
},
{
"name": "Authorization",
"value": "<Redacted by Insomnia>"
},
{
"name": "Auth",
"value": "<Redacted by Insomnia>"
},
{
"name": "Cookie",
"value": "<Redacted by Insomnia>"
},
{
"name": "x-auth-token",
"value": "<Redacted by Insomnia>"
},
{
"name": "x-api-key",
"value": "<Redacted by Insomnia>"
}
],
"authentication": {
"type": "digest",
"disabled": false,
"username": "<Redacted by Insomnia>",
"password": "<Redacted by Insomnia>"
},
"preRequestScript": "const track = +(new Date());\n\ninsomnia.environment.set(\"track\", track);\n\ninsomnia.test('Check if track exists', () => {\n insomnia.expect(insomnia.environment.get(\"track\")).to.eql(track);\n});\n",
"settingDisableRenderRequestBody": false,
"settingEncodeUrl": true,
"settingFollowRedirects": "global",
"settingSendCookies": true,
"settingStoreCookies": true,
"settingRebuildPath": true,
"afterResponseScript": "\ninsomnia.test('Check if status is 200', () => {\n insomnia.expect(insomnia.response.code).to.eql(200);\n});\n\ninsomnia.test('Check if track exists', () => {\n insomnia.expect(insomnia.environment.get(\"track\")).to.not.null;\n});\n",
"pathParameters": []
},
"response": {
"status": "OK",
"code": 200,
"headers": {
"x-powered-by": "Express",
"content-type": "application/json; charset=utf-8",
"content-length": "348",
"etag": "W/\"15c-nysNeZeBrFfZqNEw3jU0n6zGw2w\"",
"date": "Mon, 10 Nov 2025 06:22:52 GMT",
"connection": "keep-alive",
"keep-alive": "timeout=5"
},
"data": "{\"method\":\"POST\",\"headers\":{\"host\":\"localhost:4010\",\"auth\":\"<sensitive>\",\"authorization\":\"<sensitive>\",\"content-type\":\"application/json\",\"cookie\":\"<sensitive>\",\"user-agent\":\"insomnia/11.4.0\",\"x-api-key\":\"<sensitive>\",\"x-auth-token\":\"<sensitive>\",\"x-header\":\"foo\",\"accept\":\"*/*\",\"content-length\":\"17\"},\"data\":\"{\\n\\t\\\"foo\\\": \\\"bar\\\"\\n}\",\"cookies\":{}}",
"responseTime": 0.575
},
"tests": [
{
"testCase": "Check if track exists",
"status": "passed",
"executionTime": 0.027999999999792635
},
{
"testCase": "Check if status is 200",
"status": "passed",
"executionTime": 0.03275000000007822
},
{
"testCase": "Check if track exists",
"status": "passed",
"executionTime": 0.018499999999903594
}
],
"iteration": 0
}
],
"timing": {
"started": 1762755772438,
"completed": 1762755772769,
"responseAverage": 2.687666666666667,
"responseMin": 0.575,
"responseMax": 6.6080000000000005
},
"stats": {
"iterations": {
"total": 1,
"failed": 0
},
"requests": {
"total": 3,
"failed": 0
},
"tests": {
"total": 5,
"failed": 0
}
},
"error": null
}

View File

@@ -130,9 +130,9 @@ export async function getSendRequestCallbackMemDb(
}
const { statusCode: status, statusMessage, headers: headerArray, elapsedTime: responseTime } = res;
const headers = headerArray?.reduce(
const headers = headerArray?.reduce<Record<string, string>>(
(acc, { name, value }) => ({ ...acc, [name.toLowerCase() || '']: value || '' }),
[],
{},
);
const bodyBuffer = (await getBodyBuffer(res)) as Buffer;
const data = bodyBuffer ? bodyBuffer.toString('utf8') : undefined;