206 lines
8.0 KiB
Python
206 lines
8.0 KiB
Python
"""
|
|
Smoke coverage for the pages whose queries were rewritten during the database
|
|
optimization pass: the dashboard, the issues list, and the reports overview.
|
|
|
|
These routes had no request-level tests, so a query rewrite that produced
|
|
invalid SQL or a broken scope would only have surfaced in production. Each test
|
|
here drives the real route through the test client for a role that exercises a
|
|
DIFFERENT branch of the scoping code:
|
|
|
|
admin -- unscoped branch
|
|
inspector -- InspectorAssignment / get_inspector_scope() branch
|
|
customer -- CustomerAssignment / get_customer_scope() branch
|
|
|
|
The assertions are deliberately about behaviour that must hold (status code,
|
|
scope isolation), not about markup.
|
|
"""
|
|
from datetime import timedelta
|
|
|
|
import pytest
|
|
|
|
from app import db as _db
|
|
from app.models.issue import Issue
|
|
from app.models.project import Project, CustomerAssignment
|
|
from app.models.inspector_assignment import InspectorAssignment
|
|
from app.utils.time_utils import now_eastern
|
|
|
|
|
|
@pytest.fixture
|
|
def make_issue(db):
|
|
def _make(facility=None, area=None, severity='high', status='open',
|
|
hours_ago=1, **kw):
|
|
issue = Issue(
|
|
facility_id=facility.id if facility else None,
|
|
area_id=area.id if area else None,
|
|
severity=severity,
|
|
description=kw.pop('description', 'test issue'),
|
|
status=status,
|
|
reported_at=now_eastern() - timedelta(hours=hours_ago),
|
|
)
|
|
for k, v in kw.items():
|
|
setattr(issue, k, v)
|
|
db.session.add(issue)
|
|
db.session.commit()
|
|
return issue
|
|
return _make
|
|
|
|
|
|
@pytest.fixture
|
|
def project(db):
|
|
proj = Project(name='Contract A', active=True)
|
|
db.session.add(proj)
|
|
db.session.commit()
|
|
return proj
|
|
|
|
|
|
@pytest.fixture
|
|
def other_project(db):
|
|
proj = Project(name='Contract B', active=True)
|
|
db.session.add(proj)
|
|
db.session.commit()
|
|
return proj
|
|
|
|
|
|
# ── Dashboard ────────────────────────────────────────────────────────────────
|
|
|
|
@pytest.mark.parametrize('role', ['admin', 'director', 'project_manager', 'auditor'])
|
|
def test_dashboard_renders_for_staff_roles(client, login, make_user,
|
|
make_facility, make_issue,
|
|
project, role):
|
|
"""The card queries (severity / handler / SLA splits) build and run."""
|
|
facility = make_facility(project=project)
|
|
make_issue(facility=facility, severity='critical', hours_ago=100)
|
|
make_issue(facility=facility, severity='low', status='in_progress')
|
|
make_issue(facility=facility, status='resolved',
|
|
resolved_at=now_eastern())
|
|
|
|
login(make_user(role=role))
|
|
assert client.get('/').status_code == 200
|
|
|
|
|
|
def test_dashboard_renders_for_scoped_inspector(client, login, make_user, db,
|
|
make_facility, make_area,
|
|
make_issue, project):
|
|
inspector = make_user(role='inspector')
|
|
db.session.add(InspectorAssignment(user_id=inspector.id,
|
|
project_id=project.id))
|
|
db.session.commit()
|
|
|
|
facility = make_facility(project=project)
|
|
area = make_area(facility)
|
|
make_issue(area=area, severity='high', hours_ago=50) # reached via area
|
|
make_issue(facility=facility, severity='medium') # reached directly
|
|
|
|
login(inspector)
|
|
assert client.get('/').status_code == 200
|
|
|
|
|
|
def test_dashboard_renders_for_inspector_with_no_assignments(client, login,
|
|
make_user):
|
|
"""Strict scoping: no assignments must render an empty dashboard, not 500."""
|
|
login(make_user(role='inspector'))
|
|
assert client.get('/').status_code == 200
|
|
|
|
|
|
def test_dashboard_renders_for_customer(client, login, make_user, db,
|
|
make_facility, make_issue, project):
|
|
customer = make_user(role='customer')
|
|
facility = make_facility(project=project)
|
|
db.session.add(CustomerAssignment(user_id=customer.id,
|
|
project_id=project.id))
|
|
db.session.commit()
|
|
make_issue(facility=facility)
|
|
|
|
login(customer)
|
|
assert client.get('/').status_code == 200
|
|
|
|
|
|
# ── Issues list ──────────────────────────────────────────────────────────────
|
|
|
|
def test_issues_list_renders(client, login, make_user, make_facility,
|
|
make_issue, project):
|
|
facility = make_facility(project=project)
|
|
make_issue(facility=facility)
|
|
login(make_user(role='admin'))
|
|
assert client.get('/issues/').status_code == 200
|
|
|
|
|
|
@pytest.mark.parametrize('query', [
|
|
'?facility_id=abc', # non-numeric -- used to raise ValueError -> 500
|
|
'?facility_id=',
|
|
'?facility_id=999999', # numeric but nonexistent
|
|
'?contract_id=abc',
|
|
'?issue_id=abc',
|
|
'?severity=high&status=open',
|
|
'?handler_type=vendor',
|
|
'?unassigned=1',
|
|
'?sla=breached',
|
|
'?date_from=notadate&date_to=alsonot',
|
|
])
|
|
def test_issues_list_survives_malformed_filters(client, login, make_user,
|
|
make_facility, make_issue,
|
|
project, query):
|
|
"""A hand-edited or stale query string must never 500 the list."""
|
|
facility = make_facility(project=project)
|
|
make_issue(facility=facility)
|
|
login(make_user(role='admin'))
|
|
assert client.get('/issues/' + query).status_code == 200
|
|
|
|
|
|
def test_issues_list_scopes_a_customer_to_their_own_facilities(
|
|
client, login, make_user, db, make_facility, make_issue,
|
|
project, other_project):
|
|
"""The scope filter still isolates customers after the query rewrite."""
|
|
customer = make_user(role='customer')
|
|
mine = make_facility(project=project, name='Mine')
|
|
theirs = make_facility(project=other_project, name='Theirs')
|
|
db.session.add(CustomerAssignment(user_id=customer.id,
|
|
project_id=project.id))
|
|
db.session.commit()
|
|
|
|
make_issue(facility=mine, description='visible to me')
|
|
make_issue(facility=theirs, description='other customer only')
|
|
|
|
login(customer)
|
|
body = client.get('/issues/').get_data(as_text=True)
|
|
assert 'visible to me' in body
|
|
assert 'other customer only' not in body
|
|
|
|
|
|
# ── Reports overview ─────────────────────────────────────────────────────────
|
|
|
|
def test_reports_overview_renders_for_admin(client, login, make_user,
|
|
make_facility, make_template,
|
|
make_inspection, project):
|
|
admin = make_user(role='admin')
|
|
inspector = make_user(role='inspector')
|
|
facility = make_facility(project=project)
|
|
make_inspection(facility, inspector, make_template())
|
|
|
|
login(admin)
|
|
assert client.get('/reports/').status_code == 200
|
|
|
|
|
|
def test_reports_overview_inspector_scope_uses_subquery(
|
|
client, login, make_user, db, make_facility, make_template,
|
|
make_inspection, make_issue, project):
|
|
"""_scope_issue() now filters via a subquery instead of an id list."""
|
|
inspector = make_user(role='inspector')
|
|
db.session.add(InspectorAssignment(user_id=inspector.id,
|
|
project_id=project.id))
|
|
db.session.commit()
|
|
|
|
facility = make_facility(project=project)
|
|
insp = make_inspection(facility, inspector, make_template())
|
|
make_issue(facility=facility, inspection_id=insp.id)
|
|
|
|
login(inspector)
|
|
assert client.get('/reports/').status_code == 200
|
|
|
|
|
|
def test_reports_overview_inspector_with_no_inspections(client, login,
|
|
make_user):
|
|
"""IN (empty subquery) must match nothing rather than error."""
|
|
login(make_user(role='inspector'))
|
|
assert client.get('/reports/').status_code == 200
|