Skip to content

Commit caf2be8

Browse files
committed
move some files around
1 parent 2897aee commit caf2be8

File tree

2 files changed

+342
-0
lines changed

2 files changed

+342
-0
lines changed
Lines changed: 342 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,342 @@
1+
/*!
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
import * as vscode from 'vscode'
6+
import * as sinon from 'sinon'
7+
import * as assert from 'assert'
8+
import * as semver from 'semver'
9+
import * as model from '../../codewhisperer/models/model'
10+
import * as timeoutUtils from '../../shared/utilities/timeoutUtils'
11+
import * as diagnosticsProvider from '../../codewhisperer/service/diagnosticsProvider'
12+
import * as startSecurityScan from '../../codewhisperer/commands/startSecurityScan'
13+
import * as errors from '../../shared/errors'
14+
import {
15+
SecurityPanelViewProvider,
16+
stopScanMessage,
17+
showScannedFilesMessage,
18+
projectScansLimitReached,
19+
} from '../../codewhisperer'
20+
import { getTestWorkspaceFolder } from '../../testInteg/integrationTestsUtilities'
21+
import { FakeExtensionContext } from '../fakeExtensionContext'
22+
import { join } from 'path'
23+
import { assertTelemetry, closeAllEditors, getFetchStubWithResponse } from '../testUtil'
24+
import { AWSError } from 'aws-sdk'
25+
import { CodeAnalysisScope } from '../../codewhisperer'
26+
import { getTestWindow } from '../shared/vscode/window'
27+
import { SeverityLevel } from '../../test/shared/vscode/message'
28+
import { cancel, CodewhispererSecurityScan } from '../../shared'
29+
import { createMockClient, mockGetCodeScanResponse } from '../amazonqFeatureDev/utils'
30+
31+
describe('startSecurityScan', function () {
32+
let extensionContext: FakeExtensionContext
33+
let mockSecurityPanelViewProvider: SecurityPanelViewProvider
34+
let appRoot: string
35+
let appCodePath: string
36+
let editor: vscode.TextEditor
37+
const workspaceFolder = getTestWorkspaceFolder()
38+
beforeEach(async function () {
39+
extensionContext = await FakeExtensionContext.create()
40+
mockSecurityPanelViewProvider = new SecurityPanelViewProvider(extensionContext)
41+
appRoot = join(workspaceFolder, 'python3.7-plain-sam-app')
42+
appCodePath = join(appRoot, 'hello_world', 'app.py')
43+
editor = await openTestFile(appCodePath)
44+
await model.CodeScansState.instance.setScansEnabled(false)
45+
sinon.stub(timeoutUtils, 'sleep')
46+
})
47+
afterEach(function () {
48+
sinon.restore()
49+
})
50+
after(async function () {
51+
await closeAllEditors()
52+
})
53+
54+
const openTestFile = async (filePath: string) => {
55+
const doc = await vscode.workspace.openTextDocument(filePath)
56+
return await vscode.window.showTextDocument(doc, {
57+
selection: new vscode.Range(new vscode.Position(0, 0), new vscode.Position(0, 1)),
58+
})
59+
}
60+
61+
it('Should render security scan result', async function () {
62+
getFetchStubWithResponse({ status: 200, statusText: 'testing stub' })
63+
const commandSpy = sinon.spy(vscode.commands, 'executeCommand')
64+
const securityScanRenderSpy = sinon.spy(diagnosticsProvider, 'initSecurityScanRender')
65+
66+
await startSecurityScan.startSecurityScan(
67+
mockSecurityPanelViewProvider,
68+
editor,
69+
createMockClient(),
70+
extensionContext,
71+
CodeAnalysisScope.PROJECT
72+
)
73+
assert.ok(commandSpy.calledWith('workbench.action.problems.focus'))
74+
assert.ok(securityScanRenderSpy.calledOnce)
75+
const warnings = getTestWindow().shownMessages.filter((m) => m.severity === SeverityLevel.Warning)
76+
assert.strictEqual(warnings.length, 0)
77+
})
78+
79+
it('Should not focus problems panel for file scans', async function () {
80+
getFetchStubWithResponse({ status: 200, statusText: 'testing stub' })
81+
const commandSpy = sinon.spy(vscode.commands, 'executeCommand')
82+
const securityScanRenderSpy = sinon.spy(diagnosticsProvider, 'initSecurityScanRender')
83+
84+
await model.CodeScansState.instance.setScansEnabled(true)
85+
await startSecurityScan.startSecurityScan(
86+
mockSecurityPanelViewProvider,
87+
editor,
88+
createMockClient(),
89+
extensionContext,
90+
CodeAnalysisScope.FILE
91+
)
92+
assert.ok(commandSpy.neverCalledWith('workbench.action.problems.focus'))
93+
assert.ok(securityScanRenderSpy.calledOnce)
94+
const warnings = getTestWindow().shownMessages.filter((m) => m.severity === SeverityLevel.Warning)
95+
assert.strictEqual(warnings.length, 0)
96+
assertTelemetry('codewhisperer_securityScan', {
97+
codewhispererCodeScanScope: 'FILE',
98+
passive: true,
99+
})
100+
})
101+
102+
it('Should stop security scan for project scans when confirmed', async function () {
103+
getFetchStubWithResponse({ status: 200, statusText: 'testing stub' })
104+
const securityScanRenderSpy = sinon.spy(diagnosticsProvider, 'initSecurityScanRender')
105+
const securityScanStoppedErrorSpy = sinon.spy(model, 'CodeScanStoppedError')
106+
const testWindow = getTestWindow()
107+
testWindow.onDidShowMessage((message) => {
108+
if (message.message === stopScanMessage) {
109+
message.selectItem(startSecurityScan.stopScanButton)
110+
}
111+
})
112+
model.codeScanState.setToRunning()
113+
const scanPromise = startSecurityScan.startSecurityScan(
114+
mockSecurityPanelViewProvider,
115+
editor,
116+
createMockClient(),
117+
extensionContext,
118+
CodeAnalysisScope.PROJECT
119+
)
120+
await startSecurityScan.confirmStopSecurityScan()
121+
await scanPromise
122+
assert.ok(securityScanRenderSpy.notCalled)
123+
assert.ok(securityScanStoppedErrorSpy.calledOnce)
124+
const warnings = testWindow.shownMessages.filter((m) => m.severity === SeverityLevel.Warning)
125+
assert.ok(warnings.map((m) => m.message).includes(stopScanMessage))
126+
})
127+
128+
it('Should not stop security scan for project scans when not confirmed', async function () {
129+
getFetchStubWithResponse({ status: 200, statusText: 'testing stub' })
130+
const securityScanRenderSpy = sinon.spy(diagnosticsProvider, 'initSecurityScanRender')
131+
const securityScanStoppedErrorSpy = sinon.spy(model, 'CodeScanStoppedError')
132+
const testWindow = getTestWindow()
133+
testWindow.onDidShowMessage((message) => {
134+
if (message.message === stopScanMessage) {
135+
message.selectItem(cancel)
136+
}
137+
})
138+
model.codeScanState.setToRunning()
139+
const scanPromise = startSecurityScan.startSecurityScan(
140+
mockSecurityPanelViewProvider,
141+
editor,
142+
createMockClient(),
143+
extensionContext,
144+
CodeAnalysisScope.PROJECT
145+
)
146+
await startSecurityScan.confirmStopSecurityScan()
147+
await scanPromise
148+
assert.ok(securityScanRenderSpy.calledOnce)
149+
assert.ok(securityScanStoppedErrorSpy.notCalled)
150+
const warnings = testWindow.shownMessages.filter((m) => m.severity === SeverityLevel.Warning)
151+
assert.ok(warnings.map((m) => m.message).includes(stopScanMessage))
152+
})
153+
154+
it('Should stop security scan for file scans if setting is disabled', async function () {
155+
getFetchStubWithResponse({ status: 200, statusText: 'testing stub' })
156+
const securityScanRenderSpy = sinon.spy(diagnosticsProvider, 'initSecurityScanRender')
157+
const securityScanStoppedErrorSpy = sinon.spy(model, 'CodeScanStoppedError')
158+
await model.CodeScansState.instance.setScansEnabled(true)
159+
const scanPromise = startSecurityScan.startSecurityScan(
160+
mockSecurityPanelViewProvider,
161+
editor,
162+
createMockClient(),
163+
extensionContext,
164+
CodeAnalysisScope.FILE
165+
)
166+
await model.CodeScansState.instance.setScansEnabled(false)
167+
await scanPromise
168+
assert.ok(securityScanRenderSpy.notCalled)
169+
assert.ok(securityScanStoppedErrorSpy.calledOnce)
170+
})
171+
172+
it('Should highlight files after scan is completed', async function () {
173+
if (semver.lt(vscode.version, '1.78.0')) {
174+
this.skip()
175+
}
176+
getFetchStubWithResponse({ status: 200, statusText: 'testing stub' })
177+
const testWindow = getTestWindow()
178+
testWindow.onDidShowMessage((message) => {
179+
if (message.message.includes('Security scan completed')) {
180+
message.selectItem(showScannedFilesMessage)
181+
}
182+
})
183+
await startSecurityScan.startSecurityScan(
184+
mockSecurityPanelViewProvider,
185+
editor,
186+
createMockClient(),
187+
extensionContext,
188+
CodeAnalysisScope.PROJECT
189+
)
190+
assertTelemetry('codewhisperer_securityScan', {
191+
codewhispererCodeScanTotalIssues: 1,
192+
codewhispererCodeScanIssuesWithFixes: 0,
193+
codewhispererCodeScanScope: 'PROJECT',
194+
passive: false,
195+
} as CodewhispererSecurityScan)
196+
})
197+
198+
it('Should cancel a scan if a newer one has started', async function () {
199+
getFetchStubWithResponse({ status: 200, statusText: 'testing stub' })
200+
await model.CodeScansState.instance.setScansEnabled(true)
201+
202+
const scanPromise = startSecurityScan.startSecurityScan(
203+
mockSecurityPanelViewProvider,
204+
editor,
205+
createMockClient(),
206+
extensionContext,
207+
CodeAnalysisScope.FILE
208+
)
209+
await startSecurityScan.startSecurityScan(
210+
mockSecurityPanelViewProvider,
211+
editor,
212+
createMockClient(),
213+
extensionContext,
214+
CodeAnalysisScope.FILE
215+
)
216+
await scanPromise
217+
assertTelemetry('codewhisperer_securityScan', [
218+
{
219+
result: 'Cancelled',
220+
reasonDesc: 'Security scan stopped by user.',
221+
reason: 'DefaultError',
222+
} as unknown as CodewhispererSecurityScan,
223+
{
224+
result: 'Succeeded',
225+
},
226+
])
227+
})
228+
229+
it('Should not cancel a project scan if a file scan has started', async function () {
230+
getFetchStubWithResponse({ status: 200, statusText: 'testing stub' })
231+
await model.CodeScansState.instance.setScansEnabled(true)
232+
233+
const scanPromise = startSecurityScan.startSecurityScan(
234+
mockSecurityPanelViewProvider,
235+
editor,
236+
createMockClient(),
237+
extensionContext,
238+
CodeAnalysisScope.PROJECT
239+
)
240+
await startSecurityScan.startSecurityScan(
241+
mockSecurityPanelViewProvider,
242+
editor,
243+
createMockClient(),
244+
extensionContext,
245+
CodeAnalysisScope.FILE
246+
)
247+
await scanPromise
248+
assertTelemetry('codewhisperer_securityScan', [
249+
{
250+
result: 'Succeeded',
251+
codewhispererCodeScanScope: 'FILE',
252+
},
253+
{
254+
result: 'Succeeded',
255+
codewhispererCodeScanScope: 'PROJECT',
256+
},
257+
])
258+
})
259+
260+
it('Should handle failed scan job status', async function () {
261+
getFetchStubWithResponse({ status: 200, statusText: 'testing stub' })
262+
263+
const mockClient = createMockClient()
264+
mockClient.getCodeScan.resolves({
265+
...mockGetCodeScanResponse,
266+
status: 'Failed',
267+
})
268+
await startSecurityScan.startSecurityScan(
269+
mockSecurityPanelViewProvider,
270+
editor,
271+
mockClient,
272+
extensionContext,
273+
CodeAnalysisScope.PROJECT
274+
)
275+
assertTelemetry('codewhisperer_securityScan', {
276+
codewhispererCodeScanScope: 'PROJECT',
277+
result: 'Failed',
278+
reason: 'CodeScanJobFailedError',
279+
reasonDesc: 'CodeScanJobFailedError: Security scan failed.',
280+
passive: false,
281+
} as unknown as CodewhispererSecurityScan)
282+
})
283+
284+
it('Should show notification when throttled for project scans', async function () {
285+
getFetchStubWithResponse({ status: 200, statusText: 'testing stub' })
286+
const mockClient = createMockClient()
287+
mockClient.createCodeScan.throws({
288+
code: 'ThrottlingException',
289+
time: new Date(),
290+
name: 'error name',
291+
message: 'Maximum project scan count reached for this month.',
292+
} satisfies AWSError)
293+
sinon.stub(errors, 'isAwsError').returns(true)
294+
const testWindow = getTestWindow()
295+
await startSecurityScan.startSecurityScan(
296+
mockSecurityPanelViewProvider,
297+
editor,
298+
mockClient,
299+
extensionContext,
300+
CodeAnalysisScope.PROJECT
301+
)
302+
assert.ok(testWindow.shownMessages.map((m) => m.message).includes(projectScansLimitReached))
303+
assertTelemetry('codewhisperer_securityScan', {
304+
codewhispererCodeScanScope: 'PROJECT',
305+
result: 'Failed',
306+
reason: 'ThrottlingException',
307+
reasonDesc: 'ThrottlingException: Maximum project scan count reached for this month.',
308+
passive: false,
309+
} as unknown as CodewhispererSecurityScan)
310+
})
311+
312+
it('Should set monthly quota exceeded when throttled for file scans', async function () {
313+
getFetchStubWithResponse({ status: 200, statusText: 'testing stub' })
314+
await model.CodeScansState.instance.setScansEnabled(true)
315+
const mockClient = createMockClient()
316+
mockClient.createCodeScan.throws({
317+
code: 'ThrottlingException',
318+
time: new Date(),
319+
name: 'error name',
320+
message: 'Maximum auto-scans count reached for this month.',
321+
} satisfies AWSError)
322+
sinon.stub(errors, 'isAwsError').returns(true)
323+
assert.equal(model.CodeScansState.instance.isMonthlyQuotaExceeded(), false)
324+
await startSecurityScan.startSecurityScan(
325+
mockSecurityPanelViewProvider,
326+
editor,
327+
mockClient,
328+
extensionContext,
329+
CodeAnalysisScope.FILE
330+
)
331+
assert.equal(model.CodeScansState.instance.isMonthlyQuotaExceeded(), true)
332+
const warnings = getTestWindow().shownMessages.filter((m) => m.severity === SeverityLevel.Warning)
333+
assert.strictEqual(warnings.length, 0)
334+
assertTelemetry('codewhisperer_securityScan', {
335+
codewhispererCodeScanScope: 'FILE',
336+
result: 'Failed',
337+
reason: 'ThrottlingException',
338+
reasonDesc: 'ThrottlingException: Maximum auto-scans count reached for this month.',
339+
passive: true,
340+
} as unknown as CodewhispererSecurityScan)
341+
})
342+
})

0 commit comments

Comments
 (0)