Coverage for app / backend / src / tests / test_utils.py: 100%
38 statements
« prev ^ index » next coverage.py v7.13.2, created at 2026-02-03 06:18 +0000
« prev ^ index » next coverage.py v7.13.2, created at 2026-02-03 06:18 +0000
1from datetime import UTC, datetime
2from unittest.mock import patch
4import pytest
5from sqlalchemy import select, update
6from sqlalchemy.sql import func
8from couchers.db import session_scope
9from couchers.models import User
10from couchers.utils import dt_from_page_token, dt_to_page_token, http_date, now, wrap_coordinate
11from tests.fixtures.db import generate_user
14@pytest.fixture(autouse=True)
15def _(testconfig):
16 pass
19def test_page_token_time_python():
20 now_ = now()
21 assert now_ == dt_from_page_token(dt_to_page_token(now_))
24def test_page_token_time_db(db):
25 user, _ = generate_user()
27 # generate a timestamp in postgres (note use of `func`)
28 with session_scope() as session:
29 session.execute(update(User).where(User.id == user.id).values(joined=func.now()))
31 with session_scope() as session:
32 # pull it back into python
33 joined = session.execute(select(User)).scalar_one().joined
35 # roundtrip page token
36 roundtrip = dt_from_page_token(dt_to_page_token(joined))
38 # make sure euqality is still equality
39 user = session.execute(select(User).where(User.joined == roundtrip)).scalar_one()
40 assert user.joined == roundtrip
43def test_http_date_with_datetime():
44 """Test http_date with a specific datetime to verify usegmt=True is used"""
45 dt = datetime(2024, 1, 15, 10, 30, 45, tzinfo=UTC)
47 result = http_date(dt)
49 assert result == "Mon, 15 Jan 2024 10:30:45 GMT"
52def test_http_date_without_datetime():
53 """Test http_date with dt=None to verify it uses now() and usegmt=True"""
54 mock_now = datetime(2024, 3, 20, 14, 25, 30, tzinfo=UTC)
56 with patch("couchers.utils.now", return_value=mock_now):
57 result = http_date()
59 assert result == "Wed, 20 Mar 2024 14:25:30 GMT"
62def test_wrap_coordinate():
63 test_coords = [
64 ((-95, -185), (-85, 175)),
65 ((95, -180), (85, 180)), # Weird interaction in PostGIS where lng
66 # flips at -180 only when there is latitude overflow
67 ((90, -180), (90, -180)),
68 ((20, 185), (20, -175)),
69 ((0, 0), (0, 0)),
70 ((-1000, 0), (80, 0)),
71 ((1000, 0), (-80, 0)),
72 ((-180, 0), (0, 0)),
73 ((180, 0), (0, 0)),
74 ((-200, 0), (20, 0)),
75 ((200, 0), (-20, 0)),
76 ((-450, 0), (-90, 0)),
77 ((450, 0), (90, 0)),
78 ((-540, 0), (0, 0)),
79 ((540, 0), (0, 0)),
80 ((-90, 0), (-90, 0)),
81 ((90, 0), (90, 0)),
82 ((-1000, -1000), (80, 80)),
83 ((1000, 1000), (-80, -80)),
84 ((-100, -100), (-80, -100)),
85 ((100, 100), (80, 100)),
86 ((1000, 10), (-80, 10)),
87 ((-100.5, 10), (-79.5, 10)),
88 ((100.5, 10), (79.5, 10)),
89 ((-100, 10), (-80, 10)),
90 ((100, 10), (80, 10)),
91 ((-180, 10), (0, 10)),
92 ((180, 10), (0, 10)),
93 ((20, 10), (20, 10)),
94 ((-270, 10), (90, 10)),
95 ((270, 10), (-90, 10)),
96 ((-360, 10), (0, 10)),
97 ((360, 10), (0, 10)),
98 ((-90.1, 10), (-89.9, 10)),
99 ((90.1, 10), (89.9, 10)),
100 ((-90, 10), (-90, 10)),
101 ((90, 10), (90, 10)),
102 ((-80, -170), (-80, -170)),
103 ((80, 170), (80, 170)),
104 ((0, -180), (0, -180)),
105 ((0, 180), (0, 180)),
106 ((100, -180), (80, 180)),
107 ((100, 180), (80, 180)),
108 ((20, -180.1), (20, 179.9)),
109 ((20, 180.1), (20, -179.9)),
110 ((20, -180), (20, -180)),
111 ((20, 180), (20, 180)),
112 ((-90, -180), (-90, -180)),
113 ((90, 180), (90, 180)),
114 ((30, -190), (30, 170)),
115 ((30, 190), (30, -170)),
116 ((-95, -190), (-85, 170)),
117 ((95, 190), (85, -170)),
118 ((0, -200), (0, 160)),
119 ((0, 200), (0, -160)),
120 ((-100, -200), (-80, 160)),
121 ((100, 200), (80, -160)),
122 ((-200, -200), (20, 160)),
123 ((200, 200), (-20, -160)),
124 ((20, -200), (20, 160)),
125 ((20, 200), (20, -160)),
126 ((-90, 200), (-90, -160)),
127 ((90, 200), (90, -160)),
128 ((0, -270), (0, 90)),
129 ((0, 270), (0, -90)),
130 ((-45, -270), (-45, 90)),
131 ((45, 270), (45, -90)),
132 ((0, -360), (0, 0)),
133 ((0, 360), (0, 0)),
134 ((-90, -360), (-90, 0)),
135 ((90, 360), (90, 0)),
136 ((-500, -500), (-40, -140)),
137 ((500, 500), (40, 140)),
138 ((50, -500), (50, -140)),
139 ((50, 500), (50, 140)),
140 ((-500, 50), (-40, 50)),
141 ((500, 50), (40, 50)),
142 ((0, -540), (0, 180)),
143 ((0, 540), (0, 180)),
144 ((-45, -90), (-45, -90)),
145 ((45, 90), (45, 90)),
146 ]
148 for coords, coords_expected in test_coords:
149 coords_wrapped = wrap_coordinate(*coords)
150 assert coords_expected == coords_wrapped