-
Notifications
You must be signed in to change notification settings - Fork 8.6k
[Synthetics] Detect and display missing/corrupted Synthetics integrations in monitor UIs #256738
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
miguelmartin-elastic
merged 115 commits into
elastic:main
from
miguelmartin-elastic:synthetics/missing-integrations-ui
Apr 6, 2026
Merged
Changes from all commits
Commits
Show all changes
115 commits
Select commit
Hold shift + click to select a range
7736576
mock reset endpoint, UI first approach
miguelmartin-elastic daf42b8
pass monitors as prop
miguelmartin-elastic c6d6565
use icon with tooltip for missing policy warning
miguelmartin-elastic a3b0758
fetch configs from hook if none is provided
miguelmartin-elastic 043faaf
_health api spec proposal
miguelmartin-elastic 32ea22e
refactor monirots_integration_health
miguelmartin-elastic b01ce6a
add unit tests
miguelmartin-elastic 889718b
move types to common/
miguelmartin-elastic de0a995
rename from status to health
miguelmartin-elastic 86e9383
fix import
miguelmartin-elastic e4ff75e
i18n status display messages
miguelmartin-elastic 8f77683
store configId for debugging purposes
miguelmartin-elastic d16abdd
remove unused import
miguelmartin-elastic 37e3483
check if agentPolicy exists
miguelmartin-elastic d379d26
rename isUnhealthy to be more generic
miguelmartin-elastic e91cfb4
refactor unhealthy tooltip
miguelmartin-elastic e143d07
refactor ResetLocationMonitors
miguelmartin-elastic 7908d0b
checkout bulk operation reset
miguelmartin-elastic c60be8b
parse error from reason
miguelmartin-elastic 03df478
remove oas docs
miguelmartin-elastic 3ee0c34
move health api to service
miguelmartin-elastic c5cc305
fix naming
miguelmartin-elastic 00cc5a5
Changes from node scripts/eslint_all_files --no-cache --fix
kibanamachine 5bc168b
fix mock
miguelmartin-elastic 1c82841
Merge branch 'synthetics/missing-integrations-ui' of https://github.c…
miguelmartin-elastic 8fa37fe
Merge branch 'main' into synthetics/missing-integrations-ui
miguelmartin-elastic d5d0b36
Merge branch 'main' into synthetics/missing-integrations-ui
miguelmartin-elastic 158ac3d
remove spaceId
miguelmartin-elastic 2111b8f
Changes from node scripts/eslint_all_files --no-cache --fix
kibanamachine 19cf9c5
fix type
miguelmartin-elastic 996629e
Merge branch 'synthetics/missing-integrations-ui' of https://github.c…
miguelmartin-elastic e9581aa
Changes from node scripts/eslint_all_files --no-cache --fix
kibanamachine 42a2722
Support legacy policy ID format in monitor health checks
miguelmartin-elastic 4d7c9e9
Document intentional priority order of health status checks
miguelmartin-elastic 947aa55
Changes from node scripts/eslint_all_files --no-cache --fix
kibanamachine 1ad223d
Merge branch 'main' of https://github.com/elastic/kibana into synthet…
miguelmartin-elastic b0f8372
wire up reset api, add unit tests
miguelmartin-elastic b9a80f6
add toast feedback and reset confirmation modal
miguelmartin-elastic c68b4fd
listen to refresh
miguelmartin-elastic 7a9783c
Merge branch 'main' into synthetics/missing-integrations-ui
miguelmartin-elastic 10cb00b
Merge branch 'synthetics/missing-integrations-ui' of https://github.c…
miguelmartin-elastic f9886a9
Changes from node scripts/eslint_all_files --no-cache --fix
kibanamachine 1c75100
Merge branch 'main' into synthetics/missing-integrations-ui
miguelmartin-elastic 1a672bf
Merge branch 'main' into synthetics/missing-integrations-ui
miguelmartin-elastic 1347bf1
fix integ test
miguelmartin-elastic 14bb9da
* simplify reset callouts by removing internal state
miguelmartin-elastic b6a66fa
do not mark monitors with public locations only as unhealthy, cover w…
miguelmartin-elastic 6b40f25
fix minor issues
miguelmartin-elastic 0965ae8
Changes from node scripts/eslint_all_files --no-cache --fix
kibanamachine 29e9733
handle partial errors from reset api
miguelmartin-elastic 7880bf3
Revert "fix integ test"
miguelmartin-elastic 7a20f23
Merge branch 'main' of https://github.com/elastic/kibana into synthet…
miguelmartin-elastic b3a0c62
Merge branch 'synthetics/missing-integrations-ui' of https://github.c…
miguelmartin-elastic 17cd6a5
return 500 status code when reset call fails
miguelmartin-elastic 5a8232e
Merge branch 'main' into synthetics/missing-integrations-ui
miguelmartin-elastic e94d3ec
Merge branch 'synthetics/missing-integrations-ui' of https://github.c…
miguelmartin-elastic 2a35b5c
add reset action to monitor list row actions menu
miguelmartin-elastic 7016a7d
exclude non-fixable monitors from bulk reset and show skipped in modal
miguelmartin-elastic 1e949ef
skip locations with missing agent policy during default reset
miguelmartin-elastic 0cc68b0
rename getValidLocationIds and fix empty agentPolicyId edge case, add…
miguelmartin-elastic 5dbf14d
convert private locations reset action to icon type for consistent ac…
miguelmartin-elastic ac733fe
move missing integration callout from edit page to monitor details su…
miguelmartin-elastic d1c76da
add configIds filter to monitor list and link unhealthy badge to filt…
miguelmartin-elastic 764371a
align single and bulk health API error response field from 'error' to…
miguelmartin-elastic 0a340ce
add missingAgentPolicyId
miguelmartin-elastic 1dd1868
rename locations to privateLocations
miguelmartin-elastic 2495f94
rename isUnhealthy to isHealthy in MonitorHealthStatus
miguelmartin-elastic 5d6c994
Changes from node scripts/eslint_all_files --no-cache --fix
kibanamachine 6737a3c
include configIds in isMonitorsQueryFiltered to fix absoluteTotal count
miguelmartin-elastic 1f943f3
fix Reset action enabled guards in monitor list actions column
miguelmartin-elastic 5164a7f
add configIds to expected defaults in getSupportedUrlParams test
miguelmartin-elastic bb06301
Merge branch 'synthetics/missing-integrations-ui' of https://github.c…
miguelmartin-elastic 48e31d5
Merge branch 'main' of https://github.com/elastic/kibana into synthet…
miguelmartin-elastic cdefd2d
Changes from node scripts/eslint_all_files --no-cache --fix
kibanamachine 65ee6e6
Remove break_monitors.sh test script (A8)
miguelmartin-elastic ed4b8d8
Add writeAccess: false to GET monitor health route (A1)
miguelmartin-elastic bfdf133
Add permission guards to Reset action in monitor list (A2, A3)
miguelmartin-elastic d151aff
Rename isResetFixable → isFixableByReset (A5, A6)
miguelmartin-elastic 2ed2415
Remove unused getUnhealthyLocationCount function (A7)
miguelmartin-elastic 0335af5
Fix skippedMonitors in locations_table reset action (A4)
miguelmartin-elastic 277887e
Fix multiline rendering in unhealthy tooltip (A9)
miguelmartin-elastic e25f9c3
Simplify getUnhealthyMonitorsForLocation: remove unnecessary seenConf…
miguelmartin-elastic 549360c
Improve unhealthy tooltip visual hierarchy (A9 enhancement)
miguelmartin-elastic ab25068
Refactor unhealthy tooltip to use EUI components instead of inline st…
miguelmartin-elastic 0aeab27
rename explicitConfigIds to configIds
miguelmartin-elastic 9010313
Improve error statusCode capture to include all SavedObjects error types
miguelmartin-elastic 2fcb0e4
Improve error statusCode capture to include all SavedObjects error types
miguelmartin-elastic 77b2a7b
Add unit tests for statusCode capture from SavedObjects errors
miguelmartin-elastic c6f2f77
Simplify statusCode capture: use output.statusCode with 500 fallback
miguelmartin-elastic 3aa11ed
Update error tests to use real SavedObjects errors instead of generic…
miguelmartin-elastic 8fa943c
Make MonitorHealthError.statusCode required and propagate it in route…
miguelmartin-elastic 9b9d7e7
Fix reset action in private locations table not opening modal
miguelmartin-elastic c982ced
merge main
miguelmartin-elastic ec150e0
Merge branch 'main' into synthetics/missing-integrations-ui
miguelmartin-elastic abc0346
Changes from node scripts/eslint_all_files --no-cache --fix
kibanamachine 920f6f5
[Synthetics] Fix false "Failed to reset" toast when Fleet sync errors…
miguelmartin-elastic 1606e53
[Synthetics] Improve unhealthy tooltip styling per design feedback
miguelmartin-elastic ebf59fe
[Synthetics] Remove AgentPolicyMismatch and PackageNotInstalled healt…
miguelmartin-elastic b6abfac
Changes from node scripts/eslint_all_files --no-cache --fix
kibanamachine 88ce1bf
restructure delete action in private locations table to be icon-based…
miguelmartin-elastic 56c3c6d
Merge branch 'synthetics/missing-integrations-ui' of https://github.c…
miguelmartin-elastic a072b11
Merge remote-tracking branch 'upstream' into synthetics/missing-integ…
miguelmartin-elastic c3e738f
Changes from node scripts/eslint_all_files --no-cache --fix
kibanamachine cf023a0
remove unnecessary JSON.stringify from useEffect deps
miguelmartin-elastic 21e1b1e
run node scripts/i18n_check.js --fix
miguelmartin-elastic 921d465
Merge branch 'synthetics/missing-integrations-ui' of https://github.c…
miguelmartin-elastic 5f33b51
Changes from node scripts/eslint_all_files --no-cache --fix
kibanamachine 9d0640b
Merge branch 'main' into synthetics/missing-integrations-ui
miguelmartin-elastic 9275d27
fix unit test
miguelmartin-elastic 9b9a3a9
Merge branch 'synthetics/missing-integrations-ui' of https://github.c…
miguelmartin-elastic f0159d3
Changes from node scripts/eslint_all_files --no-cache --fix
kibanamachine 560c7b2
fix scout test
miguelmartin-elastic 8f981fe
Merge branch 'synthetics/missing-integrations-ui' of https://github.c…
miguelmartin-elastic 7df675a
fix scout test lint issue
miguelmartin-elastic c96927e
Changes from node scripts/eslint_all_files --no-cache --fix
kibanamachine File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
40 changes: 40 additions & 0 deletions
40
x-pack/solutions/observability/plugins/synthetics/common/runtime_types/monitor_health.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,40 @@ | ||
| /* | ||
| * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
| * or more contributor license agreements. Licensed under the Elastic License | ||
| * 2.0; you may not use this file except in compliance with the Elastic License | ||
| * 2.0. | ||
| */ | ||
|
|
||
| export enum PrivateLocationHealthStatusValue { | ||
| Healthy = 'healthy', | ||
| MissingPackagePolicy = 'missing_package_policy', | ||
| MissingAgentPolicy = 'missing_agent_policy', | ||
| MissingLocation = 'missing_location', | ||
| } | ||
|
|
||
| export interface PrivateLocationHealthStatus { | ||
| locationId: string; | ||
| locationLabel: string; | ||
| status: PrivateLocationHealthStatusValue; | ||
| packagePolicyId: string; | ||
| agentPolicyId?: string; | ||
| reason?: string; | ||
| } | ||
|
|
||
| export interface MonitorHealthStatus { | ||
| configId: string; | ||
| monitorName: string; | ||
| isHealthy: boolean; | ||
| privateLocations: PrivateLocationHealthStatus[]; | ||
| } | ||
|
|
||
| export interface MonitorHealthError { | ||
| configId: string; | ||
| message: string; | ||
| statusCode: number; | ||
| } | ||
|
|
||
| export interface MonitorsHealthResponse { | ||
| monitors: MonitorHealthStatus[]; | ||
| errors: MonitorHealthError[]; | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
46 changes: 46 additions & 0 deletions
46
...bility/plugins/synthetics/public/apps/synthetics/components/common/hooks/status_labels.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,46 @@ | ||
| /* | ||
| * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
| * or more contributor license agreements. Licensed under the Elastic License | ||
| * 2.0; you may not use this file except in compliance with the Elastic License | ||
| * 2.0. | ||
| */ | ||
|
|
||
| import { i18n } from '@kbn/i18n'; | ||
| import { PrivateLocationHealthStatusValue } from '../../../../../../common/runtime_types'; | ||
|
|
||
| export const STATUS_LABELS: Record< | ||
| Exclude<PrivateLocationHealthStatusValue, PrivateLocationHealthStatusValue.Healthy>, | ||
| string | ||
| > = { | ||
| [PrivateLocationHealthStatusValue.MissingPackagePolicy]: i18n.translate( | ||
| 'xpack.synthetics.monitorHealth.status.missingPackagePolicy', | ||
| { | ||
| defaultMessage: | ||
| 'The Fleet package policy for this monitor and private location pair does not exist.', | ||
| } | ||
| ), | ||
| [PrivateLocationHealthStatusValue.MissingAgentPolicy]: i18n.translate( | ||
| 'xpack.synthetics.monitorHealth.status.missingAgentPolicy', | ||
| { | ||
| defaultMessage: 'The agent policy referenced by this private location no longer exists.', | ||
| } | ||
| ), | ||
| [PrivateLocationHealthStatusValue.MissingLocation]: i18n.translate( | ||
| 'xpack.synthetics.monitorHealth.status.missingLocation', | ||
| { | ||
| defaultMessage: 'The monitor references a private location that no longer exists.', | ||
| } | ||
| ), | ||
| }; | ||
|
|
||
| export const getStatusLabel = (status: PrivateLocationHealthStatusValue): string | undefined => { | ||
| if (status === PrivateLocationHealthStatusValue.Healthy) return undefined; | ||
| return STATUS_LABELS[status]; | ||
| }; | ||
|
|
||
| export const RESET_FIXABLE_STATUSES = new Set([ | ||
| PrivateLocationHealthStatusValue.MissingPackagePolicy, | ||
| ]); | ||
|
|
||
| export const isFixableByResetStatus = (status: PrivateLocationHealthStatusValue): boolean => | ||
| RESET_FIXABLE_STATUSES.has(status); |
235 changes: 235 additions & 0 deletions
235
...ics/public/apps/synthetics/components/common/hooks/use_monitor_integration_health.test.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,235 @@ | ||
| /* | ||
| * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
| * or more contributor license agreements. Licensed under the Elastic License | ||
| * 2.0; you may not use this file except in compliance with the Elastic License | ||
| * 2.0. | ||
| */ | ||
|
|
||
| import { renderHook, act } from '@testing-library/react'; | ||
| import * as reactRedux from 'react-redux'; | ||
| import { PrivateLocationHealthStatusValue } from '../../../../../../common/runtime_types'; | ||
| import { useMonitorIntegrationHealth } from './use_monitor_integration_health'; | ||
|
|
||
| jest.mock('react-redux', () => ({ | ||
| ...jest.requireActual('react-redux'), | ||
| useSelector: jest.fn(), | ||
| useDispatch: jest.fn(), | ||
| })); | ||
|
|
||
| jest.mock('../../../state/monitor_management/api', () => ({ | ||
| resetMonitorAPI: jest.fn(), | ||
| resetMonitorBulkAPI: jest.fn(), | ||
| })); | ||
|
|
||
| import { resetMonitorAPI, resetMonitorBulkAPI } from '../../../state/monitor_management/api'; | ||
|
|
||
| const mockedResetMonitorAPI = resetMonitorAPI as jest.MockedFunction<typeof resetMonitorAPI>; | ||
| const mockedResetMonitorBulkAPI = resetMonitorBulkAPI as jest.MockedFunction< | ||
| typeof resetMonitorBulkAPI | ||
| >; | ||
|
|
||
| const healthyMonitor = { | ||
| configId: 'mon-1', | ||
| monitorName: 'Monitor 1', | ||
| isHealthy: true, | ||
| privateLocations: [ | ||
| { | ||
| locationId: 'loc-1', | ||
| locationLabel: 'Location 1', | ||
| status: PrivateLocationHealthStatusValue.Healthy, | ||
| packagePolicyId: 'mon-1-loc-1', | ||
| }, | ||
| ], | ||
| }; | ||
|
|
||
| const unhealthyMonitor = { | ||
| configId: 'mon-2', | ||
| monitorName: 'Monitor 2', | ||
| isHealthy: false, | ||
| privateLocations: [ | ||
| { | ||
| locationId: 'loc-1', | ||
| locationLabel: 'Location 1', | ||
| status: PrivateLocationHealthStatusValue.MissingPackagePolicy, | ||
| packagePolicyId: 'mon-2-loc-1', | ||
| reason: 'Missing', | ||
| }, | ||
| { | ||
| locationId: 'loc-2', | ||
| locationLabel: 'Location 2', | ||
| status: PrivateLocationHealthStatusValue.Healthy, | ||
| packagePolicyId: 'mon-2-loc-2', | ||
| }, | ||
| ], | ||
| }; | ||
|
|
||
| const setupSelectors = (healthData: { monitors: (typeof healthyMonitor)[]; errors: unknown[] }) => { | ||
| (reactRedux.useSelector as jest.Mock).mockImplementation((selector: any) => { | ||
| const fakeState = { | ||
| monitorList: { | ||
| data: { monitors: [] }, | ||
| loaded: true, | ||
| loading: false, | ||
| }, | ||
| monitorHealth: { | ||
| data: healthData, | ||
| loading: false, | ||
| loaded: true, | ||
| error: null, | ||
| }, | ||
| }; | ||
| return selector(fakeState); | ||
| }); | ||
| }; | ||
|
|
||
| describe('useMonitorIntegrationHealth', () => { | ||
| let dispatchSpy: jest.Mock; | ||
|
|
||
| beforeEach(() => { | ||
| jest.clearAllMocks(); | ||
| dispatchSpy = jest.fn(); | ||
| (reactRedux.useDispatch as jest.Mock).mockReturnValue(dispatchSpy); | ||
| }); | ||
|
|
||
| describe('status helpers', () => { | ||
| it('isUnhealthy returns true for unhealthy monitors', () => { | ||
| setupSelectors({ monitors: [healthyMonitor, unhealthyMonitor], errors: [] }); | ||
|
|
||
| const { result } = renderHook(() => | ||
| useMonitorIntegrationHealth({ configIds: ['mon-1', 'mon-2'] }) | ||
| ); | ||
|
|
||
| expect(result.current.isUnhealthy('mon-1')).toBe(false); | ||
| expect(result.current.isUnhealthy('mon-2')).toBe(true); | ||
| expect(result.current.isUnhealthy('non-existent')).toBe(false); | ||
| }); | ||
|
|
||
| it('getUnhealthyLocationStatuses returns only unhealthy locations', () => { | ||
| setupSelectors({ monitors: [unhealthyMonitor], errors: [] }); | ||
|
|
||
| const { result } = renderHook(() => useMonitorIntegrationHealth({ configIds: ['mon-2'] })); | ||
|
|
||
| const statuses = result.current.getUnhealthyLocationStatuses('mon-2'); | ||
| expect(statuses).toHaveLength(1); | ||
| expect(statuses[0].locationId).toBe('loc-1'); | ||
| expect(statuses[0].status).toBe(PrivateLocationHealthStatusValue.MissingPackagePolicy); | ||
| }); | ||
|
|
||
| it('getUnhealthyMonitorCountForLocation counts monitors with unhealthy status at that location', () => { | ||
| setupSelectors({ monitors: [healthyMonitor, unhealthyMonitor], errors: [] }); | ||
|
|
||
| const { result } = renderHook(() => | ||
| useMonitorIntegrationHealth({ configIds: ['mon-1', 'mon-2'] }) | ||
| ); | ||
|
|
||
| expect(result.current.getUnhealthyMonitorCountForLocation('loc-1')).toBe(1); | ||
| expect(result.current.getUnhealthyMonitorCountForLocation('loc-2')).toBe(0); | ||
| }); | ||
|
|
||
| it('getUnhealthyConfigIdsForLocation returns config IDs of unhealthy monitors', () => { | ||
| setupSelectors({ monitors: [healthyMonitor, unhealthyMonitor], errors: [] }); | ||
|
|
||
| const { result } = renderHook(() => | ||
| useMonitorIntegrationHealth({ configIds: ['mon-1', 'mon-2'] }) | ||
| ); | ||
|
|
||
| expect(result.current.getUnhealthyConfigIdsForLocation('loc-1')).toEqual(['mon-2']); | ||
| }); | ||
| }); | ||
|
|
||
| describe('resetMonitor', () => { | ||
| it('calls resetMonitorAPI and re-fetches health', async () => { | ||
| setupSelectors({ monitors: [unhealthyMonitor], errors: [] }); | ||
| mockedResetMonitorAPI.mockResolvedValue({ id: 'mon-2', reset: true }); | ||
|
|
||
| const { result } = renderHook(() => useMonitorIntegrationHealth({ configIds: ['mon-2'] })); | ||
|
|
||
| let resetResult: { error?: Error } | undefined; | ||
| await act(async () => { | ||
| resetResult = await result.current.resetMonitor('mon-2'); | ||
| }); | ||
|
|
||
| expect(resetResult).toEqual({}); | ||
| expect(mockedResetMonitorAPI).toHaveBeenCalledWith({ id: 'mon-2' }); | ||
| expect(result.current.isResetting).toBe(false); | ||
| const healthDispatches = dispatchSpy.mock.calls.filter( | ||
| ([action]: any) => action.type === '[MONITOR HEALTH] GET' | ||
| ); | ||
| expect(healthDispatches.length).toBeGreaterThanOrEqual(2); | ||
| }); | ||
|
|
||
| it('returns error and sets isResetting to false on API failure', async () => { | ||
| setupSelectors({ monitors: [unhealthyMonitor], errors: [] }); | ||
| mockedResetMonitorAPI.mockRejectedValue(new Error('Server error')); | ||
|
|
||
| const { result } = renderHook(() => useMonitorIntegrationHealth({ configIds: ['mon-2'] })); | ||
|
|
||
| let resetResult: { error?: Error } | undefined; | ||
| await act(async () => { | ||
| resetResult = await result.current.resetMonitor('mon-2'); | ||
| }); | ||
|
|
||
| expect(resetResult?.error).toBeInstanceOf(Error); | ||
| expect(resetResult?.error?.message).toBe('Server error'); | ||
| expect(result.current.isResetting).toBe(false); | ||
| }); | ||
| }); | ||
|
|
||
| describe('resetMonitors (bulk)', () => { | ||
| it('returns error and does not refetch when a result item has reset: false', async () => { | ||
| setupSelectors({ monitors: [unhealthyMonitor], errors: [] }); | ||
| mockedResetMonitorBulkAPI.mockResolvedValue({ | ||
| result: [{ id: 'mon-2', reset: false, error: 'fleet error' }], | ||
| }); | ||
|
|
||
| const { result } = renderHook(() => useMonitorIntegrationHealth({ configIds: ['mon-2'] })); | ||
|
|
||
| let resetResult: { error?: Error } | undefined; | ||
| await act(async () => { | ||
| resetResult = await result.current.resetMonitors(['mon-2']); | ||
| }); | ||
|
|
||
| expect(resetResult?.error).toBeInstanceOf(Error); | ||
| expect(result.current.isResetting).toBe(false); | ||
| const healthDispatches = dispatchSpy.mock.calls.filter( | ||
| ([action]: any) => action.type === '[MONITOR HEALTH] GET' | ||
| ); | ||
| expect(healthDispatches.length).toBe(1); // only initial fetch, no refetch | ||
| }); | ||
|
|
||
| it('returns error and does not refetch when top-level errors are present', async () => { | ||
| setupSelectors({ monitors: [unhealthyMonitor], errors: [] }); | ||
| mockedResetMonitorBulkAPI.mockResolvedValue({ | ||
| result: [{ id: 'mon-2', reset: false }], | ||
| }); | ||
|
|
||
| const { result } = renderHook(() => useMonitorIntegrationHealth({ configIds: ['mon-2'] })); | ||
|
|
||
| let resetResult: { error?: Error } | undefined; | ||
| await act(async () => { | ||
| resetResult = await result.current.resetMonitors(['mon-2']); | ||
| }); | ||
|
|
||
| expect(resetResult?.error).toBeInstanceOf(Error); | ||
| expect(result.current.isResetting).toBe(false); | ||
| }); | ||
|
|
||
| it('calls resetMonitorBulkAPI and re-fetches health', async () => { | ||
| setupSelectors({ monitors: [unhealthyMonitor], errors: [] }); | ||
| mockedResetMonitorBulkAPI.mockResolvedValue({ | ||
| result: [{ id: 'mon-2', reset: true }], | ||
| }); | ||
|
|
||
| const { result } = renderHook(() => useMonitorIntegrationHealth({ configIds: ['mon-2'] })); | ||
|
|
||
| let resetResult: { error?: Error } | undefined; | ||
| await act(async () => { | ||
| resetResult = await result.current.resetMonitors(['mon-2']); | ||
| }); | ||
|
|
||
| expect(resetResult).toEqual({}); | ||
| expect(mockedResetMonitorBulkAPI).toHaveBeenCalledWith({ ids: ['mon-2'] }); | ||
| expect(result.current.isResetting).toBe(false); | ||
| }); | ||
| }); | ||
| }); |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why is it optional?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
agentPolicyIdis optional because it's not available in all failure scenarios:missing_location), there's no agent policy to referencepackage_not_installed), policy info can't be determinedIt is set in scenarios where the information is available (healthy,
missing_package_policy,agent_policy_mismatch), and omitted when it's not determinable