diff --git a/app/crud.py b/app/crud.py index 09d182d..0410986 100644 --- a/app/crud.py +++ b/app/crud.py @@ -368,6 +368,7 @@ def create_ballot(db: Session, ballot: schemas.BallotCreate) -> schemas.BallotGe db_election = _check_public_election(db, ballot.election_ref) election = schemas.ElectionGet.model_validate(db_election) + _check_election_is_started(db_election) _check_election_is_not_ended(db_election) _check_items_in_election( @@ -407,6 +408,14 @@ def _check_public_election(db: Session, election_ref: str): ) return db_election +def _check_election_is_started(election: models.Election): + """ + Check that the election is started. + If it is not, raise an error. + """ + if election.date_start is not None and election.date_start > datetime.now(): + raise errors.ForbiddenError("The election has not started yet. You can not create votes") + def _check_election_is_not_ended(election: models.Election): """ Check that the election is not ended. @@ -454,6 +463,7 @@ def update_ballot( if db_election is None: raise errors.NotFoundError("elections") + _check_election_is_started(db_election) _check_election_is_not_ended(db_election) if len(ballot.votes) != len(vote_ids): diff --git a/app/schemas.py b/app/schemas.py index 1adc86c..6db2498 100644 --- a/app/schemas.py +++ b/app/schemas.py @@ -115,7 +115,7 @@ class ElectionBase(BaseModel): name: Name description: Description = "" ref: Ref = "" - date_start: datetime | int | str = Field(default_factory=_utc_now) + date_start: datetime | int | str | None = Field(default_factory=_utc_now) date_end: datetime | int | str | None = Field(default_factory=_in_a_long_time) hide_results: bool = True restricted: bool = False diff --git a/app/tests/test_api.py b/app/tests/test_api.py index 69c4835..9330201 100644 --- a/app/tests/test_api.py +++ b/app/tests/test_api.py @@ -59,6 +59,7 @@ class RandomElection(t.TypedDict): hide_results: bool num_voters: int date_end: t.Optional[str] + date_start: t.Optional[str] auth_for_result: bool @@ -72,6 +73,7 @@ def _random_election(num_candidates: int, num_grades: int) -> RandomElection: candidates = [{"name": _random_string(10)} for i in range(num_candidates)] name = _random_string(10) return { + "date_start":None, "candidates": candidates, "grades": grades, "name": name, @@ -396,6 +398,56 @@ def test_cannot_update_vote_on_ended_election(): assert response.status_code == 403, response.json() +## TODO: cannot change start_date if a people vote; +## +def test_cannot_create_vote_on_unstarted_election(): + """ + On an unstarted election, we are not allowed to create new votes + """ + # Create a random election + body = _random_election(10, 5) + body["date_start"] = (datetime.now() + timedelta(days=1)).isoformat() + body["date_end"] = (datetime.now() + timedelta(days=2)).isoformat() + response = client.post("/elections", json=body) + data = response.json() + assert response.status_code == 200, data + election_ref = data["ref"] + + # We create votes using the ID + votes = _generate_votes_from_response("id", data) + response = client.post( + f"/ballots", + json={"votes": votes, "election_ref": election_ref}, + ) + data = response.json() + assert response.status_code == 403, data + +def test_cannot_update_vote_on_unstarted_election(): + """ + On an unstarted election, we are not allowed to create new votes + """ + # Create a random election + body = _random_election(10, 5) + body["restricted"] = True + body["num_voters"] = 1 + body["date_start"] = (datetime.now() + timedelta(days=1)).isoformat() + body["date_end"] = (datetime.now() + timedelta(days=2)).isoformat() + response = client.post("/elections", json=body) + data = response.json() + assert response.status_code == 200, data + election_ref = data["ref"] + tokens = data["invites"] + assert len(tokens) == 1 + + # We create votes using the ID + votes = _generate_votes_from_response("id", data) + response = client.put( + f"/ballots", + json={"votes": votes, "election_ref": election_ref}, + headers={"Authorization": f"Bearer {tokens[0]}"}, + ) + data = response.json() + assert response.status_code == 403, data def test_cannot_create_vote_on_restricted_election(): """