diff --git a/src/cli/commands/index.js b/src/cli/commands/index.js index d69fe0d25f..8f3f16055c 100644 --- a/src/cli/commands/index.js +++ b/src/cli/commands/index.js @@ -1,25 +1,28 @@ const abbrev = require('abbrev'); -const hotload = require('../../lib/hotload')(__dirname); require('../../lib/spinner').isRequired = false; -// the aim of this module is to load as little as possible to keep cli boot -// time as low as possible +// Wrapper for Commonjs compatibility +async function callModule(mod, args) { + const resolvedModule = await mod; + return (resolvedModule.default || resolvedModule)(...args); +} const commands = { - auth: hotload('./auth'), - config: hotload('./config'), - help: hotload('./help'), - ignore: hotload('./ignore'), - modules: hotload('./modules'), - monitor: hotload('./monitor'), - fix: hotload('./fix'), - policy: hotload('./policy'), - protect: hotload('./protect'), - test: hotload('./test'), - version: hotload('./version'), - wizard: hotload('./protect/wizard'), - woof: hotload('./woof'), + auth: async (...args) => callModule(import('./auth'), args), + config: async (...args) => callModule(import('./config'), args), + help: async (...args) => callModule(import('./help'), args), + ignore: async (...args) => callModule(import('./ignore'), args), + monitor: async (...args) => callModule(import('./monitor'), args), + fix: async (...args) => callModule(import('./fix'), args), + policy: async (...args) => callModule(import('./policy'), args), + protect: async (...args) => callModule(import('./protect'), args), + test: async (...args) => callModule(import('./test'), args), + version: async (...args) => callModule(import('./version'), args), + wizard: async (...args) => callModule(import('./protect/wizard'), args), + woof: async (...args) => callModule(import('./woof'), args), }; + commands.aliases = abbrev(Object.keys(commands)); commands.aliases.t = 'test'; + module.exports = commands; diff --git a/src/cli/index.ts b/src/cli/index.ts index e1dfe9bf06..7e3572a11b 100755 --- a/src/cli/index.ts +++ b/src/cli/index.ts @@ -18,9 +18,10 @@ import * as alerts from '../lib/alerts'; import * as sln from '../lib/sln'; import { TestCommandResult } from './commands/types'; import { copy } from './copy'; -import spinner = require('../lib/spinner'); -import errors = require('../lib/errors/legacy-errors'); -import ansiEscapes = require('ansi-escapes'); +import * as spinner from '../lib/spinner'; +import * as errors from '../lib/errors/legacy-errors'; +import * as ansiEscapes from 'ansi-escapes'; + import { isPathToPackageFile } from '../lib/detect'; import { MissingTargetFileError, diff --git a/test/jest/acceptance/oauth-token.spec.ts b/test/jest/acceptance/oauth-token.spec.ts index 3397707ae3..3daa5f186d 100644 --- a/test/jest/acceptance/oauth-token.spec.ts +++ b/test/jest/acceptance/oauth-token.spec.ts @@ -1,88 +1,58 @@ -import { - chdirWorkspaces, - getWorkspaceJSON, -} from '../../acceptance/workspace-helper'; import { fakeServer } from '../../acceptance/fake-server'; -import cli = require('../../../src/cli/commands'); -import { chdir } from 'process'; +import { createProjectFromWorkspace } from '../util/createProject'; +import { runSnykCLI } from '../util/runSnykCLI'; describe('test using OAuth token', () => { - let oldkey: string; - let oldendpoint: string; - const apiKey = '123456789'; - const port: string = process.env.PORT || process.env.SNYK_PORT || '12345'; - - const BASE_API = '/api/v1'; - - const server = fakeServer(BASE_API, apiKey); - - const noVulnsResult = getWorkspaceJSON( - 'fail-on', - 'no-vulns', - 'vulns-result.json', - ); - - let origCwd: string; - - beforeAll(async () => { - origCwd = process.cwd(); - process.env.SNYK_API = `http://localhost:${port}${BASE_API}`; - process.env.SNYK_HOST = `http://localhost:${port}`; - - let key = await cli.config('get', 'api'); - oldkey = key; - - key = await cli.config('get', 'endpoint'); - oldendpoint = key; - - await new Promise((resolve) => { - server.listen(port, resolve); - }); + let server: ReturnType; + let env: Record; + + beforeAll((done) => { + const apiPath = '/api/v1'; + const apiPort = process.env.PORT || process.env.SNYK_PORT || '12345'; + env = { + ...process.env, + SNYK_API: 'http://localhost:' + apiPort + apiPath, + SNYK_TOKEN: '123456789', + SNYK_OAUTH_TOKEN: 'oauth-jwt-token', + }; + + server = fakeServer(apiPath, env.SNYK_TOKEN); + server.listen(apiPort, () => done()); }); afterAll(async () => { - delete process.env.SNYK_API; - delete process.env.SNYK_HOST; - delete process.env.SNYK_PORT; - delete process.env.SNYK_OAUTH_TOKEN; - await server.close(); - let key = 'set'; - let value = `api=${oldkey}`; - if (!oldkey) { - key = 'unset'; - value = 'api'; - } - await cli.config(key, value); - if (oldendpoint) { - await cli.config('endpoint', oldendpoint); - } - chdir(origCwd); }); it('successfully tests a project with an OAuth env variable set', async () => { - process.env.SNYK_OAUTH_TOKEN = 'oauth-jwt-token'; + const project = await createProjectFromWorkspace('fail-on/no-vulns'); + const jsonObj = JSON.parse(await project.read('vulns-result.json')); + server.setNextResponse(jsonObj); - server.setNextResponse(noVulnsResult); - chdirWorkspaces('fail-on'); - await cli.test('no-vulns', { - json: true, + const { code } = await runSnykCLI(`test --json`, { + cwd: project.path(), + env, }); - const req = server.popRequest(); - expect(req.headers.authorization).toBe('Bearer oauth-jwt-token'); - expect(req.method).toBe('POST'); + + expect(code).toEqual(0); + const requests = server.popRequests(2); + expect(requests[0].headers.authorization).toBe('Bearer oauth-jwt-token'); + expect(requests[0].method).toBe('POST'); }); it('successfully monitors a project with an OAuth env variable set', async () => { - process.env.SNYK_OAUTH_TOKEN = 'oauth-jwt-token'; + const project = await createProjectFromWorkspace('fail-on/no-vulns'); + const jsonObj = JSON.parse(await project.read('vulns-result.json')); + server.setNextResponse(jsonObj); - server.setNextResponse(noVulnsResult); - chdirWorkspaces('fail-on'); - await cli.monitor('no-vulns', { - json: true, + const { code } = await runSnykCLI(`monitor --json`, { + cwd: project.path(), + env, }); - const req = server.popRequest(); - expect(req.headers.authorization).toBe('Bearer oauth-jwt-token'); - expect(req.method).toBe('PUT'); + + expect(code).toEqual(0); + const requests = server.popRequests(2); + expect(requests[0].headers.authorization).toBe('Bearer oauth-jwt-token'); + expect(requests[0].method).toBe('PUT'); }); }); diff --git a/test/jest/system/lib/commands/fix/fix.spec.ts b/test/jest/system/lib/commands/fix/fix.spec.ts index 384ea8375f..135bc4a80a 100644 --- a/test/jest/system/lib/commands/fix/fix.spec.ts +++ b/test/jest/system/lib/commands/fix/fix.spec.ts @@ -3,21 +3,19 @@ import * as pathLib from 'path'; import stripAnsi from 'strip-ansi'; import { fakeServer } from '../../../../../acceptance/fake-server'; -import cli = require('../../../../../../src/cli/commands'); -const main = './bin/snyk'.replace(/\//g, pathLib.sep); -const testTimeout = 50000; describe('snyk fix (system tests)', () => { - let oldkey; - let oldendpoint; + const main = './bin/snyk'.replace(/\//g, pathLib.sep); + const testTimeout = 50000; + const apiKey = '123456789'; const port = process.env.PORT || process.env.SNYK_PORT || '12345'; - const BASE_API = '/api/v1'; const SNYK_API = 'http://localhost:' + port + BASE_API; const SNYK_HOST = 'http://localhost:' + port; const server = fakeServer(BASE_API, apiKey); + const noVulnsProjectPath = pathLib.join( __dirname, '/acceptance', @@ -25,47 +23,30 @@ describe('snyk fix (system tests)', () => { 'no-vulns', ); - beforeAll(async () => { - let key = await cli.config('get', 'api'); - oldkey = key; - - key = await cli.config('get', 'endpoint'); - oldendpoint = key; + const env = { + PATH: process.env.PATH, + SNYK_TOKEN: apiKey, + SNYK_API, + SNYK_HOST, + }; + beforeAll(async () => { await new Promise((resolve) => { server.listen(port, resolve); }); }); afterAll(async () => { - delete process.env.SNYK_API; - delete process.env.SNYK_HOST; - delete process.env.SNYK_PORT; - await server.close(); - let key = 'set'; - let value = 'api=' + oldkey; - if (!oldkey) { - key = 'unset'; - value = 'api'; - } - await cli.config(key, value); - if (oldendpoint) { - await cli.config('endpoint', oldendpoint); - } }); + it( '`errors when FF is not enabled`', (done) => { exec( `node ${main} fix --org=no-flag`, { - env: { - PATH: process.env.PATH, - SNYK_TOKEN: apiKey, - SNYK_API, - SNYK_HOST, - }, + env, }, (err, stdout, stderr) => { if (!err) { @@ -83,18 +64,14 @@ describe('snyk fix (system tests)', () => { }, testTimeout, ); + it( '`shows error when called with --source`', (done) => { exec( `node ${main} fix --source`, { - env: { - PATH: process.env.PATH, - SNYK_TOKEN: apiKey, - SNYK_API, - SNYK_HOST, - }, + env, }, (err, stdout, stderr) => { if (!err) { @@ -119,12 +96,7 @@ describe('snyk fix (system tests)', () => { exec( `node ${main} fix --docker`, { - env: { - PATH: process.env.PATH, - SNYK_TOKEN: apiKey, - SNYK_API, - SNYK_HOST, - }, + env, }, (err, stdout, stderr) => { if (!err) { @@ -149,12 +121,7 @@ describe('snyk fix (system tests)', () => { exec( `node ${main} fix --code`, { - env: { - PATH: process.env.PATH, - SNYK_TOKEN: apiKey, - SNYK_API, - SNYK_HOST, - }, + env, }, (err, stdout, stderr) => { if (!err) { @@ -179,12 +146,7 @@ describe('snyk fix (system tests)', () => { exec( `node ${main} fix ${noVulnsProjectPath}`, { - env: { - PATH: process.env.PATH, - SNYK_TOKEN: apiKey, - SNYK_API, - SNYK_HOST, - }, + env, }, (err, stdout, stderr) => { if (!err) { diff --git a/test/jest/system/snyk-test/python/snyk-test-pyproject.spec.ts b/test/jest/system/snyk-test/python/snyk-test-pyproject.spec.ts index 59fe41c386..5ab709d2c9 100644 --- a/test/jest/system/snyk-test/python/snyk-test-pyproject.spec.ts +++ b/test/jest/system/snyk-test/python/snyk-test-pyproject.spec.ts @@ -1,7 +1,7 @@ import { join } from 'path'; import { mocked } from 'ts-jest/utils'; import { NeedleResponse } from 'needle'; -import { test } from '../../../../../src/cli/commands'; +const test = require('../../../../../src/cli/commands/test'); import { loadPlugin } from '../../../../../src/lib/plugins/index'; import { CommandResult } from '../../../../../src/cli/commands/types'; import { makeRequest } from '../../../../../src/lib/request/request'; diff --git a/test/jest/unit/lib/commands/fix/fix.spec.ts b/test/jest/unit/lib/commands/fix/fix.spec.ts index 4e41e3c602..6d91e8e584 100644 --- a/test/jest/unit/lib/commands/fix/fix.spec.ts +++ b/test/jest/unit/lib/commands/fix/fix.spec.ts @@ -2,7 +2,7 @@ import * as pathLib from 'path'; import * as fs from 'fs'; import * as snykFix from '@snyk/fix'; -import cli = require('../../../../../../src/cli/commands'); +const fix = require('../../../../../../src/cli/commands/fix'); import * as snyk from '../../../../../../src/lib'; import * as featureFlags from '../../../../../../src/lib/feature-flags'; import * as analytics from '../../../../../../src/lib/analytics'; @@ -104,7 +104,7 @@ describe('snyk fix (functional tests)', () => { // pip plugin does not return targetFile, instead fix will fallback to displayTargetFile displayTargetFile: pipRequirementsTxt, }); - const res = await cli.fix('.', { + const res = await fix('.', { file: pipRequirementsTxt, dryRun: true, // prevents write to disc quiet: true, @@ -148,7 +148,7 @@ describe('snyk fix (functional tests)', () => { // pip plugin does not return targetFile, instead fix will fallback to displayTargetFile displayTargetFile: pipRequirementsTxt, }); - const res = await cli.fix('.', { + const res = await fix('.', { file: pipRequirementsTxt, dryRun: true, // prevents write to disc }); @@ -197,7 +197,7 @@ describe('snyk fix (functional tests)', () => { // pip plugin does not return targetFile, instead fix will fallback to displayTargetFile displayTargetFile: pipRequirementsCustomTxt, }); - const res = await cli.fix('.', { + const res = await fix('.', { file: pipRequirementsCustomTxt, packageManager: 'pip', dryRun: true, // prevents write to disc @@ -253,7 +253,7 @@ describe('snyk fix (functional tests)', () => { // pip plugin does not return targetFile, instead fix will fallback to displayTargetFile displayTargetFile: pipRequirementsTxt, }); - const res = await cli.fix(npmWorkspace, pipAppWorkspace, { + const res = await fix(npmWorkspace, pipAppWorkspace, { dryRun: true, // prevents write to disc quiet: true, }); @@ -305,7 +305,7 @@ describe('snyk fix (functional tests)', () => { let res; try { - await cli.fix(npmWorkspace, pipAppWorkspace, { + await fix(npmWorkspace, pipAppWorkspace, { dryRun: true, // prevents write to disc quiet: true, }); @@ -358,7 +358,7 @@ describe('snyk fix (functional tests)', () => { // pip plugin does not return targetFile, instead fix will fallback to displayTargetFile displayTargetFile: pipRequirementsTxt, }); - const res = await cli.fix(pipAppWorkspace, { + const res = await fix(pipAppWorkspace, { dryRun: true, // prevents write to disc quiet: true, }); diff --git a/test/jest/unit/snyk-code/snyk-code-test.spec.ts b/test/jest/unit/snyk-code/snyk-code-test.spec.ts index 5f799559fe..f816a07c13 100644 --- a/test/jest/unit/snyk-code/snyk-code-test.spec.ts +++ b/test/jest/unit/snyk-code/snyk-code-test.spec.ts @@ -13,7 +13,7 @@ import * as analysis from '../../../../src/lib/plugins/sast/analysis'; import { Options, TestOptions } from '../../../../src/lib/types'; import * as ecosystems from '../../../../src/lib/ecosystems'; import * as analytics from '../../../../src/lib/analytics'; -import * as cli from '../../../../src/cli/commands'; +const test = require('../../../../src/cli/commands/test/index.ts'); import { jsonStringifyLargeObject } from '../../../../src/lib/json'; const { getCodeAnalysisAndParseResults } = analysis; @@ -141,7 +141,7 @@ describe('Test snyk code', () => { expect.hasAssertions(); try { - await cli.test('some/path', options); + await test('some/path', options); } catch (error) { const errMessage = stripAscii(stripAnsi(error.message.trim())); const expectedOutput = stripAscii(stripAnsi(testOutput.trim())); @@ -174,7 +174,7 @@ describe('Test snyk code', () => { const expected = new Error(error.message); expect.hasAssertions(); try { - await cli.test('.', { + await test('.', { path: '', code: true, }); @@ -189,7 +189,7 @@ describe('Test snyk code', () => { ok: true, }); - await expect(cli.test('some/path', { code: true })).rejects.toHaveProperty( + await expect(test('some/path', { code: true })).rejects.toHaveProperty( 'userMessage', 'Snyk Code is not supported for org: enable in Settings > Snyk Code', ); @@ -201,7 +201,7 @@ describe('Test snyk code', () => { userError: 'Not enabled', }); - await expect(cli.test('some/path', { code: true })).rejects.toHaveProperty( + await expect(test('some/path', { code: true })).rejects.toHaveProperty( 'userMessage', 'Snyk Code is not supported for org.', ); @@ -217,7 +217,7 @@ describe('Test snyk code', () => { userMessage: 'Test limit reached!', }); - await expect(cli.test('some/path', { code: true })).rejects.toHaveProperty( + await expect(test('some/path', { code: true })).rejects.toHaveProperty( 'userMessage', 'Test limit reached!', ); @@ -281,7 +281,7 @@ describe('Test snyk code', () => { trackUsageSpy.mockResolvedValue({}); try { - await cli.test('some/path', options); + await test('some/path', options); } catch (error) { const errMessage = error.message.trim(); const expectedOutput = jsonStringifyLargeObject(