Endpoint telemetry, PC Detail rework and a checked backup/restore path
This commit is contained in:
186
tests/test_plugins/test_endpoints_mapped_drives.py
Normal file
186
tests/test_plugins/test_endpoints_mapped_drives.py
Normal file
@@ -0,0 +1,186 @@
|
||||
"""Mapped-drive merge: two disagreeing Windows sources into one honest row.
|
||||
|
||||
Windows reports mapped drives twice - Win32_NetworkConnection (the 'net use'
|
||||
table) and Get-SmbMapping - and the two do not agree. Captured on one machine at
|
||||
one moment, same two mappings, Get-SmbMapping said OK for both while net use
|
||||
said Disconnected and Reconnecting. Trusting the structured source alone would
|
||||
have rendered a healthy Q: on a box whose Q: was down.
|
||||
|
||||
So the probe collects both without reconciling them and _mapped_drives() decides
|
||||
what an operator is shown. Every test here pins a way that decision could
|
||||
silently start rendering a broken drive as a working one, which is the only
|
||||
failure mode of this card that matters: nobody reports a drive that looks fine.
|
||||
"""
|
||||
|
||||
from plugins.endpoints.api.routes import _mapped_drives
|
||||
|
||||
|
||||
def _netuse(localname, remotename, status):
|
||||
return {'localname': localname, 'remotename': remotename, 'status': status}
|
||||
|
||||
|
||||
def _smb(localpath, remotepath, status):
|
||||
return {'localpath': localpath, 'remotepath': remotepath, 'status': status}
|
||||
|
||||
|
||||
def test_the_worse_of_two_disagreeing_sources_is_what_gets_shown():
|
||||
"""The real capture: Get-SmbMapping says OK, net use says Disconnected.
|
||||
|
||||
The drive was down. Showing OK because one source said so is the exact bug
|
||||
this merge exists to prevent.
|
||||
"""
|
||||
rows = _mapped_drives({
|
||||
'netuse': [_netuse('Q:', r'\\server\specdata', 'Disconnected')],
|
||||
'smbmapping': [_smb('Q:', r'\\server\specdata', 'OK')],
|
||||
})
|
||||
|
||||
assert rows == [r'Q: \\server\specdata (Disconnected)']
|
||||
|
||||
|
||||
def test_reconnecting_is_worse_than_ok_and_survives_the_merge():
|
||||
"""Reconnecting is a real status from the capture, not a synonym for OK."""
|
||||
rows = _mapped_drives({
|
||||
'netuse': [_netuse('S:', r'\\server\shared', 'Reconnecting')],
|
||||
'smbmapping': [_smb('S:', r'\\server\shared', 'OK')],
|
||||
})
|
||||
|
||||
assert rows == [r'S: \\server\shared (Reconnecting)']
|
||||
|
||||
|
||||
def test_a_drive_both_sources_call_healthy_is_not_annotated():
|
||||
"""Only problems are labelled; tagging every drive '(OK)' buries the bad one."""
|
||||
rows = _mapped_drives({
|
||||
'netuse': [_netuse('H:', r'\\server\home', 'OK')],
|
||||
'smbmapping': [_smb('H:', r'\\server\home', 'OK')],
|
||||
})
|
||||
|
||||
assert rows == [r'H: \\server\home']
|
||||
|
||||
|
||||
def test_a_drive_only_one_source_knows_about_is_still_reported():
|
||||
"""Get-SmbMapping is absent on some builds and session types.
|
||||
|
||||
Requiring agreement between the sources would hide every mapped drive on
|
||||
those machines rather than showing what is known about them.
|
||||
"""
|
||||
rows = _mapped_drives({
|
||||
'netuse': [_netuse('P:', r'\\server\programs', 'Disconnected')],
|
||||
'smbmapping': [],
|
||||
})
|
||||
|
||||
assert rows == [r'P: \\server\programs (Disconnected)']
|
||||
|
||||
|
||||
def test_the_smbmapping_key_missing_entirely_degrades_to_netuse():
|
||||
"""Old agents predate the second source; the panel must still work.
|
||||
|
||||
The server ships ahead of the agent republish, so this is the live shape
|
||||
until every endpoint has the new probe.
|
||||
"""
|
||||
rows = _mapped_drives({
|
||||
'netuse': [_netuse('Q:', r'\\server\specdata', 'OK')],
|
||||
})
|
||||
|
||||
assert rows == [r'Q: \\server\specdata']
|
||||
|
||||
|
||||
def test_a_drive_with_no_status_from_either_source_reads_as_unknown():
|
||||
"""A blank annotation reads as healthy, so a failed check must not be blank."""
|
||||
rows = _mapped_drives({
|
||||
'netuse': [_netuse('Z:', r'\\server\scratch', None)],
|
||||
'smbmapping': [],
|
||||
})
|
||||
|
||||
assert rows == [r'Z: \\server\scratch (Unknown)']
|
||||
|
||||
|
||||
def test_an_unrecognised_status_is_not_assumed_healthy():
|
||||
"""Unknown vocabulary ranks below OK deliberately.
|
||||
|
||||
Paired against OK specifically: a status the server does not recognise must
|
||||
LOSE to a healthy one, or an unseen fault renders green. Pairing it against
|
||||
a known-bad status would prove nothing, since the known-bad value would win
|
||||
however unknowns are ranked.
|
||||
"""
|
||||
rows = _mapped_drives({
|
||||
'netuse': [_netuse('X:', r'\\server\thing', 'SomeNewStatus')],
|
||||
'smbmapping': [_smb('X:', r'\\server\thing', 'OK')],
|
||||
})
|
||||
|
||||
assert rows == [r'X: \\server\thing (SomeNewStatus)']
|
||||
|
||||
|
||||
def test_a_known_bad_status_beats_an_unrecognised_one():
|
||||
"""Unavailable outranks an unknown string; the worst named state wins."""
|
||||
rows = _mapped_drives({
|
||||
'netuse': [_netuse('X:', r'\\server\thing', 'Unavailable')],
|
||||
'smbmapping': [_smb('X:', r'\\server\thing', 'SomeNewStatus')],
|
||||
})
|
||||
|
||||
assert rows == [r'X: \\server\thing (Unavailable)']
|
||||
|
||||
|
||||
def test_which_source_reported_a_status_does_not_change_the_verdict():
|
||||
"""The same pair of statuses must resolve identically whichever side sent it.
|
||||
|
||||
_worse_drive_status ranks by severity and breaks ties by argument order, so
|
||||
if two statuses ever rank EQUAL the answer silently depends on which source
|
||||
happened to report which - and a drive would read healthy or broken based on
|
||||
enumeration order. Asserting both directions is what makes an accidental tie
|
||||
visible; a single-direction test passes on luck.
|
||||
"""
|
||||
remote = r'\\server\thing'
|
||||
pairs = [
|
||||
('OK', 'Disconnected'),
|
||||
('OK', 'SomeNewStatus'),
|
||||
('Reconnecting', 'Unavailable'),
|
||||
('OK', 'Reconnecting'),
|
||||
]
|
||||
|
||||
for first, second in pairs:
|
||||
forward = _mapped_drives({
|
||||
'netuse': [_netuse('X:', remote, first)],
|
||||
'smbmapping': [_smb('X:', remote, second)],
|
||||
})
|
||||
backward = _mapped_drives({
|
||||
'netuse': [_netuse('X:', remote, second)],
|
||||
'smbmapping': [_smb('X:', remote, first)],
|
||||
})
|
||||
assert forward == backward, (
|
||||
'statuses {!r}/{!r} resolve differently depending on which source '
|
||||
'reported them'.format(first, second))
|
||||
|
||||
|
||||
def test_the_two_sources_are_paired_by_drive_letter_not_by_order():
|
||||
"""The sources enumerate independently and need not agree on ordering."""
|
||||
rows = _mapped_drives({
|
||||
'netuse': [
|
||||
_netuse('Q:', r'\\server\specdata', 'OK'),
|
||||
_netuse('S:', r'\\server\shared', 'OK'),
|
||||
],
|
||||
'smbmapping': [
|
||||
_smb('S:', r'\\server\shared', 'Disconnected'),
|
||||
_smb('Q:', r'\\server\specdata', 'OK'),
|
||||
],
|
||||
})
|
||||
|
||||
assert rows == [
|
||||
r'Q: \\server\specdata',
|
||||
r'S: \\server\shared (Disconnected)',
|
||||
]
|
||||
|
||||
|
||||
def test_no_user_context_at_all_yields_no_rows():
|
||||
"""Nobody signed in is a valid state, not a fault."""
|
||||
assert _mapped_drives({}) == []
|
||||
|
||||
|
||||
def test_malformed_entries_are_skipped_rather_than_raising():
|
||||
"""The payload is agent-supplied; a bad row must not blank the whole card."""
|
||||
rows = _mapped_drives({
|
||||
'netuse': ['not-a-dict', None, {'status': 'OK'},
|
||||
_netuse('Q:', r'\\server\specdata', 'Disconnected')],
|
||||
'smbmapping': 'not-a-list',
|
||||
})
|
||||
|
||||
assert rows == [r'Q: \\server\specdata (Disconnected)']
|
||||
Reference in New Issue
Block a user