Skip to content

Commit

Permalink
chore: Add tests for relative date parsing (#19511)
Browse files Browse the repository at this point in the history
  • Loading branch information
neilkakkar authored Jan 1, 2024
1 parent 72064c8 commit 1154121
Show file tree
Hide file tree
Showing 4 changed files with 159 additions and 6 deletions.
2 changes: 1 addition & 1 deletion posthog/models/filters/test/__snapshots__/test_filter.ambr
Original file line number Diff line number Diff line change
Expand Up @@ -353,7 +353,7 @@
INNER JOIN "posthog_persondistinctid" ON ("posthog_person"."id" = "posthog_persondistinctid"."person_id")
WHERE ("posthog_persondistinctid"."distinct_id" = 'example_id'
AND "posthog_person"."team_id" = 2
AND ("posthog_person"."properties" -> 'created_at') > '"2021-02-06T10:00:00+00:00"')
AND "posthog_person"."id" = -1)
LIMIT 1
'
---
Expand Down
5 changes: 2 additions & 3 deletions posthog/models/filters/test/test_filter.py
Original file line number Diff line number Diff line change
Expand Up @@ -854,9 +854,8 @@ def test_person_relative_date_parsing_with_invalid_date(self):
.filter(properties_to_Q(filter.property_groups.flat))
.exists()
)
# matches '2m'
# TODO: Should this not match instead?
self.assertTrue(matched_person)
# needs an exact match
self.assertFalse(matched_person)

filter = Filter(
data={
Expand Down
7 changes: 6 additions & 1 deletion posthog/queries/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -435,11 +435,16 @@ def is_truthy_or_falsy_property_value(value: Any) -> bool:


def relative_date_parse_for_feature_flag_matching(value: str) -> Optional[datetime.datetime]:
regex = r"(?P<number>[0-9]+)(?P<interval>[a-z])"
regex = r"^(?P<number>[0-9]+)(?P<interval>[a-z])$"
match = re.search(regex, value)
parsed_dt = datetime.datetime.now(tz=ZoneInfo("UTC"))
if match:
number = int(match.group("number"))

if number >= 10_000:
# Guard against overflow, disallow numbers greater than 10_000
return None

interval = match.group("interval")
if interval == "h":
parsed_dt = parsed_dt - relativedelta(hours=number)
Expand Down
151 changes: 150 additions & 1 deletion posthog/queries/test/test_base.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import datetime
import re
import unittest
from unittest.mock import patch

from dateutil import parser, tz
Expand All @@ -10,7 +11,7 @@

from posthog.models.filters.path_filter import PathFilter
from posthog.models.property.property import Property
from posthog.queries.base import match_property, sanitize_property_key
from posthog.queries.base import match_property, relative_date_parse_for_feature_flag_matching, sanitize_property_key
from posthog.test.base import APIBaseTest


Expand Down Expand Up @@ -406,3 +407,151 @@ def test_sanitize_keys(key, expected):
sanitized_key = sanitize_property_key(key)

assert sanitized_key == expected


class TestRelativeDateParsing(unittest.TestCase):
def test_invalid_input(self):
with freeze_time("2020-01-01T12:01:20.1340Z"):
assert relative_date_parse_for_feature_flag_matching("1") is None
assert relative_date_parse_for_feature_flag_matching("1x") is None
assert relative_date_parse_for_feature_flag_matching("1.2y") is None
assert relative_date_parse_for_feature_flag_matching("1z") is None
assert relative_date_parse_for_feature_flag_matching("1s") is None
assert relative_date_parse_for_feature_flag_matching("123344000.134m") is None
assert relative_date_parse_for_feature_flag_matching("bazinga") is None
assert relative_date_parse_for_feature_flag_matching("000bello") is None
assert relative_date_parse_for_feature_flag_matching("000hello") is None

assert relative_date_parse_for_feature_flag_matching("000h") is not None
assert relative_date_parse_for_feature_flag_matching("1000h") is not None

def test_overflow(self):
assert relative_date_parse_for_feature_flag_matching("1000000h") is None
assert relative_date_parse_for_feature_flag_matching("100000000000000000y") is None

def test_hour_parsing(self):
with freeze_time("2020-01-01T12:01:20.1340Z"):
assert relative_date_parse_for_feature_flag_matching("1h") == datetime.datetime(
2020, 1, 1, 11, 1, 20, 134000, tzinfo=tz.gettz("UTC")
)
assert relative_date_parse_for_feature_flag_matching("2h") == datetime.datetime(
2020, 1, 1, 10, 1, 20, 134000, tzinfo=tz.gettz("UTC")
)
assert relative_date_parse_for_feature_flag_matching("24h") == datetime.datetime(
2019, 12, 31, 12, 1, 20, 134000, tzinfo=tz.gettz("UTC")
)
assert relative_date_parse_for_feature_flag_matching("30h") == datetime.datetime(
2019, 12, 31, 6, 1, 20, 134000, tzinfo=tz.gettz("UTC")
)
assert relative_date_parse_for_feature_flag_matching("48h") == datetime.datetime(
2019, 12, 30, 12, 1, 20, 134000, tzinfo=tz.gettz("UTC")
)

assert relative_date_parse_for_feature_flag_matching(
"24h"
) == relative_date_parse_for_feature_flag_matching("1d")
assert relative_date_parse_for_feature_flag_matching(
"48h"
) == relative_date_parse_for_feature_flag_matching("2d")

def test_day_parsing(self):
with freeze_time("2020-01-01T12:01:20.1340Z"):
assert relative_date_parse_for_feature_flag_matching("1d") == datetime.datetime(
2019, 12, 31, 12, 1, 20, 134000, tzinfo=tz.gettz("UTC")
)
assert relative_date_parse_for_feature_flag_matching("2d") == datetime.datetime(
2019, 12, 30, 12, 1, 20, 134000, tzinfo=tz.gettz("UTC")
)
assert relative_date_parse_for_feature_flag_matching("7d") == datetime.datetime(
2019, 12, 25, 12, 1, 20, 134000, tzinfo=tz.gettz("UTC")
)
assert relative_date_parse_for_feature_flag_matching("14d") == datetime.datetime(
2019, 12, 18, 12, 1, 20, 134000, tzinfo=tz.gettz("UTC")
)
assert relative_date_parse_for_feature_flag_matching("30d") == datetime.datetime(
2019, 12, 2, 12, 1, 20, 134000, tzinfo=tz.gettz("UTC")
)

assert relative_date_parse_for_feature_flag_matching("7d") == relative_date_parse_for_feature_flag_matching(
"1w"
)

def test_week_parsing(self):
with freeze_time("2020-01-01T12:01:20.1340Z"):
assert relative_date_parse_for_feature_flag_matching("1w") == datetime.datetime(
2019, 12, 25, 12, 1, 20, 134000, tzinfo=tz.gettz("UTC")
)
assert relative_date_parse_for_feature_flag_matching("2w") == datetime.datetime(
2019, 12, 18, 12, 1, 20, 134000, tzinfo=tz.gettz("UTC")
)
assert relative_date_parse_for_feature_flag_matching("4w") == datetime.datetime(
2019, 12, 4, 12, 1, 20, 134000, tzinfo=tz.gettz("UTC")
)
assert relative_date_parse_for_feature_flag_matching("8w") == datetime.datetime(
2019, 11, 6, 12, 1, 20, 134000, tzinfo=tz.gettz("UTC")
)

assert relative_date_parse_for_feature_flag_matching("1m") == datetime.datetime(
2019, 12, 1, 12, 1, 20, 134000, tzinfo=tz.gettz("UTC")
)
assert relative_date_parse_for_feature_flag_matching("4w") != relative_date_parse_for_feature_flag_matching(
"1m"
)

def test_month_parsing(self):
with freeze_time("2020-01-01T12:01:20.1340Z"):
assert relative_date_parse_for_feature_flag_matching("1m") == datetime.datetime(
2019, 12, 1, 12, 1, 20, 134000, tzinfo=tz.gettz("UTC")
)
assert relative_date_parse_for_feature_flag_matching("2m") == datetime.datetime(
2019, 11, 1, 12, 1, 20, 134000, tzinfo=tz.gettz("UTC")
)
assert relative_date_parse_for_feature_flag_matching("4m") == datetime.datetime(
2019, 9, 1, 12, 1, 20, 134000, tzinfo=tz.gettz("UTC")
)
assert relative_date_parse_for_feature_flag_matching("8m") == datetime.datetime(
2019, 5, 1, 12, 1, 20, 134000, tzinfo=tz.gettz("UTC")
)

assert relative_date_parse_for_feature_flag_matching("1y") == datetime.datetime(
2019, 1, 1, 12, 1, 20, 134000, tzinfo=tz.gettz("UTC")
)
assert relative_date_parse_for_feature_flag_matching(
"12m"
) == relative_date_parse_for_feature_flag_matching("1y")

with freeze_time("2020-04-03T00:00:00"):
assert relative_date_parse_for_feature_flag_matching("1m") == datetime.datetime(
2020, 3, 3, 0, 0, 0, tzinfo=tz.gettz("UTC")
)
assert relative_date_parse_for_feature_flag_matching("2m") == datetime.datetime(
2020, 2, 3, 0, 0, 0, tzinfo=tz.gettz("UTC")
)
assert relative_date_parse_for_feature_flag_matching("4m") == datetime.datetime(
2019, 12, 3, 0, 0, 0, tzinfo=tz.gettz("UTC")
)
assert relative_date_parse_for_feature_flag_matching("8m") == datetime.datetime(
2019, 8, 3, 0, 0, 0, tzinfo=tz.gettz("UTC")
)

assert relative_date_parse_for_feature_flag_matching("1y") == datetime.datetime(
2019, 4, 3, 0, 0, 0, tzinfo=tz.gettz("UTC")
)
assert relative_date_parse_for_feature_flag_matching(
"12m"
) == relative_date_parse_for_feature_flag_matching("1y")

def test_year_parsing(self):
with freeze_time("2020-01-01T12:01:20.1340Z"):
assert relative_date_parse_for_feature_flag_matching("1y") == datetime.datetime(
2019, 1, 1, 12, 1, 20, 134000, tzinfo=tz.gettz("UTC")
)
assert relative_date_parse_for_feature_flag_matching("2y") == datetime.datetime(
2018, 1, 1, 12, 1, 20, 134000, tzinfo=tz.gettz("UTC")
)
assert relative_date_parse_for_feature_flag_matching("4y") == datetime.datetime(
2016, 1, 1, 12, 1, 20, 134000, tzinfo=tz.gettz("UTC")
)
assert relative_date_parse_for_feature_flag_matching("8y") == datetime.datetime(
2012, 1, 1, 12, 1, 20, 134000, tzinfo=tz.gettz("UTC")
)

0 comments on commit 1154121

Please sign in to comment.