disallowDBAccess(); $this->disallowHttpAccess(); } final protected function getExtensionJson(): array { if ( !array_key_exists( $this->extensionJsonPath, self::$extensionJsonCache ) ) { self::$extensionJsonCache[$this->extensionJsonPath] = json_decode( file_get_contents( $this->extensionJsonPath ), true, 512, JSON_THROW_ON_ERROR ); } return self::$extensionJsonCache[$this->extensionJsonPath]; } /** @dataProvider provideHookHandlerNames */ public function testHookHandler( string $hookHandlerName ): void { $specification = $this->getExtensionJson()['HookHandlers'][$hookHandlerName]; $objectFactory = MediaWikiServices::getInstance()->getObjectFactory(); $objectFactory->createObject( $specification, [ 'allowClassName' => true, ] ); $this->assertTrue( true ); } public function provideHookHandlerNames(): iterable { foreach ( $this->getExtensionJson()['HookHandlers'] ?? [] as $hookHandlerName => $specification ) { yield [ $hookHandlerName ]; } } /** @dataProvider provideContentModelIDs */ public function testContentHandler( string $contentModelID ): void { $specification = $this->getExtensionJson()['ContentHandlers'][$contentModelID]; $objectFactory = MediaWikiServices::getInstance()->getObjectFactory(); $objectFactory->createObject( $specification, [ 'assertClass' => ContentHandler::class, 'allowCallable' => true, 'allowClassName' => true, 'extraArgs' => [ $contentModelID ], ] ); $this->assertTrue( true ); } public function provideContentModelIDs(): iterable { foreach ( $this->getExtensionJson()['ContentHandlers'] ?? [] as $contentModelID => $specification ) { yield [ $contentModelID ]; } } /** @dataProvider provideApiModuleNames */ public function testApiModule( string $moduleName ): void { $specification = $this->getExtensionJson()['APIModules'][$moduleName]; $objectFactory = MediaWikiServices::getInstance()->getObjectFactory(); $objectFactory->createObject( $specification, [ 'allowClassName' => true, 'extraArgs' => [ $this->mockApiMain(), 'modulename' ], ] ); $this->assertTrue( true ); } public function provideApiModuleNames(): iterable { foreach ( $this->getExtensionJson()['APIModules'] ?? [] as $moduleName => $specification ) { yield [ $moduleName ]; } } /** @dataProvider provideApiQueryModuleListsAndNames */ public function testApiQueryModule( string $moduleList, string $moduleName ): void { $specification = $this->getExtensionJson()[$moduleList][$moduleName]; $objectFactory = MediaWikiServices::getInstance()->getObjectFactory(); $objectFactory->createObject( $specification, [ 'allowClassName' => true, 'extraArgs' => [ $this->mockApiQuery(), 'query' ], ] ); $this->assertTrue( true ); } public function provideApiQueryModuleListsAndNames(): iterable { foreach ( [ 'APIListModules', 'APIMetaModules', 'APIPropModules' ] as $moduleList ) { foreach ( $this->getExtensionJson()[$moduleList] ?? [] as $moduleName => $specification ) { yield [ $moduleList, $moduleName ]; } } } /** @dataProvider provideSpecialPageNames */ public function testSpecialPage( string $specialPageName ): void { $specification = $this->getExtensionJson()['SpecialPages'][$specialPageName]; $objectFactory = MediaWikiServices::getInstance()->getObjectFactory(); $objectFactory->createObject( $specification, [ 'allowClassName' => true, ] ); $this->assertTrue( true ); } public function provideSpecialPageNames(): iterable { foreach ( $this->getExtensionJson()['SpecialPages'] ?? [] as $specialPageName => $specification ) { yield [ $specialPageName ]; } } /** @dataProvider provideAuthenticationProviders */ public function testAuthenticationProviders( string $providerType, string $providerName, string $providerClass ): void { $specification = $this->getExtensionJson()['AuthManagerAutoConfig'][$providerType][$providerName]; $objectFactory = MediaWikiServices::getInstance()->getObjectFactory(); $objectFactory->createObject( $specification, [ 'assertClass' => $providerClass, ] ); $this->assertTrue( true ); } public function provideAuthenticationProviders(): iterable { $config = $this->getExtensionJson()['AuthManagerAutoConfig'] ?? []; $types = [ 'preauth' => PreAuthenticationProvider::class, 'primaryauth' => PrimaryAuthenticationProvider::class, 'secondaryauth' => SecondaryAuthenticationProvider::class, ]; foreach ( $types as $providerType => $providerClass ) { foreach ( $config[$providerType] ?? [] as $providerName => $specification ) { yield [ $providerType, $providerName, $providerClass ]; } } } /** @dataProvider provideSessionProviders */ public function testSessionProviders( string $providerName ): void { $specification = $this->getExtensionJson()['SessionProviders'][$providerName]; $objectFactory = MediaWikiServices::getInstance()->getObjectFactory(); $objectFactory->createObject( $specification ); $this->assertTrue( true ); } public function provideSessionProviders(): iterable { foreach ( $this->getExtensionJson()['SessionProviders'] ?? [] as $providerName => $specification ) { yield [ $providerName ]; } } /** @dataProvider provideServicesLists */ public function testServicesSorted( array $services ): void { $sortedServices = $services; usort( $sortedServices, function ( $serviceA, $serviceB ) { $isExtensionServiceA = str_starts_with( $serviceA, $this->serviceNamePrefix ); $isExtensionServiceB = str_starts_with( $serviceB, $this->serviceNamePrefix ); if ( $isExtensionServiceA !== $isExtensionServiceB ) { return $isExtensionServiceA ? 1 : -1; } return strcmp( $serviceA, $serviceB ); } ); $this->assertSame( $sortedServices, $services, 'Services should be sorted: first all MediaWiki services, ' . "then all {$this->serviceNamePrefix}* ones." ); } public function provideServicesLists(): iterable { if ( $this->serviceNamePrefix === null ) { return; // do not test sorting } foreach ( $this->provideSpecifications() as $name => $specification ) { if ( is_array( $specification ) && array_key_exists( 'services', $specification ) ) { yield $name => [ $specification['services'] ]; } } } public function provideSpecifications(): iterable { foreach ( $this->provideHookHandlerNames() as [ $hookHandlerName ] ) { yield "HookHandlers/$hookHandlerName" => $this->getExtensionJson()['HookHandlers'][$hookHandlerName]; } foreach ( $this->provideContentModelIDs() as [ $contentModelID ] ) { yield "ContentHandlers/$contentModelID" => $this->getExtensionJson()['ContentHandlers'][$contentModelID]; } foreach ( $this->provideApiModuleNames() as [ $moduleName ] ) { yield "APIModules/$moduleName" => $this->getExtensionJson()['APIModules'][$moduleName]; } foreach ( $this->provideApiQueryModuleListsAndNames() as [ $moduleList, $moduleName ] ) { yield "$moduleList/$moduleName" => $this->getExtensionJson()[$moduleList][$moduleName]; } foreach ( $this->provideSpecialPageNames() as [ $specialPageName ] ) { yield "SpecialPages/$specialPageName" => $this->getExtensionJson()['SpecialPages'][$specialPageName]; } foreach ( $this->provideAuthenticationProviders() as [ $providerType, $providerName, $providerClass ] ) { yield "AuthManagerAutoConfig/$providerType/$providerName" => $this->getExtensionJson()['AuthManagerAutoConfig'][$providerType][$providerName]; } foreach ( $this->provideSessionProviders() as [ $providerName ] ) { yield "SessionProviders/$providerName" => $this->getExtensionJson()['SessionProviders'][$providerName]; } } private function disallowDBAccess() { $this->setService( 'DBLoadBalancerFactory', function () { $lb = $this->createMock( ILoadBalancer::class ); $lb->expects( $this->never() ) ->method( 'getMaintenanceConnectionRef' ); $lb->method( 'getLocalDomainID' ) ->willReturn( 'banana' ); // This IDatabase will fail when actually trying to do database actions $db = $this->createNoOpMock( IDatabase::class ); $lb->method( 'getConnection' ) ->willReturn( $db ); $lbFactory = $this->createMock( LBFactory::class ); $lbFactory->method( 'getMainLB' ) ->willReturn( $lb ); $lbFactory->method( 'getLocalDomainID' ) ->willReturn( 'banana' ); return $lbFactory; } ); } private function disallowHttpAccess() { $this->setService( 'HttpRequestFactory', function () { $factory = $this->createMock( HttpRequestFactory::class ); $factory->expects( $this->never() ) ->method( 'create' ); $factory->expects( $this->never() ) ->method( 'request' ); $factory->expects( $this->never() ) ->method( 'get' ); $factory->expects( $this->never() ) ->method( 'post' ); $factory->method( 'createMultiClient' ) ->willReturn( $this->createMock( MultiHttpClient::class ) ); return $factory; } ); } private function mockApiMain(): ApiMain { $request = new FauxRequest(); $ctx = new ApiTestContext(); $ctx = $ctx->newTestContext( $request ); return new ApiMain( $ctx ); } private function mockApiQuery(): ApiQuery { return $this->mockApiMain()->getModuleManager()->getModule( 'query' ); } }