Coverage for src/tests/test_public.py: 100%
83 statements
« prev ^ index » next coverage.py v7.11.0, created at 2025-12-20 11:53 +0000
« prev ^ index » next coverage.py v7.11.0, created at 2025-12-20 11:53 +0000
1import json
2from datetime import UTC, datetime
3from math import sqrt
4from unittest.mock import patch
6import pytest
7from google.protobuf import empty_pb2
9from couchers.db import session_scope
10from couchers.jobs.enqueue import queue_job
11from couchers.models import (
12 Invoice,
13 InvoiceType,
14 ProfilePublicVisibility,
15)
16from couchers.servicers.public import _get_donation_stats
17from tests.test_fixtures import ( # noqa
18 db,
19 generate_user,
20 process_jobs,
21 public_session,
22 testconfig,
23)
26@pytest.fixture(autouse=True)
27def _(testconfig):
28 pass
31def test_GetPublicMapLayer(db):
32 user1, _ = generate_user()
33 user2, _ = generate_user(username="user2", public_visibility=ProfilePublicVisibility.nothing)
34 user3, _ = generate_user()
35 user4, _ = generate_user(username="user4", public_visibility=ProfilePublicVisibility.limited)
36 user5, _ = generate_user()
38 # these are hardcoded in test_fixtures
39 test_user_coordinates = [-73.9740, 40.7108]
41 with session_scope() as session:
42 queue_job(session, "update_randomized_locations", empty_pb2.Empty())
44 process_jobs()
46 with public_session() as public:
47 http_body = public.GetPublicUsers(empty_pb2.Empty())
48 assert http_body.content_type == "application/json"
49 data = json.loads(http_body.data)
50 # Sort to ensure a deterministic order
51 data["features"].sort(key=lambda f: f["geometry"]["coordinates"][0])
52 assert data == {
53 "type": "FeatureCollection",
54 "features": [
55 {
56 "type": "Feature",
57 "geometry": {"type": "Point", "coordinates": [-74.042643848, 40.706241098]},
58 "properties": {"username": None},
59 },
60 {
61 "type": "Feature",
62 "geometry": {"type": "Point", "coordinates": [-73.974, 40.7108]},
63 "properties": {"username": "user4"},
64 },
65 {
66 "type": "Feature",
67 "geometry": {"type": "Point", "coordinates": [-73.955417734, 40.691831306]},
68 "properties": {"username": None},
69 },
70 {
71 "type": "Feature",
72 "geometry": {"type": "Point", "coordinates": [-73.928380198, 40.729706144]},
73 "properties": {"username": None},
74 },
75 ],
76 }
78 for user in data["features"]:
79 coords = user["geometry"]["coordinates"]
80 if user["properties"]["username"]:
81 assert coords == test_user_coordinates
82 else:
83 xdiff = coords[0] - test_user_coordinates[0]
84 ydiff = coords[1] - test_user_coordinates[1]
85 dist = sqrt(xdiff**2 + ydiff**2)
86 assert dist > 0.02 and dist < 0.1
89def test_GetDonationStats_empty(db):
90 """Test GetDonationStats with no donations returns zero and goal"""
91 _get_donation_stats.cache.clear()
93 with (
94 patch("couchers.servicers.public.DONATION_GOAL_USD", 2500),
95 patch("couchers.servicers.public.DONATION_OFFSET_USD", 700),
96 ):
97 with public_session() as public:
98 res = public.GetDonationStats(empty_pb2.Empty())
99 assert res.total_donated_ytd == 0
100 assert res.goal == 2500
103def test_GetDonationStats_with_donations(db):
104 """Test GetDonationStats sums on_platform donations correctly"""
105 _get_donation_stats.cache.clear()
106 user, _ = generate_user()
108 with session_scope() as session:
109 # Add some on_platform donations (should be counted)
110 session.add(
111 Invoice(
112 user_id=user.id,
113 amount=100,
114 stripe_payment_intent_id="pi_test_1",
115 stripe_receipt_url="https://example.com/receipt/1",
116 invoice_type=InvoiceType.on_platform,
117 )
118 )
119 session.add(
120 Invoice(
121 user_id=user.id,
122 amount=250,
123 stripe_payment_intent_id="pi_test_2",
124 stripe_receipt_url="https://example.com/receipt/2",
125 invoice_type=InvoiceType.on_platform,
126 )
127 )
128 session.add(
129 Invoice(
130 user_id=user.id,
131 amount=500,
132 stripe_payment_intent_id="pi_test_3",
133 stripe_receipt_url="https://example.com/receipt/3",
134 invoice_type=InvoiceType.on_platform,
135 )
136 )
138 with (
139 patch("couchers.servicers.public.DONATION_GOAL_USD", 5000),
140 patch("couchers.servicers.public.DONATION_OFFSET_USD", 0),
141 ):
142 with public_session() as public:
143 res = public.GetDonationStats(empty_pb2.Empty())
144 assert res.total_donated_ytd == 850
145 assert res.goal == 5000
148def test_GetDonationStats_excludes_merch(db):
149 """Test GetDonationStats excludes external_shop (merch) invoices"""
150 _get_donation_stats.cache.clear()
151 user, _ = generate_user()
153 with session_scope() as session:
154 # Add on_platform donation (should be counted)
155 session.add(
156 Invoice(
157 user_id=user.id,
158 amount=200,
159 stripe_payment_intent_id="pi_test_donation",
160 stripe_receipt_url="https://example.com/receipt/donation",
161 invoice_type=InvoiceType.on_platform,
162 )
163 )
164 # Add external_shop/merch purchase (should NOT be counted)
165 session.add(
166 Invoice(
167 user_id=user.id,
168 amount=50,
169 stripe_payment_intent_id="pi_test_merch",
170 stripe_receipt_url="https://example.com/receipt/merch",
171 invoice_type=InvoiceType.external_shop,
172 )
173 )
175 with (
176 patch("couchers.servicers.public.DONATION_GOAL_USD", 5000),
177 patch("couchers.servicers.public.DONATION_OFFSET_USD", 0),
178 ):
179 with public_session() as public:
180 res = public.GetDonationStats(empty_pb2.Empty())
181 # Should only count the on_platform donation, not the merch
182 assert res.total_donated_ytd == 200
183 assert res.goal == 5000
186def test_GetDonationStats_excludes_previous_years(db):
187 """Test GetDonationStats only counts current year donations"""
188 _get_donation_stats.cache.clear()
189 user, _ = generate_user()
191 with session_scope() as session:
192 # Add donation from this year (should be counted)
193 session.add(
194 Invoice(
195 user_id=user.id,
196 amount=300,
197 stripe_payment_intent_id="pi_test_this_year",
198 stripe_receipt_url="https://example.com/receipt/this_year",
199 invoice_type=InvoiceType.on_platform,
200 )
201 )
202 # Add donation from last year (should NOT be counted)
203 last_year = datetime(datetime.now(UTC).year - 1, 6, 15, tzinfo=UTC)
204 invoice = Invoice(
205 user_id=user.id,
206 amount=1000,
207 stripe_payment_intent_id="pi_test_last_year",
208 stripe_receipt_url="https://example.com/receipt/last_year",
209 invoice_type=InvoiceType.on_platform,
210 )
211 session.add(invoice)
212 session.flush()
213 # Manually set the created date to last year
214 invoice.created = last_year
216 with (
217 patch("couchers.servicers.public.DONATION_GOAL_USD", 5000),
218 patch("couchers.servicers.public.DONATION_OFFSET_USD", 0),
219 ):
220 with public_session() as public:
221 res = public.GetDonationStats(empty_pb2.Empty())
222 # Should only count this year's donation
223 assert res.total_donated_ytd == 300
224 assert res.goal == 5000