"""The Fleet list must carry the asset alias. The alias is what people actually call a PC ("Bay 3 Controller"); the hostname is what DNS calls it. The PCs list has shown both for a long time, but the Fleet list query never selected Asset.name, so the column could not be built even though the data existed - it was missing rather than broken. These pin the field on the payload and its inclusion in search, so the column cannot quietly lose its source again. """ LIST_URL = '/api/endpoints/' def _endpoint(db, hostname='fleetbox', alias='Bay 3 Controller'): """An Asset + Computer + EndpointState triple: one row on the fleet list. The list joins through Computer and filters on Asset.isactive, so all three are required for a row to appear at all. """ from shopdb.api import Asset, AssetType from plugins.computers.models import Computer from plugins.endpoints.models import EndpointState assettype = AssetType.query.filter_by(assettype='computer').first() if assettype is None: assettype = AssetType(assettype='computer', pluginname='computers', tablename='computers') db.session.add(assettype) db.session.flush() asset = Asset(assetnumber=hostname, name=alias, assettypeid=assettype.assettypeid, isactive=True) db.session.add(asset) db.session.flush() db.session.add(Computer(assetid=asset.assetid, hostname=hostname)) db.session.add(EndpointState(assetid=asset.assetid, hostname=hostname)) db.session.commit() return asset.assetid def _rows(response): return response.get_json()['data'] def test_the_fleet_list_carries_the_alias(client, db, auth_headers): _endpoint(db) response = client.get(LIST_URL, headers=auth_headers) assert response.status_code == 200, response.get_json() rows = _rows(response) assert len(rows) == 1 # Exposed as 'alias', not 'name': the row already has a hostname, and # 'name' beside 'hostname' reads as a duplicate of the same thing. assert rows[0]['alias'] == 'Bay 3 Controller' def test_an_endpoint_without_an_alias_still_lists(client, db, auth_headers): # The column renders '-' for these. A null must not drop the row or 500. _endpoint(db, hostname='namelessbox', alias=None) response = client.get(LIST_URL, headers=auth_headers) assert response.status_code == 200, response.get_json() rows = _rows(response) assert len(rows) == 1 assert rows[0]['alias'] is None assert rows[0]['hostname'] == 'namelessbox' def test_the_alias_is_searchable(client, db, auth_headers): # Someone looking for a PC types what it is called, not its hostname. _endpoint(db, hostname='pc-a', alias='Bay 3 Controller') _endpoint(db, hostname='pc-b', alias='Paint Booth') response = client.get(LIST_URL + '?search=Bay 3', headers=auth_headers) assert response.status_code == 200, response.get_json() rows = _rows(response) assert [r['hostname'] for r in rows] == ['pc-a'] def test_searching_by_hostname_still_works(client, db, auth_headers): # The alias clause was added to an existing OR; prove it did not replace # what was already there. _endpoint(db, hostname='pc-a', alias='Bay 3 Controller') _endpoint(db, hostname='pc-b', alias='Paint Booth') response = client.get(LIST_URL + '?search=pc-b', headers=auth_headers) assert response.status_code == 200, response.get_json() rows = _rows(response) assert [r['hostname'] for r in rows] == ['pc-b'] def test_the_row_keeps_its_other_columns(client, db, auth_headers): # The alias was added mid-tuple in the query, so every following column # shifted position. If that unpacking is wrong the values silently swap # rather than raising. _endpoint(db, hostname='fleetbox', alias='Bay 3 Controller') response = client.get(LIST_URL, headers=auth_headers) row = _rows(response)[0] assert row['hostname'] == 'fleetbox' assert row['assetnumber'] == 'fleetbox' assert row['alias'] == 'Bay 3 Controller' assert row['status'] in ('never', 'quiet', 'reporting') assert 'computerid' in row and row['computerid'] is not None