Skip to content

Commit 3870d73

Browse files
committed
✅ add unit tests for snowflake generation and time conversion
1 parent 414e0fe commit 3870d73

File tree

2 files changed

+67
-3
lines changed

2 files changed

+67
-3
lines changed

discord/utils/__init__.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,11 +52,9 @@
5252
raw_role_mentions,
5353
remove_markdown,
5454
escape_markdown,
55+
DISCORD_EPOCH,
5556
)
5657

57-
DISCORD_EPOCH = 1420070400000
58-
59-
6058
__all__ = (
6159
"oauth_url",
6260
"snowflake_time",
@@ -74,6 +72,7 @@
7472
"basic_autocomplete",
7573
"Undefined",
7674
"MISSING",
75+
"DISCORD_EPOCH",
7776
)
7877

7978

tests/test_snowflake_datetime.py

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
# tests/test_snowflake_utils.py
2+
3+
import datetime
4+
import pytest
5+
6+
from discord.utils import generate_snowflake, snowflake_time, DISCORD_EPOCH
7+
8+
UTC = datetime.timezone.utc
9+
10+
DATETIME_CASES = [
11+
(datetime.datetime(1970, 1, 1, 0, 0, 0, tzinfo=UTC), int(0 * 1000 - DISCORD_EPOCH)),
12+
(datetime.datetime(2000, 2, 29, 12, 0, 0, tzinfo=UTC), int(951825600 * 1000 - DISCORD_EPOCH)),
13+
(datetime.datetime(1999, 12, 31, 23, 59, 59, tzinfo=UTC), int(946684799 * 1000 - DISCORD_EPOCH)),
14+
(datetime.datetime(2023, 1, 2, 3, 4, 5, tzinfo=UTC), int(1672628645 * 1000 - DISCORD_EPOCH)),
15+
(datetime.datetime(2050, 6, 15, 7, 30, 0, tzinfo=UTC), int(2538891000 * 1000 - DISCORD_EPOCH)),
16+
]
17+
18+
19+
@pytest.mark.parametrize(("dt", "expected_ms"), DATETIME_CASES)
20+
def test_generate_snowflake_realistic(dt, expected_ms):
21+
"""Realistic mode should set lower 22 bits to 0x3FFFFF."""
22+
sf = generate_snowflake(dt, mode="realistic")
23+
# top bits are the timestamp
24+
assert (sf >> 22) == expected_ms
25+
# lower 22 bits are all ones in realistic mode
26+
assert (sf & ((1 << 22) - 1)) == 0x3FFFFF
27+
28+
29+
@pytest.mark.parametrize(("dt", "expected_ms"), DATETIME_CASES)
30+
def test_generate_snowflake_boundary_low(dt, expected_ms):
31+
"""Boundary mode low should zero out lower 22 bits."""
32+
sf = generate_snowflake(dt, mode="boundary", high=False)
33+
assert (sf >> 22) == expected_ms
34+
assert (sf & ((1 << 22) - 1)) == 0
35+
36+
37+
@pytest.mark.parametrize(("dt", "expected_ms"), DATETIME_CASES)
38+
def test_generate_snowflake_boundary_high(dt, expected_ms):
39+
"""Boundary mode high should set lower 22 bits to max."""
40+
sf = generate_snowflake(dt, mode="boundary", high=True)
41+
assert (sf >> 22) == expected_ms
42+
assert (sf & ((1 << 22) - 1)) == (2**22 - 1)
43+
44+
45+
@pytest.mark.parametrize(("dt", "expected_ms"), DATETIME_CASES)
46+
def test_snowflake_time_roundtrip_boundary(dt, expected_ms):
47+
"""Converting boundary snowflake back to datetime yields the original dt."""
48+
sf_low = generate_snowflake(dt, mode="boundary", high=False)
49+
sf_high = generate_snowflake(dt, mode="boundary", high=True)
50+
# snowflake_time ignores low bits, so both should map to dt
51+
assert snowflake_time(sf_low) == dt
52+
assert snowflake_time(sf_high) == dt
53+
54+
55+
@pytest.mark.parametrize(("dt", "expected_ms"), DATETIME_CASES)
56+
def test_snowflake_time_roundtrip_realistic(dt, expected_ms):
57+
"""Converting realistic snowflake back to datetime yields the original dt."""
58+
sf = generate_snowflake(dt, mode="realistic")
59+
assert snowflake_time(sf) == dt
60+
61+
62+
def test_generate_snowflake_invalid_mode():
63+
"""Passing an invalid mode should raise ValueError."""
64+
with pytest.raises(ValueError, match="Invalid mode 'nope'. Must be 'realistic' or 'boundary'"):
65+
generate_snowflake(datetime.datetime.now(tz=UTC), mode="nope")

0 commit comments

Comments
 (0)