3333use DG \BypassFinals ;
3434use Exception ;
3535use PHPUnit \Framework \TestCase ;
36- use ReflectionClass ;
3736use ReflectionProperty ;
3837
3938use function Amp \delay ;
@@ -64,12 +63,18 @@ protected function setUp(): void
6463 BypassFinals ::enable ();
6564 }
6665
66+ /** @var list<array> The updates saved. */
67+ private array $ updates = [];
68+
6769 private function client (): MTProto
6870 {
69- $ API = (new ReflectionClass (MTProto::class))->newInstanceWithoutConstructor ();
71+ $ API = $ this ->getMockBuilder (MTProto::class)->disableOriginalConstructor ()->onlyMethods (['saveUpdate ' ])->getMock ();
72+ $ API ->method ('saveUpdate ' )->willReturnCallback (function (array $ update ): void {
73+ $ this ->updates [] = $ update ;
74+ });
7075 $ API ->logger = new Logger ((new LoggerSettings )->setType (Logger::ECHO_LOGGER )->setLevel (Logger::FATAL_ERROR ));
7176 $ API ->wrapper = new APIWrapper (new SessionPaths (sys_get_temp_dir ().'/madeline-group-call-membership ' ));
72- (new ReflectionProperty ($ API , 'settings ' ))->setValue ($ API , new Settings );
77+ (new ReflectionProperty (MTProto::class , 'settings ' ))->setValue ($ API , new Settings );
7378 $ connection = $ this ->createMock (Connection::class);
7479 $ connection ->method ('methodCallAsyncRead ' )->willReturnCallback (function (string $ method , array $ args ): mixed {
7580 $ this ->calls [] = $ method ;
@@ -82,7 +87,7 @@ private function client(): MTProto
8287 return $ API ;
8388 }
8489
85- private function joinedCall (MTProto $ API ): GroupCallController
90+ private function joinedCall (MTProto $ API , ? int $ peerId = null , bool $ liveStory = false ): GroupCallController
8691 {
8792 $ controller = new GroupCallController ($ API , [
8893 '_ ' => 'groupCall ' ,
@@ -103,10 +108,10 @@ private function joinedCall(MTProto $API): GroupCallController
103108 'record_video_active ' => false ,
104109 'conference ' => false ,
105110 'version ' => 1 ,
106- ]);
111+ ], $ peerId , $ liveStory );
107112 (new ReflectionProperty ($ controller , 'callState ' ))->setValue ($ controller , GroupCallState::JOINED );
108113 (new ReflectionProperty ($ controller , 'source ' ))->setValue ($ controller , self ::SOURCE );
109- (new ReflectionProperty ($ API , 'groupCalls ' ))->setValue ($ API , [1 => $ controller ]);
114+ (new ReflectionProperty (MTProto::class , 'groupCalls ' ))->setValue ($ API , [1 => $ controller ]);
110115 return $ controller ;
111116 }
112117
@@ -143,6 +148,40 @@ public function testAnEndedCallIsLeft(string $rpc): void
143148 $ this ->assertSame ([], $ API ->getAllGroupCalls ());
144149 }
145150
151+ /**
152+ * A call that ended is reported to the event handler as discarded: a live story ends with an updateStory, without
153+ * the groupCallDiscarded the event handler waits for (magnaluna didn't start its live story again).
154+ */
155+ public function testAnEndedCallIsReportedAsDiscarded (): void
156+ {
157+ $ API = $ this ->client ();
158+ $ controller = $ this ->joinedCall ($ API , 783732906 , liveStory: true );
159+ $ this ->answer = static fn () => throw RPCErrorException ::make ('GROUPCALL_INVALID ' , 400 , '' );
160+
161+ self ::check ($ controller );
162+
163+ $ this ->assertSame ([[
164+ '_ ' => 'updateGroupCall ' ,
165+ 'call ' => ['_ ' => 'groupCallDiscarded ' , 'id ' => 1 , 'access_hash ' => 1234 , 'duration ' => 0 ],
166+ 'live_story ' => true ,
167+ 'peer ' => ['_ ' => 'peerUser ' , 'user_id ' => 783732906 ],
168+ ]], $ this ->updates );
169+ }
170+
171+ /**
172+ * A call we may just not be in anymore didn't end: it isn't reported as discarded.
173+ */
174+ public function testAForbiddenCallIsNotReportedAsDiscarded (): void
175+ {
176+ $ controller = $ this ->joinedCall ($ this ->client (), -1001412768105 );
177+ $ this ->answer = static fn () => throw RPCErrorException ::make ('GROUPCALL_FORBIDDEN ' , 403 , '' );
178+
179+ self ::check ($ controller );
180+
181+ $ this ->assertSame (GroupCallState::LEFT , $ controller ->getCallState ());
182+ $ this ->assertSame ([], $ this ->updates );
183+ }
184+
146185 /**
147186 * A failed WebRTC connection is rejoined right away, even if the server still lists our source: it never
148187 * recovers by itself (after a network outage, every call of a bot stayed failed for hours, sending nothing).
0 commit comments