Merge "Drop cache interwiki"
[mediawiki.git] / tests / phpunit / includes / api / query / ApiQueryTest.php
bloba0f7e392636c2980b512d4d85fdc86d9d2c066e0
1 <?php
3 namespace MediaWiki\Tests\Api\Query;
5 use MediaWiki\Api\ApiMain;
6 use MediaWiki\Api\ApiModuleManager;
7 use MediaWiki\Api\ApiQuery;
8 use MediaWiki\Api\ApiUsageException;
9 use MediaWiki\MainConfigNames;
10 use MediaWiki\Request\FauxRequest;
11 use MediaWiki\Tests\Api\ApiTestCase;
12 use MediaWiki\Tests\Api\MockApiQueryBase;
13 use MediaWiki\Tests\Unit\DummyServicesTrait;
14 use MediaWiki\Title\Title;
15 use Wikimedia\TestingAccessWrapper;
17 /**
18 * @group API
19 * @group Database
20 * @group medium
21 * @covers \MediaWiki\Api\ApiQuery
23 class ApiQueryTest extends ApiTestCase {
24 use DummyServicesTrait;
26 protected function setUp(): void {
27 parent::setUp();
29 // Setup apiquerytestiw: as interwiki prefix
30 $interwikiLookup = $this->getDummyInterwikiLookup( [
31 [ 'iw_prefix' => 'apiquerytestiw', 'iw_url' => 'wikipedia' ],
32 ] );
33 $this->setService( 'InterwikiLookup', $interwikiLookup );
36 public function testTitlesGetNormalized() {
37 $this->overrideConfigValues( [
38 MainConfigNames::CapitalLinks => true,
39 MainConfigNames::MetaNamespace => 'TestWiki',
40 ] );
42 $data = $this->doApiRequest( [
43 'action' => 'query',
44 'titles' => 'Project:articleA|article_B' ] );
46 $this->assertArrayHasKey( 'query', $data[0] );
47 $this->assertArrayHasKey( 'normalized', $data[0]['query'] );
49 $this->assertEquals(
51 'fromencoded' => false,
52 'from' => 'Project:articleA',
53 'to' => 'TestWiki:ArticleA',
55 $data[0]['query']['normalized'][0]
58 $this->assertEquals(
60 'fromencoded' => false,
61 'from' => 'article_B',
62 'to' => 'Article B'
64 $data[0]['query']['normalized'][1]
68 public function testTitlesAreRejectedIfInvalid() {
69 $title = false;
70 while ( !$title || Title::newFromText( $title )->exists() ) {
71 $title = md5( mt_rand( 0, 100_000 ) );
74 $data = $this->doApiRequest( [
75 'action' => 'query',
76 'titles' => $title . '|Talk:' ] );
78 $this->assertArrayHasKey( 'query', $data[0] );
79 $this->assertArrayHasKey( 'pages', $data[0]['query'] );
80 $this->assertCount( 2, $data[0]['query']['pages'] );
82 $this->assertArrayHasKey( -2, $data[0]['query']['pages'] );
83 $this->assertArrayHasKey( -1, $data[0]['query']['pages'] );
85 $this->assertArrayHasKey( 'missing', $data[0]['query']['pages'][-2] );
86 $this->assertArrayHasKey( 'invalid', $data[0]['query']['pages'][-1] );
89 public function testTitlesWithWhitespaces() {
90 $data = $this->doApiRequest( [
91 'action' => 'query',
92 'titles' => ' '
93 ] );
95 $this->assertArrayHasKey( 'query', $data[0] );
96 $this->assertArrayHasKey( 'pages', $data[0]['query'] );
97 $this->assertCount( 1, $data[0]['query']['pages'] );
98 $this->assertArrayHasKey( -1, $data[0]['query']['pages'] );
99 $this->assertArrayHasKey( 'invalid', $data[0]['query']['pages'][-1] );
103 * Test the ApiBase::titlePartToKey function
105 * @param string $titlePart
106 * @param int $namespace
107 * @param string $expected
108 * @param string $expectException
109 * @dataProvider provideTestTitlePartToKey
111 public function testTitlePartToKey( $titlePart, $namespace, $expected, $expectException ) {
112 $this->overrideConfigValue( MainConfigNames::CapitalLinks, true );
114 $api = new MockApiQueryBase();
115 $exceptionCaught = false;
116 try {
117 $this->assertEquals( $expected, $api->titlePartToKey( $titlePart, $namespace ) );
118 } catch ( ApiUsageException $e ) {
119 $exceptionCaught = true;
121 $this->assertEquals( $expectException, $exceptionCaught,
122 'ApiUsageException thrown by titlePartToKey' );
125 public static function provideTestTitlePartToKey() {
126 return [
127 [ 'a b c', NS_MAIN, 'A_b_c', false ],
128 [ 'x', NS_MAIN, 'X', false ],
129 [ 'y ', NS_MAIN, 'Y_', false ],
130 [ 'template:foo', NS_CATEGORY, 'Template:foo', false ],
131 [ 'apiquerytestiw:foo', NS_CATEGORY, 'Apiquerytestiw:foo', false ],
132 [ "\xF7", NS_MAIN, null, true ],
133 [ 'template:foo', NS_MAIN, null, true ],
134 [ 'apiquerytestiw:foo', NS_MAIN, null, true ],
139 * Test if all classes in the query module manager exists
141 public function testClassNamesInModuleManager() {
142 $api = new ApiMain(
143 new FauxRequest( [ 'action' => 'query', 'meta' => 'siteinfo' ] )
145 $queryApi = $api->getModuleManager()->getModule( 'query' );
146 $modules = $queryApi->getModuleManager()->getNamesWithClasses();
148 foreach ( $modules as $name => $class ) {
149 $this->assertTrue(
150 class_exists( $class ),
151 'Class ' . $class . ' for api module ' . $name . ' does not exist (with exact case)'
156 public function testShouldNotExportPagesThatUserCanNotRead() {
157 $title = Title::makeTitle( NS_MAIN, 'Test article' );
158 $this->insertPage( $title );
160 $this->setTemporaryHook( 'getUserPermissionsErrors',
161 static function ( Title $page, &$user, $action, &$result ) use ( $title ) {
162 if ( $page->equals( $title ) && $action === 'read' ) {
163 $result = false;
164 return false;
166 } );
168 $data = $this->doApiRequest( [
169 'action' => 'query',
170 'titles' => $title->getPrefixedText(),
171 'export' => 1,
172 ] );
174 $this->assertArrayHasKey( 'query', $data[0] );
175 $this->assertArrayHasKey( 'export', $data[0]['query'] );
176 // This response field contains an XML document even if no pages were exported
177 $this->assertStringNotContainsString( $title->getPrefixedText(), $data[0]['query']['export'] );
180 public function testIsReadMode() {
181 $api = new ApiMain(
182 new FauxRequest( [ 'action' => 'query', 'meta' => 'tokens', 'type' => 'login' ] )
184 $queryApi = $api->getModuleManager()->getModule( 'query' );
185 $this->assertFalse( $queryApi->isReadMode(),
186 'isReadMode() => false when meta=tokens is the only module' );
188 $api = new ApiMain( new FauxRequest( [
189 'action' => 'query', 'meta' => 'tokens', 'type' => 'login', 'rawcontinue' => 1,
190 'indexpageids' => 1
193 $queryApi = $api->getModuleManager()->getModule( 'query' );
194 $this->assertFalse( $queryApi->isReadMode(),
195 'rawcontinue and indexpageids are also allowed' );
197 $api = new ApiMain(
198 new FauxRequest( [ 'action' => 'query', 'meta' => 'tokens|siteinfo', 'type' => 'login' ] )
200 $queryApi = $api->getModuleManager()->getModule( 'query' );
201 $this->assertTrue( $queryApi->isReadMode(),
202 'isReadMode() => true when other meta modules are present' );
204 $api = new ApiMain( new FauxRequest( [
205 'action' => 'query', 'meta' => 'tokens', 'type' => 'login', 'list' => 'allpages'
206 ] ) );
207 $queryApi = $api->getModuleManager()->getModule( 'query' );
208 $this->assertTrue( $queryApi->isReadMode(),
209 'isReadMode() => true when other modules are present' );
211 $api = new ApiMain( new FauxRequest( [
212 'action' => 'query', 'meta' => 'tokens', 'type' => 'login', 'titles' => 'Foo'
213 ] ) );
214 $queryApi = $api->getModuleManager()->getModule( 'query' );
215 $this->assertTrue( $queryApi->isReadMode(),
216 'isReadMode() => true when other ApiQuery parameters are present' );
218 $api = new ApiMain( new FauxRequest( [ 'action' => 'query' ] ) );
219 $queryApi = $api->getModuleManager()->getModule( 'query' );
220 $this->assertTrue( $queryApi->isReadMode(),
221 'isReadMode() => true when no modules are requested' );
224 /** @dataProvider provideIsWriteMode */
225 public function testIsWriteMode( $queryParams, $expected ) {
226 $api = new ApiMain( new FauxRequest( array_merge( [ 'action' => 'query' ], $queryParams ) ) );
227 $queryApi = $api->getModuleManager()->getModule( 'query' );
228 $this->assertSame(
229 $expected,
230 $queryApi->isWriteMode(),
231 '::isWriteMode did not return the expected value.'
235 public static function provideIsWriteMode() {
236 return [
237 'No modules specified' => [ [], false ],
238 'Only meta=tokens' => [ [ 'meta' => 'tokens', 'type' => 'login' ], false ],
242 public function testIsWriteModeForMockedModule() {
243 $queryApi = $this->getMockBuilder( ApiQuery::class )
244 ->disableOriginalConstructor()
245 ->onlyMethods( [ 'extractRequestParams' ] )
246 ->getMock();
247 // We need to mock ::extractRequestParams because we mock the module manager
248 // and using the original implementation results in a test failure.
249 $queryApi->method( 'extractRequestParams' )->willReturn( [
250 'list' => [ 'mocked-module' ]
251 ] );
252 // Mock $queryApi->mModuleMgr to return always return a mock module that returns true from ::isWriteMode
253 $mockModuleManager = $this->createMock( ApiModuleManager::class );
254 $mockModuleManager->method( 'getModule' )->willReturnCallback( function ( $name ) {
255 $module = $this->createMock( MockApiQueryBase::class );
256 $module->method( 'isWriteMode' )
257 ->willReturn( true );
258 return $module;
259 } );
260 $queryApi = TestingAccessWrapper::newFromObject( $queryApi );
261 $queryApi->mModuleMgr = $mockModuleManager;
262 $this->assertTrue( $queryApi->isWriteMode(), '::isWriteMode did not return the expected value.' );