Move some validation logic from ApiStructureTest to ParamValidator
ApiStructureTest has a lot of logic for validating Action API settings arrays during CI. Some of that logic should be part of ParamValidator instead. Bug: T242887 Change-Id: I3c3d23e38456de19179ae3e5855397316b6e4c40 Depends-On: I04de72d731b94468d8a12b35df67f359382b3742
This commit is contained in:
parent
c2b1525908
commit
d4c2f0d899
32 changed files with 2579 additions and 523 deletions
|
|
@ -59,6 +59,39 @@ class NamespaceDef extends EnumDef {
|
|||
return parent::normalizeSettings( $settings );
|
||||
}
|
||||
|
||||
public function checkSettings( string $name, $settings, array $options, array $ret ) : array {
|
||||
$ret = parent::checkSettings( $name, $settings, $options, $ret );
|
||||
|
||||
$ret['allowedKeys'] = array_merge( $ret['allowedKeys'], [
|
||||
self::PARAM_EXTRA_NAMESPACES,
|
||||
] );
|
||||
|
||||
if ( !empty( $settings[ParamValidator::PARAM_ISMULTI] ) &&
|
||||
( $settings[ParamValidator::PARAM_ALL] ?? true ) !== true &&
|
||||
!isset( $ret['issues'][ParamValidator::PARAM_ALL] )
|
||||
) {
|
||||
$ret['issues'][ParamValidator::PARAM_ALL] =
|
||||
'PARAM_ALL cannot be false or a string for namespace-type parameters';
|
||||
}
|
||||
|
||||
$ns = $settings[self::PARAM_EXTRA_NAMESPACES] ?? [];
|
||||
if ( !is_array( $ns ) ) {
|
||||
$type = gettype( $ns );
|
||||
} elseif ( $ns === [] ) {
|
||||
$type = 'integer[]';
|
||||
} else {
|
||||
$types = array_unique( array_map( 'gettype', $ns ) );
|
||||
$type = implode( '|', $types );
|
||||
$type = count( $types ) > 1 ? "($type)[]" : "{$type}[]";
|
||||
}
|
||||
if ( $type !== 'integer[]' ) {
|
||||
$ret['issues'][self::PARAM_EXTRA_NAMESPACES] =
|
||||
"PARAM_EXTRA_NAMESPACES must be an integer[], got $type";
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
public function getParamInfo( $name, array $settings, array $options ) {
|
||||
$info = parent::getParamInfo( $name, $settings, $options );
|
||||
|
||||
|
|
|
|||
|
|
@ -75,6 +75,55 @@ class UserDef extends TypeDef {
|
|||
return parent::normalizeSettings( $settings );
|
||||
}
|
||||
|
||||
public function checkSettings( string $name, $settings, array $options, array $ret ) : array {
|
||||
$ret = parent::checkSettings( $name, $settings, $options, $ret );
|
||||
|
||||
$ret['allowedKeys'] = array_merge( $ret['allowedKeys'], [
|
||||
self::PARAM_ALLOWED_USER_TYPES, self::PARAM_RETURN_OBJECT,
|
||||
] );
|
||||
|
||||
if ( !is_bool( $settings[self::PARAM_RETURN_OBJECT] ?? false ) ) {
|
||||
$ret['issues'][self::PARAM_RETURN_OBJECT] = 'PARAM_RETURN_OBJECT must be boolean, got '
|
||||
. gettype( $settings[self::PARAM_RETURN_OBJECT] );
|
||||
}
|
||||
|
||||
$hasId = false;
|
||||
if ( isset( $settings[self::PARAM_ALLOWED_USER_TYPES] ) ) {
|
||||
if ( !is_array( $settings[self::PARAM_ALLOWED_USER_TYPES] ) ) {
|
||||
$ret['issues'][self::PARAM_ALLOWED_USER_TYPES] = 'PARAM_ALLOWED_USER_TYPES must be an array, '
|
||||
. 'got ' . gettype( $settings[self::PARAM_ALLOWED_USER_TYPES] );
|
||||
} elseif ( $settings[self::PARAM_ALLOWED_USER_TYPES] === [] ) {
|
||||
$ret['issues'][self::PARAM_ALLOWED_USER_TYPES] = 'PARAM_ALLOWED_USER_TYPES cannot be empty';
|
||||
} else {
|
||||
$bad = array_diff(
|
||||
$settings[self::PARAM_ALLOWED_USER_TYPES],
|
||||
[ 'name', 'ip', 'cidr', 'interwiki', 'id' ]
|
||||
);
|
||||
if ( $bad ) {
|
||||
$ret['issues'][self::PARAM_ALLOWED_USER_TYPES] =
|
||||
'PARAM_ALLOWED_USER_TYPES contains invalid values: ' . implode( ', ', $bad );
|
||||
}
|
||||
|
||||
$hasId = in_array( 'id', $settings[self::PARAM_ALLOWED_USER_TYPES], true );
|
||||
}
|
||||
}
|
||||
|
||||
if ( !empty( $settings[ParamValidator::PARAM_ISMULTI] ) &&
|
||||
( $hasId || !empty( $settings[self::PARAM_RETURN_OBJECT] ) ) &&
|
||||
(
|
||||
( $settings[ParamValidator::PARAM_ISMULTI_LIMIT1] ?? 100 ) > 10 ||
|
||||
( $settings[ParamValidator::PARAM_ISMULTI_LIMIT2] ?? 100 ) > 10
|
||||
)
|
||||
) {
|
||||
$ret['issues'][] = 'Multi-valued user-type parameters with PARAM_RETURN_OBJECT or allowing IDs '
|
||||
. 'should set low values (<= 10) for PARAM_ISMULTI_LIMIT1 and PARAM_ISMULTI_LIMIT2.'
|
||||
. ' (Note that "<= 10" is arbitrary. If something hits this, we can investigate a real limit '
|
||||
. 'once we have a real use case to look at.)';
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process $value to a UserIdentity, if possible
|
||||
* @param string $value
|
||||
|
|
|
|||
|
|
@ -98,21 +98,11 @@ class ApiParamValidator {
|
|||
}
|
||||
|
||||
/**
|
||||
* Adjust certain settings where ParamValidator differs from historical Action API behavior
|
||||
* @param array|mixed $settings
|
||||
* Map deprecated styles for messages for ParamValidator
|
||||
* @param array $settings
|
||||
* @return array
|
||||
*/
|
||||
public function normalizeSettings( $settings ) : array {
|
||||
$settings = $this->paramValidator->normalizeSettings( $settings );
|
||||
|
||||
if ( !isset( $settings[ParamValidator::PARAM_IGNORE_UNRECOGNIZED_VALUES] ) ) {
|
||||
$settings[ParamValidator::PARAM_IGNORE_UNRECOGNIZED_VALUES] = true;
|
||||
}
|
||||
|
||||
if ( !isset( $settings[IntegerDef::PARAM_IGNORE_RANGE] ) ) {
|
||||
$settings[IntegerDef::PARAM_IGNORE_RANGE] = empty( $settings[ApiBase::PARAM_RANGE_ENFORCE] );
|
||||
}
|
||||
|
||||
private function mapDeprecatedSettingsMessages( array $settings ) : array {
|
||||
if ( isset( $settings[EnumDef::PARAM_DEPRECATED_VALUES] ) ) {
|
||||
foreach ( $settings[EnumDef::PARAM_DEPRECATED_VALUES] as &$v ) {
|
||||
if ( $v === null || $v === true || $v instanceof MessageValue ) {
|
||||
|
|
@ -137,6 +127,193 @@ class ApiParamValidator {
|
|||
return $settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjust certain settings where ParamValidator differs from historical Action API behavior
|
||||
* @param array|mixed $settings
|
||||
* @return array
|
||||
*/
|
||||
public function normalizeSettings( $settings ) : array {
|
||||
if ( is_array( $settings ) ) {
|
||||
if ( !isset( $settings[ParamValidator::PARAM_IGNORE_UNRECOGNIZED_VALUES] ) ) {
|
||||
$settings[ParamValidator::PARAM_IGNORE_UNRECOGNIZED_VALUES] = true;
|
||||
}
|
||||
|
||||
if ( !isset( $settings[IntegerDef::PARAM_IGNORE_RANGE] ) ) {
|
||||
$settings[IntegerDef::PARAM_IGNORE_RANGE] = empty( $settings[ApiBase::PARAM_RANGE_ENFORCE] );
|
||||
}
|
||||
|
||||
$settings = $this->mapDeprecatedSettingsMessages( $settings );
|
||||
}
|
||||
|
||||
return $this->paramValidator->normalizeSettings( $settings );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check an API settings message
|
||||
* @param ApiBase $module
|
||||
* @param string $key
|
||||
* @param mixed $value
|
||||
* @param array &$ret
|
||||
*/
|
||||
private function checkSettingsMessage( ApiBase $module, string $key, $value, array &$ret ) : void {
|
||||
$msg = ApiBase::makeMessage( $value, $module );
|
||||
if ( $msg instanceof Message ) {
|
||||
$ret['messages'][] = $this->messageConverter->convertMessage( $msg );
|
||||
} else {
|
||||
$ret['issues'][] = "Message specification for $key is not valid";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check settings for the Action API.
|
||||
* @param ApiBase $module
|
||||
* @param array $params All module params to test
|
||||
* @param string $name Parameter to test
|
||||
* @param array $options Options array
|
||||
* @return array As for ParamValidator::checkSettings()
|
||||
*/
|
||||
public function checkSettings(
|
||||
ApiBase $module, array $params, string $name, array $options
|
||||
) : array {
|
||||
$options['module'] = $module;
|
||||
$settings = $params[$name];
|
||||
if ( is_array( $settings ) ) {
|
||||
$settings = $this->mapDeprecatedSettingsMessages( $settings );
|
||||
}
|
||||
$ret = $this->paramValidator->checkSettings(
|
||||
$module->encodeParamName( $name ), $settings, $options
|
||||
);
|
||||
|
||||
$ret['allowedKeys'] = array_merge( $ret['allowedKeys'], [
|
||||
ApiBase::PARAM_RANGE_ENFORCE, ApiBase::PARAM_HELP_MSG, ApiBase::PARAM_HELP_MSG_APPEND,
|
||||
ApiBase::PARAM_HELP_MSG_INFO, ApiBase::PARAM_HELP_MSG_PER_VALUE, ApiBase::PARAM_TEMPLATE_VARS,
|
||||
] );
|
||||
|
||||
if ( !is_array( $settings ) ) {
|
||||
$settings = [];
|
||||
}
|
||||
|
||||
if ( array_key_exists( ApiBase::PARAM_VALUE_LINKS, $settings ) ) {
|
||||
$ret['issues'][ApiBase::PARAM_VALUE_LINKS]
|
||||
= 'PARAM_VALUE_LINKS was deprecated in MediaWiki 1.35';
|
||||
}
|
||||
|
||||
if ( !is_bool( $settings[ApiBase::PARAM_RANGE_ENFORCE] ?? false ) ) {
|
||||
$ret['issues'][ApiBase::PARAM_RANGE_ENFORCE] = 'PARAM_RANGE_ENFORCE must be boolean, got '
|
||||
. gettype( $settings[ApiBase::PARAM_RANGE_ENFORCE] );
|
||||
}
|
||||
|
||||
if ( isset( $settings[ApiBase::PARAM_HELP_MSG] ) ) {
|
||||
$this->checkSettingsMessage(
|
||||
$module, 'PARAM_HELP_MSG', $settings[ApiBase::PARAM_HELP_MSG], $ret
|
||||
);
|
||||
}
|
||||
|
||||
if ( isset( $settings[ApiBase::PARAM_HELP_MSG_APPEND] ) ) {
|
||||
if ( !is_array( $settings[ApiBase::PARAM_HELP_MSG_APPEND] ) ) {
|
||||
$ret['issues'][ApiBase::PARAM_HELP_MSG_APPEND] = 'PARAM_HELP_MSG_APPEND must be an array, got '
|
||||
. gettype( $settings[ApiBase::PARAM_HELP_MSG_APPEND] );
|
||||
} else {
|
||||
foreach ( $settings[ApiBase::PARAM_HELP_MSG_APPEND] as $k => $v ) {
|
||||
$this->checkSettingsMessage( $module, "PARAM_HELP_MSG_APPEND[$k]", $v, $ret );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( isset( $settings[ApiBase::PARAM_HELP_MSG_INFO] ) ) {
|
||||
if ( !is_array( $settings[ApiBase::PARAM_HELP_MSG_INFO] ) ) {
|
||||
$ret['issues'][ApiBase::PARAM_HELP_MSG_INFO] = 'PARAM_HELP_MSG_INFO must be an array, got '
|
||||
. gettype( $settings[ApiBase::PARAM_HELP_MSG_INFO] );
|
||||
} else {
|
||||
$path = $module->getModulePath();
|
||||
foreach ( $settings[ApiBase::PARAM_HELP_MSG_INFO] as $k => $v ) {
|
||||
if ( !is_array( $v ) ) {
|
||||
$ret['issues'][] = "PARAM_HELP_MSG_INFO[$k] must be an array, got " . gettype( $v );
|
||||
} elseif ( !is_string( $v[0] ) ) {
|
||||
$ret['issues'][] = "PARAM_HELP_MSG_INFO[$k][0] must be a string, got " . gettype( $v[0] );
|
||||
} else {
|
||||
$v[0] = "apihelp-{$path}-paraminfo-{$v[0]}";
|
||||
$this->checkSettingsMessage( $module, "PARAM_HELP_MSG_INFO[$k]", $v, $ret );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( isset( $settings[ApiBase::PARAM_HELP_MSG_PER_VALUE] ) ) {
|
||||
if ( !is_array( $settings[ApiBase::PARAM_HELP_MSG_PER_VALUE] ) ) {
|
||||
$ret['issues'][ApiBase::PARAM_HELP_MSG_PER_VALUE] = 'PARAM_HELP_MSG_PER_VALUE must be an array,'
|
||||
. ' got ' . gettype( $settings[ApiBase::PARAM_HELP_MSG_PER_VALUE] );
|
||||
} elseif ( !is_array( $settings[ParamValidator::PARAM_TYPE] ?? '' ) ) {
|
||||
$ret['issues'][ApiBase::PARAM_HELP_MSG_PER_VALUE] = 'PARAM_HELP_MSG_PER_VALUE can only be used '
|
||||
. 'with PARAM_TYPE as an array';
|
||||
} else {
|
||||
$values = array_map( 'strval', $settings[ParamValidator::PARAM_TYPE] );
|
||||
foreach ( $settings[ApiBase::PARAM_HELP_MSG_PER_VALUE] as $k => $v ) {
|
||||
if ( !in_array( (string)$k, $values, true ) ) {
|
||||
// Or should this be allowed?
|
||||
$ret['issues'][] = "PARAM_HELP_MSG_PER_VALUE contains \"$k\", which is not in PARAM_TYPE.";
|
||||
}
|
||||
$this->checkSettingsMessage( $module, "PARAM_HELP_MSG_PER_VALUE[$k]", $v, $ret );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( isset( $settings[ApiBase::PARAM_TEMPLATE_VARS] ) ) {
|
||||
if ( !is_array( $settings[ApiBase::PARAM_TEMPLATE_VARS] ) ) {
|
||||
$ret['issues'][ApiBase::PARAM_TEMPLATE_VARS] = 'PARAM_TEMPLATE_VARS must be an array,'
|
||||
. ' got ' . gettype( $settings[ApiBase::PARAM_TEMPLATE_VARS] );
|
||||
} elseif ( $settings[ApiBase::PARAM_TEMPLATE_VARS] === [] ) {
|
||||
$ret['issues'][ApiBase::PARAM_TEMPLATE_VARS] = 'PARAM_TEMPLATE_VARS cannot be the empty array';
|
||||
} else {
|
||||
foreach ( $settings[ApiBase::PARAM_TEMPLATE_VARS] as $key => $target ) {
|
||||
if ( !preg_match( '/^[^{}]+$/', $key ) ) {
|
||||
$ret['issues'][] = "PARAM_TEMPLATE_VARS keys may not contain '{' or '}', got \"$key\"";
|
||||
} elseif ( strpos( $name, '{' . $key . '}' ) === false ) {
|
||||
$ret['issues'][] = "Parameter name must contain PARAM_TEMPLATE_VARS key {{$key}}";
|
||||
}
|
||||
if ( !is_string( $target ) && !is_int( $target ) ) {
|
||||
$ret['issues'][] = "PARAM_TEMPLATE_VARS[$key] has invalid target type " . gettype( $target );
|
||||
} elseif ( !isset( $params[$target] ) ) {
|
||||
$ret['issues'][] = "PARAM_TEMPLATE_VARS[$key] target parameter \"$target\" does not exist";
|
||||
} else {
|
||||
$settings2 = $params[$target];
|
||||
if ( empty( $settings2[ParamValidator::PARAM_ISMULTI] ) ) {
|
||||
$ret['issues'][] = "PARAM_TEMPLATE_VARS[$key] target parameter \"$target\" must have "
|
||||
. 'PARAM_ISMULTI = true';
|
||||
}
|
||||
if ( isset( $settings2[ApiBase::PARAM_TEMPLATE_VARS] ) ) {
|
||||
if ( $target === $name ) {
|
||||
$ret['issues'][] = "PARAM_TEMPLATE_VARS[$key] cannot target the parameter itself";
|
||||
}
|
||||
if ( array_diff(
|
||||
$settings2[ApiBase::PARAM_TEMPLATE_VARS],
|
||||
$settings[ApiBase::PARAM_TEMPLATE_VARS]
|
||||
) ) {
|
||||
$ret['issues'][] = "PARAM_TEMPLATE_VARS[$key]: Target's "
|
||||
. 'PARAM_TEMPLATE_VARS must be a subset of the original';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$keys = implode( '|', array_map(
|
||||
function ( $key ) {
|
||||
return preg_quote( $key, '/' );
|
||||
},
|
||||
array_keys( $settings[ApiBase::PARAM_TEMPLATE_VARS] )
|
||||
) );
|
||||
if ( !preg_match( '/^(?>[^{}]+|\{(?:' . $keys . ')\})+$/', $name ) ) {
|
||||
$ret['issues'][] = "Parameter name may not contain '{' or '}' other than '
|
||||
. 'as defined by PARAM_TEMPLATE_VARS";
|
||||
}
|
||||
}
|
||||
} elseif ( !preg_match( '/^[^{}]+$/', $name ) ) {
|
||||
$ret['issues'][] = "Parameter name may not contain '{' or '}' without PARAM_TEMPLATE_VARS";
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a ValidationException to an ApiUsageException
|
||||
* @param ApiBase $module
|
||||
|
|
|
|||
|
|
@ -30,6 +30,49 @@ class SubmoduleDef extends EnumDef {
|
|||
*/
|
||||
public const PARAM_SUBMODULE_PARAM_PREFIX = 'param-submodule-param-prefix';
|
||||
|
||||
public function checkSettings( string $name, $settings, array $options, array $ret ) : array {
|
||||
$map = $settings[self::PARAM_SUBMODULE_MAP] ?? [];
|
||||
if ( !is_array( $map ) ) {
|
||||
$ret['issues'][self::PARAM_SUBMODULE_MAP] = 'PARAM_SUBMODULE_MAP must be an array, got '
|
||||
. gettype( $map );
|
||||
// Prevent errors in parent::checkSettings()
|
||||
$settings[self::PARAM_SUBMODULE_MAP] = null;
|
||||
}
|
||||
|
||||
$ret = parent::checkSettings( $name, $settings, $options, $ret );
|
||||
|
||||
$ret['allowedKeys'] = array_merge( $ret['allowedKeys'], [
|
||||
self::PARAM_SUBMODULE_MAP, self::PARAM_SUBMODULE_PARAM_PREFIX,
|
||||
] );
|
||||
|
||||
if ( is_array( $map ) ) {
|
||||
$module = $options['module'];
|
||||
foreach ( $map as $k => $v ) {
|
||||
if ( !is_string( $v ) ) {
|
||||
$ret['issues'][] = 'Values for PARAM_SUBMODULE_MAP must be strings, '
|
||||
. "but value for \"$k\" is " . gettype( $v );
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
$submod = $module->getModuleFromPath( $v );
|
||||
} catch ( ApiUsageException $ex ) {
|
||||
$submod = null;
|
||||
}
|
||||
if ( !$submod ) {
|
||||
$ret['issues'][] = "PARAM_SUBMODULE_MAP contains \"$v\", which is not a valid module path";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( !is_string( $settings[self::PARAM_SUBMODULE_PARAM_PREFIX] ?? '' ) ) {
|
||||
$ret['issues'][self::PARAM_SUBMODULE_PARAM_PREFIX] = 'PARAM_SUBMODULE_PARAM_PREFIX must be '
|
||||
. 'a string, got ' . gettype( $settings[self::PARAM_SUBMODULE_PARAM_PREFIX] );
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
public function getEnumValues( $name, array $settings, array $options ) {
|
||||
if ( isset( $settings[self::PARAM_SUBMODULE_MAP] ) ) {
|
||||
$modules = array_keys( $settings[self::PARAM_SUBMODULE_MAP] );
|
||||
|
|
|
|||
|
|
@ -332,12 +332,11 @@ class ParamValidator {
|
|||
}
|
||||
|
||||
/**
|
||||
* Normalize a parameter settings array
|
||||
* @param array|mixed $settings Default value or an array of settings
|
||||
* using PARAM_* constants.
|
||||
* Logic shared by normalizeSettings() and checkSettings()
|
||||
* @param array|mixed $settings
|
||||
* @return array
|
||||
*/
|
||||
public function normalizeSettings( $settings ) {
|
||||
private function normalizeSettingsInternal( $settings ) {
|
||||
// Shorthand
|
||||
if ( !is_array( $settings ) ) {
|
||||
$settings = [
|
||||
|
|
@ -350,6 +349,18 @@ class ParamValidator {
|
|||
$settings[self::PARAM_TYPE] = gettype( $settings[self::PARAM_DEFAULT] ?? null );
|
||||
}
|
||||
|
||||
return $settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize a parameter settings array
|
||||
* @param array|mixed $settings Default value or an array of settings
|
||||
* using PARAM_* constants.
|
||||
* @return array
|
||||
*/
|
||||
public function normalizeSettings( $settings ) {
|
||||
$settings = $this->normalizeSettingsInternal( $settings );
|
||||
|
||||
$typeDef = $this->getTypeDef( $settings[self::PARAM_TYPE] );
|
||||
if ( $typeDef ) {
|
||||
$settings = $typeDef->normalizeSettings( $settings );
|
||||
|
|
@ -358,6 +369,136 @@ class ParamValidator {
|
|||
return $settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate a parameter settings array
|
||||
*
|
||||
* This is intended for validation of parameter settings during unit or
|
||||
* integration testing, and should implement strict checks.
|
||||
*
|
||||
* The rest of the code should generally be more permissive.
|
||||
*
|
||||
* @param string $name Parameter name
|
||||
* @param array|mixed $settings Default value or an array of settings
|
||||
* using PARAM_* constants.
|
||||
* @param array $options Options array, passed through to the TypeDef and Callbacks.
|
||||
* @return array
|
||||
* - 'issues': (string[]) Errors detected in $settings, as English text. If the settings
|
||||
* are valid, this will be the empty array.
|
||||
* - 'allowedKeys': (string[]) ParamValidator keys that are allowed in `$settings`.
|
||||
* - 'messages': (MessageValue[]) Messages to be checked for existence.
|
||||
*/
|
||||
public function checkSettings( string $name, $settings, array $options ) : array {
|
||||
$settings = $this->normalizeSettingsInternal( $settings );
|
||||
$issues = [];
|
||||
$allowedKeys = [
|
||||
self::PARAM_TYPE, self::PARAM_DEFAULT, self::PARAM_REQUIRED, self::PARAM_ISMULTI,
|
||||
self::PARAM_SENSITIVE, self::PARAM_DEPRECATED, self::PARAM_IGNORE_UNRECOGNIZED_VALUES,
|
||||
];
|
||||
$messages = [];
|
||||
|
||||
$type = $settings[self::PARAM_TYPE];
|
||||
$typeDef = null;
|
||||
if ( !is_string( $type ) && !is_array( $type ) ) {
|
||||
$issues[self::PARAM_TYPE] = 'PARAM_TYPE must be a string or array, got ' . gettype( $type );
|
||||
} else {
|
||||
$typeDef = $this->getTypeDef( $settings[self::PARAM_TYPE] );
|
||||
if ( !$typeDef ) {
|
||||
if ( is_array( $type ) ) {
|
||||
$type = 'enum';
|
||||
}
|
||||
$issues[self::PARAM_TYPE] = "Unknown/unregistered PARAM_TYPE \"$type\"";
|
||||
}
|
||||
}
|
||||
|
||||
if ( isset( $settings[self::PARAM_DEFAULT] ) ) {
|
||||
try {
|
||||
$this->validateValue(
|
||||
$name, $settings[self::PARAM_DEFAULT], $settings, [ 'is-default' => true ] + $options
|
||||
);
|
||||
} catch ( ValidationException $ex ) {
|
||||
$issues[self::PARAM_DEFAULT] = 'Value for PARAM_DEFAULT does not validate (code '
|
||||
. $ex->getFailureMessage()->getCode() . ')';
|
||||
}
|
||||
}
|
||||
|
||||
if ( !is_bool( $settings[self::PARAM_REQUIRED] ?? false ) ) {
|
||||
$issues[self::PARAM_REQUIRED] = 'PARAM_REQUIRED must be boolean, got '
|
||||
. gettype( $settings[self::PARAM_REQUIRED] );
|
||||
}
|
||||
|
||||
if ( !is_bool( $settings[self::PARAM_ISMULTI] ?? false ) ) {
|
||||
$issues[self::PARAM_ISMULTI] = 'PARAM_ISMULTI must be boolean, got '
|
||||
. gettype( $settings[self::PARAM_ISMULTI] );
|
||||
}
|
||||
|
||||
if ( !empty( $settings[self::PARAM_ISMULTI] ) ) {
|
||||
$allowedKeys = array_merge( $allowedKeys, [
|
||||
self::PARAM_ISMULTI_LIMIT1, self::PARAM_ISMULTI_LIMIT2,
|
||||
self::PARAM_ALL, self::PARAM_ALLOW_DUPLICATES
|
||||
] );
|
||||
|
||||
$limit1 = $settings[self::PARAM_ISMULTI_LIMIT1] ?? $this->ismultiLimit1;
|
||||
$limit2 = $settings[self::PARAM_ISMULTI_LIMIT2] ?? $this->ismultiLimit2;
|
||||
if ( !is_int( $limit1 ) ) {
|
||||
$issues[self::PARAM_ISMULTI_LIMIT1] = 'PARAM_ISMULTI_LIMIT1 must be an integer, got '
|
||||
. gettype( $settings[self::PARAM_ISMULTI_LIMIT1] );
|
||||
} elseif ( $limit1 <= 0 ) {
|
||||
$issues[self::PARAM_ISMULTI_LIMIT1] =
|
||||
"PARAM_ISMULTI_LIMIT1 must be greater than 0, got $limit1";
|
||||
}
|
||||
if ( !is_int( $limit2 ) ) {
|
||||
$issues[self::PARAM_ISMULTI_LIMIT2] = 'PARAM_ISMULTI_LIMIT2 must be an integer, got '
|
||||
. gettype( $settings[self::PARAM_ISMULTI_LIMIT2] );
|
||||
} elseif ( $limit2 < $limit1 ) {
|
||||
$issues[self::PARAM_ISMULTI_LIMIT2] =
|
||||
'PARAM_ISMULTI_LIMIT2 must be greater than or equal to PARAM_ISMULTI_LIMIT1, but '
|
||||
. "$limit2 < $limit1";
|
||||
}
|
||||
|
||||
$all = $settings[self::PARAM_ALL] ?? false;
|
||||
if ( !is_string( $all ) && !is_bool( $all ) ) {
|
||||
$issues[self::PARAM_ALL] = 'PARAM_ALL must be a string or boolean, got ' . gettype( $all );
|
||||
} elseif ( $all !== false && $typeDef ) {
|
||||
if ( $all === true ) {
|
||||
$all = self::ALL_DEFAULT_STRING;
|
||||
}
|
||||
$values = $typeDef->getEnumValues( $name, $settings, $options );
|
||||
if ( !is_array( $values ) ) {
|
||||
$issues[self::PARAM_ALL] = 'PARAM_ALL cannot be used with non-enumerated types';
|
||||
} elseif ( in_array( $all, $values, true ) ) {
|
||||
$issues[self::PARAM_ALL] = 'Value for PARAM_ALL conflicts with an enumerated value';
|
||||
}
|
||||
}
|
||||
|
||||
if ( !is_bool( $settings[self::PARAM_ALLOW_DUPLICATES] ?? false ) ) {
|
||||
$issues[self::PARAM_ALLOW_DUPLICATES] = 'PARAM_ALLOW_DUPLICATES must be boolean, got '
|
||||
. gettype( $settings[self::PARAM_ALLOW_DUPLICATES] );
|
||||
}
|
||||
}
|
||||
|
||||
if ( !is_bool( $settings[self::PARAM_SENSITIVE] ?? false ) ) {
|
||||
$issues[self::PARAM_SENSITIVE] = 'PARAM_SENSITIVE must be boolean, got '
|
||||
. gettype( $settings[self::PARAM_SENSITIVE] );
|
||||
}
|
||||
|
||||
if ( !is_bool( $settings[self::PARAM_DEPRECATED] ?? false ) ) {
|
||||
$issues[self::PARAM_DEPRECATED] = 'PARAM_DEPRECATED must be boolean, got '
|
||||
. gettype( $settings[self::PARAM_DEPRECATED] );
|
||||
}
|
||||
|
||||
if ( !is_bool( $settings[self::PARAM_IGNORE_UNRECOGNIZED_VALUES] ?? false ) ) {
|
||||
$issues[self::PARAM_IGNORE_UNRECOGNIZED_VALUES] = 'PARAM_IGNORE_UNRECOGNIZED_VALUES must be '
|
||||
. 'boolean, got ' . gettype( $settings[self::PARAM_IGNORE_UNRECOGNIZED_VALUES] );
|
||||
}
|
||||
|
||||
$ret = [ 'issues' => $issues, 'allowedKeys' => $allowedKeys, 'messages' => $messages ];
|
||||
if ( $typeDef ) {
|
||||
$ret = $typeDef->checkSettings( $name, $settings, $options, $ret );
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch and valiate a parameter value using a settings array
|
||||
*
|
||||
|
|
|
|||
|
|
@ -132,6 +132,32 @@ abstract class TypeDef {
|
|||
return $settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate a parameter settings array
|
||||
*
|
||||
* This is intended for validation of parameter settings during unit or
|
||||
* integration testing, and should implement strict checks.
|
||||
*
|
||||
* The rest of the code should generally be more permissive.
|
||||
*
|
||||
* @see ParamValidator::checkSettings()
|
||||
* @param string $name Parameter name
|
||||
* @param array|mixed $settings Default value or an array of settings
|
||||
* using PARAM_* constants.
|
||||
* @param array $options Options array, passed through to the TypeDef and Callbacks.
|
||||
* @param array $ret
|
||||
* - 'issues': (string[]) Errors detected in $settings, as English text. If the settings
|
||||
* are valid, this will be the empty array. Keys on input are ParamValidator constants,
|
||||
* allowing the typedef to easily override core validation; this need not be preserved
|
||||
* when returned.
|
||||
* - 'allowedKeys': (string[]) ParamValidator keys that are allowed in `$settings`.
|
||||
* - 'messages': (MessageValue[]) Messages to be checked for existence.
|
||||
* @return array $ret, with any relevant changes.
|
||||
*/
|
||||
public function checkSettings( string $name, $settings, array $options, array $ret ) : array {
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the values for enum-like parameters
|
||||
*
|
||||
|
|
|
|||
|
|
@ -84,6 +84,37 @@ class EnumDef extends TypeDef {
|
|||
);
|
||||
}
|
||||
|
||||
public function checkSettings( string $name, $settings, array $options, array $ret ) : array {
|
||||
$ret = parent::checkSettings( $name, $settings, $options, $ret );
|
||||
|
||||
$ret['allowedKeys'][] = self::PARAM_DEPRECATED_VALUES;
|
||||
|
||||
$dv = $settings[self::PARAM_DEPRECATED_VALUES] ?? [];
|
||||
if ( !is_array( $dv ?? false ) ) {
|
||||
$ret['issues'][self::PARAM_DEPRECATED_VALUES] = 'PARAM_DEPRECATED_VALUES must be an array, got '
|
||||
. gettype( $dv );
|
||||
} else {
|
||||
$values = array_map( function ( $v ) use ( $name, $settings, $options ) {
|
||||
return $this->stringifyValue( $name, $v, $settings, $options );
|
||||
}, $this->getEnumValues( $name, $settings, $options ) );
|
||||
foreach ( $dv as $k => $v ) {
|
||||
$k = $this->stringifyValue( $name, $k, $settings, $options );
|
||||
if ( !in_array( $k, $values, true ) ) {
|
||||
$ret['issues'][] = "PARAM_DEPRECATED_VALUES contains \"$k\", which is not "
|
||||
. 'one of the enumerated values';
|
||||
} elseif ( $v instanceof MessageValue ) {
|
||||
$ret['messages'][] = $v;
|
||||
} elseif ( $v !== null && $v !== true ) {
|
||||
$type = $v === false ? 'false' : ( is_object( $v ) ? get_class( $v ) : gettype( $v ) );
|
||||
$ret['issues'][] = 'Values in PARAM_DEPRECATED_VALUES must be null, true, or MessageValue, '
|
||||
. "but value for \"$k\" is $type";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
public function getEnumValues( $name, array $settings, array $options ) {
|
||||
return array_values( $settings[ParamValidator::PARAM_TYPE] );
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,6 +28,8 @@ use Wikimedia\ParamValidator\ParamValidator;
|
|||
*/
|
||||
class FloatDef extends NumericDef {
|
||||
|
||||
protected $valueType = 'double';
|
||||
|
||||
public function validate( $name, $value, array $settings, array $options ) {
|
||||
// Use a regex so as to avoid any potential oddness PHP's default conversion might allow.
|
||||
if ( !preg_match( '/^[+-]?(?:\d*\.)?\d+(?:[eE][+-]?\d+)?$/D', $value ) ) {
|
||||
|
|
|
|||
|
|
@ -48,6 +48,26 @@ class LimitDef extends IntegerDef {
|
|||
return parent::normalizeSettings( $settings );
|
||||
}
|
||||
|
||||
public function checkSettings( string $name, $settings, array $options, array $ret ) : array {
|
||||
$ret = parent::checkSettings( $name, $settings, $options, $ret );
|
||||
|
||||
if ( !empty( $settings[ParamValidator::PARAM_ISMULTI] ) &&
|
||||
!isset( $ret['issues'][ParamValidator::PARAM_ISMULTI] )
|
||||
) {
|
||||
$ret['issues'][ParamValidator::PARAM_ISMULTI] =
|
||||
'PARAM_ISMULTI cannot be used for limit-type parameters';
|
||||
}
|
||||
|
||||
if ( ( $settings[self::PARAM_MIN] ?? 0 ) < 0 ) {
|
||||
$ret['issues'][] = 'PARAM_MIN must be greater than or equal to 0';
|
||||
}
|
||||
if ( !isset( $settings[self::PARAM_MAX] ) ) {
|
||||
$ret['issues'][] = 'PARAM_MAX must be set';
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
public function getHelpInfo( $name, array $settings, array $options ) {
|
||||
$info = parent::getHelpInfo( $name, $settings, $options );
|
||||
|
||||
|
|
|
|||
|
|
@ -46,6 +46,9 @@ abstract class NumericDef extends TypeDef {
|
|||
*/
|
||||
const PARAM_MAX2 = 'param-max2';
|
||||
|
||||
/** @var string PHP type (as from `gettype()`) of values this NumericDef handles */
|
||||
protected $valueType = 'integer';
|
||||
|
||||
/**
|
||||
* Check the range of a value
|
||||
* @param int|float $value Value to check.
|
||||
|
|
@ -116,6 +119,46 @@ abstract class NumericDef extends TypeDef {
|
|||
return parent::normalizeSettings( $settings );
|
||||
}
|
||||
|
||||
public function checkSettings( string $name, $settings, array $options, array $ret ) : array {
|
||||
$ret = parent::checkSettings( $name, $settings, $options, $ret );
|
||||
|
||||
$ret['allowedKeys'] = array_merge( $ret['allowedKeys'], [
|
||||
self::PARAM_IGNORE_RANGE, self::PARAM_MIN, self::PARAM_MAX, self::PARAM_MAX2,
|
||||
] );
|
||||
|
||||
if ( !is_bool( $settings[self::PARAM_IGNORE_RANGE] ?? false ) ) {
|
||||
$ret['issues'][self::PARAM_IGNORE_RANGE] = 'PARAM_IGNORE_RANGE must be boolean, got '
|
||||
. gettype( $settings[self::PARAM_IGNORE_RANGE] );
|
||||
}
|
||||
|
||||
$min = $settings[self::PARAM_MIN] ?? null;
|
||||
$max = $settings[self::PARAM_MAX] ?? null;
|
||||
$max2 = $settings[self::PARAM_MAX2] ?? null;
|
||||
if ( $min !== null && gettype( $min ) !== $this->valueType ) {
|
||||
$ret['issues'][self::PARAM_MIN] = "PARAM_MIN must be $this->valueType, got " . gettype( $min );
|
||||
}
|
||||
if ( $max !== null && gettype( $max ) !== $this->valueType ) {
|
||||
$ret['issues'][self::PARAM_MAX] = "PARAM_MAX must be $this->valueType, got " . gettype( $max );
|
||||
}
|
||||
if ( $max2 !== null && gettype( $max2 ) !== $this->valueType ) {
|
||||
$ret['issues'][self::PARAM_MAX2] = "PARAM_MAX2 must be $this->valueType, got "
|
||||
. gettype( $max2 );
|
||||
}
|
||||
|
||||
if ( $min !== null && $max !== null && $min > $max ) {
|
||||
$ret['issues'][] = "PARAM_MIN must be less than or equal to PARAM_MAX, but $min > $max";
|
||||
}
|
||||
if ( $max2 !== null ) {
|
||||
if ( $max === null ) {
|
||||
$ret['issues'][] = 'PARAM_MAX2 cannot be used without PARAM_MAX';
|
||||
} elseif ( $max2 < $max ) {
|
||||
$ret['issues'][] = "PARAM_MAX2 must be greater than or equal to PARAM_MAX, but $max2 < $max";
|
||||
}
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
public function getParamInfo( $name, array $settings, array $options ) {
|
||||
$info = parent::getParamInfo( $name, $settings, $options );
|
||||
|
||||
|
|
|
|||
|
|
@ -20,4 +20,17 @@ class PasswordDef extends StringDef {
|
|||
return parent::normalizeSettings( $settings );
|
||||
}
|
||||
|
||||
public function checkSettings( string $name, $settings, array $options, array $ret ) : array {
|
||||
$ret = parent::checkSettings( $name, $settings, $options, $ret );
|
||||
|
||||
if ( ( $settings[ParamValidator::PARAM_SENSITIVE] ?? true ) !== true &&
|
||||
!isset( $ret['issues'][ParamValidator::PARAM_SENSITIVE] )
|
||||
) {
|
||||
$ret['issues'][ParamValidator::PARAM_SENSITIVE] =
|
||||
'Cannot set PARAM_SENSITIVE to false for password-type parameters';
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,6 +35,26 @@ class PresenceBooleanDef extends TypeDef {
|
|||
return parent::normalizeSettings( $settings );
|
||||
}
|
||||
|
||||
public function checkSettings( string $name, $settings, array $options, array $ret ) : array {
|
||||
$ret = parent::checkSettings( $name, $settings, $options, $ret );
|
||||
|
||||
if ( !empty( $settings[ParamValidator::PARAM_ISMULTI] ) &&
|
||||
!isset( $ret['issues'][ParamValidator::PARAM_ISMULTI] )
|
||||
) {
|
||||
$ret['issues'][ParamValidator::PARAM_ISMULTI] =
|
||||
'PARAM_ISMULTI cannot be used for presence-boolean-type parameters';
|
||||
}
|
||||
|
||||
if ( ( $settings[ParamValidator::PARAM_DEFAULT] ?? false ) !== false &&
|
||||
!isset( $ret['issues'][ParamValidator::PARAM_DEFAULT] )
|
||||
) {
|
||||
$ret['issues'][ParamValidator::PARAM_DEFAULT] =
|
||||
'Default for presence-boolean-type parameters must be false or null';
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
public function getParamInfo( $name, array $settings, array $options ) {
|
||||
$info = parent::getParamInfo( $name, $settings, $options );
|
||||
|
||||
|
|
|
|||
|
|
@ -90,6 +90,43 @@ class StringDef extends TypeDef {
|
|||
return $value;
|
||||
}
|
||||
|
||||
public function checkSettings( string $name, $settings, array $options, array $ret ) : array {
|
||||
$ret = parent::checkSettings( $name, $settings, $options, $ret );
|
||||
|
||||
$ret['allowedKeys'] = array_merge( $ret['allowedKeys'], [
|
||||
self::PARAM_MAX_BYTES, self::PARAM_MAX_CHARS,
|
||||
] );
|
||||
|
||||
$maxb = $settings[self::PARAM_MAX_BYTES] ?? PHP_INT_MAX;
|
||||
if ( !is_int( $maxb ) ) {
|
||||
$ret['issues'][self::PARAM_MAX_BYTES] = 'PARAM_MAX_BYTES must be an integer, got '
|
||||
. gettype( $maxb );
|
||||
} elseif ( $maxb < 0 ) {
|
||||
$ret['issues'][self::PARAM_MAX_BYTES] = 'PARAM_MAX_BYTES must be greater than or equal to 0';
|
||||
}
|
||||
|
||||
$maxc = $settings[self::PARAM_MAX_CHARS] ?? PHP_INT_MAX;
|
||||
if ( !is_int( $maxc ) ) {
|
||||
$ret['issues'][self::PARAM_MAX_CHARS] = 'PARAM_MAX_CHARS must be an integer, got '
|
||||
. gettype( $maxc );
|
||||
} elseif ( $maxc < 0 ) {
|
||||
$ret['issues'][self::PARAM_MAX_CHARS] = 'PARAM_MAX_CHARS must be greater than or equal to 0';
|
||||
}
|
||||
|
||||
if ( !$this->allowEmptyWhenRequired && !empty( $settings[ParamValidator::PARAM_REQUIRED] ) ) {
|
||||
if ( $maxb === 0 ) {
|
||||
$ret['issues'][] = 'PARAM_REQUIRED is set, allowEmptyWhenRequired is not set, and '
|
||||
. 'PARAM_MAX_BYTES is 0. That\'s impossible to satisfy.';
|
||||
}
|
||||
if ( $maxc === 0 ) {
|
||||
$ret['issues'][] = 'PARAM_REQUIRED is set, allowEmptyWhenRequired is not set, and '
|
||||
. 'PARAM_MAX_CHARS is 0. That\'s impossible to satisfy.';
|
||||
}
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
public function getParamInfo( $name, array $settings, array $options ) {
|
||||
$info = parent::getParamInfo( $name, $settings, $options );
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace Wikimedia\ParamValidator\TypeDef;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use Wikimedia\Message\MessageValue;
|
||||
use Wikimedia\ParamValidator\Callbacks;
|
||||
use Wikimedia\ParamValidator\ParamValidator;
|
||||
|
|
@ -61,6 +62,16 @@ class TimestampDef extends TypeDef {
|
|||
|
||||
$this->defaultFormat = $options['defaultFormat'] ?? 'ConvertibleTimestamp';
|
||||
$this->stringifyFormat = $options['stringifyFormat'] ?? TS_ISO_8601;
|
||||
|
||||
// Check values by trying to convert 0
|
||||
if ( $this->defaultFormat !== 'ConvertibleTimestamp' && $this->defaultFormat !== 'DateTime' &&
|
||||
ConvertibleTimestamp::convert( $this->defaultFormat, 0 ) === false
|
||||
) {
|
||||
throw new InvalidArgumentException( 'Invalid value for $options[\'defaultFormat\']' );
|
||||
}
|
||||
if ( ConvertibleTimestamp::convert( $this->stringifyFormat, 0 ) === false ) {
|
||||
throw new InvalidArgumentException( 'Invalid value for $options[\'stringifyFormat\']' );
|
||||
}
|
||||
}
|
||||
|
||||
public function validate( $name, $value, array $settings, array $options ) {
|
||||
|
|
@ -94,6 +105,23 @@ class TimestampDef extends TypeDef {
|
|||
}
|
||||
}
|
||||
|
||||
public function checkSettings( string $name, $settings, array $options, array $ret ) : array {
|
||||
$ret = parent::checkSettings( $name, $settings, $options, $ret );
|
||||
|
||||
$ret['allowedKeys'] = array_merge( $ret['allowedKeys'], [
|
||||
self::PARAM_TIMESTAMP_FORMAT,
|
||||
] );
|
||||
|
||||
$f = $settings[self::PARAM_TIMESTAMP_FORMAT] ?? $this->defaultFormat;
|
||||
if ( $f !== 'ConvertibleTimestamp' && $f !== 'DateTime' &&
|
||||
ConvertibleTimestamp::convert( $f, 0 ) === false
|
||||
) {
|
||||
$ret['issues'][self::PARAM_TIMESTAMP_FORMAT] = 'Value for PARAM_TIMESTAMP_FORMAT is not valid';
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
public function stringifyValue( $name, $value, array $settings, array $options ) {
|
||||
if ( !$value instanceof ConvertibleTimestamp ) {
|
||||
$value = new ConvertibleTimestamp( $value );
|
||||
|
|
|
|||
|
|
@ -122,6 +122,24 @@ class UploadDef extends TypeDef {
|
|||
}
|
||||
}
|
||||
|
||||
public function checkSettings( string $name, $settings, array $options, array $ret ) : array {
|
||||
$ret = parent::checkSettings( $name, $settings, $options, $ret );
|
||||
|
||||
if ( isset( $settings[ParamValidator::PARAM_DEFAULT] ) ) {
|
||||
$ret['issues'][ParamValidator::PARAM_DEFAULT] =
|
||||
'Cannot specify a default for upload-type parameters';
|
||||
}
|
||||
|
||||
if ( !empty( $settings[ParamValidator::PARAM_ISMULTI] ) &&
|
||||
!isset( $ret['issues'][ParamValidator::PARAM_ISMULTI] )
|
||||
) {
|
||||
$ret['issues'][ParamValidator::PARAM_ISMULTI] =
|
||||
'PARAM_ISMULTI cannot be used for upload-type parameters';
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
public function stringifyValue( $name, $value, array $settings, array $options ) {
|
||||
// Not going to happen.
|
||||
return null;
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ use MediaWiki\MediaWikiServices;
|
|||
use Wikimedia\Message\DataMessageValue;
|
||||
use Wikimedia\ParamValidator\ParamValidator;
|
||||
use Wikimedia\ParamValidator\SimpleCallbacks;
|
||||
use Wikimedia\ParamValidator\TypeDef\EnumDef;
|
||||
use Wikimedia\ParamValidator\TypeDef\TypeDefTestCase;
|
||||
use Wikimedia\ParamValidator\ValidationException;
|
||||
|
||||
|
|
@ -95,6 +96,149 @@ class NamespaceDefTest extends TypeDefTestCase {
|
|||
];
|
||||
}
|
||||
|
||||
public function provideCheckSettings() {
|
||||
$keys = [ 'Y', EnumDef::PARAM_DEPRECATED_VALUES, NamespaceDef::PARAM_EXTRA_NAMESPACES ];
|
||||
return [
|
||||
'Basic test' => [
|
||||
[],
|
||||
self::STDRET,
|
||||
[
|
||||
'issues' => [ 'X' ],
|
||||
'allowedKeys' => $keys,
|
||||
'messages' => [],
|
||||
],
|
||||
],
|
||||
'Test with everything' => [
|
||||
[
|
||||
ParamValidator::PARAM_ISMULTI => true,
|
||||
ParamValidator::PARAM_ALL => true,
|
||||
NamespaceDef::PARAM_EXTRA_NAMESPACES => [ -1, -2 ],
|
||||
],
|
||||
self::STDRET,
|
||||
[
|
||||
'issues' => [ 'X' ],
|
||||
'allowedKeys' => $keys,
|
||||
'messages' => [],
|
||||
],
|
||||
],
|
||||
'PARAM_ALL cannot be false' => [
|
||||
[
|
||||
ParamValidator::PARAM_ISMULTI => true,
|
||||
ParamValidator::PARAM_ALL => false,
|
||||
],
|
||||
self::STDRET,
|
||||
[
|
||||
'issues' => [
|
||||
'X',
|
||||
ParamValidator::PARAM_ALL
|
||||
=> 'PARAM_ALL cannot be false or a string for namespace-type parameters',
|
||||
],
|
||||
'allowedKeys' => $keys,
|
||||
'messages' => [],
|
||||
],
|
||||
],
|
||||
'PARAM_ALL cannot be a string' => [
|
||||
[
|
||||
ParamValidator::PARAM_ISMULTI => true,
|
||||
ParamValidator::PARAM_ALL => 'all',
|
||||
],
|
||||
self::STDRET,
|
||||
[
|
||||
'issues' => [
|
||||
'X',
|
||||
ParamValidator::PARAM_ALL
|
||||
=> 'PARAM_ALL cannot be false or a string for namespace-type parameters',
|
||||
],
|
||||
'allowedKeys' => $keys,
|
||||
'messages' => [],
|
||||
],
|
||||
],
|
||||
'PARAM_ALL ignored without PARAM_ISMULTI' => [
|
||||
[
|
||||
ParamValidator::PARAM_ALL => 'all',
|
||||
],
|
||||
self::STDRET,
|
||||
[
|
||||
'issues' => [ 'X' ],
|
||||
'allowedKeys' => $keys,
|
||||
'messages' => [],
|
||||
],
|
||||
],
|
||||
'PARAM_ALL cannot be a string, but another PARAM_ALL issue was already logged' => [
|
||||
[
|
||||
ParamValidator::PARAM_ISMULTI => true,
|
||||
ParamValidator::PARAM_ALL => 'all',
|
||||
],
|
||||
[
|
||||
'issues' => [ ParamValidator::PARAM_ALL => 'XXX' ],
|
||||
'allowedKeys' => [ 'Y' ],
|
||||
'messages' => [],
|
||||
],
|
||||
[
|
||||
'issues' => [ ParamValidator::PARAM_ALL => 'XXX' ],
|
||||
'allowedKeys' => $keys,
|
||||
'messages' => [],
|
||||
],
|
||||
],
|
||||
'Bad type for PARAM_EXTRA_NAMESPACES' => [
|
||||
[
|
||||
NamespaceDef::PARAM_EXTRA_NAMESPACES => -1,
|
||||
],
|
||||
self::STDRET,
|
||||
[
|
||||
'issues' => [
|
||||
'X',
|
||||
NamespaceDef::PARAM_EXTRA_NAMESPACES
|
||||
=> 'PARAM_EXTRA_NAMESPACES must be an integer[], got integer'
|
||||
],
|
||||
'allowedKeys' => $keys,
|
||||
'messages' => [],
|
||||
],
|
||||
],
|
||||
'Empty array for PARAM_EXTRA_NAMESPACES ok' => [
|
||||
[
|
||||
NamespaceDef::PARAM_EXTRA_NAMESPACES => [],
|
||||
],
|
||||
self::STDRET,
|
||||
[
|
||||
'issues' => [ 'X' ],
|
||||
'allowedKeys' => $keys,
|
||||
'messages' => [],
|
||||
],
|
||||
],
|
||||
'Bad value types for PARAM_EXTRA_NAMESPACES' => [
|
||||
[
|
||||
NamespaceDef::PARAM_EXTRA_NAMESPACES => [ '-1' ],
|
||||
],
|
||||
self::STDRET,
|
||||
[
|
||||
'issues' => [
|
||||
'X',
|
||||
NamespaceDef::PARAM_EXTRA_NAMESPACES
|
||||
=> 'PARAM_EXTRA_NAMESPACES must be an integer[], got string[]'
|
||||
],
|
||||
'allowedKeys' => $keys,
|
||||
'messages' => [],
|
||||
],
|
||||
],
|
||||
'Bad value types for PARAM_EXTRA_NAMESPACES (2)' => [
|
||||
[
|
||||
NamespaceDef::PARAM_EXTRA_NAMESPACES => [ 0, '-1', '-2' ],
|
||||
],
|
||||
self::STDRET,
|
||||
[
|
||||
'issues' => [
|
||||
'X',
|
||||
NamespaceDef::PARAM_EXTRA_NAMESPACES
|
||||
=> 'PARAM_EXTRA_NAMESPACES must be an integer[], got (integer|string)[]'
|
||||
],
|
||||
'allowedKeys' => $keys,
|
||||
'messages' => [],
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function provideStringifyValue() {
|
||||
return [
|
||||
'Basic test' => [ 123, '123' ],
|
||||
|
|
|
|||
|
|
@ -161,6 +161,160 @@ class UserDefTest extends TypeDefTestCase {
|
|||
];
|
||||
}
|
||||
|
||||
public function provideCheckSettings() {
|
||||
$keys = [ 'Y', UserDef::PARAM_ALLOWED_USER_TYPES, UserDef::PARAM_RETURN_OBJECT ];
|
||||
$ismultiIssue = 'Multi-valued user-type parameters with PARAM_RETURN_OBJECT or allowing IDs '
|
||||
. 'should set low values (<= 10) for PARAM_ISMULTI_LIMIT1 and PARAM_ISMULTI_LIMIT2.'
|
||||
. ' (Note that "<= 10" is arbitrary. If something hits this, we can investigate a real limit '
|
||||
. 'once we have a real use case to look at.)';
|
||||
|
||||
return [
|
||||
'Basic test' => [
|
||||
[],
|
||||
self::STDRET,
|
||||
[
|
||||
'issues' => [ 'X' ],
|
||||
'allowedKeys' => $keys,
|
||||
'messages' => [],
|
||||
],
|
||||
],
|
||||
'Test with everything' => [
|
||||
[
|
||||
UserDef::PARAM_ALLOWED_USER_TYPES => [ 'name' ],
|
||||
UserDef::PARAM_RETURN_OBJECT => true,
|
||||
],
|
||||
self::STDRET,
|
||||
[
|
||||
'issues' => [ 'X' ],
|
||||
'allowedKeys' => $keys,
|
||||
'messages' => [],
|
||||
],
|
||||
],
|
||||
'Bad types' => [
|
||||
[
|
||||
UserDef::PARAM_ALLOWED_USER_TYPES => 'name',
|
||||
UserDef::PARAM_RETURN_OBJECT => 1,
|
||||
],
|
||||
self::STDRET,
|
||||
[
|
||||
'issues' => [
|
||||
'X',
|
||||
UserDef::PARAM_RETURN_OBJECT => 'PARAM_RETURN_OBJECT must be boolean, got integer',
|
||||
UserDef::PARAM_ALLOWED_USER_TYPES => 'PARAM_ALLOWED_USER_TYPES must be an array, got string',
|
||||
],
|
||||
'allowedKeys' => $keys,
|
||||
'messages' => [],
|
||||
],
|
||||
],
|
||||
'PARAM_ALLOWED_USER_TYPES cannot be empty' => [
|
||||
[
|
||||
UserDef::PARAM_ALLOWED_USER_TYPES => [],
|
||||
],
|
||||
self::STDRET,
|
||||
[
|
||||
'issues' => [
|
||||
'X',
|
||||
UserDef::PARAM_ALLOWED_USER_TYPES => 'PARAM_ALLOWED_USER_TYPES cannot be empty',
|
||||
],
|
||||
'allowedKeys' => $keys,
|
||||
'messages' => [],
|
||||
],
|
||||
],
|
||||
'PARAM_ALLOWED_USER_TYPES invalid values' => [
|
||||
[
|
||||
UserDef::PARAM_ALLOWED_USER_TYPES => [ 'name', 'id', 'ssn', 'Q-number' ],
|
||||
],
|
||||
self::STDRET,
|
||||
[
|
||||
'issues' => [
|
||||
'X',
|
||||
UserDef::PARAM_ALLOWED_USER_TYPES
|
||||
=> 'PARAM_ALLOWED_USER_TYPES contains invalid values: ssn, Q-number',
|
||||
],
|
||||
'allowedKeys' => $keys,
|
||||
'messages' => [],
|
||||
],
|
||||
],
|
||||
'ISMULTI generally ok' => [
|
||||
[
|
||||
ParamValidator::PARAM_ISMULTI => true,
|
||||
],
|
||||
self::STDRET,
|
||||
[
|
||||
'issues' => [ 'X' ],
|
||||
'allowedKeys' => $keys,
|
||||
'messages' => [],
|
||||
],
|
||||
],
|
||||
'ISMULTI with ID not ok (1)' => [
|
||||
[
|
||||
ParamValidator::PARAM_ISMULTI => true,
|
||||
UserDef::PARAM_ALLOWED_USER_TYPES => [ 'id' ],
|
||||
],
|
||||
self::STDRET,
|
||||
[
|
||||
'issues' => [ 'X', $ismultiIssue ],
|
||||
'allowedKeys' => $keys,
|
||||
'messages' => [],
|
||||
],
|
||||
],
|
||||
'ISMULTI with ID not ok (2)' => [
|
||||
[
|
||||
ParamValidator::PARAM_ISMULTI => true,
|
||||
ParamValidator::PARAM_ISMULTI_LIMIT1 => 10,
|
||||
ParamValidator::PARAM_ISMULTI_LIMIT2 => 11,
|
||||
UserDef::PARAM_ALLOWED_USER_TYPES => [ 'id' ],
|
||||
],
|
||||
self::STDRET,
|
||||
[
|
||||
'issues' => [ 'X', $ismultiIssue ],
|
||||
'allowedKeys' => $keys,
|
||||
'messages' => [],
|
||||
],
|
||||
],
|
||||
'ISMULTI with ID ok with low limits' => [
|
||||
[
|
||||
ParamValidator::PARAM_ISMULTI => true,
|
||||
ParamValidator::PARAM_ISMULTI_LIMIT1 => 10,
|
||||
ParamValidator::PARAM_ISMULTI_LIMIT2 => 10,
|
||||
UserDef::PARAM_ALLOWED_USER_TYPES => [ 'id' ],
|
||||
],
|
||||
self::STDRET,
|
||||
[
|
||||
'issues' => [ 'X' ],
|
||||
'allowedKeys' => $keys,
|
||||
'messages' => [],
|
||||
],
|
||||
],
|
||||
'ISMULTI with RETURN_OBJECT also not ok' => [
|
||||
[
|
||||
ParamValidator::PARAM_ISMULTI => true,
|
||||
UserDef::PARAM_RETURN_OBJECT => true,
|
||||
],
|
||||
self::STDRET,
|
||||
[
|
||||
'issues' => [ 'X', $ismultiIssue ],
|
||||
'allowedKeys' => $keys,
|
||||
'messages' => [],
|
||||
],
|
||||
],
|
||||
'ISMULTI with RETURN_OBJECT also ok with low limits' => [
|
||||
[
|
||||
ParamValidator::PARAM_ISMULTI => true,
|
||||
ParamValidator::PARAM_ISMULTI_LIMIT1 => 10,
|
||||
ParamValidator::PARAM_ISMULTI_LIMIT2 => 10,
|
||||
UserDef::PARAM_RETURN_OBJECT => true,
|
||||
],
|
||||
self::STDRET,
|
||||
[
|
||||
'issues' => [ 'X' ],
|
||||
'allowedKeys' => $keys,
|
||||
'messages' => [],
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function provideGetInfo() {
|
||||
return [
|
||||
'Basic test' => [
|
||||
|
|
|
|||
|
|
@ -135,6 +135,398 @@ class ApiParamValidatorTest extends ApiTestCase {
|
|||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideCheckSettings
|
||||
* @param array $params All module parameters.
|
||||
* @param string $name Parameter to test.
|
||||
* @param array $expect
|
||||
*/
|
||||
public function testCheckSettings( array $params, string $name, array $expect ) : void {
|
||||
[ $validator, $main ] = $this->getValidator( new FauxRequest( [] ) );
|
||||
$module = $main->getModuleFromPath( 'query+allpages' );
|
||||
|
||||
$mock = $this->getMockBuilder( ParamValidator::class )
|
||||
->disableOriginalConstructor()
|
||||
->setMethods( [ 'checkSettings' ] )
|
||||
->getMock();
|
||||
$mock->expects( $this->once() )->method( 'checkSettings' )
|
||||
->willReturnCallback( function ( $n, $settings, $options ) use ( $name, $module ) {
|
||||
$this->assertSame( "ap$name", $n );
|
||||
$this->assertSame( [ 'module' => $module ], $options );
|
||||
|
||||
$ret = [ 'issues' => [ 'X' ], 'allowedKeys' => [ 'Y' ], 'messages' => [] ];
|
||||
$stack = is_array( $settings ) ? [ &$settings ] : [];
|
||||
while ( $stack ) {
|
||||
foreach ( $stack[0] as $k => $v ) {
|
||||
if ( $v instanceof MessageValue ) {
|
||||
$ret['messages'][] = $v;
|
||||
} elseif ( is_array( $v ) ) {
|
||||
$stack[] = &$stack[0][$k];
|
||||
}
|
||||
}
|
||||
array_shift( $stack );
|
||||
}
|
||||
return $ret;
|
||||
} );
|
||||
TestingAccessWrapper::newFromObject( $validator )->paramValidator = $mock;
|
||||
|
||||
$this->assertEquals( $expect, $validator->checkSettings( $module, $params, $name, [] ) );
|
||||
}
|
||||
|
||||
public function provideCheckSettings() {
|
||||
$keys = [
|
||||
'Y', ApiBase::PARAM_RANGE_ENFORCE, ApiBase::PARAM_HELP_MSG, ApiBase::PARAM_HELP_MSG_APPEND,
|
||||
ApiBase::PARAM_HELP_MSG_INFO, ApiBase::PARAM_HELP_MSG_PER_VALUE, ApiBase::PARAM_TEMPLATE_VARS,
|
||||
];
|
||||
|
||||
return [
|
||||
'Basic test' => [
|
||||
[ 'test' => null ],
|
||||
'test',
|
||||
[
|
||||
'issues' => [ 'X' ],
|
||||
'allowedKeys' => $keys,
|
||||
'messages' => [],
|
||||
]
|
||||
],
|
||||
'Message mapping' => [
|
||||
[ 'test' => [
|
||||
EnumDef::PARAM_DEPRECATED_VALUES => [
|
||||
'a' => true,
|
||||
'b' => 'bbb',
|
||||
'c' => [ 'ccc', 'p1', 'p2' ],
|
||||
'd' => Message::newFromKey( 'ddd' )->plaintextParams( 'p1', 'p2' ),
|
||||
],
|
||||
] ],
|
||||
'test',
|
||||
[
|
||||
'issues' => [ 'X' ],
|
||||
'allowedKeys' => $keys,
|
||||
'messages' => [
|
||||
DataMessageValue::new( 'bbb', [], 'bogus', [ '💩' => 'back-compat' ] ),
|
||||
DataMessageValue::new( 'ccc', [], 'bogus', [ '💩' => 'back-compat' ] )
|
||||
->params( 'p1', 'p2' ),
|
||||
DataMessageValue::new( 'ddd', [], 'bogus', [ '💩' => 'back-compat' ] )
|
||||
->plaintextParams( 'p1', 'p2' ),
|
||||
],
|
||||
]
|
||||
],
|
||||
'Test everything' => [
|
||||
[
|
||||
'xxx' => [
|
||||
ParamValidator::PARAM_TYPE => 'not tested here',
|
||||
ParamValidator::PARAM_ISMULTI => true
|
||||
],
|
||||
'test-{x}' => [
|
||||
ApiBase::PARAM_TYPE => [],
|
||||
ApiBase::PARAM_RANGE_ENFORCE => true,
|
||||
ApiBase::PARAM_HELP_MSG => 'foo',
|
||||
ApiBase::PARAM_HELP_MSG_APPEND => [],
|
||||
ApiBase::PARAM_HELP_MSG_INFO => [],
|
||||
ApiBase::PARAM_HELP_MSG_PER_VALUE => [],
|
||||
ApiBase::PARAM_TEMPLATE_VARS => [
|
||||
'x' => 'xxx',
|
||||
]
|
||||
],
|
||||
],
|
||||
'test-{x}',
|
||||
[
|
||||
'issues' => [ 'X' ],
|
||||
'allowedKeys' => $keys,
|
||||
'messages' => [
|
||||
MessageValue::new( 'foo' ),
|
||||
],
|
||||
]
|
||||
],
|
||||
'Bad types' => [
|
||||
[ 'test' => [
|
||||
ApiBase::PARAM_RANGE_ENFORCE => 1,
|
||||
ApiBase::PARAM_HELP_MSG => false,
|
||||
ApiBase::PARAM_HELP_MSG_APPEND => 'foo',
|
||||
ApiBase::PARAM_HELP_MSG_INFO => 'bar',
|
||||
ApiBase::PARAM_HELP_MSG_PER_VALUE => true,
|
||||
ApiBase::PARAM_TEMPLATE_VARS => false,
|
||||
] ],
|
||||
'test',
|
||||
[
|
||||
'issues' => [
|
||||
'X',
|
||||
ApiBase::PARAM_RANGE_ENFORCE => 'PARAM_RANGE_ENFORCE must be boolean, got integer',
|
||||
'Message specification for PARAM_HELP_MSG is not valid',
|
||||
ApiBase::PARAM_HELP_MSG_APPEND => 'PARAM_HELP_MSG_APPEND must be an array, got string',
|
||||
ApiBase::PARAM_HELP_MSG_INFO => 'PARAM_HELP_MSG_INFO must be an array, got string',
|
||||
ApiBase::PARAM_HELP_MSG_PER_VALUE => 'PARAM_HELP_MSG_PER_VALUE must be an array, got boolean',
|
||||
ApiBase::PARAM_TEMPLATE_VARS => 'PARAM_TEMPLATE_VARS must be an array, got boolean',
|
||||
],
|
||||
'allowedKeys' => $keys,
|
||||
'messages' => [],
|
||||
]
|
||||
],
|
||||
'PARAM_VALUE_LINKS is deprecated' => [
|
||||
[ 'test' => [ ApiBase::PARAM_VALUE_LINKS => null ] ],
|
||||
'test',
|
||||
[
|
||||
'issues' => [
|
||||
'X',
|
||||
ApiBase::PARAM_VALUE_LINKS => 'PARAM_VALUE_LINKS was deprecated in MediaWiki 1.35',
|
||||
],
|
||||
'allowedKeys' => $keys,
|
||||
'messages' => [],
|
||||
]
|
||||
],
|
||||
'PARAM_HELP_MSG (string)' => [
|
||||
[ 'test' => [
|
||||
ApiBase::PARAM_HELP_MSG => 'foo',
|
||||
] ],
|
||||
'test',
|
||||
[
|
||||
'issues' => [ 'X' ],
|
||||
'allowedKeys' => $keys,
|
||||
'messages' => [
|
||||
MessageValue::new( 'foo' ),
|
||||
],
|
||||
]
|
||||
],
|
||||
'PARAM_HELP_MSG (array)' => [
|
||||
[ 'test' => [
|
||||
ApiBase::PARAM_HELP_MSG => [ 'foo', 'bar' ],
|
||||
] ],
|
||||
'test',
|
||||
[
|
||||
'issues' => [ 'X' ],
|
||||
'allowedKeys' => $keys,
|
||||
'messages' => [
|
||||
MessageValue::new( 'foo', [ 'bar' ] ),
|
||||
],
|
||||
]
|
||||
],
|
||||
'PARAM_HELP_MSG (Message)' => [
|
||||
[ 'test' => [
|
||||
ApiBase::PARAM_HELP_MSG => Message::newFromKey( 'foo' )->numParams( 123 ),
|
||||
] ],
|
||||
'test',
|
||||
[
|
||||
'issues' => [ 'X' ],
|
||||
'allowedKeys' => $keys,
|
||||
'messages' => [
|
||||
MessageValue::new( 'foo' )->numParams( 123 ),
|
||||
],
|
||||
]
|
||||
],
|
||||
'PARAM_HELP_MSG_APPEND' => [
|
||||
[ 'test' => [ ApiBase::PARAM_HELP_MSG_APPEND => [
|
||||
'foo',
|
||||
false,
|
||||
[ 'bar', 'p1', 'p2' ],
|
||||
Message::newFromKey( 'baz' )->numParams( 123 ),
|
||||
] ] ],
|
||||
'test',
|
||||
[
|
||||
'issues' => [
|
||||
'X',
|
||||
'Message specification for PARAM_HELP_MSG_APPEND[1] is not valid',
|
||||
],
|
||||
'allowedKeys' => $keys,
|
||||
'messages' => [
|
||||
MessageValue::new( 'foo' ),
|
||||
MessageValue::new( 'bar', [ 'p1', 'p2' ] ),
|
||||
MessageValue::new( 'baz' )->numParams( 123 ),
|
||||
],
|
||||
]
|
||||
],
|
||||
'PARAM_HELP_MSG_INFO' => [
|
||||
[ 'test' => [ ApiBase::PARAM_HELP_MSG_INFO => [
|
||||
'foo',
|
||||
[ false ],
|
||||
[ 'foo' ],
|
||||
[ 'bar', 'p1', 'p2' ],
|
||||
] ] ],
|
||||
'test',
|
||||
[
|
||||
'issues' => [
|
||||
'X',
|
||||
'PARAM_HELP_MSG_INFO[0] must be an array, got string',
|
||||
'PARAM_HELP_MSG_INFO[1][0] must be a string, got boolean',
|
||||
],
|
||||
'allowedKeys' => $keys,
|
||||
'messages' => [
|
||||
MessageValue::new( 'apihelp-query+allpages-paraminfo-foo' ),
|
||||
MessageValue::new( 'apihelp-query+allpages-paraminfo-bar', [ 'p1', 'p2' ] ),
|
||||
],
|
||||
]
|
||||
],
|
||||
'PARAM_HELP_MSG_PER_VALUE for non-array type' => [
|
||||
[ 'test' => [
|
||||
ParamValidator::PARAM_TYPE => 'namespace',
|
||||
ApiBase::PARAM_HELP_MSG_PER_VALUE => [],
|
||||
] ],
|
||||
'test',
|
||||
[
|
||||
'issues' => [
|
||||
'X',
|
||||
ApiBase::PARAM_HELP_MSG_PER_VALUE
|
||||
=> 'PARAM_HELP_MSG_PER_VALUE can only be used with PARAM_TYPE as an array',
|
||||
],
|
||||
'allowedKeys' => $keys,
|
||||
'messages' => [],
|
||||
]
|
||||
],
|
||||
'PARAM_HELP_MSG_PER_VALUE' => [
|
||||
[ 'test' => [
|
||||
ParamValidator::PARAM_TYPE => [ 'a', 'b', 'c', 'd' ],
|
||||
ApiBase::PARAM_HELP_MSG_PER_VALUE => [
|
||||
'a' => null,
|
||||
'b' => 'bbb',
|
||||
'c' => [ 'ccc', 'p1', 'p2' ],
|
||||
'd' => Message::newFromKey( 'ddd' )->numParams( 123 ),
|
||||
'e' => 'eee',
|
||||
],
|
||||
] ],
|
||||
'test',
|
||||
[
|
||||
'issues' => [
|
||||
'X',
|
||||
'Message specification for PARAM_HELP_MSG_PER_VALUE[a] is not valid',
|
||||
'PARAM_HELP_MSG_PER_VALUE contains "e", which is not in PARAM_TYPE.',
|
||||
],
|
||||
'allowedKeys' => $keys,
|
||||
'messages' => [
|
||||
MessageValue::new( 'bbb' ),
|
||||
MessageValue::new( 'ccc', [ 'p1', 'p2' ] ),
|
||||
MessageValue::new( 'ddd' )->numParams( 123 ),
|
||||
MessageValue::new( 'eee' ),
|
||||
],
|
||||
]
|
||||
],
|
||||
'Template-style parameter name without PARAM_TEMPLATE_VARS' => [
|
||||
[ 'test{x}' => null ],
|
||||
'test{x}',
|
||||
[
|
||||
'issues' => [
|
||||
'X',
|
||||
"Parameter name may not contain '{' or '}' without PARAM_TEMPLATE_VARS",
|
||||
],
|
||||
'allowedKeys' => $keys,
|
||||
'messages' => [],
|
||||
]
|
||||
],
|
||||
'PARAM_TEMPLATE_VARS cannot be empty' => [
|
||||
[ 'test{x}' => [
|
||||
ApiBase::PARAM_TEMPLATE_VARS => [],
|
||||
] ],
|
||||
'test{x}',
|
||||
[
|
||||
'issues' => [
|
||||
'X',
|
||||
ApiBase::PARAM_TEMPLATE_VARS => 'PARAM_TEMPLATE_VARS cannot be the empty array',
|
||||
],
|
||||
'allowedKeys' => $keys,
|
||||
'messages' => [],
|
||||
]
|
||||
],
|
||||
'PARAM_TEMPLATE_VARS, ok' => [
|
||||
[
|
||||
'ok' => [
|
||||
ParamValidator::PARAM_ISMULTI => true,
|
||||
],
|
||||
'ok-templated-{x}' => [
|
||||
ParamValidator::PARAM_ISMULTI => true,
|
||||
ApiBase::PARAM_TEMPLATE_VARS => [
|
||||
'x' => 'ok',
|
||||
],
|
||||
],
|
||||
'test-{a}-{b}' => [
|
||||
ApiBase::PARAM_TEMPLATE_VARS => [
|
||||
'a' => 'ok',
|
||||
'b' => 'ok-templated-{x}',
|
||||
],
|
||||
],
|
||||
],
|
||||
'test-{a}-{b}',
|
||||
[
|
||||
'issues' => [ 'X' ],
|
||||
'allowedKeys' => $keys,
|
||||
'messages' => [],
|
||||
]
|
||||
],
|
||||
'PARAM_TEMPLATE_VARS simple errors' => [
|
||||
[
|
||||
'ok' => [
|
||||
ParamValidator::PARAM_ISMULTI => true,
|
||||
],
|
||||
'not-multi' => false,
|
||||
'test-{a}-{b}-{c}' => [
|
||||
ApiBase::PARAM_TEMPLATE_VARS => [
|
||||
'{x}' => 'ok',
|
||||
'not-in-name' => 'ok',
|
||||
'a' => false,
|
||||
'b' => 'missing',
|
||||
'c' => 'not-multi',
|
||||
],
|
||||
],
|
||||
],
|
||||
'test-{a}-{b}-{c}',
|
||||
[
|
||||
'issues' => [
|
||||
'X',
|
||||
"PARAM_TEMPLATE_VARS keys may not contain '{' or '}', got \"{x}\"",
|
||||
'Parameter name must contain PARAM_TEMPLATE_VARS key {not-in-name}',
|
||||
'PARAM_TEMPLATE_VARS[a] has invalid target type boolean',
|
||||
'PARAM_TEMPLATE_VARS[b] target parameter "missing" does not exist',
|
||||
'PARAM_TEMPLATE_VARS[c] target parameter "not-multi" must have PARAM_ISMULTI = true',
|
||||
],
|
||||
'allowedKeys' => $keys,
|
||||
'messages' => [],
|
||||
]
|
||||
],
|
||||
'PARAM_TEMPLATE_VARS no recursion' => [
|
||||
[
|
||||
'test-{a}' => [
|
||||
ParamValidator::PARAM_ISMULTI => true,
|
||||
ApiBase::PARAM_TEMPLATE_VARS => [
|
||||
'a' => 'test-{a}',
|
||||
],
|
||||
],
|
||||
],
|
||||
'test-{a}',
|
||||
[
|
||||
'issues' => [
|
||||
'X',
|
||||
'PARAM_TEMPLATE_VARS[a] cannot target the parameter itself'
|
||||
],
|
||||
'allowedKeys' => $keys,
|
||||
'messages' => [],
|
||||
]
|
||||
],
|
||||
'PARAM_TEMPLATE_VARS targeting another template, target must be a subset' => [
|
||||
[
|
||||
'ok1' => [ ParamValidator::PARAM_ISMULTI => true ],
|
||||
'ok2' => [ ParamValidator::PARAM_ISMULTI => true ],
|
||||
'test1-{a}' => [
|
||||
ApiBase::PARAM_TEMPLATE_VARS => [
|
||||
'a' => 'test2-{a}',
|
||||
],
|
||||
],
|
||||
'test2-{a}' => [
|
||||
ParamValidator::PARAM_ISMULTI => true,
|
||||
ApiBase::PARAM_TEMPLATE_VARS => [
|
||||
'a' => 'ok2',
|
||||
],
|
||||
],
|
||||
],
|
||||
'test1-{a}',
|
||||
[
|
||||
'issues' => [
|
||||
'X',
|
||||
'PARAM_TEMPLATE_VARS[a]: Target\'s PARAM_TEMPLATE_VARS must be a subset of the original',
|
||||
],
|
||||
'allowedKeys' => $keys,
|
||||
'messages' => [],
|
||||
]
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideGetValue
|
||||
* @param string|null $data Request value
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ use ApiModuleManager;
|
|||
use MockApi;
|
||||
use Wikimedia\Message\DataMessageValue;
|
||||
use Wikimedia\ParamValidator\ParamValidator;
|
||||
use Wikimedia\ParamValidator\TypeDef\EnumDef;
|
||||
use Wikimedia\ParamValidator\TypeDef\TypeDefTestCase;
|
||||
use Wikimedia\ParamValidator\ValidationException;
|
||||
use Wikimedia\TestingAccessWrapper;
|
||||
|
|
@ -119,6 +120,85 @@ class SubmoduleDefTest extends TypeDefTestCase {
|
|||
];
|
||||
}
|
||||
|
||||
public function provideCheckSettings() {
|
||||
$opts = [
|
||||
'module' => $this->mockApi(),
|
||||
];
|
||||
$keys = [
|
||||
'Y', EnumDef::PARAM_DEPRECATED_VALUES,
|
||||
SubmoduleDef::PARAM_SUBMODULE_MAP, SubmoduleDef::PARAM_SUBMODULE_PARAM_PREFIX
|
||||
];
|
||||
|
||||
return [
|
||||
'Basic test' => [
|
||||
[],
|
||||
self::STDRET,
|
||||
[
|
||||
'issues' => [ 'X' ],
|
||||
'allowedKeys' => $keys,
|
||||
'messages' => [],
|
||||
],
|
||||
$opts
|
||||
],
|
||||
'Test with everything' => [
|
||||
[
|
||||
SubmoduleDef::PARAM_SUBMODULE_MAP => [
|
||||
'foo' => 'testmod+mod1', 'bar' => 'testmod+mod2'
|
||||
],
|
||||
SubmoduleDef::PARAM_SUBMODULE_PARAM_PREFIX => 'g',
|
||||
],
|
||||
self::STDRET,
|
||||
[
|
||||
'issues' => [ 'X' ],
|
||||
'allowedKeys' => $keys,
|
||||
'messages' => [],
|
||||
],
|
||||
$opts
|
||||
],
|
||||
'Bad types' => [
|
||||
[
|
||||
SubmoduleDef::PARAM_SUBMODULE_MAP => false,
|
||||
SubmoduleDef::PARAM_SUBMODULE_PARAM_PREFIX => true,
|
||||
],
|
||||
self::STDRET,
|
||||
[
|
||||
'issues' => [
|
||||
'X',
|
||||
SubmoduleDef::PARAM_SUBMODULE_MAP => 'PARAM_SUBMODULE_MAP must be an array, got boolean',
|
||||
SubmoduleDef::PARAM_SUBMODULE_PARAM_PREFIX
|
||||
=> 'PARAM_SUBMODULE_PARAM_PREFIX must be a string, got boolean',
|
||||
],
|
||||
'allowedKeys' => $keys,
|
||||
'messages' => [],
|
||||
],
|
||||
$opts
|
||||
],
|
||||
'Bad values in map' => [
|
||||
[
|
||||
SubmoduleDef::PARAM_SUBMODULE_MAP => [
|
||||
'a' => 'testmod+mod1',
|
||||
'b' => false,
|
||||
'c' => null,
|
||||
'd' => 'testmod+mod7',
|
||||
'r' => 'testmod+recurse+recurse',
|
||||
],
|
||||
],
|
||||
self::STDRET,
|
||||
[
|
||||
'issues' => [
|
||||
'X',
|
||||
'Values for PARAM_SUBMODULE_MAP must be strings, but value for "b" is boolean',
|
||||
'Values for PARAM_SUBMODULE_MAP must be strings, but value for "c" is NULL',
|
||||
'PARAM_SUBMODULE_MAP contains "testmod+mod7", which is not a valid module path',
|
||||
],
|
||||
'allowedKeys' => $keys,
|
||||
'messages' => [],
|
||||
],
|
||||
$opts
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function provideGetEnumValues() {
|
||||
$opts = [
|
||||
'module' => $this->mockApi(),
|
||||
|
|
|
|||
|
|
@ -162,6 +162,293 @@ class ParamValidatorTest extends \PHPUnit\Framework\TestCase {
|
|||
];
|
||||
}
|
||||
|
||||
/** @dataProvider provideCheckSettings */
|
||||
public function testCheckSettings( $settings, array $expect ) : void {
|
||||
$callbacks = new SimpleCallbacks( [] );
|
||||
|
||||
$mb = $this->getMockBuilder( TypeDef::class )
|
||||
->setConstructorArgs( [ $callbacks ] )
|
||||
->setMethods( [ 'checkSettings' ] );
|
||||
$mock1 = $mb->getMockForAbstractClass();
|
||||
$mock1->method( 'checkSettings' )->willReturnCallback(
|
||||
function ( string $name, $settings, array $options, array $ret ) {
|
||||
$ret['allowedKeys'][] = 'XXX-test';
|
||||
if ( isset( $settings['XXX-test'] ) ) {
|
||||
$ret['issues']['XXX-test'] = 'XXX-test was ' . $settings['XXX-test'];
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
);
|
||||
$mock2 = $mb->getMockForAbstractClass();
|
||||
$mock2->method( 'checkSettings' )->willReturnCallback(
|
||||
function ( string $name, $settings, array $options, array $ret ) {
|
||||
return $ret;
|
||||
}
|
||||
);
|
||||
|
||||
$validator = new ParamValidator(
|
||||
$callbacks,
|
||||
new ObjectFactory( $this->getMockForAbstractClass( ContainerInterface::class ) ),
|
||||
[ 'typeDefs' => [ 'foo' => $mock1, 'NULL' => $mock2 ] + ParamValidator::$STANDARD_TYPES ]
|
||||
);
|
||||
|
||||
$this->assertEquals( $expect, $validator->checkSettings( 'dummy', $settings, [] ) );
|
||||
}
|
||||
|
||||
public function provideCheckSettings() : array {
|
||||
$normalKeys = [
|
||||
ParamValidator::PARAM_TYPE, ParamValidator::PARAM_DEFAULT, ParamValidator::PARAM_REQUIRED,
|
||||
ParamValidator::PARAM_ISMULTI, ParamValidator::PARAM_SENSITIVE, ParamValidator::PARAM_DEPRECATED,
|
||||
ParamValidator::PARAM_IGNORE_UNRECOGNIZED_VALUES,
|
||||
];
|
||||
$multiKeys = array_merge( $normalKeys, [
|
||||
ParamValidator::PARAM_ISMULTI_LIMIT1, ParamValidator::PARAM_ISMULTI_LIMIT2,
|
||||
ParamValidator::PARAM_ALL, ParamValidator::PARAM_ALLOW_DUPLICATES
|
||||
] );
|
||||
$multiEnumKeys = array_merge( $multiKeys, [ TypeDef\EnumDef::PARAM_DEPRECATED_VALUES ] );
|
||||
|
||||
return [
|
||||
'Basic test' => [
|
||||
null,
|
||||
[
|
||||
'issues' => [],
|
||||
'allowedKeys' => $normalKeys,
|
||||
'messages' => [],
|
||||
],
|
||||
],
|
||||
'Basic multi-value' => [
|
||||
[
|
||||
ParamValidator::PARAM_ISMULTI => true,
|
||||
],
|
||||
[
|
||||
'issues' => [],
|
||||
'allowedKeys' => $multiKeys,
|
||||
'messages' => [],
|
||||
],
|
||||
],
|
||||
'Test with everything' => [
|
||||
[
|
||||
ParamValidator::PARAM_TYPE => [ 'a', 'b', 'c', 'd' ],
|
||||
ParamValidator::PARAM_DEFAULT => 'a|b',
|
||||
ParamValidator::PARAM_REQUIRED => true,
|
||||
ParamValidator::PARAM_ISMULTI => true,
|
||||
ParamValidator::PARAM_SENSITIVE => false,
|
||||
ParamValidator::PARAM_DEPRECATED => false,
|
||||
ParamValidator::PARAM_IGNORE_UNRECOGNIZED_VALUES => true,
|
||||
ParamValidator::PARAM_ISMULTI_LIMIT1 => 10,
|
||||
ParamValidator::PARAM_ISMULTI_LIMIT2 => 20,
|
||||
ParamValidator::PARAM_ALL => 'all',
|
||||
ParamValidator::PARAM_ALLOW_DUPLICATES => true,
|
||||
],
|
||||
[
|
||||
'issues' => [],
|
||||
'allowedKeys' => $multiEnumKeys,
|
||||
'messages' => [],
|
||||
],
|
||||
],
|
||||
'Lots of bad types' => [
|
||||
[
|
||||
ParamValidator::PARAM_TYPE => false,
|
||||
ParamValidator::PARAM_REQUIRED => 1,
|
||||
ParamValidator::PARAM_ISMULTI => 1,
|
||||
ParamValidator::PARAM_ISMULTI_LIMIT1 => '10',
|
||||
ParamValidator::PARAM_ISMULTI_LIMIT2 => '100',
|
||||
ParamValidator::PARAM_ALL => [],
|
||||
ParamValidator::PARAM_ALLOW_DUPLICATES => 1,
|
||||
ParamValidator::PARAM_SENSITIVE => 1,
|
||||
ParamValidator::PARAM_DEPRECATED => 1,
|
||||
ParamValidator::PARAM_IGNORE_UNRECOGNIZED_VALUES => 1,
|
||||
],
|
||||
[
|
||||
'issues' => [
|
||||
ParamValidator::PARAM_TYPE => 'PARAM_TYPE must be a string or array, got boolean',
|
||||
ParamValidator::PARAM_REQUIRED => 'PARAM_REQUIRED must be boolean, got integer',
|
||||
ParamValidator::PARAM_ISMULTI => 'PARAM_ISMULTI must be boolean, got integer',
|
||||
ParamValidator::PARAM_ISMULTI_LIMIT1 => 'PARAM_ISMULTI_LIMIT1 must be an integer, got string',
|
||||
ParamValidator::PARAM_ISMULTI_LIMIT2 => 'PARAM_ISMULTI_LIMIT2 must be an integer, got string',
|
||||
ParamValidator::PARAM_ALL => 'PARAM_ALL must be a string or boolean, got array',
|
||||
ParamValidator::PARAM_ALLOW_DUPLICATES
|
||||
=> 'PARAM_ALLOW_DUPLICATES must be boolean, got integer',
|
||||
ParamValidator::PARAM_SENSITIVE => 'PARAM_SENSITIVE must be boolean, got integer',
|
||||
ParamValidator::PARAM_DEPRECATED => 'PARAM_DEPRECATED must be boolean, got integer',
|
||||
ParamValidator::PARAM_IGNORE_UNRECOGNIZED_VALUES
|
||||
=> 'PARAM_IGNORE_UNRECOGNIZED_VALUES must be boolean, got integer',
|
||||
],
|
||||
'allowedKeys' => $multiKeys,
|
||||
'messages' => [],
|
||||
],
|
||||
],
|
||||
'Multi-value stuff is ignored without ISMULTI' => [
|
||||
[
|
||||
ParamValidator::PARAM_ISMULTI_LIMIT1 => '10',
|
||||
ParamValidator::PARAM_ISMULTI_LIMIT2 => '100',
|
||||
ParamValidator::PARAM_ALL => [],
|
||||
ParamValidator::PARAM_ALLOW_DUPLICATES => 1,
|
||||
],
|
||||
[
|
||||
'issues' => [],
|
||||
'allowedKeys' => $normalKeys,
|
||||
'messages' => [],
|
||||
],
|
||||
],
|
||||
'PARAM_TYPE is not registered' => [
|
||||
[ ParamValidator::PARAM_TYPE => 'xyz' ],
|
||||
[
|
||||
'issues' => [
|
||||
ParamValidator::PARAM_TYPE => 'Unknown/unregistered PARAM_TYPE "xyz"',
|
||||
],
|
||||
'allowedKeys' => $normalKeys,
|
||||
'messages' => [],
|
||||
],
|
||||
],
|
||||
'PARAM_DEFAULT value doesn\'t validate' => [
|
||||
[
|
||||
ParamValidator::PARAM_TYPE => [ 'a', 'b', 'c', 'd' ],
|
||||
ParamValidator::PARAM_DEFAULT => 'a|b',
|
||||
],
|
||||
[
|
||||
'issues' => [
|
||||
ParamValidator::PARAM_DEFAULT => 'Value for PARAM_DEFAULT does not validate (code badvalue)',
|
||||
],
|
||||
'allowedKeys' => array_merge( $normalKeys, [ TypeDef\EnumDef::PARAM_DEPRECATED_VALUES ] ),
|
||||
'messages' => [],
|
||||
],
|
||||
],
|
||||
'PARAM_ISMULTI_LIMIT1 out of range' => [
|
||||
[
|
||||
ParamValidator::PARAM_ISMULTI => true,
|
||||
ParamValidator::PARAM_ISMULTI_LIMIT1 => 0,
|
||||
],
|
||||
[
|
||||
'issues' => [
|
||||
ParamValidator::PARAM_ISMULTI_LIMIT1 => 'PARAM_ISMULTI_LIMIT1 must be greater than 0, got 0',
|
||||
],
|
||||
'allowedKeys' => $multiKeys,
|
||||
'messages' => [],
|
||||
],
|
||||
],
|
||||
'PARAM_ISMULTI_LIMIT2 out of range' => [
|
||||
[
|
||||
ParamValidator::PARAM_ISMULTI => true,
|
||||
ParamValidator::PARAM_ISMULTI_LIMIT1 => 100,
|
||||
ParamValidator::PARAM_ISMULTI_LIMIT2 => 10,
|
||||
],
|
||||
[
|
||||
'issues' => [
|
||||
// phpcs:ignore Generic.Files.LineLength.TooLong
|
||||
ParamValidator::PARAM_ISMULTI_LIMIT2 => 'PARAM_ISMULTI_LIMIT2 must be greater than or equal to PARAM_ISMULTI_LIMIT1, but 10 < 100',
|
||||
],
|
||||
'allowedKeys' => $multiKeys,
|
||||
'messages' => [],
|
||||
],
|
||||
],
|
||||
'PARAM_ISMULTI_LIMIT1 = LIMIT2 is ok' => [
|
||||
[
|
||||
ParamValidator::PARAM_ISMULTI => true,
|
||||
ParamValidator::PARAM_ISMULTI_LIMIT1 => 10,
|
||||
ParamValidator::PARAM_ISMULTI_LIMIT2 => 10,
|
||||
],
|
||||
[
|
||||
'issues' => [],
|
||||
'allowedKeys' => $multiKeys,
|
||||
'messages' => [],
|
||||
],
|
||||
],
|
||||
'PARAM_ALL false is ok with non-enumerated type' => [
|
||||
[
|
||||
ParamValidator::PARAM_ISMULTI => true,
|
||||
ParamValidator::PARAM_ALL => false,
|
||||
],
|
||||
[
|
||||
'issues' => [],
|
||||
'allowedKeys' => $multiKeys,
|
||||
'messages' => [],
|
||||
],
|
||||
],
|
||||
'PARAM_ALL true is not ok with non-enumerated type' => [
|
||||
[
|
||||
ParamValidator::PARAM_ISMULTI => true,
|
||||
ParamValidator::PARAM_ALL => true,
|
||||
],
|
||||
[
|
||||
'issues' => [
|
||||
ParamValidator::PARAM_ALL => 'PARAM_ALL cannot be used with non-enumerated types',
|
||||
],
|
||||
'allowedKeys' => $multiKeys,
|
||||
'messages' => [],
|
||||
],
|
||||
],
|
||||
'PARAM_ALL string is not ok with non-enumerated type' => [
|
||||
[
|
||||
ParamValidator::PARAM_ISMULTI => true,
|
||||
ParamValidator::PARAM_ALL => 'all',
|
||||
],
|
||||
[
|
||||
'issues' => [
|
||||
ParamValidator::PARAM_ALL => 'PARAM_ALL cannot be used with non-enumerated types',
|
||||
],
|
||||
'allowedKeys' => $multiKeys,
|
||||
'messages' => [],
|
||||
],
|
||||
],
|
||||
'PARAM_ALL true value collision' => [
|
||||
[
|
||||
ParamValidator::PARAM_TYPE => [ 'a', 'b', '*' ],
|
||||
ParamValidator::PARAM_ISMULTI => true,
|
||||
ParamValidator::PARAM_ALL => true,
|
||||
],
|
||||
[
|
||||
'issues' => [
|
||||
ParamValidator::PARAM_ALL => 'Value for PARAM_ALL conflicts with an enumerated value',
|
||||
],
|
||||
'allowedKeys' => $multiEnumKeys,
|
||||
'messages' => [],
|
||||
],
|
||||
],
|
||||
'PARAM_ALL string value collision' => [
|
||||
[
|
||||
ParamValidator::PARAM_TYPE => [ 'a', 'b', 'all' ],
|
||||
ParamValidator::PARAM_ISMULTI => true,
|
||||
ParamValidator::PARAM_ALL => 'all',
|
||||
],
|
||||
[
|
||||
'issues' => [
|
||||
ParamValidator::PARAM_ALL => 'Value for PARAM_ALL conflicts with an enumerated value',
|
||||
],
|
||||
'allowedKeys' => $multiEnumKeys,
|
||||
'messages' => [],
|
||||
],
|
||||
],
|
||||
'TypeDef is called' => [
|
||||
[
|
||||
ParamValidator::PARAM_TYPE => 'foo',
|
||||
'XXX-test' => '!!!',
|
||||
],
|
||||
[
|
||||
'issues' => [
|
||||
'XXX-test' => 'XXX-test was !!!',
|
||||
],
|
||||
'allowedKeys' => array_merge( $normalKeys, [ 'XXX-test' ] ),
|
||||
'messages' => [],
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function testCheckSettings_noEnum() : void {
|
||||
$callbacks = new SimpleCallbacks( [] );
|
||||
$validator = new ParamValidator(
|
||||
$callbacks,
|
||||
new ObjectFactory( $this->getMockForAbstractClass( ContainerInterface::class ) ),
|
||||
[ 'typeDefs' => [] ]
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
[ ParamValidator::PARAM_TYPE => 'Unknown/unregistered PARAM_TYPE "enum"' ],
|
||||
$validator->checkSettings( 'dummy', [ ParamValidator::PARAM_TYPE => [ 'xyz' ] ], [] )['issues']
|
||||
);
|
||||
}
|
||||
|
||||
/** @dataProvider provideExplodeMultiValue */
|
||||
public function testExplodeMultiValue( $value, $limit, $expect ) {
|
||||
$this->assertSame( $expect, ParamValidator::explodeMultiValue( $value, $limit ) );
|
||||
|
|
|
|||
|
|
@ -68,6 +68,69 @@ class EnumDefTest extends TypeDefTestCase {
|
|||
];
|
||||
}
|
||||
|
||||
public function provideCheckSettings() {
|
||||
return [
|
||||
'Basic test' => [
|
||||
[
|
||||
ParamValidator::PARAM_TYPE => [ 'a', 'b', 'c', 'd', 'e' ],
|
||||
],
|
||||
self::STDRET,
|
||||
[
|
||||
'issues' => [ 'X' ],
|
||||
'allowedKeys' => [ 'Y', EnumDef::PARAM_DEPRECATED_VALUES ],
|
||||
'messages' => [],
|
||||
],
|
||||
],
|
||||
'Bad type for PARAM_DEPRECATED_VALUES' => [
|
||||
[
|
||||
ParamValidator::PARAM_TYPE => [ 'a', 'b', 'c', 'd', 'e' ],
|
||||
EnumDef::PARAM_DEPRECATED_VALUES => false,
|
||||
],
|
||||
self::STDRET,
|
||||
[
|
||||
'issues' => [
|
||||
'X',
|
||||
EnumDef::PARAM_DEPRECATED_VALUES => 'PARAM_DEPRECATED_VALUES must be an array, got boolean',
|
||||
],
|
||||
'allowedKeys' => [ 'Y', EnumDef::PARAM_DEPRECATED_VALUES ],
|
||||
'messages' => [],
|
||||
],
|
||||
],
|
||||
'PARAM_DEPRECATED_VALUES value errors' => [
|
||||
[
|
||||
ParamValidator::PARAM_TYPE => [ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 0, '1' ],
|
||||
EnumDef::PARAM_DEPRECATED_VALUES => [
|
||||
'b' => null,
|
||||
'c' => false,
|
||||
'd' => true,
|
||||
'e' => MessageValue::new( 'e' ),
|
||||
'f' => 'f',
|
||||
'g' => $this,
|
||||
0 => true,
|
||||
1 => true,
|
||||
'x' => null,
|
||||
],
|
||||
],
|
||||
self::STDRET,
|
||||
[
|
||||
'issues' => [
|
||||
'X',
|
||||
// phpcs:disable Generic.Files.LineLength
|
||||
'Values in PARAM_DEPRECATED_VALUES must be null, true, or MessageValue, but value for "c" is false',
|
||||
'Values in PARAM_DEPRECATED_VALUES must be null, true, or MessageValue, but value for "f" is string',
|
||||
'Values in PARAM_DEPRECATED_VALUES must be null, true, or MessageValue, but value for "g" is ' . static::class,
|
||||
// phpcs:enable
|
||||
'PARAM_DEPRECATED_VALUES contains "x", which is not one of the enumerated values',
|
||||
],
|
||||
'allowedKeys' => [ 'Y', EnumDef::PARAM_DEPRECATED_VALUES ],
|
||||
'messages' => [
|
||||
MessageValue::new( 'e' ),
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function provideGetEnumValues() {
|
||||
return [
|
||||
'Basic test' => [
|
||||
|
|
|
|||
|
|
@ -88,6 +88,59 @@ class FloatDefTest extends TypeDefTestCase {
|
|||
];
|
||||
}
|
||||
|
||||
public function provideCheckSettings() {
|
||||
$keys = [
|
||||
'Y', FloatDef::PARAM_IGNORE_RANGE,
|
||||
FloatDef::PARAM_MIN, FloatDef::PARAM_MAX, FloatDef::PARAM_MAX2
|
||||
];
|
||||
|
||||
return [
|
||||
'Basic test' => [
|
||||
[],
|
||||
self::STDRET,
|
||||
[
|
||||
'issues' => [ 'X' ],
|
||||
'allowedKeys' => $keys,
|
||||
'messages' => [],
|
||||
],
|
||||
],
|
||||
'Test with everything' => [
|
||||
[
|
||||
FloatDef::PARAM_IGNORE_RANGE => true,
|
||||
FloatDef::PARAM_MIN => -100.0,
|
||||
FloatDef::PARAM_MAX => -90.0,
|
||||
FloatDef::PARAM_MAX2 => -80.0,
|
||||
],
|
||||
self::STDRET,
|
||||
[
|
||||
'issues' => [ 'X' ],
|
||||
'allowedKeys' => $keys,
|
||||
'messages' => [],
|
||||
],
|
||||
],
|
||||
'Bad types' => [
|
||||
[
|
||||
FloatDef::PARAM_IGNORE_RANGE => 1,
|
||||
FloatDef::PARAM_MIN => 1,
|
||||
FloatDef::PARAM_MAX => '2',
|
||||
FloatDef::PARAM_MAX2 => '3',
|
||||
],
|
||||
self::STDRET,
|
||||
[
|
||||
'issues' => [
|
||||
'X',
|
||||
FloatDef::PARAM_IGNORE_RANGE => 'PARAM_IGNORE_RANGE must be boolean, got integer',
|
||||
FloatDef::PARAM_MIN => 'PARAM_MIN must be double, got integer',
|
||||
FloatDef::PARAM_MAX => 'PARAM_MAX must be double, got string',
|
||||
FloatDef::PARAM_MAX2 => 'PARAM_MAX2 must be double, got string',
|
||||
],
|
||||
'allowedKeys' => $keys,
|
||||
'messages' => [],
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function provideStringifyValue() {
|
||||
$digits = defined( 'PHP_FLOAT_DIG' ) ? PHP_FLOAT_DIG : 15;
|
||||
|
||||
|
|
|
|||
|
|
@ -134,6 +134,127 @@ class IntegerDefTest extends TypeDefTestCase {
|
|||
];
|
||||
}
|
||||
|
||||
public function provideCheckSettings() {
|
||||
$keys = [
|
||||
'Y', IntegerDef::PARAM_IGNORE_RANGE,
|
||||
IntegerDef::PARAM_MIN, IntegerDef::PARAM_MAX, IntegerDef::PARAM_MAX2
|
||||
];
|
||||
|
||||
return [
|
||||
'Basic test' => [
|
||||
[],
|
||||
self::STDRET,
|
||||
[
|
||||
'issues' => [ 'X' ],
|
||||
'allowedKeys' => $keys,
|
||||
'messages' => [],
|
||||
],
|
||||
],
|
||||
'Test with everything' => [
|
||||
[
|
||||
IntegerDef::PARAM_IGNORE_RANGE => true,
|
||||
IntegerDef::PARAM_MIN => -100,
|
||||
IntegerDef::PARAM_MAX => -90,
|
||||
IntegerDef::PARAM_MAX2 => -80,
|
||||
],
|
||||
self::STDRET,
|
||||
[
|
||||
'issues' => [ 'X' ],
|
||||
'allowedKeys' => $keys,
|
||||
'messages' => [],
|
||||
],
|
||||
],
|
||||
'Bad types' => [
|
||||
[
|
||||
IntegerDef::PARAM_IGNORE_RANGE => 1,
|
||||
IntegerDef::PARAM_MIN => 1.0,
|
||||
IntegerDef::PARAM_MAX => '2',
|
||||
IntegerDef::PARAM_MAX2 => '3',
|
||||
],
|
||||
self::STDRET,
|
||||
[
|
||||
'issues' => [
|
||||
'X',
|
||||
IntegerDef::PARAM_IGNORE_RANGE => 'PARAM_IGNORE_RANGE must be boolean, got integer',
|
||||
IntegerDef::PARAM_MIN => 'PARAM_MIN must be integer, got double',
|
||||
IntegerDef::PARAM_MAX => 'PARAM_MAX must be integer, got string',
|
||||
IntegerDef::PARAM_MAX2 => 'PARAM_MAX2 must be integer, got string',
|
||||
],
|
||||
'allowedKeys' => $keys,
|
||||
'messages' => [],
|
||||
],
|
||||
],
|
||||
'Min == max' => [
|
||||
[
|
||||
IntegerDef::PARAM_MIN => 1,
|
||||
IntegerDef::PARAM_MAX => 1,
|
||||
],
|
||||
self::STDRET,
|
||||
[
|
||||
'issues' => [ 'X' ],
|
||||
'allowedKeys' => $keys,
|
||||
'messages' => [],
|
||||
],
|
||||
],
|
||||
'Min > max' => [
|
||||
[
|
||||
IntegerDef::PARAM_MIN => 2,
|
||||
IntegerDef::PARAM_MAX => 1,
|
||||
],
|
||||
self::STDRET,
|
||||
[
|
||||
'issues' => [
|
||||
'X',
|
||||
'PARAM_MIN must be less than or equal to PARAM_MAX, but 2 > 1',
|
||||
],
|
||||
'allowedKeys' => $keys,
|
||||
'messages' => [],
|
||||
],
|
||||
],
|
||||
'Max2 without max' => [
|
||||
[
|
||||
IntegerDef::PARAM_MAX2 => 1,
|
||||
],
|
||||
self::STDRET,
|
||||
[
|
||||
'issues' => [
|
||||
'X',
|
||||
'PARAM_MAX2 cannot be used without PARAM_MAX',
|
||||
],
|
||||
'allowedKeys' => $keys,
|
||||
'messages' => [],
|
||||
],
|
||||
],
|
||||
'Max2 == max' => [
|
||||
[
|
||||
IntegerDef::PARAM_MAX => 1,
|
||||
IntegerDef::PARAM_MAX2 => 1,
|
||||
],
|
||||
self::STDRET,
|
||||
[
|
||||
'issues' => [ 'X' ],
|
||||
'allowedKeys' => $keys,
|
||||
'messages' => [],
|
||||
],
|
||||
],
|
||||
'Max2 < max' => [
|
||||
[
|
||||
IntegerDef::PARAM_MAX => -10,
|
||||
IntegerDef::PARAM_MAX2 => -11,
|
||||
],
|
||||
self::STDRET,
|
||||
[
|
||||
'issues' => [
|
||||
'X',
|
||||
'PARAM_MAX2 must be greater than or equal to PARAM_MAX, but -11 < -10',
|
||||
],
|
||||
'allowedKeys' => $keys,
|
||||
'messages' => [],
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function provideGetInfo() {
|
||||
return [
|
||||
'Basic' => [
|
||||
|
|
|
|||
|
|
@ -57,6 +57,115 @@ class LimitDefTest extends TypeDefTestCase {
|
|||
];
|
||||
}
|
||||
|
||||
public function provideCheckSettings() {
|
||||
$keys = [
|
||||
'Y', IntegerDef::PARAM_IGNORE_RANGE,
|
||||
IntegerDef::PARAM_MIN, IntegerDef::PARAM_MAX, IntegerDef::PARAM_MAX2
|
||||
];
|
||||
|
||||
return [
|
||||
'Basic test' => [
|
||||
[
|
||||
LimitDef::PARAM_MAX => 10,
|
||||
],
|
||||
self::STDRET,
|
||||
[
|
||||
'issues' => [ 'X' ],
|
||||
'allowedKeys' => $keys,
|
||||
'messages' => [],
|
||||
],
|
||||
],
|
||||
'Test with everything' => [
|
||||
[
|
||||
LimitDef::PARAM_IGNORE_RANGE => true,
|
||||
LimitDef::PARAM_MIN => 0,
|
||||
LimitDef::PARAM_MAX => 10,
|
||||
LimitDef::PARAM_MAX2 => 100,
|
||||
],
|
||||
self::STDRET,
|
||||
[
|
||||
'issues' => [ 'X' ],
|
||||
'allowedKeys' => $keys,
|
||||
'messages' => [],
|
||||
],
|
||||
],
|
||||
'PARAM_ISMULTI not allowed' => [
|
||||
[
|
||||
ParamValidator::PARAM_ISMULTI => true,
|
||||
LimitDef::PARAM_MAX => 10,
|
||||
],
|
||||
self::STDRET,
|
||||
[
|
||||
'issues' => [
|
||||
'X',
|
||||
ParamValidator::PARAM_ISMULTI => 'PARAM_ISMULTI cannot be used for limit-type parameters',
|
||||
],
|
||||
'allowedKeys' => $keys,
|
||||
'messages' => [],
|
||||
],
|
||||
],
|
||||
'PARAM_ISMULTI not allowed, but another ISMULTI issue was already logged' => [
|
||||
[
|
||||
ParamValidator::PARAM_ISMULTI => true,
|
||||
LimitDef::PARAM_MAX => 10,
|
||||
],
|
||||
[
|
||||
'issues' => [
|
||||
ParamValidator::PARAM_ISMULTI => 'XXX',
|
||||
],
|
||||
'allowedKeys' => [ 'Y' ],
|
||||
'messages' => [],
|
||||
],
|
||||
[
|
||||
'issues' => [
|
||||
ParamValidator::PARAM_ISMULTI => 'XXX',
|
||||
],
|
||||
'allowedKeys' => $keys,
|
||||
'messages' => [],
|
||||
],
|
||||
],
|
||||
'PARAM_MIN == 0' => [
|
||||
[
|
||||
LimitDef::PARAM_MIN => 0,
|
||||
LimitDef::PARAM_MAX => 2,
|
||||
],
|
||||
self::STDRET,
|
||||
[
|
||||
'issues' => [ 'X' ],
|
||||
'allowedKeys' => $keys,
|
||||
'messages' => [],
|
||||
],
|
||||
],
|
||||
'PARAM_MIN < 0' => [
|
||||
[
|
||||
LimitDef::PARAM_MIN => -1,
|
||||
LimitDef::PARAM_MAX => 2,
|
||||
],
|
||||
self::STDRET,
|
||||
[
|
||||
'issues' => [
|
||||
'X',
|
||||
'PARAM_MIN must be greater than or equal to 0',
|
||||
],
|
||||
'allowedKeys' => $keys,
|
||||
'messages' => [],
|
||||
],
|
||||
],
|
||||
'PARAM_MAX is required' => [
|
||||
[],
|
||||
self::STDRET,
|
||||
[
|
||||
'issues' => [
|
||||
'X',
|
||||
'PARAM_MAX must be set',
|
||||
],
|
||||
'allowedKeys' => $keys,
|
||||
'messages' => [],
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function provideGetInfo() {
|
||||
return [
|
||||
'Basic' => [
|
||||
|
|
|
|||
|
|
@ -20,4 +20,45 @@ class PasswordDefTest extends StringDefTest {
|
|||
];
|
||||
}
|
||||
|
||||
public function provideCheckSettings() {
|
||||
$keys = [ 'Y', StringDef::PARAM_MAX_BYTES, StringDef::PARAM_MAX_CHARS ];
|
||||
|
||||
yield from parent::provideCheckSettings();
|
||||
|
||||
yield 'PARAM_SENSITIVE cannot be false' => [
|
||||
[
|
||||
ParamValidator::PARAM_SENSITIVE => false,
|
||||
],
|
||||
self::STDRET,
|
||||
[
|
||||
'issues' => [
|
||||
'X',
|
||||
ParamValidator::PARAM_SENSITIVE
|
||||
=> 'Cannot set PARAM_SENSITIVE to false for password-type parameters',
|
||||
],
|
||||
'allowedKeys' => $keys,
|
||||
'messages' => [],
|
||||
],
|
||||
];
|
||||
yield 'PARAM_SENSITIVE cannot be false, but another PARAM_SENSITIVE issue was already logged' => [
|
||||
[
|
||||
ParamValidator::PARAM_SENSITIVE => false,
|
||||
],
|
||||
[
|
||||
'issues' => [
|
||||
ParamValidator::PARAM_SENSITIVE => 'XXX',
|
||||
],
|
||||
'allowedKeys' => [ 'Y' ],
|
||||
'messages' => [],
|
||||
],
|
||||
[
|
||||
'issues' => [
|
||||
ParamValidator::PARAM_SENSITIVE => 'XXX',
|
||||
],
|
||||
'allowedKeys' => $keys,
|
||||
'messages' => [],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,6 +28,94 @@ class PresenceBooleanDefTest extends TypeDefTestCase {
|
|||
];
|
||||
}
|
||||
|
||||
public function provideCheckSettings() {
|
||||
return [
|
||||
'Basic test' => [
|
||||
[],
|
||||
self::STDRET,
|
||||
self::STDRET,
|
||||
],
|
||||
'PARAM_ISMULTI not allowed' => [
|
||||
[
|
||||
ParamValidator::PARAM_ISMULTI => true,
|
||||
],
|
||||
self::STDRET,
|
||||
[
|
||||
'issues' => [
|
||||
'X',
|
||||
ParamValidator::PARAM_ISMULTI
|
||||
=> 'PARAM_ISMULTI cannot be used for presence-boolean-type parameters',
|
||||
],
|
||||
'allowedKeys' => [ 'Y' ],
|
||||
'messages' => [],
|
||||
],
|
||||
],
|
||||
'PARAM_ISMULTI not allowed, but another ISMULTI issue was already logged' => [
|
||||
[
|
||||
ParamValidator::PARAM_ISMULTI => true,
|
||||
],
|
||||
[
|
||||
'issues' => [
|
||||
ParamValidator::PARAM_ISMULTI => 'XXX',
|
||||
],
|
||||
'allowedKeys' => [ 'Y' ],
|
||||
'messages' => [],
|
||||
],
|
||||
[
|
||||
'issues' => [
|
||||
ParamValidator::PARAM_ISMULTI => 'XXX',
|
||||
],
|
||||
'allowedKeys' => [ 'Y' ],
|
||||
'messages' => [],
|
||||
],
|
||||
],
|
||||
'PARAM_DEFAULT can be false' => [
|
||||
[ ParamValidator::PARAM_DEFAULT => false ],
|
||||
self::STDRET,
|
||||
self::STDRET,
|
||||
],
|
||||
'PARAM_DEFAULT can be null' => [
|
||||
[ ParamValidator::PARAM_DEFAULT => null ],
|
||||
self::STDRET,
|
||||
self::STDRET,
|
||||
],
|
||||
'PARAM_DEFAULT cannot be true' => [
|
||||
[
|
||||
ParamValidator::PARAM_DEFAULT => true,
|
||||
],
|
||||
self::STDRET,
|
||||
[
|
||||
'issues' => [
|
||||
'X',
|
||||
ParamValidator::PARAM_DEFAULT
|
||||
=> 'Default for presence-boolean-type parameters must be false or null',
|
||||
],
|
||||
'allowedKeys' => [ 'Y' ],
|
||||
'messages' => [],
|
||||
],
|
||||
],
|
||||
'PARAM_DEFAULT invalid, but another DEFAULT issue was already logged' => [
|
||||
[
|
||||
ParamValidator::PARAM_DEFAULT => true,
|
||||
],
|
||||
[
|
||||
'issues' => [
|
||||
ParamValidator::PARAM_DEFAULT => 'XXX',
|
||||
],
|
||||
'allowedKeys' => [ 'Y' ],
|
||||
'messages' => [],
|
||||
],
|
||||
[
|
||||
'issues' => [
|
||||
ParamValidator::PARAM_DEFAULT => 'XXX',
|
||||
],
|
||||
'allowedKeys' => [ 'Y' ],
|
||||
'messages' => [],
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function provideGetInfo() {
|
||||
return [
|
||||
'Basic test' => [
|
||||
|
|
|
|||
|
|
@ -92,6 +92,113 @@ class StringDefTest extends TypeDefTestCase {
|
|||
];
|
||||
}
|
||||
|
||||
public function provideCheckSettings() {
|
||||
$keys = [ 'Y', StringDef::PARAM_MAX_BYTES, StringDef::PARAM_MAX_CHARS ];
|
||||
|
||||
return [
|
||||
'Basic test' => [
|
||||
[],
|
||||
self::STDRET,
|
||||
[
|
||||
'issues' => [ 'X' ],
|
||||
'allowedKeys' => $keys,
|
||||
'messages' => [],
|
||||
],
|
||||
],
|
||||
'Test with everything' => [
|
||||
[
|
||||
StringDef::PARAM_MAX_BYTES => 255,
|
||||
StringDef::PARAM_MAX_CHARS => 100,
|
||||
],
|
||||
self::STDRET,
|
||||
[
|
||||
'issues' => [ 'X' ],
|
||||
'allowedKeys' => $keys,
|
||||
'messages' => [],
|
||||
],
|
||||
],
|
||||
'Bad types' => [
|
||||
[
|
||||
StringDef::PARAM_MAX_BYTES => '255',
|
||||
StringDef::PARAM_MAX_CHARS => 100.0,
|
||||
],
|
||||
self::STDRET,
|
||||
[
|
||||
'issues' => [
|
||||
'X',
|
||||
StringDef::PARAM_MAX_BYTES => 'PARAM_MAX_BYTES must be an integer, got string',
|
||||
StringDef::PARAM_MAX_CHARS => 'PARAM_MAX_CHARS must be an integer, got double',
|
||||
],
|
||||
'allowedKeys' => $keys,
|
||||
'messages' => [],
|
||||
],
|
||||
],
|
||||
'Out of range' => [
|
||||
[
|
||||
StringDef::PARAM_MAX_BYTES => -1,
|
||||
StringDef::PARAM_MAX_CHARS => -1,
|
||||
],
|
||||
self::STDRET,
|
||||
[
|
||||
'issues' => [
|
||||
'X',
|
||||
StringDef::PARAM_MAX_BYTES => 'PARAM_MAX_BYTES must be greater than or equal to 0',
|
||||
StringDef::PARAM_MAX_CHARS => 'PARAM_MAX_CHARS must be greater than or equal to 0',
|
||||
],
|
||||
'allowedKeys' => $keys,
|
||||
'messages' => [],
|
||||
],
|
||||
],
|
||||
'Zero not allowed when required and !allowEmptyWhenRequired' => [
|
||||
[
|
||||
ParamValidator::PARAM_REQUIRED => true,
|
||||
StringDef::PARAM_MAX_BYTES => 0,
|
||||
StringDef::PARAM_MAX_CHARS => 0,
|
||||
],
|
||||
self::STDRET,
|
||||
[
|
||||
'issues' => [
|
||||
'X',
|
||||
// phpcs:ignore Generic.Files.LineLength
|
||||
'PARAM_REQUIRED is set, allowEmptyWhenRequired is not set, and PARAM_MAX_BYTES is 0. That\'s impossible to satisfy.',
|
||||
// phpcs:ignore Generic.Files.LineLength
|
||||
'PARAM_REQUIRED is set, allowEmptyWhenRequired is not set, and PARAM_MAX_CHARS is 0. That\'s impossible to satisfy.',
|
||||
],
|
||||
'allowedKeys' => $keys,
|
||||
'messages' => [],
|
||||
],
|
||||
[ 'allowEmptyWhenRequired' => false ],
|
||||
],
|
||||
'Zero allowed when not required' => [
|
||||
[
|
||||
StringDef::PARAM_MAX_BYTES => 0,
|
||||
StringDef::PARAM_MAX_CHARS => 0,
|
||||
],
|
||||
self::STDRET,
|
||||
[
|
||||
'issues' => [ 'X' ],
|
||||
'allowedKeys' => $keys,
|
||||
'messages' => [],
|
||||
],
|
||||
[ 'allowEmptyWhenRequired' => false ],
|
||||
],
|
||||
'Zero allowed when allowEmptyWhenRequired' => [
|
||||
[
|
||||
ParamValidator::PARAM_REQUIRED => true,
|
||||
StringDef::PARAM_MAX_BYTES => 0,
|
||||
StringDef::PARAM_MAX_CHARS => 0,
|
||||
],
|
||||
self::STDRET,
|
||||
[
|
||||
'issues' => [ 'X' ],
|
||||
'allowedKeys' => $keys,
|
||||
'messages' => [],
|
||||
],
|
||||
[ 'allowEmptyWhenRequired' => true ],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function provideGetInfo() {
|
||||
return [
|
||||
'Basic test' => [
|
||||
|
|
|
|||
|
|
@ -23,6 +23,34 @@ class TimestampDefTest extends TypeDefTestCase {
|
|||
return new static::$testClass( $callbacks, $options );
|
||||
}
|
||||
|
||||
/** @dataProvider provideConstructorOptions */
|
||||
public function testConstructorOptions( array $options, $ok ) : void {
|
||||
if ( $ok ) {
|
||||
$this->assertTrue( true ); // dummy
|
||||
} else {
|
||||
$this->expectException( \InvalidArgumentException::class );
|
||||
}
|
||||
$this->getInstance( new SimpleCallbacks( [] ), $options );
|
||||
}
|
||||
|
||||
public function provideConstructorOptions() : array {
|
||||
return [
|
||||
'Basic test' => [ [], true ],
|
||||
'Default format ConvertibleTimestamp' => [ [ 'defaultFormat' => 'ConvertibleTimestamp' ], true ],
|
||||
'Default format DateTime' => [ [ 'defaultFormat' => 'DateTime' ], true ],
|
||||
'Default format TS_ISO_8601' => [ [ 'defaultFormat' => TS_ISO_8601 ], true ],
|
||||
'Default format invalid (string)' => [ [ 'defaultFormat' => 'foobar' ], false ],
|
||||
'Default format invalid (int)' => [ [ 'defaultFormat' => 1000 ], false ],
|
||||
'Stringify format ConvertibleTimestamp' => [
|
||||
[ 'stringifyFormat' => 'ConvertibleTimestamp' ], false
|
||||
],
|
||||
'Stringify format DateTime' => [ [ 'stringifyFormat' => 'DateTime' ], false ],
|
||||
'Stringify format TS_ISO_8601' => [ [ 'stringifyFormat' => TS_ISO_8601 ], true ],
|
||||
'Stringify format invalid (string)' => [ [ 'stringifyFormat' => 'foobar' ], false ],
|
||||
'Stringify format invalid (int)' => [ [ 'stringifyFormat' => 1000 ], false ],
|
||||
];
|
||||
}
|
||||
|
||||
/** @dataProvider provideValidate */
|
||||
public function testValidate(
|
||||
$value, $expect, array $settings = [], array $options = [], array $expectConds = []
|
||||
|
|
@ -82,6 +110,73 @@ class TimestampDefTest extends TypeDefTestCase {
|
|||
];
|
||||
}
|
||||
|
||||
public function provideCheckSettings() {
|
||||
$keys = [ 'Y', TimestampDef::PARAM_TIMESTAMP_FORMAT ];
|
||||
|
||||
return [
|
||||
'Basic test' => [
|
||||
[],
|
||||
self::STDRET,
|
||||
[
|
||||
'issues' => [ 'X' ],
|
||||
'allowedKeys' => $keys,
|
||||
'messages' => [],
|
||||
],
|
||||
],
|
||||
'Test with format ConvertibleTimestamp' => [
|
||||
[ TimestampDef::PARAM_TIMESTAMP_FORMAT => 'ConvertibleTimestamp' ],
|
||||
self::STDRET,
|
||||
[
|
||||
'issues' => [ 'X' ],
|
||||
'allowedKeys' => $keys,
|
||||
'messages' => [],
|
||||
],
|
||||
],
|
||||
'Test with format DateTime' => [
|
||||
[ TimestampDef::PARAM_TIMESTAMP_FORMAT => 'DateTime' ],
|
||||
self::STDRET,
|
||||
[
|
||||
'issues' => [ 'X' ],
|
||||
'allowedKeys' => $keys,
|
||||
'messages' => [],
|
||||
],
|
||||
],
|
||||
'Test with format TS_ISO_8601' => [
|
||||
[ TimestampDef::PARAM_TIMESTAMP_FORMAT => TS_ISO_8601 ],
|
||||
self::STDRET,
|
||||
[
|
||||
'issues' => [ 'X' ],
|
||||
'allowedKeys' => $keys,
|
||||
'messages' => [],
|
||||
],
|
||||
],
|
||||
'Test with invalid format (string)' => [
|
||||
[ TimestampDef::PARAM_TIMESTAMP_FORMAT => 'foobar' ],
|
||||
self::STDRET,
|
||||
[
|
||||
'issues' => [
|
||||
'X',
|
||||
TimestampDef::PARAM_TIMESTAMP_FORMAT => 'Value for PARAM_TIMESTAMP_FORMAT is not valid',
|
||||
],
|
||||
'allowedKeys' => $keys,
|
||||
'messages' => [],
|
||||
],
|
||||
],
|
||||
'Test with invalid format (int)' => [
|
||||
[ TimestampDef::PARAM_TIMESTAMP_FORMAT => 1000 ],
|
||||
self::STDRET,
|
||||
[
|
||||
'issues' => [
|
||||
'X',
|
||||
TimestampDef::PARAM_TIMESTAMP_FORMAT => 'Value for PARAM_TIMESTAMP_FORMAT is not valid',
|
||||
],
|
||||
'allowedKeys' => $keys,
|
||||
'messages' => [],
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function provideStringifyValue() {
|
||||
$specific = new ConvertibleTimestamp( '20180203040506' );
|
||||
|
||||
|
|
|
|||
|
|
@ -15,6 +15,9 @@ use Wikimedia\ParamValidator\ValidationException;
|
|||
*/
|
||||
abstract class TypeDefTestCase extends \PHPUnit\Framework\TestCase {
|
||||
|
||||
/** Standard "$ret" array for provideCheckSettings */
|
||||
protected const STDRET = [ 'issues' => [ 'X' ], 'allowedKeys' => [ 'Y' ], 'messages' => [] ];
|
||||
|
||||
/** @var string|null TypeDef class name being tested */
|
||||
protected static $testClass = null;
|
||||
|
||||
|
|
@ -116,6 +119,32 @@ abstract class TypeDefTestCase extends \PHPUnit\Framework\TestCase {
|
|||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideCheckSettings
|
||||
* @param array $settings
|
||||
* @param array $ret Input $ret array
|
||||
* @param array $expect
|
||||
* @param array $options Options array
|
||||
*/
|
||||
public function testCheckSettings(
|
||||
array $settings,
|
||||
array $ret,
|
||||
array $expect,
|
||||
array $options = []
|
||||
) : void {
|
||||
$typeDef = $this->getInstance( new SimpleCallbacks( [] ), $options );
|
||||
$this->assertEquals( $expect, $typeDef->checkSettings( 'test', $settings, $options, $ret ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array|Iterable
|
||||
*/
|
||||
public function provideCheckSettings() {
|
||||
return [
|
||||
'Basic test' => [ [], self::STDRET, self::STDRET ],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideGetEnumValues
|
||||
* @param array $settings
|
||||
|
|
|
|||
|
|
@ -173,6 +173,76 @@ class UploadDefTest extends TypeDefTestCase {
|
|||
$typeDef->validate( 'test', $value, [], [] );
|
||||
}
|
||||
|
||||
public function provideCheckSettings() {
|
||||
return [
|
||||
'Basic test' => [
|
||||
[],
|
||||
self::STDRET,
|
||||
self::STDRET,
|
||||
],
|
||||
'PARAM_ISMULTI not allowed' => [
|
||||
[
|
||||
ParamValidator::PARAM_ISMULTI => true,
|
||||
],
|
||||
self::STDRET,
|
||||
[
|
||||
'issues' => [
|
||||
'X',
|
||||
ParamValidator::PARAM_ISMULTI
|
||||
=> 'PARAM_ISMULTI cannot be used for upload-type parameters',
|
||||
],
|
||||
'allowedKeys' => [ 'Y' ],
|
||||
'messages' => [],
|
||||
],
|
||||
],
|
||||
'PARAM_ISMULTI not allowed, but another ISMULTI issue was already logged' => [
|
||||
[
|
||||
ParamValidator::PARAM_ISMULTI => true,
|
||||
],
|
||||
[
|
||||
'issues' => [
|
||||
ParamValidator::PARAM_ISMULTI => 'XXX',
|
||||
],
|
||||
'allowedKeys' => [ 'Y' ],
|
||||
'messages' => [],
|
||||
],
|
||||
[
|
||||
'issues' => [
|
||||
ParamValidator::PARAM_ISMULTI => 'XXX',
|
||||
],
|
||||
'allowedKeys' => [ 'Y' ],
|
||||
'messages' => [],
|
||||
],
|
||||
],
|
||||
'PARAM_DEFAULT can be null' => [
|
||||
[ ParamValidator::PARAM_DEFAULT => null ],
|
||||
self::STDRET,
|
||||
self::STDRET,
|
||||
],
|
||||
'PARAM_DEFAULT is otherwise not allowed' => [
|
||||
[
|
||||
ParamValidator::PARAM_DEFAULT => true,
|
||||
],
|
||||
[
|
||||
'issues' => [
|
||||
'X',
|
||||
ParamValidator::PARAM_DEFAULT => 'XXX',
|
||||
],
|
||||
'allowedKeys' => [ 'Y' ],
|
||||
'messages' => [],
|
||||
],
|
||||
[
|
||||
'issues' => [
|
||||
'X',
|
||||
ParamValidator::PARAM_DEFAULT => 'Cannot specify a default for upload-type parameters',
|
||||
],
|
||||
'allowedKeys' => [ 'Y' ],
|
||||
'messages' => [],
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function provideStringifyValue() {
|
||||
return [
|
||||
'Yeah, right' => [ $this->makeUpload(), null ],
|
||||
|
|
|
|||
|
|
@ -16,6 +16,8 @@ class TypeDefTest extends \PHPUnit\Framework\TestCase {
|
|||
->getMockForAbstractClass();
|
||||
|
||||
$this->assertSame( [ 'foobar' ], $typeDef->normalizeSettings( [ 'foobar' ] ) );
|
||||
$ret = [ 'issues' => [], 'allowedKeys' => [], 'messages' => [] ];
|
||||
$this->assertSame( $ret, $typeDef->checkSettings( 'foobar', [], [], $ret ) );
|
||||
$this->assertNull( $typeDef->getEnumValues( 'foobar', [], [] ) );
|
||||
$this->assertSame( '123', $typeDef->stringifyValue( 'foobar', 123, [], [] ) );
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
<?php
|
||||
|
||||
use MediaWiki\MediaWikiServices;
|
||||
use Wikimedia\TestingAccessWrapper;
|
||||
|
||||
/**
|
||||
|
|
@ -27,82 +26,6 @@ class ApiStructureTest extends MediaWikiTestCase {
|
|||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* Values are an array, where each array value is a permitted type. A type
|
||||
* can be a string, which is the name of an internal type or a
|
||||
* class/interface. Or it can be an array, in which case the value must be
|
||||
* an array whose elements are the types given in the array (e.g., [
|
||||
* 'string', integer' ] means an array whose entries are strings and/or
|
||||
* integers).
|
||||
*/
|
||||
private static $paramTypes = [
|
||||
// ApiBase::PARAM_DFLT => as appropriate for PARAM_TYPE
|
||||
ApiBase::PARAM_ISMULTI => [ 'boolean' ],
|
||||
ApiBase::PARAM_TYPE => [ 'string', [ 'string' ] ],
|
||||
ApiBase::PARAM_MAX => [ 'integer' ],
|
||||
ApiBase::PARAM_MAX2 => [ 'integer' ],
|
||||
ApiBase::PARAM_MIN => [ 'integer' ],
|
||||
ApiBase::PARAM_ALLOW_DUPLICATES => [ 'boolean' ],
|
||||
ApiBase::PARAM_DEPRECATED => [ 'boolean' ],
|
||||
ApiBase::PARAM_REQUIRED => [ 'boolean' ],
|
||||
ApiBase::PARAM_RANGE_ENFORCE => [ 'boolean' ],
|
||||
ApiBase::PARAM_HELP_MSG => [ 'string', 'array', Message::class ],
|
||||
ApiBase::PARAM_HELP_MSG_APPEND => [ [ 'string', 'array', Message::class ] ],
|
||||
ApiBase::PARAM_HELP_MSG_INFO => [ [ 'array' ] ],
|
||||
ApiBase::PARAM_VALUE_LINKS => [ [ 'string' ] ],
|
||||
ApiBase::PARAM_HELP_MSG_PER_VALUE => [ [ 'string', 'array', Message::class ] ],
|
||||
ApiBase::PARAM_SUBMODULE_MAP => [ [ 'string' ] ],
|
||||
ApiBase::PARAM_SUBMODULE_PARAM_PREFIX => [ 'string' ],
|
||||
ApiBase::PARAM_ALL => [ 'boolean', 'string' ],
|
||||
ApiBase::PARAM_EXTRA_NAMESPACES => [ [ 'integer' ] ],
|
||||
ApiBase::PARAM_SENSITIVE => [ 'boolean' ],
|
||||
ApiBase::PARAM_DEPRECATED_VALUES => [ 'array' ],
|
||||
ApiBase::PARAM_ISMULTI_LIMIT1 => [ 'integer' ],
|
||||
ApiBase::PARAM_ISMULTI_LIMIT2 => [ 'integer' ],
|
||||
ApiBase::PARAM_MAX_BYTES => [ 'integer' ],
|
||||
ApiBase::PARAM_MAX_CHARS => [ 'integer' ],
|
||||
ApiBase::PARAM_TEMPLATE_VARS => [ 'array' ],
|
||||
];
|
||||
|
||||
// param => [ other param that must be present => required value or null ]
|
||||
private static $paramRequirements = [
|
||||
ApiBase::PARAM_ALLOW_DUPLICATES => [ ApiBase::PARAM_ISMULTI => true ],
|
||||
ApiBase::PARAM_ALL => [ ApiBase::PARAM_ISMULTI => true ],
|
||||
ApiBase::PARAM_ISMULTI_LIMIT1 => [
|
||||
ApiBase::PARAM_ISMULTI => true,
|
||||
ApiBase::PARAM_ISMULTI_LIMIT2 => null,
|
||||
],
|
||||
ApiBase::PARAM_ISMULTI_LIMIT2 => [
|
||||
ApiBase::PARAM_ISMULTI => true,
|
||||
ApiBase::PARAM_ISMULTI_LIMIT1 => null,
|
||||
],
|
||||
];
|
||||
|
||||
// param => type(s) allowed for this param ('array' is any array)
|
||||
private static $paramAllowedTypes = [
|
||||
ApiBase::PARAM_MAX => [ 'integer', 'limit' ],
|
||||
ApiBase::PARAM_MAX2 => 'limit',
|
||||
ApiBase::PARAM_MIN => [ 'integer', 'limit' ],
|
||||
ApiBase::PARAM_RANGE_ENFORCE => 'integer',
|
||||
ApiBase::PARAM_VALUE_LINKS => 'array',
|
||||
ApiBase::PARAM_HELP_MSG_PER_VALUE => 'array',
|
||||
ApiBase::PARAM_SUBMODULE_MAP => 'submodule',
|
||||
ApiBase::PARAM_SUBMODULE_PARAM_PREFIX => 'submodule',
|
||||
ApiBase::PARAM_ALL => 'array',
|
||||
ApiBase::PARAM_EXTRA_NAMESPACES => 'namespace',
|
||||
ApiBase::PARAM_DEPRECATED_VALUES => 'array',
|
||||
ApiBase::PARAM_MAX_BYTES => [ 'NULL', 'string', 'text', 'password' ],
|
||||
ApiBase::PARAM_MAX_CHARS => [ 'NULL', 'string', 'text', 'password' ],
|
||||
];
|
||||
|
||||
private static $paramProhibitedTypes = [
|
||||
ApiBase::PARAM_ISMULTI => [ 'boolean', 'limit', 'upload' ],
|
||||
ApiBase::PARAM_ALL => 'namespace',
|
||||
ApiBase::PARAM_SENSITIVE => 'password',
|
||||
];
|
||||
|
||||
private static $constantNames = null;
|
||||
|
||||
/**
|
||||
* Initialize/fetch the ApiMain instance for testing
|
||||
* @return ApiMain
|
||||
|
|
@ -158,62 +81,6 @@ class ApiStructureTest extends MediaWikiTestCase {
|
|||
$this->checkMessage( $module->getSummaryMessage(), 'Module summary' );
|
||||
$this->checkMessage( $module->getExtendedDescription(), 'Module help top text' );
|
||||
|
||||
// Parameters. Lots of messages in here.
|
||||
$params = $module->getFinalParams( ApiBase::GET_VALUES_FOR_HELP );
|
||||
$tags = [];
|
||||
foreach ( $params as $name => $settings ) {
|
||||
if ( !is_array( $settings ) ) {
|
||||
$settings = [];
|
||||
}
|
||||
|
||||
// Basic description message
|
||||
if ( isset( $settings[ApiBase::PARAM_HELP_MSG] ) ) {
|
||||
$msg = $settings[ApiBase::PARAM_HELP_MSG];
|
||||
} else {
|
||||
$msg = "apihelp-{$path}-param-{$name}";
|
||||
}
|
||||
$this->checkMessage( $msg, "Parameter $name description" );
|
||||
|
||||
// If param-per-value is in use, each value's message
|
||||
if ( isset( $settings[ApiBase::PARAM_HELP_MSG_PER_VALUE] ) ) {
|
||||
$this->assertIsArray( $settings[ApiBase::PARAM_HELP_MSG_PER_VALUE],
|
||||
"Parameter $name PARAM_HELP_MSG_PER_VALUE is array" );
|
||||
$this->assertIsArray( $settings[ApiBase::PARAM_TYPE],
|
||||
"Parameter $name PARAM_TYPE is array for msg-per-value mode" );
|
||||
$valueMsgs = $settings[ApiBase::PARAM_HELP_MSG_PER_VALUE];
|
||||
foreach ( $settings[ApiBase::PARAM_TYPE] as $value ) {
|
||||
if ( isset( $valueMsgs[$value] ) ) {
|
||||
$msg = $valueMsgs[$value];
|
||||
} else {
|
||||
$msg = "apihelp-{$path}-paramvalue-{$name}-{$value}";
|
||||
}
|
||||
$this->checkMessage( $msg, "Parameter $name value $value" );
|
||||
}
|
||||
}
|
||||
|
||||
// Appended messages (e.g. "disabled in miser mode")
|
||||
if ( isset( $settings[ApiBase::PARAM_HELP_MSG_APPEND] ) ) {
|
||||
$this->assertIsArray( $settings[ApiBase::PARAM_HELP_MSG_APPEND],
|
||||
"Parameter $name PARAM_HELP_MSG_APPEND is array" );
|
||||
foreach ( $settings[ApiBase::PARAM_HELP_MSG_APPEND] as $i => $msg ) {
|
||||
$this->checkMessage( $msg, "Parameter $name HELP_MSG_APPEND #$i" );
|
||||
}
|
||||
}
|
||||
|
||||
// Info tags (e.g. "only usable in mode 1") are typically shared by
|
||||
// several parameters, so accumulate them and test them later.
|
||||
if ( !empty( $settings[ApiBase::PARAM_HELP_MSG_INFO] ) ) {
|
||||
foreach ( $settings[ApiBase::PARAM_HELP_MSG_INFO] as $i ) {
|
||||
$tags[array_shift( $i )] = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Info tags (e.g. "only usable in mode 1") accumulated above
|
||||
foreach ( $tags as $tag => $dummy ) {
|
||||
$this->checkMessage( "apihelp-{$path}-paraminfo-{$tag}", "HELP_MSG_INFO tag $tag" );
|
||||
}
|
||||
|
||||
// Messages for examples.
|
||||
foreach ( $module->getExamplesMessages() as $qs => $msg ) {
|
||||
$this->assertStringStartsNotWith( 'api.php?', $qs,
|
||||
|
|
@ -242,399 +109,72 @@ class ApiStructureTest extends MediaWikiTestCase {
|
|||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideParameterConsistency
|
||||
* @dataProvider provideParameters
|
||||
* @param string $path
|
||||
* @param array $params
|
||||
* @param string $name
|
||||
*/
|
||||
public function testParameterConsistency( $path ) {
|
||||
public function testParameters( string $path, array $params, string $name ) : void {
|
||||
$main = self::getMain();
|
||||
$module = TestingAccessWrapper::newFromObject( $main->getModuleFromPath( $path ) );
|
||||
|
||||
$paramsPlain = $module->getFinalParams();
|
||||
$paramsForHelp = $module->getFinalParams( ApiBase::GET_VALUES_FOR_HELP );
|
||||
$dataName = $this->dataName();
|
||||
$this->assertNotSame( '', $name, "$dataName: Name cannot be empty" );
|
||||
$this->assertArrayHasKey( $name, $params, "$dataName: Sanity check" );
|
||||
|
||||
// avoid warnings about empty tests when no parameter needs to be checked
|
||||
$this->assertTrue( true );
|
||||
$ret = $main->getParamValidator()->checkSettings(
|
||||
$main->getModuleFromPath( $path ), $params, $name, []
|
||||
);
|
||||
|
||||
if ( self::$constantNames === null ) {
|
||||
self::$constantNames = [];
|
||||
|
||||
foreach ( ( new ReflectionClass( 'ApiBase' ) )->getConstants() as $key => $val ) {
|
||||
if ( substr( $key, 0, 6 ) === 'PARAM_' ) {
|
||||
self::$constantNames[$val] = $key;
|
||||
}
|
||||
// Warn about unknown keys. Don't fail, they might be for forward- or back-compat.
|
||||
if ( is_array( $params[$name] ) ) {
|
||||
$keys = array_diff(
|
||||
array_keys( $params[$name] ),
|
||||
$ret['allowedKeys']
|
||||
);
|
||||
if ( $keys ) {
|
||||
// Don't fail for this, for back-compat
|
||||
$this->addWarning(
|
||||
"$dataName: Unrecognized settings keys were used: " . implode( ', ', $keys )
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ( [ $paramsPlain, $paramsForHelp ] as $params ) {
|
||||
foreach ( $params as $param => $config ) {
|
||||
if ( !is_array( $config ) ) {
|
||||
$config = [ ApiBase::PARAM_DFLT => $config ];
|
||||
}
|
||||
if ( !isset( $config[ApiBase::PARAM_TYPE] ) ) {
|
||||
$config[ApiBase::PARAM_TYPE] = isset( $config[ApiBase::PARAM_DFLT] )
|
||||
? gettype( $config[ApiBase::PARAM_DFLT] )
|
||||
: 'NULL';
|
||||
}
|
||||
if ( count( $ret['issues'] ) === 1 ) {
|
||||
$this->fail( "$dataName: Validation failed: " . reset( $ret['issues'] ) );
|
||||
} elseif ( $ret['issues'] ) {
|
||||
$this->fail( "$dataName: Validation failed:\n* " . implode( "\n* ", $ret['issues'] ) );
|
||||
}
|
||||
|
||||
foreach ( self::$paramTypes as $key => $types ) {
|
||||
if ( !isset( $config[$key] ) ) {
|
||||
continue;
|
||||
}
|
||||
$keyName = self::$constantNames[$key];
|
||||
$this->validateType( $types, $config[$key], $param, $keyName );
|
||||
}
|
||||
|
||||
foreach ( self::$paramRequirements as $key => $required ) {
|
||||
if ( !isset( $config[$key] ) ) {
|
||||
continue;
|
||||
}
|
||||
foreach ( $required as $requireKey => $requireVal ) {
|
||||
$this->assertArrayHasKey( $requireKey, $config,
|
||||
"$param: When " . self::$constantNames[$key] . " is set, " .
|
||||
self::$constantNames[$requireKey] . " must also be set" );
|
||||
if ( $requireVal !== null ) {
|
||||
$this->assertSame( $requireVal, $config[$requireKey],
|
||||
"$param: When " . self::$constantNames[$key] . " is set, " .
|
||||
self::$constantNames[$requireKey] . " must equal " .
|
||||
var_export( $requireVal, true ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ( self::$paramAllowedTypes as $key => $allowedTypes ) {
|
||||
if ( !isset( $config[$key] ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$actualType = is_array( $config[ApiBase::PARAM_TYPE] )
|
||||
? 'array' : $config[ApiBase::PARAM_TYPE];
|
||||
|
||||
$this->assertContains(
|
||||
$actualType,
|
||||
(array)$allowedTypes,
|
||||
"$param: " . self::$constantNames[$key] .
|
||||
" can only be used with PARAM_TYPE " .
|
||||
implode( ', ', (array)$allowedTypes )
|
||||
);
|
||||
}
|
||||
|
||||
foreach ( self::$paramProhibitedTypes as $key => $prohibitedTypes ) {
|
||||
if ( !isset( $config[$key] ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$actualType = is_array( $config[ApiBase::PARAM_TYPE] )
|
||||
? 'array' : $config[ApiBase::PARAM_TYPE];
|
||||
|
||||
$this->assertNotContains(
|
||||
$actualType,
|
||||
(array)$prohibitedTypes,
|
||||
"$param: " . self::$constantNames[$key] .
|
||||
" cannot be used with PARAM_TYPE " .
|
||||
implode( ', ', (array)$prohibitedTypes )
|
||||
);
|
||||
}
|
||||
|
||||
if ( isset( $config[ApiBase::PARAM_DFLT] ) ) {
|
||||
$this->assertFalse(
|
||||
isset( $config[ApiBase::PARAM_REQUIRED] ) &&
|
||||
$config[ApiBase::PARAM_REQUIRED],
|
||||
"$param: A required parameter cannot have a default" );
|
||||
|
||||
$this->validateDefault( $param, $config );
|
||||
}
|
||||
|
||||
if ( $config[ApiBase::PARAM_TYPE] === 'limit' ) {
|
||||
$this->assertTrue(
|
||||
isset( $config[ApiBase::PARAM_MAX] ) &&
|
||||
isset( $config[ApiBase::PARAM_MAX2] ),
|
||||
"$param: PARAM_MAX and PARAM_MAX2 are required for limits"
|
||||
);
|
||||
$this->assertGreaterThanOrEqual(
|
||||
$config[ApiBase::PARAM_MAX],
|
||||
$config[ApiBase::PARAM_MAX2],
|
||||
"$param: PARAM_MAX cannot be greater than PARAM_MAX2"
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
isset( $config[ApiBase::PARAM_MIN] ) &&
|
||||
isset( $config[ApiBase::PARAM_MAX] )
|
||||
) {
|
||||
$this->assertGreaterThanOrEqual(
|
||||
$config[ApiBase::PARAM_MIN],
|
||||
$config[ApiBase::PARAM_MAX],
|
||||
"$param: PARAM_MIN cannot be greater than PARAM_MAX"
|
||||
);
|
||||
}
|
||||
|
||||
if ( isset( $config[ApiBase::PARAM_RANGE_ENFORCE] ) ) {
|
||||
$this->assertTrue(
|
||||
isset( $config[ApiBase::PARAM_MIN] ) ||
|
||||
isset( $config[ApiBase::PARAM_MAX] ),
|
||||
"$param: PARAM_RANGE_ENFORCE can only be set together with " .
|
||||
"PARAM_MIN or PARAM_MAX"
|
||||
);
|
||||
}
|
||||
|
||||
if ( isset( $config[ApiBase::PARAM_DEPRECATED_VALUES] ) ) {
|
||||
foreach ( $config[ApiBase::PARAM_DEPRECATED_VALUES] as $key => $unused ) {
|
||||
$this->assertContains( $key, $config[ApiBase::PARAM_TYPE],
|
||||
"$param: Deprecated value \"$key\" is not allowed, " .
|
||||
"how can it be deprecated?" );
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
isset( $config[ApiBase::PARAM_ISMULTI_LIMIT1] ) ||
|
||||
isset( $config[ApiBase::PARAM_ISMULTI_LIMIT2] )
|
||||
) {
|
||||
$this->assertGreaterThanOrEqual( 0, $config[ApiBase::PARAM_ISMULTI_LIMIT1],
|
||||
"$param: PARAM_ISMULTI_LIMIT1 cannot be negative" );
|
||||
// Zero for both doesn't make sense, but you could have
|
||||
// zero for non-bots
|
||||
$this->assertGreaterThanOrEqual( 1, $config[ApiBase::PARAM_ISMULTI_LIMIT2],
|
||||
"$param: PARAM_ISMULTI_LIMIT2 cannot be negative or zero" );
|
||||
$this->assertGreaterThanOrEqual(
|
||||
$config[ApiBase::PARAM_ISMULTI_LIMIT1],
|
||||
$config[ApiBase::PARAM_ISMULTI_LIMIT2],
|
||||
"$param: PARAM_ISMULTI limit cannot be smaller for users with " .
|
||||
"apihighlimits rights" );
|
||||
}
|
||||
|
||||
if ( isset( $config[ApiBase::PARAM_MAX_BYTES] ) ) {
|
||||
$this->assertGreaterThanOrEqual( 1, $config[ApiBase::PARAM_MAX_BYTES],
|
||||
"$param: PARAM_MAX_BYTES cannot be negative or zero" );
|
||||
}
|
||||
|
||||
if ( isset( $config[ApiBase::PARAM_MAX_CHARS] ) ) {
|
||||
$this->assertGreaterThanOrEqual( 1, $config[ApiBase::PARAM_MAX_CHARS],
|
||||
"$param: PARAM_MAX_CHARS cannot be negative or zero" );
|
||||
}
|
||||
|
||||
if (
|
||||
isset( $config[ApiBase::PARAM_MAX_BYTES] ) &&
|
||||
isset( $config[ApiBase::PARAM_MAX_CHARS] )
|
||||
) {
|
||||
// Length of a string in chars is always <= length in bytes,
|
||||
// so PARAM_MAX_CHARS is pointless if > PARAM_MAX_BYTES
|
||||
$this->assertGreaterThanOrEqual(
|
||||
$config[ApiBase::PARAM_MAX_CHARS],
|
||||
$config[ApiBase::PARAM_MAX_BYTES],
|
||||
"$param: PARAM_MAX_BYTES cannot be less than PARAM_MAX_CHARS"
|
||||
);
|
||||
}
|
||||
|
||||
if ( isset( $config[ApiBase::PARAM_TEMPLATE_VARS] ) ) {
|
||||
$this->assertNotSame( [], $config[ApiBase::PARAM_TEMPLATE_VARS],
|
||||
"$param: PARAM_TEMPLATE_VARS cannot be empty" );
|
||||
foreach ( $config[ApiBase::PARAM_TEMPLATE_VARS] as $key => $target ) {
|
||||
$this->assertRegExp( '/^[^{}]+$/', $key,
|
||||
"$param: PARAM_TEMPLATE_VARS key may not contain '{' or '}'" );
|
||||
|
||||
$this->assertStringContainsString( '{' . $key . '}', $param,
|
||||
"$param: Name must contain PARAM_TEMPLATE_VARS key {" . $key . "}" );
|
||||
$this->assertArrayHasKey( $target, $params,
|
||||
"$param: PARAM_TEMPLATE_VARS target parameter '$target' does not exist" );
|
||||
$config2 = $params[$target];
|
||||
$this->assertTrue( !empty( $config2[ApiBase::PARAM_ISMULTI] ),
|
||||
"$param: PARAM_TEMPLATE_VARS target parameter '$target' must have PARAM_ISMULTI = true" );
|
||||
|
||||
if ( isset( $config2[ApiBase::PARAM_TEMPLATE_VARS] ) ) {
|
||||
$this->assertNotSame( $param, $target,
|
||||
"$param: PARAM_TEMPLATE_VARS cannot target itself" );
|
||||
|
||||
$this->assertArraySubset(
|
||||
$config2[ApiBase::PARAM_TEMPLATE_VARS],
|
||||
$config[ApiBase::PARAM_TEMPLATE_VARS],
|
||||
true,
|
||||
"$param: PARAM_TEMPLATE_VARS target parameter '$target': "
|
||||
. "the target's PARAM_TEMPLATE_VARS must be a subset of the original."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$keys = implode( '|',
|
||||
array_map(
|
||||
function ( $key ) {
|
||||
return preg_quote( $key, '/' );
|
||||
},
|
||||
array_keys( $config[ApiBase::PARAM_TEMPLATE_VARS] )
|
||||
)
|
||||
);
|
||||
$this->assertRegExp( '/^(?>[^{}]+|\{(?:' . $keys . ')\})+$/', $param,
|
||||
"$param: Name may not contain '{' or '}' other than as defined by PARAM_TEMPLATE_VARS" );
|
||||
} else {
|
||||
$this->assertRegExp( '/^[^{}]+$/', $param,
|
||||
"$param: Name may not contain '{' or '}' without PARAM_TEMPLATE_VARS" );
|
||||
}
|
||||
// Check message existence
|
||||
$done = [];
|
||||
foreach ( $ret['messages'] as $msg ) {
|
||||
// We don't really care about the parameters, so do it simply
|
||||
$key = $msg->getKey();
|
||||
if ( !isset( $done[$key] ) ) {
|
||||
$done[$key] = true;
|
||||
$this->checkMessage( $key, "$dataName: Parameter" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Throws if $value does not match one of the types specified in $types.
|
||||
*
|
||||
* @param array $types From self::$paramTypes array
|
||||
* @param mixed $value Value to check
|
||||
* @param string $param Name of param we're checking, for error messages
|
||||
* @param string $desc Description for error messages
|
||||
*/
|
||||
private function validateType( $types, $value, $param, $desc ) {
|
||||
if ( count( $types ) === 1 ) {
|
||||
// Only one type allowed
|
||||
if ( is_string( $types[0] ) ) {
|
||||
$this->assertSame( $types[0], gettype( $value ), "$param: $desc type" );
|
||||
} else {
|
||||
// Array whose values have specified types, recurse
|
||||
$this->assertIsArray( $value, "$param: $desc type" );
|
||||
foreach ( $value as $subvalue ) {
|
||||
$this->validateType( $types[0], $subvalue, $param, "$desc value" );
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Multiple options
|
||||
foreach ( $types as $type ) {
|
||||
if ( is_string( $type ) ) {
|
||||
if ( class_exists( $type ) || interface_exists( $type ) ) {
|
||||
if ( $value instanceof $type ) {
|
||||
return;
|
||||
}
|
||||
} elseif ( gettype( $value ) === $type ) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// Array whose values have specified types, recurse
|
||||
try {
|
||||
$this->validateType( [ $type ], $value, $param, "$desc type" );
|
||||
// Didn't throw, so we're good
|
||||
return;
|
||||
} catch ( Exception $unused ) {
|
||||
}
|
||||
}
|
||||
}
|
||||
// Doesn't match any of them
|
||||
$this->fail( "$param: $desc has incorrect type" );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that $default is a valid default for $type.
|
||||
*
|
||||
* @param string $param Name of param, for error messages
|
||||
* @param array $config Array of configuration options for this parameter
|
||||
*/
|
||||
private function validateDefault( $param, $config ) {
|
||||
$type = $config[ApiBase::PARAM_TYPE];
|
||||
$default = $config[ApiBase::PARAM_DFLT];
|
||||
|
||||
if ( !empty( $config[ApiBase::PARAM_ISMULTI] ) ) {
|
||||
if ( $default === '' ) {
|
||||
// The empty array is fine
|
||||
return;
|
||||
}
|
||||
$defaults = explode( '|', $default );
|
||||
$config[ApiBase::PARAM_ISMULTI] = false;
|
||||
foreach ( $defaults as $defaultValue ) {
|
||||
// Only allow integers in their simplest form with no leading
|
||||
// or trailing characters etc.
|
||||
if ( $type === 'integer' && $defaultValue === (string)(int)$defaultValue ) {
|
||||
$defaultValue = (int)$defaultValue;
|
||||
}
|
||||
$config[ApiBase::PARAM_DFLT] = $defaultValue;
|
||||
$this->validateDefault( $param, $config );
|
||||
}
|
||||
return;
|
||||
}
|
||||
switch ( $type ) {
|
||||
case 'boolean':
|
||||
$this->assertFalse( $default,
|
||||
"$param: Boolean params may only default to false" );
|
||||
break;
|
||||
|
||||
case 'integer':
|
||||
$this->assertIsInt( $default,
|
||||
"$param: Default $default is not an integer" );
|
||||
break;
|
||||
|
||||
case 'limit':
|
||||
if ( $default === 'max' ) {
|
||||
break;
|
||||
}
|
||||
$this->assertIsInt( $default,
|
||||
"$param: Default $default is neither an integer nor \"max\"" );
|
||||
break;
|
||||
|
||||
case 'namespace':
|
||||
$validValues = MediaWikiServices::getInstance()->getNamespaceInfo()->
|
||||
getValidNamespaces();
|
||||
if (
|
||||
isset( $config[ApiBase::PARAM_EXTRA_NAMESPACES] ) &&
|
||||
is_array( $config[ApiBase::PARAM_EXTRA_NAMESPACES] )
|
||||
) {
|
||||
$validValues = array_merge(
|
||||
$validValues,
|
||||
$config[ApiBase::PARAM_EXTRA_NAMESPACES]
|
||||
);
|
||||
}
|
||||
$this->assertContains( $default, $validValues,
|
||||
"$param: Default $default is not a valid namespace" );
|
||||
break;
|
||||
|
||||
case 'NULL':
|
||||
case 'password':
|
||||
case 'string':
|
||||
case 'submodule':
|
||||
case 'tags':
|
||||
case 'text':
|
||||
$this->assertIsString( $default,
|
||||
"$param: Default $default is not a string" );
|
||||
break;
|
||||
|
||||
case 'timestamp':
|
||||
if ( $default === 'now' ) {
|
||||
return;
|
||||
}
|
||||
$this->assertNotFalse( wfTimestamp( TS_MW, $default ),
|
||||
"$param: Default $default is not a valid timestamp" );
|
||||
break;
|
||||
|
||||
case 'user':
|
||||
// @todo Should we make user validation a public static method
|
||||
// in ApiBase() or something so we don't have to resort to
|
||||
// this? Or in User for that matter.
|
||||
$wrapper = TestingAccessWrapper::newFromObject( new ApiMain() );
|
||||
try {
|
||||
$wrapper->validateUser( $default, '' );
|
||||
} catch ( ApiUsageException $e ) {
|
||||
$this->fail( "$param: Default $default is not a valid username/IP address" );
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
if ( is_array( $type ) ) {
|
||||
$this->assertContains( $default, $type,
|
||||
"$param: Default $default is not any of " .
|
||||
implode( ', ', $type ) );
|
||||
} else {
|
||||
$this->fail( "Unrecognized type $type" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array List of API module paths to test
|
||||
*/
|
||||
public static function provideParameterConsistency() {
|
||||
public static function provideParameters() : Iterator {
|
||||
$main = self::getMain();
|
||||
$paths = self::getSubModulePaths( $main->getModuleManager() );
|
||||
array_unshift( $paths, $main->getModulePath() );
|
||||
$argsets = [
|
||||
'plain' => [],
|
||||
'for help' => [ ApiBase::GET_VALUES_FOR_HELP ],
|
||||
];
|
||||
|
||||
$ret = [];
|
||||
foreach ( $paths as $path ) {
|
||||
$ret[] = [ $path ];
|
||||
$module = $main->getModuleFromPath( $path );
|
||||
foreach ( $argsets as $argset => $args ) {
|
||||
$params = $module->getFinalParams( ...$args );
|
||||
foreach ( $params as $param => $dummy ) {
|
||||
yield "Module $path, $argset, parameter $param" => [ $path, $params, $param ];
|
||||
}
|
||||
}
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
Loading…
Reference in a new issue