feat(api): add CloudWatch alarms and metric charts to the cloud explorer
CI / checks (pull_request) Successful in 27m29s

This commit is contained in:
Zakaria
2026-10-24 22:48:41 -04:00
parent f4b3e45f3d
commit 6dd33b8f65
26 changed files with 1551 additions and 1 deletions
@@ -0,0 +1,74 @@
import {QueryClient, QueryClientProvider} from '@tanstack/react-query'
import {render, screen, within} from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import {beforeEach, expect, test, vi} from 'vitest'
import {CloudWatchAlarmPanel} from './CloudWatchAlarmPanel'
import {HttpError} from '@/api/HttpClient'
import type {CloudResource} from '@/types/resource'
const api = vi.hoisted(() => ({
getCloudResource: vi.fn(),
describeAlarmHistory: vi.fn(),
getMetricSeries: vi.fn(),
setAlarmState: vi.fn(),
setAlarmActionsEnabled: vi.fn(),
}))
vi.mock('@/api/cloudProxyClient', () => api)
vi.mock('@/lib/accountStore', () => ({useAccountId: () => '000000000000'}))
const alarm: CloudResource = {
id: 'high-errors', name: 'high-errors', cloud: 'aws', service: 'monitoring', type: 'alarm', region: 'us-east-1', createdAt: null, status: 'ALARM',
metadata: {
alarmType: 'metric', stateReason: 'Threshold crossed', actionsEnabled: true, alarmActions: ['arn:aws:sns:us-east-1:000000000000:alerts'], okActions: [],
namespace: 'App', metricName: 'Errors', dimensions: [], statistic: 'Sum', threshold: 5, condition: 'Errors Sum > 5 for 1 × 5 min',
},
}
function renderPanel() {
const client = new QueryClient({defaultOptions: {queries: {retry: false}}})
return render(<QueryClientProvider client={client}><CloudWatchAlarmPanel resource={alarm} runtimeReachable/></QueryClientProvider>)
}
beforeEach(() => {
vi.clearAllMocks()
api.getCloudResource.mockResolvedValue(alarm)
api.describeAlarmHistory.mockResolvedValue([{timestamp: null, type: 'StateUpdate', summary: 'Alarm updated from OK to ALARM'}])
api.getMetricSeries.mockResolvedValue({label: 'Errors', statistic: 'Sum', periodSeconds: 60, points: [{timestamp: '2026-10-25T10:00:00Z', value: 7}]})
api.setAlarmState.mockResolvedValue(undefined)
api.setAlarmActionsEnabled.mockResolvedValue(undefined)
})
test('shows the state, its metric with the threshold, the targets and the history', async () => {
renderPanel()
expect(screen.getByText('Threshold crossed')).toBeInTheDocument()
expect(await screen.findByText('Threshold 5')).toBeInTheDocument()
expect(screen.getByText('arn:aws:sns:us-east-1:000000000000:alerts')).toBeInTheDocument()
expect(await screen.findByText('Alarm updated from OK to ALARM')).toBeInTheDocument()
expect(api.getMetricSeries).toHaveBeenCalledWith('aws', expect.objectContaining({namespace: 'App', metricName: 'Errors', statistic: 'Sum', rangeMinutes: 180}), expect.anything())
})
test('sets a state to test the actions, and turns the actions off', async () => {
const user = userEvent.setup()
renderPanel()
const form = await screen.findByRole('form', {name: 'Set alarm state'})
await user.selectOptions(within(form).getByLabelText('State'), 'OK')
await user.type(within(form).getByLabelText('Reason'), 'Testing')
await user.click(within(form).getByRole('button', {name: 'Set state'}))
expect(api.setAlarmState).toHaveBeenCalledWith('aws', 'high-errors', 'OK', 'Testing')
expect(await screen.findByText(/Set to OK/)).toBeInTheDocument()
await user.click(within(form).getByRole('button', {name: 'Turn actions off'}))
expect(api.setAlarmActionsEnabled).toHaveBeenCalledWith('aws', 'high-errors', false)
})
test('says when the runtime does not record alarm history, without an error', async () => {
api.describeAlarmHistory.mockRejectedValue(new HttpError(501, 'Not Implemented', {code: 'operation_not_implemented', error: 'Operation is not implemented by the selected runtime'}, '/history'))
renderPanel()
expect(await screen.findByText(/does not record alarm history yet/)).toBeInTheDocument()
expect(screen.queryByRole('alert')).not.toBeInTheDocument()
})