1<?php 2 3namespace MediaWiki\Api\Validator; 4 5use ApiMain; 6use ApiModuleManager; 7use MockApi; 8use Wikimedia\Message\DataMessageValue; 9use Wikimedia\ParamValidator\ParamValidator; 10use Wikimedia\ParamValidator\SimpleCallbacks; 11use Wikimedia\ParamValidator\TypeDef\EnumDef; 12use Wikimedia\ParamValidator\TypeDef\TypeDefTestCase; 13use Wikimedia\ParamValidator\ValidationException; 14use Wikimedia\TestingAccessWrapper; 15 16/** 17 * @covers MediaWiki\Api\Validator\SubmoduleDef 18 */ 19class SubmoduleDefTest extends TypeDefTestCase { 20 21 protected function getInstance( SimpleCallbacks $callbacks, array $options ) { 22 return new SubmoduleDef( $callbacks, $options ); 23 } 24 25 private function mockApi() { 26 $api = $this->getMockBuilder( MockApi::class ) 27 ->setMethods( [ 'getModuleManager' ] ) 28 ->getMock(); 29 $w = TestingAccessWrapper::newFromObject( $api ); 30 $w->mModuleName = 'testmod'; 31 $w->mMainModule = new ApiMain; 32 $w->mModulePrefix = 'tt'; 33 34 $w->mMainModule->getModuleManager()->addModule( 'testmod', 'action', [ 35 'class' => MockApi::class, 36 'factory' => static function () use ( $api ) { 37 return $api; 38 }, 39 ] ); 40 41 $dep = $this->getMockBuilder( MockApi::class ) 42 ->setMethods( [ 'isDeprecated' ] ) 43 ->getMock(); 44 $dep->method( 'isDeprecated' )->willReturn( true ); 45 $int = $this->getMockBuilder( MockApi::class ) 46 ->setMethods( [ 'isInternal' ] ) 47 ->getMock(); 48 $int->method( 'isInternal' )->willReturn( true ); 49 $depint = $this->getMockBuilder( MockApi::class ) 50 ->setMethods( [ 'isDeprecated', 'isInternal' ] ) 51 ->getMock(); 52 $depint->method( 'isDeprecated' )->willReturn( true ); 53 $depint->method( 'isInternal' )->willReturn( true ); 54 55 $manager = new ApiModuleManager( $api ); 56 $api->method( 'getModuleManager' )->willReturn( $manager ); 57 $manager->addModule( 'mod1', 'test', MockApi::class ); 58 $manager->addModule( 'mod2', 'test', MockApi::class ); 59 $manager->addModule( 'dep', 'test', [ 60 'class' => MockApi::class, 61 'factory' => static function () use ( $dep ) { 62 return $dep; 63 }, 64 ] ); 65 $manager->addModule( 'depint', 'test', [ 66 'class' => MockApi::class, 67 'factory' => static function () use ( $depint ) { 68 return $depint; 69 }, 70 ] ); 71 $manager->addModule( 'int', 'test', [ 72 'class' => MockApi::class, 73 'factory' => static function () use ( $int ) { 74 return $int; 75 }, 76 ] ); 77 $manager->addModule( 'recurse', 'test', [ 78 'class' => MockApi::class, 79 'factory' => static function () use ( $api ) { 80 return $api; 81 }, 82 ] ); 83 $manager->addModule( 'mod3', 'xyz', MockApi::class ); 84 85 $this->assertSame( $api, $api->getModuleFromPath( 'testmod' ), 'sanity check' ); 86 $this->assertSame( $dep, $api->getModuleFromPath( 'testmod+dep' ), 'sanity check' ); 87 $this->assertSame( $int, $api->getModuleFromPath( 'testmod+int' ), 'sanity check' ); 88 $this->assertSame( $depint, $api->getModuleFromPath( 'testmod+depint' ), 'sanity check' ); 89 90 return $api; 91 } 92 93 public function provideValidate() { 94 $opts = [ 95 'module' => $this->mockApi(), 96 ]; 97 $map = [ 98 SubmoduleDef::PARAM_SUBMODULE_MAP => [ 99 'mod2' => 'testmod+mod1', 100 'mod3' => 'testmod+mod3', 101 ], 102 ]; 103 104 return [ 105 'Basic' => [ 'mod1', 'mod1', [], $opts ], 106 'Nonexistent submodule' => [ 107 'mod3', 108 new ValidationException( 109 DataMessageValue::new( 'paramvalidator-badvalue', [], 'badvalue', [] ), 'test', 'mod3', [] 110 ), 111 [], 112 $opts, 113 ], 114 'Mapped' => [ 'mod3', 'mod3', $map, $opts ], 115 'Mapped, not in map' => [ 116 'mod1', 117 new ValidationException( 118 DataMessageValue::new( 'paramvalidator-badvalue', [], 'badvalue', [] ), 'test', 'mod1', $map 119 ), 120 $map, 121 $opts, 122 ], 123 ]; 124 } 125 126 public function provideCheckSettings() { 127 $opts = [ 128 'module' => $this->mockApi(), 129 ]; 130 $keys = [ 131 'Y', EnumDef::PARAM_DEPRECATED_VALUES, 132 SubmoduleDef::PARAM_SUBMODULE_MAP, SubmoduleDef::PARAM_SUBMODULE_PARAM_PREFIX 133 ]; 134 135 return [ 136 'Basic test' => [ 137 [], 138 self::STDRET, 139 [ 140 'issues' => [ 'X' ], 141 'allowedKeys' => $keys, 142 'messages' => [], 143 ], 144 $opts 145 ], 146 'Test with everything' => [ 147 [ 148 SubmoduleDef::PARAM_SUBMODULE_MAP => [ 149 'foo' => 'testmod+mod1', 'bar' => 'testmod+mod2' 150 ], 151 SubmoduleDef::PARAM_SUBMODULE_PARAM_PREFIX => 'g', 152 ], 153 self::STDRET, 154 [ 155 'issues' => [ 'X' ], 156 'allowedKeys' => $keys, 157 'messages' => [], 158 ], 159 $opts 160 ], 161 'Bad types' => [ 162 [ 163 SubmoduleDef::PARAM_SUBMODULE_MAP => false, 164 SubmoduleDef::PARAM_SUBMODULE_PARAM_PREFIX => true, 165 ], 166 self::STDRET, 167 [ 168 'issues' => [ 169 'X', 170 SubmoduleDef::PARAM_SUBMODULE_MAP => 'PARAM_SUBMODULE_MAP must be an array, got boolean', 171 SubmoduleDef::PARAM_SUBMODULE_PARAM_PREFIX 172 => 'PARAM_SUBMODULE_PARAM_PREFIX must be a string, got boolean', 173 ], 174 'allowedKeys' => $keys, 175 'messages' => [], 176 ], 177 $opts 178 ], 179 'Bad values in map' => [ 180 [ 181 SubmoduleDef::PARAM_SUBMODULE_MAP => [ 182 'a' => 'testmod+mod1', 183 'b' => false, 184 'c' => null, 185 'd' => 'testmod+mod7', 186 'r' => 'testmod+recurse+recurse', 187 ], 188 ], 189 self::STDRET, 190 [ 191 'issues' => [ 192 'X', 193 'Values for PARAM_SUBMODULE_MAP must be strings, but value for "b" is boolean', 194 'Values for PARAM_SUBMODULE_MAP must be strings, but value for "c" is NULL', 195 'PARAM_SUBMODULE_MAP contains "testmod+mod7", which is not a valid module path', 196 ], 197 'allowedKeys' => $keys, 198 'messages' => [], 199 ], 200 $opts 201 ], 202 ]; 203 } 204 205 public function provideGetEnumValues() { 206 $opts = [ 207 'module' => $this->mockApi(), 208 ]; 209 210 return [ 211 'Basic test' => [ 212 [ ParamValidator::PARAM_TYPE => 'submodule' ], 213 [ 'mod1', 'mod2', 'dep', 'depint', 'int', 'recurse' ], 214 $opts, 215 ], 216 'Mapped' => [ 217 [ 218 ParamValidator::PARAM_TYPE => 'submodule', 219 SubmoduleDef::PARAM_SUBMODULE_MAP => [ 220 'mod2' => 'test+mod1', 221 'mod3' => 'test+mod3', 222 ] 223 ], 224 [ 'mod2', 'mod3' ], 225 $opts, 226 ], 227 ]; 228 } 229 230 public function provideGetInfo() { 231 $opts = [ 232 'module' => $this->mockApi(), 233 ]; 234 235 return [ 236 'Basic' => [ 237 [], 238 [ 239 'type' => [ 'mod1', 'mod2', 'recurse', 'dep', 'int', 'depint' ], 240 'submodules' => [ 241 'mod1' => 'testmod+mod1', 242 'mod2' => 'testmod+mod2', 243 'recurse' => 'testmod+recurse', 244 'dep' => 'testmod+dep', 245 'int' => 'testmod+int', 246 'depint' => 'testmod+depint', 247 ], 248 'deprecatedvalues' => [ 'dep', 'depint' ], 249 'internalvalues' => [ 'depint', 'int' ], 250 ], 251 [ 252 // phpcs:ignore Generic.Files.LineLength.TooLong 253 ParamValidator::PARAM_TYPE => '<message key="paramvalidator-help-type-enum"><text>1</text><list listType="comma"><text>[[Special:ApiHelp/testmod+mod1|<span dir="ltr" lang="en">mod1</span>]]</text><text>[[Special:ApiHelp/testmod+mod2|<span dir="ltr" lang="en">mod2</span>]]</text><text>[[Special:ApiHelp/testmod+recurse|<span dir="ltr" lang="en">recurse</span>]]</text><text>[[Special:ApiHelp/testmod+dep|<span dir="ltr" lang="en" class="apihelp-deprecated-value">dep</span>]]</text><text>[[Special:ApiHelp/testmod+int|<span dir="ltr" lang="en" class="apihelp-internal-value">int</span>]]</text><text>[[Special:ApiHelp/testmod+depint|<span dir="ltr" lang="en" class="apihelp-deprecated-value apihelp-internal-value">depint</span>]]</text></list><num>6</num></message>', 254 ParamValidator::PARAM_ISMULTI => null, 255 ], 256 $opts, 257 ], 258 'Mapped' => [ 259 [ 260 ParamValidator::PARAM_DEFAULT => 'mod3|mod4', 261 ParamValidator::PARAM_ISMULTI => true, 262 SubmoduleDef::PARAM_SUBMODULE_PARAM_PREFIX => 'g', 263 SubmoduleDef::PARAM_SUBMODULE_MAP => [ 264 'xyz' => 'testmod+dep', 265 'mod3' => 'testmod+mod3', 266 'mod4' => 'testmod+mod4', // doesn't exist 267 ], 268 ], 269 [ 270 'type' => [ 'mod3', 'mod4', 'xyz' ], 271 'submodules' => [ 272 'mod3' => 'testmod+mod3', 273 'mod4' => 'testmod+mod4', 274 'xyz' => 'testmod+dep', 275 ], 276 'submoduleparamprefix' => 'g', 277 'deprecatedvalues' => [ 'xyz' ], 278 ], 279 [ 280 // phpcs:ignore Generic.Files.LineLength.TooLong 281 ParamValidator::PARAM_TYPE => '<message key="paramvalidator-help-type-enum"><text>2</text><list listType="comma"><text>[[Special:ApiHelp/testmod+mod3|<span dir="ltr" lang="en">mod3</span>]]</text><text>[[Special:ApiHelp/testmod+mod4|<span dir="ltr" lang="en">mod4</span>]]</text><text>[[Special:ApiHelp/testmod+dep|<span dir="ltr" lang="en" class="apihelp-deprecated-value">xyz</span>]]</text></list><num>3</num></message>', 282 ParamValidator::PARAM_ISMULTI => null, 283 ], 284 $opts, 285 ], 286 ]; 287 } 288 289} 290