Page Menu
Home
WickedGov Phorge
Search
Configure Global Search
Log In
Files
F4100920
ApiOptionsTest.php
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
14 KB
Referenced Files
None
Subscribers
None
ApiOptionsTest.php
View Options
<?php
namespace
MediaWiki\Tests\Api
;
namespace
MediaWiki\Tests\Api
;
use
MediaWiki\Api\ApiMain
;
use
MediaWiki\Api\ApiOptions
;
use
MediaWiki\Api\ApiUsageException
;
use
MediaWiki\Context\DerivativeContext
;
use
MediaWiki\Context\IContextSource
;
use
MediaWiki\Context\RequestContext
;
use
MediaWiki\Preferences\DefaultPreferencesFactory
;
use
MediaWiki\Request\FauxRequest
;
use
MediaWiki\Tests\Unit\Permissions\MockAuthorityTrait
;
use
MediaWiki\Title\Title
;
use
MediaWiki\User\Options\UserOptionsManager
;
use
MediaWiki\User\User
;
use
PHPUnit\Framework\MockObject\MockObject
;
/**
* @group API
* @group Database
* @group medium
*
* @covers \MediaWiki\Api\ApiOptions
*/
class
ApiOptionsTest
extends
ApiTestCase
{
use
MockAuthorityTrait
;
/** @var MockObject */
private
$mUserMock
;
/** @var MockObject */
private
$userOptionsManagerMock
;
/** @var ApiOptions */
private
$mTested
;
/** @var array */
private
$mSession
;
/** @var DerivativeContext */
private
$mContext
;
private
const
SUCCESS
=
[
'options'
=>
'success'
];
protected
function
setUp
():
void
{
parent
::
setUp
();
$this
->
mUserMock
=
$this
->
createMock
(
User
::
class
);
// No actual DB data
$this
->
mUserMock
->
method
(
'getInstanceForUpdate'
)->
willReturn
(
$this
->
mUserMock
);
$this
->
mUserMock
->
method
(
'isAllowedAny'
)->
willReturn
(
true
);
// Create a new context
$this
->
mContext
=
new
DerivativeContext
(
new
RequestContext
()
);
$this
->
mContext
->
getContext
()->
setTitle
(
Title
::
makeTitle
(
NS_MAIN
,
'Test'
)
);
$this
->
mContext
->
setAuthority
(
$this
->
mockUserAuthorityWithPermissions
(
$this
->
mUserMock
,
[
'editmyoptions'
]
)
);
$main
=
new
ApiMain
(
$this
->
mContext
);
// Empty session
$this
->
mSession
=
[];
$this
->
userOptionsManagerMock
=
$this
->
createNoOpMock
(
UserOptionsManager
::
class
,
[
'getOptions'
,
'resetOptionsByName'
,
'setOption'
,
'isOptionGlobal'
]
);
// Needs to return something
$this
->
userOptionsManagerMock
->
method
(
'getOptions'
)->
willReturn
(
[]
);
$preferencesFactory
=
$this
->
createNoOpMock
(
DefaultPreferencesFactory
::
class
,
[
'getFormDescriptor'
,
'listResetKinds'
,
'getResetKinds'
,
'getOptionNamesForReset'
]
);
$preferencesFactory
->
method
(
'getFormDescriptor'
)
->
willReturnCallback
(
[
$this
,
'getPreferencesFormDescription'
]
);
$preferencesFactory
->
method
(
'listResetKinds'
)->
willReturn
(
[
'registered'
,
'registered-multiselect'
,
'registered-checkmatrix'
,
'userjs'
,
'special'
,
'unused'
]
);
$preferencesFactory
->
method
(
'getResetKinds'
)
->
willReturnCallback
(
[
$this
,
'getResetKinds'
]
);
$preferencesFactory
->
method
(
'getOptionNamesForReset'
)
->
willReturn
(
[]
);
$this
->
mTested
=
new
ApiOptions
(
$main
,
'options'
,
$this
->
userOptionsManagerMock
,
$preferencesFactory
);
$this
->
mergeMwGlobalArrayValue
(
'wgDefaultUserOptions'
,
[
'testradio'
=>
'option1'
,
]
);
}
public
function
getPreferencesFormDescription
()
{
$preferences
=
[];
foreach
(
[
'name'
,
'willBeNull'
,
'willBeEmpty'
,
'willBeHappy'
]
as
$k
)
{
$preferences
[
$k
]
=
[
'type'
=>
'text'
,
'section'
=>
'test'
,
'label'
=>
"
\u
{00A0}"
,
];
}
$preferences
[
'testmultiselect'
]
=
[
'type'
=>
'multiselect'
,
'options'
=>
[
'Test'
=>
[
'<span dir="auto">Some HTML here for option 1</span>'
=>
'opt1'
,
'<span dir="auto">Some HTML here for option 2</span>'
=>
'opt2'
,
'<span dir="auto">Some HTML here for option 3</span>'
=>
'opt3'
,
'<span dir="auto">Some HTML here for option 4</span>'
=>
'opt4'
,
],
],
'section'
=>
'test'
,
'label'
=>
"
\u
{00A0}"
,
'prefix'
=>
'testmultiselect-'
,
'default'
=>
[],
];
$preferences
[
'testradio'
]
=
[
'type'
=>
'radio'
,
'options'
=>
[
'Option 1'
=>
'option1'
,
'Option 2'
=>
'option2'
],
'section'
=>
'test'
,
];
return
$preferences
;
}
/**
* @param mixed $unused
* @param IContextSource $context
* @param array|null $options
*
* @return array
*/
public
function
getResetKinds
(
$unused
,
IContextSource
$context
,
$options
=
null
)
{
// Match with above.
$kinds
=
[
'name'
=>
'registered'
,
'willBeNull'
=>
'registered'
,
'willBeEmpty'
=>
'registered'
,
'willBeHappy'
=>
'registered'
,
'testradio'
=>
'registered'
,
'testmultiselect-opt1'
=>
'registered-multiselect'
,
'testmultiselect-opt2'
=>
'registered-multiselect'
,
'testmultiselect-opt3'
=>
'registered-multiselect'
,
'testmultiselect-opt4'
=>
'registered-multiselect'
,
'special'
=>
'special'
,
];
if
(
$options
===
null
)
{
return
$kinds
;
}
$mapping
=
[];
foreach
(
$options
as
$key
=>
$value
)
{
if
(
isset
(
$kinds
[
$key
]
)
)
{
$mapping
[
$key
]
=
$kinds
[
$key
];
}
elseif
(
str_starts_with
(
$key
,
'userjs-'
)
)
{
$mapping
[
$key
]
=
'userjs'
;
}
else
{
$mapping
[
$key
]
=
'unused'
;
}
}
return
$mapping
;
}
private
function
getSampleRequest
(
$custom
=
[]
)
{
$request
=
[
'token'
=>
'123ABC'
,
'change'
=>
null
,
'optionname'
=>
null
,
'optionvalue'
=>
null
,
];
return
array_merge
(
$request
,
$custom
);
}
private
function
executeQuery
(
$request
)
{
$this
->
mContext
->
setRequest
(
new
FauxRequest
(
$request
,
true
,
$this
->
mSession
)
);
$this
->
mUserMock
->
method
(
'getRequest'
)->
willReturn
(
$this
->
mContext
->
getRequest
()
);
$this
->
mTested
->
execute
();
return
$this
->
mTested
->
getResult
()->
getResultData
(
null
,
[
'Strip'
=>
'all'
]
);
}
public
function
testNoToken
()
{
$request
=
$this
->
getSampleRequest
(
[
'token'
=>
null
]
);
$this
->
expectException
(
ApiUsageException
::
class
);
$this
->
executeQuery
(
$request
);
}
public
function
testAnon
()
{
$this
->
mUserMock
->
method
(
'isRegistered'
)
->
willReturn
(
false
);
try
{
$request
=
$this
->
getSampleRequest
();
$this
->
executeQuery
(
$request
);
}
catch
(
ApiUsageException
$e
)
{
$this
->
assertApiErrorCode
(
'notloggedin'
,
$e
);
return
;
}
$this
->
fail
(
"ApiUsageException was not thrown"
);
}
public
function
testNoOptionname
()
{
$this
->
mUserMock
->
method
(
'isRegistered'
)->
willReturn
(
true
);
$this
->
mUserMock
->
method
(
'isNamed'
)->
willReturn
(
true
);
try
{
$request
=
$this
->
getSampleRequest
(
[
'optionvalue'
=>
'1'
]
);
$this
->
executeQuery
(
$request
);
}
catch
(
ApiUsageException
$e
)
{
$this
->
assertApiErrorCode
(
'nooptionname'
,
$e
);
return
;
}
$this
->
fail
(
"ApiUsageException was not thrown"
);
}
public
function
testNoChanges
()
{
$this
->
mUserMock
->
method
(
'isRegistered'
)->
willReturn
(
true
);
$this
->
mUserMock
->
method
(
'isNamed'
)->
willReturn
(
true
);
$this
->
userOptionsManagerMock
->
expects
(
$this
->
never
()
)
->
method
(
'resetOptionsByName'
);
$this
->
userOptionsManagerMock
->
expects
(
$this
->
never
()
)
->
method
(
'setOption'
);
$this
->
mUserMock
->
expects
(
$this
->
never
()
)
->
method
(
'saveSettings'
);
try
{
$request
=
$this
->
getSampleRequest
();
$this
->
executeQuery
(
$request
);
}
catch
(
ApiUsageException
$e
)
{
$this
->
assertApiErrorCode
(
'nochanges'
,
$e
);
return
;
}
$this
->
fail
(
"ApiUsageException was not thrown"
);
}
public
function
userScenarios
()
{
return
[
[
true
,
true
,
false
],
[
true
,
false
,
true
],
];
}
/**
* @dataProvider userScenarios
*/
public
function
testReset
(
$isRegistered
,
$isNamed
,
$expectException
)
{
$this
->
mUserMock
->
method
(
'isRegistered'
)->
willReturn
(
$isRegistered
);
$this
->
mUserMock
->
method
(
'isNamed'
)->
willReturn
(
$isNamed
);
if
(
$expectException
)
{
$this
->
userOptionsManagerMock
->
expects
(
$this
->
never
()
)->
method
(
'resetOptionsByName'
);
$this
->
userOptionsManagerMock
->
expects
(
$this
->
never
()
)->
method
(
'setOption'
);
$this
->
mUserMock
->
expects
(
$this
->
never
()
)->
method
(
'saveSettings'
);
}
else
{
$this
->
userOptionsManagerMock
->
expects
(
$this
->
once
()
)->
method
(
'resetOptionsByName'
);
$this
->
userOptionsManagerMock
->
expects
(
$this
->
never
()
)->
method
(
'setOption'
);
$this
->
mUserMock
->
expects
(
$this
->
once
()
)->
method
(
'saveSettings'
);
}
$request
=
$this
->
getSampleRequest
(
[
'reset'
=>
''
]
);
try
{
$response
=
$this
->
executeQuery
(
$request
);
if
(
$expectException
)
{
$this
->
fail
(
'Expected a "notloggedin" error.'
);
}
else
{
$this
->
assertEquals
(
self
::
SUCCESS
,
$response
);
}
}
catch
(
ApiUsageException
$e
)
{
if
(
!
$expectException
)
{
$this
->
fail
(
'Unexpected "notloggedin" error.'
);
}
else
{
$this
->
assertApiErrorCode
(
'notloggedin'
,
$e
);
}
}
}
/**
* @dataProvider userScenarios
*/
public
function
testResetKinds
(
$isRegistered
,
$isNamed
,
$expectException
)
{
$this
->
mUserMock
->
method
(
'isRegistered'
)->
willReturn
(
$isRegistered
);
$this
->
mUserMock
->
method
(
'isNamed'
)->
willReturn
(
$isNamed
);
if
(
$expectException
)
{
$this
->
mUserMock
->
expects
(
$this
->
never
()
)->
method
(
'saveSettings'
);
$this
->
userOptionsManagerMock
->
expects
(
$this
->
never
()
)->
method
(
'resetOptionsByName'
);
$this
->
userOptionsManagerMock
->
expects
(
$this
->
never
()
)->
method
(
'setOption'
);
}
else
{
$this
->
userOptionsManagerMock
->
expects
(
$this
->
once
()
)->
method
(
'resetOptionsByName'
);
$this
->
userOptionsManagerMock
->
expects
(
$this
->
never
()
)->
method
(
'setOption'
);
$this
->
mUserMock
->
expects
(
$this
->
once
()
)->
method
(
'saveSettings'
);
}
$request
=
$this
->
getSampleRequest
(
[
'reset'
=>
''
,
'resetkinds'
=>
'registered'
]
);
try
{
$response
=
$this
->
executeQuery
(
$request
);
if
(
$expectException
)
{
$this
->
fail
(
"Expected an ApiUsageException"
);
}
else
{
$this
->
assertEquals
(
self
::
SUCCESS
,
$response
);
}
}
catch
(
ApiUsageException
$e
)
{
if
(
!
$expectException
)
{
throw
$e
;
}
$this
->
assertNotNull
(
$e
->
getMessageObject
()
);
$this
->
assertApiErrorCode
(
'notloggedin'
,
$e
);
}
}
/**
* @dataProvider userScenarios
*/
public
function
testResetChangeOption
(
$isRegistered
,
$isNamed
,
$expectException
)
{
$this
->
mUserMock
->
method
(
'isRegistered'
)->
willReturn
(
$isRegistered
);
$this
->
mUserMock
->
method
(
'isNamed'
)->
willReturn
(
$isNamed
);
if
(
$expectException
)
{
$this
->
userOptionsManagerMock
->
expects
(
$this
->
never
()
)->
method
(
'resetOptionsByName'
);
$this
->
userOptionsManagerMock
->
expects
(
$this
->
never
()
)->
method
(
'setOption'
);
$this
->
mUserMock
->
expects
(
$this
->
never
()
)->
method
(
'saveSettings'
);
}
else
{
$this
->
userOptionsManagerMock
->
expects
(
$this
->
once
()
)->
method
(
'resetOptionsByName'
);
$expectedOptions
=
[
'willBeHappy'
=>
'Happy'
,
'name'
=>
'value'
,
];
$this
->
userOptionsManagerMock
->
expects
(
$this
->
exactly
(
count
(
$expectedOptions
)
)
)
->
method
(
'setOption'
)
->
willReturnCallback
(
function
(
$user
,
$oname
,
$val
)
use
(
&
$expectedOptions
)
{
$this
->
assertSame
(
$this
->
mUserMock
,
$user
);
$this
->
assertArrayHasKey
(
$oname
,
$expectedOptions
);
$this
->
assertSame
(
$expectedOptions
[
$oname
],
$val
);
unset
(
$expectedOptions
[
$oname
]
);
}
);
$this
->
mUserMock
->
expects
(
$this
->
once
()
)->
method
(
'saveSettings'
);
}
$args
=
[
'reset'
=>
''
,
'change'
=>
'willBeHappy=Happy'
,
'optionname'
=>
'name'
,
'optionvalue'
=>
'value'
];
try
{
$response
=
$this
->
executeQuery
(
$this
->
getSampleRequest
(
$args
)
);
if
(
$expectException
)
{
$this
->
fail
(
"Expected an ApiUsageException"
);
}
else
{
$this
->
assertEquals
(
self
::
SUCCESS
,
$response
);
}
}
catch
(
ApiUsageException
$e
)
{
if
(
!
$expectException
)
{
throw
$e
;
}
$this
->
assertNotNull
(
$e
->
getMessageObject
()
);
$this
->
assertApiErrorCode
(
'notloggedin'
,
$e
);
}
}
/**
* @dataProvider provideOptionManupulation
*/
public
function
testOptionManupulation
(
array
$params
,
array
$setOptions
,
?
array
$result
=
null
,
$message
=
''
)
{
$this
->
mUserMock
->
method
(
'isRegistered'
)->
willReturn
(
true
);
$this
->
mUserMock
->
method
(
'isNamed'
)->
willReturn
(
true
);
$this
->
userOptionsManagerMock
->
expects
(
$this
->
never
()
)
->
method
(
'resetOptionsByName'
);
$expectedOptions
=
[];
foreach
(
$setOptions
as
[
$opt
,
$val
]
)
{
$expectedOptions
[
$opt
]
=
$val
;
}
$this
->
userOptionsManagerMock
->
expects
(
$this
->
exactly
(
count
(
$setOptions
)
)
)
->
method
(
'setOption'
)
->
willReturnCallback
(
function
(
$user
,
$oname
,
$val
)
use
(
&
$expectedOptions
)
{
$this
->
assertSame
(
$this
->
mUserMock
,
$user
);
$this
->
assertArrayHasKey
(
$oname
,
$expectedOptions
);
$this
->
assertSame
(
$expectedOptions
[
$oname
],
$val
);
unset
(
$expectedOptions
[
$oname
]
);
}
);
if
(
$setOptions
)
{
$this
->
mUserMock
->
expects
(
$this
->
once
()
)
->
method
(
'saveSettings'
);
}
else
{
$this
->
mUserMock
->
expects
(
$this
->
never
()
)
->
method
(
'saveSettings'
);
}
$request
=
$this
->
getSampleRequest
(
$params
);
$response
=
$this
->
executeQuery
(
$request
);
if
(
!
$result
)
{
$result
=
self
::
SUCCESS
;
}
$this
->
assertEquals
(
$result
,
$response
,
$message
);
}
public
static
function
provideOptionManupulation
()
{
return
[
[
[
'change'
=>
'userjs-option=1'
],
[
[
'userjs-option'
,
'1'
]
],
null
,
'Setting userjs options'
,
],
[
[
'change'
=>
'willBeNull|willBeEmpty=|willBeHappy=Happy'
],
[
[
'willBeNull'
,
null
],
[
'willBeEmpty'
,
''
],
[
'willBeHappy'
,
'Happy'
],
],
null
,
'Basic option setting'
,
],
[
[
'change'
=>
'testradio=option2'
],
[
[
'testradio'
,
'option2'
]
],
null
,
'Changing radio options'
,
],
[
[
'change'
=>
'testradio'
],
[
[
'testradio'
,
null
]
],
null
,
'Resetting radio options'
,
],
[
[
'change'
=>
'unknownOption=1'
],
[],
[
'options'
=>
'success'
,
'warnings'
=>
[
'options'
=>
[
'warnings'
=>
"Validation error for
\"
unknownOption
\"
: not a valid preference."
],
],
],
'Unrecognized options should be rejected'
,
],
[
[
'change'
=>
'special=1'
],
[],
[
'options'
=>
'success'
,
'warnings'
=>
[
'options'
=>
[
'warnings'
=>
"Validation error for
\"
special
\"
: cannot be set by this module."
]
]
],
'Refuse setting special options'
,
],
[
[
'change'
=>
'testmultiselect-opt1=1|testmultiselect-opt2|'
.
'testmultiselect-opt3=|testmultiselect-opt4=0'
],
[
[
'testmultiselect-opt1'
,
true
],
[
'testmultiselect-opt2'
,
null
],
[
'testmultiselect-opt3'
,
false
],
[
'testmultiselect-opt4'
,
false
],
],
null
,
'Setting multiselect options'
,
],
[
[
'optionname'
=>
'name'
,
'optionvalue'
=>
'value'
],
[
[
'name'
,
'value'
]
],
null
,
'Setting options via optionname/optionvalue'
],
[
[
'optionname'
=>
'name'
],
[
[
'name'
,
null
]
],
null
,
'Resetting options via optionname without optionvalue'
,
],
[
[
'optionname'
=>
'name'
,
'optionvalue'
=>
str_repeat
(
'测试'
,
16383
)
],
[],
[
'options'
=>
'success'
,
'warnings'
=>
[
'options'
=>
[
'warnings'
=>
'Validation error for "name": value too long (no more than 65,530 bytes allowed).'
],
],
],
'Options with too long value should be rejected'
,
],
];
}
}
File Metadata
Details
Attached
Mime Type
text/x-php
Expires
Tue, Aug 18, 16:43 (3 w, 4 d ago)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
59/d9/a910f3f00ffc5a3f5cac7fc72daa
Default Alt Text
ApiOptionsTest.php (14 KB)
Attached To
Mode
rMWPROD MediaWiki Production
Attached
Detach File
Event Timeline
Log In to Comment