Endpoint telemetry, PC Detail rework and a checked backup/restore path
This commit is contained in:
168
tests/test_plugins/test_endpoints_dellupdates.py
Normal file
168
tests/test_plugins/test_endpoints_dellupdates.py
Normal file
@@ -0,0 +1,168 @@
|
||||
"""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)'
|
||||
Reference in New Issue
Block a user