Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

setNextRequest alternative implementation #8132

Draft
wants to merge 5 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions packages/insomnia-inso/src/cli.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@
'$PWD/packages/insomnia-inso/bin/inso run collection -w packages/insomnia-inso/src/examples/after-response.yml wrk_616795 --verbose',
// select request by id
'$PWD/packages/insomnia-inso/bin/inso run collection -w packages/insomnia-inso/src/examples/three-requests.yml -i req_3fd28aabbb18447abab1f45e6ee4bdc1 -i req_6063adcdab5b409e9b4f00f47322df4a',
// setNextRequest runs the next request then ends
'$PWD/packages/insomnia-inso/bin/inso run collection -w packages/insomnia-inso/src/examples/set-next-request.yml wrk_cbc89e',
// multiple --env-var overrides
'$PWD/packages/insomnia-inso/bin/inso run collection -w packages/insomnia-inso/src/examples/with-missing-env-vars.yml -i req_3fd28aabbb18447abab1f45e6ee4bdc1 --env-var firstkey=first --env-var secondkey=second',
// globals file path env overrides
Expand All @@ -56,6 +58,8 @@
'$PWD/packages/insomnia-inso/bin/inso lint spec packages/insomnia-inso/src/db/fixtures/insomnia-v4/malformed.yaml',
// after-response script and test
'$PWD/packages/insomnia-inso/bin/inso run collection -w packages/insomnia-inso/src/examples/after-response-failed-test.yml wrk_616795 --verbose',
// failing test
'$PWD/packages/insomnia-inso/bin/inso run collection -w packages/insomnia-inso/src/examples/set-next-request.yml -i req_6a0343d51ca74de7a2c73e34211354ab',
];

describe('inso dev bundle', () => {
Expand All @@ -65,7 +69,7 @@
if (result.code !== 0) {
console.log(result);
}
expect(result.code).toBe(0);

Check failure on line 72 in packages/insomnia-inso/src/cli.test.ts

View workflow job for this annotation

GitHub Actions / Test

src/cli.test.ts > inso dev bundle > exit codes are consistent > exit code should be 0: %p $PWD/packages/insomnia-inso/bin/inso run collection -w packages/insomnia-inso/src/examples/set-next-request.yml wrk_cbc89e

AssertionError: expected 1 to be +0 // Object.is equality - Expected + Received - 0 + 1 ❯ src/cli.test.ts:72:27
});
it.each(shouldReturnErrorCode)('exit code should be 1: %p', async input => {
const result = await runCliFromRoot(input);
Expand Down
85 changes: 58 additions & 27 deletions packages/insomnia-inso/src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@ import consola, { BasicReporter, FancyReporter, LogLevel, logType } from 'consol
import { cosmiconfig } from 'cosmiconfig';
import fs from 'fs';
import { JSON_ORDER_PREFIX, JSON_ORDER_SEPARATOR } from 'insomnia/src/common/constants';
import { getSendRequestCallbackMemDb } from 'insomnia/src/common/send-request';
import { getSendRequestCallbackMemDb, wrapAroundIterationOverIterationData } from 'insomnia/src/common/send-request';
import { UserUploadEnvironment } from 'insomnia/src/models/environment';
import { invariant } from 'insomnia/src/utils/invariant';
import { deserializeNDJSON } from 'insomnia/src/utils/ndjson';
import { type RequestTestResult } from 'insomnia-sdk';
import { generate, runTestsCli } from 'insomnia-testing';
Expand All @@ -23,6 +24,7 @@ import { Database, isFile, loadDb } from './db';
import { insomniaExportAdapter } from './db/adapters/insomnia-adapter';
import { loadApiSpec, promptApiSpec } from './db/models/api-spec';
import { loadEnvironment, promptEnvironment } from './db/models/environment';
import { BaseModel } from './db/models/types';
import { loadTestSuites, promptTestSuites } from './db/models/unit-test-suite';
import { matchIdIsh } from './db/models/util';
import { loadWorkspace, promptWorkspace } from './db/models/workspace';
Expand Down Expand Up @@ -534,39 +536,68 @@ export const go = (args?: string[]) => {
const iterationCount = parseInt(options.iterationCount, 10);

const iterationData = await pathToIterationData(options.iterationData, options.envVar);
const sendRequest = await getSendRequestCallbackMemDb(environment._id, db, { validateSSL: !options.disableCertValidation }, iterationData, iterationCount);
let success = true;
for (let i = 0; i < iterationCount; i++) {
const sendRequest = await getSendRequestCallbackMemDb(environment._id, db, { validateSSL: !options.disableCertValidation }, iterationCount);
let requestOrTestFailed = false;
let workflowQueue: { req: BaseModel; iteration: number; iterationData?: UserUploadEnvironment }[] = [];
// build a workflow queue of requests to run and approriate iteration data
for (let iteration = 0; iteration < iterationCount; iteration++) {
for (const req of requestsToRun) {
if (options.bail && !success) {
return;
const getCurrentRowOfIterationData = wrapAroundIterationOverIterationData(iterationData, iteration);
workflowQueue.push({ req, iteration, iterationData: getCurrentRowOfIterationData });
}
}
// loop over workflow queue and if nextrequest exists drop the queue and add it to run next with the same iteration data
// if bail is true, exit on first request or test failure
while (workflowQueue.length) {
// remove the first element in the queue
const current = workflowQueue.shift();
invariant(current, 'something went wrong with the workflow current item should be defined');
logger.log(`Running request: ${current.req.name} ${current.req._id}`);
const res = await sendRequest(current.req._id, current.iteration, current.iterationData);
if (!res) {
logger.error('Timed out while running script');
requestOrTestFailed = true;
if (options.bail) {
return process.exit(1);
}
logger.log(`Running request: ${req.name} ${req._id}`);
const res = await sendRequest(req._id, i);
if (!res) {
logger.error('Timed out while running script');
success = false;
continue;
// continue to next in workflow queue
continue;
}
// TODO: add support for skipRequest
if (res.nextRequestIdOrName) {
logger.trace(`setNextRequest(${res.nextRequestIdOrName}) found, adding to workflow queue, and removing any others`);

// TODO: add support for null input to exit 0
const nextRequest = requestsToRun.find(r => r.name === res.nextRequestIdOrName || r._id === res.nextRequestIdOrName);
if (nextRequest) {
workflowQueue = [];
// TODO: should we use the same iteration data as previous or next in list?
workflowQueue.push({ req: nextRequest, iteration: current.iteration, iterationData: current.iterationData });
}
// logger.debug(res);
const timelineString = await readFile(res.timelinePath, 'utf8');
const appendNewLineIfNeeded = (str: string) => str.endsWith('\n') ? str : str + '\n';
const timeline = deserializeNDJSON(timelineString).map(e => appendNewLineIfNeeded(e.value)).join('');
logger.trace(timeline);
if (res.testResults?.length) {
console.log(`
Test results:`);
console.log(logTestResult(options.reporter, res.testResults));
const hasFailedTests = res.testResults.some(t => t.status === 'failed');
if (hasFailedTests) {
success = false;
}
// Log timeline in --verbose
const timelineString = await readFile(res.timelinePath, 'utf8');
const appendNewLineIfNeeded = (str: string) => str.endsWith('\n') ? str : str + '\n';
const timeline = deserializeNDJSON(timelineString).map(e => appendNewLineIfNeeded(e.value)).join('');
logger.trace(timeline);
// Log test results
if (res.testResults?.length) {
console.log(`
Test results:`);
console.log(logTestResult(options.reporter, res.testResults));
const hasFailedTests = res.testResults.some(t => t.status === 'failed');
if (hasFailedTests) {
requestOrTestFailed = true;
if (options.bail) {
return process.exit(1);
}
}

await new Promise(r => setTimeout(r, parseInt(options.delayRequest, 10)));
}
// Delay between requests
await new Promise(r => setTimeout(r, parseInt(options.delayRequest, 10)));
}
return process.exit(success ? 0 : 1);
// Exit with 1 if any request or test failed
return process.exit(requestOrTestFailed ? 1 : 0);
} catch (error) {
logErrorAndExit(error);
}
Expand Down
113 changes: 113 additions & 0 deletions packages/insomnia-inso/src/examples/set-next-request.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
_type: export
__export_format: 4
__export_date: 2024-10-26T22:34:07.758Z
__export_source: insomnia.desktop.app:v10.1.1
resources:
- _id: req_5b004a6d43ef4e62a9117b9e08daa61e
parentId: wrk_cbc89ea8669648b8970ae684f9ff08b8
modified: 1729982030396
created: 1729981763331
url: localhost:4010/echo
name: setNextRequest
description: ""
method: GET
body: {}
parameters: []
headers:
- name: User-Agent
value: insomnia/10.1.1
authentication: {}
metaSortKey: -1729981822482
isPrivate: false
pathParameters: []
afterResponseScript: |+
insomnia.execution.setNextRequest("Passing Request");
settingStoreCookies: true
settingSendCookies: true
settingDisableRenderRequestBody: false
settingEncodeUrl: true
settingRebuildPath: true
settingFollowRedirects: global
_type: request
- _id: wrk_cbc89ea8669648b8970ae684f9ff08b8
parentId: null
modified: 1729981758905
created: 1729981758905
name: test next request
description: ""
scope: collection
_type: workspace
- _id: req_6a0343d51ca74de7a2c73e34211354ab
parentId: wrk_cbc89ea8669648b8970ae684f9ff08b8
modified: 1729981999436
created: 1729981846012
url: localhost:4010/echo
name: Failing Request
description: ""
method: GET
body: {}
parameters: []
headers:
- name: User-Agent
value: insomnia/10.1.1
authentication: {}
preRequestScript: |-
insomnia.test('Failing test', () => {
insomnia.expect(true).to.eql(false);
});
metaSortKey: -1729981763231
isPrivate: false
pathParameters: []
settingStoreCookies: true
settingSendCookies: true
settingDisableRenderRequestBody: false
settingEncodeUrl: true
settingRebuildPath: true
settingFollowRedirects: global
_type: request
- _id: req_704fcb5bf8e047cbaee4f8ca23a97249
parentId: wrk_cbc89ea8669648b8970ae684f9ff08b8
modified: 1729982010136
created: 1729981822432
url: localhost:4010/echo
name: Passing Request
description: ""
method: GET
body: {}
parameters: []
headers:
- name: User-Agent
value: insomnia/10.1.1
authentication: {}
preRequestScript: |-
insomnia.test('Passing test', () => {
insomnia.expect(true).to.eql(true);
});
metaSortKey: -1729981763131
isPrivate: false
pathParameters: []
settingStoreCookies: true
settingSendCookies: true
settingDisableRenderRequestBody: false
settingEncodeUrl: true
settingRebuildPath: true
settingFollowRedirects: global
_type: request
- _id: env_6d416edc2e4fd8b59c75eae67c16f3b21e1e2de2
parentId: wrk_cbc89ea8669648b8970ae684f9ff08b8
modified: 1729982036362
created: 1729981758906
name: Base Environment
data: {}
dataPropertyOrder: {}
color: null
isPrivate: false
metaSortKey: 1729981758906
_type: environment
- _id: jar_6d416edc2e4fd8b59c75eae67c16f3b21e1e2de2
parentId: wrk_cbc89ea8669648b8970ae684f9ff08b8
modified: 1729982036362
created: 1729981758906
name: Default Jar
cookies: []
_type: cookie_jar
14 changes: 7 additions & 7 deletions packages/insomnia/src/common/send-request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import { generateId } from './misc';
// The network layer uses settings from the settings model
// We want to give consumers the ability to override certain settings
type SettingsOverride = Pick<Settings, 'validateSSL'>;
const wrapAroundIterationOverIterationData = (list?: UserUploadEnvironment[], currentIteration?: number): UserUploadEnvironment | undefined => {
export const wrapAroundIterationOverIterationData = (list?: UserUploadEnvironment[], currentIteration?: number): UserUploadEnvironment | undefined => {
if (currentIteration === undefined || !Array.isArray(list) || list.length === 0) {
return undefined;
}
Expand All @@ -33,7 +33,7 @@ const wrapAroundIterationOverIterationData = (list?: UserUploadEnvironment[], cu
};
return list[(currentIteration + 1) % list.length];
};
export async function getSendRequestCallbackMemDb(environmentId: string, memDB: any, settingsOverrides?: SettingsOverride, iterationData?: UserUploadEnvironment[], iterationCount?: number) {
export async function getSendRequestCallbackMemDb(environmentId: string, memDB: any, settingsOverrides?: SettingsOverride, iterationTotal?: number) {
// Initialize the DB in-memory and fill it with data if we're given one
await database.init(
modelTypes(),
Expand Down Expand Up @@ -118,10 +118,9 @@ export async function getSendRequestCallbackMemDb(environmentId: string, memDB:
};

// Return callback helper to send requests
return async function sendRequest(requestId: string, iteration?: number) {
return async function sendRequest(requestId: string, iteration?: number, iterationDataRow?: UserUploadEnvironment) {
const requestData = await fetchInsoRequestData(requestId, environmentId);
const getCurrentRowOfIterationData = wrapAroundIterationOverIterationData(iterationData, iteration);
const mutatedContext = await tryToExecutePreRequestScript(requestData, getCurrentRowOfIterationData, iteration, iterationCount);
const mutatedContext = await tryToExecutePreRequestScript(requestData, iterationDataRow, iteration, iterationTotal);
if (mutatedContext === null) {
console.error('Time out while executing pre-request script');
return null;
Expand Down Expand Up @@ -167,7 +166,8 @@ export async function getSendRequestCallbackMemDb(environmentId: string, memDB:
const headers = headerArray?.reduce((acc, { name, value }) => ({ ...acc, [name.toLowerCase() || '']: value || '' }), []);
const bodyBuffer = await getBodyBuffer(res) as Buffer;
const data = bodyBuffer ? bodyBuffer.toString('utf8') : undefined;

return { status, statusMessage, data, headers, responseTime, timelinePath: requestData.timelinePath, testResults: postMutatedContext.requestTestResults };
// TODO: find out why requestTestResults can be undefined and eliminate the case so its always an array
const testResults = [...(mutatedContext.requestTestResults || []), ...(postMutatedContext.requestTestResults || [])];
return { status, statusMessage, data, headers, responseTime, timelinePath: requestData.timelinePath, testResults, nextRequestIdOrName: postMutatedContext?.execution?.nextRequestIdOrName };
};
}
Loading