1818class RouteBuilderTest extends TestCase
1919{
2020 public const METHOD_DEFAULTS = [
21- 'PUT ' , 'POST ' , 'PATCH ' , 'GET ' , 'DELETE ' ,
21+ 'GET ' , 'POST ' , 'PUT ' , 'PATCH ' , 'DELETE ' ,
2222 ];
2323
2424 /**
2525 * @dataProvider dataProvider
26- *
27- * @return void
2826 */
2927 public function testBuild (
3028 string |null $ routeName ,
31- callable |null $ action ,
29+ callable |string | null $ action ,
3230 string |null $ uri ,
3331 string |null $ pattern ,
3432 array |null $ methods ,
35- string |null $ allowedException
36- ) {
33+ array |null $ allowedException
34+ ): void {
3735 $ builder = new RouteBuilder ();
3836
3937 if ($ routeName ) {
@@ -53,33 +51,231 @@ public function testBuild(
5351 }
5452
5553 if ($ allowedException ) {
56- $ this ->expectException ($ allowedException );
54+ $ this ->expectException ($ allowedException[ ' class ' ] );
5755 }
5856
5957 try {
6058 $ route = $ builder ->build ();
6159 } catch (RouteInvalidConfigurationException $ configurationException ) {
6260 $ this ->assertNotEmpty ($ configurationException ->getMessages ());
61+ $ this ->assertEquals ($ allowedException ['messages ' ], $ configurationException ->getMessages ());
6362
6463 throw $ configurationException ;
6564 }
6665
67- $ this ->assertIsCallable ( $ route ->getController ());
66+ $ this ->assertEquals ( $ action , $ route ->getController ());
6867 $ this ->assertEquals ($ uri , $ route ->getUri ());
6968 $ this ->assertEquals ($ methods ?: self ::METHOD_DEFAULTS , $ route ->getMethods ());
7069 $ this ->assertEquals ($ pattern , $ route ->getPattern ());
7170 $ this ->assertEquals ($ routeName , $ route ->getName ());
7271 }
7372
74- public function dataProvider ()
73+ public static function dataProvider (): array
7574 {
75+ $ emptyRouteAction = static function () {};
76+
7677 return [
77- ['test ' , function () {}, '/{test}.{_format} ' , '/\/(.[aA-zZ0-9-_]+)\.(.[aA-zZ0-9-_]+)$/ ' , ['POST ' ], null ],
78- ['test ' , function () {}, '/{test}-{date}.{_format} ' , '/\/(.[aA-zZ0-9-_]+)-(.[aA-zZ0-9-_]+)\.(.[aA-zZ0-9-_]+)$/ ' , ['POST ' ], null ],
79- [null , function () {}, '/{test}.{_format} ' , '/\/(.[aA-zZ0-9-_]+)\.(.[aA-zZ0-9-_]+)$/ ' , null , null ],
80- ['test ' , null , '/{test}.{_format} ' , null , null , RouteInvalidConfigurationException::class],
81- ['test ' , function () {}, '/test ' , null , null , null ],
82- ['test ' , null , null , null , null , RouteInvalidConfigurationException::class],
78+ 'Correct minimal route: just action and uri ' => [
79+ 'routeName ' => null ,
80+ 'action ' => $ emptyRouteAction ,
81+ 'uri ' => '/uri ' ,
82+ 'pattern ' => null ,
83+ 'methods ' => null ,
84+ 'allowedException ' => null ,
85+ ],
86+ 'Empty uri: null ' => [
87+ 'routeName ' => null ,
88+ 'action ' => $ emptyRouteAction ,
89+ 'uri ' => null ,
90+ 'pattern ' => null ,
91+ 'methods ' => null ,
92+ 'allowedException ' => [
93+ 'class ' => RouteInvalidConfigurationException::class,
94+ 'messages ' => [
95+ 'Uri can not be empty. ' ,
96+ ],
97+ ],
98+ ],
99+ 'Empty uri: empty string ' => [
100+ 'routeName ' => null ,
101+ 'action ' => $ emptyRouteAction ,
102+ 'uri ' => '' ,
103+ 'pattern ' => null ,
104+ 'methods ' => null ,
105+ 'allowedException ' => [
106+ 'class ' => RouteInvalidConfigurationException::class,
107+ 'messages ' => [
108+ 'Uri can not be empty. ' ,
109+ ],
110+ ],
111+ ],
112+ 'Invalid route name 1 ' => [
113+ 'routeName ' => '0invalid ' ,
114+ 'action ' => $ emptyRouteAction ,
115+ 'uri ' => '/uri ' ,
116+ 'pattern ' => null ,
117+ 'methods ' => null ,
118+ 'allowedException ' => [
119+ 'class ' => RouteInvalidConfigurationException::class,
120+ 'messages ' => [
121+ 'The route name must match "[a-zA-Z][a-zA-Z0-9_]*". ' ,
122+ ],
123+ ],
124+ ],
125+ 'Invalid route name 2 ' => [
126+ 'routeName ' => '.invalid ' ,
127+ 'action ' => $ emptyRouteAction ,
128+ 'uri ' => '/uri ' ,
129+ 'pattern ' => null ,
130+ 'methods ' => null ,
131+ 'allowedException ' => [
132+ 'class ' => RouteInvalidConfigurationException::class,
133+ 'messages ' => [
134+ 'The route name must match "[a-zA-Z][a-zA-Z0-9_]*". ' ,
135+ ],
136+ ],
137+ ],
138+ 'Invalid route name 3 ' => [
139+ 'routeName ' => 'invalid@again ' ,
140+ 'action ' => $ emptyRouteAction ,
141+ 'uri ' => '/uri ' ,
142+ 'pattern ' => null ,
143+ 'methods ' => null ,
144+ 'allowedException ' => [
145+ 'class ' => RouteInvalidConfigurationException::class,
146+ 'messages ' => [
147+ 'The route name must match "[a-zA-Z][a-zA-Z0-9_]*". ' ,
148+ ],
149+ ],
150+ ],
151+ 'Empty action ' => [
152+ 'routeName ' => 'test ' ,
153+ 'action ' => null ,
154+ 'uri ' => '/uri ' ,
155+ 'pattern ' => null ,
156+ 'methods ' => null ,
157+ 'allowedException ' => [
158+ 'class ' => RouteInvalidConfigurationException::class,
159+ 'messages ' => [
160+ 'The route action can not be empty. ' ,
161+ ],
162+ ],
163+ ],
164+ 'Class string action with route name ' => [
165+ 'routeName ' => 'index ' ,
166+ 'action ' => Action::class,
167+ 'uri ' => '/uri ' ,
168+ 'pattern ' => null ,
169+ 'methods ' => null ,
170+ 'allowedException ' => null ,
171+ ],
172+ 'Class string action with route name null ' => [
173+ 'routeName ' => null ,
174+ 'action ' => Action::class,
175+ 'uri ' => '/uri ' ,
176+ 'pattern ' => null ,
177+ 'methods ' => null ,
178+ 'allowedException ' => [
179+ 'class ' => RouteInvalidConfigurationException::class,
180+ 'messages ' => [
181+ 'The route action should be callable. Examples: `[object, "method|<route_name>"], [Classname, "metnod|<routeName>"], Classname::method, Classname, function() {}` Current value: ' .Action::class,
182+ ],
183+ ],
184+ ],
185+ 'Class string action with empty route name ' => [
186+ 'routeName ' => '' ,
187+ 'action ' => Action::class,
188+ 'uri ' => '/uri ' ,
189+ 'pattern ' => null ,
190+ 'methods ' => null ,
191+ 'allowedException ' => [
192+ 'class ' => RouteInvalidConfigurationException::class,
193+ 'messages ' => [
194+ 'The route action should be callable. Examples: `[object, "method|<route_name>"], [Classname, "metnod|<routeName>"], Classname::method, Classname, function() {}` Current value: ' .Action::class,
195+ ],
196+ ],
197+ ],
198+ 'All errors at the same time 1 ' => [
199+ 'routeName ' => '1invalid@ ' ,
200+ 'action ' => null ,
201+ 'uri ' => null ,
202+ 'pattern ' => null ,
203+ 'methods ' => null ,
204+ 'allowedException ' => [
205+ 'class ' => RouteInvalidConfigurationException::class,
206+ 'messages ' => [
207+ 'Uri can not be empty. ' ,
208+ 'The route name must match "[a-zA-Z][a-zA-Z0-9_]*". ' ,
209+ 'The route action can not be empty. ' ,
210+ ],
211+ ],
212+ ],
213+ 'All errors at the same time 2 ' => [
214+ 'routeName ' => '' ,
215+ 'action ' => Action::class,
216+ 'uri ' => null ,
217+ 'pattern ' => null ,
218+ 'methods ' => null ,
219+ 'allowedException ' => [
220+ 'class ' => RouteInvalidConfigurationException::class,
221+ 'messages ' => [
222+ 'Uri can not be empty. ' ,
223+ 'The route action should be callable. Examples: `[object, "method|<route_name>"], [Classname, "metnod|<routeName>"], Classname::method, Classname, function() {}` Current value: ' .Action::class,
224+ ],
225+ ],
226+ ],
227+ 'Correct GET route ' => [
228+ 'routeName ' => 'test ' ,
229+ 'action ' => $ emptyRouteAction ,
230+ 'uri ' => '/uri ' ,
231+ 'pattern ' => null ,
232+ 'methods ' => ['GET ' ],
233+ 'allowedException ' => null ,
234+ ],
235+ 'Correct POST route ' => [
236+ 'routeName ' => 'test ' ,
237+ 'action ' => $ emptyRouteAction ,
238+ 'uri ' => '/uri ' ,
239+ 'pattern ' => null ,
240+ 'methods ' => ['POST ' ],
241+ 'allowedException ' => null ,
242+ ],
243+ 'Simple dynamic uri ' => [
244+ 'routeName ' => 'test ' ,
245+ 'action ' => $ emptyRouteAction ,
246+ 'uri ' => '/{test} ' ,
247+ 'pattern ' => '/\/([a-zA-Z_][a-zA-Z0-9_]*)$/ ' ,
248+ 'methods ' => null ,
249+ 'allowedException ' => null ,
250+ ],
251+ 'Simple dynamic uri with two placeholders ' => [
252+ 'routeName ' => 'test ' ,
253+ 'action ' => $ emptyRouteAction ,
254+ 'uri ' => '/{test}_{another_test} ' ,
255+ 'pattern ' => '/\/([a-zA-Z_][a-zA-Z0-9_]*)_([a-zA-Z_][a-zA-Z0-9_]*)$/ ' ,
256+ 'methods ' => null ,
257+ 'allowedException ' => null ,
258+ ],
259+ 'Placeholder with default value ' => [
260+ 'routeName ' => 'test ' ,
261+ 'action ' => $ emptyRouteAction ,
262+ 'uri ' => '/{page:0} ' ,
263+ 'pattern ' => '/\/([a-zA-Z_][a-zA-Z0-9_]*)$/ ' ,
264+ 'methods ' => null ,
265+ 'allowedException ' => null ,
266+ ],
267+ // TODO: implement regex for placeholders
268+ // 'Placeholder with regex' => [
269+ // 'routeName' => 'test',
270+ // 'action' => $emptyRouteAction,
271+ // 'uri' => '/{page}',
272+ // 'placeholders_regex' => [
273+ // 'page' => '[1-9]\\d*'; // any number without leading zeros
274+ // ],
275+ // 'pattern' => '/\/([a-zA-Z_][a-zA-Z_0-9]*)$/',
276+ // 'methods' => null,
277+ // 'allowedException' => null
278+ // ],
83279 ];
84280 }
85281
@@ -101,3 +297,13 @@ public function testClear()
101297 $ this ->assertNotEquals ($ routeA ->getName (), $ routeB ->getName ());
102298 }
103299}
300+
301+ /**
302+ * @internal
303+ */
304+ final class Action
305+ {
306+ public function index ()
307+ {
308+ }
309+ }
0 commit comments