Part markers become their own assets under the operation they serve

This commit is contained in:
2026-08-10 15:55:36 -04:00
parent 28b1dfb89e
commit 2924a53346
5 changed files with 371 additions and 1 deletions

View File

@@ -891,3 +891,120 @@ def test_machinelink_alerts_are_off_by_default(client, db, collector_key,
assert alerts == []
assert len(_links(machine_3015.assetid)) == 1
assert any('taken over' in w for w in r.get_json()['data']['warnings'])
# =============================================================================
# Part markers: a marker is its own asset, under the operation it serves
# =============================================================================
@pytest.fixture
def operation_0615(db, machine_3015):
"""An operation number that more than one part marker serves.
machine_3015 is depended on for the 'machine' asset type, the 'controls'
relationship type and 'partof', which the marker links need.
"""
from shopdb.core.models import AssetType, Asset, RelationshipType
db.session.add(RelationshipType(relationshiptype='partof',
isdirectional=True))
mt = AssetType.query.filter_by(assettype='machine').first()
asset = Asset(assetnumber='0615', name='Operation 0615',
assettypeid=mt.assettypeid, statusid=1)
db.session.add(asset)
db.session.commit()
return asset
def _marker(hostname):
return {'hostname': hostname, 'machinenumber': '0615',
'pctype': 'gea-shopfloor-partmarker'}
def test_a_marker_pc_gets_a_marker_asset_of_its_own(
client, db, collector_key, computer_assettype, operation_0615):
from shopdb.core.models import Asset
client.post('/api/collector/computers', json=_marker('MARKERPC1'),
headers={'X-API-Key': collector_key})
marker = Asset.query.filter_by(assetnumber='MARKERPC1-PARTMARKER').first()
assert marker is not None
assert marker.machine is not None
assert marker.machine.machinetype.machinetype == 'Part Marker'
def test_two_markers_share_an_operation_without_contesting_it(
client, db, collector_key, computer_assettype, operation_0615):
"""The reason markers became assets. Both markers serve 0615, and an
operation holds any number of them, so neither takes the other's place."""
from shopdb.core.models import Asset, AssetRelationship, RelationshipType
for host in ('MARKERPC1', 'MARKERPC2'):
client.post('/api/collector/computers', json=_marker(host),
headers={'X-API-Key': collector_key})
partof = RelationshipType.query.filter_by(relationshiptype='partof').first()
members = AssetRelationship.query.filter_by(
targetassetid=operation_0615.assetid,
relationshiptypeid=partof.relationshiptypeid,
isactive=True).all()
assert len(members) == 2
names = sorted(db.session.get(Asset, rel.sourceassetid).assetnumber
for rel in members)
assert names == ['MARKERPC1-PARTMARKER', 'MARKERPC2-PARTMARKER']
def test_a_marker_pc_does_not_claim_the_operation_directly(
client, db, collector_key, computer_assettype, operation_0615):
"""Control of the operation follows the partof rail from the marker. A
direct PC claim would be the link two markers cannot both hold."""
from shopdb.core.models import AssetRelationship
client.post('/api/collector/computers', json=_marker('MARKERPC1'),
headers={'X-API-Key': collector_key})
direct = AssetRelationship.query.filter_by(
targetassetid=operation_0615.assetid,
label='collector:machine').all()
assert direct == []
def test_reporting_again_reuses_the_marker(
client, db, collector_key, computer_assettype, operation_0615):
"""GE-Enforce posts every cycle; a marker must not be minted each time."""
from shopdb.core.models import Asset
for _ in range(3):
client.post('/api/collector/computers', json=_marker('MARKERPC1'),
headers={'X-API-Key': collector_key})
markers = Asset.query.filter(
Asset.assetnumber.like('%-PARTMARKER')).all()
assert len(markers) == 1
def test_moving_a_marker_archives_the_old_operation(
client, db, collector_key, computer_assettype, operation_0615,
machine_3015):
"""Where a marker used to live stays answerable."""
from shopdb.core.models import AssetRelationship, RelationshipType
client.post('/api/collector/computers', json=_marker('MARKERPC1'),
headers={'X-API-Key': collector_key})
moved = dict(_marker('MARKERPC1'), machinenumber='3015')
client.post('/api/collector/computers', json=moved,
headers={'X-API-Key': collector_key})
partof = RelationshipType.query.filter_by(relationshiptype='partof').first()
rows = AssetRelationship.query.filter_by(
relationshiptypeid=partof.relationshiptypeid).all()
active = [r for r in rows if r.isactive]
assert len(rows) == 2, 'the old membership must be kept, not deleted'
assert len(active) == 1
assert active[0].targetassetid == machine_3015.assetid
def test_an_ordinary_pc_gets_no_marker(
client, db, collector_key, computer_assettype, operation_0615):
from shopdb.core.models import Asset
client.post('/api/collector/computers',
json={'hostname': 'PLAINPC', 'machinenumber': '0615',
'pctype': 'gea-shopfloor-collections'},
headers={'X-API-Key': collector_key})
assert Asset.query.filter(Asset.assetnumber.like('%-PARTMARKER')).all() == []