wiki.techinc.nl/tests/phpunit/includes/auth/AuthenticationRequestTest.php
Umherirrender e44aee1f74 auth: Add missing documentation to class properties
Add doc-typehints to class properties found by the PropertyDocumentation
sniff to improve the documentation.

Once the sniff is enabled it avoids that new code is missing type
declarations. This is focused on documentation and does not change code.

Change-Id: Ib6081f5519d2294bb14f81bf399f9c45315f2b69
2024-09-01 11:27:45 +02:00

537 lines
13 KiB
PHP

<?php
namespace MediaWiki\Tests\Auth;
use MediaWiki\Auth\AuthenticationRequest;
use MediaWiki\Auth\PasswordAuthenticationRequest;
use MediaWiki\Message\Message;
use MediaWikiIntegrationTestCase;
use UnexpectedValueException;
/**
* @group AuthManager
* @covers \MediaWiki\Auth\AuthenticationRequest
*/
class AuthenticationRequestTest extends MediaWikiIntegrationTestCase {
public function testBasics() {
$mock = $this->getMockForAbstractClass( AuthenticationRequest::class );
$this->assertSame( get_class( $mock ), $mock->getUniqueId() );
$this->assertIsArray( $mock->getMetadata() );
$ret = $mock->describeCredentials();
$this->assertIsArray( $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 )
->onlyMethods( [ 'loadFromSubmission' ] );
$data = [ 'foo', 'bar' ];
$req1 = $mb->getMockForAbstractClass();
$req1->expects( $this->once() )->method( 'loadFromSubmission' )
->with( $this->identicalTo( $data ) )
->willReturn( false );
$req2 = $mb->getMockForAbstractClass();
$req2->expects( $this->once() )->method( 'loadFromSubmission' )
->with( $this->identicalTo( $data ) )
->willReturn( 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->method( 'getFieldInfo' )->willReturn( [
'username' => [
'type' => 'string',
],
] );
$reqs[] = $req;
}
$req = $mb->getMockForAbstractClass();
$req->method( 'getFieldInfo' )->willReturn( [] );
$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->method( 'getFieldInfo' )->willReturn( [
'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->method( 'getFieldInfo' )->willReturn( [
'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->method( 'getFieldInfo' )->willReturn( [
'string1' => [
'type' => 'checkbox',
'label' => $msg,
'help' => $msg,
],
] );
$req4 = $this->createMock( AuthenticationRequest::class );
$req4->required = AuthenticationRequest::REQUIRED;
$req4->method( 'getFieldInfo' )->willReturn( [] );
// Basic combining
$this->assertEquals( [], AuthenticationRequest::mergeFieldInfo( [] ) );
$fields = AuthenticationRequest::mergeFieldInfo( [ $req1 ] );
$expect = $req1->getFieldInfo();
foreach ( $expect as &$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 &$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( AuthenticationRequestForLoadFromSubmission::class );
$mock->method( 'getFieldInfo' )
->willReturn( $fieldInfo );
$ret = $mock->loadFromSubmission( $data );
if ( is_array( $expectState ) ) {
$this->assertTrue( $ret );
$expect = $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' => [] ]
],
];
}
}
// Dynamic properties from the testLoadFromSubmission not working in php8.2
abstract class AuthenticationRequestForLoadFromSubmission extends AuthenticationRequest {
/** @var array */
public $choose;
/** @var bool */
public $push;
/** @var bool */
public $check;
/** @var string */
public $field;
}