Coverage for app/backend/src/tests/test_screens.py: 100%
89 statements
« prev ^ index » next coverage.py v7.16.2, created at 2026-10-10 18:39 +0000
« prev ^ index » next coverage.py v7.16.2, created at 2026-10-10 18:39 +0000
1from datetime import timedelta
3from google.protobuf import empty_pb2
5from couchers.db import session_scope
6from couchers.proto import discussions_pb2, events_pb2, messages_pb2, requests_pb2, screens_pb2
7from couchers.utils import datetime_to_iso8601_local, now, today
8from tests.fixtures.db import generate_user
9from tests.fixtures.sessions import (
10 account_session,
11 discussions_session,
12 events_session,
13 requests_session,
14 screens_session,
15)
16from tests.test_communities import create_community
17from tests.test_requests import valid_request_text
19UPCOMING_STATUSES = [
20 messages_pb2.HOST_REQUEST_STATUS_ACCEPTED,
21 messages_pb2.HOST_REQUEST_STATUS_CONFIRMED,
22]
25def _setup_accepted_host_request(token_surfer, host_user_id, moderator):
26 from_date = today() + timedelta(days=2)
27 to_date = today() + timedelta(days=3)
28 with requests_session(token_surfer) as api:
29 host_request_id = api.CreateHostRequest(
30 requests_pb2.CreateHostRequestReq(
31 host_user_id=host_user_id,
32 from_date=from_date.isoformat(),
33 to_date=to_date.isoformat(),
34 text=valid_request_text(),
35 )
36 ).host_request_id
37 moderator.approve_host_request(host_request_id)
38 return host_request_id
41def test_GetDashboard_matches_individual_rpcs(db, moderator):
42 user1, token1 = generate_user()
43 user2, token2 = generate_user()
45 host_request_id = _setup_accepted_host_request(token1, user2.id, moderator)
46 with requests_session(token2) as api:
47 api.RespondHostRequest(
48 requests_pb2.RespondHostRequestReq(
49 host_request_id=host_request_id,
50 status=messages_pb2.HOST_REQUEST_STATUS_ACCEPTED,
51 text="Sure, come on over!",
52 )
53 )
55 # the dashboard response must be identical to fanning out to the individual RPCs with the
56 # same parameters the web frontend uses
57 with requests_session(token1) as api:
58 surfing = api.ListHostRequests(
59 requests_pb2.ListHostRequestsReq(
60 only_sent=True,
61 only_active=True,
62 status_in=UPCOMING_STATUSES,
63 sort_by=requests_pb2.HOST_REQUEST_SORT_BY_FROM_DATE,
64 )
65 )
66 hosting = api.ListHostRequests(
67 requests_pb2.ListHostRequestsReq(
68 only_received=True,
69 only_active=True,
70 status_in=UPCOMING_STATUSES,
71 sort_by=requests_pb2.HOST_REQUEST_SORT_BY_FROM_DATE,
72 )
73 )
74 with events_session(token1) as api:
75 my_events = api.ListMyEvents(events_pb2.ListMyEventsReq(page_size=3))
76 community_events = api.ListMyEvents(
77 events_pb2.ListMyEventsReq(
78 page_size=3, my_communities=True, my_communities_exclude_global=True, exclude_attending=True
79 )
80 )
81 with discussions_session(token1) as api:
82 discussions = api.ListMyCommunitiesDiscussions(discussions_pb2.ListMyCommunitiesDiscussionsReq(page_size=3))
83 with account_session(token1) as api:
84 reminders = api.GetReminders(empty_pb2.Empty())
86 with screens_session(token1) as api:
87 res = api.GetDashboard(screens_pb2.GetDashboardReq())
89 assert res.reminders == reminders
90 assert res.surfing == surfing
91 assert res.hosting == hosting
92 assert res.my_events == my_events
93 assert res.community_events == community_events
94 assert res.discussions == discussions
96 # the surfer sees their upcoming trip under surfing, nothing under hosting
97 assert len(res.surfing.host_requests) == 1
98 assert res.surfing.host_requests[0].host_request_id == host_request_id
99 assert res.surfing.host_requests[0].status == messages_pb2.HOST_REQUEST_STATUS_ACCEPTED
100 assert len(res.hosting.host_requests) == 0
103def test_GetDashboard_buckets_by_role(db, moderator):
104 user1, token1 = generate_user()
105 user2, token2 = generate_user()
107 host_request_id = _setup_accepted_host_request(token1, user2.id, moderator)
108 with requests_session(token2) as api:
109 api.RespondHostRequest(
110 requests_pb2.RespondHostRequestReq(
111 host_request_id=host_request_id,
112 status=messages_pb2.HOST_REQUEST_STATUS_ACCEPTED,
113 text="Sure, come on over!",
114 )
115 )
117 # the host sees the upcoming stay under hosting, nothing under surfing
118 with screens_session(token2) as api:
119 res = api.GetDashboard(screens_pb2.GetDashboardReq())
120 assert len(res.hosting.host_requests) == 1
121 assert res.hosting.host_requests[0].host_request_id == host_request_id
122 assert len(res.surfing.host_requests) == 0
125def test_GetDashboard_community_events_excludes_attending(db, moderator):
126 # Pins the exclude_attending parameter the web frontend sends: an event the user is
127 # attending shows under my_events and must not also duplicate into community_events.
128 user1, token1 = generate_user()
129 user2, token2 = generate_user()
131 with session_scope() as session:
132 # community_events excludes global-level communities, so nest down to a subregion
133 world = create_community(session, 0, 100, "World", [user1, user2], [], None)
134 macroregion = create_community(session, 0, 100, "Macroregion", [user1, user2], [], world)
135 region = create_community(session, 0, 100, "Region", [user1, user2], [], macroregion)
136 subregion = create_community(session, 0, 100, "Subregion", [user1, user2], [], region)
137 community_id = subregion.id
139 start = now()
141 def make_event(hours: int) -> events_pb2.CreateEventReq:
142 return events_pb2.CreateEventReq(
143 title="Test Event",
144 content="Test content.",
145 location=events_pb2.EventLocation(address="Near Null Island", lat=0.1, lng=0.2),
146 parent_community_id=community_id,
147 start_datetime_iso8601_local=datetime_to_iso8601_local(start + timedelta(hours=hours)),
148 end_datetime_iso8601_local=datetime_to_iso8601_local(start + timedelta(hours=hours + 1)),
149 )
151 with events_session(token2) as api:
152 e_attending = api.CreateEvent(make_event(1)).event_id
153 e_community_only = api.CreateEvent(make_event(2)).event_id
155 moderator.approve_event_occurrence(e_attending)
156 moderator.approve_event_occurrence(e_community_only)
158 with events_session(token1) as api:
159 api.SetEventAttendance(
160 events_pb2.SetEventAttendanceReq(event_id=e_attending, attendance_state=events_pb2.ATTENDANCE_STATE_GOING)
161 )
163 with screens_session(token1) as api:
164 res = api.GetDashboard(screens_pb2.GetDashboardReq())
166 # the attended event shows under my_events (which with no flags includes all relationships)...
167 assert e_attending in {e.event_id for e in res.my_events.events}
168 # ...while community_events must exclude it (exclude_attending), showing only the rest
169 assert [e.event_id for e in res.community_events.events] == [e_community_only]
172def test_GetDashboard_empty(db):
173 user, token = generate_user()
174 with screens_session(token) as api:
175 res = api.GetDashboard(screens_pb2.GetDashboardReq())
176 assert len(res.surfing.host_requests) == 0
177 assert len(res.hosting.host_requests) == 0
178 assert len(res.my_events.events) == 0
179 assert len(res.community_events.events) == 0
180 assert len(res.discussions.discussions) == 0
181 assert len(res.reminders.reminders) == 0