Aug 21 - Fix plan usage counter
This commit is contained in:
@@ -0,0 +1,156 @@
|
||||
"""
|
||||
tests/test_quota_counts.py
|
||||
---------------------------
|
||||
Regression guards for the live quota counters in app/tenancy/quota.py.
|
||||
|
||||
Why these exist
|
||||
---------------
|
||||
`count_issues_this_month()` filtered on `Issue.created_at`, a column the issues
|
||||
table does not have (the timestamp is `reported_at`; `created_at` belongs to
|
||||
IssueComment, declared in the same module). Referencing a missing mapped
|
||||
attribute raises AttributeError while the query is being BUILT — and every
|
||||
caller wraps the counters in try/except, so the failure was completely silent:
|
||||
|
||||
* `/settings/plan` evaluated all four counters inside one dict literal, so the
|
||||
single failure discarded the whole dict and the page rendered 0 for EVERY
|
||||
axis — including users and facilities, which were working. It read as
|
||||
"usage numbers never update".
|
||||
* `@quota_soft_check('issues')` swallowed the same exception and returned
|
||||
None, so the issues quota was never evaluated for any tenant.
|
||||
|
||||
A counter that returns a wrong number is visible. A counter that raises behind
|
||||
a try/except is not — which is why these assert the counters actually COUNT,
|
||||
rather than merely that they do not raise.
|
||||
|
||||
The counters are plain queries against the bound session; they do not gate on
|
||||
MULTI_TENANT_ENABLED, so they can be called directly on the single-tenant test
|
||||
fixture.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def client(app):
|
||||
with app.app_context():
|
||||
from app import db
|
||||
db.drop_all()
|
||||
db.create_all()
|
||||
yield app.test_client()
|
||||
db.session.remove()
|
||||
|
||||
|
||||
def _facility(name='Main Office', active=True):
|
||||
from app import db
|
||||
from app.models.facility import Facility
|
||||
fac = Facility(name=name, active=active)
|
||||
db.session.add(fac)
|
||||
db.session.commit()
|
||||
return fac
|
||||
|
||||
|
||||
def _user(username, role='inspector', active=True):
|
||||
from app import db
|
||||
from app.models.user import User
|
||||
u = User(username=username, full_name=username.title(), role=role,
|
||||
email=f'{username}@example.com', active=active, password_set=True)
|
||||
u.set_password('pw-correct1')
|
||||
db.session.add(u)
|
||||
db.session.commit()
|
||||
return u
|
||||
|
||||
|
||||
# ── issues ───────────────────────────────────────────────────────────────────
|
||||
|
||||
def test_issue_counter_counts_issues_reported_this_month(client):
|
||||
"""The regression: this raised AttributeError instead of returning 1."""
|
||||
from app import db
|
||||
from app.models.issue import Issue
|
||||
from app.tenancy.quota import count_issues_this_month
|
||||
|
||||
fac = _facility()
|
||||
assert count_issues_this_month() == 0
|
||||
|
||||
db.session.add(Issue(facility_id=fac.id, severity='high',
|
||||
description='Leaking faucet', status='open'))
|
||||
db.session.commit()
|
||||
|
||||
assert count_issues_this_month() == 1
|
||||
|
||||
|
||||
def test_issue_counter_excludes_a_previous_month(client):
|
||||
"""Proves the month window is applied to the right column, not just that
|
||||
the query runs."""
|
||||
from datetime import timedelta
|
||||
from app import db
|
||||
from app.models.issue import Issue
|
||||
from app.tenancy.quota import count_issues_this_month, _month_window
|
||||
|
||||
fac = _facility()
|
||||
start, _ = _month_window()
|
||||
|
||||
db.session.add(Issue(facility_id=fac.id, severity='low', description='Old',
|
||||
status='open', reported_at=start - timedelta(days=1)))
|
||||
db.session.add(Issue(facility_id=fac.id, severity='low', description='New',
|
||||
status='open'))
|
||||
db.session.commit()
|
||||
|
||||
assert count_issues_this_month() == 1
|
||||
|
||||
|
||||
# ── inspections ──────────────────────────────────────────────────────────────
|
||||
|
||||
def test_inspection_counter_counts_only_completed(client):
|
||||
from app import db
|
||||
from app.models.inspection import Inspection, InspectionTemplate
|
||||
from app.tenancy.quota import count_inspections_this_month
|
||||
|
||||
fac = _facility()
|
||||
insp = _user('ivy')
|
||||
tmpl = InspectionTemplate(name='Restroom Check', active=True, form_schema=[])
|
||||
db.session.add(tmpl)
|
||||
db.session.commit()
|
||||
|
||||
db.session.add(Inspection(template_id=tmpl.id, facility_id=fac.id,
|
||||
inspector_id=insp.id, status='completed'))
|
||||
db.session.add(Inspection(template_id=tmpl.id, facility_id=fac.id,
|
||||
inspector_id=insp.id, status='in_progress'))
|
||||
db.session.commit()
|
||||
|
||||
assert count_inspections_this_month() == 1
|
||||
|
||||
|
||||
# ── users / facilities ───────────────────────────────────────────────────────
|
||||
|
||||
def test_user_and_facility_counters_count_active_rows_only(client):
|
||||
from app.tenancy.quota import count_active_users, count_active_facilities
|
||||
|
||||
_user('ada', 'admin')
|
||||
_user('gone', 'inspector', active=False)
|
||||
_facility('Live Site')
|
||||
_facility('Closed Site', active=False)
|
||||
|
||||
assert count_active_users() == 1
|
||||
assert count_active_facilities() == 1
|
||||
|
||||
|
||||
# ── the window itself ────────────────────────────────────────────────────────
|
||||
|
||||
def test_month_window_is_eastern_and_brackets_now(client):
|
||||
"""Rule 2: rows are stamped with now_eastern(), so the window must be too.
|
||||
|
||||
A UTC-clock window on an Eastern-stamped table misplaces the month boundary
|
||||
by 4-5 hours — wrong only on the 1st, and self-healing before anyone looks.
|
||||
"""
|
||||
from app.tenancy.quota import _month_window
|
||||
from app.utils.time_utils import now_eastern
|
||||
|
||||
start, end = _month_window()
|
||||
now = now_eastern()
|
||||
|
||||
assert start.tzinfo is None and end.tzinfo is None
|
||||
assert start <= now < end
|
||||
assert (start.day, start.hour, start.minute, start.second) == (1, 0, 0, 0)
|
||||
assert end.day == 1
|
||||
assert (end.year, end.month) == ((now.year + 1, 1) if now.month == 12
|
||||
else (now.year, now.month + 1))
|
||||
Reference in New Issue
Block a user