Files
shopdb-flask/tests/test_plugins/test_supplies_card_blindness.py
Cam Proudlock 870e068c44 Rebuild the dashboard around what needs a person
The board was one card per source in a fixed grid: every source got the same
small box whatever it had to say (warranties showed 5 of 50), and a box per
source flattened priority, while the largest type on the page belonged to counts
that need nobody.

Now a severity rollup, one worst-first queue of everything needing a person, and
a row of context beneath. The queue is full width, capped per source so a noisy
check cannot bury a rare one, and names what did not fit with a link to it. Chips
filter the queue by check as well as by severity; both narrow what is already
loaded, so the counts and rows cannot disagree.

Dropped: an 'All assets' tile shown beside its own four addends, a printer strip
carrying the same count a third time, three cards whose job was to display 0, and
a Recent Devices table listing five machines all 'In Use'.

Contract 0.26.0 adds optional 'shortname' on dashboard cards - a title is written
as a heading and ran wider than the hostname it qualified as a row badge.

Also: the supplies card reported 'Low Supplies 0' when Zabbix was unreachable,
which reads as reassurance rather than the truth that nothing could see the
cartridges. It now distinguishes unreachable (says so) from unconfigured (stays
quiet - the site declined the feature) from a real zero.

Full suite 2182 passed, frontend 314 passed.
2026-08-29 21:35:03 -04:00

91 lines
3.4 KiB
Python

"""The supplies dashboard card must not report "nothing low" when it cannot see.
_get_low_supplies_data returned the same empty result for three different
situations - Zabbix not configured, Zabbix unreachable, and every cartridge
full - so the dashboard displayed "Low Supplies 0 / Critical 0" while the
monitoring was dead. That is not a missing feature; it is a wrong statement, and
it reads as reassurance.
These pin the three cases apart.
"""
from unittest.mock import patch
import pytest
from shopdb.api import db
from shopdb.core.models import Setting
def _call(client, auth_headers):
response = client.get('/api/printers/dashboard/supplies', headers=auth_headers)
assert response.status_code == 200
return response.get_json()['data']
class FakeService:
def __init__(self, configured, reachable):
self.isconfigured = configured
self.isreachable = reachable
@pytest.fixture(autouse=True)
def _clear_cache(app):
from shopdb.extensions import cache
cache.delete('printers_low_supplies')
yield
cache.delete('printers_low_supplies')
def test_unreachable_says_so_rather_than_reporting_zero(client, auth_headers):
"""Configured and unanswerable: the cartridges are NOT known to be fine."""
with patch('plugins.printers.api.asset_routes.ZabbixService',
return_value=FakeService(configured=True, reachable=False)):
data = _call(client, auth_headers)
assert data['rows'] == []
assert data['unavailable'] == 'Supply monitoring is not reachable'
def test_unconfigured_stays_quiet(client, auth_headers):
"""The site did not ask for supply monitoring.
A permanent "not configured" line would be a card reporting nothing wrong
every day, which is how a board teaches people to stop reading it. It has
nothing to say, so it says nothing and the card hides.
"""
with patch('plugins.printers.api.asset_routes.ZabbixService',
return_value=FakeService(configured=False, reachable=False)):
data = _call(client, auth_headers)
assert data['rows'] == []
assert data.get('unavailable') is None
def test_reachable_with_nothing_low_is_not_flagged_as_blind(client, auth_headers):
"""Zabbix answered and every cartridge is fine. That is a real zero."""
with patch('plugins.printers.api.asset_routes._get_low_supplies_data',
return_value={'printers': [],
'summary': {'total_checked': 3, 'low': 0, 'critical': 0}}):
data = _call(client, auth_headers)
assert data['rows'] == []
assert data.get('unavailable') is None
def test_the_envelope_carries_rows_so_the_card_can_still_render(client, auth_headers):
"""The response became an envelope to carry the reason; rows must survive."""
printer = {
'printerid': 1, 'assetnumber': 'PRN-1', 'printername': 'Front office',
'location': 'Bay 3', 'mapx': None, 'mapy': None, 'levelid': None,
'supplies': [{'name': 'Black', 'remaining': 2, 'status': 'critical'}],
}
with patch('plugins.printers.api.asset_routes._get_low_supplies_data',
return_value={'printers': [printer],
'summary': {'total_checked': 1, 'low': 1, 'critical': 1}}):
data = _call(client, auth_headers)
assert data.get('unavailable') is None
assert len(data['rows']) == 1
assert data['rows'][0]['printername'] == 'Front office'
assert data['rows'][0]['iscritical'] is True