aboutsummaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/managers/thl/test_survey.py33
-rw-r--r--tests/models/thl/test_survey.py140
2 files changed, 172 insertions, 1 deletions
diff --git a/tests/managers/thl/test_survey.py b/tests/managers/thl/test_survey.py
index e114b70..928fe6c 100644
--- a/tests/managers/thl/test_survey.py
+++ b/tests/managers/thl/test_survey.py
@@ -2,7 +2,7 @@ from __future__ import annotations
import uuid
from collections.abc import Callable
-from datetime import UTC, datetime
+from datetime import UTC, datetime, time
from decimal import Decimal
from typing import TYPE_CHECKING
@@ -23,6 +23,7 @@ from generalresearch.models.thl.survey.model import (
SurveyCategoryModel,
SurveyEligibilityDefinition,
SurveyStat,
+ TimeInterval,
)
if TYPE_CHECKING:
@@ -112,6 +113,36 @@ class TestSurvey:
assert res2[0] == res[0]
assert len(res2) == len(surveys2)
+ def test_active_hours_round_trip(
+ self,
+ delete_buyers_surveys: Callable[..., None],
+ survey_manager: SurveyManager,
+ ):
+ delete_buyers_surveys()
+ active_hours = [
+ TimeInterval(start=time(5), end=time(1), timezone=None),
+ TimeInterval(
+ start=time(8),
+ end=time(17),
+ timezone="America/New_York",
+ ),
+ ]
+ survey = Survey(
+ source=Source.TESTING,
+ survey_id="active-hours",
+ buyer_code="active-hours-buyer",
+ active_hours=active_hours,
+ )
+
+ survey_manager.create_or_update([survey])
+ result = survey_manager.filter_by_natural_key(
+ source=survey.source,
+ survey_ids=[survey.survey_id],
+ )
+
+ assert len(result) == 1
+ assert result[0].active_hours == active_hours
+
def test_category(self, survey_manager: SurveyManager):
survey1 = Survey(id=562289, survey_id="a", source=Source.TESTING)
survey2 = Survey(id=562290, survey_id="a", source=Source.TESTING)
diff --git a/tests/models/thl/test_survey.py b/tests/models/thl/test_survey.py
new file mode 100644
index 0000000..b283e50
--- /dev/null
+++ b/tests/models/thl/test_survey.py
@@ -0,0 +1,140 @@
+from datetime import UTC, datetime, time
+from unittest.mock import patch
+from zoneinfo import ZoneInfo
+
+import pytest
+
+from generalresearch.models.definitions import Source
+from generalresearch.models.thl.survey.model import Survey, TimeInterval
+
+
+@pytest.mark.parametrize(
+ ("hour", "minute", "expected"),
+ [
+ (6, 59, False),
+ (7, 0, True),
+ (12, 0, True),
+ (22, 0, True),
+ (22, 1, False),
+ ],
+)
+def test_is_currently_active_in_worker_local_time(hour, minute, expected):
+ interval = TimeInterval(start=time(7), end=time(22), timezone=None)
+ worker_time = datetime(
+ 2026, 1, 15, hour, minute, tzinfo=ZoneInfo("America/Chicago")
+ )
+
+ assert interval.is_currently_active(worker_time) is expected
+
+
+@pytest.mark.parametrize(
+ ("hour", "expected"),
+ [
+ (21, False),
+ (22, True),
+ (23, True),
+ (0, True),
+ (6, True),
+ (7, False),
+ ],
+)
+def test_is_currently_active_for_interval_crossing_midnight(hour, expected):
+ interval = TimeInterval(start=time(22), end=time(6), timezone=None)
+ worker_time = datetime(2026, 1, 15, hour, tzinfo=UTC)
+
+ assert interval.is_currently_active(worker_time) is expected
+
+
+def test_is_currently_active_converts_to_interval_timezone():
+ interval = TimeInterval(
+ start=time(7),
+ end=time(9),
+ timezone="America/New_York",
+ )
+ # 08:00 in New York on this date.
+ worker_time = datetime(
+ 2026, 1, 15, 7, tzinfo=ZoneInfo("America/Chicago")
+ )
+
+ assert interval.is_currently_active(worker_time) is True
+
+
+def test_is_currently_active_requires_aware_worker_time():
+ interval = TimeInterval(start=time(7), end=time(22), timezone=None)
+
+ with pytest.raises(AssertionError):
+ interval.is_currently_active(datetime(2026, 1, 15, 12))
+
+
+def test_is_currently_active_requires_worker_timezone_for_local_interval():
+ interval = TimeInterval(start=time(7), end=time(22), timezone=None)
+
+ assert interval.is_currently_active_by_worker_tz(worker_tz=None) is False
+
+
+@pytest.mark.parametrize(
+ ("interval_timezone", "worker_timezone"),
+ [
+ (None, ZoneInfo("America/Chicago")),
+ ("America/New_York", None),
+ ],
+)
+def test_is_currently_active_supports_interval_crossing_midnight(
+ interval_timezone, worker_timezone
+):
+ interval = TimeInterval(
+ start=time(22),
+ end=time(6),
+ timezone=interval_timezone,
+ )
+ current_timezone = (
+ ZoneInfo(interval_timezone) if interval_timezone else worker_timezone
+ )
+ current_time = datetime(2026, 1, 15, 23, tzinfo=current_timezone)
+
+ with patch("generalresearch.models.thl.survey.model.datetime") as mock_datetime:
+ mock_datetime.now.return_value = current_time
+
+ assert interval.is_currently_active_by_worker_tz(worker_timezone) is True
+ mock_datetime.now.assert_called_once_with(tz=current_timezone)
+
+
+@pytest.mark.parametrize(
+ ("off_start", "off_end", "active_start", "active_end"),
+ [
+ (time(1), time(5), time(5), time(1)),
+ (time(22), time(6), time(6), time(22)),
+ ],
+)
+def test_from_off_hours_returns_complementary_active_hours(
+ off_start, off_end, active_start, active_end
+):
+ off_hours = TimeInterval(
+ start=off_start,
+ end=off_end,
+ timezone="America/New_York",
+ )
+
+ active_hours = Survey.active_hours_from_off_hours(off_hours)
+
+ assert active_hours == [
+ TimeInterval(
+ start=active_start,
+ end=active_end,
+ timezone="America/New_York",
+ )
+ ]
+
+
+def test_from_off_hours_result_can_be_assigned_to_survey():
+ off_hours = TimeInterval(start=time(1), end=time(5), timezone=None)
+
+ survey = Survey(
+ source=Source.TESTING,
+ survey_id="off-hours-test",
+ active_hours=Survey.active_hours_from_off_hours(off_hours),
+ )
+
+ assert survey.active_hours == [
+ TimeInterval(start=time(5), end=time(1), timezone=None)
+ ]