1+ """
2+ Unit tests for FletX Routing Configuration Module
3+ """
4+
5+ import pytest
6+ import re
7+ from unittest .mock import Mock , patch
8+
9+ from fletx .core .routing .config import (
10+ RouteDefinition ,
11+ RoutePattern ,
12+ RouterConfig ,
13+ ModuleRouter ,
14+ router_config
15+ )
16+ from fletx .core .routing .models import RouteType
17+ from fletx .core .routing .guards import RouteGuard
18+ from fletx .core .routing .middleware import RouteMiddleware
19+
20+
21+ class TestRouteDefinition :
22+ """Test RouteDefinition class."""
23+
24+ def test_creation_with_defaults (self ):
25+ """Test basic creation with default values."""
26+ component = Mock ()
27+ route_def = RouteDefinition (path = "/test" , component = component )
28+
29+ assert route_def .path == "/test"
30+ assert route_def .component == component
31+ assert route_def .route_type == RouteType .PAGE
32+ assert route_def .guards == []
33+ assert route_def .middleware == []
34+ assert route_def .data == {}
35+ assert route_def .children == []
36+ assert route_def .parent is None
37+
38+ def test_creation_with_all_params (self ):
39+ """Test creation with all parameters."""
40+ guard , middleware = Mock (spec = RouteGuard ), Mock (spec = RouteMiddleware )
41+
42+ route_def = RouteDefinition (
43+ path = "/test" ,
44+ component = Mock (),
45+ route_type = RouteType .MODULE ,
46+ guards = [guard ],
47+ middleware = [middleware ],
48+ data = {"key" : "value" },
49+ resolve = {"resolver" : Mock ()},
50+ meta = {"meta_key" : "meta_value" }
51+ )
52+
53+ assert route_def .route_type == RouteType .MODULE
54+ assert route_def .guards == [guard ]
55+ assert route_def .data == {"key" : "value" }
56+ assert "resolver" in route_def .resolve
57+
58+ def test_parent_child_relationship (self ):
59+ """Test parent-child relationships."""
60+ parent = RouteDefinition (path = "/parent" , component = Mock ())
61+ child = RouteDefinition (path = "/child" , component = Mock (), parent = parent )
62+ parent .children .append (child )
63+
64+ assert child .parent == parent
65+ assert child in parent .children
66+
67+
68+ class TestRoutePattern :
69+ """Test RoutePattern class."""
70+
71+ def test_simple_path (self ):
72+ """Test simple path matching."""
73+ pattern = RoutePattern ("/test" )
74+
75+ assert pattern .match ("/test" ) == {}
76+ assert pattern .match ("/other" ) is None
77+
78+ def test_parameters (self ):
79+ """Test parameter extraction."""
80+ pattern = RoutePattern ("/user/:id/profile/:section" )
81+
82+ assert pattern .param_names == ["id" , "section" ]
83+ assert pattern .match ("/user/123/profile/settings" ) == {
84+ "id" : "123" , "section" : "settings"
85+ }
86+ assert pattern .match ("/user/123" ) is None
87+
88+ def test_wildcard (self ):
89+ """Test wildcard matching."""
90+ pattern = RoutePattern ("/files/*" )
91+
92+ assert pattern .match ("/files/doc.pdf" ) == {}
93+ assert pattern .match ("/files/folder/file.txt" ) == {}
94+ assert pattern .match ("/files" ) is None
95+
96+ def test_mixed_params_and_wildcard (self ):
97+ """Test mixed parameters and wildcards."""
98+ pattern = RoutePattern ("/api/:version/*" )
99+
100+ assert pattern .match ("/api/v1/users" ) == {"version" : "v1" }
101+ assert pattern .match ("/api/v2/data/export" ) == {"version" : "v2" }
102+
103+ def test_regex_compilation (self ):
104+ """Test regex pattern compilation."""
105+ pattern = RoutePattern ("/user/:id" )
106+
107+ assert isinstance (pattern .regex_pattern , re .Pattern )
108+ assert pattern .regex_pattern .match ("/user/123" ).group (1 ) == "123"
109+
110+
111+ class TestRouterConfig :
112+ """Test RouterConfig class."""
113+
114+ def setup_method (self ):
115+ self .config = RouterConfig ()
116+ self .component = Mock ()
117+
118+ def test_add_route_simple (self ):
119+ """Test adding a simple route."""
120+ route_def = self .config .add_route ("/test" , self .component )
121+
122+ assert route_def .path == "/test"
123+ assert "/test" in self .config ._routes
124+
125+ def test_add_route_with_params (self ):
126+ """Test adding route with parameters."""
127+ self .config .add_route ("/user/:id" , self .component )
128+
129+ assert len (self .config ._route_patterns ) == 1
130+ assert self .config ._route_patterns [0 ][0 ].pattern == "/user/:id"
131+
132+ def test_add_multiple_routes (self ):
133+ """Test adding multiple routes."""
134+ routes = [
135+ {"path" : "/route1" , "component" : self .component },
136+ {"path" : "/route2" , "component" : self .component },
137+ ]
138+ self .config .add_routes (routes )
139+
140+ assert len (self .config ._routes ) == 2
141+
142+ def test_add_nested_routes (self ):
143+ """Test nested route creation."""
144+ parent = self .config .add_route ("/parent" , self .component )
145+ nested = [{"path" : "/child" , "component" : self .component }]
146+
147+ self .config .add_nested_routes ("/parent" , nested )
148+
149+ child = self .config ._routes ["/parent/child" ]
150+ assert child .parent == parent
151+ assert child in parent .children
152+
153+ def test_nested_routes_parent_not_found (self ):
154+ """Test error when parent route doesn't exist."""
155+ with pytest .raises (ValueError , match = "Parent route not found" ):
156+ self .config .add_nested_routes ("/nonexistent" , [])
157+
158+ def test_add_module_routes (self ):
159+ """Test adding module routes."""
160+ module = Mock (spec = ModuleRouter )
161+ module .get_routes .return_value = [
162+ RouteDefinition (path = "/mod" , component = self .component )
163+ ]
164+ module ._config ._route_patterns = []
165+
166+ self .config .add_module_routes ("/api" , module )
167+
168+ route = self .config ._routes ["/api/mod" ]
169+ assert route .route_type == RouteType .MODULE
170+ assert route .meta ["module" ] == module
171+
172+ def test_get_route (self ):
173+ """Test getting route by path."""
174+ route = self .config .add_route ("/test" , self .component )
175+
176+ assert self .config .get_route ("/test" ) == route
177+ assert self .config .get_route ("/nonexistent" ) is None
178+
179+ def test_match_route_exact (self ):
180+ """Test exact route matching."""
181+ route = self .config .add_route ("/test" , self .component )
182+
183+ matched , params = self .config .match_route ("/test" )
184+ assert matched == route
185+ assert params == {}
186+
187+ def test_match_route_pattern (self ):
188+ """Test pattern route matching."""
189+ route = self .config .add_route ("/user/:id" , self .component )
190+
191+ matched , params = self .config .match_route ("/user/123" )
192+ assert matched == route
193+ assert params == {"id" : "123" }
194+
195+ def test_match_route_no_match (self ):
196+ """Test no route matches."""
197+ self .config .add_route ("/test" , self .component )
198+
199+ assert self .config .match_route ("/other" ) is None
200+
201+ def test_get_routes_by_type (self ):
202+ """Test filtering routes by type."""
203+ page = self .config .add_route ("/page" , self .component , route_type = RouteType .PAGE )
204+ module = self .config .add_route ("/mod" , self .component , route_type = RouteType .MODULE )
205+
206+ assert self .config .get_routes_by_type (RouteType .PAGE ) == [page ]
207+ assert self .config .get_routes_by_type (RouteType .MODULE ) == [module ]
208+
209+ def test_get_child_routes (self ):
210+ """Test getting child routes."""
211+ self .config .add_route ("/parent" , self .component )
212+ self .config .add_nested_routes ("/parent" , [
213+ {"path" : "/child" , "component" : self .component }
214+ ])
215+
216+ assert len (self .config .get_child_routes ("/parent" )) == 1
217+ assert self .config .get_child_routes ("/nonexistent" ) == []
218+
219+ def test_get_route_hierarchy (self ):
220+ """Test getting route hierarchy."""
221+ gp = self .config .add_route ("/gp" , self .component )
222+ p = self .config .add_route ("/gp/p" , self .component )
223+ c = self .config .add_route ("/gp/p/c" , self .component )
224+
225+ p .parent , c .parent = gp , p
226+ gp .children .append (p )
227+ p .children .append (c )
228+
229+ hierarchy = self .config .get_route_hierarchy ("/gp/p/c" )
230+ assert hierarchy == [gp , p , c ]
231+
232+
233+ class TestModuleRouter :
234+ """Test ModuleRouter class."""
235+
236+ def setup_method (self ):
237+ self .component = Mock ()
238+
239+ def test_initialization (self ):
240+ """Test module router initialization."""
241+ class TestRouter (ModuleRouter ):
242+ name = "test"
243+ base_path = "/test"
244+ routes = [{"path" : "/r1" , "component" : Mock ()}]
245+ sub_routers = []
246+
247+ router = TestRouter ()
248+ assert router .name == "test"
249+ assert len (router .get_routes ()) == 1
250+
251+ def test_add_route (self ):
252+ """Test adding route to module."""
253+ class TestRouter (ModuleRouter ):
254+ name = "test"
255+ base_path = "/test"
256+ routes = []
257+ sub_routers = []
258+
259+ router = TestRouter ()
260+ route = router .add_route ("/new" , self .component )
261+
262+ assert route .path == "/new"
263+
264+ def test_match_route (self ):
265+ """Test route matching in module."""
266+ class TestRouter (ModuleRouter ):
267+ name = "test"
268+ base_path = "/test"
269+ routes = [{"path" : "/user/:id" , "component" : Mock ()}]
270+ sub_routers = []
271+
272+ router = TestRouter ()
273+ matched , params = router .match_route ("/user/123" )
274+
275+ assert params == {"id" : "123" }
276+
277+ def test_add_subrouters (self ):
278+ """Test adding sub-routers."""
279+ class SubRouter (ModuleRouter ):
280+ name = "sub"
281+ base_path = "/sub"
282+ routes = [{"path" : "/r" , "component" : Mock ()}]
283+ sub_routers = []
284+
285+ class MainRouter (ModuleRouter ):
286+ name = "main"
287+ base_path = "/main"
288+ routes = []
289+ sub_routers = [SubRouter ]
290+
291+ router = MainRouter ()
292+ routes = router .get_routes ()
293+
294+ assert len (routes ) == 1
295+ assert routes [0 ].path == "/sub/r"
296+
297+
298+ class TestGlobalConfig :
299+ """Test global router configuration."""
300+
301+ def test_singleton (self ):
302+ """Test router_config is singleton."""
303+ from fletx .core .routing .config import router_config as c1 , router_config as c2
304+ assert c1 is c2
305+
306+
307+ class TestIntegration :
308+ """Integration tests."""
309+
310+ def setup_method (self ):
311+ self .config = RouterConfig ()
312+ self .component = Mock ()
313+
314+ def test_complex_routing (self ):
315+ """Test complex routing scenario."""
316+ self .config .add_route ("/" , self .component )
317+ self .config .add_route ("/user/:id" , self .component )
318+ self .config .add_route ("/files/*" , self .component )
319+
320+ assert self .config .match_route ("/" )[0 ] is not None
321+ _ , params = self .config .match_route ("/user/123" )
322+ assert params == {"id" : "123" }
323+ assert self .config .match_route ("/files/doc.pdf" )[0 ] is not None
324+
325+ def test_deep_hierarchy (self ):
326+ """Test deep route hierarchy."""
327+ self .config .add_route ("/app" , self .component )
328+ self .config .add_nested_routes ("/app" , [
329+ {"path" : "/dashboard" , "component" : self .component }
330+ ])
331+ self .config .add_nested_routes ("/app/dashboard" , [
332+ {"path" : "/settings" , "component" : self .component }
333+ ])
334+
335+ hierarchy = self .config .get_route_hierarchy ("/app/dashboard/settings" )
336+ assert len (hierarchy ) == 3
0 commit comments