94 lines
2.9 KiB
Python
94 lines
2.9 KiB
Python
"""
|
||
tests/test_staff_login.py
|
||
Tests for staff phone+passcode login flow and brute-force lockout.
|
||
"""
|
||
|
||
import pytest
|
||
from app.extensions import db, bcrypt as _bcrypt
|
||
from app.models.platform import Tenant, Plan
|
||
from app.models.salon import Staff, Location
|
||
|
||
|
||
def _seed_staff(app, phone="5551234567", passcode="1234"):
|
||
with app.app_context():
|
||
plan = Plan.query.first()
|
||
tenant = Tenant(
|
||
slug=f"staff-test-{phone}",
|
||
name="Staff Test Salon",
|
||
owner_email="owner@stafftest.com",
|
||
plan_id=plan.id,
|
||
status="active",
|
||
)
|
||
db.session.add(tenant)
|
||
db.session.flush()
|
||
|
||
location = Location(
|
||
tenant_id=tenant.id,
|
||
name="Main",
|
||
is_primary=True,
|
||
is_active=True,
|
||
)
|
||
db.session.add(location)
|
||
db.session.flush()
|
||
|
||
staff = Staff(
|
||
tenant_id=tenant.id,
|
||
name="Jane Nail Tech",
|
||
phone=phone,
|
||
passcode_hash=_bcrypt.generate_password_hash(passcode).decode("utf-8"),
|
||
staff_type="full_time",
|
||
pay_type="hourly",
|
||
is_active=True,
|
||
)
|
||
staff.locations.append(location)
|
||
db.session.add(staff)
|
||
db.session.commit()
|
||
return staff.id
|
||
|
||
|
||
class TestStaffLogin:
|
||
def test_staff_login_page_loads(self, tenant_client):
|
||
resp = tenant_client.get("/staff-login")
|
||
assert resp.status_code == 200
|
||
assert b"Staff Login" in resp.data
|
||
|
||
def test_valid_staff_login(self, tenant_app, tenant_client):
|
||
_seed_staff(tenant_app, phone="5550000001", passcode="1234")
|
||
resp = tenant_client.post(
|
||
"/staff-login",
|
||
data={"phone": "5550000001", "passcode": "1234"},
|
||
follow_redirects=True,
|
||
)
|
||
assert resp.status_code == 200
|
||
|
||
def test_invalid_passcode(self, tenant_app, tenant_client):
|
||
_seed_staff(tenant_app, phone="5550000002", passcode="5678")
|
||
resp = tenant_client.post(
|
||
"/staff-login",
|
||
data={"phone": "5550000002", "passcode": "9999"},
|
||
follow_redirects=True,
|
||
)
|
||
assert b"Invalid" in resp.data
|
||
|
||
def test_passcode_too_short(self, tenant_client):
|
||
resp = tenant_client.post(
|
||
"/staff-login",
|
||
data={"phone": "5550000003", "passcode": "12"},
|
||
follow_redirects=True,
|
||
)
|
||
assert b"4" in resp.data # "4–6 digits" error message
|
||
|
||
def test_brute_force_lockout(self, tenant_app, tenant_client):
|
||
_seed_staff(tenant_app, phone="5550000009", passcode="1234")
|
||
for _ in range(5):
|
||
tenant_client.post(
|
||
"/staff-login",
|
||
data={"phone": "5550000009", "passcode": "9999"},
|
||
)
|
||
resp = tenant_client.post(
|
||
"/staff-login",
|
||
data={"phone": "5550000009", "passcode": "1234"},
|
||
follow_redirects=True,
|
||
)
|
||
assert b"locked" in resp.data.lower()
|