Aug 19 - Update test files

This commit is contained in:
2026-08-19 16:34:42 -04:00
parent a4a8d84801
commit 8b2582705d
2 changed files with 95 additions and 17 deletions
+62 -15
View File
@@ -15,10 +15,13 @@ Runs on the in-memory SQLite app fixture (multi-tenancy inert). Covers:
* facility / inspection / issue list routes stay scoped for the new role * facility / inspection / issue list routes stay scoped for the new role
* the notification matrix exposes an External Inspector column whose defaults * the notification matrix exposes an External Inspector column whose defaults
mirror the Inspector column mirror the Inspector column
* creating an external inspector sends an invitation instead of setting a * an invited Customer Inspector is created with password_set False and a
password: password_set is False and a set-password token is minted set-password token, and cannot log in until they use it
* creating any other role still requires a password * creating any other role still requires a password
* the assign-contracts page accepts an external inspector (it 404'd before) * creating a Customer Inspector goes through Customer Management, and
User Management no longer offers the role (phase51)
* the staff assign-contracts page redirects a customer-side account to
the page that owns it, and still serves our own inspectors
The regression guard that matters most is The regression guard that matters most is
test_external_inspector_scope_is_not_unrestricted: if a future edit reverts a test_external_inspector_scope_is_not_unrestricted: if a future edit reverts a
@@ -224,23 +227,27 @@ def test_matrix_defaults_mirror_the_inspector_column(client):
# ── Invitation flow ────────────────────────────────────────────────────────── # ── Invitation flow ──────────────────────────────────────────────────────────
def test_creating_external_inspector_invites_instead_of_setting_password(client): def test_creating_external_inspector_invites_instead_of_setting_password(client):
"""phase51 moved this account type to Customer Management.
The invariant is unchanged and is what this test guards: a Customer
Inspector is INVITED, never given a password we chose. Only the door
changed — /customers/new instead of /auth/users/new — because both
customer-side roles are now owned by /customers.
"""
from app.models.user import User from app.models.user import User
env = _seed() env = _seed()
_login(client, env['admin']) _login(client, env['admin'])
resp = client.post('/auth/users/new', data={ resp = client.post('/customers/new', data={
'username': 'newxan',
'full_name': 'New Xan', 'full_name': 'New Xan',
'email': 'newxan@example.com', 'email': 'newxan@example.com',
'role': 'external_inspector', 'role': 'external_inspector',
'password': '',
'confirm_password': '',
}, follow_redirects=True) }, follow_redirects=True)
assert resp.status_code == 200 assert resp.status_code == 200
created = User.query.filter_by(username='newxan').first() created = User.query.filter_by(email='newxan@example.com').first()
assert created is not None, 'external inspector was not created' assert created is not None, 'customer inspector was not created'
assert created.role == 'external_inspector' assert created.role == 'external_inspector'
# Invited, not password-set: login is blocked until they use the link. # Invited, not password-set: login is blocked until they use the link.
assert created.password_set is False assert created.password_set is False
@@ -248,22 +255,42 @@ def test_creating_external_inspector_invites_instead_of_setting_password(client)
assert created.set_password_token_expires is not None assert created.set_password_token_expires is not None
def test_user_management_no_longer_creates_customer_side_accounts(client):
"""The other half of the move: User Management must not mint one.
UserForm stopped offering 'external_inspector', so a crafted POST hits
SelectField validation and nothing is created. Without this, a second
creation path could quietly reappear and skip the invitation flow.
"""
from app.models.user import User
env = _seed()
_login(client, env['admin'])
client.post('/auth/users/new', data={
'username': 'sneaky',
'full_name': 'Sneaky Xan',
'email': 'sneaky@example.com',
'role': 'external_inspector',
'password': '',
'confirm_password': '',
}, follow_redirects=True)
assert User.query.filter_by(username='sneaky').first() is None
def test_invited_external_inspector_cannot_log_in_until_setup(client): def test_invited_external_inspector_cannot_log_in_until_setup(client):
from app.models.user import User from app.models.user import User
env = _seed() env = _seed()
_login(client, env['admin']) _login(client, env['admin'])
client.post('/auth/users/new', data={ client.post('/customers/new', data={
'username': 'newxan',
'full_name': 'New Xan', 'full_name': 'New Xan',
'email': 'newxan@example.com', 'email': 'newxan@example.com',
'role': 'external_inspector', 'role': 'external_inspector',
'password': '',
'confirm_password': '',
}, follow_redirects=True) }, follow_redirects=True)
client.get('/auth/logout', follow_redirects=True) client.get('/auth/logout', follow_redirects=True)
created = User.query.filter_by(username='newxan').first() created = User.query.filter_by(email='newxan@example.com').first()
# The placeholder hash is random, so no password can work; assert the # The placeholder hash is random, so no password can work; assert the
# account is in the blocked state rather than guessing a credential. # account is in the blocked state rather than guessing a credential.
assert created.password_set is False assert created.password_set is False
@@ -289,11 +316,31 @@ def test_creating_a_normal_role_still_requires_a_password(client):
# ── Assign-contracts page ──────────────────────────────────────────────────── # ── Assign-contracts page ────────────────────────────────────────────────────
def test_assign_contracts_page_accepts_external_inspector(client): def test_assign_contracts_page_redirects_customer_side_to_customer_management(client):
"""phase51: one editor per account, not two.
The staff assign-contracts URL still exists for our own inspectors, but a
customer-side account is redirected to the page that now owns it. Editing
one through UserForm would fail anyway — 'external_inspector' is no longer
an offered role choice, so SelectField would reject the stored value.
"""
env = _seed() env = _seed()
_login(client, env['admin']) _login(client, env['admin'])
resp = client.get(f"/auth/users/{env['external'].id}/assign-contracts") resp = client.get(f"/auth/users/{env['external'].id}/assign-contracts")
assert resp.status_code == 302
assert f"/customers/{env['external'].id}" in resp.headers['Location']
# …and the page it redirects to is the real editor.
assert client.get(f"/customers/{env['external'].id}").status_code == 200
def test_assign_contracts_page_still_accepts_our_own_inspector(client):
"""The staff path must not have been broken by the redirect guard."""
env = _seed()
_login(client, env['admin'])
resp = client.get(f"/auth/users/{env['internal'].id}/assign-contracts")
assert resp.status_code == 200 assert resp.status_code == 200
+33 -2
View File
@@ -343,8 +343,39 @@ def test_inspector_is_not_offered_schedule_creation(client):
assert 'Create First Schedule' not in body assert 'Create First Schedule' not in body
def test_customers_are_still_barred(client): def test_customer_directors_may_reach_the_schedule_list(client):
"""A Customer Director plans work at their OWN facilities (Aug 2026).
They reach the list — scoped to their assignments, which for an
unassigned account like this one means an empty list, not a 403.
"""
tmpl, fac = _seed() tmpl, fac = _seed()
cust = _user('cara', 'customer') cust = _user('cara', 'customer')
_login(client, cust) _login(client, cust)
assert client.get('/inspection-schedules/?tab=completed').status_code == 403 assert client.get('/inspection-schedules/?tab=completed').status_code == 200
def test_customers_still_cannot_start_a_scheduled_inspection(client):
"""Planning is not performing — the boundary that replaced the old 403.
A Customer Director may create and edit a schedule; Start belongs to the
assigned inspector, and a customer is never one.
"""
from app import db
from app.models.inspection_schedule import InspectionSchedule
from app.utils.time_utils import now_eastern
tmpl, fac = _seed()
insp = _user('ivy', 'inspector')
sched = InspectionSchedule(
name='Weekly check', template_id=tmpl.id, facility_id=fac.id,
inspector_id=insp.id, frequency='weekly', mode='plan', active=True,
created_at=now_eastern(), next_run_at=now_eastern(),
)
db.session.add(sched)
db.session.commit()
sid = sched.id
cust = _user('cara', 'customer')
_login(client, cust)
assert client.get(f'/inspection-schedules/{sid}/start').status_code == 403