Skip to content

Commit 3f0dcea

Browse files
committed
dev: add tests to health check
1 parent b310c75 commit 3f0dcea

File tree

30 files changed

+1182
-963
lines changed

30 files changed

+1182
-963
lines changed

src/bootstrap/jobs/health_check_api.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ use torrust_tracker_configuration::HealthCheckApi;
2222
use super::Started;
2323
use crate::servers::health_check_api::server;
2424
use crate::servers::registar::ServiceRegistry;
25+
use crate::servers::signals::Halted;
2526

2627
/// This function starts a new Health Check API server with the provided
2728
/// configuration.
@@ -40,12 +41,14 @@ pub async fn start_job(config: &HealthCheckApi, register: ServiceRegistry) -> Jo
4041
.expect("it should have a valid health check bind address");
4142

4243
let (tx_start, rx_start) = oneshot::channel::<Started>();
44+
let (tx_halt, rx_halt) = tokio::sync::oneshot::channel::<Halted>();
45+
drop(tx_halt);
4346

4447
// Run the API server
4548
let join_handle = tokio::spawn(async move {
4649
info!(target: "Health Check API", "Starting on: http://{}", bind_addr);
4750

48-
let handle = server::start(bind_addr, tx_start, register);
51+
let handle = server::start(bind_addr, tx_start, rx_halt, register);
4952

5053
if let Ok(()) = handle.await {
5154
info!(target: "Health Check API", "Stopped server running on: http://{}", bind_addr);

src/servers/health_check_api/handlers.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,11 @@ pub(crate) async fn health_check_handler(State(register): State<ServiceRegistry>
2121
checks = mutex.await.values().map(ServiceRegistration::spawn_check).collect();
2222
}
2323

24+
// if we do not have any checks, lets return a `none` result.
25+
if checks.is_empty() {
26+
return responses::none();
27+
}
28+
2429
let jobs = checks.drain(..).map(|c| {
2530
tokio::spawn(async move {
2631
CheckReport {

src/servers/health_check_api/resources.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use serde::{Deserialize, Serialize};
66
pub enum Status {
77
Ok,
88
Error,
9+
None,
910
}
1011

1112
#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq)]
@@ -34,6 +35,15 @@ pub struct Report {
3435
}
3536

3637
impl Report {
38+
#[must_use]
39+
pub fn none() -> Report {
40+
Self {
41+
status: Status::None,
42+
message: String::new(),
43+
details: Vec::default(),
44+
}
45+
}
46+
3747
#[must_use]
3848
pub fn ok(details: Vec<CheckReport>) -> Report {
3949
Self {

src/servers/health_check_api/responses.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,7 @@ pub fn ok(details: Vec<CheckReport>) -> Json<Report> {
99
pub fn error(message: String, details: Vec<CheckReport>) -> Json<Report> {
1010
Json(Report::error(message, details))
1111
}
12+
13+
pub fn none() -> Json<Report> {
14+
Json(Report::none())
15+
}

src/servers/health_check_api/server.rs

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,44 +8,44 @@ use axum::routing::get;
88
use axum::{Json, Router};
99
use axum_server::Handle;
1010
use futures::Future;
11-
use log::info;
1211
use serde_json::json;
13-
use tokio::sync::oneshot::Sender;
12+
use tokio::sync::oneshot::{Receiver, Sender};
1413

1514
use crate::bootstrap::jobs::Started;
1615
use crate::servers::health_check_api::handlers::health_check_handler;
1716
use crate::servers::registar::ServiceRegistry;
17+
use crate::servers::signals::{graceful_shutdown, Halted};
1818

1919
/// Starts Health Check API server.
2020
///
2121
/// # Panics
2222
///
2323
/// Will panic if binding to the socket address fails.
2424
pub fn start(
25-
address: SocketAddr,
25+
bind_to: SocketAddr,
2626
tx: Sender<Started>,
27+
rx_halt: Receiver<Halted>,
2728
register: ServiceRegistry,
2829
) -> impl Future<Output = Result<(), std::io::Error>> {
29-
let app = Router::new()
30+
let router = Router::new()
3031
.route("/", get(|| async { Json(json!({})) }))
3132
.route("/health_check", get(health_check_handler))
3233
.with_state(register);
3334

34-
let handle = Handle::new();
35-
let cloned_handle = handle.clone();
36-
37-
let socket = std::net::TcpListener::bind(address).expect("Could not bind tcp_listener to address.");
35+
let socket = std::net::TcpListener::bind(bind_to).expect("Could not bind tcp_listener to address.");
3836
let address = socket.local_addr().expect("Could not get local_addr from tcp_listener.");
3937

40-
tokio::task::spawn(async move {
41-
tokio::signal::ctrl_c().await.expect("Failed to listen to shutdown signal.");
42-
info!("Stopping Torrust Health Check API server o http://{} ...", address);
43-
cloned_handle.shutdown();
44-
});
38+
let handle = Handle::new();
39+
40+
tokio::task::spawn(graceful_shutdown(
41+
handle.clone(),
42+
rx_halt,
43+
format!("shutting down http server on socket address: {address}"),
44+
));
4545

4646
let running = axum_server::from_tcp(socket)
4747
.handle(handle)
48-
.serve(app.into_make_service_with_connect_info::<SocketAddr>());
48+
.serve(router.into_make_service_with_connect_info::<SocketAddr>());
4949

5050
tx.send(Started { address })
5151
.expect("the Health Check API server should not be dropped");

src/shared/bit_torrent/tracker/udp/client.rs

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
use std::io::Cursor;
22
use std::net::SocketAddr;
33
use std::sync::Arc;
4+
use std::time::Duration;
45

56
use aquatic_udp_protocol::{ConnectRequest, Request, Response, TransactionId};
67
use tokio::net::UdpSocket;
8+
use tokio::time;
79

810
use crate::shared::bit_torrent::tracker::udp::{source_address, MAX_PACKET_SIZE};
911

@@ -112,6 +114,8 @@ pub async fn new_udp_tracker_client_connected(remote_address: &str) -> UdpTracke
112114
/// # Errors
113115
///
114116
/// It will return an error if unable to connect to the UDP service.
117+
///
118+
/// # Panics
115119
pub async fn check(binding: &SocketAddr) -> Result<String, String> {
116120
let client = new_udp_tracker_client_connected(binding.to_string().as_str()).await;
117121

@@ -121,11 +125,23 @@ pub async fn check(binding: &SocketAddr) -> Result<String, String> {
121125

122126
client.send(connect_request.into()).await;
123127

124-
let response = client.receive().await;
128+
let process = move |response| {
129+
if matches!(response, Response::Connect(_connect_response)) {
130+
Ok("Connected".to_string())
131+
} else {
132+
Err("Did not Connect".to_string())
133+
}
134+
};
135+
136+
let sleep = time::sleep(Duration::from_millis(2000));
137+
tokio::pin!(sleep);
125138

126-
if matches!(response, Response::Connect(_connect_response)) {
127-
Ok("Connected".to_string())
128-
} else {
129-
Err("Did not Connect".to_string())
139+
tokio::select! {
140+
() = &mut sleep => {
141+
Err("Timed Out".to_string())
142+
}
143+
response = client.receive() => {
144+
process(response)
145+
}
130146
}
131147
}

tests/common/app.rs

Lines changed: 0 additions & 8 deletions
This file was deleted.

tests/common/mod.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
pub mod app;
21
pub mod fixtures;
32
pub mod http;
43
pub mod udp;

tests/servers/api/environment.rs

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
use std::net::SocketAddr;
2+
use std::sync::Arc;
3+
4+
use futures::executor::block_on;
5+
use torrust_tracker::bootstrap::app::initialize_with_configuration;
6+
use torrust_tracker::bootstrap::jobs::make_rust_tls;
7+
use torrust_tracker::core::peer::Peer;
8+
use torrust_tracker::core::Tracker;
9+
use torrust_tracker::servers::apis::server::{ApiServer, Launcher, Running, Stopped};
10+
use torrust_tracker::servers::registar::Registar;
11+
use torrust_tracker::shared::bit_torrent::info_hash::InfoHash;
12+
use torrust_tracker_configuration::{Configuration, HttpApi};
13+
14+
use super::connection_info::ConnectionInfo;
15+
16+
pub struct Environment<S> {
17+
pub config: Arc<HttpApi>,
18+
pub tracker: Arc<Tracker>,
19+
pub registar: Registar,
20+
pub server: ApiServer<S>,
21+
}
22+
23+
impl<S> Environment<S> {
24+
/// Add a torrent to the tracker
25+
pub async fn add_torrent_peer(&self, info_hash: &InfoHash, peer: &Peer) {
26+
self.tracker.update_torrent_with_peer_and_get_stats(info_hash, peer).await;
27+
}
28+
}
29+
30+
impl Environment<Stopped> {
31+
pub fn new(configuration: &Arc<Configuration>) -> Self {
32+
let tracker = initialize_with_configuration(configuration);
33+
34+
let config = Arc::new(configuration.http_api.clone());
35+
36+
let bind_to = config
37+
.bind_address
38+
.parse::<std::net::SocketAddr>()
39+
.expect("Tracker API bind_address invalid.");
40+
41+
let tls = block_on(make_rust_tls(config.ssl_enabled, &config.ssl_cert_path, &config.ssl_key_path))
42+
.map(|tls| tls.expect("tls config failed"));
43+
44+
let server = ApiServer::new(Launcher::new(bind_to, tls));
45+
46+
Self {
47+
config,
48+
tracker,
49+
registar: Registar::default(),
50+
server,
51+
}
52+
}
53+
54+
pub async fn start(self) -> Environment<Running> {
55+
let access_tokens = Arc::new(self.config.access_tokens.clone());
56+
57+
Environment {
58+
config: self.config,
59+
tracker: self.tracker.clone(),
60+
registar: self.registar.clone(),
61+
server: self
62+
.server
63+
.start(self.tracker, self.registar.give_form(), access_tokens)
64+
.await
65+
.unwrap(),
66+
}
67+
}
68+
}
69+
70+
impl Environment<Running> {
71+
pub async fn new(configuration: &Arc<Configuration>) -> Self {
72+
Environment::<Stopped>::new(configuration).start().await
73+
}
74+
75+
pub async fn stop(self) -> Environment<Stopped> {
76+
Environment {
77+
config: self.config,
78+
tracker: self.tracker,
79+
registar: Registar::default(),
80+
server: self.server.stop().await.unwrap(),
81+
}
82+
}
83+
84+
pub fn get_connection_info(&self) -> ConnectionInfo {
85+
ConnectionInfo {
86+
bind_address: self.server.state.binding.to_string(),
87+
api_token: self.config.access_tokens.get("admin").cloned(),
88+
}
89+
}
90+
91+
pub fn bind_address(&self) -> SocketAddr {
92+
self.server.state.binding
93+
}
94+
}

tests/servers/api/mod.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
use std::sync::Arc;
22

33
use torrust_tracker::core::Tracker;
4+
use torrust_tracker::servers::apis::server;
45

56
pub mod connection_info;
6-
pub mod test_environment;
7+
pub mod environment;
78
pub mod v1;
89

10+
pub type Started = environment::Environment<server::Running>;
11+
912
/// It forces a database error by dropping all tables.
1013
/// That makes any query fail.
1114
/// code-review: alternatively we could inject a database mock in the future.

0 commit comments

Comments
 (0)