@@ -6,7 +6,7 @@ mod error;
66mod ui;
77
88use crate :: api:: build_api_router;
9- use crate :: data_provider:: SessionDataProvider ;
9+ use crate :: data_provider:: { DataProvider , SessionDataProvider } ;
1010use axum:: Router ;
1111use hotfix:: message:: FixMessage ;
1212use hotfix:: session:: SessionRef ;
@@ -16,9 +16,13 @@ struct AppState<P> {
1616 data_provider : P ,
1717}
1818
19- #[ cfg( feature = "ui" ) ]
2019pub fn build_router < M : FixMessage > ( session_ref : SessionRef < M > ) -> Router {
2120 let data_provider = SessionDataProvider { session_ref } ;
21+ build_router_with_provider ( data_provider)
22+ }
23+
24+ #[ cfg( feature = "ui" ) ]
25+ fn build_router_with_provider ( data_provider : impl DataProvider + ' static ) -> Router {
2226 let state = AppState { data_provider } ;
2327 Router :: new ( )
2428 . nest ( "/api" , build_api_router ( ) )
@@ -27,10 +31,146 @@ pub fn build_router<M: FixMessage>(session_ref: SessionRef<M>) -> Router {
2731}
2832
2933#[ cfg( not( feature = "ui" ) ) ]
30- pub fn build_router < M : FixMessage > ( session_ref : SessionRef < M > ) -> Router {
31- let data_provider = SessionDataProvider { session_ref } ;
34+ fn build_router_with_provider ( data_provider : impl DataProvider + ' static ) -> Router {
3235 let state = AppState { data_provider } ;
3336 Router :: new ( )
3437 . nest ( "/api" , build_api_router ( ) )
3538 . with_state ( state)
3639}
40+
41+ #[ cfg( test) ]
42+ mod tests {
43+ use crate :: build_router_with_provider;
44+ use crate :: data_provider:: DataProvider ;
45+ use axum:: body:: { Body , to_bytes} ;
46+ use axum:: http:: Request ;
47+ use hotfix:: session:: { SessionInfo , Status } ;
48+ use serde_json:: Value ;
49+ use tower:: Service ;
50+
51+ #[ derive( Clone ) ]
52+ struct FakeDataProvider {
53+ session_info : SessionInfo ,
54+ }
55+
56+ #[ async_trait:: async_trait]
57+ impl DataProvider for FakeDataProvider {
58+ async fn get_session_info ( & self ) -> SessionInfo {
59+ self . session_info . clone ( )
60+ }
61+ }
62+
63+ const DATA_PROVIDER : & FakeDataProvider = & FakeDataProvider {
64+ session_info : SessionInfo {
65+ next_sender_seq_number : 3 ,
66+ next_target_seq_number : 5 ,
67+ status : Status :: AwaitingLogon ,
68+ } ,
69+ } ;
70+
71+ #[ tokio:: test]
72+ async fn test_get_health ( ) {
73+ let mut router = build_router_with_provider ( DATA_PROVIDER . clone ( ) ) ;
74+
75+ let response = router
76+ . call ( Request :: get ( "/api/health" ) . body :: < Body > ( "" . into ( ) ) . unwrap ( ) )
77+ . await
78+ . unwrap ( ) ;
79+
80+ assert_eq ! ( 200 , response. status( ) ) ;
81+ }
82+
83+ #[ tokio:: test]
84+ async fn test_get_session_info ( ) {
85+ let mut router = build_router_with_provider ( DATA_PROVIDER . clone ( ) ) ;
86+
87+ let response = router
88+ . call (
89+ Request :: get ( "/api/session-info" )
90+ . body :: < Body > ( "" . into ( ) )
91+ . unwrap ( ) ,
92+ )
93+ . await
94+ . unwrap ( ) ;
95+
96+ assert_eq ! ( 200 , response. status( ) ) ;
97+
98+ let body = to_bytes ( response. into_body ( ) , usize:: MAX ) . await . unwrap ( ) ;
99+ let parsed: Value = serde_json:: from_slice ( & body) . unwrap ( ) ;
100+
101+ let next_sender_seq_number = parsed
102+ . get ( "session_info" )
103+ . and_then ( |session_info| session_info. get ( "next_sender_seq_number" ) )
104+ . and_then ( |next_sender_seq_number| next_sender_seq_number. as_u64 ( ) )
105+ . unwrap ( ) ;
106+ assert_eq ! ( 3 , next_sender_seq_number) ;
107+
108+ let next_target_seq_number = parsed
109+ . get ( "session_info" )
110+ . and_then ( |session_info| session_info. get ( "next_target_seq_number" ) )
111+ . and_then ( |next_sender_seq_number| next_sender_seq_number. as_u64 ( ) )
112+ . unwrap ( ) ;
113+ assert_eq ! ( 5 , next_target_seq_number) ;
114+
115+ let status = parsed
116+ . get ( "session_info" )
117+ . and_then ( |session_info| session_info. get ( "status" ) )
118+ . and_then ( |status| status. as_str ( ) )
119+ . unwrap ( ) ;
120+ assert_eq ! ( "AwaitingLogon" , status) ;
121+ }
122+
123+ #[ cfg( feature = "ui" ) ]
124+ #[ tokio:: test]
125+ async fn test_get_dashboard ( ) {
126+ let mut router = build_router_with_provider ( DATA_PROVIDER . clone ( ) ) ;
127+ let response = router
128+ . call ( Request :: get ( "/" ) . body :: < Body > ( "" . into ( ) ) . unwrap ( ) )
129+ . await
130+ . unwrap ( ) ;
131+
132+ assert_eq ! ( 200 , response. status( ) ) ;
133+
134+ let headers = response. headers ( ) ;
135+ assert_eq ! (
136+ headers. get( "content-type" ) . unwrap( ) ,
137+ "text/html; charset=utf-8"
138+ ) ;
139+ }
140+
141+ #[ cfg( not( feature = "ui" ) ) ]
142+ #[ tokio:: test]
143+ async fn test_get_dashboard_without_ui_feature_returns_404 ( ) {
144+ let mut router = build_router_with_provider ( DATA_PROVIDER . clone ( ) ) ;
145+ let response = router
146+ . call ( Request :: get ( "/" ) . body :: < Body > ( "" . into ( ) ) . unwrap ( ) )
147+ . await
148+ . unwrap ( ) ;
149+
150+ assert_eq ! ( 404 , response. status( ) ) ;
151+ }
152+
153+ #[ cfg( feature = "ui" ) ]
154+ #[ tokio:: test]
155+ async fn test_get_static_assets ( ) {
156+ let mut router = build_router_with_provider ( DATA_PROVIDER . clone ( ) ) ;
157+ let response = router
158+ . call (
159+ Request :: get ( "/static/tailwind.js" )
160+ . body :: < Body > ( "" . into ( ) )
161+ . unwrap ( ) ,
162+ )
163+ . await
164+ . unwrap ( ) ;
165+
166+ assert_eq ! ( 200 , response. status( ) ) ;
167+
168+ let headers = response. headers ( ) ;
169+ let content_type = headers. get ( "content-type" ) . unwrap ( ) . to_str ( ) . unwrap ( ) ;
170+ assert ! (
171+ content_type. contains( "application/javascript" )
172+ || content_type. contains( "text/javascript" )
173+ || content_type. contains( "application/x-javascript" )
174+ ) ;
175+ }
176+ }
0 commit comments