@@ -93,6 +93,36 @@ private function getPermissions(int $sponsor_id, int $member_id): array
9393 // addSponsorUserToGroup
9494 // -------------------------------------------------------------------------
9595
96+ /**
97+ * MQ race: the group event arrives before the membership event, so there is
98+ * no Sponsor_Users row yet when addSponsorUserToGroup is called.
99+ * The service must create the row eagerly, flush the UoW so the INSERT is
100+ * visible to the raw SQL retry, and then successfully write the permission.
101+ */
102+ public function testAddSponsorUserToGroupEagerlyCreatesRowAndWritesPermissionOnRetry (): void
103+ {
104+ // sponsors[1] has no Sponsor_Users row — the member is not yet a user
105+ // of this sponsor, simulating the race condition.
106+ $ sponsor_id = self ::$ sponsors [1 ]->getId ();
107+ $ member_id = self ::$ member ->getId ();
108+ $ external_id = self ::$ member ->getUserExternalId ();
109+ $ summit_id = self ::$ summit ->getId ();
110+
111+ $ conn = self ::$ em ->getConnection ();
112+
113+ // Confirm no row exists before the call.
114+ $ exists = $ conn ->executeQuery (
115+ 'SELECT COUNT(*) FROM Sponsor_Users WHERE SponsorID = ? AND MemberID = ? ' ,
116+ [$ sponsor_id , $ member_id ]
117+ )->fetchOne ();
118+ $ this ->assertEquals (0 , (int )$ exists , 'Pre-condition: no Sponsor_Users row should exist ' );
119+
120+ $ this ->getService ()->addSponsorUserToGroup ($ external_id , IGroup::Sponsors, $ sponsor_id , $ summit_id );
121+
122+ // The row must have been created and the permission written.
123+ $ this ->assertContains (IGroup::Sponsors, $ this ->getPermissions ($ sponsor_id , $ member_id ));
124+ }
125+
96126 /**
97127 * The group slug must be written into the Sponsor_Users.Permissions JSON
98128 * column for the correct (SponsorID, MemberID) row.
0 commit comments