Skip to content

Commit b97698a

Browse files
committed
test(api): [#187] add tests for new 'tag' context
1 parent e766b4c commit b97698a

File tree

12 files changed

+297
-2
lines changed

12 files changed

+297
-2
lines changed

src/databases/mysql.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -778,7 +778,7 @@ impl Database for Mysql {
778778
.bind(name)
779779
.fetch_one(&self.pool)
780780
.await
781-
.map_err(|err| database::Error::TagNotFound)
781+
.map_err(|_| database::Error::TagNotFound)
782782
}
783783

784784
async fn get_tags(&self) -> Result<Vec<TorrentTag>, database::Error> {

src/databases/sqlite.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -768,7 +768,7 @@ impl Database for Sqlite {
768768
.bind(name)
769769
.fetch_one(&self.pool)
770770
.await
771-
.map_err(|err| database::Error::TagNotFound)
771+
.map_err(|_| database::Error::TagNotFound)
772772
}
773773

774774
async fn get_tags(&self) -> Result<Vec<TorrentTag>, database::Error> {

tests/common/client.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use serde::Serialize;
44
use super::connection_info::ConnectionInfo;
55
use super::contexts::category::forms::{AddCategoryForm, DeleteCategoryForm};
66
use super::contexts::settings::form::UpdateSettings;
7+
use super::contexts::tag::forms::{AddTagForm, DeleteTagForm};
78
use super::contexts::torrent::forms::UpdateTorrentFrom;
89
use super::contexts::torrent::requests::InfoHash;
910
use super::contexts::user::forms::{LoginForm, RegistrationForm, TokenRenewalForm, TokenVerificationForm, Username};
@@ -67,6 +68,22 @@ impl Client {
6768
self.http_client.delete_with_body("/category", &delete_category_form).await
6869
}
6970

71+
// Context: tag
72+
73+
pub async fn get_tags(&self) -> TextResponse {
74+
// code-review: some endpoint are using plural
75+
// (for instance, `get_categories`) and some singular.
76+
self.http_client.get("/tags", Query::empty()).await
77+
}
78+
79+
pub async fn add_tag(&self, add_tag_form: AddTagForm) -> TextResponse {
80+
self.http_client.post("/tag", &add_tag_form).await
81+
}
82+
83+
pub async fn delete_tag(&self, delete_tag_form: DeleteTagForm) -> TextResponse {
84+
self.http_client.delete_with_body("/tag", &delete_tag_form).await
85+
}
86+
7087
// Context: root
7188

7289
pub async fn root(&self) -> TextResponse {

tests/common/contexts/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,6 @@ pub mod about;
22
pub mod category;
33
pub mod root;
44
pub mod settings;
5+
pub mod tag;
56
pub mod torrent;
67
pub mod user;
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
use rand::Rng;
2+
3+
pub fn random_tag_name() -> String {
4+
format!("category name {}", random_id())
5+
}
6+
7+
fn random_id() -> u64 {
8+
let mut rng = rand::thread_rng();
9+
rng.gen_range(0..1_000_000)
10+
}

tests/common/contexts/tag/forms.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
use serde::Serialize;
2+
3+
#[derive(Serialize)]
4+
pub struct AddTagForm {
5+
pub name: String,
6+
}
7+
8+
#[derive(Serialize)]
9+
pub struct DeleteTagForm {
10+
pub tag_id: i64,
11+
}

tests/common/contexts/tag/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
pub mod fixtures;
2+
pub mod forms;
3+
pub mod responses;
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
use serde::Deserialize;
2+
3+
#[derive(Deserialize)]
4+
pub struct AddedTagResponse {
5+
pub data: String,
6+
}
7+
8+
#[derive(Deserialize)]
9+
pub struct DeletedTagResponse {
10+
pub data: i64, // tag_id
11+
}
12+
13+
#[derive(Deserialize, Debug)]
14+
pub struct ListResponse {
15+
pub data: Vec<ListItem>,
16+
}
17+
18+
impl ListResponse {
19+
pub fn find_tag_id(&self, tag_name: &str) -> i64 {
20+
self.data.iter().find(|tag| tag.name == tag_name).unwrap().tag_id
21+
}
22+
}
23+
24+
#[derive(Deserialize, Debug, PartialEq)]
25+
pub struct ListItem {
26+
pub tag_id: i64,
27+
pub name: String,
28+
}

tests/e2e/contexts/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,6 @@ pub mod about;
22
pub mod category;
33
pub mod root;
44
pub mod settings;
5+
pub mod tag;
56
pub mod torrent;
67
pub mod user;

tests/e2e/contexts/tag/contract.rs

Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
//! API contract for `tag` context.
2+
use torrust_index_backend::web::api;
3+
4+
use crate::common::asserts::assert_json_ok;
5+
use crate::common::client::Client;
6+
use crate::common::contexts::tag::fixtures::random_tag_name;
7+
use crate::common::contexts::tag::forms::{AddTagForm, DeleteTagForm};
8+
use crate::common::contexts::tag::responses::{AddedTagResponse, DeletedTagResponse, ListResponse};
9+
use crate::e2e::contexts::tag::steps::{add_random_tag, add_tag};
10+
use crate::e2e::contexts::user::steps::{new_logged_in_admin, new_logged_in_user};
11+
use crate::e2e::environment::TestEnv;
12+
13+
#[tokio::test]
14+
async fn it_should_return_an_empty_tag_list_when_there_are_no_tags() {
15+
let mut env = TestEnv::new();
16+
env.start(api::Implementation::ActixWeb).await;
17+
let client = Client::unauthenticated(&env.server_socket_addr().unwrap());
18+
19+
let response = client.get_tags().await;
20+
21+
assert_json_ok(&response);
22+
}
23+
24+
#[tokio::test]
25+
async fn it_should_return_a_tag_list() {
26+
let mut env = TestEnv::new();
27+
env.start(api::Implementation::ActixWeb).await;
28+
let client = Client::unauthenticated(&env.server_socket_addr().unwrap());
29+
30+
// Add a tag
31+
let tag_name = random_tag_name();
32+
let response = add_tag(&tag_name, &env).await;
33+
assert_eq!(response.status, 200);
34+
35+
let response = client.get_tags().await;
36+
37+
let res: ListResponse = serde_json::from_str(&response.body).unwrap();
38+
39+
// There should be at least the tag we added.
40+
// Since this is an E2E test that could be executed in a shred env,
41+
// there might be more tags.
42+
assert!(!res.data.is_empty());
43+
if let Some(content_type) = &response.content_type {
44+
assert_eq!(content_type, "application/json");
45+
}
46+
assert_eq!(response.status, 200);
47+
}
48+
49+
#[tokio::test]
50+
async fn it_should_not_allow_adding_a_new_tag_to_unauthenticated_users() {
51+
let mut env = TestEnv::new();
52+
env.start(api::Implementation::ActixWeb).await;
53+
let client = Client::unauthenticated(&env.server_socket_addr().unwrap());
54+
55+
let response = client
56+
.add_tag(AddTagForm {
57+
name: "TAG NAME".to_string(),
58+
})
59+
.await;
60+
61+
assert_eq!(response.status, 401);
62+
}
63+
64+
#[tokio::test]
65+
async fn it_should_not_allow_adding_a_new_tag_to_non_admins() {
66+
let mut env = TestEnv::new();
67+
env.start(api::Implementation::ActixWeb).await;
68+
69+
let logged_non_admin = new_logged_in_user(&env).await;
70+
71+
let client = Client::authenticated(&env.server_socket_addr().unwrap(), &logged_non_admin.token);
72+
73+
let response = client
74+
.add_tag(AddTagForm {
75+
name: "TAG NAME".to_string(),
76+
})
77+
.await;
78+
79+
assert_eq!(response.status, 403);
80+
}
81+
82+
#[tokio::test]
83+
async fn it_should_allow_admins_to_add_new_tags() {
84+
let mut env = TestEnv::new();
85+
env.start(api::Implementation::ActixWeb).await;
86+
87+
let logged_in_admin = new_logged_in_admin(&env).await;
88+
let client = Client::authenticated(&env.server_socket_addr().unwrap(), &logged_in_admin.token);
89+
90+
let tag_name = random_tag_name();
91+
92+
let response = client
93+
.add_tag(AddTagForm {
94+
name: tag_name.to_string(),
95+
})
96+
.await;
97+
98+
let res: AddedTagResponse = serde_json::from_str(&response.body).unwrap();
99+
100+
assert_eq!(res.data, tag_name);
101+
if let Some(content_type) = &response.content_type {
102+
assert_eq!(content_type, "application/json");
103+
}
104+
assert_eq!(response.status, 200);
105+
}
106+
107+
#[tokio::test]
108+
async fn it_should_allow_adding_duplicated_tags() {
109+
// code-review: is this an intended behavior?
110+
111+
let mut env = TestEnv::new();
112+
env.start(api::Implementation::ActixWeb).await;
113+
114+
// Add a tag
115+
let random_tag_name = random_tag_name();
116+
let response = add_tag(&random_tag_name, &env).await;
117+
assert_eq!(response.status, 200);
118+
119+
// Try to add the same tag again
120+
let response = add_tag(&random_tag_name, &env).await;
121+
assert_eq!(response.status, 200);
122+
}
123+
124+
#[tokio::test]
125+
async fn it_should_allow_adding_a_tag_with_an_empty_name() {
126+
// code-review: is this an intended behavior?
127+
128+
let mut env = TestEnv::new();
129+
env.start(api::Implementation::ActixWeb).await;
130+
131+
let empty_tag_name = String::new();
132+
let response = add_tag(&empty_tag_name, &env).await;
133+
assert_eq!(response.status, 200);
134+
}
135+
136+
#[tokio::test]
137+
async fn it_should_allow_admins_to_delete_tags() {
138+
let mut env = TestEnv::new();
139+
env.start(api::Implementation::ActixWeb).await;
140+
141+
let logged_in_admin = new_logged_in_admin(&env).await;
142+
let client = Client::authenticated(&env.server_socket_addr().unwrap(), &logged_in_admin.token);
143+
144+
let (tag_id, _tag_name) = add_random_tag(&env).await;
145+
146+
let response = client.delete_tag(DeleteTagForm { tag_id }).await;
147+
148+
let res: DeletedTagResponse = serde_json::from_str(&response.body).unwrap();
149+
150+
assert_eq!(res.data, tag_id);
151+
if let Some(content_type) = &response.content_type {
152+
assert_eq!(content_type, "application/json");
153+
}
154+
assert_eq!(response.status, 200);
155+
}
156+
157+
#[tokio::test]
158+
async fn it_should_not_allow_non_admins_to_delete_tags() {
159+
let mut env = TestEnv::new();
160+
env.start(api::Implementation::ActixWeb).await;
161+
162+
let logged_in_non_admin = new_logged_in_user(&env).await;
163+
let client = Client::authenticated(&env.server_socket_addr().unwrap(), &logged_in_non_admin.token);
164+
165+
let (tag_id, _tag_name) = add_random_tag(&env).await;
166+
167+
let response = client.delete_tag(DeleteTagForm { tag_id }).await;
168+
169+
assert_eq!(response.status, 403);
170+
}
171+
172+
#[tokio::test]
173+
async fn it_should_not_allow_guests_to_delete_tags() {
174+
let mut env = TestEnv::new();
175+
env.start(api::Implementation::ActixWeb).await;
176+
let client = Client::unauthenticated(&env.server_socket_addr().unwrap());
177+
178+
let (tag_id, _tag_name) = add_random_tag(&env).await;
179+
180+
let response = client.delete_tag(DeleteTagForm { tag_id }).await;
181+
182+
assert_eq!(response.status, 401);
183+
}

0 commit comments

Comments
 (0)