"""Tests for notificationtypes.boardorder (row order on the shopfloor board). Rows used to run in display-style order and then alphabetically, so what led the screen was an accident of styling and the alphabet. Each type now carries a position, low first, and a shared category row sits wherever its earliest-ordered type puts it. The sort itself is frontend (ShopfloorDashboard.vue); pinned here is the contract it sorts on - the feed carries boardorder per card, the type API round-trips and validates it, and the migration seeds the shipped order. """ from plugins.notifications.models import Notification, NotificationType def _make_type(db, typename, boardorder=100, displaystyle='grid', boardcategory=None): t = NotificationType(typename=typename, typecolor='#17a2b8', isactive=True, displaystyle=displaystyle, boardcategory=boardcategory, boardorder=boardorder) db.session.add(t) db.session.commit() return t def _make_note(db, ntype, text): n = Notification(notificationtypeid=ntype.notificationtypeid, notification=text, businessunitid=None, isactive=True, isshopfloor=True) db.session.add(n) db.session.commit() return n def _current(client): resp = client.get('/api/notifications/shopfloor') assert resp.status_code == 200, resp.get_json() return resp.get_json()['data']['current'] def test_feed_carries_the_order_per_card(client, db): ntype = _make_type(db, 'Recognition', boardorder=10) _make_note(db, ntype, 'ten years') assert _current(client)[0]['boardorder'] == 10 def test_a_type_defaults_to_the_back(client, db): """A new type lands behind the seeded rows rather than jumping the queue.""" ntype = NotificationType(typename='Fresh', typecolor='#17a2b8', isactive=True) db.session.add(ntype) db.session.commit() assert ntype.boardorder == 100 def test_api_round_trips_the_order(client, db, auth_headers): create = client.post('/api/notifications/types', json={'typename': 'Awareness', 'boardorder': 30}, headers=auth_headers) assert create.status_code in (200, 201), create.get_json() typeid = create.get_json()['data']['notificationtypeid'] assert create.get_json()['data']['boardorder'] == 30 updated = client.put(f'/api/notifications/types/{typeid}', json={'boardorder': 40}, headers=auth_headers) assert updated.get_json()['data']['boardorder'] == 40 def test_api_rejects_a_nonsense_order(client, db, auth_headers): resp = client.post('/api/notifications/types', json={'typename': 'Bad', 'boardorder': 'first'}, headers=auth_headers) assert resp.status_code == 400 assert 'boardorder' in resp.get_data(as_text=True) def test_api_rejects_an_out_of_range_order(client, db, auth_headers): resp = client.post('/api/notifications/types', json={'typename': 'Bad', 'boardorder': 1000}, headers=auth_headers) assert resp.status_code == 400 def test_order_change_moves_the_config_version(client, db, auth_headers): """Open kiosks reload on a layout change; a re-ordered board is one.""" ntype = _make_type(db, 'Awareness') before = client.get('/api/notifications/shopfloor').get_json()['data']['configversion'] client.put(f'/api/notifications/types/{ntype.notificationtypeid}', json={'boardorder': 30}, headers=auth_headers) after = client.get('/api/notifications/shopfloor').get_json()['data']['configversion'] assert before != after def test_types_in_one_category_can_carry_different_orders(client, db): """The row takes the lowest; the feed still reports each type's own.""" change = _make_type(db, 'Change', boardorder=30, boardcategory='Current Events') incident = _make_type(db, 'Incident', boardorder=50, boardcategory='Current Events') _make_note(db, change, 'retooling') _make_note(db, incident, 'press down') orders = {c['typename']: c['boardorder'] for c in _current(client)} assert orders == {'Change': 30, 'Incident': 50}