Jul 10 - Add test suite
This commit is contained in:
@@ -0,0 +1,138 @@
|
||||
"""
|
||||
Integration tests for the login-free public QR pages (app/routes/public.py).
|
||||
|
||||
These lock in CLAUDE.md rule 74 (occupant-safe): the pages are token-addressed,
|
||||
404 on inactive/unknown facilities, and must never leak checklist/template
|
||||
names, per-inspection scores, or issue descriptions. Covers both the facility
|
||||
page (/f/<token>) and the phase39 per-area page (/f/area/<token>).
|
||||
"""
|
||||
from app.models.issue import Issue
|
||||
|
||||
|
||||
# ── Facility page ─────────────────────────────────────────────────────────────
|
||||
|
||||
def test_facility_page_renders_and_shows_counts(
|
||||
client, make_facility, make_user, make_template, make_inspection, make_issue):
|
||||
fac = make_facility()
|
||||
inspector = make_user(role='inspector')
|
||||
tpl = make_template(name='SECRET-CHECKLIST-NAME')
|
||||
make_inspection(fac, inspector, tpl, overall_score=93.8, days_ago=5)
|
||||
make_inspection(fac, inspector, tpl, overall_score=88.0, days_ago=10)
|
||||
make_issue(facility=fac, status='open')
|
||||
|
||||
resp = client.get(f'/f/{fac.public_token}')
|
||||
assert resp.status_code == 200
|
||||
body = resp.get_data(as_text=True)
|
||||
assert fac.name in body
|
||||
# Aggregate rating is shown...
|
||||
assert 'Avg Score' in body
|
||||
assert 'Report a Problem' in body
|
||||
|
||||
|
||||
def test_facility_page_does_not_leak_internal_detail(
|
||||
client, make_facility, make_user, make_template, make_inspection, make_issue):
|
||||
fac = make_facility()
|
||||
inspector = make_user(role='inspector')
|
||||
tpl = make_template(name='SECRET-CHECKLIST-NAME')
|
||||
make_inspection(fac, inspector, tpl, overall_score=77.0, days_ago=3)
|
||||
make_issue(facility=fac, status='open',
|
||||
description='CONFIDENTIAL broken toilet in stall 3')
|
||||
|
||||
body = client.get(f'/f/{fac.public_token}').get_data(as_text=True)
|
||||
# Rule 74: no checklist/template names, no issue descriptions.
|
||||
assert 'SECRET-CHECKLIST-NAME' not in body
|
||||
assert 'CONFIDENTIAL' not in body
|
||||
assert 'stall 3' not in body
|
||||
|
||||
|
||||
def test_inactive_facility_404(client, make_facility):
|
||||
fac = make_facility(active=False)
|
||||
assert client.get(f'/f/{fac.public_token}').status_code == 404
|
||||
|
||||
|
||||
def test_unknown_token_404(client):
|
||||
assert client.get('/f/does-not-exist').status_code == 404
|
||||
|
||||
|
||||
def test_facility_report_creates_open_issue(client, db, make_facility):
|
||||
fac = make_facility()
|
||||
resp = client.post(
|
||||
f'/f/{fac.public_token}/report',
|
||||
data={'description': 'The lobby floor is very slippery today.'},
|
||||
follow_redirects=True,
|
||||
)
|
||||
assert resp.status_code == 200
|
||||
issues = Issue.query.filter_by(facility_id=fac.id).all()
|
||||
assert len(issues) == 1
|
||||
iss = issues[0]
|
||||
assert iss.status == 'open'
|
||||
assert iss.severity == 'medium'
|
||||
assert iss.reported_by is None # public reporter is not a User
|
||||
assert 'slippery' in iss.description
|
||||
|
||||
|
||||
def test_report_honeypot_silently_drops(client, make_facility):
|
||||
fac = make_facility()
|
||||
resp = client.post(
|
||||
f'/f/{fac.public_token}/report',
|
||||
data={'description': 'spam spam spam', 'website': 'http://bot.example'},
|
||||
follow_redirects=True,
|
||||
)
|
||||
assert resp.status_code == 200
|
||||
assert Issue.query.filter_by(facility_id=fac.id).count() == 0
|
||||
|
||||
|
||||
def test_report_rejects_too_short_description(client, make_facility):
|
||||
fac = make_facility()
|
||||
resp = client.post(
|
||||
f'/f/{fac.public_token}/report',
|
||||
data={'description': 'x'}, # under the 5-char minimum
|
||||
)
|
||||
assert resp.status_code == 400
|
||||
assert Issue.query.filter_by(facility_id=fac.id).count() == 0
|
||||
|
||||
|
||||
# ── Area page (phase39) ───────────────────────────────────────────────────────
|
||||
|
||||
def test_area_page_renders(client, make_facility, make_area):
|
||||
fac = make_facility()
|
||||
area = make_area(fac)
|
||||
resp = client.get(f'/f/area/{area.public_token}')
|
||||
assert resp.status_code == 200
|
||||
body = resp.get_data(as_text=True)
|
||||
assert area.name in body
|
||||
assert fac.name in body
|
||||
assert 'Report a Problem' in body
|
||||
|
||||
|
||||
def test_area_page_404_when_facility_inactive(client, make_facility, make_area):
|
||||
fac = make_facility(active=False)
|
||||
area = make_area(fac)
|
||||
# Area token is valid, but its parent facility is inactive -> 404.
|
||||
assert client.get(f'/f/area/{area.public_token}').status_code == 404
|
||||
|
||||
|
||||
def test_area_report_sets_area_id(client, make_facility, make_area):
|
||||
fac = make_facility()
|
||||
area = make_area(fac)
|
||||
resp = client.post(
|
||||
f'/f/area/{area.public_token}/report',
|
||||
data={'description': 'Paper towel dispenser is empty in this restroom.'},
|
||||
follow_redirects=True,
|
||||
)
|
||||
assert resp.status_code == 200
|
||||
iss = Issue.query.filter_by(area_id=area.id).one()
|
||||
assert iss.facility_id == fac.id
|
||||
assert iss.area_id == area.id
|
||||
assert iss.status == 'open'
|
||||
assert iss.reported_by is None
|
||||
|
||||
|
||||
def test_area_and_facility_tokens_do_not_collide(
|
||||
client, make_facility, make_area):
|
||||
"""A facility token hitting the area route (and vice-versa) must 404,
|
||||
proving the /f/<token> and /f/area/<token> routes stay distinct."""
|
||||
fac = make_facility()
|
||||
area = make_area(fac)
|
||||
# Facility token on the area route -> no area with that token -> 404
|
||||
assert client.get(f'/f/area/{fac.public_token}').status_code == 404
|
||||
Reference in New Issue
Block a user