"""Application des rôles : COORDINATION / RÉFÉRENT (LEAD) / BÉNÉVOLE.""" from app.models import Assignment, Role from tests.factories import ( dt_at, make_position, make_shift, make_volunteer, ) def _e(edition, suffix=""): return f"/editions/{edition.id}{suffix}" def test_all_endpoints_require_authentication(raw_client, edition): assert raw_client.get(_e(edition, "/volunteers")).status_code == 401 assert raw_client.get(_e(edition, "/matrix")).status_code == 401 assert raw_client.post(_e(edition, "/positions"), json={}).status_code == 401 def test_account_from_another_edition_is_forbidden( session, as_account, edition, make_account ): import datetime as dt from app.models import Edition other = Edition( year=2027, start_date=dt.date(2027, 7, 1), end_date=dt.date(2027, 7, 4) ) session.add(other) session.commit() session.refresh(other) from app.models import Account stranger = Account( edition_id=other.id, email="x@e.fr", role=Role.COORDINATOR ) session.add(stranger) session.commit() session.refresh(stranger) client = as_account(stranger) assert client.get(_e(edition, "/volunteers")).status_code == 403 # --------------------------------------------------------------------------- # # BÉNÉVOLE # # --------------------------------------------------------------------------- # def test_volunteer_scope(session, edition, make_account, as_account): me = make_volunteer(session, edition, email="me@e.fr") other = make_volunteer(session, edition, email="other@e.fr") account = make_account(Role.VOLUNTEER, volunteer_id=me.id, email="me@e.fr") client = as_account(account) assert client.get(_e(edition, "/volunteers")).status_code == 403 assert client.get(_e(edition, f"/volunteers/{me.id}")).status_code == 200 assert client.get(_e(edition, f"/volunteers/{other.id}")).status_code == 403 assert client.get(_e(edition, "/matrix")).status_code == 403 assert ( client.post( _e(edition, "/volunteers"), json={"first_name": "A", "last_name": "B"}, ).status_code == 403 ) def test_volunteer_sees_only_own_assignments( session, edition, make_account, as_account ): me = make_volunteer(session, edition, email="me@e.fr") other = make_volunteer(session, edition, email="other@e.fr") position = make_position(session, edition) shift = make_shift( session, edition, position, start=dt_at(10, 9), end=dt_at(10, 12) ) session.add_all( [ Assignment( edition_id=edition.id, volunteer_id=me.id, shift_id=shift.id ), ] ) session.commit() account = make_account(Role.VOLUNTEER, volunteer_id=me.id, email="me@e.fr") client = as_account(account) mine = client.get(_e(edition, "/assignments")) assert mine.status_code == 200 assert all(a["volunteer_id"] == str(me.id) for a in mine.json()) assert ( client.get( _e(edition, "/assignments"), params={"volunteer_id": str(other.id)}, ).status_code == 403 ) # --------------------------------------------------------------------------- # # RÉFÉRENT (LEAD) # # --------------------------------------------------------------------------- # def test_lead_write_is_limited_to_own_positions( session, edition, make_account, as_account ): lead = make_account(Role.LEAD, email="lead@e.fr") mine = make_position(session, edition, name="Bar") mine.lead_account_id = lead.id session.add(mine) session.commit() theirs = make_position(session, edition, name="Régie") client = as_account(lead) # Lecture sur toute l'édition. assert client.get(_e(edition, "/matrix")).status_code == 200 assert client.get(_e(edition, "/positions")).status_code == 200 # Création de poste réservée à la coordination. assert ( client.post( _e(edition, "/positions"), json={"name": "N", "category": "BAR"}, ).status_code == 403 ) # PATCH : son poste oui, celui d'un autre non. assert ( client.patch( _e(edition, f"/positions/{mine.id}"), json={"location": "Chapiteau"} ).status_code == 200 ) assert ( client.patch( _e(edition, f"/positions/{theirs.id}"), json={"location": "X"} ).status_code == 403 ) # Créneau : sur son poste oui, sur celui d'un autre non. ok = client.post( _e(edition, "/shifts"), json={ "position_id": str(mine.id), "starts_at": "2026-07-10T09:00:00", "ends_at": "2026-07-10T12:00:00", }, ) assert ok.status_code == 201 ko = client.post( _e(edition, "/shifts"), json={ "position_id": str(theirs.id), "starts_at": "2026-07-10T09:00:00", "ends_at": "2026-07-10T12:00:00", }, ) assert ko.status_code == 403 # Ni bénévoles ni import. assert ( client.post( _e(edition, "/volunteers"), json={"first_name": "A", "last_name": "B"}, ).status_code == 403 ) assert ( client.post( _e(edition, "/import/analyze"), files={"file": ("x.csv", b"a;b\n1;2\n", "text/csv")}, ).status_code == 403 ) def test_lead_assignment_scope(session, edition, make_account, as_account): lead = make_account(Role.LEAD, email="lead@e.fr") mine = make_position(session, edition, name="Bar") mine.lead_account_id = lead.id session.add(mine) session.commit() theirs = make_position(session, edition, name="Accueil") my_shift = make_shift( session, edition, mine, start=dt_at(10, 9), end=dt_at(10, 12) ) their_shift = make_shift( session, edition, theirs, start=dt_at(11, 9), end=dt_at(11, 12) ) volunteer = make_volunteer(session, edition, email="v@e.fr") client = as_account(lead) ok = client.post( _e(edition, "/assignments"), json={ "volunteer_id": str(volunteer.id), "shift_id": str(my_shift.id), }, ) assert ok.status_code == 201 ko = client.post( _e(edition, "/assignments"), json={ "volunteer_id": str(volunteer.id), "shift_id": str(their_shift.id), }, ) assert ko.status_code == 403 # --------------------------------------------------------------------------- # # Comptes (coordination uniquement) # # --------------------------------------------------------------------------- # def test_account_management_is_coordinator_only( session, edition, coordinator, make_account, as_account ): coord_client = as_account(coordinator) created = coord_client.post( _e(edition, "/accounts"), json={ "email": "nouveau.referent@e.fr", "role": "LEAD", "volunteer_id": None, }, ) assert created.status_code == 201 account_id = created.json()["id"] assert coord_client.get(_e(edition, "/accounts")).status_code == 200 lead_client = as_account(make_account(Role.LEAD, email="lead@e.fr")) assert lead_client.get(_e(edition, "/accounts")).status_code == 403 assert ( lead_client.delete( _e(edition, f"/accounts/{account_id}") ).status_code == 403 ) # `as_account` fixe l'identité active : on repasse en COORDINATION. coord_client = as_account(coordinator) assert ( coord_client.delete( _e(edition, f"/accounts/{account_id}") ).status_code == 204 )