2015-11-26 19:31:09 +00:00
|
|
|
<?php
|
|
|
|
|
2021-04-07 22:22:05 +00:00
|
|
|
use MediaWiki\Extension\Math\MathRestbaseInterface;
|
2021-08-11 13:38:51 +00:00
|
|
|
use MediaWiki\Extension\Math\Tests\MathMockHttpTrait;
|
2021-04-07 22:22:05 +00:00
|
|
|
|
2015-11-26 19:31:09 +00:00
|
|
|
/**
|
|
|
|
* Test the interface to access Restbase paths
|
|
|
|
* /media/math/check/{type}
|
|
|
|
* /media/math/render/{format}/{hash}
|
|
|
|
*
|
2021-04-07 22:22:05 +00:00
|
|
|
* @covers \MediaWiki\Extension\Math\MathRestbaseInterface
|
2016-02-12 16:57:37 +00:00
|
|
|
*
|
2015-11-26 19:31:09 +00:00
|
|
|
* @group Math
|
2016-02-12 16:57:37 +00:00
|
|
|
*
|
2018-04-13 14:06:39 +00:00
|
|
|
* @license GPL-2.0-or-later
|
2015-11-26 19:31:09 +00:00
|
|
|
*/
|
2021-10-11 22:51:10 +00:00
|
|
|
class MathRestbaseInterfaceTest extends MediaWikiIntegrationTestCase {
|
2021-08-11 13:38:51 +00:00
|
|
|
use MathMockHttpTrait;
|
2015-11-26 19:31:09 +00:00
|
|
|
|
|
|
|
public function testSuccess() {
|
2021-08-11 13:38:51 +00:00
|
|
|
$this->setupGoodMathRestBaseMockHttp();
|
|
|
|
|
2015-11-26 19:31:09 +00:00
|
|
|
$input = '\\sin x^2';
|
|
|
|
$rbi = new MathRestbaseInterface( $input );
|
|
|
|
$this->assertTrue( $rbi->getSuccess(), "Assuming that $input is valid input." );
|
|
|
|
$this->assertEquals( '\\sin x^{2}', $rbi->getCheckedTex() );
|
2020-04-05 11:38:45 +00:00
|
|
|
$this->assertStringContainsString( '<mi>sin</mi>', $rbi->getMathML() );
|
2021-08-11 13:38:51 +00:00
|
|
|
$this->assertStringContainsString( '/svg/RESOURCE_LOCATION', $rbi->getFullSvgUrl() );
|
|
|
|
$this->assertStringContainsString( '/png/RESOURCE_LOCATION', $rbi->getFullPngUrl() );
|
2015-11-26 19:31:09 +00:00
|
|
|
}
|
|
|
|
|
2023-05-09 20:58:07 +00:00
|
|
|
public function testBatchEvaluate() {
|
|
|
|
$body = [
|
|
|
|
'success' => true,
|
|
|
|
'checked' => 'CHECKED',
|
|
|
|
'identifiers' => []
|
|
|
|
];
|
|
|
|
|
|
|
|
$response = [
|
|
|
|
'code' => 200,
|
|
|
|
'headers' => [],
|
|
|
|
'body' => json_encode( $body )
|
|
|
|
];
|
|
|
|
|
|
|
|
$responses = [
|
|
|
|
[ // for https://wikimedia.org/api/rest_v1/media/math/check/tex with input1
|
|
|
|
'headers' => [ 'x-resource-location' => 'deadbeef1' ],
|
|
|
|
'body' => json_encode( [ 'checked' => 'CHECKED1' ] + $body )
|
|
|
|
] + $response,
|
|
|
|
|
|
|
|
[ // for https://wikimedia.org/api/rest_v1/media/math/check/tex with input2
|
|
|
|
'headers' => [ 'x-resource-location' => 'deadbeef2' ],
|
|
|
|
'body' => json_encode( [ 'checked' => 'CHECKED2' ] + $body )
|
|
|
|
] + $response,
|
|
|
|
|
|
|
|
[ // for https://wikimedia.org/api/rest_v1/media/math/render/mml/deadbeef1
|
|
|
|
'body' => 'MML1'
|
|
|
|
] + $response,
|
|
|
|
|
|
|
|
[ // for https://wikimedia.org/api/rest_v1/media/math/render/mml/deadbeef2
|
|
|
|
'body' => 'MML2'
|
|
|
|
] + $response,
|
|
|
|
];
|
|
|
|
|
|
|
|
$httpClient = $this->createNoOpMock( MultiHttpClient::class, [ 'runMulti' ] );
|
|
|
|
$httpClient->method( 'runMulti' )->willReturnCallback(
|
|
|
|
static function ( array $requests ) use ( &$responses ) {
|
|
|
|
foreach ( $requests as &$req ) {
|
|
|
|
$resp = array_shift( $responses );
|
|
|
|
$req['response'] = $resp;
|
|
|
|
}
|
|
|
|
return $requests;
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
$this->installMockHttp( $httpClient );
|
|
|
|
|
|
|
|
// NOTE: Using fake response, the input is ignored.
|
|
|
|
$rbi1 = new MathRestbaseInterface( 'input1' );
|
|
|
|
$rbi2 = new MathRestbaseInterface( 'input2' );
|
|
|
|
|
|
|
|
MathRestbaseInterface::batchEvaluate( [ $rbi1, $rbi2 ] );
|
|
|
|
|
|
|
|
$this->assertTrue( $rbi1->getSuccess() );
|
|
|
|
$this->assertEquals( 'CHECKED1', $rbi1->getCheckedTex() );
|
|
|
|
$this->assertEquals( 'MML1', $rbi1->getMathML() );
|
|
|
|
|
|
|
|
$this->assertTrue( $rbi2->getSuccess() );
|
|
|
|
$this->assertEquals( 'CHECKED2', $rbi2->getCheckedTex() );
|
|
|
|
$this->assertEquals( 'MML2', $rbi2->getMathML() );
|
|
|
|
}
|
|
|
|
|
2015-11-26 19:31:09 +00:00
|
|
|
public function testFail() {
|
2021-08-11 13:38:51 +00:00
|
|
|
$this->setupBadMathRestBaseMockHttp();
|
|
|
|
|
2015-11-26 19:31:09 +00:00
|
|
|
$input = '\\sin\\newcommand';
|
|
|
|
$rbi = new MathRestbaseInterface( $input );
|
|
|
|
$this->assertFalse( $rbi->getSuccess(), "Assuming that $input is invalid input." );
|
2020-01-14 07:43:50 +00:00
|
|
|
$this->assertNull( $rbi->getCheckedTex() );
|
2015-11-26 19:31:09 +00:00
|
|
|
$this->assertEquals( 'Illegal TeX function', $rbi->getError()->error->message );
|
|
|
|
}
|
|
|
|
|
2016-01-29 13:05:54 +00:00
|
|
|
public function testChem() {
|
2021-08-11 13:38:51 +00:00
|
|
|
$this->setupGoodChemRestBaseMockHttp();
|
|
|
|
|
2016-01-29 13:05:54 +00:00
|
|
|
$input = '\ce{H2O}';
|
|
|
|
$rbi = new MathRestbaseInterface( $input, 'chem' );
|
|
|
|
$this->assertTrue( $rbi->checkTeX(), "Assuming that $input is valid input." );
|
|
|
|
$this->assertTrue( $rbi->getSuccess(), "Assuming that $input is valid input." );
|
|
|
|
$this->assertEquals( '{\ce {H2O}}', $rbi->getCheckedTex() );
|
2020-04-05 11:38:45 +00:00
|
|
|
$this->assertStringContainsString( '<msubsup>', $rbi->getMathML() );
|
|
|
|
$this->assertStringContainsString( '<mtext>H</mtext>', $rbi->getMathML() );
|
2016-01-29 13:05:54 +00:00
|
|
|
}
|
2019-07-14 13:30:32 +00:00
|
|
|
|
2015-11-26 19:31:09 +00:00
|
|
|
public function testException() {
|
2021-08-11 13:38:51 +00:00
|
|
|
$this->setupBadMathRestBaseMockHttp();
|
|
|
|
|
|
|
|
$input = '\\sin\\newcommand';
|
2015-11-26 19:31:09 +00:00
|
|
|
$rbi = new MathRestbaseInterface( $input );
|
2019-10-16 02:28:43 +00:00
|
|
|
$this->expectException( MWException::class );
|
|
|
|
$this->expectExceptionMessage( 'TeX input is invalid.' );
|
2015-11-26 19:31:09 +00:00
|
|
|
$rbi->getMathML();
|
|
|
|
}
|
|
|
|
|
|
|
|
public function testExceptionSvg() {
|
2021-08-11 13:38:51 +00:00
|
|
|
$this->setupBadMathRestBaseMockHttp();
|
|
|
|
|
|
|
|
$input = '\\sin\\newcommand';
|
2015-11-26 19:31:09 +00:00
|
|
|
$rbi = new MathRestbaseInterface( $input );
|
2019-10-16 02:28:43 +00:00
|
|
|
$this->expectException( MWException::class );
|
|
|
|
$this->expectExceptionMessage( 'TeX input is invalid.' );
|
2015-11-26 19:31:09 +00:00
|
|
|
$rbi->getFullSvgUrl();
|
|
|
|
}
|
|
|
|
|
2016-08-17 17:41:04 +00:00
|
|
|
/**
|
|
|
|
* Incorporate the "details" in the error message, if the check requests passes, but the
|
|
|
|
* mml/svg/complete endpoints returns an error
|
|
|
|
*/
|
|
|
|
public function testLateError() {
|
2018-05-04 02:00:55 +00:00
|
|
|
// phpcs:ignore Generic.Files.LineLength.TooLong
|
2016-08-17 17:41:04 +00:00
|
|
|
$input = '{"type":"https://mediawiki.org/wiki/HyperSwitch/errors/bad_request","title":"Bad Request","method":"POST","detail":["TeX parse error: Missing close brace"],"uri":"/complete"}';
|
2019-10-16 02:28:43 +00:00
|
|
|
$this->expectException( MWException::class );
|
|
|
|
$this->expectExceptionMessage( 'Cannot get mml. TeX parse error: Missing close brace' );
|
2016-08-17 17:41:04 +00:00
|
|
|
MathRestbaseInterface::throwContentError( 'mml', $input );
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Incorporate the "details" in the error message, if the check requests passes, but the
|
|
|
|
* mml/svg/complete endpoints returns an error
|
|
|
|
*/
|
|
|
|
public function testLateErrorString() {
|
2018-05-04 02:00:55 +00:00
|
|
|
// phpcs:ignore Generic.Files.LineLength.TooLong
|
2016-08-17 17:41:04 +00:00
|
|
|
$input = '{"type":"https://mediawiki.org/wiki/HyperSwitch/errors/bad_request","title":"Bad Request","method":"POST","detail": "TeX parse error: Missing close brace","uri":"/complete"}';
|
2019-10-16 02:28:43 +00:00
|
|
|
$this->expectException( MWException::class );
|
|
|
|
$this->expectExceptionMessage( 'Cannot get mml. TeX parse error: Missing close brace' );
|
2016-08-17 17:41:04 +00:00
|
|
|
MathRestbaseInterface::throwContentError( 'mml', $input );
|
|
|
|
}
|
|
|
|
|
|
|
|
public function testLateErrorNoDetail() {
|
|
|
|
$input = '';
|
2019-10-16 02:28:43 +00:00
|
|
|
$this->expectException( MWException::class );
|
|
|
|
$this->expectExceptionMessage( 'Cannot get mml. Server problem.' );
|
2016-08-17 17:41:04 +00:00
|
|
|
MathRestbaseInterface::throwContentError( 'mml', $input );
|
|
|
|
}
|
2023-05-04 21:24:34 +00:00
|
|
|
|
|
|
|
public function dataProviderForTestUrlUsedByCheckTeX() {
|
|
|
|
$path = 'media/math/check/tex';
|
|
|
|
|
|
|
|
yield 'Math FullRestbaseURL default case' => [
|
|
|
|
[],
|
|
|
|
[
|
|
|
|
'url' => "https://wikimedia.org/api/rest_v1/$path",
|
|
|
|
'method' => 'POST',
|
|
|
|
'body' => [ 'type' => 'tex', 'q' => '\sin\newcommand' ]
|
|
|
|
],
|
|
|
|
];
|
|
|
|
|
|
|
|
yield 'Math FullRestbaseURL case' => [
|
|
|
|
[
|
|
|
|
'MathFullRestbaseURL' => 'https://myWiki.test/'
|
|
|
|
],
|
|
|
|
[
|
|
|
|
'url' => "https://myWiki.test/v1/$path",
|
|
|
|
'method' => 'POST',
|
|
|
|
'body' => [ 'type' => 'tex', 'q' => '\sin\newcommand' ]
|
|
|
|
],
|
|
|
|
];
|
|
|
|
|
|
|
|
yield 'Internal Restbase URL case' => [
|
|
|
|
[
|
|
|
|
'MathUseInternalRestbasePath' => true,
|
|
|
|
'VirtualRestConfig' => [
|
|
|
|
'modules' => [
|
|
|
|
'restbase' => [ 'url' => 'http://restbase.test.internal/api/' ]
|
|
|
|
]
|
|
|
|
],
|
|
|
|
'MathFullRestbaseURL' => 'https://myWiki.test/'
|
|
|
|
],
|
|
|
|
[
|
|
|
|
'url' => "http://restbase.test.internal/api/localhost/v1/$path",
|
|
|
|
'method' => 'POST',
|
|
|
|
'body' => [ 'type' => 'tex', 'q' => '\sin\newcommand' ]
|
|
|
|
],
|
|
|
|
];
|
|
|
|
|
|
|
|
yield 'VisualEditor case' => [
|
|
|
|
[
|
|
|
|
'MathFullRestbaseURL' => null,
|
|
|
|
'VisualEditorFullRestbaseURL' => "https://VisualEditor/api/rest_v1/"
|
|
|
|
],
|
|
|
|
[
|
|
|
|
'url' => "https://VisualEditor/api/rest_v1/v1/$path",
|
|
|
|
'method' => 'POST',
|
|
|
|
'body' => [ 'type' => 'tex', 'q' => '\sin\newcommand' ]
|
|
|
|
],
|
|
|
|
];
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @dataProvider dataProviderForTestUrlUsedByCheckTeX
|
|
|
|
*/
|
|
|
|
public function testUrlUsedByCheckTeX( array $config, array $expected ) {
|
|
|
|
$response = [
|
|
|
|
'headers' => [
|
|
|
|
'x-resource-location' => 'deadbeef'
|
|
|
|
],
|
|
|
|
'body' => json_encode( [
|
|
|
|
'success' => true,
|
|
|
|
'checked' => 'who cares',
|
|
|
|
'identifiers' => [],
|
|
|
|
] )
|
|
|
|
];
|
|
|
|
|
|
|
|
$this->expectMathRestBaseMockHttpRequest( [ $expected ], [ $response ] );
|
|
|
|
|
|
|
|
$this->overrideConfigValues( $config );
|
|
|
|
|
|
|
|
$input = '\\sin\\newcommand';
|
|
|
|
$rbi = new MathRestbaseInterface( $input );
|
|
|
|
|
|
|
|
$rbi->checkTeX();
|
|
|
|
}
|
|
|
|
|
|
|
|
public function dataProviderForTestUrlUsedByGetML() {
|
|
|
|
$path1 = 'media/math/check/tex';
|
|
|
|
$path2 = 'media/math/render/mml/deadbeef';
|
|
|
|
|
|
|
|
yield 'Math FullRestbaseURL default case' => [
|
|
|
|
[],
|
|
|
|
[
|
|
|
|
[ 'url' => "https://wikimedia.org/api/rest_v1/$path1", 'method' => 'POST' ],
|
|
|
|
[ 'url' => "https://wikimedia.org/api/rest_v1/$path2", 'method' => 'GET' ],
|
|
|
|
],
|
|
|
|
];
|
|
|
|
|
|
|
|
yield 'Math FullRestbaseURL case' => [
|
|
|
|
[
|
|
|
|
'MathFullRestbaseURL' => 'https://myWiki.test/'
|
|
|
|
],
|
|
|
|
[
|
|
|
|
[
|
|
|
|
'url' => "https://myWiki.test/v1/$path1",
|
|
|
|
'method' => 'POST'
|
|
|
|
],
|
|
|
|
[
|
|
|
|
'url' => "https://myWiki.test/v1/$path2",
|
|
|
|
'method' => 'GET'
|
|
|
|
],
|
|
|
|
],
|
|
|
|
];
|
|
|
|
|
|
|
|
yield 'Internal Restbase URL case' => [
|
|
|
|
[
|
|
|
|
'MathUseInternalRestbasePath' => true,
|
|
|
|
'VirtualRestConfig' => [
|
|
|
|
'modules' => [
|
|
|
|
'restbase' => [ 'url' => 'http://restbase.test.internal/api/' ]
|
|
|
|
]
|
|
|
|
],
|
|
|
|
],
|
|
|
|
[
|
|
|
|
[
|
|
|
|
'url' => "http://restbase.test.internal/api/localhost/v1/$path1",
|
|
|
|
'method' => 'POST'
|
|
|
|
],
|
|
|
|
[
|
|
|
|
'url' => "http://restbase.test.internal/api/localhost/v1/$path2",
|
|
|
|
'method' => 'GET'
|
|
|
|
],
|
|
|
|
],
|
|
|
|
];
|
|
|
|
|
|
|
|
yield 'VisualEditor case' => [
|
|
|
|
[
|
|
|
|
'MathFullRestbaseURL' => null,
|
|
|
|
'VisualEditorFullRestbaseURL' => 'https://visual-editor.org/api/rest_v1/'
|
|
|
|
],
|
|
|
|
[
|
|
|
|
[
|
|
|
|
'url' => "https://visual-editor.org/api/rest_v1/v1/$path1",
|
|
|
|
'method' => 'POST'
|
|
|
|
],
|
|
|
|
[
|
|
|
|
'url' => "https://visual-editor.org/api/rest_v1/v1/$path2",
|
|
|
|
'method' => 'GET'
|
|
|
|
],
|
|
|
|
],
|
|
|
|
];
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @dataProvider dataProviderForTestUrlUsedByGetML
|
|
|
|
*/
|
|
|
|
public function testUrlUsedByGetML( array $config, array $expectedList ) {
|
|
|
|
$response1 = [
|
|
|
|
'headers' => [
|
|
|
|
'x-resource-location' => 'deadbeef'
|
|
|
|
],
|
|
|
|
'body' => json_encode( [
|
|
|
|
'success' => true,
|
|
|
|
'checked' => 'who cares',
|
|
|
|
'identifiers' => [],
|
|
|
|
] )
|
|
|
|
];
|
|
|
|
$response2 = [
|
|
|
|
'body' => 'who cares'
|
|
|
|
];
|
|
|
|
|
|
|
|
$this->expectMathRestBaseMockHttpRequest( $expectedList, [ $response1, $response2 ] );
|
|
|
|
|
|
|
|
$this->overrideConfigValues( $config );
|
|
|
|
|
|
|
|
$input = '\\sin\\newcommand';
|
|
|
|
$rbi = new MathRestbaseInterface( $input );
|
|
|
|
|
|
|
|
$rbi->getMathML();
|
|
|
|
}
|
2023-05-12 17:03:01 +00:00
|
|
|
|
|
|
|
public function dataProviderForTestGetUrl() {
|
|
|
|
$path = 'media/math/render/svg/2uejd9dj3jd';
|
|
|
|
|
|
|
|
yield 'Math FullRestbaseURL default case' => [
|
|
|
|
$path, false, [], 'https://wikimedia.org/api/rest_v1/media/math/render/svg/2uejd9dj3jd'
|
|
|
|
];
|
|
|
|
|
|
|
|
yield 'Math FullRestbaseURL case' => [ $path, false, [
|
|
|
|
'MathFullRestbaseURL' => "https://myWiki.test/",
|
|
|
|
'VisualEditorFullRestbaseURL' => 'VisualEditor/api/rest_' // This should be ignored
|
|
|
|
], 'https://myWiki.test/v1/media/math/render/svg/2uejd9dj3jd' ];
|
|
|
|
|
|
|
|
yield 'VirtualRestConfig case' => [
|
|
|
|
$path,
|
|
|
|
true,
|
|
|
|
[
|
|
|
|
'MathUseInternalRestbasePath' => true,
|
|
|
|
'VirtualRestConfig' => [
|
|
|
|
'modules' => [ 'restbase' => [ 'url' => 'http://restbase.test.internal/api/' ] ]
|
|
|
|
],
|
|
|
|
'MathFullRestbaseURL' => "https://myWiki.test/",
|
|
|
|
'VisualEditorFullRestbaseURL' => 'VisualEditor/api/rest_' // This should be ignored
|
|
|
|
],
|
|
|
|
'http://restbase.test.internal/api/localhost/v1/media/math/render/svg/2uejd9dj3jd'
|
|
|
|
];
|
|
|
|
|
|
|
|
yield 'VirtualRestConfig case with Domain' => [
|
|
|
|
$path,
|
|
|
|
true,
|
|
|
|
[
|
|
|
|
'MathUseInternalRestbasePath' => true,
|
|
|
|
'VirtualRestConfig' => [
|
|
|
|
'modules' => [
|
|
|
|
'restbase' => [
|
|
|
|
'url' => 'http://restbase.test.internal/api', // Should work with trailing slash '/'
|
|
|
|
'domain' => 'testDomain'
|
|
|
|
]
|
|
|
|
]
|
|
|
|
],
|
|
|
|
'MathFullRestbaseURL' => "https://myWiki.test/",
|
|
|
|
'VisualEditorFullRestbaseURL' => 'VisualEditor/api/rest_' // This should be ignored
|
2023-04-28 14:44:15 +00:00
|
|
|
],
|
|
|
|
'http://restbase.test.internal/api/testDomain/v1/media/math/render/svg/2uejd9dj3jd'
|
|
|
|
];
|
|
|
|
|
|
|
|
yield 'VirtualRestConfig case with full URL as domain' => [
|
|
|
|
$path,
|
|
|
|
true,
|
|
|
|
[
|
|
|
|
'MathUseInternalRestbasePath' => true,
|
|
|
|
'VirtualRestConfig' => [
|
|
|
|
'modules' => [
|
|
|
|
'restbase' => [
|
|
|
|
'url' => 'http://restbase.test.internal/api', // Should work with trailing slash '/'
|
|
|
|
'domain' => 'https://testDomain:1234/' // domain name should be extracted from url
|
|
|
|
]
|
|
|
|
]
|
|
|
|
],
|
|
|
|
'MathFullRestbaseURL' => "https://myWiki.test/",
|
|
|
|
'VisualEditorFullRestbaseURL' => 'VisualEditor/api/rest_' // This should be ignored
|
2023-05-12 17:03:01 +00:00
|
|
|
],
|
|
|
|
'http://restbase.test.internal/api/testDomain/v1/media/math/render/svg/2uejd9dj3jd'
|
|
|
|
];
|
|
|
|
|
|
|
|
yield 'VisualEditor case' => [
|
|
|
|
$path,
|
|
|
|
true,
|
|
|
|
[
|
|
|
|
'MathFullRestbaseURL' => null,
|
|
|
|
'VisualEditorFullRestbaseURL' => 'VisualEditor/api/rest_'
|
|
|
|
],
|
|
|
|
'VisualEditor/api/rest_v1/media/math/render/svg/2uejd9dj3jd'
|
|
|
|
];
|
|
|
|
|
|
|
|
yield 'Exception case' => [ $path, false, [ 'MathFullRestbaseURL' => "", ], '', true ];
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @dataProvider dataProviderForTestGetUrl
|
|
|
|
* @param string $path
|
|
|
|
* @param bool $internal
|
|
|
|
* @param array $config
|
|
|
|
* @param string $expected
|
|
|
|
* @param bool $expectingException
|
|
|
|
*/
|
|
|
|
public function testGetUrl( $path, $internal, $config, $expected, $expectingException = false ) {
|
|
|
|
if ( $expectingException ) {
|
|
|
|
$this->expectException( MWException::class );
|
|
|
|
}
|
|
|
|
$this->overrideConfigValues( $config );
|
|
|
|
$input = '\\sin\\newcommand';
|
|
|
|
$rbi = new MathRestbaseInterface( $input );
|
|
|
|
$actual = $rbi->getUrl( $path, $internal );
|
|
|
|
$this->assertSame( $expected, $actual );
|
|
|
|
}
|
2015-11-26 19:31:09 +00:00
|
|
|
}
|