private async startTestGen()

in packages/core/src/amazonqTest/chat/controller/controller.ts [458:612]


    private async startTestGen(message: any, regenerateTests: boolean) {
        const session: Session = this.sessionStorage.getSession()
        // Perform session cleanup before start of unit test generation workflow unless there is an existing job in progress.
        if (!ChatSessionManager.Instance.getIsInProgress()) {
            await this.sessionCleanUp()
        }
        const tabID = this.sessionStorage.setActiveTab(message.tabID)
        getLogger().debug('startTestGen message: %O', message)
        getLogger().debug('startTestGen tabId: %O', message.tabID)
        let fileName = ''
        let filePath = ''
        let userFacingMessage = ''
        let userPrompt = ''
        session.testGenerationStartTime = performance.now()

        try {
            if (ChatSessionManager.Instance.getIsInProgress()) {
                void vscode.window.showInformationMessage(
                    "There is already a test generation job in progress. Cancel current job or wait until it's finished to try again."
                )
                return
            }
            if (testGenState.isCancelling()) {
                void vscode.window.showInformationMessage(
                    'There is a test generation job being cancelled. Please wait for cancellation to finish.'
                )
                return
            }
            // Truncating the user prompt if the prompt is more than 4096.
            userPrompt = message.prompt.slice(0, maxUserPromptLength)

            // check that the session is authenticated
            const authState = await AuthUtil.instance.getChatAuthState()
            if (authState.amazonQ !== 'connected') {
                void this.messenger.sendAuthNeededExceptionMessage(authState, tabID)
                session.isAuthenticating = true
                return
            }

            // check that a project/workspace is open
            const workspaceFolders = vscode.workspace.workspaceFolders
            if (workspaceFolders === undefined || workspaceFolders.length === 0) {
                this.messenger.sendUnrecoverableErrorResponse('no-project-found', tabID)
                return
            }

            // check if IDE has active file open.
            const activeEditor = vscode.window.activeTextEditor
            // also check all open editors and allow this to proceed if only one is open (even if not main focus)
            const allVisibleEditors = vscode.window.visibleTextEditors
            const openFileEditors = allVisibleEditors.filter((editor) => editor.document.uri.scheme === 'file')
            const hasOnlyOneOpenFileSplitView = openFileEditors.length === 1
            getLogger().debug(`hasOnlyOneOpenSplitView: ${hasOnlyOneOpenFileSplitView}`)
            // is not a file if the currently highlighted window is not a file, and there is either more than one or no file windows open
            const isNotFile = activeEditor?.document.uri.scheme !== 'file' && !hasOnlyOneOpenFileSplitView
            getLogger().debug(`activeEditor: ${activeEditor}, isNotFile: ${isNotFile}`)
            if (!activeEditor || isNotFile) {
                this.messenger.sendUnrecoverableErrorResponse(
                    isNotFile ? 'invalid-file-type' : 'no-open-file-found',
                    tabID
                )
                this.messenger.sendUpdatePlaceholder(
                    tabID,
                    'Please open and highlight a source code file in order to generate tests.'
                )
                this.messenger.sendChatInputEnabled(tabID, true)
                this.sessionStorage.getSession().conversationState = ConversationState.WAITING_FOR_INPUT
                return
            }

            const fileEditorToTest = hasOnlyOneOpenFileSplitView ? openFileEditors[0] : activeEditor
            getLogger().debug(`File path: ${fileEditorToTest.document.uri.fsPath}`)
            filePath = fileEditorToTest.document.uri.fsPath
            fileName = path.basename(filePath)
            userFacingMessage = userPrompt
                ? regenerateTests
                    ? `${userPrompt}`
                    : `/test ${userPrompt}`
                : `/test Generate unit tests for \`${fileName}\``

            session.hasUserPromptSupplied = userPrompt.length > 0

            // displaying user message prompt in Test tab
            this.messenger.sendMessage(userFacingMessage, tabID, 'prompt')
            this.messenger.sendChatInputEnabled(tabID, false)
            this.sessionStorage.getSession().conversationState = ConversationState.IN_PROGRESS
            this.messenger.sendUpdatePromptProgress(message.tabID, testGenProgressField)

            const language = await this.getLanguageForFilePath(filePath)
            session.fileLanguage = language
            const workspaceFolder = vscode.workspace.getWorkspaceFolder(fileEditorToTest.document.uri)

            /*
                For Re:Invent 2024 we are supporting only java and python for unit test generation, rest of the languages shows the similar experience as CWC
            */
            if (!['java', 'python'].includes(language) || workspaceFolder === undefined) {
                if (!workspaceFolder) {
                    // File is outside of workspace
                    const unsupportedMessage = `<span style="color: #EE9D28;">&#9888;<b>I can't generate tests for ${fileName}</b> because the file is outside of workspace scope.<br></span> I can still provide examples, instructions and code suggestions.`
                    this.messenger.sendMessage(unsupportedMessage, tabID, 'answer')
                }
                // Keeping this metric as is. TODO - Change to true once we support through other feature
                session.isSupportedLanguage = false
                await this.onCodeGeneration(
                    session,
                    userPrompt,
                    tabID,
                    fileName,
                    filePath,
                    workspaceFolder !== undefined
                )
            } else {
                this.messenger.sendCapabilityCard({ tabID })
                this.messenger.sendMessage(testGenSummaryMessage(fileName), message.tabID, 'answer-part')

                // Grab the selection from the fileEditorToTest and get the vscode Range
                const selection = fileEditorToTest.selection
                let selectionRange = undefined
                if (
                    selection.start.line !== selection.end.line ||
                    selection.start.character !== selection.end.character
                ) {
                    selectionRange = new vscode.Range(
                        selection.start.line,
                        selection.start.character,
                        selection.end.line,
                        selection.end.character
                    )
                }
                session.isCodeBlockSelected = selectionRange !== undefined
                session.isSupportedLanguage = true

                /**
                 * Zip the project
                 * Create pre-signed URL and upload artifact to S3
                 * send API request to startTestGeneration API
                 * Poll from getTestGeneration API
                 * Get Diff from exportResultArchive API
                 */
                ChatSessionManager.Instance.setIsInProgress(true)
                await startTestGenerationProcess(filePath, message.prompt, tabID, true, selectionRange)
            }
        } catch (err: any) {
            // TODO: refactor error handling to be more robust
            ChatSessionManager.Instance.setIsInProgress(false)
            getLogger().error('startTestGen failed: %O', err)
            this.messenger.sendUpdatePromptProgress(message.tabID, cancellingProgressField)
            this.sendErrorMessage({ tabID, error: err })
            this.messenger.sendChatInputEnabled(tabID, true)
            this.sessionStorage.getSession().conversationState = ConversationState.WAITING_FOR_INPUT
            await sleep(2000)
            // eslint-disable-next-line unicorn/no-null
            this.messenger.sendUpdatePromptProgress(message.tabID, null)
        }
    }