From a523ae699d9d4c488ce401f75ae12bf86de208ac Mon Sep 17 00:00:00 2001 From: Jacob Chung Date: Tue, 18 Feb 2025 15:36:37 -0800 Subject: [PATCH 1/4] refactor shortAnswer just testgen related objects extractShortAnswer: use packageInfoList --- packages/core/src/amazonqTest/app.ts | 2 +- .../amazonqTest/chat/controller/controller.ts | 22 ++--- .../src/amazonqTest/chat/session/session.ts | 7 +- .../codewhisperer/client/user-service-2.json | 90 ++++++++++++++++++- .../core/src/codewhisperer/models/model.ts | 5 +- .../codewhisperer/service/testGenHandler.ts | 44 +++++---- 6 files changed, 128 insertions(+), 42 deletions(-) diff --git a/packages/core/src/amazonqTest/app.ts b/packages/core/src/amazonqTest/app.ts index 041670b0c2e..62ed24aee0d 100644 --- a/packages/core/src/amazonqTest/app.ts +++ b/packages/core/src/amazonqTest/app.ts @@ -23,7 +23,7 @@ export function init(appContext: AmazonQAppInitContext) { authClicked: new vscode.EventEmitter(), startTestGen: new vscode.EventEmitter(), processHumanChatMessage: new vscode.EventEmitter(), - updateShortAnswer: new vscode.EventEmitter(), + updateTargetFileInfo: new vscode.EventEmitter(), showCodeGenerationResults: new vscode.EventEmitter(), openDiff: new vscode.EventEmitter(), formActionClicked: new vscode.EventEmitter(), diff --git a/packages/core/src/amazonqTest/chat/controller/controller.ts b/packages/core/src/amazonqTest/chat/controller/controller.ts index 96309e8714b..fd798309f35 100644 --- a/packages/core/src/amazonqTest/chat/controller/controller.ts +++ b/packages/core/src/amazonqTest/chat/controller/controller.ts @@ -53,7 +53,7 @@ import { randomUUID } from '../../../shared/crypto' import { tempDirPath, testGenerationLogsDir } from '../../../shared/filesystemUtilities' import { CodeReference } from '../../../codewhispererChat/view/connector/connector' import { TelemetryHelper } from '../../../codewhisperer/util/telemetryHelper' -import { ShortAnswer, ShortAnswerReference, testGenState } from '../../../codewhisperer/models/model' +import { Reference, testGenState } from '../../../codewhisperer/models/model' import { referenceLogText, TestGenerationBuildStep, @@ -63,6 +63,7 @@ import { } from '../../../codewhisperer/models/constants' import { UserWrittenCodeTracker } from '../../../codewhisperer/tracker/userWrittenCodeTracker' import { ReferenceLogViewProvider } from '../../../codewhisperer/service/referenceLogViewProvider' +import { TargetFileInfo } from '../../../codewhisperer/client/codewhispereruserclient' export interface TestChatControllerEventEmitters { readonly tabOpened: vscode.EventEmitter @@ -70,7 +71,7 @@ export interface TestChatControllerEventEmitters { readonly authClicked: vscode.EventEmitter readonly startTestGen: vscode.EventEmitter readonly processHumanChatMessage: vscode.EventEmitter - readonly updateShortAnswer: vscode.EventEmitter + readonly updateTargetFileInfo: vscode.EventEmitter readonly showCodeGenerationResults: vscode.EventEmitter readonly openDiff: vscode.EventEmitter readonly formActionClicked: vscode.EventEmitter @@ -131,8 +132,8 @@ export class TestController { return this.handleFormActionClicked(data) }) - this.chatControllerMessageListeners.updateShortAnswer.event((data) => { - return this.updateShortAnswer(data) + this.chatControllerMessageListeners.updateTargetFileInfo.event((data) => { + return this.updateTargetFileInfo(data) }) this.chatControllerMessageListeners.showCodeGenerationResults.event((data) => { @@ -586,10 +587,9 @@ export class TestController { } } - private async updateShortAnswer(message: { + private async updateTargetFileInfo(message: { tabID: string - status: string - shortAnswer?: ShortAnswer + targetFileInfo?: TargetFileInfo testGenerationJobGroupName: string testGenerationJobId: string type: ChatItemType @@ -599,11 +599,11 @@ export class TestController { type: 'answer', tabID: message.tabID, message: testGenSummaryMessage( - path.basename(message.shortAnswer?.sourceFilePath ?? message.filePath), - message.shortAnswer?.planSummary?.replaceAll('```', '') + path.basename(message.targetFileInfo?.filePath ?? message.filePath), + message.targetFileInfo?.filePlan?.replaceAll('```', '') ), canBeVoted: true, - filePath: message.shortAnswer?.testFilePath, + filePath: message.targetFileInfo?.testFilePath, }) } @@ -663,7 +663,7 @@ export class TestController { filePaths: [data.filePath], }, codeReference: session.references.map( - (ref: ShortAnswerReference) => + (ref: Reference) => ({ ...ref, information: `${ref.licenseName} - ${ref.repository}`, diff --git a/packages/core/src/amazonqTest/chat/session/session.ts b/packages/core/src/amazonqTest/chat/session/session.ts index e6fb850a3fb..4ae4dfd2f4d 100644 --- a/packages/core/src/amazonqTest/chat/session/session.ts +++ b/packages/core/src/amazonqTest/chat/session/session.ts @@ -3,8 +3,8 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { ShortAnswer, ShortAnswerReference } from '../../../codewhisperer/models/model' -import { TestGenerationJob } from '../../../codewhisperer/client/codewhispereruserclient' +import { ShortAnswer, Reference } from '../../../codewhisperer/models/model' +import { TargetFileInfo, TestGenerationJob } from '../../../codewhisperer/client/codewhispereruserclient' export enum ConversationState { IDLE, @@ -42,6 +42,7 @@ export class Session { public projectRootPath: string = '' public fileLanguage: string | undefined = 'plaintext' public stopIteration: boolean = false + public targetFileInfo: TargetFileInfo | undefined // Telemetry public testGenerationStartTime: number = 0 @@ -64,7 +65,7 @@ export class Session { public testCoveragePercentage: number = 90 public isInProgress: boolean = false public acceptedJobId = '' - public references: ShortAnswerReference[] = [] + public references: Reference[] = [] constructor() {} diff --git a/packages/core/src/codewhisperer/client/user-service-2.json b/packages/core/src/codewhisperer/client/user-service-2.json index 1f33cb8c98c..ed7661bec4c 100644 --- a/packages/core/src/codewhisperer/client/user-service-2.json +++ b/packages/core/src/codewhisperer/client/user-service-2.json @@ -1760,6 +1760,39 @@ "type": "string", "enum": ["OPTIN", "OPTOUT"] }, + "PackageInfo": { + "type": "structure", + "members": { + "executionCommand": { "shape": "SensitiveString" }, + "buildCommand": { "shape": "SensitiveString" }, + "buildOrder": { "shape": "PackageInfoBuildOrderInteger" }, + "testFramework": { "shape": "String" }, + "packageSummary": { "shape": "PackageInfoPackageSummaryString" }, + "packagePlan": { "shape": "PackageInfoPackagePlanString" }, + "targetFileInfoList": { "shape": "TargetFileInfoList" } + } + }, + "PackageInfoBuildOrderInteger": { + "type": "integer", + "box": true, + "min": 0 + }, + "PackageInfoList": { + "type": "list", + "member": { "shape": "PackageInfo" } + }, + "PackageInfoPackagePlanString": { + "type": "string", + "max": 30720, + "min": 0, + "sensitive": true + }, + "PackageInfoPackageSummaryString": { + "type": "string", + "max": 30720, + "min": 0, + "sensitive": true + }, "PaginationToken": { "type": "string", "max": 2048, @@ -2418,6 +2451,45 @@ "min": 1, "sensitive": true }, + "TargetFileInfo": { + "type": "structure", + "members": { + "filePath": { "shape": "SensitiveString" }, + "testFilePath": { "shape": "SensitiveString" }, + "testCoverage": { "shape": "TargetFileInfoTestCoverageInteger" }, + "fileSummary": { "shape": "TargetFileInfoFileSummaryString" }, + "filePlan": { "shape": "TargetFileInfoFilePlanString" }, + "codeReferences": { "shape": "References" }, + "numberOfTestMethods": { "shape": "TargetFileInfoNumberOfTestMethodsInteger" } + } + }, + "TargetFileInfoFilePlanString": { + "type": "string", + "max": 30720, + "min": 0, + "sensitive": true + }, + "TargetFileInfoFileSummaryString": { + "type": "string", + "max": 30720, + "min": 0, + "sensitive": true + }, + "TargetFileInfoList": { + "type": "list", + "member": { "shape": "TargetFileInfo" } + }, + "TargetFileInfoNumberOfTestMethodsInteger": { + "type": "integer", + "box": true, + "min": 0 + }, + "TargetFileInfoTestCoverageInteger": { + "type": "integer", + "box": true, + "max": 100, + "min": 0 + }, "TaskAssistPlan": { "type": "list", "member": { "shape": "TaskAssistPlanStep" }, @@ -2556,7 +2628,11 @@ "status": { "shape": "TestGenerationJobStatus" }, "shortAnswer": { "shape": "SensitiveString" }, "creationTime": { "shape": "Timestamp" }, - "progressRate": { "shape": "TestGenerationJobProgressRateInteger" } + "progressRate": { "shape": "TestGenerationJobProgressRateInteger" }, + "jobStatusReason": { "shape": "String" }, + "jobSummary": { "shape": "TestGenerationJobJobSummaryString" }, + "jobPlan": { "shape": "TestGenerationJobJobPlanString" }, + "packageInfoList": { "shape": "PackageInfoList" } }, "documentation": "

Represents a test generation job

" }, @@ -2567,6 +2643,18 @@ "min": 1, "pattern": "[a-zA-Z0-9-_]+" }, + "TestGenerationJobJobPlanString": { + "type": "string", + "max": 30720, + "min": 0, + "sensitive": true + }, + "TestGenerationJobJobSummaryString": { + "type": "string", + "max": 30720, + "min": 0, + "sensitive": true + }, "TestGenerationJobProgressRateInteger": { "type": "integer", "box": true, diff --git a/packages/core/src/codewhisperer/models/model.ts b/packages/core/src/codewhisperer/models/model.ts index f7d1fe60f1b..f7a150cb1f0 100644 --- a/packages/core/src/codewhisperer/models/model.ts +++ b/packages/core/src/codewhisperer/models/model.ts @@ -1165,7 +1165,7 @@ export interface FolderInfo { name: string } -export interface ShortAnswerReference { +export interface Reference { licenseName?: string repository?: string url?: string @@ -1175,6 +1175,7 @@ export interface ShortAnswerReference { } } +// TODO: remove ShortAnswer because it will be deprecated export interface ShortAnswer { testFilePath: string buildCommands: string[] @@ -1185,6 +1186,6 @@ export interface ShortAnswer { testCoverage?: number stopIteration?: string errorMessage?: string - codeReferences?: ShortAnswerReference[] + codeReferences?: References numberOfTestMethods?: number } diff --git a/packages/core/src/codewhisperer/service/testGenHandler.ts b/packages/core/src/codewhisperer/service/testGenHandler.ts index cb2875ab78e..c3a6ebae148 100644 --- a/packages/core/src/codewhisperer/service/testGenHandler.ts +++ b/packages/core/src/codewhisperer/service/testGenHandler.ts @@ -18,12 +18,11 @@ import { CreateUploadUrlError, ExportResultsArchiveError, InvalidSourceZipError, - TestGenFailedError, TestGenStoppedError, TestGenTimedOutError, } from '../../amazonqTest/error' import { getMd5, uploadArtifactToS3 } from './securityScanHandler' -import { ShortAnswer, testGenState } from '../models/model' +import { testGenState, Reference } from '../models/model' import { ChatSessionManager } from '../../amazonqTest/chat/storages/chatSession' import { createCodeWhispererChatStreamingClient } from '../../shared/clients/codewhispererChatClient' import { downloadExportResultArchive } from '../../shared/utilities/download' @@ -156,35 +155,32 @@ export async function pollTestJobStatus( status: 'InProgress', progressRate, }) - const shortAnswerString = resp.testGenerationJob?.shortAnswer - if (shortAnswerString) { - const parsedShortAnswer = JSON.parse(shortAnswerString) - const shortAnswer: ShortAnswer = JSON.parse(parsedShortAnswer) - // Stop the Unit test generation workflow if IDE receive stopIteration = true - if (shortAnswer.stopIteration === 'true') { - session.stopIteration = true - throw new TestGenFailedError(shortAnswer.planSummary) - } - if (shortAnswer.numberOfTestMethods) { - session.numberOfTestsGenerated = Number(shortAnswer.numberOfTestMethods) + const packageInfoList = resp.testGenerationJob?.packageInfoList ?? [] + const packageInfo = packageInfoList[0] + const targetFileInfo = packageInfo?.targetFileInfoList?.[0] + + if (packageInfo) { + // TODO: will need some fields from packageInfo + } + if (targetFileInfo) { + if (targetFileInfo.numberOfTestMethods) { + session.numberOfTestsGenerated = Number(targetFileInfo.numberOfTestMethods) } - if (shortAnswer.codeReferences) { - session.references = shortAnswer.codeReferences + if (targetFileInfo.codeReferences) { + session.references = targetFileInfo.codeReferences as Reference[] } if (initialExecution) { - session.generatedFilePath = shortAnswer?.testFilePath ?? '' - const currentPlanSummary = session.shortAnswer?.planSummary - const newPlanSummary = shortAnswer?.planSummary - const status = shortAnswer.stopIteration + session.generatedFilePath = targetFileInfo.testFilePath ?? '' + const currentPlanSummary = session.targetFileInfo?.filePlan + const newPlanSummary = targetFileInfo?.filePlan if (currentPlanSummary !== newPlanSummary && newPlanSummary) { const chatControllers = testGenState.getChatControllers() if (chatControllers) { const currentSession = ChatSessionManager.Instance.getSession() - chatControllers.updateShortAnswer.fire({ + chatControllers.updateTargetFileInfo.fire({ tabID: currentSession.tabID, - status, - shortAnswer, + targetFileInfo, testGenerationJobGroupName: resp.testGenerationJob?.testGenerationJobGroupName, testGenerationJobId: resp.testGenerationJob?.testGenerationJobId, filePath, @@ -192,8 +188,8 @@ export async function pollTestJobStatus( } } } - ChatSessionManager.Instance.getSession().shortAnswer = shortAnswer } + ChatSessionManager.Instance.getSession().targetFileInfo = targetFileInfo if (resp.testGenerationJob?.status !== CodeWhispererConstants.TestGenerationJobStatus.IN_PROGRESS) { // This can be FAILED or COMPLETED status = resp.testGenerationJob?.status as CodeWhispererConstants.TestGenerationJobStatus @@ -243,7 +239,7 @@ export async function exportResultsArchive( const zip = new AdmZip(pathToArchive) zip.extractAllTo(pathToArchiveDir, true) - const testFilePathFromResponse = session?.shortAnswer?.testFilePath + const testFilePathFromResponse = session?.targetFileInfo?.testFilePath const testFilePath = testFilePathFromResponse ? testFilePathFromResponse.split('/').slice(1).join('/') // remove the project name : await getTestFilePathFromZip(pathToArchiveDir) From 6f21d0613c7713cdea1c8e541faf394ed5bdcfcf Mon Sep 17 00:00:00 2001 From: Jacob Chung Date: Fri, 7 Mar 2025 10:32:09 -0800 Subject: [PATCH 2/4] TODO --- packages/core/src/codewhisperer/service/testGenHandler.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/src/codewhisperer/service/testGenHandler.ts b/packages/core/src/codewhisperer/service/testGenHandler.ts index c3a6ebae148..8228ad7431b 100644 --- a/packages/core/src/codewhisperer/service/testGenHandler.ts +++ b/packages/core/src/codewhisperer/service/testGenHandler.ts @@ -160,7 +160,7 @@ export async function pollTestJobStatus( const targetFileInfo = packageInfo?.targetFileInfoList?.[0] if (packageInfo) { - // TODO: will need some fields from packageInfo + // TODO: will need some fields from packageInfo such as buildCommand, packagePlan, packageSummary } if (targetFileInfo) { if (targetFileInfo.numberOfTestMethods) { From 99f5cfca9accaa58b3ae94c2af6367021875d53d Mon Sep 17 00:00:00 2001 From: Jacob Chung Date: Tue, 11 Mar 2025 14:27:49 -0700 Subject: [PATCH 3/4] display test plan summary --- .../Feature-96409066-931c-4488-b943-7bee68626547.json | 4 ++++ packages/core/src/amazonqTest/chat/controller/controller.ts | 2 +- packages/core/src/amazonqTest/chat/session/session.ts | 1 + packages/core/src/codewhisperer/service/testGenHandler.ts | 3 +++ 4 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 packages/amazonq/.changes/next-release/Feature-96409066-931c-4488-b943-7bee68626547.json diff --git a/packages/amazonq/.changes/next-release/Feature-96409066-931c-4488-b943-7bee68626547.json b/packages/amazonq/.changes/next-release/Feature-96409066-931c-4488-b943-7bee68626547.json new file mode 100644 index 00000000000..43ea135f6f9 --- /dev/null +++ b/packages/amazonq/.changes/next-release/Feature-96409066-931c-4488-b943-7bee68626547.json @@ -0,0 +1,4 @@ +{ + "type": "Feature", + "description": "/test: display test plan summary in chat after generating tests" +} diff --git a/packages/core/src/amazonqTest/chat/controller/controller.ts b/packages/core/src/amazonqTest/chat/controller/controller.ts index fd798309f35..d269b7fab24 100644 --- a/packages/core/src/amazonqTest/chat/controller/controller.ts +++ b/packages/core/src/amazonqTest/chat/controller/controller.ts @@ -653,7 +653,7 @@ export class TestController { tabID: data.tabID, messageType: 'answer', codeGenerationId: '', - message: `Please see the unit tests generated below. Click “View diff” to review the changes in the code editor.`, + message: `${session.jobSummary}\n\n Please see the unit tests generated below. Click "View diff" to review the changes in the code editor.`, canBeVoted: true, messageId: '', followUps, diff --git a/packages/core/src/amazonqTest/chat/session/session.ts b/packages/core/src/amazonqTest/chat/session/session.ts index 4ae4dfd2f4d..6cabfd8677e 100644 --- a/packages/core/src/amazonqTest/chat/session/session.ts +++ b/packages/core/src/amazonqTest/chat/session/session.ts @@ -43,6 +43,7 @@ export class Session { public fileLanguage: string | undefined = 'plaintext' public stopIteration: boolean = false public targetFileInfo: TargetFileInfo | undefined + public jobSummary: string = '' // Telemetry public testGenerationStartTime: number = 0 diff --git a/packages/core/src/codewhisperer/service/testGenHandler.ts b/packages/core/src/codewhisperer/service/testGenHandler.ts index 8228ad7431b..bd3f2167d83 100644 --- a/packages/core/src/codewhisperer/service/testGenHandler.ts +++ b/packages/core/src/codewhisperer/service/testGenHandler.ts @@ -155,6 +155,9 @@ export async function pollTestJobStatus( status: 'InProgress', progressRate, }) + const jobSummary = resp.testGenerationJob?.jobSummary ?? '' + const jobSummaryNoBackticks = jobSummary.replace(/^`+|`+$/g, '') + ChatSessionManager.Instance.getSession().jobSummary = jobSummaryNoBackticks const packageInfoList = resp.testGenerationJob?.packageInfoList ?? [] const packageInfo = packageInfoList[0] const targetFileInfo = packageInfo?.targetFileInfoList?.[0] From 7fc20168a332597ede9a6ffb4ad64694e8b37ca7 Mon Sep 17 00:00:00 2001 From: Jacob Chung Date: Tue, 11 Mar 2025 14:37:15 -0700 Subject: [PATCH 4/4] nit --- packages/core/src/amazonqTest/chat/controller/controller.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/src/amazonqTest/chat/controller/controller.ts b/packages/core/src/amazonqTest/chat/controller/controller.ts index f4276f61daf..4e29bb31464 100644 --- a/packages/core/src/amazonqTest/chat/controller/controller.ts +++ b/packages/core/src/amazonqTest/chat/controller/controller.ts @@ -705,7 +705,7 @@ export class TestController { tabID: data.tabID, messageType: 'answer', codeGenerationId: '', - message: `${session.jobSummary}\n\n Please see the unit tests generated below. Click "View diff" to review the changes in the code editor.`, + message: `${session.jobSummary}\n\n Please see the unit tests generated below. Click “View diff” to review the changes in the code editor.`, canBeVoted: true, messageId: '', followUps,