Skip to content

Commit

Permalink
test: [torrust#109] add more E2E tests for categories
Browse files Browse the repository at this point in the history
  • Loading branch information
josecelano committed Apr 25, 2023
1 parent a2aba45 commit 2b6c04f
Show file tree
Hide file tree
Showing 4 changed files with 272 additions and 8 deletions.
4 changes: 4 additions & 0 deletions src/routes/category.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ pub async fn delete_category(
payload: web::Json<Category>,
app_data: WebAppData,
) -> ServiceResult<impl Responder> {
// code-review: why do we need to send the whole category object to delete it?
// And we should use the ID instead of the name, because the name could change
// or we could add support for multiple languages.

// check for user
let user = app_data.auth.get_user_compact_from_request(&req).await?;

Expand Down
49 changes: 43 additions & 6 deletions tests/e2e/client.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use reqwest::Response as ReqwestResponse;
use serde::Serialize;

use super::contexts::category::{AddCategoryForm, DeleteCategoryForm};
use super::contexts::user::{LoginForm, RegistrationForm, TokenRenewalForm, TokenVerificationForm, Username};
use crate::e2e::connection_info::ConnectionInfo;
use crate::e2e::http::{Query, ReqwestQuery};
Expand Down Expand Up @@ -34,6 +35,14 @@ impl Client {
self.http_client.get("category", Query::empty()).await
}

pub async fn add_category(&self, add_category_form: AddCategoryForm) -> Response {
self.http_client.post("category", &add_category_form).await
}

pub async fn delete_category(&self, delete_category_form: DeleteCategoryForm) -> Response {
self.http_client.delete_with_body("category", &delete_category_form).await
}

// Context: root

pub async fn root(&self) -> Response {
Expand Down Expand Up @@ -82,12 +91,21 @@ impl Http {
}

pub async fn post<T: Serialize + ?Sized>(&self, path: &str, form: &T) -> Response {
let response = reqwest::Client::new()
.post(self.base_url(path).clone())
.json(&form)
.send()
.await
.unwrap();
let response = match &self.connection_info.token {
Some(token) => reqwest::Client::new()
.post(self.base_url(path).clone())
.bearer_auth(token)
.json(&form)
.send()
.await
.unwrap(),
None => reqwest::Client::new()
.post(self.base_url(path).clone())
.json(&form)
.send()
.await
.unwrap(),
};
Response::from(response).await
}

Expand All @@ -108,6 +126,25 @@ impl Http {
Response::from(response).await
}

async fn delete_with_body<T: Serialize + ?Sized>(&self, path: &str, form: &T) -> Response {
let response = match &self.connection_info.token {
Some(token) => reqwest::Client::new()
.delete(self.base_url(path).clone())
.bearer_auth(token)
.json(&form)
.send()
.await
.unwrap(),
None => reqwest::Client::new()
.delete(self.base_url(path).clone())
.json(&form)
.send()
.await
.unwrap(),
};
Response::from(response).await
}

pub async fn get_request_with_query(&self, path: &str, params: Query) -> Response {
get(&self.base_url(path), Some(params)).await
}
Expand Down
226 changes: 224 additions & 2 deletions tests/e2e/contexts/category.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,39 @@
use serde::{Deserialize, Serialize};

use crate::e2e::asserts::assert_json_ok;
use crate::e2e::contexts::category::fixtures::{add_category, random_category_name};
use crate::e2e::contexts::user::fixtures::{logged_in_admin, logged_in_user};
use crate::e2e::environment::TestEnv;

// Request data

#[derive(Serialize)]
pub struct AddCategoryForm {
pub name: String,
pub icon: Option<String>,
}

pub type DeleteCategoryForm = AddCategoryForm;

// Response data

#[derive(Deserialize)]
pub struct AddedCategoryResponse {
pub data: String,
}

#[derive(Deserialize, Debug)]
pub struct ListResponse {
pub data: Vec<ListItem>,
}

#[derive(Deserialize, Debug, PartialEq)]
pub struct ListItem {
pub category_id: i64,
pub name: String,
pub num_torrents: i64,
}

#[tokio::test]
#[cfg_attr(not(feature = "e2e-tests"), ignore)]
async fn it_should_return_an_empty_category_list_when_there_are_no_categories() {
Expand All @@ -11,10 +44,199 @@ async fn it_should_return_an_empty_category_list_when_there_are_no_categories()
assert_json_ok(&response);
}

#[tokio::test]
#[cfg_attr(not(feature = "e2e-tests"), ignore)]
async fn it_should_return_a_category_list() {
// Add a category
let category_name = random_category_name();
let response = add_category(&category_name).await;
assert_eq!(response.status, 200);

let client = TestEnv::default().unauthenticated_client();

let response = client.get_categories().await;

let res: ListResponse = serde_json::from_str(&response.body).unwrap();

// There should be at least the category we added.
// Since this is an E2E test, there might be more categories.
assert!(res.data.len() > 1);
if let Some(content_type) = &response.content_type {
assert_eq!(content_type, "application/json");
}
assert_eq!(response.status, 200);
}

#[tokio::test]
#[cfg_attr(not(feature = "e2e-tests"), ignore)]
async fn it_should_not_allow_adding_a_new_category_to_unauthenticated_users() {
let client = TestEnv::default().unauthenticated_client();

let response = client
.add_category(AddCategoryForm {
name: "CATEGORY NAME".to_string(),
icon: None,
})
.await;

assert_eq!(response.status, 401);
}

#[tokio::test]
#[cfg_attr(not(feature = "e2e-tests"), ignore)]
async fn it_should_not_allow_adding_a_new_category_to_non_admins() {
let logged_non_admin = logged_in_user().await;
let client = TestEnv::default().authenticated_client(&logged_non_admin.token);

let response = client
.add_category(AddCategoryForm {
name: "CATEGORY NAME".to_string(),
icon: None,
})
.await;

assert_eq!(response.status, 403);
}

#[tokio::test]
#[cfg_attr(not(feature = "e2e-tests"), ignore)]
async fn it_should_allow_admins_to_add_new_categories() {
let logged_in_admin = logged_in_admin().await;
let client = TestEnv::default().authenticated_client(&logged_in_admin.token);

let category_name = random_category_name();

let response = client
.add_category(AddCategoryForm {
name: category_name.to_string(),
icon: None,
})
.await;

let res: AddedCategoryResponse = serde_json::from_str(&response.body).unwrap();

assert_eq!(res.data, category_name);
if let Some(content_type) = &response.content_type {
assert_eq!(content_type, "application/json");
}
assert_eq!(response.status, 200);
}

#[tokio::test]
#[cfg_attr(not(feature = "e2e-tests"), ignore)]
async fn it_should_not_allow_adding_duplicated_categories() {
// Add a category
let random_category_name = random_category_name();
let response = add_category(&random_category_name).await;
assert_eq!(response.status, 200);

// Try to add the same category again
let response = add_category(&random_category_name).await;
assert_eq!(response.status, 400);
}

#[tokio::test]
#[cfg_attr(not(feature = "e2e-tests"), ignore)]
async fn it_should_allow_admins_to_delete_categories() {
let logged_in_admin = logged_in_admin().await;
let client = TestEnv::default().authenticated_client(&logged_in_admin.token);

// Add a category
let category_name = random_category_name();
let response = add_category(&category_name).await;
assert_eq!(response.status, 200);

let response = client
.delete_category(DeleteCategoryForm {
name: category_name.to_string(),
icon: None,
})
.await;

let res: AddedCategoryResponse = serde_json::from_str(&response.body).unwrap();

assert_eq!(res.data, category_name);
if let Some(content_type) = &response.content_type {
assert_eq!(content_type, "application/json");
}
assert_eq!(response.status, 200);
}

#[tokio::test]
#[cfg_attr(not(feature = "e2e-tests"), ignore)]
async fn it_should_not_allow_non_admins_to_delete_categories() {
// Add a category
let category_name = random_category_name();
let response = add_category(&category_name).await;
assert_eq!(response.status, 200);

let logged_in_non_admin = logged_in_user().await;
let client = TestEnv::default().authenticated_client(&logged_in_non_admin.token);

let response = client
.delete_category(DeleteCategoryForm {
name: category_name.to_string(),
icon: None,
})
.await;

assert_eq!(response.status, 403);
}

#[tokio::test]
#[cfg_attr(not(feature = "e2e-tests"), ignore)]
async fn it_should_not_allow_guests_to_delete_categories() {
// Add a category
let category_name = random_category_name();
let response = add_category(&category_name).await;
assert_eq!(response.status, 200);

let client = TestEnv::default().unauthenticated_client();

let response = client
.delete_category(DeleteCategoryForm {
name: category_name.to_string(),
icon: None,
})
.await;

assert_eq!(response.status, 401);
}

/* todo:
- it_should_not_allow_adding_a_new_category_to_unauthenticated_clients
- it should allow adding a new category to authenticated clients
- it should not allow adding a new category with an empty name
- it should not allow adding a new category with an empty icon
- it should allow adding a new category with an optional icon
- ...
*/

pub mod fixtures {

use rand::Rng;

use super::AddCategoryForm;
use crate::e2e::contexts::user::fixtures::logged_in_admin;
use crate::e2e::environment::TestEnv;
use crate::e2e::response::Response;

pub async fn add_category(category_name: &str) -> Response {
let logged_in_admin = logged_in_admin().await;
let client = TestEnv::default().authenticated_client(&logged_in_admin.token);

client
.add_category(AddCategoryForm {
name: category_name.to_string(),
icon: None,
})
.await
}

pub fn random_category_name() -> String {
format!("category name {}", random_id())
}

fn random_id() -> u64 {
let mut rng = rand::thread_rng();
rng.gen_range(0..1_000_000)
}
}
1 change: 1 addition & 0 deletions tests/e2e/response.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use reqwest::Response as ReqwestResponse;

#[derive(Debug)]
pub struct Response {
pub status: u16,
pub content_type: Option<String>,
Expand Down

0 comments on commit 2b6c04f

Please sign in to comment.