Skip to content

Commit 0d97bdb

Browse files
authored
chore: test cases for hotfix-status (#179)
* Add test case for hotfix-status API endpoints * Add test cases for UI views of hotfix-status
1 parent fdb032d commit 0d97bdb

4 files changed

Lines changed: 156 additions & 6 deletions

File tree

Cargo.lock

Lines changed: 5 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,12 @@ displaydoc = "0.2"
2727
mime_guess = "2.0.5"
2828
rust-embed = "8.7"
2929
serde = "^1.0.177"
30+
serde_json = "1.0.143"
3031
thiserror = "1"
3132
tokio = { version = "^1" }
3233
tokio-rustls = "^0.26"
3334
toml = "^0.8.8"
35+
tower = "0.5"
3436
tracing = "^0.1.37"
3537

3638
[workspace.lints.rust]

crates/hotfix-status/Cargo.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,8 @@ mime_guess = { workspace = true, optional = true }
2828
rust-embed = { workspace = true, features = ["axum-ex"], optional = true }
2929
serde = { workspace = true, features = ["derive"] }
3030
thiserror = { workspace = true }
31+
32+
[dev-dependencies]
33+
serde_json = { workspace = true }
34+
tokio = { workspace = true, features = ["full"] }
35+
tower = { workspace = true }

crates/hotfix-status/src/lib.rs

Lines changed: 144 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ mod error;
66
mod ui;
77

88
use crate::api::build_api_router;
9-
use crate::data_provider::SessionDataProvider;
9+
use crate::data_provider::{DataProvider, SessionDataProvider};
1010
use axum::Router;
1111
use hotfix::message::FixMessage;
1212
use hotfix::session::SessionRef;
@@ -16,9 +16,13 @@ struct AppState<P> {
1616
data_provider: P,
1717
}
1818

19-
#[cfg(feature = "ui")]
2019
pub 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

Comments
 (0)