Merge "Update docs/hooks.txt for ShowSearchHitTitle"
[mediawiki.git] / tests / phpunit / includes / auth / AuthenticationRequestTest.php
blob7d2ba8d7494f591b6d94c3f4a3f3ef28d4cf5b33
1 <?php
3 namespace MediaWiki\Auth;
5 /**
6 * @group AuthManager
7 * @covers MediaWiki\Auth\AuthenticationRequest
8 */
9 class AuthenticationRequestTest extends \MediaWikiTestCase {
10 public function testBasics() {
11 $mock = $this->getMockForAbstractClass( AuthenticationRequest::class );
13 $this->assertSame( get_class( $mock ), $mock->getUniqueId() );
15 $this->assertType( 'array', $mock->getMetadata() );
17 $ret = $mock->describeCredentials();
18 $this->assertInternalType( 'array', $ret );
19 $this->assertArrayHasKey( 'provider', $ret );
20 $this->assertInstanceOf( 'Message', $ret['provider'] );
21 $this->assertArrayHasKey( 'account', $ret );
22 $this->assertInstanceOf( 'Message', $ret['account'] );
25 public function testLoadRequestsFromSubmission() {
26 $mb = $this->getMockBuilder( AuthenticationRequest::class )
27 ->setMethods( [ 'loadFromSubmission' ] );
29 $data = [ 'foo', 'bar' ];
31 $req1 = $mb->getMockForAbstractClass();
32 $req1->expects( $this->once() )->method( 'loadFromSubmission' )
33 ->with( $this->identicalTo( $data ) )
34 ->will( $this->returnValue( false ) );
36 $req2 = $mb->getMockForAbstractClass();
37 $req2->expects( $this->once() )->method( 'loadFromSubmission' )
38 ->with( $this->identicalTo( $data ) )
39 ->will( $this->returnValue( true ) );
41 $this->assertSame(
42 [ $req2 ],
43 AuthenticationRequest::loadRequestsFromSubmission( [ $req1, $req2 ], $data )
47 public function testGetRequestByClass() {
48 $mb = $this->getMockBuilder(
49 AuthenticationRequest::class, 'AuthenticationRequestTest_AuthenticationRequest2'
52 $reqs = [
53 $this->getMockForAbstractClass(
54 AuthenticationRequest::class, [], 'AuthenticationRequestTest_AuthenticationRequest1'
56 $mb->getMockForAbstractClass(),
57 $mb->getMockForAbstractClass(),
58 $this->getMockForAbstractClass(
59 PasswordAuthenticationRequest::class, [],
60 'AuthenticationRequestTest_PasswordAuthenticationRequest'
64 $this->assertNull( AuthenticationRequest::getRequestByClass(
65 $reqs, 'AuthenticationRequestTest_AuthenticationRequest0'
66 ) );
67 $this->assertSame( $reqs[0], AuthenticationRequest::getRequestByClass(
68 $reqs, 'AuthenticationRequestTest_AuthenticationRequest1'
69 ) );
70 $this->assertNull( AuthenticationRequest::getRequestByClass(
71 $reqs, 'AuthenticationRequestTest_AuthenticationRequest2'
72 ) );
73 $this->assertNull( AuthenticationRequest::getRequestByClass(
74 $reqs, PasswordAuthenticationRequest::class
75 ) );
76 $this->assertNull( AuthenticationRequest::getRequestByClass(
77 $reqs, 'ClassThatDoesNotExist'
78 ) );
80 $this->assertNull( AuthenticationRequest::getRequestByClass(
81 $reqs, 'AuthenticationRequestTest_AuthenticationRequest0', true
82 ) );
83 $this->assertSame( $reqs[0], AuthenticationRequest::getRequestByClass(
84 $reqs, 'AuthenticationRequestTest_AuthenticationRequest1', true
85 ) );
86 $this->assertNull( AuthenticationRequest::getRequestByClass(
87 $reqs, 'AuthenticationRequestTest_AuthenticationRequest2', true
88 ) );
89 $this->assertSame( $reqs[3], AuthenticationRequest::getRequestByClass(
90 $reqs, PasswordAuthenticationRequest::class, true
91 ) );
92 $this->assertNull( AuthenticationRequest::getRequestByClass(
93 $reqs, 'ClassThatDoesNotExist', true
94 ) );
97 public function testGetUsernameFromRequests() {
98 $mb = $this->getMockBuilder( AuthenticationRequest::class );
100 for ( $i = 0; $i < 3; $i++ ) {
101 $req = $mb->getMockForAbstractClass();
102 $req->expects( $this->any() )->method( 'getFieldInfo' )->will( $this->returnValue( [
103 'username' => [
104 'type' => 'string',
106 ] ) );
107 $reqs[] = $req;
110 $req = $mb->getMockForAbstractClass();
111 $req->expects( $this->any() )->method( 'getFieldInfo' )->will( $this->returnValue( [] ) );
112 $req->username = 'baz';
113 $reqs[] = $req;
115 $this->assertNull( AuthenticationRequest::getUsernameFromRequests( $reqs ) );
117 $reqs[1]->username = 'foo';
118 $this->assertSame( 'foo', AuthenticationRequest::getUsernameFromRequests( $reqs ) );
120 $reqs[0]->username = 'foo';
121 $reqs[2]->username = 'foo';
122 $this->assertSame( 'foo', AuthenticationRequest::getUsernameFromRequests( $reqs ) );
124 $reqs[1]->username = 'bar';
125 try {
126 AuthenticationRequest::getUsernameFromRequests( $reqs );
127 $this->fail( 'Expected exception not thrown' );
128 } catch ( \UnexpectedValueException $ex ) {
129 $this->assertSame(
130 'Conflicting username fields: "bar" from ' .
131 get_class( $reqs[1] ) . '::$username vs. "foo" from ' .
132 get_class( $reqs[0] ) . '::$username',
133 $ex->getMessage()
138 public function testMergeFieldInfo() {
139 $msg = wfMessage( 'foo' );
141 $req1 = $this->getMock( AuthenticationRequest::class );
142 $req1->required = AuthenticationRequest::REQUIRED;
143 $req1->expects( $this->any() )->method( 'getFieldInfo' )->will( $this->returnValue( [
144 'string1' => [
145 'type' => 'string',
146 'label' => $msg,
147 'help' => $msg,
149 'string2' => [
150 'type' => 'string',
151 'label' => $msg,
152 'help' => $msg,
154 'optional' => [
155 'type' => 'string',
156 'label' => $msg,
157 'help' => $msg,
158 'optional' => true,
160 'select' => [
161 'type' => 'select',
162 'options' => [ 'foo' => $msg, 'baz' => $msg ],
163 'label' => $msg,
164 'help' => $msg,
166 ] ) );
168 $req2 = $this->getMock( AuthenticationRequest::class );
169 $req2->required = AuthenticationRequest::REQUIRED;
170 $req2->expects( $this->any() )->method( 'getFieldInfo' )->will( $this->returnValue( [
171 'string1' => [
172 'type' => 'string',
173 'label' => $msg,
174 'help' => $msg,
175 'sensitive' => true,
177 'string3' => [
178 'type' => 'string',
179 'label' => $msg,
180 'help' => $msg,
182 'select' => [
183 'type' => 'select',
184 'options' => [ 'bar' => $msg, 'baz' => $msg ],
185 'label' => $msg,
186 'help' => $msg,
188 ] ) );
190 $req3 = $this->getMock( AuthenticationRequest::class );
191 $req3->required = AuthenticationRequest::REQUIRED;
192 $req3->expects( $this->any() )->method( 'getFieldInfo' )->will( $this->returnValue( [
193 'string1' => [
194 'type' => 'checkbox',
195 'label' => $msg,
196 'help' => $msg,
198 ] ) );
200 $req4 = $this->getMock( AuthenticationRequest::class );
201 $req4->required = AuthenticationRequest::REQUIRED;
202 $req4->expects( $this->any() )->method( 'getFieldInfo' )->will( $this->returnValue( [] ) );
204 // Basic combining
206 $fields = AuthenticationRequest::mergeFieldInfo( [ $req1 ] );
207 $expect = $req1->getFieldInfo();
208 foreach ( $expect as $name => &$options ) {
209 $options['optional'] = !empty( $options['optional'] );
210 $options['sensitive'] = !empty( $options['sensitive'] );
212 unset( $options );
213 $this->assertEquals( $expect, $fields );
215 $fields = AuthenticationRequest::mergeFieldInfo( [ $req1, $req4 ] );
216 $this->assertEquals( $expect, $fields );
218 try {
219 AuthenticationRequest::mergeFieldInfo( [ $req1, $req3 ] );
220 $this->fail( 'Expected exception not thrown' );
221 } catch ( \UnexpectedValueException $ex ) {
222 $this->assertSame(
223 'Field type conflict for "string1", "string" vs "checkbox"',
224 $ex->getMessage()
228 $fields = AuthenticationRequest::mergeFieldInfo( [ $req1, $req2 ] );
229 $expect += $req2->getFieldInfo();
230 $expect['string1']['sensitive'] = true;
231 $expect['string2']['optional'] = false;
232 $expect['string3']['optional'] = false;
233 $expect['string3']['sensitive'] = false;
234 $expect['select']['options']['bar'] = $msg;
235 $this->assertEquals( $expect, $fields );
237 // Combining with something not required
239 $req1->required = AuthenticationRequest::PRIMARY_REQUIRED;
241 $fields = AuthenticationRequest::mergeFieldInfo( [ $req1, $req2 ] );
242 $expect += $req2->getFieldInfo();
243 $expect['string1']['optional'] = false;
244 $expect['string1']['sensitive'] = true;
245 $expect['string3']['optional'] = false;
246 $expect['select']['optional'] = false;
247 $expect['select']['options']['bar'] = $msg;
248 $this->assertEquals( $expect, $fields );
250 $req2->required = AuthenticationRequest::PRIMARY_REQUIRED;
252 $fields = AuthenticationRequest::mergeFieldInfo( [ $req1, $req2 ] );
253 $expect = $req1->getFieldInfo() + $req2->getFieldInfo();
254 foreach ( $expect as $name => &$options ) {
255 $options['sensitive'] = !empty( $options['sensitive'] );
257 $expect['string1']['optional'] = false;
258 $expect['string1']['sensitive'] = true;
259 $expect['string2']['optional'] = true;
260 $expect['string3']['optional'] = true;
261 $expect['select']['optional'] = false;
262 $expect['select']['options']['bar'] = $msg;
263 $this->assertEquals( $expect, $fields );
267 * @dataProvider provideLoadFromSubmission
268 * @param array $fieldInfo
269 * @param array $data
270 * @param array|bool $expectState
272 public function testLoadFromSubmission( $fieldInfo, $data, $expectState ) {
273 $mock = $this->getMockForAbstractClass( AuthenticationRequest::class );
274 $mock->expects( $this->any() )->method( 'getFieldInfo' )
275 ->will( $this->returnValue( $fieldInfo ) );
277 $ret = $mock->loadFromSubmission( $data );
278 if ( is_array( $expectState ) ) {
279 $this->assertTrue( $ret );
280 $expect = call_user_func( [ get_class( $mock ), '__set_state' ], $expectState );
281 $this->assertEquals( $expect, $mock );
282 } else {
283 $this->assertFalse( $ret );
287 public static function provideLoadFromSubmission() {
288 return [
289 'No fields' => [
291 $data = [ 'foo' => 'bar' ],
292 false
295 'Simple field' => [
297 'field' => [
298 'type' => 'string',
301 $data = [ 'field' => 'string!' ],
302 $data
304 'Simple field, not supplied' => [
306 'field' => [
307 'type' => 'string',
311 false
313 'Simple field, empty' => [
315 'field' => [
316 'type' => 'string',
319 [ 'field' => '' ],
320 false
322 'Simple field, optional, not supplied' => [
324 'field' => [
325 'type' => 'string',
326 'optional' => true,
330 false
332 'Simple field, optional, empty' => [
334 'field' => [
335 'type' => 'string',
336 'optional' => true,
339 $data = [ 'field' => '' ],
340 $data
343 'Checkbox, checked' => [
345 'check' => [
346 'type' => 'checkbox',
349 [ 'check' => '' ],
350 [ 'check' => true ]
352 'Checkbox, unchecked' => [
354 'check' => [
355 'type' => 'checkbox',
359 false
361 'Checkbox, optional, unchecked' => [
363 'check' => [
364 'type' => 'checkbox',
365 'optional' => true,
369 [ 'check' => false ]
372 'Button, used' => [
374 'push' => [
375 'type' => 'button',
378 [ 'push' => '' ],
379 [ 'push' => true ]
381 'Button, unused' => [
383 'push' => [
384 'type' => 'button',
388 false
390 'Button, optional, unused' => [
392 'push' => [
393 'type' => 'button',
394 'optional' => true,
398 [ 'push' => false ]
400 'Button, image-style' => [
402 'push' => [
403 'type' => 'button',
406 [ 'push_x' => 0, 'push_y' => 0 ],
407 [ 'push' => true ]
410 'Select' => [
412 'choose' => [
413 'type' => 'select',
414 'options' => [
415 'foo' => wfMessage( 'mainpage' ),
416 'bar' => wfMessage( 'mainpage' ),
420 $data = [ 'choose' => 'foo' ],
421 $data
423 'Select, invalid choice' => [
425 'choose' => [
426 'type' => 'select',
427 'options' => [
428 'foo' => wfMessage( 'mainpage' ),
429 'bar' => wfMessage( 'mainpage' ),
433 $data = [ 'choose' => 'baz' ],
434 false
436 'Multiselect (2)' => [
438 'choose' => [
439 'type' => 'multiselect',
440 'options' => [
441 'foo' => wfMessage( 'mainpage' ),
442 'bar' => wfMessage( 'mainpage' ),
446 $data = [ 'choose' => [ 'foo', 'bar' ] ],
447 $data
449 'Multiselect (1)' => [
451 'choose' => [
452 'type' => 'multiselect',
453 'options' => [
454 'foo' => wfMessage( 'mainpage' ),
455 'bar' => wfMessage( 'mainpage' ),
459 $data = [ 'choose' => [ 'bar' ] ],
460 $data
462 'Multiselect, string for some reason' => [
464 'choose' => [
465 'type' => 'multiselect',
466 'options' => [
467 'foo' => wfMessage( 'mainpage' ),
468 'bar' => wfMessage( 'mainpage' ),
472 [ 'choose' => 'foo' ],
473 [ 'choose' => [ 'foo' ] ]
475 'Multiselect, invalid choice' => [
477 'choose' => [
478 'type' => 'multiselect',
479 'options' => [
480 'foo' => wfMessage( 'mainpage' ),
481 'bar' => wfMessage( 'mainpage' ),
485 [ 'choose' => [ 'foo', 'baz' ] ],
486 false
488 'Multiselect, empty' => [
490 'choose' => [
491 'type' => 'multiselect',
492 'options' => [
493 'foo' => wfMessage( 'mainpage' ),
494 'bar' => wfMessage( 'mainpage' ),
498 [ 'choose' => [] ],
499 false
501 'Multiselect, optional, nothing submitted' => [
503 'choose' => [
504 'type' => 'multiselect',
505 'options' => [
506 'foo' => wfMessage( 'mainpage' ),
507 'bar' => wfMessage( 'mainpage' ),
509 'optional' => true,
513 [ 'choose' => [] ]