3 namespace MediaWiki\Auth
;
7 * @covers MediaWiki\Auth\AuthenticationRequest
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 ) );
43 AuthenticationRequest
::loadRequestsFromSubmission( [ $req1, $req2 ], $data )
47 public function testGetRequestByClass() {
48 $mb = $this->getMockBuilder(
49 AuthenticationRequest
::class, 'AuthenticationRequestTest_AuthenticationRequest2'
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'
67 $this->assertSame( $reqs[0], AuthenticationRequest
::getRequestByClass(
68 $reqs, 'AuthenticationRequestTest_AuthenticationRequest1'
70 $this->assertNull( AuthenticationRequest
::getRequestByClass(
71 $reqs, 'AuthenticationRequestTest_AuthenticationRequest2'
73 $this->assertNull( AuthenticationRequest
::getRequestByClass(
74 $reqs, PasswordAuthenticationRequest
::class
76 $this->assertNull( AuthenticationRequest
::getRequestByClass(
77 $reqs, 'ClassThatDoesNotExist'
80 $this->assertNull( AuthenticationRequest
::getRequestByClass(
81 $reqs, 'AuthenticationRequestTest_AuthenticationRequest0', true
83 $this->assertSame( $reqs[0], AuthenticationRequest
::getRequestByClass(
84 $reqs, 'AuthenticationRequestTest_AuthenticationRequest1', true
86 $this->assertNull( AuthenticationRequest
::getRequestByClass(
87 $reqs, 'AuthenticationRequestTest_AuthenticationRequest2', true
89 $this->assertSame( $reqs[3], AuthenticationRequest
::getRequestByClass(
90 $reqs, PasswordAuthenticationRequest
::class, true
92 $this->assertNull( AuthenticationRequest
::getRequestByClass(
93 $reqs, 'ClassThatDoesNotExist', true
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( [
110 $req = $mb->getMockForAbstractClass();
111 $req->expects( $this->any() )->method( 'getFieldInfo' )->will( $this->returnValue( [] ) );
112 $req->username
= 'baz';
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';
126 AuthenticationRequest
::getUsernameFromRequests( $reqs );
127 $this->fail( 'Expected exception not thrown' );
128 } catch ( \UnexpectedValueException
$ex ) {
130 'Conflicting username fields: "bar" from ' .
131 get_class( $reqs[1] ) . '::$username vs. "foo" from ' .
132 get_class( $reqs[0] ) . '::$username',
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( [
162 'options' => [ 'foo' => $msg, 'baz' => $msg ],
168 $req2 = $this->getMock( AuthenticationRequest
::class );
169 $req2->required
= AuthenticationRequest
::REQUIRED
;
170 $req2->expects( $this->any() )->method( 'getFieldInfo' )->will( $this->returnValue( [
183 'options' => [ 'bar' => $msg, 'baz' => $msg ],
189 $req3 = $this->getMock( AuthenticationRequest
::class );
190 $req3->required
= AuthenticationRequest
::REQUIRED
;
191 $req3->expects( $this->any() )->method( 'getFieldInfo' )->will( $this->returnValue( [
193 'type' => 'checkbox',
199 $req4 = $this->getMock( AuthenticationRequest
::class );
200 $req4->required
= AuthenticationRequest
::REQUIRED
;
201 $req4->expects( $this->any() )->method( 'getFieldInfo' )->will( $this->returnValue( [] ) );
205 $fields = AuthenticationRequest
::mergeFieldInfo( [ $req1 ] );
206 $expect = $req1->getFieldInfo();
207 foreach ( $expect as $name => &$options ) {
208 $options['optional'] = !empty( $options['optional'] );
211 $this->assertEquals( $expect, $fields );
213 $fields = AuthenticationRequest
::mergeFieldInfo( [ $req1, $req4 ] );
214 $this->assertEquals( $expect, $fields );
217 AuthenticationRequest
::mergeFieldInfo( [ $req1, $req3 ] );
218 $this->fail( 'Expected exception not thrown' );
219 } catch ( \UnexpectedValueException
$ex ) {
221 'Field type conflict for "string1", "string" vs "checkbox"',
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 );
233 // Combining with something not required
235 $req1->required
= AuthenticationRequest
::PRIMARY_REQUIRED
;
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 );
245 $req2->required
= AuthenticationRequest
::PRIMARY_REQUIRED
;
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 );
258 * @dataProvider provideLoadFromSubmission
259 * @param array $fieldInfo
261 * @param array|bool $expectState
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 ) );
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 );
274 $this->assertFalse( $ret );
278 public static function provideLoadFromSubmission() {
282 $data = [ 'foo' => 'bar' ],
292 $data = [ 'field' => 'string!' ],
295 'Simple field, not supplied' => [
304 'Simple field, empty' => [
313 'Simple field, optional, not supplied' => [
323 'Simple field, optional, empty' => [
330 $data = [ 'field' => '' ],
334 'Checkbox, checked' => [
337 'type' => 'checkbox',
343 'Checkbox, unchecked' => [
346 'type' => 'checkbox',
352 'Checkbox, optional, unchecked' => [
355 'type' => 'checkbox',
372 'Button, unused' => [
381 'Button, optional, unused' => [
391 'Button, image-style' => [
397 [ 'push_x' => 0, 'push_y' => 0 ],
406 'foo' => wfMessage( 'mainpage' ),
407 'bar' => wfMessage( 'mainpage' ),
411 $data = [ 'choose' => 'foo' ],
414 'Select, invalid choice' => [
419 'foo' => wfMessage( 'mainpage' ),
420 'bar' => wfMessage( 'mainpage' ),
424 $data = [ 'choose' => 'baz' ],
427 'Multiselect (2)' => [
430 'type' => 'multiselect',
432 'foo' => wfMessage( 'mainpage' ),
433 'bar' => wfMessage( 'mainpage' ),
437 $data = [ 'choose' => [ 'foo', 'bar' ] ],
440 'Multiselect (1)' => [
443 'type' => 'multiselect',
445 'foo' => wfMessage( 'mainpage' ),
446 'bar' => wfMessage( 'mainpage' ),
450 $data = [ 'choose' => [ 'bar' ] ],
453 'Multiselect, string for some reason' => [
456 'type' => 'multiselect',
458 'foo' => wfMessage( 'mainpage' ),
459 'bar' => wfMessage( 'mainpage' ),
463 [ 'choose' => 'foo' ],
464 [ 'choose' => [ 'foo' ] ]
466 'Multiselect, invalid choice' => [
469 'type' => 'multiselect',
471 'foo' => wfMessage( 'mainpage' ),
472 'bar' => wfMessage( 'mainpage' ),
476 [ 'choose' => [ 'foo', 'baz' ] ],
479 'Multiselect, empty' => [
482 'type' => 'multiselect',
484 'foo' => wfMessage( 'mainpage' ),
485 'bar' => wfMessage( 'mainpage' ),
492 'Multiselect, optional, nothing submitted' => [
495 'type' => 'multiselect',
497 'foo' => wfMessage( 'mainpage' ),
498 'bar' => wfMessage( 'mainpage' ),