Skip to content

Commit

Permalink
refactor(api): [#143] extract mods for API testing
Browse files Browse the repository at this point in the history
Code for API testing have been reorganized.
  • Loading branch information
josecelano committed Jan 2, 2023
1 parent cbf8837 commit 5ee3f93
Show file tree
Hide file tree
Showing 11 changed files with 397 additions and 374 deletions.
2 changes: 1 addition & 1 deletion src/apis/mod.rs
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
pub mod server;
pub mod routes;
pub mod server;
2 changes: 2 additions & 0 deletions src/apis/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ pub fn start_tls(
_ssl_key_path: &str,
_tracker: &Arc<tracker::Tracker>,
) -> impl Future<Output = hyper::Result<()>> {
// todo: for the time being, it's just a copy & paste from start(...).

let app = Router::new().route("/", get(root));

let server = axum::Server::bind(&socket_addr).serve(app.into_make_service());
Expand Down
2 changes: 1 addition & 1 deletion src/jobs/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
pub mod http_tracker;
pub mod torrent_cleanup;
pub mod tracker_api;
pub mod udp_tracker;
pub mod tracker_apis;
pub mod udp_tracker;
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
pub mod api;
pub mod apis;
pub mod config;
pub mod databases;
pub mod http;
Expand All @@ -9,7 +10,6 @@ pub mod setup;
pub mod stats;
pub mod tracker;
pub mod udp;
pub mod apis;

#[macro_use]
extern crate lazy_static;
Expand Down
17 changes: 17 additions & 0 deletions tests/api/asserts.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
use reqwest::Response;

pub async fn assert_token_not_valid(response: Response) {
assert_eq!(response.status(), 500);
assert_eq!(
response.text().await.unwrap(),
"Unhandled rejection: Err { reason: \"token not valid\" }"
);
}

pub async fn assert_unauthorized(response: Response) {
assert_eq!(response.status(), 500);
assert_eq!(
response.text().await.unwrap(),
"Unhandled rejection: Err { reason: \"unauthorized\" }"
);
}
162 changes: 162 additions & 0 deletions tests/api/client.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
use reqwest::Response;

use super::connection_info::ConnectionInfo;
use super::Version;

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, version: &Version) -> Self {
Self {
connection_info,
base_path: match version {
Version::Warp => "/api/".to_string(),
Version::Axum => "/".to_string(),
},
}
}

pub async fn generate_auth_key(&self, seconds_valid: i32) -> Response {
self.post(&format!("key/{}", &seconds_valid)).await
}

pub async fn delete_auth_key(&self, key: &str) -> Response {
self.delete(&format!("key/{}", &key)).await
}

pub async fn reload_keys(&self) -> Response {
self.get("keys/reload", Query::default()).await
}

pub async fn whitelist_a_torrent(&self, info_hash: &str) -> Response {
self.post(&format!("whitelist/{}", &info_hash)).await
}

pub async fn remove_torrent_from_whitelist(&self, info_hash: &str) -> Response {
self.delete(&format!("whitelist/{}", &info_hash)).await
}

pub async fn reload_whitelist(&self) -> Response {
self.get("whitelist/reload", Query::default()).await
}

pub async fn get_torrent(&self, info_hash: &str) -> Response {
self.get(&format!("torrent/{}", &info_hash), Query::default()).await
}

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

pub async fn get_tracker_statistics(&self) -> Response {
self.get("stats", Query::default()).await
}

pub async fn get(&self, path: &str, params: Query) -> Response {
let mut query: Query = params;

if let Some(token) = &self.connection_info.api_token {
query.add_param(QueryParam::new("token", token));
};

reqwest::Client::builder()
.build()
.unwrap()
.get(self.base_url(path))
.query(&ReqwestQuery::from(query))
.send()
.await
.unwrap()
}

async fn post(&self, path: &str) -> Response {
reqwest::Client::new()
.post(self.base_url(path).clone())
.query(&ReqwestQuery::from(self.query_with_token()))
.send()
.await
.unwrap()
}

async fn delete(&self, path: &str) -> Response {
reqwest::Client::new()
.delete(self.base_url(path).clone())
.query(&ReqwestQuery::from(self.query_with_token()))
.send()
.await
.unwrap()
}

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

fn query_with_token(&self) -> Query {
match &self.connection_info.api_token {
Some(token) => Query::with_token(token),
None => Query::default(),
}
}
}
29 changes: 29 additions & 0 deletions tests/api/connection_info.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
pub fn connection_with_invalid_token(bind_address: &str) -> ConnectionInfo {
ConnectionInfo::authenticated(bind_address, "invalid token")
}

pub fn connection_with_no_token(bind_address: &str) -> ConnectionInfo {
ConnectionInfo::anonymous(bind_address)
}

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

impl ConnectionInfo {
pub fn authenticated(bind_address: &str, api_token: &str) -> Self {
Self {
bind_address: bind_address.to_string(),
api_token: Some(api_token.to_string()),
}
}

pub fn anonymous(bind_address: &str) -> Self {
Self {
bind_address: bind_address.to_string(),
api_token: None,
}
}
}
17 changes: 17 additions & 0 deletions tests/api/fixtures.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
use std::net::{IpAddr, Ipv4Addr, SocketAddr};

use aquatic_udp_protocol::{AnnounceEvent, NumberOfBytes};
use torrust_tracker::protocol::clock::DurationSinceUnixEpoch;
use torrust_tracker::tracker::peer;

pub fn sample_peer() -> peer::Peer {
peer::Peer {
peer_id: peer::Id(*b"-qB00000000000000000"),
peer_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(126, 0, 0, 1)), 8080),
updated: DurationSinceUnixEpoch::new(1_669_397_478_934, 0),
uploaded: NumberOfBytes(0),
downloaded: NumberOfBytes(0),
left: NumberOfBytes(0),
event: AnnounceEvent::Started,
}
}
Loading

0 comments on commit 5ee3f93

Please sign in to comment.