75 lines
3.7 KiB
TypeScript
75 lines
3.7 KiB
TypeScript
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()
|
||
})
|
||
|