Merge "StringUtils: Add a utility for checking if a string is a valid regex"
[lhc/web/wiklou.git] / tests / phpunit / includes / api / ApiPageSetTest.php
1 <?php
2
3 use MediaWiki\MediaWikiServices;
4 use Wikimedia\TestingAccessWrapper;
5
6 /**
7 * @group API
8 * @group medium
9 * @group Database
10 * @covers ApiPageSet
11 */
12 class ApiPageSetTest extends ApiTestCase {
13 public static function provideRedirectMergePolicy() {
14 return [
15 'By default nothing is merged' => [
16 null,
17 []
18 ],
19
20 'A simple merge policy adds the redirect data in' => [
21 function ( $current, $new ) {
22 if ( !isset( $current['index'] ) || $new['index'] < $current['index'] ) {
23 $current['index'] = $new['index'];
24 }
25 return $current;
26 },
27 [ 'index' => 1 ],
28 ],
29 ];
30 }
31
32 /**
33 * @dataProvider provideRedirectMergePolicy
34 */
35 public function testRedirectMergePolicyWithArrayResult( $mergePolicy, $expect ) {
36 list( $target, $pageSet ) = $this->createPageSetWithRedirect();
37 $pageSet->setRedirectMergePolicy( $mergePolicy );
38 $result = [
39 $target->getArticleID() => []
40 ];
41 $pageSet->populateGeneratorData( $result );
42 $this->assertEquals( $expect, $result[$target->getArticleID()] );
43 }
44
45 /**
46 * @dataProvider provideRedirectMergePolicy
47 */
48 public function testRedirectMergePolicyWithApiResult( $mergePolicy, $expect ) {
49 list( $target, $pageSet ) = $this->createPageSetWithRedirect();
50 $pageSet->setRedirectMergePolicy( $mergePolicy );
51 $result = new ApiResult( false );
52 $result->addValue( null, 'pages', [
53 $target->getArticleID() => []
54 ] );
55 $pageSet->populateGeneratorData( $result, [ 'pages' ] );
56 $this->assertEquals(
57 $expect,
58 $result->getResultData( [ 'pages', $target->getArticleID() ] )
59 );
60 }
61
62 protected function createPageSetWithRedirect() {
63 $target = Title::makeTitle( NS_MAIN, 'UTRedirectTarget' );
64 $sourceA = Title::makeTitle( NS_MAIN, 'UTRedirectSourceA' );
65 $sourceB = Title::makeTitle( NS_MAIN, 'UTRedirectSourceB' );
66 self::editPage( 'UTRedirectTarget', 'api page set test' );
67 self::editPage( 'UTRedirectSourceA', '#REDIRECT [[UTRedirectTarget]]' );
68 self::editPage( 'UTRedirectSourceB', '#REDIRECT [[UTRedirectTarget]]' );
69
70 $request = new FauxRequest( [ 'redirects' => 1 ] );
71 $context = new RequestContext();
72 $context->setRequest( $request );
73 $main = new ApiMain( $context );
74 $pageSet = new ApiPageSet( $main );
75
76 $pageSet->setGeneratorData( $sourceA, [ 'index' => 1 ] );
77 $pageSet->setGeneratorData( $sourceB, [ 'index' => 3 ] );
78 $pageSet->populateFromTitles( [ $sourceA, $sourceB ] );
79
80 return [ $target, $pageSet ];
81 }
82
83 public function testHandleNormalization() {
84 $context = new RequestContext();
85 $context->setRequest( new FauxRequest( [ 'titles' => "a|B|a\xcc\x8a" ] ) );
86 $main = new ApiMain( $context );
87 $pageSet = new ApiPageSet( $main );
88 $pageSet->execute();
89
90 $this->assertSame(
91 [ 0 => [ 'A' => -1, 'B' => -2, 'Å' => -3 ] ],
92 $pageSet->getAllTitlesByNamespace()
93 );
94 $this->assertSame(
95 [
96 [ 'fromencoded' => true, 'from' => 'a%CC%8A', 'to' => 'å' ],
97 [ 'fromencoded' => false, 'from' => 'a', 'to' => 'A' ],
98 [ 'fromencoded' => false, 'from' => 'å', 'to' => 'Å' ],
99 ],
100 $pageSet->getNormalizedTitlesAsResult()
101 );
102 }
103
104 public function testSpecialRedirects() {
105 $id1 = self::editPage( 'UTApiPageSet', 'UTApiPageSet in the default language' )
106 ->value['revision']->getTitle()->getArticleID();
107 $id2 = self::editPage( 'UTApiPageSet/de', 'UTApiPageSet in German' )
108 ->value['revision']->getTitle()->getArticleID();
109
110 $user = $this->getTestUser()->getUser();
111 $userName = $user->getName();
112 $userDbkey = str_replace( ' ', '_', $userName );
113 $request = new FauxRequest( [
114 'titles' => implode( '|', [
115 'Special:MyContributions',
116 'Special:MyPage',
117 'Special:MyTalk/subpage',
118 'Special:MyLanguage/UTApiPageSet',
119 ] ),
120 ] );
121 $context = new RequestContext();
122 $context->setRequest( $request );
123 $context->setUser( $user );
124
125 $main = new ApiMain( $context );
126 $pageSet = new ApiPageSet( $main );
127 $pageSet->execute();
128
129 $this->assertEquals( [
130 ], $pageSet->getRedirectTitlesAsResult() );
131 $this->assertEquals( [
132 [ 'ns' => -1, 'title' => 'Special:MyContributions', 'special' => true ],
133 [ 'ns' => -1, 'title' => 'Special:MyPage', 'special' => true ],
134 [ 'ns' => -1, 'title' => 'Special:MyTalk/subpage', 'special' => true ],
135 [ 'ns' => -1, 'title' => 'Special:MyLanguage/UTApiPageSet', 'special' => true ],
136 ], $pageSet->getInvalidTitlesAndRevisions() );
137 $this->assertEquals( [
138 ], $pageSet->getAllTitlesByNamespace() );
139
140 $request->setVal( 'redirects', 1 );
141 $main = new ApiMain( $context );
142 $pageSet = new ApiPageSet( $main );
143 $pageSet->execute();
144
145 $this->assertEquals( [
146 [ 'from' => 'Special:MyPage', 'to' => "User:$userName" ],
147 [ 'from' => 'Special:MyTalk/subpage', 'to' => "User talk:$userName/subpage" ],
148 [ 'from' => 'Special:MyLanguage/UTApiPageSet', 'to' => 'UTApiPageSet' ],
149 ], $pageSet->getRedirectTitlesAsResult() );
150 $this->assertEquals( [
151 [ 'ns' => -1, 'title' => 'Special:MyContributions', 'special' => true ],
152 [ 'ns' => 2, 'title' => "User:$userName", 'missing' => true ],
153 [ 'ns' => 3, 'title' => "User talk:$userName/subpage", 'missing' => true ],
154 ], $pageSet->getInvalidTitlesAndRevisions() );
155 $this->assertEquals( [
156 0 => [ 'UTApiPageSet' => $id1 ],
157 2 => [ $userDbkey => -2 ],
158 3 => [ "$userDbkey/subpage" => -3 ],
159 ], $pageSet->getAllTitlesByNamespace() );
160
161 $context->setLanguage( 'de' );
162 $main = new ApiMain( $context );
163 $pageSet = new ApiPageSet( $main );
164 $pageSet->execute();
165
166 $this->assertEquals( [
167 [ 'from' => 'Special:MyPage', 'to' => "User:$userName" ],
168 [ 'from' => 'Special:MyTalk/subpage', 'to' => "User talk:$userName/subpage" ],
169 [ 'from' => 'Special:MyLanguage/UTApiPageSet', 'to' => 'UTApiPageSet/de' ],
170 ], $pageSet->getRedirectTitlesAsResult() );
171 $this->assertEquals( [
172 [ 'ns' => -1, 'title' => 'Special:MyContributions', 'special' => true ],
173 [ 'ns' => 2, 'title' => "User:$userName", 'missing' => true ],
174 [ 'ns' => 3, 'title' => "User talk:$userName/subpage", 'missing' => true ],
175 ], $pageSet->getInvalidTitlesAndRevisions() );
176 $this->assertEquals( [
177 0 => [ 'UTApiPageSet/de' => $id2 ],
178 2 => [ $userDbkey => -2 ],
179 3 => [ "$userDbkey/subpage" => -3 ],
180 ], $pageSet->getAllTitlesByNamespace() );
181 }
182
183 /**
184 * Test that ApiPageSet is calling GenderCache for provided user names to prefill the
185 * GenderCache and avoid a performance issue when loading each users' gender on it's own.
186 * The test is setting the "missLimit" to 0 on the GenderCache to trigger misses logic.
187 * When the "misses" property is no longer 0 at the end of the test,
188 * something was requested which is not part of the cache. Than the test is failing.
189 */
190 public function testGenderCaching() {
191 // Set up the user namespace to have gender aliases to trigger the gender cache
192 $this->setMwGlobals( [
193 'wgExtraGenderNamespaces' => [ NS_USER => [ 'male' => 'Male', 'female' => 'Female' ] ]
194 ] );
195 $this->overrideMwServices();
196
197 // User names to test with - it is not needed that the user exists in the database
198 // to trigger gender cache
199 $userNames = [
200 'Female',
201 'Unknown',
202 'Male',
203 ];
204
205 // Prepare the gender cache for testing - this is a fresh instance due to service override
206 $genderCache = TestingAccessWrapper::newFromObject(
207 MediaWikiServices::getInstance()->getGenderCache()
208 );
209 $genderCache->missLimit = 0;
210
211 // Do an api request to trigger ApiPageSet code
212 $this->doApiRequest( [
213 'action' => 'query',
214 'titles' => 'User:' . implode( '|User:', $userNames ),
215 ] );
216
217 $this->assertEquals( 0, $genderCache->misses,
218 'ApiPageSet does not prefill the gender cache correctly' );
219 $this->assertEquals( $userNames, array_keys( $genderCache->cache ),
220 'ApiPageSet does not prefill all users into the gender cache' );
221 }
222 }