Sync up with Parsoid parserTests.
[lhc/web/wiklou.git] / tests / phpunit / includes / auth / AuthenticationRequestTest.php
1 <?php
2
3 namespace MediaWiki\Auth;
4
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 );
12
13 $this->assertSame( get_class( $mock ), $mock->getUniqueId() );
14
15 $this->assertType( 'array', $mock->getMetadata() );
16
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'] );
23 }
24
25 public function testLoadRequestsFromSubmission() {
26 $mb = $this->getMockBuilder( AuthenticationRequest::class )
27 ->setMethods( [ 'loadFromSubmission' ] );
28
29 $data = [ 'foo', 'bar' ];
30
31 $req1 = $mb->getMockForAbstractClass();
32 $req1->expects( $this->once() )->method( 'loadFromSubmission' )
33 ->with( $this->identicalTo( $data ) )
34 ->will( $this->returnValue( false ) );
35
36 $req2 = $mb->getMockForAbstractClass();
37 $req2->expects( $this->once() )->method( 'loadFromSubmission' )
38 ->with( $this->identicalTo( $data ) )
39 ->will( $this->returnValue( true ) );
40
41 $this->assertSame(
42 [ $req2 ],
43 AuthenticationRequest::loadRequestsFromSubmission( [ $req1, $req2 ], $data )
44 );
45 }
46
47 public function testGetRequestByClass() {
48 $mb = $this->getMockBuilder(
49 AuthenticationRequest::class, 'AuthenticationRequestTest_AuthenticationRequest2'
50 );
51
52 $reqs = [
53 $this->getMockForAbstractClass(
54 AuthenticationRequest::class, [], 'AuthenticationRequestTest_AuthenticationRequest1'
55 ),
56 $mb->getMockForAbstractClass(),
57 $mb->getMockForAbstractClass(),
58 $this->getMockForAbstractClass(
59 PasswordAuthenticationRequest::class, [],
60 'AuthenticationRequestTest_PasswordAuthenticationRequest'
61 ),
62 ];
63
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 ) );
79
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 ) );
95 }
96
97 public function testGetUsernameFromRequests() {
98 $mb = $this->getMockBuilder( AuthenticationRequest::class );
99
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',
105 ],
106 ] ) );
107 $reqs[] = $req;
108 }
109
110 $req = $mb->getMockForAbstractClass();
111 $req->expects( $this->any() )->method( 'getFieldInfo' )->will( $this->returnValue( [] ) );
112 $req->username = 'baz';
113 $reqs[] = $req;
114
115 $this->assertNull( AuthenticationRequest::getUsernameFromRequests( $reqs ) );
116
117 $reqs[1]->username = 'foo';
118 $this->assertSame( 'foo', AuthenticationRequest::getUsernameFromRequests( $reqs ) );
119
120 $reqs[0]->username = 'foo';
121 $reqs[2]->username = 'foo';
122 $this->assertSame( 'foo', AuthenticationRequest::getUsernameFromRequests( $reqs ) );
123
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()
134 );
135 }
136 }
137
138 public function testMergeFieldInfo() {
139 $msg = wfMessage( 'foo' );
140
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,
148 ],
149 'string2' => [
150 'type' => 'string',
151 'label' => $msg,
152 'help' => $msg,
153 ],
154 'optional' => [
155 'type' => 'string',
156 'label' => $msg,
157 'help' => $msg,
158 'optional' => true,
159 ],
160 'select' => [
161 'type' => 'select',
162 'options' => [ 'foo' => $msg, 'baz' => $msg ],
163 'label' => $msg,
164 'help' => $msg,
165 ],
166 ] ) );
167
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 ],
176 'string3' => [
177 'type' => 'string',
178 'label' => $msg,
179 'help' => $msg,
180 ],
181 'select' => [
182 'type' => 'select',
183 'options' => [ 'bar' => $msg, 'baz' => $msg ],
184 'label' => $msg,
185 'help' => $msg,
186 ],
187 ] ) );
188
189 $req3 = $this->getMock( AuthenticationRequest::class );
190 $req3->required = AuthenticationRequest::REQUIRED;
191 $req3->expects( $this->any() )->method( 'getFieldInfo' )->will( $this->returnValue( [
192 'string1' => [
193 'type' => 'checkbox',
194 'label' => $msg,
195 'help' => $msg,
196 ],
197 ] ) );
198
199 $req4 = $this->getMock( AuthenticationRequest::class );
200 $req4->required = AuthenticationRequest::REQUIRED;
201 $req4->expects( $this->any() )->method( 'getFieldInfo' )->will( $this->returnValue( [] ) );
202
203 // Basic combining
204
205 $fields = AuthenticationRequest::mergeFieldInfo( [ $req1 ] );
206 $expect = $req1->getFieldInfo();
207 foreach ( $expect as $name => &$options ) {
208 $options['optional'] = !empty( $options['optional'] );
209 }
210 unset( $options );
211 $this->assertEquals( $expect, $fields );
212
213 $fields = AuthenticationRequest::mergeFieldInfo( [ $req1, $req4 ] );
214 $this->assertEquals( $expect, $fields );
215
216 try {
217 AuthenticationRequest::mergeFieldInfo( [ $req1, $req3 ] );
218 $this->fail( 'Expected exception not thrown' );
219 } catch ( \UnexpectedValueException $ex ) {
220 $this->assertSame(
221 'Field type conflict for "string1", "string" vs "checkbox"',
222 $ex->getMessage()
223 );
224 }
225
226 $fields = AuthenticationRequest::mergeFieldInfo( [ $req1, $req2 ] );
227 $expect += $req2->getFieldInfo();
228 $expect['string2']['optional'] = false;
229 $expect['string3']['optional'] = false;
230 $expect['select']['options']['bar'] = $msg;
231 $this->assertEquals( $expect, $fields );
232
233 // Combining with something not required
234
235 $req1->required = AuthenticationRequest::PRIMARY_REQUIRED;
236
237 $fields = AuthenticationRequest::mergeFieldInfo( [ $req1, $req2 ] );
238 $expect += $req2->getFieldInfo();
239 $expect['string1']['optional'] = false;
240 $expect['string3']['optional'] = false;
241 $expect['select']['optional'] = false;
242 $expect['select']['options']['bar'] = $msg;
243 $this->assertEquals( $expect, $fields );
244
245 $req2->required = AuthenticationRequest::PRIMARY_REQUIRED;
246
247 $fields = AuthenticationRequest::mergeFieldInfo( [ $req1, $req2 ] );
248 $expect = $req1->getFieldInfo() + $req2->getFieldInfo();
249 $expect['string1']['optional'] = false;
250 $expect['string2']['optional'] = true;
251 $expect['string3']['optional'] = true;
252 $expect['select']['optional'] = false;
253 $expect['select']['options']['bar'] = $msg;
254 $this->assertEquals( $expect, $fields );
255 }
256
257 /**
258 * @dataProvider provideLoadFromSubmission
259 * @param array $fieldInfo
260 * @param array $data
261 * @param array|bool $expectState
262 */
263 public function testLoadFromSubmission( $fieldInfo, $data, $expectState ) {
264 $mock = $this->getMockForAbstractClass( AuthenticationRequest::class );
265 $mock->expects( $this->any() )->method( 'getFieldInfo' )
266 ->will( $this->returnValue( $fieldInfo ) );
267
268 $ret = $mock->loadFromSubmission( $data );
269 if ( is_array( $expectState ) ) {
270 $this->assertTrue( $ret );
271 $expect = call_user_func( [ get_class( $mock ), '__set_state' ], $expectState );
272 $this->assertEquals( $expect, $mock );
273 } else {
274 $this->assertFalse( $ret );
275 }
276 }
277
278 public static function provideLoadFromSubmission() {
279 return [
280 'No fields' => [
281 [],
282 $data = [ 'foo' => 'bar' ],
283 false
284 ],
285
286 'Simple field' => [
287 [
288 'field' => [
289 'type' => 'string',
290 ],
291 ],
292 $data = [ 'field' => 'string!' ],
293 $data
294 ],
295 'Simple field, not supplied' => [
296 [
297 'field' => [
298 'type' => 'string',
299 ],
300 ],
301 [],
302 false
303 ],
304 'Simple field, empty' => [
305 [
306 'field' => [
307 'type' => 'string',
308 ],
309 ],
310 [ 'field' => '' ],
311 false
312 ],
313 'Simple field, optional, not supplied' => [
314 [
315 'field' => [
316 'type' => 'string',
317 'optional' => true,
318 ],
319 ],
320 [],
321 false
322 ],
323 'Simple field, optional, empty' => [
324 [
325 'field' => [
326 'type' => 'string',
327 'optional' => true,
328 ],
329 ],
330 $data = [ 'field' => '' ],
331 $data
332 ],
333
334 'Checkbox, checked' => [
335 [
336 'check' => [
337 'type' => 'checkbox',
338 ],
339 ],
340 [ 'check' => '' ],
341 [ 'check' => true ]
342 ],
343 'Checkbox, unchecked' => [
344 [
345 'check' => [
346 'type' => 'checkbox',
347 ],
348 ],
349 [],
350 false
351 ],
352 'Checkbox, optional, unchecked' => [
353 [
354 'check' => [
355 'type' => 'checkbox',
356 'optional' => true,
357 ],
358 ],
359 [],
360 [ 'check' => false ]
361 ],
362
363 'Button, used' => [
364 [
365 'push' => [
366 'type' => 'button',
367 ],
368 ],
369 [ 'push' => '' ],
370 [ 'push' => true ]
371 ],
372 'Button, unused' => [
373 [
374 'push' => [
375 'type' => 'button',
376 ],
377 ],
378 [],
379 false
380 ],
381 'Button, optional, unused' => [
382 [
383 'push' => [
384 'type' => 'button',
385 'optional' => true,
386 ],
387 ],
388 [],
389 [ 'push' => false ]
390 ],
391 'Button, image-style' => [
392 [
393 'push' => [
394 'type' => 'button',
395 ],
396 ],
397 [ 'push_x' => 0, 'push_y' => 0 ],
398 [ 'push' => true ]
399 ],
400
401 'Select' => [
402 [
403 'choose' => [
404 'type' => 'select',
405 'options' => [
406 'foo' => wfMessage( 'mainpage' ),
407 'bar' => wfMessage( 'mainpage' ),
408 ],
409 ],
410 ],
411 $data = [ 'choose' => 'foo' ],
412 $data
413 ],
414 'Select, invalid choice' => [
415 [
416 'choose' => [
417 'type' => 'select',
418 'options' => [
419 'foo' => wfMessage( 'mainpage' ),
420 'bar' => wfMessage( 'mainpage' ),
421 ],
422 ],
423 ],
424 $data = [ 'choose' => 'baz' ],
425 false
426 ],
427 'Multiselect (2)' => [
428 [
429 'choose' => [
430 'type' => 'multiselect',
431 'options' => [
432 'foo' => wfMessage( 'mainpage' ),
433 'bar' => wfMessage( 'mainpage' ),
434 ],
435 ],
436 ],
437 $data = [ 'choose' => [ 'foo', 'bar' ] ],
438 $data
439 ],
440 'Multiselect (1)' => [
441 [
442 'choose' => [
443 'type' => 'multiselect',
444 'options' => [
445 'foo' => wfMessage( 'mainpage' ),
446 'bar' => wfMessage( 'mainpage' ),
447 ],
448 ],
449 ],
450 $data = [ 'choose' => [ 'bar' ] ],
451 $data
452 ],
453 'Multiselect, string for some reason' => [
454 [
455 'choose' => [
456 'type' => 'multiselect',
457 'options' => [
458 'foo' => wfMessage( 'mainpage' ),
459 'bar' => wfMessage( 'mainpage' ),
460 ],
461 ],
462 ],
463 [ 'choose' => 'foo' ],
464 [ 'choose' => [ 'foo' ] ]
465 ],
466 'Multiselect, invalid choice' => [
467 [
468 'choose' => [
469 'type' => 'multiselect',
470 'options' => [
471 'foo' => wfMessage( 'mainpage' ),
472 'bar' => wfMessage( 'mainpage' ),
473 ],
474 ],
475 ],
476 [ 'choose' => [ 'foo', 'baz' ] ],
477 false
478 ],
479 'Multiselect, empty' => [
480 [
481 'choose' => [
482 'type' => 'multiselect',
483 'options' => [
484 'foo' => wfMessage( 'mainpage' ),
485 'bar' => wfMessage( 'mainpage' ),
486 ],
487 ],
488 ],
489 [ 'choose' => [] ],
490 false
491 ],
492 'Multiselect, optional, nothing submitted' => [
493 [
494 'choose' => [
495 'type' => 'multiselect',
496 'options' => [
497 'foo' => wfMessage( 'mainpage' ),
498 'bar' => wfMessage( 'mainpage' ),
499 ],
500 'optional' => true,
501 ],
502 ],
503 [],
504 [ 'choose' => [] ]
505 ],
506 ];
507 }
508 }