Skip to content

Commit 9ced404

Browse files
Validate that a given campaign config doesn't have 2 iterations starting on the same day.
1 parent 25d2f0d commit 9ced404

3 files changed

Lines changed: 30 additions & 1 deletion

File tree

src/eligibility_signposting_api/model/rules.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
from __future__ import annotations
22

33
import typing
4+
from collections import Counter
45
from datetime import UTC, date, datetime
56
from enum import StrEnum
67
from functools import cached_property
78
from operator import attrgetter
89
from typing import Literal, NewType
910

10-
from pydantic import BaseModel, Field, field_serializer, field_validator
11+
from pydantic import BaseModel, Field, field_serializer, field_validator, model_validator
1112

1213
if typing.TYPE_CHECKING: # pragma: no cover
1314
from pydantic import SerializationInfo
@@ -162,6 +163,15 @@ def parse_dates(cls, v: str | date) -> date:
162163
def serialize_dates(v: date, _info: SerializationInfo) -> str:
163164
return v.strftime("%Y%m%d")
164165

166+
@model_validator(mode="after")
167+
def check_no_overlapping_iterations(self) -> typing.Self:
168+
iterations_by_date = Counter([i.iteration_date for i in self.iterations])
169+
if multiple_found := next(((d, c) for d, c in iterations_by_date.most_common() if c > 1), None):
170+
iteration_date, count = multiple_found
171+
message = f"{count} iterations with iteration date {iteration_date} in campaign {self.id}"
172+
raise ValueError(message)
173+
return self
174+
165175
@cached_property
166176
def campaign_live(self) -> bool:
167177
today = datetime.now(tz=UTC).date()

tests/unit/model/__init__.py

Whitespace-only changes.

tests/unit/model/test_rules.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import pytest
2+
from faker import Faker
3+
4+
from tests.fixtures.builders.model.rule import CampaignConfigFactory, IterationFactory
5+
6+
7+
def test_iteration_with_overlapping_start_dates_not_allowed(faker: Faker):
8+
# Given
9+
iteration_date = faker.date("%Y%m%d")
10+
iteration1 = IterationFactory.build(iteration_date=iteration_date)
11+
iteration2 = IterationFactory.build(iteration_date=iteration_date)
12+
13+
# When, Then
14+
with pytest.raises(
15+
ValueError,
16+
match=r"1 validation error for CampaignConfig\n"
17+
r".*2 iterations with iteration date",
18+
):
19+
CampaignConfigFactory.build(iterations=[iteration1, iteration2])

0 commit comments

Comments
 (0)