diff options
Diffstat (limited to 'www/wiki/tests/phpunit/includes/auth/AuthenticationRequestTest.php')
-rw-r--r-- | www/wiki/tests/phpunit/includes/auth/AuthenticationRequestTest.php | 517 |
1 files changed, 517 insertions, 0 deletions
diff --git a/www/wiki/tests/phpunit/includes/auth/AuthenticationRequestTest.php b/www/wiki/tests/phpunit/includes/auth/AuthenticationRequestTest.php new file mode 100644 index 00000000..1bc0f31f --- /dev/null +++ b/www/wiki/tests/phpunit/includes/auth/AuthenticationRequestTest.php @@ -0,0 +1,517 @@ +<?php + +namespace MediaWiki\Auth; + +/** + * @group AuthManager + * @covers MediaWiki\Auth\AuthenticationRequest + */ +class AuthenticationRequestTest extends \MediaWikiTestCase { + public function testBasics() { + $mock = $this->getMockForAbstractClass( AuthenticationRequest::class ); + + $this->assertSame( get_class( $mock ), $mock->getUniqueId() ); + + $this->assertType( 'array', $mock->getMetadata() ); + + $ret = $mock->describeCredentials(); + $this->assertInternalType( 'array', $ret ); + $this->assertArrayHasKey( 'provider', $ret ); + $this->assertInstanceOf( \Message::class, $ret['provider'] ); + $this->assertArrayHasKey( 'account', $ret ); + $this->assertInstanceOf( \Message::class, $ret['account'] ); + } + + public function testLoadRequestsFromSubmission() { + $mb = $this->getMockBuilder( AuthenticationRequest::class ) + ->setMethods( [ 'loadFromSubmission' ] ); + + $data = [ 'foo', 'bar' ]; + + $req1 = $mb->getMockForAbstractClass(); + $req1->expects( $this->once() )->method( 'loadFromSubmission' ) + ->with( $this->identicalTo( $data ) ) + ->will( $this->returnValue( false ) ); + + $req2 = $mb->getMockForAbstractClass(); + $req2->expects( $this->once() )->method( 'loadFromSubmission' ) + ->with( $this->identicalTo( $data ) ) + ->will( $this->returnValue( true ) ); + + $this->assertSame( + [ $req2 ], + AuthenticationRequest::loadRequestsFromSubmission( [ $req1, $req2 ], $data ) + ); + } + + public function testGetRequestByClass() { + $mb = $this->getMockBuilder( + AuthenticationRequest::class, 'AuthenticationRequestTest_AuthenticationRequest2' + ); + + $reqs = [ + $this->getMockForAbstractClass( + AuthenticationRequest::class, [], 'AuthenticationRequestTest_AuthenticationRequest1' + ), + $mb->getMockForAbstractClass(), + $mb->getMockForAbstractClass(), + $this->getMockForAbstractClass( + PasswordAuthenticationRequest::class, [], + 'AuthenticationRequestTest_PasswordAuthenticationRequest' + ), + ]; + + $this->assertNull( AuthenticationRequest::getRequestByClass( + $reqs, 'AuthenticationRequestTest_AuthenticationRequest0' + ) ); + $this->assertSame( $reqs[0], AuthenticationRequest::getRequestByClass( + $reqs, 'AuthenticationRequestTest_AuthenticationRequest1' + ) ); + $this->assertNull( AuthenticationRequest::getRequestByClass( + $reqs, 'AuthenticationRequestTest_AuthenticationRequest2' + ) ); + $this->assertNull( AuthenticationRequest::getRequestByClass( + $reqs, PasswordAuthenticationRequest::class + ) ); + $this->assertNull( AuthenticationRequest::getRequestByClass( + $reqs, 'ClassThatDoesNotExist' + ) ); + + $this->assertNull( AuthenticationRequest::getRequestByClass( + $reqs, 'AuthenticationRequestTest_AuthenticationRequest0', true + ) ); + $this->assertSame( $reqs[0], AuthenticationRequest::getRequestByClass( + $reqs, 'AuthenticationRequestTest_AuthenticationRequest1', true + ) ); + $this->assertNull( AuthenticationRequest::getRequestByClass( + $reqs, 'AuthenticationRequestTest_AuthenticationRequest2', true + ) ); + $this->assertSame( $reqs[3], AuthenticationRequest::getRequestByClass( + $reqs, PasswordAuthenticationRequest::class, true + ) ); + $this->assertNull( AuthenticationRequest::getRequestByClass( + $reqs, 'ClassThatDoesNotExist', true + ) ); + } + + public function testGetUsernameFromRequests() { + $mb = $this->getMockBuilder( AuthenticationRequest::class ); + + for ( $i = 0; $i < 3; $i++ ) { + $req = $mb->getMockForAbstractClass(); + $req->expects( $this->any() )->method( 'getFieldInfo' )->will( $this->returnValue( [ + 'username' => [ + 'type' => 'string', + ], + ] ) ); + $reqs[] = $req; + } + + $req = $mb->getMockForAbstractClass(); + $req->expects( $this->any() )->method( 'getFieldInfo' )->will( $this->returnValue( [] ) ); + $req->username = 'baz'; + $reqs[] = $req; + + $this->assertNull( AuthenticationRequest::getUsernameFromRequests( $reqs ) ); + + $reqs[1]->username = 'foo'; + $this->assertSame( 'foo', AuthenticationRequest::getUsernameFromRequests( $reqs ) ); + + $reqs[0]->username = 'foo'; + $reqs[2]->username = 'foo'; + $this->assertSame( 'foo', AuthenticationRequest::getUsernameFromRequests( $reqs ) ); + + $reqs[1]->username = 'bar'; + try { + AuthenticationRequest::getUsernameFromRequests( $reqs ); + $this->fail( 'Expected exception not thrown' ); + } catch ( \UnexpectedValueException $ex ) { + $this->assertSame( + 'Conflicting username fields: "bar" from ' . + get_class( $reqs[1] ) . '::$username vs. "foo" from ' . + get_class( $reqs[0] ) . '::$username', + $ex->getMessage() + ); + } + } + + public function testMergeFieldInfo() { + $msg = wfMessage( 'foo' ); + + $req1 = $this->createMock( AuthenticationRequest::class ); + $req1->required = AuthenticationRequest::REQUIRED; + $req1->expects( $this->any() )->method( 'getFieldInfo' )->will( $this->returnValue( [ + 'string1' => [ + 'type' => 'string', + 'label' => $msg, + 'help' => $msg, + ], + 'string2' => [ + 'type' => 'string', + 'label' => $msg, + 'help' => $msg, + ], + 'optional' => [ + 'type' => 'string', + 'label' => $msg, + 'help' => $msg, + 'optional' => true, + ], + 'select' => [ + 'type' => 'select', + 'options' => [ 'foo' => $msg, 'baz' => $msg ], + 'label' => $msg, + 'help' => $msg, + ], + ] ) ); + + $req2 = $this->createMock( AuthenticationRequest::class ); + $req2->required = AuthenticationRequest::REQUIRED; + $req2->expects( $this->any() )->method( 'getFieldInfo' )->will( $this->returnValue( [ + 'string1' => [ + 'type' => 'string', + 'label' => $msg, + 'help' => $msg, + 'sensitive' => true, + ], + 'string3' => [ + 'type' => 'string', + 'label' => $msg, + 'help' => $msg, + ], + 'select' => [ + 'type' => 'select', + 'options' => [ 'bar' => $msg, 'baz' => $msg ], + 'label' => $msg, + 'help' => $msg, + ], + ] ) ); + + $req3 = $this->createMock( AuthenticationRequest::class ); + $req3->required = AuthenticationRequest::REQUIRED; + $req3->expects( $this->any() )->method( 'getFieldInfo' )->will( $this->returnValue( [ + 'string1' => [ + 'type' => 'checkbox', + 'label' => $msg, + 'help' => $msg, + ], + ] ) ); + + $req4 = $this->createMock( AuthenticationRequest::class ); + $req4->required = AuthenticationRequest::REQUIRED; + $req4->expects( $this->any() )->method( 'getFieldInfo' )->will( $this->returnValue( [] ) ); + + // Basic combining + + $fields = AuthenticationRequest::mergeFieldInfo( [ $req1 ] ); + $expect = $req1->getFieldInfo(); + foreach ( $expect as $name => &$options ) { + $options['optional'] = !empty( $options['optional'] ); + $options['sensitive'] = !empty( $options['sensitive'] ); + } + unset( $options ); + $this->assertEquals( $expect, $fields ); + + $fields = AuthenticationRequest::mergeFieldInfo( [ $req1, $req4 ] ); + $this->assertEquals( $expect, $fields ); + + try { + AuthenticationRequest::mergeFieldInfo( [ $req1, $req3 ] ); + $this->fail( 'Expected exception not thrown' ); + } catch ( \UnexpectedValueException $ex ) { + $this->assertSame( + 'Field type conflict for "string1", "string" vs "checkbox"', + $ex->getMessage() + ); + } + + $fields = AuthenticationRequest::mergeFieldInfo( [ $req1, $req2 ] ); + $expect += $req2->getFieldInfo(); + $expect['string1']['sensitive'] = true; + $expect['string2']['optional'] = false; + $expect['string3']['optional'] = false; + $expect['string3']['sensitive'] = false; + $expect['select']['options']['bar'] = $msg; + $this->assertEquals( $expect, $fields ); + + // Combining with something not required + + $req1->required = AuthenticationRequest::PRIMARY_REQUIRED; + + $fields = AuthenticationRequest::mergeFieldInfo( [ $req1, $req2 ] ); + $expect += $req2->getFieldInfo(); + $expect['string1']['optional'] = false; + $expect['string1']['sensitive'] = true; + $expect['string3']['optional'] = false; + $expect['select']['optional'] = false; + $expect['select']['options']['bar'] = $msg; + $this->assertEquals( $expect, $fields ); + + $req2->required = AuthenticationRequest::PRIMARY_REQUIRED; + + $fields = AuthenticationRequest::mergeFieldInfo( [ $req1, $req2 ] ); + $expect = $req1->getFieldInfo() + $req2->getFieldInfo(); + foreach ( $expect as $name => &$options ) { + $options['sensitive'] = !empty( $options['sensitive'] ); + } + $expect['string1']['optional'] = false; + $expect['string1']['sensitive'] = true; + $expect['string2']['optional'] = true; + $expect['string3']['optional'] = true; + $expect['select']['optional'] = false; + $expect['select']['options']['bar'] = $msg; + $this->assertEquals( $expect, $fields ); + } + + /** + * @dataProvider provideLoadFromSubmission + * @param array $fieldInfo + * @param array $data + * @param array|bool $expectState + */ + public function testLoadFromSubmission( $fieldInfo, $data, $expectState ) { + $mock = $this->getMockForAbstractClass( AuthenticationRequest::class ); + $mock->expects( $this->any() )->method( 'getFieldInfo' ) + ->will( $this->returnValue( $fieldInfo ) ); + + $ret = $mock->loadFromSubmission( $data ); + if ( is_array( $expectState ) ) { + $this->assertTrue( $ret ); + $expect = call_user_func( [ get_class( $mock ), '__set_state' ], $expectState ); + $this->assertEquals( $expect, $mock ); + } else { + $this->assertFalse( $ret ); + } + } + + public static function provideLoadFromSubmission() { + return [ + 'No fields' => [ + [], + $data = [ 'foo' => 'bar' ], + false + ], + + 'Simple field' => [ + [ + 'field' => [ + 'type' => 'string', + ], + ], + $data = [ 'field' => 'string!' ], + $data + ], + 'Simple field, not supplied' => [ + [ + 'field' => [ + 'type' => 'string', + ], + ], + [], + false + ], + 'Simple field, empty' => [ + [ + 'field' => [ + 'type' => 'string', + ], + ], + [ 'field' => '' ], + false + ], + 'Simple field, optional, not supplied' => [ + [ + 'field' => [ + 'type' => 'string', + 'optional' => true, + ], + ], + [], + false + ], + 'Simple field, optional, empty' => [ + [ + 'field' => [ + 'type' => 'string', + 'optional' => true, + ], + ], + $data = [ 'field' => '' ], + $data + ], + + 'Checkbox, checked' => [ + [ + 'check' => [ + 'type' => 'checkbox', + ], + ], + [ 'check' => '' ], + [ 'check' => true ] + ], + 'Checkbox, unchecked' => [ + [ + 'check' => [ + 'type' => 'checkbox', + ], + ], + [], + false + ], + 'Checkbox, optional, unchecked' => [ + [ + 'check' => [ + 'type' => 'checkbox', + 'optional' => true, + ], + ], + [], + [ 'check' => false ] + ], + + 'Button, used' => [ + [ + 'push' => [ + 'type' => 'button', + ], + ], + [ 'push' => '' ], + [ 'push' => true ] + ], + 'Button, unused' => [ + [ + 'push' => [ + 'type' => 'button', + ], + ], + [], + false + ], + 'Button, optional, unused' => [ + [ + 'push' => [ + 'type' => 'button', + 'optional' => true, + ], + ], + [], + [ 'push' => false ] + ], + 'Button, image-style' => [ + [ + 'push' => [ + 'type' => 'button', + ], + ], + [ 'push_x' => 0, 'push_y' => 0 ], + [ 'push' => true ] + ], + + 'Select' => [ + [ + 'choose' => [ + 'type' => 'select', + 'options' => [ + 'foo' => wfMessage( 'mainpage' ), + 'bar' => wfMessage( 'mainpage' ), + ], + ], + ], + $data = [ 'choose' => 'foo' ], + $data + ], + 'Select, invalid choice' => [ + [ + 'choose' => [ + 'type' => 'select', + 'options' => [ + 'foo' => wfMessage( 'mainpage' ), + 'bar' => wfMessage( 'mainpage' ), + ], + ], + ], + $data = [ 'choose' => 'baz' ], + false + ], + 'Multiselect (2)' => [ + [ + 'choose' => [ + 'type' => 'multiselect', + 'options' => [ + 'foo' => wfMessage( 'mainpage' ), + 'bar' => wfMessage( 'mainpage' ), + ], + ], + ], + $data = [ 'choose' => [ 'foo', 'bar' ] ], + $data + ], + 'Multiselect (1)' => [ + [ + 'choose' => [ + 'type' => 'multiselect', + 'options' => [ + 'foo' => wfMessage( 'mainpage' ), + 'bar' => wfMessage( 'mainpage' ), + ], + ], + ], + $data = [ 'choose' => [ 'bar' ] ], + $data + ], + 'Multiselect, string for some reason' => [ + [ + 'choose' => [ + 'type' => 'multiselect', + 'options' => [ + 'foo' => wfMessage( 'mainpage' ), + 'bar' => wfMessage( 'mainpage' ), + ], + ], + ], + [ 'choose' => 'foo' ], + [ 'choose' => [ 'foo' ] ] + ], + 'Multiselect, invalid choice' => [ + [ + 'choose' => [ + 'type' => 'multiselect', + 'options' => [ + 'foo' => wfMessage( 'mainpage' ), + 'bar' => wfMessage( 'mainpage' ), + ], + ], + ], + [ 'choose' => [ 'foo', 'baz' ] ], + false + ], + 'Multiselect, empty' => [ + [ + 'choose' => [ + 'type' => 'multiselect', + 'options' => [ + 'foo' => wfMessage( 'mainpage' ), + 'bar' => wfMessage( 'mainpage' ), + ], + ], + ], + [ 'choose' => [] ], + false + ], + 'Multiselect, optional, nothing submitted' => [ + [ + 'choose' => [ + 'type' => 'multiselect', + 'options' => [ + 'foo' => wfMessage( 'mainpage' ), + 'bar' => wfMessage( 'mainpage' ), + ], + 'optional' => true, + ], + ], + [], + [ 'choose' => [] ] + ], + ]; + } +} |