"""Installed-applications detail endpoint serializes correctly. Regression guard: GET /api/applications/machines/ serializes each ComputerInstalledApp via its to_dict(). A missing to_dict raised a 500 that the PCDetail page swallowed (v-if on a non-empty list), so real PCs silently showed no installed software. This exercises the with-data path that the empty-list case short-circuits past. """ from shopdb.extensions import db as _db from shopdb.core.models import Asset, AssetType, Application from plugins.computers.models import Computer, ComputerInstalledApp def _seed_pc_with_app(client_db): 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='PC-APPTEST', assettypeid=atype.assettypeid) _db.session.add(asset) _db.session.flush() comp = Computer(assetid=asset.assetid, hostname='PC-APPTEST') _db.session.add(comp) _db.session.flush() app = Application(appname='Test App') _db.session.add(app) _db.session.flush() link = ComputerInstalledApp(computerid=comp.computerid, appid=app.appid, installedversion='1.2.3') _db.session.add(link) _db.session.commit() return comp def test_installed_apps_endpoint_returns_data(client, db, auth_headers): comp = _seed_pc_with_app(db) resp = client.get(f'/api/applications/machines/{comp.computerid}', headers=auth_headers) assert resp.status_code == 200 rows = resp.get_json()['data'] assert len(rows) == 1 row = rows[0] assert row['appname'] == 'Test App' assert row['installedversion'] == '1.2.3' assert row['computerid'] == comp.computerid def test_installed_app_to_dict_prefers_curated_version(db): comp = _seed_pc_with_app(db) link = ComputerInstalledApp.query.filter_by(computerid=comp.computerid).first() data = link.to_dict() assert data['appname'] == 'Test App' assert data['installedversion'] == '1.2.3' assert data['isactive'] is True