169 lines
6.1 KiB
Python
169 lines
6.1 KiB
Python
"""Pending Dell Command | Update reporting: ingest and status-card exposure.
|
|
|
|
The agent's shopdb-dellupdates utility reads the log the \\ShopDB-DellCommandScan
|
|
scheduled task writes and reports a short summary on the normal telemetry
|
|
check-in. These tests cover the ingest write path and the three-state rendering
|
|
on the status panel, which is the part most likely to be broken by a well-meaning
|
|
simplification: "never scanned" must NOT render as "No".
|
|
"""
|
|
|
|
import json
|
|
|
|
from shopdb.core.models import Asset, AssetType
|
|
|
|
|
|
STATUS_URL = '/api/endpoints/asset/{}/status'
|
|
|
|
|
|
def _computer(db, hostname):
|
|
"""Create a computer Asset + Computer row; return the asset."""
|
|
from plugins.computers.models import Computer, ComputerType
|
|
|
|
atype = AssetType.query.filter_by(assettype='computer').first()
|
|
if not atype:
|
|
atype = AssetType(assettype='computer')
|
|
db.session.add(atype)
|
|
db.session.flush()
|
|
|
|
ctype = ComputerType.query.filter_by(computertype='CMM PC').first()
|
|
if not ctype:
|
|
ctype = ComputerType(computertype='CMM PC')
|
|
db.session.add(ctype)
|
|
db.session.flush()
|
|
|
|
asset = Asset(assetnumber=hostname, assettypeid=atype.assettypeid,
|
|
isactive=True)
|
|
db.session.add(asset)
|
|
db.session.flush()
|
|
|
|
db.session.add(Computer(assetid=asset.assetid, hostname=hostname,
|
|
computertypeid=ctype.computertypeid))
|
|
db.session.flush()
|
|
return asset
|
|
|
|
|
|
def _checkin(db, hostname, payload):
|
|
"""Run a check-in for hostname with the extra payload keys merged in."""
|
|
from plugins.endpoints.plugin import EndpointsPlugin
|
|
|
|
body = {'hostname': hostname}
|
|
body.update(payload)
|
|
EndpointsPlugin().apply_collector_payload(body)
|
|
db.session.commit()
|
|
|
|
|
|
def _status_fields(client, asset, auth_headers):
|
|
response = client.get(STATUS_URL.format(asset.assetid), headers=auth_headers)
|
|
assert response.status_code == 200
|
|
return {f['label']: f['value']
|
|
for f in response.get_json()['data']['fields']}
|
|
|
|
|
|
def test_checkin_persists_the_dell_summary(client, db, app, auth_headers):
|
|
"""A reported dellupdates payload is stored on the endpoint state."""
|
|
from plugins.endpoints.models.endpoint import EndpointState
|
|
|
|
_computer(db, 'DELL01')
|
|
_checkin(db, 'DELL01', {'dellupdates': {
|
|
'status': 'ok', 'count': 1, 'truncated': False,
|
|
'updates': [{'name': 'Dell BIOS', 'severity': 'Urgent',
|
|
'version': '1.2.3'}],
|
|
}})
|
|
|
|
state = EndpointState.query.filter_by(
|
|
assetid=Asset.query.filter_by(assetnumber='DELL01').first().assetid
|
|
).first()
|
|
stored = json.loads(state.dellupdatesjson)
|
|
assert stored['count'] == 1
|
|
assert stored['updates'][0]['name'] == 'Dell BIOS'
|
|
|
|
|
|
def test_pending_updates_show_yes_with_a_count(client, db, app, auth_headers):
|
|
"""A scan with pending items renders Yes plus how many."""
|
|
asset = _computer(db, 'DELL02')
|
|
_checkin(db, 'DELL02', {'dellupdates': {
|
|
'status': 'ok', 'count': 3, 'truncated': False, 'updates': [],
|
|
}})
|
|
|
|
fields = _status_fields(client, asset, auth_headers)
|
|
assert fields['Pending Dell updates'] == 'Yes (3)'
|
|
|
|
|
|
def test_a_clean_scan_shows_no(client, db, app, auth_headers):
|
|
"""A scan that found nothing renders No, not a hidden row."""
|
|
asset = _computer(db, 'DELL03')
|
|
_checkin(db, 'DELL03', {'dellupdates': {
|
|
'status': 'ok', 'count': 0, 'truncated': False, 'updates': [],
|
|
}})
|
|
|
|
fields = _status_fields(client, asset, auth_headers)
|
|
assert fields['Pending Dell updates'] == 'No'
|
|
|
|
|
|
def test_a_never_scanned_endpoint_says_so(client, db, app, auth_headers):
|
|
"""Never scanned renders as its own visible state, not as No and not hidden.
|
|
|
|
Two failure modes are being guarded at once: showing 'No' would claim the
|
|
machine is current when nobody ever checked, and hiding the row entirely
|
|
reads to an operator as a broken panel.
|
|
"""
|
|
asset = _computer(db, 'DELL04')
|
|
_checkin(db, 'DELL04', {})
|
|
|
|
fields = _status_fields(client, asset, auth_headers)
|
|
assert fields['Pending Dell updates'] == 'Never scanned'
|
|
|
|
|
|
def test_an_unknown_status_says_never_scanned(client, db, app, auth_headers):
|
|
"""The utility's own 'never scanned' answer renders as never scanned."""
|
|
asset = _computer(db, 'DELL05')
|
|
_checkin(db, 'DELL05', {'dellupdates': {
|
|
'status': 'unknown', 'count': 0, 'truncated': False, 'updates': [],
|
|
}})
|
|
|
|
fields = _status_fields(client, asset, auth_headers)
|
|
assert fields['Pending Dell updates'] == 'Never scanned'
|
|
|
|
|
|
def test_a_failed_scan_read_is_distinct_from_never_scanned(client, db, app,
|
|
auth_headers):
|
|
"""An unreadable scan log is its own state, not silence and not clean."""
|
|
asset = _computer(db, 'DELL06')
|
|
_checkin(db, 'DELL06', {'dellupdates': {
|
|
'status': 'error', 'count': 0, 'truncated': False, 'updates': [],
|
|
}})
|
|
|
|
fields = _status_fields(client, asset, auth_headers)
|
|
assert fields['Pending Dell updates'] == 'Unknown (scan unreadable)'
|
|
|
|
|
|
def test_a_malformed_blob_does_not_break_the_panel(client, db, app, auth_headers):
|
|
"""A corrupt cached payload degrades to the unknown state, not a 500."""
|
|
from plugins.endpoints.models.endpoint import EndpointState
|
|
|
|
asset = _computer(db, 'DELL07')
|
|
_checkin(db, 'DELL07', {})
|
|
|
|
state = EndpointState.query.filter_by(assetid=asset.assetid).first()
|
|
state.dellupdatesjson = 'not json at all'
|
|
db.session.commit()
|
|
|
|
fields = _status_fields(client, asset, auth_headers)
|
|
assert fields['Pending Dell updates'] == 'Never scanned'
|
|
|
|
|
|
def test_an_omitted_key_preserves_the_last_result(client, db, app, auth_headers):
|
|
"""A check-in without the key does not erase a previously reported scan.
|
|
|
|
The agent omits dellupdates entirely when the collector utility is absent,
|
|
so overwriting to null would discard a still-valid result.
|
|
"""
|
|
asset = _computer(db, 'DELL08')
|
|
_checkin(db, 'DELL08', {'dellupdates': {
|
|
'status': 'ok', 'count': 2, 'truncated': False, 'updates': [],
|
|
}})
|
|
_checkin(db, 'DELL08', {})
|
|
|
|
fields = _status_fields(client, asset, auth_headers)
|
|
assert fields['Pending Dell updates'] == 'Yes (2)'
|