Replaced hand-rolled time freezing in tests with freezegun.
Format / ruff (push) Successful in 10s
Lint / ruff (push) Successful in 11s
Unit Tests / pytest (push) Successful in 16s
Type Check / mypy (push) Successful in 18s

This commit is contained in:
2026-02-16 13:33:06 -05:00
parent e3ea05fb07
commit 141d0db1c6
3 changed files with 49 additions and 39 deletions
+1
View File
@@ -25,6 +25,7 @@ Repository = "https://git.logal.dev/LogalDeveloper/Owlbot"
dev = [
"pytest>=9.0.2",
"pytest-asyncio>=1.3.0",
"freezegun>=1.5.5",
"mypy>=1.18.1",
"types-PyYAML>=6.0.12.20250915",
"ruff>=0.15.1",
+13 -39
View File
@@ -19,46 +19,16 @@ templates and user scenarios.
"""
import random
from contextlib import contextmanager
from datetime import UTC, datetime
from typing import ClassVar
from unittest.mock import patch
import aiosqlite
import pytest
from freezegun import freeze_time
import owlbot.builtin_modules.custom_commands.placeholder_handlers as _ph
from owlbot.builtin_modules.custom_commands.placeholder_handlers import _format_duration
from owlbot.builtin_modules.custom_commands.placeholders import process_placeholders
_real_datetime = datetime
class _FrozenDatetime(datetime):
"""datetime subclass whose now() returns a fixed instant."""
_frozen: datetime
@classmethod
def now(cls, tz=None):
return cls._frozen
@contextmanager
def freeze_time(year, month, day, hour=0, minute=0, second=0):
"""Temporarily replace datetime in placeholder_handlers with a frozen clock."""
_FrozenDatetime._frozen = _real_datetime(
year,
month,
day,
hour,
minute,
second,
tzinfo=UTC,
)
with patch.object(_ph, "datetime", _FrozenDatetime):
yield _FrozenDatetime._frozen
class MockStorage:
"""In-memory SQLite storage that executes real SQL queries."""
@@ -583,17 +553,19 @@ class TestCountdownCountup:
async def test_countdown_future_date(self):
"""$(countdown) with a future date returns the formatted duration."""
with freeze_time(2026, 6, 15, 12, 0, 0) as now:
with freeze_time("2026-06-15 12:00:00", tz_offset=0):
now = datetime.now(UTC)
# Dec 25 2099 00:00:00 EST = Dec 25 2099 05:00:00 UTC
target = _real_datetime(2099, 12, 25, 5, 0, 0, tzinfo=UTC)
target = datetime(2099, 12, 25, 5, 0, 0, tzinfo=UTC)
expected = _format_duration(int((target - now).total_seconds()))
result = await process("$(countdown Dec 25 2099 12:00:00 AM EST)")
assert result == expected
async def test_countup_past_date(self):
"""$(countup) with a past date returns the formatted elapsed duration."""
with freeze_time(2026, 6, 15, 12, 0, 0) as now:
target = _real_datetime(2020, 1, 1, 0, 0, 0, tzinfo=UTC)
with freeze_time("2026-06-15 12:00:00", tz_offset=0):
now = datetime.now(UTC)
target = datetime(2020, 1, 1, 0, 0, 0, tzinfo=UTC)
expected = _format_duration(int((now - target).total_seconds()))
result = await process("$(countup Jan 1 2020 12:00:00 AM UTC)")
assert result == expected
@@ -642,9 +614,10 @@ class TestCountdownCountup:
async def test_fractional_timezone_offset(self):
"""A fractional timezone offset like IST (UTC+5:30) is applied correctly."""
with freeze_time(2026, 6, 15, 12, 0, 0) as now:
with freeze_time("2026-06-15 12:00:00", tz_offset=0):
now = datetime.now(UTC)
# Jan 1 2020 12:00:00 AM IST = Dec 31 2019 18:30:00 UTC
target = _real_datetime(2019, 12, 31, 18, 30, 0, tzinfo=UTC)
target = datetime(2019, 12, 31, 18, 30, 0, tzinfo=UTC)
expected = _format_duration(int((now - target).total_seconds()))
result = await process("$(countup Jan 1 2020 12:00:00 AM IST)")
assert result == expected
@@ -905,8 +878,9 @@ class TestCaseInsensitivity:
async def test_countup_case_insensitive(self):
"""$(COUNTUP) resolves the same as $(countup)."""
with freeze_time(2026, 6, 15, 12, 0, 0) as now:
target = _real_datetime(2020, 1, 1, 0, 0, 0, tzinfo=UTC)
with freeze_time("2026-06-15 12:00:00", tz_offset=0):
now = datetime.now(UTC)
target = datetime(2020, 1, 1, 0, 0, 0, tzinfo=UTC)
expected = _format_duration(int((now - target).total_seconds()))
result = await process("$(COUNTUP Jan 1 2020 12:00:00 AM UTC)")
assert result == expected
Generated
+35
View File
@@ -109,6 +109,18 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/fb/1a/02f105147f7f2e06ed4f734ff5a6439590bb275a53dd91fc73df6312298a/cronsim-2.7-py3-none-any.whl", hash = "sha256:1e1431fa08c51dc7f72e67e571c7c7a09af26420169b607badd4ca9677ffad1e", size = 14213, upload-time = "2025-10-21T16:38:20.431Z" },
]
[[package]]
name = "freezegun"
version = "1.5.5"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "python-dateutil" },
]
sdist = { url = "https://files.pythonhosted.org/packages/95/dd/23e2f4e357f8fd3bdff613c1fe4466d21bfb00a6177f238079b17f7b1c84/freezegun-1.5.5.tar.gz", hash = "sha256:ac7742a6cc6c25a2c35e9292dfd554b897b517d2dec26891a2e8debf205cb94a", size = 35914, upload-time = "2025-08-09T10:39:08.338Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/5e/2e/b41d8a1a917d6581fc27a35d05561037b048e47df50f27f8ac9c7e27a710/freezegun-1.5.5-py3-none-any.whl", hash = "sha256:cd557f4a75cf074e84bc374249b9dd491eaeacd61376b9eb3c423282211619d2", size = 19266, upload-time = "2025-08-09T10:39:06.636Z" },
]
[[package]]
name = "frozenlist"
version = "1.8.0"
@@ -332,6 +344,7 @@ dependencies = [
[package.dev-dependencies]
dev = [
{ name = "freezegun" },
{ name = "mypy" },
{ name = "pytest" },
{ name = "pytest-asyncio" },
@@ -350,6 +363,7 @@ requires-dist = [
[package.metadata.requires-dev]
dev = [
{ name = "freezegun", specifier = ">=1.5.5" },
{ name = "mypy", specifier = ">=1.18.1" },
{ name = "pytest", specifier = ">=9.0.2" },
{ name = "pytest-asyncio", specifier = ">=1.3.0" },
@@ -460,6 +474,18 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/e5/35/f8b19922b6a25bc0880171a2f1a003eaeb93657475193ab516fd87cac9da/pytest_asyncio-1.3.0-py3-none-any.whl", hash = "sha256:611e26147c7f77640e6d0a92a38ed17c3e9848063698d5c93d5aa7aa11cebff5", size = 15075, upload-time = "2025-11-10T16:07:45.537Z" },
]
[[package]]
name = "python-dateutil"
version = "2.9.0.post0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "six" },
]
sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432, upload-time = "2024-03-01T18:36:20.211Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" },
]
[[package]]
name = "pyyaml"
version = "6.0.3"
@@ -511,6 +537,15 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/2a/07/5bda6a85b220c64c65686bc85bd0bbb23b29c62b3a9f9433fa55f17cda93/ruff-0.15.1-py3-none-win_arm64.whl", hash = "sha256:5ff7d5f0f88567850f45081fac8f4ec212be8d0b963e385c3f7d0d2eb4899416", size = 10874604, upload-time = "2026-02-12T23:09:05.515Z" },
]
[[package]]
name = "six"
version = "1.17.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" },
]
[[package]]
name = "types-pyyaml"
version = "6.0.12.20250915"