Aug 19 - Update test files
This commit is contained in:
@@ -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
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user