Skip to content

Commit

Permalink
Merge pull request #13 from MartinGotelli/timestamp_matcher
Browse files Browse the repository at this point in the history
feat: Add timestamp matcher
  • Loading branch information
MartinGotelli authored Aug 25, 2024
2 parents d63dc98 + 3fe9399 commit 682ae85
Show file tree
Hide file tree
Showing 11 changed files with 133 additions and 3 deletions.
1 change: 1 addition & 0 deletions src/pytest_matchers/pytest_matchers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
is_number,
is_strict_dict,
is_string,
is_timestamp,
is_uuid,
not_empty_string,
one_of,
Expand Down
5 changes: 5 additions & 0 deletions src/pytest_matchers/pytest_matchers/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
SameValue,
StrictDict,
String,
Timestamp,
UUID,
)

Expand Down Expand Up @@ -108,6 +109,10 @@ def is_datetime_string(
return DatetimeString(expected_format, min_value=min_value, max_value=max_value)


def is_timestamp(min_value: float | Any = None, max_value: float | Any = None) -> Timestamp:
return Timestamp(min_value=min_value, max_value=max_value)


def is_iso_8601_date(**kwargs) -> DatetimeString:
return is_datetime_string("%Y-%m-%d", **kwargs)

Expand Down
1 change: 1 addition & 0 deletions src/pytest_matchers/pytest_matchers/matchers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,4 @@
from .json import JSON
from .strict_dict import StrictDict
from .uuid import UUID
from .timestamp import Timestamp
6 changes: 3 additions & 3 deletions src/pytest_matchers/pytest_matchers/matchers/between.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ def _matches_max(self, value: Any) -> bool:
return value < self._max

def _suffix_repr(self) -> str:
if self._min and self._max:
if self._min is not None and self._max is not None:
if self._min_inclusive and self._max_inclusive:
return f"between {self._min} and {self._max}"
if not self._min_inclusive and not self._max_inclusive:
Expand All @@ -70,7 +70,7 @@ def concatenated_repr(self) -> str:
return self._suffix_repr()

def _min_repr(self):
if not self._min:
if self._min is None:
return ""
return (
f"greater or equal than {self._min}"
Expand All @@ -79,7 +79,7 @@ def _min_repr(self):
)

def _max_repr(self):
if not self._max:
if self._max is None:
return ""
return (
f"lower or equal than {self._max}" if self._max_inclusive else f"lower than {self._max}"
Expand Down
34 changes: 34 additions & 0 deletions src/pytest_matchers/pytest_matchers/matchers/timestamp.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
from datetime import datetime
from typing import Any

from pytest_matchers.matchers import Matcher, Number
from pytest_matchers.matchers.between import between_matcher
from pytest_matchers.matchers.matcher_factory import matcher
from pytest_matchers.utils.matcher_utils import matches_or_none
from pytest_matchers.utils.repr_utils import concat_reprs


def _as_timestamp(value: float | datetime) -> float:
if isinstance(value, datetime):
return value.timestamp()
return value


@matcher
class Timestamp(Matcher):
def __init__(self, *, min_value: float | datetime = None, max_value: float | datetime = None):
super().__init__()
self._instance_matcher = Number()
self._limit_matcher = between_matcher(
_as_timestamp(min_value),
_as_timestamp(max_value),
None,
None,
None,
)

def matches(self, value: Any) -> bool:
return self._instance_matcher == value and matches_or_none(self._limit_matcher, value)

def __repr__(self) -> str:
return concat_reprs("To be a timestamp", self._limit_matcher)
8 changes: 8 additions & 0 deletions src/tests/matchers/test_between.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ def test_repr():
assert repr(matcher) == "To be lower than 2"
matcher = Between(1, 1)
assert repr(matcher) == "To be 1"
matcher = Between(0, None)
assert repr(matcher) == "To be greater or equal than 0"
matcher = Between(None, 0)
assert repr(matcher) == "To be lower or equal than 0"


def test_concatenated_repr():
Expand All @@ -59,6 +63,10 @@ def test_concatenated_repr():
assert matcher.concatenated_repr() == "lower than 2"
matcher = Between(1, 1)
assert matcher.concatenated_repr() == "equal to 1"
matcher = Between(0, None)
assert matcher.concatenated_repr() == "greater or equal than 0"
matcher = Between(None, 0)
assert matcher.concatenated_repr() == "lower or equal than 0"


def test_matches_float_inclusive():
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ def test_matches_number():
matcher = Number()
assert matcher == 20
assert matcher == 20.0
assert matcher == "20"
assert matcher != [] # pylint: disable=use-implicit-booleaness-not-comparison
assert matcher != None # pylint: disable=singleton-comparison
assert matcher != "string"
assert matcher != ["string"]

Expand All @@ -50,6 +53,7 @@ def test_matches_type():
matcher = Number(int)
assert matcher == 20
assert matcher != 20.0
assert matcher != "20"
assert matcher != "string"
assert matcher != ["string"]

Expand Down
File renamed without changes.
58 changes: 58 additions & 0 deletions src/tests/matchers/test_timestamp.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
from datetime import datetime, timedelta

from pytest_matchers.matchers import Timestamp


def test_create():
matcher = Timestamp()
assert isinstance(matcher, Timestamp)
matcher = Timestamp(min_value=0, max_value=1)
assert isinstance(matcher, Timestamp)
matcher = Timestamp(min_value=0)
assert isinstance(matcher, Timestamp)


def test_repr():
matcher = Timestamp()
assert repr(matcher) == "To be a timestamp"
matcher = Timestamp(min_value=0)
assert repr(matcher) == "To be a timestamp greater or equal than 0"
matcher = Timestamp(max_value=1)
assert repr(matcher) == "To be a timestamp lower or equal than 1"
matcher = Timestamp(min_value=0, max_value=1)
assert repr(matcher) == "To be a timestamp between 0 and 1"


def test_matches():
matcher = Timestamp()
assert matcher == 0.0
assert matcher == 1.0
assert matcher != "string"
assert matcher == 20
assert matcher != ["string"]
assert matcher != datetime.now()
assert matcher == datetime.now().timestamp()


def test_matches_with_limit():
now_timestamp = datetime.now().timestamp()
tomorrow_timestamp = datetime.now().timestamp() + 86400
matcher = Timestamp(min_value=now_timestamp, max_value=tomorrow_timestamp)
assert matcher == now_timestamp
assert matcher == now_timestamp + 2000
assert matcher == tomorrow_timestamp
assert matcher == int(now_timestamp + 2000)
assert matcher != now_timestamp - 86400
assert matcher != tomorrow_timestamp + 86400


def test_matches_with_datetime_limit():
now = datetime.now()
tomorrow = now + timedelta(days=1)
matcher = Timestamp(min_value=now, max_value=tomorrow)
assert matcher == now.timestamp()
assert matcher == now.timestamp() + 2000
assert matcher == tomorrow.timestamp()
assert matcher == int(now.timestamp() + 2000)
assert matcher != now.timestamp() - 86400
assert matcher != tomorrow.timestamp() + 86400
19 changes: 19 additions & 0 deletions src/tests/test_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
is_number,
is_strict_dict,
is_string,
is_timestamp,
is_uuid,
not_empty_string,
one_of,
Expand Down Expand Up @@ -211,6 +212,24 @@ def test_is_datetime():
)


def test_is_timestamp():
date = datetime(2021, 1, 1)
assert 1612137600 == is_timestamp()
assert -1612137600 == is_timestamp()
assert date.timestamp() == is_timestamp(min_value=datetime(2021, 1, 1))
assert date.timestamp() == is_timestamp(max_value=datetime(2021, 1, 1))
assert date.timestamp() == is_timestamp(
min_value=datetime(2021, 1, 1),
max_value=datetime(2021, 1, 2),
)
assert date.timestamp() != is_timestamp(
min_value=datetime(2021, 1, 2),
max_value=datetime(2021, 1, 3),
)
assert date != is_timestamp()
assert "string" != is_timestamp()


def test_is_datetime_string():
assert "2021-01-01" != is_datetime_string("erroneous_format")
assert "2021-01-01" == is_datetime_string("%Y-%m-%d")
Expand Down

0 comments on commit 682ae85

Please sign in to comment.