{creneaux} devient un tableau par jour de festival (horaire, poste,
référent avec son téléphone), en HTML dans l'email et en lignes lisibles
dans la version texte — envoi réel et email de test.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
132 lines
5.2 KiB
Python
132 lines
5.2 KiB
Python
from sqlmodel import select
|
||
|
||
from app.models import Assignment, MagicLinkToken, Role
|
||
from tests.factories import dt_at, make_position, make_shift, make_volunteer
|
||
from tests.mail import plain_text
|
||
|
||
|
||
def _url(edition):
|
||
return f"/editions/{edition.id}/notifications/planning"
|
||
|
||
|
||
def test_one_email_per_volunteer_with_an_address(client, edition, session, outbox):
|
||
position = make_position(session, edition, name="Bar")
|
||
s1 = make_shift(session, edition, position, start=dt_at(10, 9), end=dt_at(10, 13))
|
||
s2 = make_shift(session, edition, position, start=dt_at(11, 9), end=dt_at(11, 12))
|
||
ana = make_volunteer(session, edition, first_name="Ana", email="ana@e.fr")
|
||
make_volunteer(session, edition, first_name="Bob", email="bob@e.fr")
|
||
make_volunteer(session, edition, first_name="Cid", email=None)
|
||
session.add_all(
|
||
[
|
||
Assignment(edition_id=edition.id, volunteer_id=ana.id, shift_id=s1.id),
|
||
Assignment(edition_id=edition.id, volunteer_id=ana.id, shift_id=s2.id),
|
||
]
|
||
)
|
||
session.commit()
|
||
|
||
resp = client.post(
|
||
_url(edition),
|
||
json={
|
||
"subject": "Planning de {prenom}",
|
||
"body": "Tu as {nb_creneaux} créneau(x), {nb_heures} h.\n{creneaux}",
|
||
},
|
||
)
|
||
assert resp.status_code == 200
|
||
body = resp.json()
|
||
assert body["queued"] == 2
|
||
assert body["skipped_no_email"] == 1
|
||
assert len(outbox) == 2
|
||
|
||
by_to = {m["To"]: m for m in outbox}
|
||
assert by_to["ana@e.fr"]["Subject"] == "Planning de Ana"
|
||
ana_body = plain_text(by_to["ana@e.fr"])
|
||
assert "Tu as 2 créneau(x), 7.0 h." in ana_body
|
||
assert "Bar" in ana_body
|
||
assert "Tu as 0 créneau(x), 0.0 h." in plain_text(by_to["bob@e.fr"])
|
||
|
||
|
||
def test_default_template_is_used_when_body_omitted(client, edition, session, outbox):
|
||
make_volunteer(session, edition, first_name="Ana", email="ana@e.fr")
|
||
resp = client.post(_url(edition), json={})
|
||
assert resp.status_code == 200
|
||
assert plain_text(outbox[0]).startswith("Bonjour Ana,")
|
||
|
||
|
||
def test_magic_link_is_embedded_when_placeholder_present(
|
||
client, edition, session, make_account, outbox
|
||
):
|
||
ana = make_volunteer(session, edition, first_name="Ana", email="ana@e.fr")
|
||
make_account(Role.VOLUNTEER, volunteer_id=ana.id, email="ana@e.fr")
|
||
|
||
resp = client.post(
|
||
_url(edition), json={"body": "Connecte-toi : {lien_connexion}"}
|
||
)
|
||
assert resp.status_code == 200
|
||
assert "/connexion?token=" in plain_text(outbox[0])
|
||
assert len(session.exec(select(MagicLinkToken)).all()) == 1
|
||
|
||
|
||
def test_no_magic_link_token_without_placeholder(
|
||
client, edition, session, make_account, outbox
|
||
):
|
||
ana = make_volunteer(session, edition, first_name="Ana", email="ana@e.fr")
|
||
make_account(Role.VOLUNTEER, volunteer_id=ana.id, email="ana@e.fr")
|
||
client.post(_url(edition), json={"body": "Bonjour {prenom}"})
|
||
assert session.exec(select(MagicLinkToken)).all() == []
|
||
|
||
|
||
def test_can_target_a_subset(client, edition, session, outbox):
|
||
ana = make_volunteer(session, edition, email="ana@e.fr")
|
||
make_volunteer(session, edition, email="bob@e.fr")
|
||
resp = client.post(
|
||
_url(edition), json={"volunteer_ids": [str(ana.id)]}
|
||
)
|
||
assert resp.json()["queued"] == 1
|
||
assert outbox[0]["To"] == "ana@e.fr"
|
||
|
||
|
||
def test_planning_notification_is_coordinator_only(
|
||
edition, session, make_account, as_account, outbox
|
||
):
|
||
lead = make_account(Role.LEAD, email="lead@e.fr")
|
||
resp = as_account(lead).post(_url(edition), json={})
|
||
assert resp.status_code == 403
|
||
|
||
|
||
def test_shifts_placeholder_is_a_table_by_day_with_the_referent(
|
||
client, edition, session, outbox, make_account
|
||
):
|
||
rita = make_volunteer(session, edition, first_name="Rita", last_name="Ref", email="r@e.fr")
|
||
rita.phone = "06 11 22 33 44"
|
||
session.add(rita)
|
||
lead = make_account(Role.LEAD, volunteer_id=rita.id)
|
||
bar = make_position(session, edition, name="Bar central")
|
||
bar.lead_account_id = lead.id
|
||
session.add(bar)
|
||
parking = make_position(session, edition, name="Parking")
|
||
s1 = make_shift(session, edition, bar, start=dt_at(10, 18), end=dt_at(10, 22))
|
||
s2 = make_shift(session, edition, parking, start=dt_at(11, 9), end=dt_at(11, 12))
|
||
ana = make_volunteer(session, edition, first_name="Ana", email="ana@e.fr")
|
||
session.add_all(
|
||
[
|
||
Assignment(edition_id=edition.id, volunteer_id=ana.id, shift_id=s1.id),
|
||
Assignment(edition_id=edition.id, volunteer_id=ana.id, shift_id=s2.id),
|
||
]
|
||
)
|
||
session.commit()
|
||
|
||
client.post(
|
||
_url(edition), json={"volunteer_ids": [str(ana.id)], "body": "Ton planning :\n\n{creneaux}"}
|
||
)
|
||
(message,) = outbox
|
||
html = message.get_body(preferencelist=("html",)).get_content()
|
||
# Vrai tableau : en-têtes, une ligne de jour, horaire, poste, référent.
|
||
assert "<table" in html and "Référent" in html
|
||
assert "18:00–22:00" in html and "Bar central" in html
|
||
assert "Rita Ref · 06 11 22 33 44" in html
|
||
assert "à désigner" in html # Parking n'a pas de référent
|
||
assert "" not in html
|
||
text = plain_text(message)
|
||
assert "18:00–22:00 · Bar central · référent : Rita Ref · 06 11 22 33 44" in text
|
||
assert "09:00–12:00 · Parking · référent : à désigner" in text
|