Page MenuHomeWickedGov Phorge

FilterValidatorTest.php
No OneTemporary

Size
22 KB
Referenced Files
None
Subscribers
None

FilterValidatorTest.php

<?php
namespace MediaWiki\Extension\AbuseFilter\Tests\Unit;
use Generator;
use MediaWiki\Config\ServiceOptions;
use MediaWiki\Extension\AbuseFilter\AbuseFilterPermissionManager;
use MediaWiki\Extension\AbuseFilter\ChangeTags\ChangeTagValidator;
use MediaWiki\Extension\AbuseFilter\Filter\AbstractFilter;
use MediaWiki\Extension\AbuseFilter\FilterValidator;
use MediaWiki\Extension\AbuseFilter\Parser\Exception\ExceptionBase;
use MediaWiki\Extension\AbuseFilter\Parser\Exception\InternalException;
use MediaWiki\Extension\AbuseFilter\Parser\Exception\UserVisibleException;
use MediaWiki\Extension\AbuseFilter\Parser\FilterEvaluator;
use MediaWiki\Extension\AbuseFilter\Parser\ParserStatus;
use MediaWiki\Extension\AbuseFilter\Parser\RuleCheckerFactory;
use MediaWiki\Permissions\Authority;
use MediaWiki\Status\Status;
use MediaWiki\Tests\Unit\Permissions\MockAuthorityTrait;
use MediaWikiUnitTestCase;
use PHPUnit\Framework\MockObject\MockObject;
/**
* @group Test
* @group AbuseFilter
* @group AbuseFilterSave
* @covers \MediaWiki\Extension\AbuseFilter\FilterValidator
*/
class FilterValidatorTest extends MediaWikiUnitTestCase {
use MockAuthorityTrait;
/**
* @param AbuseFilterPermissionManager|null $permissionManager
* @param FilterEvaluator|null $ruleChecker
* @param array $restrictions
* @param array $validFilterGroups
* @return FilterValidator
*/
private function getFilterValidator(
?AbuseFilterPermissionManager $permissionManager = null,
?FilterEvaluator $ruleChecker = null,
array $restrictions = [],
array $validFilterGroups = [ 'default' ]
): FilterValidator {
if ( !$ruleChecker ) {
$ruleChecker = $this->createMock( FilterEvaluator::class );
$ruleChecker->method( 'checkSyntax' )->willReturn(
new ParserStatus( null, [], 1 )
);
$ruleChecker->method( 'getUsedVars' )->willReturnCallback( static function ( string $rules ) {
preg_match_all( '/user_\w+/i', $rules, $matches, PREG_PATTERN_ORDER );
return array_map( 'strtolower', $matches[0] );
} );
}
$checkerFactory = $this->createMock( RuleCheckerFactory::class );
$checkerFactory->method( 'newRuleChecker' )->willReturn( $ruleChecker );
if ( !$permissionManager ) {
$permissionManager = $this->createMock( AbuseFilterPermissionManager::class );
$permissionManager->method( 'canEditFilter' )->willReturn( true );
}
return new FilterValidator(
$this->createMock( ChangeTagValidator::class ),
$checkerFactory,
$permissionManager,
new ServiceOptions(
FilterValidator::CONSTRUCTOR_OPTIONS,
[
'AbuseFilterActionRestrictions' => array_fill_keys( $restrictions, true ),
'AbuseFilterValidGroups' => $validFilterGroups,
'AbuseFilterProtectedVariables' => [ 'user_unnamed_ip' ],
]
)
);
}
/**
* @param array $actions
* @return AbstractFilter|MockObject
*/
private function getFilterWithActions( array $actions ): AbstractFilter {
$ret = $this->createMock( AbstractFilter::class );
$ret->method( 'getRules' )->willReturn( '1' );
$ret->method( 'getName' )->willReturn( 'Foo' );
$ret->method( 'getActions' )->willReturn( $actions );
return $ret;
}
/**
* Helper to check that $expectedError is null and $actual is good, or the messages match.
*/
private function assertFilterValidatorStatus( ?string $expectedError, Status $actual ): void {
if ( $expectedError ) {
// Most of the FilterValidator error statuses are marked as non-fatal errors, and
// assertStatusWarning() (despite its name) checks for non-fatal errors, not warnings.
// The name and the distinction is confusing. See also T309859.
$this->assertStatusWarning( $expectedError, $actual );
} else {
$this->assertStatusGood( $actual );
}
}
/**
* @param ExceptionBase|null $excep
* @param Status $expected
* @dataProvider provideSyntax
*/
public function testCheckValidSyntax( ?ExceptionBase $excep, Status $expected ) {
$ruleChecker = $this->createMock( FilterEvaluator::class );
$syntaxStatus = new ParserStatus( $excep, [], 1 );
$ruleChecker->method( 'checkSyntax' )->willReturn( $syntaxStatus );
$validator = $this->getFilterValidator( null, $ruleChecker );
$actual = $validator->checkValidSyntax( $this->createMock( AbstractFilter::class ) );
$this->assertStatusMessagesExactly( $expected, $actual );
}
public function provideSyntax(): Generator {
yield 'valid' => [ null, Status::newGood() ];
$excText = 'Internal error text';
yield 'invalid, internal error' => [
new InternalException( $excText ),
Status::newFatal( 'abusefilter-edit-badsyntax', $excText )
];
$excMsg = $this->getMockMessage( $excText );
$excep = $this->createMock( UserVisibleException::class );
$excep->method( 'getMessageObj' )->willReturn( $excMsg );
yield 'invalid, user error' => [ $excep, Status::newFatal( 'abusefilter-edit-badsyntax', $excMsg ) ];
}
/**
* @param string $rules
* @param string $name
* @param string|null $expectedError
* @dataProvider provideRequiredFields
*/
public function testCheckRequiredFields( string $rules, string $name, ?string $expectedError ) {
$filter = $this->createMock( AbstractFilter::class );
$filter->method( 'getRules' )->willReturn( $rules );
$filter->method( 'getName' )->willReturn( $name );
$actual = $this->getFilterValidator()->checkRequiredFields( $filter );
$this->assertFilterValidatorStatus( $expectedError, $actual );
}
public static function provideRequiredFields(): array {
return [
'valid' => [ '0', '0', null ],
'no rules' => [ '', 'bar', 'abusefilter-edit-missingfields' ],
'no name' => [ 'bar', ' ', 'abusefilter-edit-missingfields' ],
'no rules and no name' => [ '', '', 'abusefilter-edit-missingfields' ]
];
}
/**
* @param array $actions
* @param string|null $expectedError
* @dataProvider provideEmptyMessages
*/
public function testCheckEmptyMessages( array $actions, ?string $expectedError ) {
$actual = $this->getFilterValidator()->checkEmptyMessages( $this->getFilterWithActions( $actions ) );
$this->assertFilterValidatorStatus( $expectedError, $actual );
}
public static function provideEmptyMessages(): array {
return [
'valid' => [ [ 'warn' => [ 'foo' ], 'disallow' => [ 'bar' ] ], null ],
'empty warn' => [ [ 'warn' => [ '' ], 'disallow' => [ 'bar' ] ], 'abusefilter-edit-invalid-warn-message' ],
'empty disallow' =>
[ [ 'warn' => [ 'foo' ], 'disallow' => [ '' ] ], 'abusefilter-edit-invalid-disallow-message' ],
'both empty' => [ [ 'warn' => [ '' ], 'disallow' => [ '' ] ], 'abusefilter-edit-invalid-warn-message' ]
];
}
/**
* @param bool $enabled
* @param bool $deleted
* @param string|null $expectedError
* @dataProvider provideConflictingFields
*/
public function testCheckConflictingFields( bool $enabled, bool $deleted, ?string $expectedError ) {
$filter = $this->createMock( AbstractFilter::class );
$filter->method( 'isEnabled' )->willReturn( $enabled );
$filter->method( 'isDeleted' )->willReturn( $deleted );
$actual = $this->getFilterValidator()->checkConflictingFields( $filter );
$this->assertFilterValidatorStatus( $expectedError, $actual );
}
public static function provideConflictingFields(): array {
return [
'valid' => [ true, false, null ],
'invalid' => [ true, true, 'abusefilter-edit-deleting-enabled' ]
];
}
/**
* @param bool $canEditNew
* @param bool $canEditOrig
* @param string|null $expectedError
* @dataProvider provideCheckGlobalFilterEditPermission
*/
public function testCheckGlobalFilterEditPermission(
bool $canEditNew,
bool $canEditOrig,
?string $expectedError
) {
$permManager = $this->createMock( AbuseFilterPermissionManager::class );
$permManager->method( 'canEditFilter' )->willReturnOnConsecutiveCalls( $canEditNew, $canEditOrig );
$validator = $this->getFilterValidator( $permManager );
$actual = $validator->checkGlobalFilterEditPermission(
$this->createMock( Authority::class ),
$this->createMock( AbstractFilter::class ),
$this->createMock( AbstractFilter::class )
);
if ( $expectedError ) {
$this->assertStatusError( $expectedError, $actual );
} else {
$this->assertStatusGood( $actual );
}
}
public static function provideCheckGlobalFilterEditPermission(): array {
return [
'none' => [ false, false, 'abusefilter-edit-notallowed-global' ],
'cur only' => [ true, false, 'abusefilter-edit-notallowed-global' ],
'orig only' => [ false, true, 'abusefilter-edit-notallowed-global' ],
'both' => [ true, true, null ]
];
}
/**
* @param array $actions
* @param bool $isGlobal
* @param string|null $expectedError
* @dataProvider provideMessagesOnGlobalFilters
*/
public function testCheckMessagesOnGlobalFilters( array $actions, bool $isGlobal, ?string $expectedError ) {
$filter = $this->getFilterWithActions( $actions );
$filter->method( 'isGlobal' )->willReturn( $isGlobal );
$actual = $this->getFilterValidator()->checkMessagesOnGlobalFilters( $filter );
$this->assertFilterValidatorStatus( $expectedError, $actual );
}
public static function provideMessagesOnGlobalFilters(): array {
return [
'valid' => [
[ 'warn' => [ 'abusefilter-warning' ], 'disallow' => [ 'abusefilter-disallowed' ] ],
true,
null
],
'custom warn' => [
[ 'warn' => [ 'foo' ], 'disallow' => [ 'abusefilter-disallowed' ] ],
true,
'abusefilter-edit-notallowed-global-custom-msg'
],
'custom disallow' => [
[ 'warn' => [ 'abusefilter-warn' ], 'disallow' => [ 'bar' ] ],
true,
'abusefilter-edit-notallowed-global-custom-msg'
],
'both custom' => [
[ 'warn' => [ 'xxx' ], 'disallow' => [ 'yyy' ] ],
true,
'abusefilter-edit-notallowed-global-custom-msg'
],
'both custom but not global' => [ [ 'warn' => [ 'xxx' ], 'disallow' => [ 'yyy' ] ], false, null ]
];
}
/**
* @param AbstractFilter $newFilter
* @param AbstractFilter $oldFilter
* @param array $restrictions
* @param AbuseFilterPermissionManager $permManager
* @param string|null $expectedError
* @dataProvider provideRestrictedActions
*/
public function testCheckRestrictedActions(
AbstractFilter $newFilter,
AbstractFilter $oldFilter,
array $restrictions,
AbuseFilterPermissionManager $permManager,
?string $expectedError
) {
$validator = $this->getFilterValidator( $permManager, null, $restrictions );
$performer = $this->createMock( Authority::class );
$actual = $validator->checkRestrictedActions( $performer, $newFilter, $oldFilter );
$this->assertFilterValidatorStatus( $expectedError, $actual );
}
public function provideRestrictedActions(): Generator {
$canModifyRestrictedPM = $this->createMock( AbuseFilterPermissionManager::class );
$canModifyRestrictedPM->method( 'canEditFilterWithRestrictedActions' )->willReturn( true );
$cannotModifyRestrictedPM = $this->createMock( AbuseFilterPermissionManager::class );
$cannotModifyRestrictedPM->method( 'canEditFilterWithRestrictedActions' )->willReturn( false );
$newFilter = $oldFilter = $this->getFilterWithActions( [] );
yield 'no restricted actions, with modify-restricted' =>
[ $newFilter, $oldFilter, [], $canModifyRestrictedPM, null ];
yield 'no restricted actions, no modify-restricted' =>
[ $newFilter, $oldFilter, [], $cannotModifyRestrictedPM, null ];
$restrictions = [ 'degroup' ];
$restricted = $this->getFilterWithActions( [ 'warn' => [ 'foo' ], 'degroup' => [] ] );
$unrestricted = $this->getFilterWithActions( [ 'warn' => [ 'foo' ] ] );
yield 'restricted actions in new version, no modify-restricted' =>
[ $restricted, $unrestricted, $restrictions, $cannotModifyRestrictedPM, 'abusefilter-edit-restricted' ];
yield 'restricted actions in old version, no modify-restricted' =>
[ $unrestricted, $restricted, $restrictions, $cannotModifyRestrictedPM, 'abusefilter-edit-restricted' ];
yield 'restricted actions in new version, with modify-restricted' =>
[ $restricted, $unrestricted, $restrictions, $canModifyRestrictedPM, null ];
yield 'restricted actions in old version, with modify-restricted' =>
[ $unrestricted, $restricted, $restrictions, $canModifyRestrictedPM, null ];
}
public function testCheckProtectedVariablesGood() {
$filter = $this->createMock( AbstractFilter::class );
$filter->method( 'getRules' )->willReturn( 'user_unnamed_ip' );
$filter->method( 'isProtected' )->willReturn( true );
$this->assertStatusGood(
$this->getFilterValidator()->checkProtectedVariables( $filter )
);
}
public function testCheckProtectedVariablesUpdatedFilter() {
$oldFilterUnprotected = $this->createMock( AbstractFilter::class );
$oldFilterUnprotected->method( 'getRules' )->willReturn( 'user_name' );
$oldFilterUnprotected->method( 'isProtected' )->willReturn( false );
$oldFilterProtected = $this->createMock( AbstractFilter::class );
$oldFilterProtected->method( 'getRules' )->willReturn( 'user_unnamed_ip' );
$oldFilterProtected->method( 'isProtected' )->willReturn( true );
$newFilterUnprotected = $this->createMock( AbstractFilter::class );
$newFilterUnprotected->method( 'getRules' )->willReturn( 'user_name' );
$newFilterUnprotected->method( 'isProtected' )->willReturn( false );
$newFilterProtected = $this->createMock( AbstractFilter::class );
$newFilterProtected->method( 'getRules' )->willReturn( 'user_unnamed_ip' );
$newFilterProtected->method( 'isProtected' )->willReturn( true );
$this->assertStatusGood(
$this->getFilterValidator()->checkProtectedVariables( $newFilterUnprotected, $oldFilterProtected )
);
$this->assertStatusGood(
$this->getFilterValidator()->checkProtectedVariables( $newFilterProtected, $oldFilterUnprotected )
);
}
public function testCheckProtectedVariablesError() {
$filter = $this->createMock( AbstractFilter::class );
$filter->method( 'getRules' )->willReturn( 'user_unnamed_ip' );
$filter->method( 'isProtected' )->willReturn( false );
$this->assertFilterValidatorStatus(
'abusefilter-edit-protected-variable-not-protected',
$this->getFilterValidator()->checkProtectedVariables( $filter )
);
}
/**
* @dataProvider provideCheckCanViewProtectedVariables
*/
public function testCheckCanViewProtectedVariables( $data ) {
$performer = $this->mockRegisteredAuthorityWithPermissions( $data[ 'rights' ] );
$permManager = $this->createMock( AbuseFilterPermissionManager::class );
$permManager->method( 'getForbiddenVariables' )->willReturn( [] );
$filter = $this->createMock( AbstractFilter::class );
$filter->method( 'getRules' )->willReturn( $data[ 'rules' ] );
$this->assertStatusGood( $this->getFilterValidator( $permManager )
->checkCanViewProtectedVariables( $performer, $filter )
);
}
/**
* @dataProvider provideCheckCanViewProtectedVariablesError
*/
public function testCheckCanViewProtectedVariablesError( $data ) {
$performer = $this->mockRegisteredAuthorityWithPermissions( $data[ 'rights' ] );
$permManager = $this->createMock( AbuseFilterPermissionManager::class );
$permManager->method( 'getForbiddenVariables' )->willReturn( [ 'user_unnamed_ip' ] );
$filter = $this->createMock( AbstractFilter::class );
$filter->method( 'getRules' )->willReturn( $data[ 'rules' ] );
$this->assertFilterValidatorStatus(
'abusefilter-edit-protected-variable',
$this->getFilterValidator( $permManager )->checkCanViewProtectedVariables( $performer, $filter )
);
}
public static function provideCheckCanViewProtectedVariables(): array {
return [
'cannot view, no protected vars' => [
[
'rights' => [],
'rules' => 'user_name'
],
0
],
'can view, protected vars' => [
[
'rights' => [ 'abusefilter-access-protected-vars' ],
'rules' => 'user_unnamed_ip'
],
0
],
'can view, no protected vars' => [
[
'rights' => [ 'abusefilter-access-protected-vars' ],
'rules' => 'user_name'
],
0
]
];
}
public static function provideCheckCanViewProtectedVariablesError(): array {
return [
'cannot view, protected vars' => [
[
'rights' => [],
'rules' => 'user_unnamed_ip'
]
],
];
}
public function testCheckAllTags_noTags() {
$this->assertFilterValidatorStatus(
'tags-create-no-name',
$this->getFilterValidator()->checkAllTags( [] )
);
}
/**
* @param array $params Throttle parameters
* @param string|null $expectedError The expected error message. Null if validations should pass
* @dataProvider provideThrottleParameters
*/
public function testCheckThrottleParameters( array $params, ?string $expectedError ) {
$actual = $this->getFilterValidator()->checkThrottleParameters( $params );
$this->assertFilterValidatorStatus( $expectedError, $actual );
}
/**
* Data provider for testCheckThrottleParameters
* @return array
*/
public static function provideThrottleParameters() {
return [
[ [ '1', '5,23', 'user', 'ip', 'page,range', 'ip,user', 'range,ip' ], null ],
[ [ '1', '5.3,23', 'user', 'ip' ], 'abusefilter-edit-invalid-throttlecount' ],
[ [ '1', '-3,23', 'user', 'ip' ], 'abusefilter-edit-invalid-throttlecount' ],
[ [ '1', '5,2.3', 'user', 'ip' ], 'abusefilter-edit-invalid-throttleperiod' ],
[ [ '1', '4,-14', 'user', 'ip' ], 'abusefilter-edit-invalid-throttleperiod' ],
[ [ '1', '3,33,44', 'user', 'ip' ], 'abusefilter-edit-invalid-throttleperiod' ],
[ [ '1', '3,33' ], 'abusefilter-edit-empty-throttlegroups' ],
[ [ '1', '3,33', 'user', 'ip,foo,user' ], 'abusefilter-edit-invalid-throttlegroups' ],
[ [ '1', '3,33', 'foo', 'ip,user' ], 'abusefilter-edit-invalid-throttlegroups' ],
[ [ '1', '3,33', 'foo', 'ip,user,bar' ], 'abusefilter-edit-invalid-throttlegroups' ],
[ [ '1', '3,33', 'user', 'ip,page,user' ], null ],
[
[ '1', '3,33', 'ip', 'user', 'user,ip', 'ip,user', 'user,ip,user', 'user', 'ip,ip,user' ],
'abusefilter-edit-duplicated-throttlegroups'
],
[ [ '1', '3,33', 'ip,ip,user' ], 'abusefilter-edit-duplicated-throttlegroups' ],
[ [ '1', '3,33', 'user,ip', 'ip,user' ], 'abusefilter-edit-duplicated-throttlegroups' ],
];
}
/**
* @param AbstractFilter $newFilter
* @param string|null $expectedError
* @param AbuseFilterPermissionManager|null $permissionManager
* @param FilterEvaluator|null $ruleChecker
* @param array $restrictions
* @param bool $isFatalError
* @dataProvider provideCheckAll
*/
public function testCheckAll(
AbstractFilter $newFilter,
?string $expectedError,
?AbuseFilterPermissionManager $permissionManager = null,
?FilterEvaluator $ruleChecker = null,
array $restrictions = [],
bool $isFatalError = false
) {
$validator = $this->getFilterValidator( $permissionManager, $ruleChecker, $restrictions );
$origFilter = $this->createMock( AbstractFilter::class );
$actual = $validator->checkAll( $newFilter, $origFilter, $this->createMock( Authority::class ) );
if ( $expectedError && $isFatalError ) {
$this->assertStatusError( $expectedError, $actual );
} elseif ( $expectedError ) {
$this->assertStatusWarning( $expectedError, $actual );
} else {
$this->assertStatusGood( $actual );
}
}
public function provideCheckAll(): Generator {
$noopFilter = $this->createMock( AbstractFilter::class );
$noopFilter->method( 'getRules' )->willReturn( '1' );
$noopFilter->method( 'getName' )->willReturn( 'Foo' );
$noopFilter->method( 'isEnabled' )->willReturn( true );
$ruleChecker = $this->createMock( FilterEvaluator::class );
$syntaxStatus = new ParserStatus( $this->createMock( UserVisibleException::class ), [], 1 );
$ruleChecker->method( 'checkSyntax' )->willReturn( $syntaxStatus );
yield 'invalid syntax' => [ $noopFilter, 'abusefilter-edit-badsyntax', null, $ruleChecker ];
$missingFieldsFilter = $this->createMock( AbstractFilter::class );
$missingFieldsFilter->method( 'getRules' )->willReturn( '' );
$missingFieldsFilter->method( 'getName' )->willReturn( '' );
yield 'missing required fields' => [ $missingFieldsFilter, 'abusefilter-edit-missingfields' ];
$conflictFieldsFilter = $this->createMock( AbstractFilter::class );
$conflictFieldsFilter->method( 'getRules' )->willReturn( '1' );
$conflictFieldsFilter->method( 'getName' )->willReturn( 'Foo' );
$conflictFieldsFilter->method( 'isEnabled' )->willReturn( true );
$conflictFieldsFilter->method( 'isDeleted' )->willReturn( true );
yield 'conflicting fields' => [ $conflictFieldsFilter, 'abusefilter-edit-deleting-enabled' ];
yield 'invalid tags' => [ $this->getFilterWithActions( [ 'tag' => [] ] ), 'tags-create-no-name' ];
yield 'missing required messages' =>
[ $this->getFilterWithActions( [ 'warn' => [ '' ] ] ), 'abusefilter-edit-invalid-warn-message' ];
yield 'invalid throttle params' => [
$this->getFilterWithActions( [ 'throttle' => [ '1', '5.3,23', 'user', 'ip' ] ] ),
'abusefilter-edit-invalid-throttlecount'
];
$permManager = $this->createMock( AbuseFilterPermissionManager::class );
$permManager->method( 'canEditFilter' )->willReturn( false );
yield 'global filter, no modify-global' => [ $noopFilter, 'abusefilter-edit-notallowed-global', $permManager,
null, [], true ];
$customWarnFilter = $this->getFilterWithActions( [ 'warn' => [ 'foo' ] ] );
$customWarnFilter->method( 'isGlobal' )->willReturn( true );
yield 'global filter, custom message' => [ $customWarnFilter, 'abusefilter-edit-notallowed-global-custom-msg' ];
$permManager = $this->createMock( AbuseFilterPermissionManager::class );
$permManager->method( 'canEditFilter' )->willReturn( true );
$permManager->method( 'canEditFilterWithRestrictedActions' )->willReturn( false );
$restrictedFilter = $this->getFilterWithActions( [ 'degroup' => [] ] );
yield 'restricted actions' => [
$restrictedFilter,
'abusefilter-edit-restricted',
$permManager,
null,
[ 'degroup' ]
];
$invalidGroupFilter = $this->createMock( AbstractFilter::class );
$invalidGroupFilter->method( 'getRules' )->willReturn( 'true' );
$invalidGroupFilter->method( 'getName' )->willReturn( 'Foo' );
$invalidGroupFilter->expects( $this->atLeastOnce() )->method( 'getGroup' )->willReturn( 'xxx-invalid' );
yield 'invalid group' => [ $invalidGroupFilter, 'abusefilter-edit-invalid-group' ];
$filter = $this->createMock( AbstractFilter::class );
$filter->method( 'getRules' )->willReturn( 'true' );
$filter->method( 'getName' )->willReturn( 'Foo' );
$filter->method( 'getGroup' )->willReturn( 'default' );
yield 'valid' => [ $filter, null ];
}
/**
* @param string $group
* @param string[] $validGroups
* @param string|null $expectedError
* @dataProvider provideGroups
*/
public function testCheckGroup( string $group, array $validGroups, ?string $expectedError ) {
$filter = $this->createMock( AbstractFilter::class );
$filter->expects( $this->atLeastOnce() )->method( 'getGroup' )->willReturn( $group );
$actual = $this->getFilterValidator( null, null, [], $validGroups )->checkGroup( $filter );
$this->assertFilterValidatorStatus( $expectedError, $actual );
}
public static function provideGroups(): Generator {
$allowed = [ 'default' ];
yield 'Default, pass' => [ 'default', $allowed, null ];
$extraGroup = 'foobar';
$allowed[] = $extraGroup;
yield 'Extra, pass' => [ $extraGroup, $allowed, null ];
yield 'Unknown, fail' => [ 'some-unknown-group', $allowed, 'abusefilter-edit-invalid-group' ];
}
}

File Metadata

Mime Type
text/x-php
Expires
Fri, Jul 3, 18:52 (1 d, 13 h)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
0d/72/7f3e58112a59b6889df5cdcd9ac6
Default Alt Text
FilterValidatorTest.php (22 KB)

Event Timeline