diff options
Diffstat (limited to 'tests/phpunit/unit')
4 files changed, 339 insertions, 0 deletions
diff --git a/tests/phpunit/unit/includes/Notification/MiddlewareChainTest.php b/tests/phpunit/unit/includes/Notification/MiddlewareChainTest.php new file mode 100644 index 000000000000..c133727d84cb --- /dev/null +++ b/tests/phpunit/unit/includes/Notification/MiddlewareChainTest.php @@ -0,0 +1,170 @@ +<?php + +namespace MediaWiki\Tests\Notification; + +use MediaWiki\Notification\MiddlewareChain; +use MediaWiki\Notification\Notification; +use MediaWiki\Notification\NotificationEnvelope; +use MediaWiki\Notification\NotificationMiddlewareInterface; +use MediaWiki\Notification\NotificationsBatch; +use MediaWiki\Notification\RecipientSet; +use MediaWiki\User\UserIdentity; +use MediaWikiUnitTestCase; + +/** + * @covers \MediaWiki\Notification\MiddlewareChain + */ +class MiddlewareChainTest extends MediaWikiUnitTestCase { + + private function getSut( array $middlewares ): MiddlewareChain { + $specs = []; + foreach ( $middlewares as $middleware ) { + $specs[] = [ 'factory' => static fn () => $middleware ]; + } + return new MiddlewareChain( $this->createSimpleObjectFactory(), $specs ); + } + + public function testEmptyMiddlewareChainReturnsOriginalBatch() { + $userIdentity = $this->createMock( UserIdentity::class ); + $notificationToSend = new Notification( 'test', [] ); + $recipients = new RecipientSet( [ $userIdentity ] ); + + $envelopes = new NotificationsBatch( + new NotificationEnvelope( $notificationToSend, $recipients ) + ); + + $sut = $this->getSut( [] ); + $this->assertSame( $envelopes, $sut->process( $envelopes ) ); + } + + public function testExecutesInOrderAndModifiesBatch() { + $userIdentity = $this->createMock( UserIdentity::class ); + $notificationToSend = new Notification( 'test', [] ); + $recipients = new RecipientSet( [ $userIdentity ] ); + + $noOpMiddleware = $this->createMock( NotificationMiddlewareInterface::class ); + $noOpMiddleware->expects( $this->exactly( 2 ) ) + ->method( 'handle' ) + ->willReturnCallback( function ( NotificationsBatch $batch, callable $next ) { + $this->assertCount( 1, $batch ); + $next(); + } ); + $emptyMiddleware = $this->createMock( NotificationMiddlewareInterface::class ); + $emptyMiddleware->expects( $this->once() ) + ->method( 'handle' ) + ->willReturnCallback( static function ( NotificationsBatch $batch, callable $next ) { + foreach ( $batch as $envelope ) { + $batch->remove( $envelope ); + } + $next(); + } ); + $makeSureBatchIsEmptyMiddleware = $this->createMock( NotificationMiddlewareInterface::class ); + $makeSureBatchIsEmptyMiddleware->expects( $this->once() ) + ->method( 'handle' ) + ->willReturnCallback( function ( NotificationsBatch $batch, callable $next ) { + $this->assertCount( 0, $batch ); + $next(); + } ); + + $middlewareChain = $this->getSut( [ + $noOpMiddleware, $noOpMiddleware, $emptyMiddleware, $makeSureBatchIsEmptyMiddleware, + ] ); + + $result = $middlewareChain->process( new NotificationsBatch( + new NotificationEnvelope( $notificationToSend, $recipients ) + ) ); + $this->assertCount( 0, $result ); + } + + public function testMiddlewareDoesntCallNext() { + $userIdentity = $this->createMock( UserIdentity::class ); + $keptNotification = new Notification( 'test', [] ); + $recipients = new RecipientSet( [ $userIdentity ] ); + + $suppressWelcomeMiddleware = $this->createMock( NotificationMiddlewareInterface::class ); + $suppressWelcomeMiddleware->expects( $this->once() ) + ->method( 'handle' ) + ->willReturnCallback( static function ( NotificationsBatch $batch, callable $next ) { + // no op, but also don't call next + } ); + + $middlewareChain = $this->getSut( [ + $suppressWelcomeMiddleware, + ] ); + + $batch = $middlewareChain->process( + new NotificationsBatch( + new NotificationEnvelope( $keptNotification, $recipients ) + ) + ); + $this->assertCount( 1, $batch ); + $envelopes = iterator_to_array( $batch ); + $this->assertSame( $keptNotification, $envelopes[0]->getNotification() ); + } + + public function testSupressNotification() { + $userIdentity = $this->createMock( UserIdentity::class ); + $keptNotification = new Notification( 'test', [] ); + $removedNotification = new Notification( 'welcome', [] ); + $recipients = new RecipientSet( [ $userIdentity ] ); + + $suppressWelcomeMiddleware = $this->createMock( NotificationMiddlewareInterface::class ); + $suppressWelcomeMiddleware->expects( $this->once() ) + ->method( 'handle' ) + ->willReturnCallback( static function ( NotificationsBatch $batch, callable $next ) { + foreach ( $batch as $envelope ) { + if ( $envelope->getNotification()->getType() === 'welcome' ) { + $batch->remove( $envelope ); + } + } + $next(); + } ); + + $middlewareChain = $this->getSut( [ + $suppressWelcomeMiddleware, + ] ); + + $batch = $middlewareChain->process( new NotificationsBatch( + new NotificationEnvelope( $keptNotification, $recipients ), + new NotificationEnvelope( $removedNotification, $recipients ) + ) ); + $this->assertCount( 1, $batch ); + foreach ( $batch as $envelope ) { + $this->assertSame( $keptNotification, $envelope->getNotification() ); + } + } + + /** + * Test case when Middleware calls NotificationService::notify() to inject new notification + * This can cause endless loops where Middleware triggers a notification, that triggers the + * middleware again. + * + * @return void + */ + public function testBadMiddlewareTriesToSendNotificationInsteadOfInjectingToBatch() { + $userIdentity = $this->createMock( UserIdentity::class ); + $keptNotification = new Notification( 'test', [] ); + $recipients = new RecipientSet( [ $userIdentity ] ); + + $suppressWelcomeMiddleware = $this->createMock( NotificationMiddlewareInterface::class ); + $suppressWelcomeMiddleware->expects( $this->once() ) + ->method( 'handle' ) + ->willReturnCallback( static function ( NotificationsBatch $batch, callable $next ) { + // no op, but also don't call next + } ); + + $middlewareChain = $this->getSut( [ + $suppressWelcomeMiddleware, + ] ); + + $batch = $middlewareChain->process( + new NotificationsBatch( + new NotificationEnvelope( $keptNotification, $recipients ) + ) + ); + $this->assertCount( 1, $batch ); + $envelopes = iterator_to_array( $batch ); + $this->assertSame( $keptNotification, $envelopes[0]->getNotification() ); + } + +} diff --git a/tests/phpunit/unit/includes/Notification/NotificationBatchTest.php b/tests/phpunit/unit/includes/Notification/NotificationBatchTest.php new file mode 100644 index 000000000000..4cda67741215 --- /dev/null +++ b/tests/phpunit/unit/includes/Notification/NotificationBatchTest.php @@ -0,0 +1,94 @@ +<?php + +namespace MediaWiki\Tests\Notification; + +use MediaWiki\Notification\Notification; +use MediaWiki\Notification\NotificationEnvelope; +use MediaWiki\Notification\NotificationsBatch; +use MediaWiki\Notification\RecipientSet; +use MediaWikiUnitTestCase; +use stdClass; + +/** + * @covers \MediaWiki\Notification\NotificationsBatch + * @covers \MediaWiki\Notification\NotificationEnvelope + */ +class NotificationBatchTest extends MediaWikiUnitTestCase { + + public function testAdds() { + $first = new NotificationEnvelope( new Notification( 'first' ), new RecipientSet( [] ) ); + $second = new NotificationEnvelope( new Notification( 'second' ), new RecipientSet( [] ) ); + + $batch = new NotificationsBatch( $first ); + $batch->add( $second ); + + $this->assertCount( 2, $batch ); + $envelopesArray = iterator_to_array( $batch ); + $this->assertSame( $first, $envelopesArray[0] ); + $this->assertSame( $second, $envelopesArray[1] ); + } + + public function testRemoveWhenExists() { + $first = new NotificationEnvelope( new Notification( 'first' ), new RecipientSet( [] ) ); + $second = new NotificationEnvelope( new Notification( 'second' ), new RecipientSet( [] ) ); + $third = new NotificationEnvelope( new Notification( 'third' ), new RecipientSet( [] ) ); + $last = new NotificationEnvelope( new Notification( 'fourth' ), new RecipientSet( [] ) ); + + $batch = new NotificationsBatch( $first, $second, $third, $last ); + $this->assertCount( 4, $batch ); + + $batch->remove( $second ); + $this->assertCount( 3, $batch ); + $envelopesArray = iterator_to_array( $batch ); + $this->assertSame( $first, $envelopesArray[0] ); + $this->assertSame( $third, $envelopesArray[1] ); + $this->assertSame( $last, $envelopesArray[2] ); + + $batch->remove( $first ); + $this->assertCount( 2, $batch ); + $envelopesArray = iterator_to_array( $batch ); + $this->assertSame( $third, $envelopesArray[0] ); + $this->assertSame( $last, $envelopesArray[1] ); + + $batch->remove( $last ); + $this->assertCount( 1, $batch ); + $envelopesArray = iterator_to_array( $batch ); + $this->assertSame( $third, $envelopesArray[0] ); + } + + public function testRemovesWhenNotExists() { + $first = new NotificationEnvelope( new Notification( 'first' ), new RecipientSet( [] ) ); + $second = new NotificationEnvelope( new Notification( 'second' ), new RecipientSet( [] ) ); + $other = new NotificationEnvelope( new Notification( 'third' ), new RecipientSet( [] ) ); + + $batch = new NotificationsBatch( $first, $second ); + $batch->remove( $other ); + + $this->assertCount( 2, $batch ); + $envelopesArray = iterator_to_array( $batch ); + $this->assertSame( $first, $envelopesArray[0] ); + $this->assertSame( $second, $envelopesArray[1] ); + } + + public function testFilters() { + $first = new NotificationEnvelope( new Notification( 'first' ), new RecipientSet( [] ) ); + $second = new NotificationEnvelope( new Notification( 'second' ), new RecipientSet( [] ) ); + + $filterMock = $this->getMockBuilder( stdClass::class ) + ->addMethods( [ '__invoke' ] ) + ->getMock(); + + $filterMock->expects( $this->exactly( 2 ) ) + ->method( '__invoke' ) + ->willReturnCallback( static function ( NotificationEnvelope $envelope ) use ( $first ) { + return $envelope->equals( $first ); + } ); + + $batch = new NotificationsBatch( $first, $second ); + $batch->filter( $filterMock ); + $this->assertCount( 1, $batch ); + $envelopesArray = iterator_to_array( $batch ); + $this->assertSame( $first, $envelopesArray[0] ); + } + +} diff --git a/tests/phpunit/unit/includes/Notification/NotificationServiceTest.php b/tests/phpunit/unit/includes/Notification/NotificationServiceTest.php index 242a3ac6f515..e62a1be23e41 100644 --- a/tests/phpunit/unit/includes/Notification/NotificationServiceTest.php +++ b/tests/phpunit/unit/includes/Notification/NotificationServiceTest.php @@ -2,6 +2,7 @@ namespace MediaWiki\Tests\Notification; +use MediaWiki\Notification\MiddlewareChain; use MediaWiki\Notification\Notification; use MediaWiki\Notification\NotificationHandler; use MediaWiki\Notification\NotificationService; @@ -16,6 +17,36 @@ use RuntimeException; */ class NotificationServiceTest extends MediaWikiUnitTestCase { + protected function getEmptyMiddleware() { + $middleware = $this->createMock( MiddlewareChain::class ); + $middleware->expects( $this->any() ) + ->method( 'process' ) + ->willReturnArgument( 0 ); + return $middleware; + } + + public function testTriggersMiddleware() { + $notification = new Notification( 'test.middleware' ); + $recipients = new RecipientSet( [] ); + $middleware = $this->createMock( MiddlewareChain::class ); + $middleware->expects( $this->once() ) + ->method( 'process' ) + ->willReturnCallback( function ( $batch ) { + $envelopes = iterator_to_array( $batch ); + $this->assertCount( 1, $envelopes ); + $this->assertSame( 'test.middleware', $envelopes[0]->getNotification()->getType() ); + return $batch; + } ); + + $svc = new NotificationService( + new NullLogger(), + $this->createSimpleObjectFactory(), + $middleware, + [] + ); + $svc->notify( $notification, $recipients ); + } + public function testBasic() { $recipients = new RecipientSet( [] ); @@ -34,6 +65,7 @@ class NotificationServiceTest extends MediaWikiUnitTestCase { $svc = new NotificationService( new NullLogger(), $this->createSimpleObjectFactory(), + $this->getEmptyMiddleware(), [ [ 'types' => [ 'A' ], 'factory' => static fn () => $handlerA ], [ 'types' => [ '*' ], 'factory' => static fn () => $handlerB ], @@ -56,6 +88,7 @@ class NotificationServiceTest extends MediaWikiUnitTestCase { $svc = new NotificationService( new NullLogger(), $this->createSimpleObjectFactory(), + $this->getEmptyMiddleware(), [ [ 'types' => [ 'A/*' ], 'factory' => static fn () => $handler ], ] @@ -74,6 +107,7 @@ class NotificationServiceTest extends MediaWikiUnitTestCase { $svc = new NotificationService( new NullLogger(), $this->createSimpleObjectFactory(), + $this->getEmptyMiddleware(), [ [ 'types' => [ '*' ], 'factory' => static fn () => $handler ], [ 'types' => [ '*' ], 'factory' => static fn () => $handler ], @@ -93,6 +127,7 @@ class NotificationServiceTest extends MediaWikiUnitTestCase { $svc = new NotificationService( $mockLogger, $this->createSimpleObjectFactory(), + $this->getEmptyMiddleware(), [ [ 'types' => [ 'B' ], 'factory' => static fn () => $handler ], ] diff --git a/tests/phpunit/unit/includes/Notification/SuppressNotificationByTypeMiddlewareTest.php b/tests/phpunit/unit/includes/Notification/SuppressNotificationByTypeMiddlewareTest.php new file mode 100644 index 000000000000..ce56c776b088 --- /dev/null +++ b/tests/phpunit/unit/includes/Notification/SuppressNotificationByTypeMiddlewareTest.php @@ -0,0 +1,40 @@ +<?php + +namespace MediaWiki\Tests\Notification; + +use MediaWiki\Notification\Middleware\SuppressNotificationByTypeMiddleware; +use MediaWiki\Notification\Notification; +use MediaWiki\Notification\NotificationEnvelope; +use MediaWiki\Notification\NotificationsBatch; +use MediaWiki\Notification\RecipientSet; +use MediaWikiUnitTestCase; + +/** + * @covers \MediaWiki\Notification\NotificationService + */ +class SuppressNotificationByTypeMiddlewareTest extends MediaWikiUnitTestCase { + + public function testRemovesNotificationTest() { + $typeToRemove = 'test'; + $sut = new SuppressNotificationByTypeMiddleware( $typeToRemove ); + $notificationA = new Notification( 'first' ); + $notificationB = new Notification( $typeToRemove ); + $notificationC = new Notification( 'last' ); + $recipients = new RecipientSet( [] ); + $test = $this; + + $batch = new NotificationsBatch( + new NotificationEnvelope( $notificationA, $recipients ), + new NotificationEnvelope( $notificationB, $recipients ), + new NotificationEnvelope( $notificationC, $recipients ) + ); + + $sut->handle( $batch, static function () use ( $test, $batch ) { + $test->assertCount( 2, $batch ); + $envelopes = iterator_to_array( $batch ); + $test->assertSame( 'first', $envelopes[0]->getNotification()->getType() ); + $test->assertSame( 'last', $envelopes[1]->getNotification()->getType() ); + } ); + $this->assertCount( 2, $batch ); + } +} |