wiki.techinc.nl/tests/phpunit/unit/includes/GlobalFunctions/WfEscapeWikiTextTest.php
Bartosz Dziewoński 7d9ca602db UserAuthority: Fix wikitext escaping for block errors (again)
In 279fd16bab, I made what I thought
was a trivial change to UserAuthorityTest:

(diff slightly modified here for clarity)
-		$message = $permissionStatus->getErrors()[2]['message'];
+		$message = $permissionStatus->getMessages()[2];
 		$this->assertArrayEquals(
 			$this->getFakeBlockMessageParams(),
 			$message->getParams()
 		);

And in 3d92cb2f82, I made what I thought
was also a trivial change to UserAuthority:

(diff slightly modified here for clarity, likewise)
-			foreach ( $errors as $err ) {
-				$status->fatal( wfMessage( ...$err ) );
-			}
+			$status->merge( $tempStatus );

However, it turns out these two pieces of code had vital roles:

* The code in UserAuthority ensured that the final status contains
  Message objects instead of key strings + parameter arrays, and thus
  does not trigger wikitext escaping in a legacy code path (T368821).

* The code in UserAuthorityTest accessed the internals of the same
  status with (now deprecated) getErrors() to check that it indeed
  contained a Message object, rather then a key string, which would
  cause a test failure due to a fatal error in the code below.
  getMessages() returns objects regardless of what's inside the
  status, so the test never fails.

Thus I managed to disarm the regression test, and then cause exactly
the regression it was supposed to prevent: block error messages on
Special:CreateAccount have parameters shown as wikitext (T306494).

Restore a foreach loop instead of `$status->merge()` to fix that, and
document why it is there. Change the test so that it actually runs
the code whose behavior it wants to verify, instead of a related but
different method, hopefully making it more resilient against future
developers.

(I found the bug because the test started failing with the refactoring
I'm trying to do in I625a48a6ecd3fad5c2ed76b23343a0fef91e1b83.)

Bug: T306494
Change-Id: I7601fc51702cb33ef9d2b341ea555dc230d31537
2024-07-22 19:42:50 +00:00

133 lines
2.9 KiB
PHP

<?php
/**
* @group GlobalFunctions
* @covers ::wfEscapeWikiText
*/
class WfEscapeWikiTextTest extends MediaWikiUnitTestCase {
/**
* @dataProvider provideEscape
*/
public function testEscape( $input, $expected ) {
// save global
global $wgEnableMagicLinks;
$old = $wgEnableMagicLinks;
$wgEnableMagicLinks = [];
try {
$actual = wfEscapeWikiText( $input );
// Sanity check that the output can be decoded back to the input
// input as well.
$decoded = html_entity_decode( $actual, ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML5 );
$this->assertEquals( $decoded, (string)$input );
// And that the output was what we expected
$this->assertEquals( $expected, $actual );
} finally {
// restore global
$wgEnableMagicLinks = $old;
}
}
public function provideEscape() {
return [
'null' => [
null,
'',
],
'false' => [
false,
'',
],
'empty string' => [
'',
'',
],
'no escapes' => [
'a',
'a',
],
'braces and brackets' => [
'[[WikiLink]] {{Template}} <html>',
'&#91;&#91;WikiLink&#93;&#93; &#123;&#123;Template&#125;&#125; &#60;html&#62;',
],
'quotes' => [
'"\'',
'&#34;&#39;',
],
'tokens' => [
'{| |- |+ !! ~~~~~ __FOO__',
'&#123;&#124; &#124;- &#124;+ &#33;! ~~&#126;~~ _&#95;FOO_&#95;',
],
'start of line' => [
"* foo\n! bar\n# bat\n:baz\n pre\n----",
"&#42; foo\n&#33; bar\n&#35; bat\n&#58;baz\n&#32;pre\n&#45;---",
],
'paragraph separators' => [
"a\n\n\n\nb",
"a\n&#10;\n&#10;b",
],
'language converter' => [
'-{ foo ; bar }-',
'&#45;&#123; foo &#59; bar &#125;-',
],
'left-side context: |+' => [
'+ foo + bar',
'&#43; foo + bar',
],
'left-side context: |-' => [
'- foo - bar',
'&#45; foo - bar',
],
'left-side context: __FOO__' => [
'_FOO__',
'&#95;FOO_&#95;',
],
'left-side context: ~~~' => [
'~~ long string here',
'&#126;~ long string here',
],
'left-side context: newlines' => [
"\n\n\nFoo",
"&#10;\n&#10;Foo",
],
'right-side context: ~~~' => [
'long string here ~~',
'long string here ~&#126;',
],
'right-side context: __FOO__' => [
'__FOO_',
'&#95;&#95;FOO&#95;',
],
'right-side context: newlines' => [
"foo\n\n\n",
"foo\n&#10;&#10;",
],
// A single character input needs to be protected against both
// left-side context and right-side context.
'both-side context: +' => [ // | + + (left side)
'+',
'&#43;',
],
'both-side context: -' => [ // | + - (left side)
'-',
'&#45;',
],
'both-side context: _' => [ // _ + _FOO as well as __FOO_ + _
'_',
'&#95;',
],
'both-side context: ~' => [ // ~ + ~~ as well as ~~ + ~
'~',
'&#126;',
],
'both-side context: \\n' => [ // \n + \n
"\n",
'&#10;',
],
'both-side context: \\t' => [ // \n + \t + \n becomes paragraph break
"\t",
'&#9;',
],
];
}
}