""" 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))