Refactor ApiTestCase to get token from ApiQueryTokens
[lhc/web/wiklou.git] / tests / phpunit / includes / PathRouterTest.php
1 <?php
2
3 /**
4 * Tests for the PathRouter parsing.
5 *
6 * @covers PathRouter
7 */
8 class PathRouterTest extends MediaWikiTestCase {
9
10 /**
11 * @var PathRouter
12 */
13 protected $basicRouter;
14
15 protected function setUp() {
16 parent::setUp();
17 $router = new PathRouter;
18 $router->add( "/wiki/$1" );
19 $this->basicRouter = $router;
20 }
21
22 /**
23 * Test basic path parsing
24 */
25 public function testBasic() {
26 $matches = $this->basicRouter->parse( "/wiki/Foo" );
27 $this->assertEquals( $matches, [ 'title' => "Foo" ] );
28 }
29
30 /**
31 * Test loose path auto-$1
32 */
33 public function testLoose() {
34 $router = new PathRouter;
35 $router->add( "/" ); # Should be the same as "/$1"
36 $matches = $router->parse( "/Foo" );
37 $this->assertEquals( $matches, [ 'title' => "Foo" ] );
38
39 $router = new PathRouter;
40 $router->add( "/wiki" ); # Should be the same as /wiki/$1
41 $matches = $router->parse( "/wiki/Foo" );
42 $this->assertEquals( $matches, [ 'title' => "Foo" ] );
43
44 $router = new PathRouter;
45 $router->add( "/wiki/" ); # Should be the same as /wiki/$1
46 $matches = $router->parse( "/wiki/Foo" );
47 $this->assertEquals( $matches, [ 'title' => "Foo" ] );
48 }
49
50 /**
51 * Test to ensure that path is based on specifity, not order
52 */
53 public function testOrder() {
54 $router = new PathRouter;
55 $router->add( "/$1" );
56 $router->add( "/a/$1" );
57 $router->add( "/b/$1" );
58 $matches = $router->parse( "/a/Foo" );
59 $this->assertEquals( $matches, [ 'title' => "Foo" ] );
60
61 $router = new PathRouter;
62 $router->add( "/b/$1" );
63 $router->add( "/a/$1" );
64 $router->add( "/$1" );
65 $matches = $router->parse( "/a/Foo" );
66 $this->assertEquals( $matches, [ 'title' => "Foo" ] );
67 }
68
69 /**
70 * Test the handling of key based arrays with a url parameter
71 */
72 public function testKeyParameter() {
73 $router = new PathRouter;
74 $router->add( [ 'edit' => "/edit/$1" ], [ 'action' => '$key' ] );
75 $matches = $router->parse( "/edit/Foo" );
76 $this->assertEquals( $matches, [ 'title' => "Foo", 'action' => 'edit' ] );
77 }
78
79 /**
80 * Test the handling of $2 inside paths
81 */
82 public function testAdditionalParameter() {
83 // Basic $2
84 $router = new PathRouter;
85 $router->add( '/$2/$1', [ 'test' => '$2' ] );
86 $matches = $router->parse( "/asdf/Foo" );
87 $this->assertEquals( $matches, [ 'title' => "Foo", 'test' => 'asdf' ] );
88 }
89
90 /**
91 * Test additional restricted value parameter
92 */
93 public function testRestrictedValue() {
94 $router = new PathRouter;
95 $router->add( '/$2/$1',
96 [ 'test' => '$2' ],
97 [ '$2' => [ 'a', 'b' ] ]
98 );
99 $router->add( '/$2/$1',
100 [ 'test2' => '$2' ],
101 [ '$2' => 'c' ]
102 );
103 $router->add( '/$1' );
104
105 $matches = $router->parse( "/asdf/Foo" );
106 $this->assertEquals( $matches, [ 'title' => "asdf/Foo" ] );
107
108 $matches = $router->parse( "/a/Foo" );
109 $this->assertEquals( $matches, [ 'title' => "Foo", 'test' => 'a' ] );
110
111 $matches = $router->parse( "/c/Foo" );
112 $this->assertEquals( $matches, [ 'title' => "Foo", 'test2' => 'c' ] );
113 }
114
115 public function callbackForTest( &$matches, $data ) {
116 $matches['x'] = $data['$1'];
117 $matches['foo'] = $data['foo'];
118 }
119
120 public function testCallback() {
121 $router = new PathRouter;
122 $router->add( "/$1",
123 [ 'a' => 'b', 'data:foo' => 'bar' ],
124 [ 'callback' => [ $this, 'callbackForTest' ] ]
125 );
126 $matches = $router->parse( '/Foo' );
127 $this->assertEquals( $matches, [
128 'title' => "Foo",
129 'x' => 'Foo',
130 'a' => 'b',
131 'foo' => 'bar'
132 ] );
133 }
134
135 /**
136 * Test to ensure that matches are not made if a parameter expects nonexistent input
137 */
138 public function testFail() {
139 $router = new PathRouter;
140 $router->add( "/wiki/$1", [ 'title' => "$1$2" ] );
141 $matches = $router->parse( "/wiki/A" );
142 $this->assertEquals( [], $matches );
143 }
144
145 /**
146 * Test to ensure weight of paths is handled correctly
147 */
148 public function testWeight() {
149 $router = new PathRouter;
150 $router->addStrict( "/Bar", [ 'ping' => 'pong' ] );
151 $router->add( "/asdf-$1", [ 'title' => 'qwerty-$1' ] );
152 $router->add( "/$1" );
153 $router->add( "/qwerty-$1", [ 'title' => 'asdf-$1' ] );
154 $router->addStrict( "/Baz", [ 'marco' => 'polo' ] );
155 $router->add( "/a/$1" );
156 $router->add( "/asdf/$1" );
157 $router->add( "/$2/$1", [ 'unrestricted' => '$2' ] );
158 $router->add( [ 'qwerty' => "/qwerty/$1" ], [ 'qwerty' => '$key' ] );
159 $router->add( "/$2/$1", [ 'restricted-to-y' => '$2' ], [ '$2' => 'y' ] );
160
161 foreach (
162 [
163 '/Foo' => [ 'title' => 'Foo' ],
164 '/Bar' => [ 'ping' => 'pong' ],
165 '/Baz' => [ 'marco' => 'polo' ],
166 '/asdf-foo' => [ 'title' => 'qwerty-foo' ],
167 '/qwerty-bar' => [ 'title' => 'asdf-bar' ],
168 '/a/Foo' => [ 'title' => 'Foo' ],
169 '/asdf/Foo' => [ 'title' => 'Foo' ],
170 '/qwerty/Foo' => [ 'title' => 'Foo', 'qwerty' => 'qwerty' ],
171 '/baz/Foo' => [ 'title' => 'Foo', 'unrestricted' => 'baz' ],
172 '/y/Foo' => [ 'title' => 'Foo', 'restricted-to-y' => 'y' ],
173 ] as $path => $result
174 ) {
175 $this->assertEquals( $router->parse( $path ), $result );
176 }
177 }
178
179 /**
180 * Make sure the router handles titles like Special:Recentchanges correctly
181 */
182 public function testSpecial() {
183 $matches = $this->basicRouter->parse( "/wiki/Special:Recentchanges" );
184 $this->assertEquals( $matches, [ 'title' => "Special:Recentchanges" ] );
185 }
186
187 /**
188 * Make sure the router decodes urlencoding properly
189 */
190 public function testUrlencoding() {
191 $matches = $this->basicRouter->parse( "/wiki/Title_With%20Space" );
192 $this->assertEquals( $matches, [ 'title' => "Title_With Space" ] );
193 }
194
195 public static function provideRegexpChars() {
196 return [
197 [ "$" ],
198 [ "$1" ],
199 [ "\\" ],
200 [ "\\$1" ],
201 ];
202 }
203
204 /**
205 * Make sure the router doesn't break on special characters like $ used in regexp replacements
206 * @dataProvider provideRegexpChars
207 */
208 public function testRegexpChars( $char ) {
209 $matches = $this->basicRouter->parse( "/wiki/$char" );
210 $this->assertEquals( $matches, [ 'title' => "$char" ] );
211 }
212
213 /**
214 * Make sure the router handles characters like +&() properly
215 */
216 public function testCharacters() {
217 $matches = $this->basicRouter->parse( "/wiki/Plus+And&Dollar\\Stuff();[]{}*" );
218 $this->assertEquals( $matches, [ 'title' => "Plus+And&Dollar\\Stuff();[]{}*" ] );
219 }
220
221 /**
222 * Make sure the router handles unicode characters correctly
223 * @depends testSpecial
224 * @depends testUrlencoding
225 * @depends testCharacters
226 */
227 public function testUnicode() {
228 $matches = $this->basicRouter->parse( "/wiki/Spécial:Modifications_récentes" );
229 $this->assertEquals( $matches, [ 'title' => "Spécial:Modifications_récentes" ] );
230
231 $matches = $this->basicRouter->parse( "/wiki/Sp%C3%A9cial:Modifications_r%C3%A9centes" );
232 $this->assertEquals( $matches, [ 'title' => "Spécial:Modifications_récentes" ] );
233 }
234
235 /**
236 * Ensure the router doesn't choke on long paths.
237 */
238 public function testLength() {
239 // phpcs:disable Generic.Files.LineLength
240 $matches = $this->basicRouter->parse(
241 "/wiki/Lorem_ipsum_dolor_sit_amet,_consectetur_adipisicing_elit,_sed_do_eiusmod_tempor_incididunt_ut_labore_et_dolore_magna_aliqua._Ut_enim_ad_minim_veniam,_quis_nostrud_exercitation_ullamco_laboris_nisi_ut_aliquip_ex_ea_commodo_consequat._Duis_aute_irure_dolor_in_reprehenderit_in_voluptate_velit_esse_cillum_dolore_eu_fugiat_nulla_pariatur._Excepteur_sint_occaecat_cupidatat_non_proident,_sunt_in_culpa_qui_officia_deserunt_mollit_anim_id_est_laborum."
242 );
243 $this->assertEquals(
244 $matches,
245 [ 'title' => "Lorem_ipsum_dolor_sit_amet,_consectetur_adipisicing_elit,_sed_do_eiusmod_tempor_incididunt_ut_labore_et_dolore_magna_aliqua._Ut_enim_ad_minim_veniam,_quis_nostrud_exercitation_ullamco_laboris_nisi_ut_aliquip_ex_ea_commodo_consequat._Duis_aute_irure_dolor_in_reprehenderit_in_voluptate_velit_esse_cillum_dolore_eu_fugiat_nulla_pariatur._Excepteur_sint_occaecat_cupidatat_non_proident,_sunt_in_culpa_qui_officia_deserunt_mollit_anim_id_est_laborum." ]
246 );
247 // phpcs:enable
248 }
249
250 /**
251 * Ensure that the php passed site of parameter values are not urldecoded
252 */
253 public function testPatternUrlencoding() {
254 $router = new PathRouter;
255 $router->add( "/wiki/$1", [ 'title' => '%20:$1' ] );
256 $matches = $router->parse( "/wiki/Foo" );
257 $this->assertEquals( $matches, [ 'title' => '%20:Foo' ] );
258 }
259
260 /**
261 * Ensure that raw parameter values do not have any variable replacements or urldecoding
262 */
263 public function testRawParamValue() {
264 $router = new PathRouter;
265 $router->add( "/wiki/$1", [ 'title' => [ 'value' => 'bar%20$1' ] ] );
266 $matches = $router->parse( "/wiki/Foo" );
267 $this->assertEquals( $matches, [ 'title' => 'bar%20$1' ] );
268 }
269 }