Repository: couchers-org/couchers
Issue: ListEventAttendees doesn't always return the current user correctly even when they are attending an event

Here are some rough reproduction steps:
1. Create an event
2. Transfer the event to a community (although this step probably not necessarily, only needed for it to show up in UI/`ListEvents` from `communities.proto`
3. Change the current user's attendance state to "not going" (with `SetEventAttendance`)
4. Change the current user's attendance state to "going" again

**Expected behaviour**
`ListEventAttendees` should return the current user's ID

**Actual/current behaviour**
`ListEventAttendees` returns another user's ID. This ID seems to be determined from the row's auto increment ID in `event_occurence_attendees` in the database

Basically can't release the event feature until this is fixed...

Other notes: I suspect `ListEventOrganizers` and `ListEventSubscribers` could also have this same problem, so probably worth double checking that too...

Changed files:
app/backend/src/tests/test_events.py, app/backend/src/couchers/servicers/events.py

diff --git a/app/backend/src/couchers/servicers/events.py b/app/backend/src/couchers/servicers/events.py
index 913bc5469..b8ddcc5c2 100644
--- a/app/backend/src/couchers/servicers/events.py
+++ b/app/backend/src/couchers/servicers/events.py
@@ -530,8 +530,8 @@ class Events(events_pb2_grpc.EventsServicer):
                 .all()
             )
             return events_pb2.ListEventAttendeesRes(
-                attendee_user_ids=[attendee.id for attendee in attendees[:page_size]],
-                next_page_token=str(attendees[-1].id) if len(attendees) > page_size else None,
+                attendee_user_ids=[attendee.user_id for attendee in attendees[:page_size]],
+                next_page_token=str(attendees[-1].user_id) if len(attendees) > page_size else None,
             )
 
     def ListEventSubscribers(self, request, context):
@@ -559,8 +559,8 @@ class Events(events_pb2_grpc.EventsServicer):
                 .all()
             )
             return events_pb2.ListEventSubscribersRes(
-                subscriber_user_ids=[subscriber.id for subscriber in subscribers[:page_size]],
-                next_page_token=str(subscribers[-1].id) if len(subscribers) > page_size else None,
+                subscriber_user_ids=[subscriber.user_id for subscriber in subscribers[:page_size]],
+                next_page_token=str(subscribers[-1].user_id) if len(subscribers) > page_size else None,
             )
 
     def ListEventOrganizers(self, request, context):
@@ -588,8 +588,8 @@ class Events(events_pb2_grpc.EventsServicer):
                 .all()
             )
             return events_pb2.ListEventOrganizersRes(
-                organizer_user_ids=[organizer.id for organizer in organizers[:page_size]],
-                next_page_token=str(organizers[-1].id) if len(organizers) > page_size else None,
+                organizer_user_ids=[organizer.user_id for organizer in organizers[:page_size]],
+                next_page_token=str(organizers[-1].user_id) if len(organizers) > page_size else None,
             )
 
     def TransferEvent(self, request, context):
diff --git a/app/backend/src/tests/test_events.py b/app/backend/src/tests/test_events.py
index 40e157559..527e8c804 100644
--- a/app/backend/src/tests/test_events.py
+++ b/app/backend/src/tests/test_events.py
@@ -1798,3 +1798,65 @@ def test_RemoveEventOrganizer(db):
         res = api.GetEvent(events_pb2.GetEventReq(event_id=event_id))
         assert not res.organizer
         assert res.organizer_count == 1
+
+
+def test_ListEventAttendees_regression(db):
+    # see issue #1617:
+    #
+    # 1. Create an event
+    # 2. Transfer the event to a community (although this step probably not necessarily, only needed for it to show up in UI/`ListEvents` from `communities.proto`
+    # 3. Change the current user's attendance state to "not going" (with `SetEventAttendance`)
+    # 4. Change the current user's attendance state to "going" again
+    #
+    # **Expected behaviour**
+    # `ListEventAttendees` should return the current user's ID
+    #
+    # **Actual/current behaviour**
+    # `ListEventAttendees` returns another user's ID. This ID seems to be determined from the row's auto increment ID in `event_occurence_attendees` in the database
+
+    user1, token1 = generate_user()
+    user2, token2 = generate_user()
+    user3, token3 = generate_user()
+    user4, token4 = generate_user()
+    user5, token5 = generate_user()
+
+    with session_scope() as session:
+        c_id = create_community(session, 0, 2, "Community", [user1], [], None).id
+
+    start_time = now() + timedelta(hours=2)
+    end_time = start_time + timedelta(hours=3)
+
+    with events_session(token1) as api:
+        res = api.CreateEvent(
+            events_pb2.CreateEventReq(
+                title="Dummy Title",
+                content="Dummy content.",
+                online_information=events_pb2.OnlineEventInformation(
+                    link="https://couchers.org",
+                ),
+                parent_community_id=c_id,
+                start_time=Timestamp_from_datetime(start_time),
+                end_time=Timestamp_from_datetime(end_time),
+                timezone="UTC",
+            )
+        )
+
+        res = api.TransferEvent(
+            events_pb2.TransferEventReq(
+                event_id=res.event_id,
+                new_owner_community_id=c_id,
+            )
+        )
+
+        event_id = res.event_id
+
+        api.SetEventAttendance(
+            events_pb2.SetEventAttendanceReq(event_id=event_id, attendance_state=events_pb2.ATTENDANCE_STATE_NOT_GOING)
+        )
+        api.SetEventAttendance(
+            events_pb2.SetEventAttendanceReq(event_id=event_id, attendance_state=events_pb2.ATTENDANCE_STATE_GOING)
+        )
+
+        res = api.ListEventAttendees(events_pb2.ListEventAttendeesReq(event_id=event_id))
+        assert len(res.attendee_user_ids) == 1
+        assert res.attendee_user_ids[0] == user1.id