Stay quiet when a site never asked for supply monitoring
Some checks failed
CI / naming (push) Successful in 4s
CI / frontend (push) Successful in 14s
CI / migrations-mysql (push) Successful in 42s
CI / backend (push) Has been cancelled

Splitting "cannot see the cartridges" from "nothing is low" was right, but it
treated two unlike cases as one. isconfigured means enabled AND a URL AND a
token, so its absence means the site did not ask for supply monitoring at all -
and a permanent "Supply monitoring is not configured" line is a card reporting
nothing wrong every day, which is how a board teaches people to stop reading it.
It matters for sites adopting ShopDB without Zabbix, who would otherwise inherit
a warning about a feature they declined.

Not configured now returns a plain empty and the card hides. Only configured and
unanswerable reports itself, because that is something meant to work that is not
working.

Also withdraws an unfounded inference. A dev box has no route to Zabbix by
design, so "not reachable" there is the expected state and says nothing about
what production can see. The value of this change is not that it revealed a
production fault; it is that when production's Zabbix does go down, the board
will say so instead of displaying "Low Supplies 0".

Four tests pin the three cases apart: unreachable reports, unconfigured stays
quiet, and a real zero from a reachable Zabbix is not flagged as blindness. The
fourth holds the envelope's rows, since carrying a reason changed the response
shape. Verified they are not vacuous by restoring the original branch in place -
the unreachable test fails against it.

Full suite 2182 passed.
This commit is contained in:
cproudlock
2026-08-29 21:34:48 -04:00
parent b80bd8731f
commit b5fc5c01d1
2 changed files with 97 additions and 2 deletions

View File

@@ -0,0 +1,90 @@
"""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