| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486 |
- from datetime import datetime, timedelta
- from httpx import AsyncClient
- import pytest
- from sqlalchemy import select
- from sqlalchemy.orm import Session
- from app.main import app
- from app.models import (
- Commission,
- CommissionMember,
- Project,
- Slot,
- SlotTemplate,
- Sms,
- User,
- Volunteer,
- OrgRole,
- )
- from app.tests.conftest import default_sms_id, default_volunteer_id
- def get_arthur(session) -> Volunteer:
- result = session.execute(select(Volunteer).where(Volunteer.id == default_volunteer_id))
- arthur = result.scalars().first()
- assert arthur is not None
- return arthur
- @pytest.fixture()
- def user_headers(make_org_user) -> dict:
- _,headers = make_org_user(role=OrgRole.RESPO_BENEVOLE)
- return headers
- @pytest.fixture()
- async def test_create_sms_batch_bad_input(
- client: AsyncClient,
- user_headers: dict,
- session: Session,
- default_public_project: Project,
- ):
- response = await client.post(
- app.url_path_for("create_sms_batch", project_id=default_public_project.id),
- json={"is_public": False},
- )
- assert response.status_code == 401
- async def test_basic(
- client: AsyncClient,
- user_headers: dict,
- session: Session,
- default_public_project: Project,
- ):
- slot = Slot(
- project_id=default_public_project.id,
- title="replanter excalibure",
- starting_time=datetime.now() + timedelta(minutes=30),
- ending_time=datetime.now() + timedelta(minutes=60),
- )
- slot.volunteers.append(get_arthur(session))
- session.add(slot)
- session.commit()
- response = await client.post(
- app.url_path_for("create_sms_batch", project_id=default_public_project.id),
- headers=user_headers,
- json={"template": "Bonjour {prenom}!\n{titre}"},
- )
- assert response.status_code == 200
- result = session.execute(
- select(Sms).where(
- (Sms.project_id == default_public_project.id) & (Sms.id != default_sms_id)
- )
- )
- sms = result.scalars().first()
- assert sms is not None
- assert sms.content == "Bonjour Arthur!\nreplanter excalibure"
- async def test_multiple_volunteer(
- client: AsyncClient,
- user_headers: dict,
- session: Session,
- default_public_project: Project,
- ):
- slot = Slot(
- project_id=default_public_project.id,
- title="replanter excalibure",
- starting_time=datetime.now() + timedelta(minutes=30),
- ending_time=datetime.now() + timedelta(minutes=60),
- )
- slot.volunteers.append(get_arthur(session))
- slot.volunteers.append(
- Volunteer(
- project_id=default_public_project.id,
- name="benevole 2",
- surname="echo",
- phone_number="t",
- email="",
- automatic_sms=True,
- )
- )
- session.add(slot)
- session.commit()
- response = await client.post(
- app.url_path_for("create_sms_batch", project_id=default_public_project.id),
- headers=user_headers,
- json={"template": "Bonjour {prenom}!\n{titre}"},
- )
- assert response.status_code == 200
- result = session.execute(
- select(Sms).where(
- (Sms.project_id == default_public_project.id) & (Sms.id != default_sms_id)
- )
- )
- count_sms = sum(1 for _ in result.fetchall())
- assert count_sms == 2
- @pytest.mark.parametrize("automatic_sms,expected", [(True, True), (False, False)])
- async def test_volunteer_settings(
- client: AsyncClient,
- user_headers: dict,
- session: Session,
- default_public_project: Project,
- automatic_sms: bool,
- expected: bool,
- ):
- slot = Slot(
- project_id=default_public_project.id,
- title="replanter excalibure",
- starting_time=datetime.now() + timedelta(minutes=30),
- ending_time=datetime.now() + timedelta(minutes=60),
- )
- arthur = get_arthur(session)
- arthur.automatic_sms = automatic_sms
- slot.volunteers.append(arthur)
- session.add(slot)
- session.commit()
- response = await client.post(
- app.url_path_for("create_sms_batch", project_id=default_public_project.id),
- headers=user_headers,
- json={"template": "Bonjour {prenom}!\n{titre}"},
- )
- assert response.status_code == 200
- result = session.execute(
- select(Sms).where(
- (Sms.project_id == default_public_project.id) & (Sms.id != default_sms_id)
- )
- )
- sms = result.scalars().first()
- assert (sms is not None) == expected
- async def test_only_future_slot(
- client: AsyncClient,
- user_headers: dict,
- session: Session,
- default_public_project: Project,
- ):
- slot = Slot(
- project_id=default_public_project.id,
- title="replanter excalibure",
- starting_time=datetime.now() + timedelta(minutes=-30),
- ending_time=datetime.now() + timedelta(minutes=60),
- )
- slot.volunteers.append(get_arthur(session))
- session.add(slot)
- session.commit()
- response = await client.post(
- app.url_path_for("create_sms_batch", project_id=default_public_project.id),
- headers=user_headers,
- json={"template": "Bonjour {prenom}!\n{titre}"},
- )
- assert response.status_code == 200
- result = session.execute(
- select(Sms).where(
- (Sms.project_id == default_public_project.id) & (Sms.id != default_sms_id)
- )
- )
- sms = result.scalars().first()
- assert sms is None
- @pytest.mark.parametrize("delta_t,expected", [(32, False), (28, True)])
- async def test_time_delta(
- client: AsyncClient,
- user_headers: dict,
- session: Session,
- default_public_project: Project,
- delta_t: int,
- expected,
- ):
- slot = Slot(
- project_id=default_public_project.id,
- title="replanter excalibure",
- starting_time=datetime.now() + timedelta(minutes=30),
- ending_time=datetime.now() + timedelta(minutes=60),
- )
- slot.volunteers.append(get_arthur(session))
- session.add(slot)
- session.commit()
- response = await client.post(
- app.url_path_for("create_sms_batch", project_id=default_public_project.id),
- headers=user_headers,
- json={"template": "Bonjour {prenom}!\n{titre}", "delta_t": delta_t},
- )
- assert response.status_code == 200
- result = session.execute(
- select(Sms).where(
- (Sms.project_id == default_public_project.id) & (Sms.id != default_sms_id)
- )
- )
- sms = result.scalars().first()
- assert (sms is not None) == expected
- starting_time = datetime.now() + timedelta(minutes=30)
- @pytest.mark.parametrize(
- "template,expected",
- [
- ("no escape", "no escape"),
- ("{titre}", "replanter excalibure"),
- ("{prenom}", "Arthur"),
- ("{prenom} {nom}", "Arthur Pandragon"),
- ("{debut}", starting_time.strftime("%Hh%M")),
- (
- "{titre} {description}, {respo}",
- "replanter excalibure {description}, {respo}",
- ),
- ],
- )
- async def test_content_no_template(
- client: AsyncClient,
- user_headers: dict,
- session: Session,
- default_public_project: Project,
- template: str,
- expected: str,
- ):
- """No SlotTemplate attached at all -- {respo}/{description} placeholders
- are left untouched, since the replace only happens when slot.template
- is not None. Unaffected by the commission/override change."""
- slot = Slot(
- project_id=default_public_project.id,
- title="replanter excalibure",
- starting_time=starting_time,
- ending_time=datetime.now() + timedelta(minutes=60),
- )
- slot.volunteers.append(get_arthur(session))
- session.add(slot)
- session.commit()
- response = await client.post(
- app.url_path_for("create_sms_batch", project_id=default_public_project.id),
- headers=user_headers,
- json={"template": template},
- )
- assert response.status_code == 200
- result = session.execute(
- select(Sms).where(
- (Sms.project_id == default_public_project.id) & (Sms.id != default_sms_id)
- )
- )
- sms = result.scalars().first()
- assert sms is not None
- assert sms.content == expected
- @pytest.mark.parametrize(
- "template,expected",
- [
- ("no escape", "no escape"),
- ("{titre}", "replanter excalibure"),
- ("{prenom}", "Arthur"),
- ("{prenom} {nom}", "Arthur Pandragon"),
- ("{debut}", starting_time.strftime("%Hh%M")),
- ],
- )
- async def test_content_with_template_non_respo_placeholders(
- client: AsyncClient,
- user_headers: dict,
- session: Session,
- default_public_project: Project,
- template: str,
- expected: str,
- ):
- """Placeholders unrelated to {respo} behave the same regardless of how
- the contact is resolved -- kept separate from the {respo}-specific cases
- below since those need commission/override setup."""
- slot_template = SlotTemplate(
- project_id=default_public_project.id,
- title="replanter excalibure",
- description="rendre l'épee à son caillou",
- place="gros caillou",
- )
- slot = Slot(
- project_id=default_public_project.id,
- title="replanter excalibure",
- starting_time=starting_time,
- ending_time=datetime.now() + timedelta(minutes=60),
- )
- slot.template = slot_template
- slot.volunteers.append(get_arthur(session))
- session.add(slot_template)
- session.add(slot)
- session.commit()
- response = await client.post(
- app.url_path_for("create_sms_batch", project_id=default_public_project.id),
- headers=user_headers,
- json={"template": template},
- )
- assert response.status_code == 200
- result = session.execute(
- select(Sms).where(
- (Sms.project_id == default_public_project.id) & (Sms.id != default_sms_id)
- )
- )
- sms = result.scalars().first()
- assert sms is not None
- assert sms.content == expected
- async def test_respo_placeholder_uses_override_when_set(
- client: AsyncClient,
- user_headers: dict,
- session: Session,
- default_public_project: Project,
- ):
- """responsible_override wins even if a commission is also attached."""
- commission = Commission(project_id=default_public_project.id, name="Bar")
- session.add(commission)
- session.commit()
- slot_template = SlotTemplate(
- project_id=default_public_project.id,
- title="replanter excalibure",
- description="rendre l'épee à son caillou",
- place="gros caillou",
- commission_id=commission.id,
- responsible_override="dieu",
- )
- slot = Slot(
- project_id=default_public_project.id,
- title="replanter excalibure",
- starting_time=starting_time,
- ending_time=datetime.now() + timedelta(minutes=60),
- )
- slot.template = slot_template
- slot.volunteers.append(get_arthur(session))
- session.add(slot_template)
- session.add(slot)
- session.commit()
- response = await client.post(
- app.url_path_for("create_sms_batch", project_id=default_public_project.id),
- headers=user_headers,
- json={"template": "{titre} {description}, {respo}"},
- )
- assert response.status_code == 200
- result = session.execute(
- select(Sms).where(
- (Sms.project_id == default_public_project.id) & (Sms.id != default_sms_id)
- )
- )
- sms = result.scalars().first()
- assert sms is not None
- assert sms.content == "replanter excalibure rendre l'épee à son caillou, dieu"
- async def test_respo_placeholder_derives_from_commission_members(
- client: AsyncClient,
- user_headers: dict,
- session: Session,
- default_public_project: Project,
- ):
- """No override set -- {respo} falls back to the commission's members'
- name : phone_number, comma-joined."""
- commission = Commission(project_id=default_public_project.id, name="Bar")
- session.add(commission)
- session.commit()
- member1 = User(
- id="c1a1a1a1-0000-0000-0000-000000000001",
- email="merlin@test.com",
- hashed_password="x",
- name="Merlin",
- phone_number="0601020304",
- )
- member2 = User(
- id="c1a1a1a1-0000-0000-0000-000000000002",
- email="morgane@test.com",
- hashed_password="x",
- name="Morgane",
- phone_number="0605060708",
- )
- session.add_all([member1, member2])
- session.commit()
- session.add_all(
- [
- CommissionMember(commission_id=commission.id, user_id=member1.id),
- CommissionMember(commission_id=commission.id, user_id=member2.id),
- ]
- )
- session.commit()
- slot_template = SlotTemplate(
- project_id=default_public_project.id,
- title="replanter excalibure",
- description="rendre l'épee à son caillou",
- place="gros caillou",
- commission_id=commission.id,
- # responsible_override intentionally left unset
- )
- slot = Slot(
- project_id=default_public_project.id,
- title="replanter excalibure",
- starting_time=starting_time,
- ending_time=datetime.now() + timedelta(minutes=60),
- )
- slot.template = slot_template
- slot.volunteers.append(get_arthur(session))
- session.add(slot_template)
- session.add(slot)
- session.commit()
- response = await client.post(
- app.url_path_for("create_sms_batch", project_id=default_public_project.id),
- headers=user_headers,
- json={"template": "{titre} {description}, {respo}"},
- )
- assert response.status_code == 200
- result = session.execute(
- select(Sms).where(
- (Sms.project_id == default_public_project.id) & (Sms.id != default_sms_id)
- )
- )
- sms = result.scalars().first()
- assert sms is not None
- assert sms.content == (
- "replanter excalibure rendre l'épee à son caillou, "
- "Merlin : 0601020304, Morgane : 0605060708"
- )
- async def test_respo_placeholder_empty_when_no_commission_or_override(
- client: AsyncClient,
- user_headers: dict,
- session: Session,
- default_public_project: Project,
- ):
- """Template attached but no commission and no override -- {respo}
- resolves to an empty string rather than leaving the placeholder literal
- (that untouched-placeholder behavior is only for slot.template is None,
- covered in test_content_no_template)."""
- slot_template = SlotTemplate(
- project_id=default_public_project.id,
- title="replanter excalibure",
- description="rendre l'épee à son caillou",
- place="gros caillou",
- )
- slot = Slot(
- project_id=default_public_project.id,
- title="replanter excalibure",
- starting_time=starting_time,
- ending_time=datetime.now() + timedelta(minutes=60),
- )
- slot.template = slot_template
- slot.volunteers.append(get_arthur(session))
- session.add(slot_template)
- session.add(slot)
- session.commit()
- response = await client.post(
- app.url_path_for("create_sms_batch", project_id=default_public_project.id),
- headers=user_headers,
- json={"template": "{titre}, {respo}"},
- )
- assert response.status_code == 200
- result = session.execute(
- select(Sms).where(
- (Sms.project_id == default_public_project.id) & (Sms.id != default_sms_id)
- )
- )
- sms = result.scalars().first()
- assert sms is not None
- assert sms.content == "replanter excalibure, "
|