Skip to content

Commit 975f8d3

Browse files
authored
fix(amazonFeatureDev): accepted files telemetry aws#4689
Problem File acceptance telemetry was not counting deleted files, i.e. the files that code generation marked to be deleted, even if they were rejected or not. Solution Count deleted files for the metric and add tests to assure this.
1 parent 63c6005 commit 975f8d3

File tree

2 files changed

+121
-19
lines changed

2 files changed

+121
-19
lines changed

packages/core/src/amazonqFeatureDev/controllers/chat/controller.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -449,9 +449,15 @@ export class FeatureDevController {
449449
let session
450450
try {
451451
session = await this.sessionStorage.getSession(message.tabID)
452+
453+
const acceptedFiles = (paths?: { rejected: boolean }[]) => (paths || []).filter(i => !i.rejected).length
454+
455+
const amazonqNumberOfFilesAccepted =
456+
acceptedFiles(session.state.filePaths) + acceptedFiles(session.state.deletedFiles)
457+
452458
telemetry.amazonq_isAcceptedCodeChanges.emit({
453459
amazonqConversationId: session.conversationId,
454-
amazonqNumberOfFilesAccepted: session.state.filePaths?.filter(i => !i.rejected).length ?? -1,
460+
amazonqNumberOfFilesAccepted,
455461
enabled: true,
456462
result: 'Succeeded',
457463
})

packages/core/src/test/amazonqFeatureDev/controllers/chat/controller.test.ts

Lines changed: 114 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,23 @@ import * as assert from 'assert'
88
import * as path from 'path'
99
import sinon from 'sinon'
1010
import { waitUntil } from '../../../../shared/utilities/timeoutUtils'
11-
import { ControllerSetup, createController, createSession } from '../../utils'
12-
import { CurrentWsFolders, FollowUpTypes, createUri } from '../../../../amazonqFeatureDev/types'
11+
import { ControllerSetup, createController, createSession, generateVirtualMemoryUri } from '../../utils'
12+
import {
13+
CurrentWsFolders,
14+
FollowUpTypes,
15+
createUri,
16+
NewFileInfo,
17+
DeletedFileInfo,
18+
} from '../../../../amazonqFeatureDev/types'
1319
import { Session } from '../../../../amazonqFeatureDev/session/session'
1420
import { Prompter } from '../../../../shared/ui/prompter'
1521
import { assertTelemetry, toFile } from '../../../testUtil'
1622
import { SelectedFolderNotInWorkspaceFolderError } from '../../../../amazonqFeatureDev/errors'
17-
import { CodeGenState, PrepareRefinementState } from '../../../../amazonqFeatureDev/session/sessionState'
23+
import {
24+
CodeGenState,
25+
PrepareCodeGenState,
26+
PrepareRefinementState,
27+
} from '../../../../amazonqFeatureDev/session/sessionState'
1828
import { FeatureDevClient } from '../../../../amazonqFeatureDev/client/featureDev'
1929

2030
let mockGetCodeGeneration: sinon.SinonStub
@@ -26,6 +36,40 @@ describe('Controller', () => {
2636
let session: Session
2737
let controllerSetup: ControllerSetup
2838

39+
const getFilePaths = (controllerSetup: ControllerSetup): NewFileInfo[] => [
40+
{
41+
zipFilePath: 'myfile1.js',
42+
relativePath: 'myfile1.js',
43+
fileContent: '',
44+
rejected: false,
45+
virtualMemoryUri: generateVirtualMemoryUri(uploadID, 'myfile1.js'),
46+
workspaceFolder: controllerSetup.workspaceFolder,
47+
},
48+
{
49+
zipFilePath: 'myfile2.js',
50+
relativePath: 'myfile2.js',
51+
fileContent: '',
52+
rejected: true,
53+
virtualMemoryUri: generateVirtualMemoryUri(uploadID, 'myfile2.js'),
54+
workspaceFolder: controllerSetup.workspaceFolder,
55+
},
56+
]
57+
58+
const getDeletedFiles = (): DeletedFileInfo[] => [
59+
{
60+
zipFilePath: 'myfile3.js',
61+
relativePath: 'myfile3.js',
62+
rejected: false,
63+
workspaceFolder: controllerSetup.workspaceFolder,
64+
},
65+
{
66+
zipFilePath: 'myfile4.js',
67+
relativePath: 'myfile4.js',
68+
rejected: true,
69+
workspaceFolder: controllerSetup.workspaceFolder,
70+
},
71+
]
72+
2973
before(() => {
3074
sinon.stub(performance, 'now').returns(0)
3175
})
@@ -245,7 +289,6 @@ describe('Controller', () => {
245289
})
246290

247291
describe('fileClicked', () => {
248-
const filePath = 'myfile.js'
249292
async function createCodeGenState() {
250293
mockGetCodeGeneration = sinon.stub().resolves({ codeGenerationStatus: { status: 'Complete' } })
251294

@@ -269,16 +312,7 @@ describe('Controller', () => {
269312
const codeGenState = new CodeGenState(
270313
testConfig,
271314
testApproach,
272-
[
273-
{
274-
zipFilePath: 'myfile.js',
275-
relativePath: 'myfile.js',
276-
fileContent: '',
277-
rejected: false,
278-
virtualMemoryUri: '' as unknown as vscode.Uri,
279-
workspaceFolder: controllerSetup.workspaceFolder,
280-
},
281-
],
315+
getFilePaths(controllerSetup),
282316
[],
283317
[],
284318
tabID,
@@ -293,7 +327,12 @@ describe('Controller', () => {
293327
})
294328
return newSession
295329
}
296-
async function fileClicked(getSessionStub: sinon.SinonStub<[tabID: string], Promise<Session>>, action: string) {
330+
331+
async function fileClicked(
332+
getSessionStub: sinon.SinonStub<[tabID: string], Promise<Session>>,
333+
action: string,
334+
filePath: string
335+
) {
297336
controllerSetup.emitters.fileClicked.fire({
298337
tabID,
299338
conversationID,
@@ -307,20 +346,77 @@ describe('Controller', () => {
307346
}, {})
308347
return getSessionStub.getCall(0).returnValue
309348
}
349+
310350
it('clicking the "Reject File" button updates the file state to "rejected: true"', async () => {
351+
const filePath = getFilePaths(controllerSetup)[0].zipFilePath
311352
const session = await createCodeGenState()
312353
const getSessionStub = sinon.stub(controllerSetup.sessionStorage, 'getSession').resolves(session)
313354

314-
const rejectFile = await fileClicked(getSessionStub, 'reject-change')
355+
const rejectFile = await fileClicked(getSessionStub, 'reject-change', filePath)
315356
assert.strictEqual(rejectFile.state.filePaths?.find(i => i.relativePath === filePath)?.rejected, true)
316357
})
358+
317359
it('clicking the "Reject File" button and then "Revert Reject File", updates the file state to "rejected: false"', async () => {
360+
const filePath = getFilePaths(controllerSetup)[0].zipFilePath
318361
const session = await createCodeGenState()
319362
const getSessionStub = sinon.stub(controllerSetup.sessionStorage, 'getSession').resolves(session)
320363

321-
await fileClicked(getSessionStub, 'reject-change')
322-
const revertRejection = await fileClicked(getSessionStub, 'revert-rejection')
364+
await fileClicked(getSessionStub, 'reject-change', filePath)
365+
const revertRejection = await fileClicked(getSessionStub, 'revert-rejection', filePath)
323366
assert.strictEqual(revertRejection.state.filePaths?.find(i => i.relativePath === filePath)?.rejected, false)
324367
})
325368
})
369+
370+
describe('insertCode', () => {
371+
it('sets the number of files accepted counting also deleted files', async () => {
372+
async function insertCode() {
373+
const initialState = new PrepareCodeGenState(
374+
{
375+
conversationId: conversationID,
376+
proxyClient: new FeatureDevClient(),
377+
workspaceRoots: [''],
378+
workspaceFolders: [controllerSetup.workspaceFolder],
379+
uploadId: uploadID,
380+
},
381+
'',
382+
getFilePaths(controllerSetup),
383+
getDeletedFiles(),
384+
[],
385+
tabID,
386+
0
387+
)
388+
389+
const newSession = await createSession({
390+
messenger: controllerSetup.messenger,
391+
sessionState: initialState,
392+
conversationID,
393+
tabID,
394+
uploadID,
395+
})
396+
const getSessionStub = sinon.stub(controllerSetup.sessionStorage, 'getSession').resolves(newSession)
397+
398+
controllerSetup.emitters.followUpClicked.fire({
399+
tabID,
400+
conversationID,
401+
followUp: {
402+
type: FollowUpTypes.InsertCode,
403+
},
404+
})
405+
406+
// Wait until the controller has time to process the event
407+
await waitUntil(() => {
408+
return Promise.resolve(getSessionStub.callCount > 0)
409+
}, {})
410+
}
411+
412+
await insertCode()
413+
414+
assertTelemetry('amazonq_isAcceptedCodeChanges', {
415+
amazonqConversationId: conversationID,
416+
amazonqNumberOfFilesAccepted: 2,
417+
enabled: true,
418+
result: 'Succeeded',
419+
})
420+
})
421+
})
326422
})

0 commit comments

Comments
 (0)