"""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']