diff --git a/tests/test_plugins/test_endpoints_mapped_drives.py b/tests/test_plugins/test_endpoints_mapped_drives.py new file mode 100644 index 00000000..4855999d --- /dev/null +++ b/tests/test_plugins/test_endpoints_mapped_drives.py @@ -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)']