3 use MediaWiki\Context\DerivativeContext
;
4 use MediaWiki\Context\RequestContext
;
5 use MediaWiki\LinkedData\PageDataRequestHandler
;
6 use MediaWiki\Output\OutputPage
;
7 use MediaWiki\Request\FauxRequest
;
8 use MediaWiki\Request\FauxResponse
;
9 use MediaWiki\Title\Title
;
10 use MediaWiki\Title\TitleFactory
;
13 * @covers \MediaWiki\LinkedData\PageDataRequestHandler
16 class PageDataRequestHandlerTest
extends \MediaWikiLangTestCase
{
21 private $interfaceTitle;
28 protected function setUp(): void
{
31 $this->interfaceTitle
= Title
::newFromText( __CLASS__
);
32 // Force the content model to avoid DB queries.
33 $this->interfaceTitle
->setContentModel( CONTENT_MODEL_WIKITEXT
);
34 $this->obLevel
= ob_get_level();
37 protected function assertPostConditions(): void
{
38 $obLevel = ob_get_level();
39 if ( $obLevel !== $this->obLevel
) {
40 $this->fail( "Test changed output buffer level: was {$this->obLevel}" .
41 "before test, but $obLevel after test."
44 parent
::assertPostConditions();
47 protected function tearDown(): void
{
48 while ( ob_get_level() > $this->obLevel
) {
55 * @return PageDataRequestHandler
57 protected function newHandler() {
58 return new PageDataRequestHandler();
62 * @param array $params
63 * @param string[] $headers
67 protected function makeOutputPage( array $params, array $headers ) {
69 $request = new FauxRequest( $params );
70 $request->response()->header( 'Status: 200 OK', true, 200 ); // init/reset
72 foreach ( $headers as $name => $value ) {
73 $request->setHeader( strtoupper( $name ), $value );
76 // construct Context and OutputPage
77 $context = new DerivativeContext( RequestContext
::getMain() );
78 $context->setRequest( $request );
80 $output = new OutputPage( $context );
81 $output->setTitle( $this->interfaceTitle
);
82 $context->setOutput( $output );
87 public static function handleRequestProvider() {
90 $cases[] = [ '', [], [], 'Invalid title', 400 ];
94 [ 'target' => 'Helsinki' ],
98 [ 'Location' => '?title=Helsinki&action=raw' ]
102 foreach ( $cases as $c ) {
106 if ( isset( $case[1]['target'] ) ) {
107 $case[0] .= $case[1]['target'];
108 unset( $case[1]['target'] );
111 $subpageCases[] = $case;
114 $cases = array_merge( $cases, $subpageCases );
118 [ 'target' => 'Helsinki' ],
119 [ 'Accept' => 'text/HTML' ],
122 [ 'Location' => '/wiki/Helsinki' ]
128 'target' => 'Helsinki',
129 'revision' => '4242',
131 [ 'Accept' => 'text/HTML' ],
134 [ 'Location' => '?title=Helsinki&oldid=4242' ]
143 [ 'Location' => '?title=Helsinki&action=raw' ]
146 // #31: /Q5 with "Accept: text/foobar" triggers a 406
150 [ 'Accept' => 'text/foobar' ],
151 'No matching format found',
158 [ 'Accept' => 'text/HTML' ],
166 [ 'Accept' => 'text/HTML' ],
174 [ 'Accept' => 'text/HTML' ],
182 [ 'Accept' => 'text/HTML' ],
185 [ 'Location' => '/wiki/Helsinki' ]
191 [ 'Accept' => 'text/HTML' ],
194 [ 'Location' => '/wiki/Helsinki' ]
200 [ 'Accept' => 'text/HTML' ],
203 [ 'Location' => '/wiki/AC/DC' ]
210 * @dataProvider handleRequestProvider
212 * @param string $subpage The subpage to request (or '')
213 * @param array $params Request parameters
214 * @param array $headers Request headers
215 * @param string $expectedOutput
216 * @param int $expectedStatusCode Expected HTTP status code.
217 * @param string[] $expectedHeaders Expected HTTP response headers.
219 public function testHandleRequest(
223 $expectedOutput = '',
224 $expectedStatusCode = 200,
225 array $expectedHeaders = []
227 $titleFactory = $this->createMock( TitleFactory
::class );
228 $titleFactory->method( 'newFromTextThrow' )->willReturnCallback( static function ( $text, $ns ) {
229 // Force the content model to avoid DB queries.
230 $ret = Title
::newFromTextThrow( $text, $ns );
231 $ret->setContentModel( CONTENT_MODEL_WIKITEXT
);
234 $this->setService( 'TitleFactory', $titleFactory );
235 $output = $this->makeOutputPage( $params, $headers );
236 $request = $output->getRequest();
238 /** @var FauxResponse $response */
239 $response = $request->response();
242 $handler = $this->newHandler();
246 $handler->handleRequest( $subpage, $request, $output );
248 if ( $output->getRedirect() !== '' ) {
249 // hack to apply redirect to web response
253 $text = ob_get_clean();
255 $this->assertEquals( $expectedStatusCode, $response->getStatusCode(), 'status code' );
256 $this->assertSame( $expectedOutput, $text, 'output' );
258 foreach ( $expectedHeaders as $name => $exp ) {
259 $value = $response->getHeader( $name );
260 $this->assertNotNull( $value, "header: $name" );
261 $this->assertIsString( $value, "header: $name" );
262 $this->assertStringEndsWith( $exp, $value, "header: $name" );
264 } catch ( HttpError
$e ) {
266 $this->assertEquals( $expectedStatusCode, $e->getStatusCode(), 'status code' );
267 $this->assertStringContainsString( $expectedOutput, $e->getHTML(), 'error output' );
270 // We always set "Access-Control-Allow-Origin: *"
271 $this->assertSame( '*', $response->getHeader( 'Access-Control-Allow-Origin' ) );
274 public static function provideHttpContentNegotiation() {
275 $helsinki = Title
::makeTitle( NS_MAIN
, 'Helsinki' );
276 // Force the content model to avoid DB queries.
277 $helsinki->setContentModel( CONTENT_MODEL_WIKITEXT
);
279 'Accept Header of HTML' => [
281 [ 'ACCEPT' => 'text/html' ], // headers
284 'Accept Header without weights' => [
286 [ 'ACCEPT' => '*/*, text/html, text/x-wiki' ],
287 'Helsinki&action=raw'
289 'Accept Header with weights' => [
291 [ 'ACCEPT' => 'text/*; q=0.5, text/json; q=0.7, application/rdf+xml; q=0.8' ],
292 'Helsinki&action=raw'
294 'Accept Header accepting evertyhing and HTML' => [
296 [ 'ACCEPT' => 'text/html, */*' ],
297 'Helsinki&action=raw'
299 'No Accept Header' => [
302 'Helsinki&action=raw'
308 * @dataProvider provideHttpContentNegotiation
310 * @param Title $title
311 * @param array $headers Request headers
312 * @param string $expectedRedirectSuffix Expected suffix of the HTTP Location header.
314 public function testHttpContentNegotiation(
317 $expectedRedirectSuffix
319 /** @var FauxResponse $response */
320 $output = $this->makeOutputPage( [], $headers );
321 $request = $output->getRequest();
323 $handler = $this->newHandler();
324 $handler->httpContentNegotiation( $request, $output, $title );
326 $this->assertStringEndsWith(
327 $expectedRedirectSuffix,
328 $output->getRedirect(),