Files
shopdb-flask/tests/test_plugins/test_geenforce_compliance.py
Cam Proudlock a9d0dfff4a Initial import: shopdb-flask multi-site asset platform
Flask + Vue rewrite of the classic-ASP shopdb, built as a plugin
framework so sister GE Aerospace sites can adopt it. Product version
0.7.0-pre-0.8.0, plugin contract 0.11.0, 966 backend tests.

Key entry points:
- docs/INSTALL-WINDOWS-IIS.md - tested Windows + IIS install runbook
  (own-site and subpath deployment methods)
- docs/PILOT-DEPLOY.md - production pilot: stand up + import + verify
- docs/IMPORT-API.md - schema-agnostic legacy data import contract
- scripts/site_imports/wjf/ - West Jefferson reference import loader
- docs/adr/ - twelve accepted architecture decision records
2026-07-14 07:58:51 -04:00

167 lines
7.1 KiB
Python

"""GE-Enforce compliance: fleet-install coverage per app-linked entry.
Compliance pairs each app-linked manifest entry with collected PC data
(ComputerInstalledApp) to answer 'how many PCs have this app, and how many on
the expected version'. Only entries carrying a curated appid appear; unlinked
entries are skipped. FileVersion detection supplies the expected-version target;
every other detection method yields a null target (no meaningful match). When
the optional computers plugin is absent, counts degrade to null gracefully.
"""
from shopdb.core.models import Asset, AssetType, Application
from plugins.geenforce import service
def _seed_app(db, appname):
app_row = Application(appname=appname)
db.session.add(app_row)
db.session.commit()
return app_row.appid
def _seed_pc_with_app(db, hostname, appid, installedversion, isactive=True):
"""Create a Computer + one ComputerInstalledApp row for it."""
from plugins.computers.models import Computer, ComputerInstalledApp
atype = AssetType.query.filter_by(assettype='computer').first()
if not atype:
atype = AssetType(assettype='computer')
db.session.add(atype)
db.session.flush()
asset = Asset(assetnumber=f'PC-{hostname}', assettypeid=atype.assettypeid)
db.session.add(asset)
db.session.flush()
comp = Computer(assetid=asset.assetid, hostname=hostname)
db.session.add(comp)
db.session.flush()
link = ComputerInstalledApp(computerid=comp.computerid, appid=appid,
installedversion=installedversion,
isactive=isactive)
db.session.add(link)
db.session.commit()
return comp
def _create_scope(client, auth_headers, name='gea-shopfloor-cmm'):
resp = client.post('/api/geenforce/scopes',
json={'scopename': name, 'phase': 'runtime'},
headers=auth_headers)
assert resp.status_code == 201, resp.get_json()
return resp.get_json()['data']['scopeid']
def _add_entry(client, auth_headers, scopeid, entry):
resp = client.post(f'/api/geenforce/scopes/{scopeid}/entries',
json=entry, headers=auth_headers)
assert resp.status_code == 201, resp.get_json()
return resp.get_json()['data']['entryid']
def test_compliance_only_lists_app_linked_entries(client, db, auth_headers):
appid = _seed_app(db, 'PC-DMIS')
scopeid = _create_scope(client, auth_headers)
_add_entry(client, auth_headers, scopeid,
{'Name': 'PC-DMIS 2019', 'Type': 'MSI', 'appid': appid,
'DetectionMethod': 'FileVersion', 'DetectionPath': 'C:\\p.exe',
'DetectionValue': '2019 R1'})
# An unlinked entry (no appid) must NOT appear.
_add_entry(client, auth_headers, scopeid,
{'Name': 'Unlinked', 'Type': 'MSI', 'Installer': 'apps/x.msi'})
data = client.get(f'/api/geenforce/scopes/{scopeid}/compliance',
headers=auth_headers).get_json()['data']
assert data['computersplugin'] is True
assert [r['entryname'] for r in data['rows']] == ['PC-DMIS 2019']
row = data['rows'][0]
assert row['appid'] == appid
assert row['appname'] == 'PC-DMIS'
assert row['expectedversion'] == '2019 R1'
def test_compliance_counts_active_installs_and_version_match(client, db,
auth_headers):
appid = _seed_app(db, 'PC-DMIS')
# Two PCs on the expected version, one on a different version, one inactive.
_seed_pc_with_app(db, 'CMM01', appid, '2019 R1')
_seed_pc_with_app(db, 'CMM02', appid, '2019 R1')
_seed_pc_with_app(db, 'CMM03', appid, '2016 R2')
_seed_pc_with_app(db, 'CMM04', appid, '2019 R1', isactive=False)
scopeid = _create_scope(client, auth_headers)
_add_entry(client, auth_headers, scopeid,
{'Name': 'PC-DMIS 2019', 'Type': 'MSI', 'appid': appid,
'DetectionMethod': 'FileVersion', 'DetectionPath': 'C:\\p.exe',
'DetectionValue': '2019 R1'})
row = client.get(f'/api/geenforce/scopes/{scopeid}/compliance',
headers=auth_headers).get_json()['data']['rows'][0]
# Inactive row excluded: 3 active installs, 2 on the expected version.
assert row['installedcount'] == 3
assert row['versionmatchcount'] == 2
assert '2 of 3' in row['coveragenote']
def test_compliance_non_fileversion_has_null_version_target(client, db,
auth_headers):
appid = _seed_app(db, 'eDNC')
_seed_pc_with_app(db, 'DNC01', appid, '5.0')
scopeid = _create_scope(client, auth_headers)
# Registry detection is not a version string -> no expected-version target.
_add_entry(client, auth_headers, scopeid,
{'Name': 'eDNC', 'Type': 'MSI', 'appid': appid,
'DetectionMethod': 'Registry',
'DetectionPath': 'HKLM\\Software\\eDNC'})
row = client.get(f'/api/geenforce/scopes/{scopeid}/compliance',
headers=auth_headers).get_json()['data']['rows'][0]
assert row['expectedversion'] is None
assert row['installedcount'] == 1
assert row['versionmatchcount'] is None
assert 'no version target' in row['coveragenote']
def test_compliance_not_installed_anywhere(client, db, auth_headers):
appid = _seed_app(db, 'Orphan')
scopeid = _create_scope(client, auth_headers)
_add_entry(client, auth_headers, scopeid,
{'Name': 'Orphan', 'Type': 'MSI', 'appid': appid,
'DetectionMethod': 'FileVersion', 'DetectionPath': 'C:\\o.exe',
'DetectionValue': '1.0'})
row = client.get(f'/api/geenforce/scopes/{scopeid}/compliance',
headers=auth_headers).get_json()['data']['rows'][0]
assert row['installedcount'] == 0
assert row['versionmatchcount'] == 0
assert row['coveragenote'] == 'not installed on any collected PC'
def test_compliance_scope_not_found(client, db, auth_headers):
resp = client.get('/api/geenforce/scopes/999999/compliance',
headers=auth_headers)
assert resp.status_code == 404
def test_compliance_requires_manage_permission(client, db, member_headers):
resp = client.get('/api/geenforce/scopes/1/compliance',
headers=member_headers)
assert resp.status_code == 403
def test_compliance_graceful_without_computers_plugin(client, db, auth_headers,
monkeypatch):
appid = _seed_app(db, 'PC-DMIS')
scopeid = _create_scope(client, auth_headers)
_add_entry(client, auth_headers, scopeid,
{'Name': 'PC-DMIS 2019', 'Type': 'MSI', 'appid': appid,
'DetectionMethod': 'FileVersion', 'DetectionPath': 'C:\\p.exe',
'DetectionValue': '2019 R1'})
monkeypatch.setattr(service, '_installed_app_model', lambda: None)
data = client.get(f'/api/geenforce/scopes/{scopeid}/compliance',
headers=auth_headers).get_json()['data']
assert data['computersplugin'] is False
row = data['rows'][0]
assert row['installedcount'] is None
assert row['versionmatchcount'] is None
assert 'computers plugin not installed' in row['coveragenote']