Aug 27 - Update code to follow-up with ST functions
This commit is contained in:
@@ -0,0 +1,131 @@
|
||||
"""
|
||||
tests/test_followup_ownership.py
|
||||
---------------------------------
|
||||
phase56 — a follow-up has exactly ONE owner, and every surface must agree.
|
||||
|
||||
`Inspection.follow_up_owner` (the loaded-row property) and
|
||||
`Inspection.follow_up_owned_by()` (the SQL predicate) are two expressions of
|
||||
the same rule: the assignee when one is set, the inspection's own inspector
|
||||
otherwise. They are declared next to each other because they must not drift.
|
||||
|
||||
What these guard
|
||||
----------------
|
||||
The predicate's two arms are mutually exclusive ON PURPOSE. Drop the
|
||||
`is_(None)` from the second arm and the original inspector keeps matching a
|
||||
follow-up that was handed to somebody else — two people turn up to do the same
|
||||
re-inspection, and nothing errors.
|
||||
|
||||
The three query surfaces (mobile list filter, web dashboard card, iPad stats
|
||||
KPI) each used to express ownership by hand, and three of them tested
|
||||
AUTHORSHIP: an assignee saw the work in their list while both dashboards read
|
||||
0. These tests pin the predicate itself; the surfaces now call it rather than
|
||||
re-deriving it.
|
||||
"""
|
||||
|
||||
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 _user(username, role='inspector'):
|
||||
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=True, password_set=True)
|
||||
u.set_password('pw-correct1')
|
||||
db.session.add(u)
|
||||
db.session.commit()
|
||||
return u
|
||||
|
||||
|
||||
def _inspection(inspector, assignee=None, follow_up=True):
|
||||
from app import db
|
||||
from app.models.facility import Facility
|
||||
from app.models.inspection import Inspection, InspectionTemplate
|
||||
|
||||
fac = Facility.query.first()
|
||||
if fac is None:
|
||||
fac = Facility(name='Main Office', active=True)
|
||||
db.session.add(fac)
|
||||
db.session.commit()
|
||||
tmpl = InspectionTemplate.query.first()
|
||||
if tmpl is None:
|
||||
tmpl = InspectionTemplate(name='Restroom Check', active=True, form_schema=[])
|
||||
db.session.add(tmpl)
|
||||
db.session.commit()
|
||||
|
||||
insp = Inspection(template_id=tmpl.id, facility_id=fac.id,
|
||||
inspector_id=inspector.id, status='completed',
|
||||
follow_up_required=follow_up,
|
||||
follow_up_assigned_to=(assignee.id if assignee else None))
|
||||
db.session.add(insp)
|
||||
db.session.commit()
|
||||
return insp
|
||||
|
||||
|
||||
def _owned_ids(user):
|
||||
from app.models.inspection import Inspection
|
||||
return sorted(i.id for i in Inspection.query
|
||||
.filter(Inspection.follow_up_owned_by(user.id)).all())
|
||||
|
||||
|
||||
# ── the property ─────────────────────────────────────────────────────────────
|
||||
|
||||
def test_owner_is_the_inspector_when_unassigned(client):
|
||||
ivy = _user('ivy')
|
||||
insp = _inspection(ivy)
|
||||
assert insp.follow_up_owner.id == ivy.id
|
||||
|
||||
|
||||
def test_owner_is_the_assignee_when_assigned(client):
|
||||
ivy, sam = _user('ivy'), _user('sam')
|
||||
insp = _inspection(ivy, assignee=sam)
|
||||
assert insp.follow_up_owner.id == sam.id
|
||||
|
||||
|
||||
# ── the SQL predicate ────────────────────────────────────────────────────────
|
||||
|
||||
def test_predicate_matches_unassigned_own_work(client):
|
||||
ivy = _user('ivy')
|
||||
insp = _inspection(ivy)
|
||||
assert _owned_ids(ivy) == [insp.id]
|
||||
|
||||
|
||||
def test_predicate_matches_work_handed_to_me(client):
|
||||
ivy, sam = _user('ivy'), _user('sam')
|
||||
insp = _inspection(ivy, assignee=sam)
|
||||
assert _owned_ids(sam) == [insp.id]
|
||||
|
||||
|
||||
def test_predicate_releases_the_original_inspector_once_assigned(client):
|
||||
"""The `is_(None)` on the second arm. Without it Ivy still matches, and two
|
||||
people turn up to do the same re-inspection."""
|
||||
ivy, sam = _user('ivy'), _user('sam')
|
||||
_inspection(ivy, assignee=sam)
|
||||
assert _owned_ids(ivy) == []
|
||||
|
||||
|
||||
def test_every_row_has_exactly_one_owner(client):
|
||||
"""The property and the predicate must partition the rows, not overlap."""
|
||||
from app.models.inspection import Inspection
|
||||
|
||||
ivy, sam, zoe = _user('ivy'), _user('sam'), _user('zoe')
|
||||
_inspection(ivy) # unassigned, Ivy's
|
||||
_inspection(ivy, assignee=sam) # Ivy's work, Sam's follow-up
|
||||
_inspection(sam, assignee=sam) # Sam's work, explicitly Sam's
|
||||
_inspection(zoe) # unassigned, Zoe's
|
||||
|
||||
everyone = [ivy, sam, zoe]
|
||||
for insp in Inspection.query.all():
|
||||
owners = [u.id for u in everyone if insp.id in _owned_ids(u)]
|
||||
assert owners == [insp.follow_up_owner.id], (
|
||||
f'inspection {insp.id}: predicate says {owners}, '
|
||||
f'property says {insp.follow_up_owner.id}')
|
||||
Reference in New Issue
Block a user