Test disk health rows
Every test pins a way the card could start showing a failing or unreadable drive as fine, which is the only failure mode that matters here: nobody investigates a disk that looks healthy. Covers failure-first ordering, null predictfailure reading as unknown rather than OK, unknown outranking OK, both sources surviving for one drive, order independence, and malformed payloads. The ranking was mutation-checked: swapping the unknown and OK ranks fails the suite. Also updates the panel count assertion, which correctly caught the new card.
This commit is contained in:
108
tests/test_plugins/test_endpoints_disk_health.py
Normal file
108
tests/test_plugins/test_endpoints_disk_health.py
Normal file
@@ -0,0 +1,108 @@
|
||||
"""Disk health rows: a dying drive must never render as a healthy one.
|
||||
|
||||
The agent reports drive health from two sources because neither covers the
|
||||
fleet. MSFT_PhysicalDisk is the only one that reports NVMe - verified on a
|
||||
two-NVMe box where the classic ATA SMART class returned zero instances while
|
||||
both drives were present and healthy. Win32_DiskDrive covers ATA/SATA and hosts
|
||||
where the Storage namespace is missing.
|
||||
|
||||
That means the panel can receive the same physical drive twice, from sources
|
||||
that may disagree, plus rows where health could not be read at all. Every test
|
||||
here pins a way this card could start showing a failing or unreadable drive as
|
||||
fine, which is the only failure mode that matters: nobody investigates a disk
|
||||
that looks healthy.
|
||||
"""
|
||||
|
||||
from plugins.endpoints.api.routes import _diskhealth_rows
|
||||
|
||||
|
||||
def _drive(deviceid, predictfailure, healthstatus='Healthy',
|
||||
source='physicaldisk', model='Samsung SSD'):
|
||||
return {
|
||||
'deviceid': deviceid,
|
||||
'model': model,
|
||||
'mediatype': 'SSD',
|
||||
'bustype': 'NVMe',
|
||||
'healthstatus': healthstatus,
|
||||
'predictfailure': predictfailure,
|
||||
'source': source,
|
||||
}
|
||||
|
||||
|
||||
def test_a_failing_drive_sorts_above_healthy_ones():
|
||||
"""On a multi-disk box the bad drive must not be below the fold."""
|
||||
rows = _diskhealth_rows([
|
||||
_drive('0', False),
|
||||
_drive('1', True, healthstatus='Unhealthy'),
|
||||
_drive('2', False),
|
||||
])
|
||||
|
||||
assert [row['deviceid'] for row in rows] == ['1', '0', '2']
|
||||
assert rows[0]['verdict'] == 'Failure predicted'
|
||||
|
||||
|
||||
def test_a_drive_that_did_not_answer_reads_as_unknown_not_ok():
|
||||
"""Null predictfailure is an unread drive, not a healthy one."""
|
||||
rows = _diskhealth_rows([_drive('0', None, healthstatus='')])
|
||||
|
||||
assert rows[0]['verdict'] == 'Unknown'
|
||||
assert rows[0]['healthstatus'] == 'Unknown'
|
||||
|
||||
|
||||
def test_unknown_outranks_ok_so_silence_is_not_buried_under_health():
|
||||
"""A drive that stopped answering is a weaker claim than one saying OK."""
|
||||
rows = _diskhealth_rows([
|
||||
_drive('0', False),
|
||||
_drive('1', None),
|
||||
])
|
||||
|
||||
assert [row['verdict'] for row in rows] == ['Unknown', 'OK']
|
||||
|
||||
|
||||
def test_both_sources_survive_for_the_same_hardware():
|
||||
"""Two disagreeing views of one drive is the signal, not noise.
|
||||
|
||||
Collapsing them would hide the case worth looking at: one source calling a
|
||||
drive healthy while the other predicts failure.
|
||||
"""
|
||||
rows = _diskhealth_rows([
|
||||
_drive('0', False, source='physicaldisk'),
|
||||
_drive('0', True, healthstatus='Pred Fail', source='diskdrive'),
|
||||
])
|
||||
|
||||
assert len(rows) == 2
|
||||
assert {row['source'] for row in rows} == {'physicaldisk', 'diskdrive'}
|
||||
assert rows[0]['verdict'] == 'Failure predicted'
|
||||
|
||||
|
||||
def test_input_order_does_not_change_which_drive_is_shown_first():
|
||||
"""The verdict ranking must decide the order, not arrival order."""
|
||||
healthy = _drive('0', False)
|
||||
failing = _drive('1', True)
|
||||
|
||||
forward = _diskhealth_rows([healthy, failing])
|
||||
backward = _diskhealth_rows([failing, healthy])
|
||||
|
||||
assert forward == backward
|
||||
assert forward[0]['deviceid'] == '1'
|
||||
|
||||
|
||||
def test_an_nvme_drive_is_reported_rather_than_dropped():
|
||||
"""The whole reason for the second source: ATA SMART returns nothing here."""
|
||||
rows = _diskhealth_rows([_drive('0', False, source='physicaldisk')])
|
||||
|
||||
assert rows[0]['bustype'] == 'NVMe'
|
||||
assert rows[0]['verdict'] == 'OK'
|
||||
|
||||
|
||||
def test_a_malformed_payload_does_not_take_the_panel_down():
|
||||
rows = _diskhealth_rows([_drive('0', False), 'not a dict', None])
|
||||
|
||||
assert len(rows) == 1
|
||||
assert rows[0]['deviceid'] == '0'
|
||||
|
||||
|
||||
def test_a_missing_or_wrong_shaped_payload_is_an_empty_panel():
|
||||
assert _diskhealth_rows(None) == []
|
||||
assert _diskhealth_rows({}) == []
|
||||
assert _diskhealth_rows([]) == []
|
||||
@@ -69,7 +69,7 @@ def test_endpoints_asset_panels_are_computer_scoped():
|
||||
"""Every asset panel targets computer assets only, with an endpoint."""
|
||||
plugin = EndpointsPlugin()
|
||||
panels = plugin.get_asset_panels()
|
||||
assert len(panels) == 12
|
||||
assert len(panels) == 13
|
||||
for panel in panels:
|
||||
assert panel['assettypes'] == ['computer']
|
||||
assert panel['endpoint'].startswith('/api/endpoints/')
|
||||
|
||||
Reference in New Issue
Block a user