wiki.techinc.nl/tests/phpunit/integration/includes/Rest/Handler/ModuleSpecHandlerTest.php
Umherirrender 97e55f4795 tests: Remove global state from Rest Handler tests
Functions provided by the HandlerTestTrait and used in other rest tests

Reduce session leaking from global state within the rest test

Bug: T376970
Change-Id: Ie0dc52441c6a6ca408812c5d0cc6d791834780e5
2024-10-11 23:18:22 +02:00

253 lines
6.1 KiB
PHP

<?php
namespace MediaWiki\Tests\Rest\Handler;
use JsonSchemaAssertionTrait;
use MediaWiki\Config\ServiceOptions;
use MediaWiki\MainConfigNames;
use MediaWiki\Rest\BasicAccess\StaticBasicAuthorizer;
use MediaWiki\Rest\Handler;
use MediaWiki\Rest\Handler\ModuleSpecHandler;
use MediaWiki\Rest\Reporter\MWErrorReporter;
use MediaWiki\Rest\RequestData;
use MediaWiki\Rest\RequestInterface;
use MediaWiki\Rest\ResponseFactory;
use MediaWiki\Rest\Router;
use MediaWiki\Rest\Validator\Validator;
use MediaWikiIntegrationTestCase;
use PHPUnit\Framework\Assert;
use PHPUnit\Framework\Constraint\Constraint;
use Wikimedia\Message\ITextFormatter;
use Wikimedia\Message\MessageValue;
use Wikimedia\ParamValidator\ParamValidator;
/**
* @covers \MediaWiki\Rest\Handler\ModuleSpecHandler
*
* @group Database
*/
class ModuleSpecHandlerTest extends MediaWikiIntegrationTestCase {
use HandlerTestTrait;
use JsonSchemaAssertionTrait;
private function createRouter(
RequestInterface $request,
$specFile
): Router {
$services = $this->getServiceContainer();
$conf = $services->getMainConfig();
$authority = $this->mockRegisteredUltimateAuthority();
$authorizer = new StaticBasicAuthorizer();
$objectFactory = $services->getObjectFactory();
$restValidator = new Validator( $objectFactory,
$request,
$authority
);
$formatter = new class implements ITextFormatter {
public function getLangCode() {
return 'qqx';
}
public function format( MessageValue $message ) {
return $message->dump();
}
};
$responseFactory = new ResponseFactory( [ $formatter ] );
return ( new Router(
[ $specFile ],
[],
new ServiceOptions( Router::CONSTRUCTOR_OPTIONS, $conf ),
$services->getLocalServerObjectCache(),
$responseFactory,
$authorizer,
$authority,
$objectFactory,
$restValidator,
new MWErrorReporter(),
$services->getHookContainer(),
$this->getSession( true )
) );
}
private function newHandler() {
$config = $this->getServiceContainer()->getMainConfig();
return new ModuleSpecHandler(
$config
);
}
private function assertWellFormedOAS( array $spec ) {
$this->assertMatchesJsonSchema(
__DIR__ . '/data/OpenApi-3.0.json',
$spec
);
}
private static function assertContainsRecursive(
array $expected,
array $actual,
string $message = ''
) {
foreach ( $expected as $key => $value ) {
Assert::assertArrayHasKey( $key, $actual, $message );
if ( is_array( $value ) ) {
Assert::assertIsArray( $actual[$key], $message );
self::assertContainsRecursive( $value, $actual[$key], $message );
} elseif ( $value instanceof Constraint ) {
$value->evaluate( $actual[$key], $message );
} else {
Assert::assertSame( $value, $actual[$key], $message );
}
}
}
public static function provideGetInfoSpecSuccess() {
yield 'module and version' => [
__DIR__ . '/SpecTestModule.json',
[
'pathParams' => [ 'module' => 'mock', 'version' => 'v1' ]
],
[
'info' => [
'title' => 'mock/v1 Module',
'version' => '1.3-test',
'contact' => [
'email' => 'test@example.com'
],
],
'servers' => [
[ 'url' => 'https://example.com:1234/api/mock/v1' ]
],
'paths' => [
'/foo/bar' => [
'get' => [
'parameters' => [ [ 'name' => 'q', 'in' => 'query' ] ],
'responses' => [ 200 => [ 'description' => 'OK' ] ]
],
'post' => [
'requestBody' => [
'required' => true,
'content' => [
'application/json' => [
'schema' => [
'type' => 'object',
'required' => [ 'b' ],
'properties' => [
'b' => [ 'type' => 'string' ]
],
]
]
]
],
'responses' => [ 200 => [ 'description' => 'OK' ] ]
],
]
],
'components' => [
'schemas' => [
'boolean-param' => [ 'type' => 'boolean' ],
],
'responses' => [
'GenericErrorResponse' => self::anything(),
],
]
]
];
yield 'prefix-less module' => [
__DIR__ . '/SpecTestFlatRoutes.json',
[
'pathParams' => [ 'module' => '-' ]
],
[
'info' => [
'title' => 'Extra Routes',
'version' => 'undefined',
'license' => [
'name' => 'Test License',
'url' => 'https://example.com/license',
],
],
'servers' => [
[ 'url' => 'https://example.com:1234/api' ]
],
'paths' => [
'/mock/v1/foo/bar' => [
'get' => [ 'responses' => [ 200 => [ 'description' => 'OK' ] ] ],
]
],
]
];
}
/**
* @dataProvider provideGetInfoSpecSuccess
*/
public function testGetInfoSpecSuccess( $specFile, $params, $expected ) {
$this->overrideConfigValues( [
MainConfigNames::RightsText => 'Test License',
MainConfigNames::RightsUrl => 'https://example.com/license',
MainConfigNames::EmergencyContact => 'test@example.com',
MainConfigNames::CanonicalServer => 'https://example.com:1234',
MainConfigNames::RestPath => '/api',
] );
$request = new RequestData( $params );
$router = $this->createRouter( $request, $specFile );
$handler = $this->newHandler();
$response = $this->executeHandler(
$handler,
$request,
[],
[],
[],
[],
null,
null,
$router
);
$this->assertSame( 200, $response->getStatusCode() );
$this->assertArrayHasKey( 'Content-Type', $response->getHeaders() );
$this->assertSame( 'application/json', $response->getHeaderLine( 'Content-Type' ) );
$data = json_decode( (string)$response->getBody(), true );
$this->assertIsArray( $data, 'Body must be a JSON array' );
$this->assertWellFormedOAS( $data );
$this->assertContainsRecursive( $expected, $data );
}
public static function newFooBarHandler() {
return new class extends Handler {
public function getParamSettings() {
return [
'q' => [
Handler::PARAM_SOURCE => 'query',
ParamValidator::PARAM_REQUIRED => 'false',
],
];
}
public function getBodyParamSettings(): array {
return [
'b' => [
Handler::PARAM_SOURCE => 'body',
ParamValidator::PARAM_REQUIRED => 'true',
],
];
}
public function execute() {
return 'foo bar';
}
};
}
}