Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
### [Report an Issue](https://github.com/atlassian/atlascode/issues)

## What's new in 4.0.32

### Bug Fixes

- **RovoDev**: Fixed `AxiosError` in `RovoDevFeedbackManager.submitFeedback` being sent to Sentry and triggering SLO errors. Feedback submission failures (network errors, non-2xx responses) are now logged as warnings instead, since they are non-critical and expected in transient failure scenarios.

## What's new in 4.0.31

### Bug Fixes
Expand Down
61 changes: 50 additions & 11 deletions src/rovo-dev/rovoDevFeedbackManager.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,23 +12,18 @@ jest.mock('./api/extensionApi', () => ({
ExtensionApi: jest.fn().mockImplementation(() => mockExtensionApiInstance),
}));

jest.mock('./rovoDevTelemetryProvider', () => ({
RovoDevTelemetryProvider: {
logError: jest.fn(),
},
}));

jest.mock('lodash', () => ({
...jest.requireActual('lodash'),
truncate: jest.fn((str, options) => str),
}));

import { AxiosError } from 'axios';
import { Logger } from 'src/logger';
import { UserInfo } from 'src/rovo-dev/api/extensionApiTypes';
import * as vscode from 'vscode';

import { getAxiosInstance } from './api/extensionApi';
import { RovoDevFeedbackManager } from './rovoDevFeedbackManager';
import { RovoDevTelemetryProvider } from './rovoDevTelemetryProvider';

describe('RovoDevFeedbackManager', () => {
const mockTransport = jest.fn();
Expand Down Expand Up @@ -158,8 +153,50 @@ describe('RovoDevFeedbackManager', () => {
expect(vscode.window.showErrorMessage).not.toHaveBeenCalled();
});

it('should handle submission error and show error message', async () => {
const error = new Error('Network error');
it('should handle AxiosError with response status and log warning without sending to Sentry', async () => {
const axiosError = new AxiosError('Request failed with status code 500', '500', undefined, undefined, {
status: 500,
statusText: 'Internal Server Error',
headers: {},
config: {} as any,
data: {},
});
mockTransport.mockRejectedValue(axiosError);

const feedback = {
feedbackType: 'general' as const,
feedbackMessage: 'Test feedback',
canContact: false,
};

await RovoDevFeedbackManager.submitFeedback(feedback);

expect(Logger.warn).toHaveBeenCalledWith(expect.stringContaining('HTTP 500'));
expect(vscode.window.showErrorMessage).toHaveBeenCalledWith(
'There was an error submitting your feedback. Please try again later.',
);
});

it('should handle AxiosError without response (network error) and log warning', async () => {
const axiosError = new AxiosError('Network Error', 'ERR_NETWORK');
mockTransport.mockRejectedValue(axiosError);

const feedback = {
feedbackType: 'general' as const,
feedbackMessage: 'Test feedback',
canContact: false,
};

await RovoDevFeedbackManager.submitFeedback(feedback);

expect(Logger.warn).toHaveBeenCalledWith(expect.stringContaining('Network error'));
expect(vscode.window.showErrorMessage).toHaveBeenCalledWith(
'There was an error submitting your feedback. Please try again later.',
);
});

it('should handle non-Axios errors and log warning', async () => {
const error = new Error('Unexpected error');
mockTransport.mockRejectedValue(error);

const feedback = {
Expand All @@ -170,11 +207,13 @@ describe('RovoDevFeedbackManager', () => {

await RovoDevFeedbackManager.submitFeedback(feedback);

expect(RovoDevTelemetryProvider.logError).toHaveBeenCalledWith(error, 'Error submitting Rovo Dev feedback');
expect(Logger.warn).toHaveBeenCalledWith(
'Feedback submission failed with unexpected error:',
'Error: Unexpected error',
);
expect(vscode.window.showErrorMessage).toHaveBeenCalledWith(
'There was an error submitting your feedback. Please try again later.',
);
expect(vscode.window.showInformationMessage).not.toHaveBeenCalled();
});

it('should include context information in payload: BBY', async () => {
Expand Down
11 changes: 9 additions & 2 deletions src/rovo-dev/rovoDevFeedbackManager.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { isAxiosError } from 'axios';
import { truncate } from 'lodash';
import { Logger } from 'src/logger';
import { UserInfo } from 'src/rovo-dev/api/extensionApiTypes';
import * as vscode from 'vscode';

import { ExtensionApi, getAxiosInstance } from './api/extensionApi';
import { MIN_SUPPORTED_ROVODEV_VERSION } from './rovoDevProcessManager';
import { RovoDevTelemetryProvider } from './rovoDevTelemetryProvider';

interface FeedbackObject {
feedbackType: 'bug' | 'reportContent' | 'general';
Expand Down Expand Up @@ -91,7 +92,13 @@ export class RovoDevFeedbackManager {
data: payload,
});
} catch (error) {
RovoDevTelemetryProvider.logError(error, 'Error submitting Rovo Dev feedback');
if (isAxiosError(error)) {
const status = error.response?.status;
const message = error.message;
Logger.warn(`Feedback submission failed: ${status ? `HTTP ${status}` : 'Network error'} - ${message}`);
} else {
Logger.warn('Feedback submission failed with unexpected error:', String(error));
}
vscode.window.showErrorMessage('There was an error submitting your feedback. Please try again later.');
return;
}
Expand Down
Loading