-
Notifications
You must be signed in to change notification settings - Fork 2.2k
feat: inso collection runner #7700
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -15,6 +15,7 @@ import { loadDb } from './db'; | |
| import { loadApiSpec, promptApiSpec } from './db/models/api-spec'; | ||
| import { loadEnvironment, promptEnvironment } from './db/models/environment'; | ||
| import { loadTestSuites, promptTestSuites } from './db/models/unit-test-suite'; | ||
| import { loadWorkspace, promptWorkspace } from './db/models/workspace'; | ||
|
|
||
| export interface GlobalOptions { | ||
| ci: boolean; | ||
|
|
@@ -295,6 +296,100 @@ export const go = (args?: string[]) => { | |
| return process.exit(1); | ||
| }); | ||
|
|
||
| run.command('collection [identifier]') | ||
| .description('Run Insomnia request collection, identifier can be a workspace id') | ||
| .option('-t, --requestNamePattern <regex>', 'run requests that match the regex', '') | ||
| .option('-e, --env <identifier>', 'environment to use', '') | ||
| .option('-b, --bail', 'abort ("bail") after first test failure', false) | ||
| .option('--disableCertValidation', 'disable certificate validation for requests with SSL', false) | ||
| .action(async (identifier, cmd: { env: string; disableCertValidation: true; requestNamePattern: string; bail: boolean }) => { | ||
| const globals: { config: string; workingDir: string; exportFile: string; ci: boolean; printOptions: boolean; verbose: boolean } = program.optsWithGlobals(); | ||
|
|
||
| const commandOptions = { ...globals, ...cmd }; | ||
| const __configFile = await loadCosmiConfig(commandOptions.config, commandOptions.workingDir); | ||
|
|
||
| const options = { | ||
| reporter: defaultReporter, | ||
| ...__configFile?.options || {}, | ||
| ...commandOptions, | ||
| ...(__configFile ? { __configFile } : {}), | ||
| }; | ||
| logger.level = options.verbose ? LogLevel.Verbose : LogLevel.Info; | ||
| options.ci && logger.setReporters([new BasicReporter()]); | ||
| options.printOptions && logger.log('Loaded options', options, '\n'); | ||
| let pathToSearch = ''; | ||
| const useLocalAppData = !options.workingDir && !options.exportFile; | ||
| if (useLocalAppData) { | ||
| logger.warn('No working directory or export file provided, using local app data directory.'); | ||
| pathToSearch = localAppDir; | ||
| } else { | ||
| pathToSearch = path.resolve(options.workingDir || process.cwd(), options.exportFile || ''); | ||
| } | ||
| if (options.reporter && !reporterTypesSet.has(options.reporter)) { | ||
| logger.fatal(`Reporter "${options.reporter}" not unrecognized. Options are [${reporterTypes.join(', ')}].`); | ||
| return process.exit(1); | ||
| } | ||
|
|
||
| const db = await loadDb({ | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Probably we could be a bit defensive here and check
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree the db load logic is over complex I'm thinking of a better way to abstract it. Will follow up with a pr soon |
||
| pathToSearch, | ||
| filterTypes: [], | ||
| }); | ||
|
|
||
| const workspace = identifier ? loadWorkspace(db, identifier) : await promptWorkspace(db, !!options.ci); | ||
|
|
||
| if (!workspace) { | ||
| logger.fatal('No workspace found; cannot run requests.', identifier); | ||
| return process.exit(1); | ||
| } | ||
|
|
||
| // Find environment | ||
| const workspaceId = workspace._id; | ||
| const environment = options.env ? loadEnvironment(db, workspaceId, options.env) : await promptEnvironment(db, !!options.ci, workspaceId); | ||
|
|
||
| if (!environment) { | ||
| logger.fatal('No environment identified; cannot run requests without a valid environment.'); | ||
| return process.exit(1); | ||
| } | ||
|
|
||
| const getRequestGroupIdsRecursively = (from: string[]): string[] => { | ||
| const parentIds = db.RequestGroup.filter(rg => from.includes(rg.parentId)).map(rg => rg._id); | ||
| return [...parentIds, ...(parentIds.length > 0 ? getRequestGroupIdsRecursively(parentIds) : [])]; | ||
| }; | ||
| const allRequestGroupIds = getRequestGroupIdsRecursively([workspaceId]); | ||
| let requests = db.Request.filter(req => [workspaceId, ...allRequestGroupIds].includes(req.parentId)); | ||
|
|
||
| if (options.requestNamePattern) { | ||
| requests = requests.filter(req => req.name.match(new RegExp(options.requestNamePattern))); | ||
Check warningCode scanning / Semgrep OSS Semgrep Finding: javascript.lang.security.audit.detect-non-literal-regexp.detect-non-literal-regexp
RegExp() called with a `cmd` function argument, this might allow an attacker to cause a Regular Expression Denial-of-Service (ReDoS) within your application as RegExP blocks the main thread. For this reason, it is recommended to use hardcoded regexes instead. If your regex is run on user-controlled input, consider performing input validation or use a regex checking/sanitization library such as https://www.npmjs.com/package/recheck to verify that the regex does not appear vulnerable to ReDoS.
|
||
| } | ||
| if (!requests.length) { | ||
| logger.fatal('No requests identified; nothing to run.'); | ||
| return process.exit(1); | ||
| } | ||
|
|
||
| try { | ||
| // eslint-disable-next-line @typescript-eslint/no-var-requires -- Load lazily when needed, otherwise this require slows down the entire CLI. | ||
| const { getSendRequestCallbackMemDb } = require('insomnia-send-request'); | ||
| const sendRequest = await getSendRequestCallbackMemDb(environment._id, db, { validateSSL: !options.disableCertValidation }); | ||
| let success = true; | ||
| for (const req of requests) { | ||
| if (options.bail && !success) { | ||
| return; | ||
| } | ||
| logger.log(`Running request: ${req.name} ${req._id}`); | ||
| const res = await sendRequest(req._id); | ||
| logger.trace(res); | ||
| if (res.status !== 200) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: Just a todo item that we might check test results to decide if it is failed in the future. |
||
| success = false; | ||
| logger.error(`Request failed with status ${res.status}`); | ||
| } | ||
| } | ||
| return process.exit(success ? 0 : 1); | ||
| } catch (error) { | ||
| logErrorAndExit(error); | ||
| } | ||
| return process.exit(1); | ||
| }); | ||
|
|
||
| program.command('lint') | ||
| .description('Lint a yaml file in the workingDir or the provided file path (with .spectral.yml) or a spec in an Insomnia database directory') | ||
| .command('spec [identifier]') | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: just double check that command options will be overridden by the config file? Original I thought command options might have highest priority.