Files
benevoles/backend/tests/test_api_assignments.py
T
AdamThomasandClaude Opus 5.5 0f904f5e3d
ci-cd / build-backend (push) Successful in 45s
ci-cd / build-frontend (push) Successful in 24s
ci-cd / deploy (push) Successful in 27s
Deux postes en même temps possibles, malus dans le solveur
Le chevauchement de créneaux (SHIFT_OVERLAP) n'est plus bloquant : il
remonte en avertissement et l'affectation est enregistrée. Le solveur ne
l'exclut plus mais applique un fort malus au bénévole déjà affecté
ailleurs au même moment (proposé seulement sous l'effectif minimal, faute
de mieux) ; il n'est jamais reproposé sur un créneau où il est déjà.
Même malus dans le classement par défaut du panneau et dans le mock.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 10:38:52 +01:00

149 lines
5.0 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import datetime as dt
from tests.factories import (
dt_at,
make_position,
make_shift,
make_volunteer,
)
def _url(edition, suffix=""):
return f"/editions/{edition.id}/assignments{suffix}"
def _setup(session, edition):
position = make_position(session, edition)
shift = make_shift(
session, edition, position, start=dt_at(10, 14), end=dt_at(10, 18)
)
volunteer = make_volunteer(session, edition, email="v@ex.fr")
return volunteer, shift
def test_create_assignment_clean(client, edition, session, day_parts):
volunteer, shift = _setup(session, edition)
resp = client.post(
_url(edition),
json={"volunteer_id": str(volunteer.id), "shift_id": str(shift.id)},
)
assert resp.status_code == 201
body = resp.json()
assert body["warnings"] == []
assert body["assignment"]["source"] == "MANUAL"
assert body["assignment"]["locked"] is False
def test_long_day_without_rest_raises_no_warning(client, edition, session, day_parts):
# Plus de limite d'heures par jour ni de repos minimal : 14h–18h puis
# 18h–04h (14 h d'affilée) passe sans avertissement.
volunteer, shift = _setup(session, edition)
position = make_position(session, edition, name="Bar")
following = make_shift(
session, edition, position, start=dt_at(10, 18), end=dt_at(11, 4)
)
for s in (shift, following):
resp = client.post(
_url(edition),
json={"volunteer_id": str(volunteer.id), "shift_id": str(s.id)},
)
assert resp.status_code == 201
assert resp.json()["warnings"] == []
def test_create_assignment_overlap_is_allowed_with_warning(
client, edition, session, day_parts
):
# Un bénévole peut tenir deux postes en même temps : l'affectation est
# enregistrée, le chevauchement remonte en avertissement.
volunteer, shift = _setup(session, edition)
assert client.post(
_url(edition),
json={"volunteer_id": str(volunteer.id), "shift_id": str(shift.id)},
).status_code == 201
position = make_position(session, edition, name="Parking")
overlapping = make_shift(
session, edition, position, start=dt_at(10, 16), end=dt_at(10, 20)
)
resp = client.post(
_url(edition),
json={
"volunteer_id": str(volunteer.id),
"shift_id": str(overlapping.id),
},
)
assert resp.status_code == 201
warnings = resp.json()["warnings"]
assert [w["code"] for w in warnings] == ["SHIFT_OVERLAP"]
assert warnings[0]["severity"] == "WARNING"
listed = client.get(
_url(edition), params={"shift_id": str(overlapping.id)}
).json()
assert len(listed) == 1
def test_create_duplicate_assignment_conflicts(client, edition, session, day_parts):
volunteer, shift = _setup(session, edition)
body = {"volunteer_id": str(volunteer.id), "shift_id": str(shift.id)}
assert client.post(_url(edition), json=body).status_code == 201
resp = client.post(_url(edition), json=body)
assert resp.status_code == 409
assert resp.json()["detail"]["code"] == "assignment.duplicate"
def test_assignment_wrong_edition_is_422(client, edition, session, day_parts):
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)
stranger = make_volunteer(session, other, email="s@ex.fr")
_, shift = _setup(session, edition)
resp = client.post(
_url(edition),
json={"volunteer_id": str(stranger.id), "shift_id": str(shift.id)},
)
assert resp.status_code == 422
assert resp.json()["detail"]["code"] == "assignment.wrong_edition"
def test_validate_endpoint_does_not_persist(client, edition, session, day_parts):
volunteer, shift = _setup(session, edition)
resp = client.post(
_url(edition, "/validate"),
json={"volunteer_id": str(volunteer.id), "shift_id": str(shift.id)},
)
assert resp.status_code == 200
body = resp.json()
assert body["blocking"] is False
assert body["violations"] == []
assert client.get(_url(edition)).json() == []
def test_patch_locked_keeps_assignment(client, edition, session, day_parts):
volunteer, shift = _setup(session, edition)
aid = client.post(
_url(edition),
json={"volunteer_id": str(volunteer.id), "shift_id": str(shift.id)},
).json()["assignment"]["id"]
resp = client.patch(_url(edition, f"/{aid}"), json={"locked": True})
assert resp.status_code == 200
assert resp.json()["assignment"]["locked"] is True
assert resp.json()["warnings"] == []
def test_delete_assignment(client, edition, session, day_parts):
volunteer, shift = _setup(session, edition)
aid = client.post(
_url(edition),
json={"volunteer_id": str(volunteer.id), "shift_id": str(shift.id)},
).json()["assignment"]["id"]
assert client.delete(_url(edition, f"/{aid}")).status_code == 204
assert client.get(_url(edition, f"/{aid}")).status_code == 404