Skip to content

Commit

Permalink
test(http): [#159] HTTP tracker tests scaffolding
Browse files Browse the repository at this point in the history
  • Loading branch information
josecelano committed Jan 17, 2023
1 parent c26b356 commit 3449202
Show file tree
Hide file tree
Showing 11 changed files with 276 additions and 87 deletions.
20 changes: 16 additions & 4 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,27 +84,39 @@ pub enum Error {
}

/// This configuration is used for testing. It generates random config values so they do not collide
/// if you run more than one tracker at the same time.
/// if you run more than one tracker at the same time.
///
/// # Panics
///
/// Will panic if it can't convert the temp file path to string
#[must_use]
pub fn ephemeral_configuration() -> Configuration {
// todo: disable services that are not needed.
// For example: a test for the UDP tracker should disable the API and HTTP tracker.

let mut config = Configuration {
log_level: Some("off".to_owned()),
log_level: Some("off".to_owned()), // Change to `debug` for tests debugging
..Default::default()
};

// Ephemeral socket addresses
// Ephemeral socket address for API
let api_port = random_port();
config.http_api.enabled = true;
config.http_api.bind_address = format!("127.0.0.1:{}", &api_port);

// Ephemeral socket address for UDP tracker
let upd_port = random_port();
config.udp_trackers[0].enabled = true;
config.udp_trackers[0].bind_address = format!("127.0.0.1:{}", &upd_port);

// Ephemeral socket address for HTTP tracker
let http_port = random_port();
config.http_trackers[0].enabled = true;
config.http_trackers[0].bind_address = format!("127.0.0.1:{}", &http_port);

// Ephemeral sqlite database
let temp_directory = env::temp_dir();
let temp_file = temp_directory.join(format!("data_{}_{}.db", &api_port, &upd_port));
let temp_file = temp_directory.join(format!("data_{}_{}_{}.db", &api_port, &upd_port, &http_port));
config.db_path = temp_file.to_str().unwrap().to_owned();

config
Expand Down
90 changes: 9 additions & 81 deletions tests/api/client.rs
Original file line number Diff line number Diff line change
@@ -1,71 +1,14 @@
use reqwest::Response;

use super::connection_info::ConnectionInfo;
use crate::common::http::{get, Query, QueryParam, ReqwestQuery};

/// API Client
pub struct Client {
connection_info: ConnectionInfo,
base_path: String,
}

type ReqwestQuery = Vec<ReqwestQueryParam>;
type ReqwestQueryParam = (String, String);

#[derive(Default, Debug)]
pub struct Query {
params: Vec<QueryParam>,
}

impl Query {
pub fn empty() -> Self {
Self { params: vec![] }
}

pub fn params(params: Vec<QueryParam>) -> Self {
Self { params }
}

pub fn add_param(&mut self, param: QueryParam) {
self.params.push(param);
}

fn with_token(token: &str) -> Self {
Self {
params: vec![QueryParam::new("token", token)],
}
}
}

impl From<Query> for ReqwestQuery {
fn from(url_search_params: Query) -> Self {
url_search_params
.params
.iter()
.map(|param| ReqwestQueryParam::from((*param).clone()))
.collect()
}
}

#[derive(Clone, Debug)]
pub struct QueryParam {
name: String,
value: String,
}

impl QueryParam {
pub fn new(name: &str, value: &str) -> Self {
Self {
name: name.to_string(),
value: value.to_string(),
}
}
}

impl From<QueryParam> for ReqwestQueryParam {
fn from(param: QueryParam) -> Self {
(param.name, param.value)
}
}

impl Client {
pub fn new(connection_info: ConnectionInfo) -> Self {
Self {
Expand Down Expand Up @@ -138,37 +81,22 @@ impl Client {
.unwrap()
}

fn base_url(&self, path: &str) -> String {
format!("http://{}{}{path}", &self.connection_info.bind_address, &self.base_path)
}

// Unauthenticated GET request with query component
pub async fn get_request_with_query(&self, path: &str, params: Query) -> Response {
reqwest::Client::builder()
.build()
.unwrap()
.get(self.base_url(path))
.query(&ReqwestQuery::from(params))
.send()
.await
.unwrap()
get(&self.base_url(path), Some(params)).await
}

// Unauthenticated GET request
pub async fn get_request(&self, path: &str) -> Response {
reqwest::Client::builder()
.build()
.unwrap()
.get(self.base_url(path))
.send()
.await
.unwrap()
get(&self.base_url(path), None).await
}

fn query_with_token(&self) -> Query {
match &self.connection_info.api_token {
Some(token) => Query::with_token(token),
Some(token) => Query::params([QueryParam::new("token", token)].to_vec()),
None => Query::default(),
}
}

fn base_url(&self, path: &str) -> String {
format!("http://{}{}{path}", &self.connection_info.bind_address, &self.base_path)
}
}
75 changes: 75 additions & 0 deletions tests/common/http.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
use reqwest::Response;

pub type ReqwestQuery = Vec<ReqwestQueryParam>;
pub type ReqwestQueryParam = (String, String);

pub async fn get(path: &str, query: Option<Query>) -> Response {
match query {
Some(params) => reqwest::Client::builder()
.build()
.unwrap()
.get(path)
.query(&ReqwestQuery::from(params))
.send()
.await
.unwrap(),
None => reqwest::Client::builder().build().unwrap().get(path).send().await.unwrap(),
}
}

#[derive(Clone, Debug)]
pub struct ConnectionInfo {
pub bind_address: String,
}

/// URL Query component
#[derive(Default, Debug)]
pub struct Query {
params: Vec<QueryParam>,
}

impl Query {
pub fn empty() -> Self {
Self { params: vec![] }
}

pub fn params(params: Vec<QueryParam>) -> Self {
Self { params }
}

pub fn add_param(&mut self, param: QueryParam) {
self.params.push(param);
}
}

impl From<Query> for ReqwestQuery {
fn from(url_search_params: Query) -> Self {
url_search_params
.params
.iter()
.map(|param| ReqwestQueryParam::from((*param).clone()))
.collect()
}
}

/// URL query param
#[derive(Clone, Debug)]
pub struct QueryParam {
name: String,
value: String,
}

impl QueryParam {
pub fn new(name: &str, value: &str) -> Self {
Self {
name: name.to_string(),
value: value.to_string(),
}
}
}

impl From<QueryParam> for ReqwestQueryParam {
fn from(param: QueryParam) -> Self {
(param.name, param.value)
}
}
1 change: 1 addition & 0 deletions tests/common/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub mod http;
7 changes: 7 additions & 0 deletions tests/http/asserts.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
use reqwest::Response;

pub async fn assert_internal_server_error(response: Response) {
assert_eq!(response.status(), 200);
/* cspell:disable-next-line */
assert_eq!(response.text().await.unwrap(), "d14:failure reason21:internal server errore");
}
35 changes: 35 additions & 0 deletions tests/http/client.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
use reqwest::Response;

use super::connection_info::ConnectionInfo;
use crate::common::http::{get, Query};

/// HTTP Tracker Client
pub struct Client {
connection_info: ConnectionInfo,
base_path: String,
}

impl Client {
pub fn new(connection_info: ConnectionInfo) -> Self {
Self {
connection_info,
base_path: "/".to_string(),
}
}

pub async fn announce(&self, params: Query) -> Response {
self.get("announce", params).await
}

pub async fn scrape(&self, params: Query) -> Response {
self.get("scrape", params).await
}

async fn get(&self, path: &str, params: Query) -> Response {
get(&self.base_url(path), Some(params)).await
}

fn base_url(&self, path: &str) -> String {
format!("http://{}{}{path}", &self.connection_info.bind_address, &self.base_path)
}
}
16 changes: 16 additions & 0 deletions tests/http/connection_info.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
use torrust_tracker::tracker::auth::Key;

#[derive(Clone, Debug)]
pub struct ConnectionInfo {
pub bind_address: String,
pub aut_key: Option<Key>,
}

impl ConnectionInfo {
pub fn anonymous(bind_address: &str) -> Self {
Self {
bind_address: bind_address.to_string(),
aut_key: None,
}
}
}
4 changes: 4 additions & 0 deletions tests/http/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
pub mod asserts;
pub mod client;
pub mod connection_info;
pub mod server;
64 changes: 64 additions & 0 deletions tests/http/server.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
use core::panic;
use std::sync::Arc;

use torrust_tracker::config::{ephemeral_configuration, Configuration};
use torrust_tracker::jobs::http_tracker;
use torrust_tracker::tracker::statistics::Keeper;
use torrust_tracker::{ephemeral_instance_keys, logging, static_time, tracker};

use super::connection_info::ConnectionInfo;

pub fn tracker_configuration() -> Arc<Configuration> {
Arc::new(ephemeral_configuration())
}

pub async fn start_default_http_tracker() -> Server {
let configuration = tracker_configuration();
start_custom_http_tracker(configuration.clone()).await
}

pub async fn start_custom_http_tracker(configuration: Arc<Configuration>) -> Server {
let server = start(&configuration);
http_tracker::start_job(&configuration.http_trackers[0], server.tracker.clone()).await;
server
}

fn start(configuration: &Arc<Configuration>) -> Server {
let connection_info = ConnectionInfo::anonymous(&configuration.http_trackers[0].bind_address.clone());

// Set the time of Torrust app starting
lazy_static::initialize(&static_time::TIME_AT_APP_START);

// Initialize the Ephemeral Instance Random Seed
lazy_static::initialize(&ephemeral_instance_keys::RANDOM_SEED);

// Initialize stats tracker
let (stats_event_sender, stats_repository) = Keeper::new_active_instance();

// Initialize Torrust tracker
let tracker = match tracker::Tracker::new(configuration, Some(stats_event_sender), stats_repository) {
Ok(tracker) => Arc::new(tracker),
Err(error) => {
panic!("{}", error)
}
};

// Initialize logging
logging::setup(configuration);

Server {
tracker,
connection_info,
}
}

pub struct Server {
pub tracker: Arc<tracker::Tracker>,
pub connection_info: ConnectionInfo,
}

impl Server {
pub fn get_connection_info(&self) -> ConnectionInfo {
self.connection_info.clone()
}
}
Loading

0 comments on commit 3449202

Please sign in to comment.