Skip to content

Commit e3ed344

Browse files
committed
tests: [#120] E2E tests for torrent routes
1 parent ea36618 commit e3ed344

File tree

19 files changed

+1021
-55
lines changed

19 files changed

+1021
-55
lines changed

.github/workflows/develop.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,6 @@ jobs:
2424
- uses: taiki-e/install-action@cargo-llvm-cov
2525
- uses: taiki-e/install-action@nextest
2626
- name: Test Coverage
27-
run: cargo llvm-cov nextest
27+
run: cargo llvm-cov nextest
2828
- name: E2E Tests
2929
run: ./docker/bin/run-e2e-tests.sh

bin/install.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ fi
88
# Generate storage directory if it does not exist
99
mkdir -p "./storage/database"
1010

11-
# Generate the sqlite database for the index baclend if it does not exist
11+
# Generate the sqlite database for the index backend if it does not exist
1212
if ! [ -f "./storage/database/data.db" ]; then
1313
# todo: it should get the path from config.toml and only do it when we use sqlite
1414
touch ./storage/database/data.db

docker/bin/e2e-env-reset.sh

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
#!/bin/bash
2+
3+
# Delete the SQLite databases and recreate them.
4+
5+
./docker/bin/e2e-env-down.sh
6+
7+
rm -f ./storage/database/torrust_index_backend_e2e_testing.db
8+
rm -f ./storage/database/torrust_tracker_e2e_testing.db
9+
10+
# Generate storage directory if it does not exist
11+
mkdir -p "./storage/database"
12+
13+
# Generate the sqlite database for the index backend if it does not exist
14+
if ! [ -f "./storage/database/torrust_index_backend_e2e_testing.db" ]; then
15+
# todo: it should get the path from config.toml and only do it when we use sqlite
16+
touch ./storage/database/torrust_index_backend_e2e_testing.db
17+
echo ";" | sqlite3 ./storage/database/torrust_index_backend_e2e_testing.db
18+
fi
19+
20+
# Generate the sqlite database for the tracker if it does not exist
21+
if ! [ -f "./storage/database/torrust_tracker_e2e_testing.db" ]; then
22+
touch ./storage/database/torrust_tracker_e2e_testing.db
23+
echo ";" | sqlite3 ./storage/database/torrust_tracker_e2e_testing.db
24+
fi
25+
26+
./docker/bin/e2e-env-up.sh

docker/bin/run-e2e-tests.sh

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ wait_for_container_to_be_healthy() {
3535
return 1
3636
}
3737

38+
# Install tool to create torrent files
39+
cargo install imdl
40+
3841
cp .env.local .env
3942
./bin/install.sh
4043

project-words.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ hasher
1717
Hasher
1818
httpseeds
1919
imagoodboy
20+
imdl
21+
infohash
2022
jsonwebtoken
2123
leechers
2224
Leechers
@@ -41,6 +43,8 @@ strftime
4143
sublicensable
4244
sublist
4345
subpoints
46+
tempdir
47+
tempfile
4448
torrust
4549
Torrust
4650
upgrader

tests/e2e/asserts.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
use crate::e2e::response::Response;
1+
use crate::e2e::responses::TextResponse;
22

33
// Text responses
44

5-
pub fn assert_response_title(response: &Response, title: &str) {
5+
pub fn assert_response_title(response: &TextResponse, title: &str) {
66
let title_element = format!("<title>{title}</title>");
77

88
assert!(
@@ -11,14 +11,14 @@ pub fn assert_response_title(response: &Response, title: &str) {
1111
);
1212
}
1313

14-
pub fn assert_text_ok(response: &Response) {
14+
pub fn assert_text_ok(response: &TextResponse) {
1515
assert_eq!(response.status, 200);
1616
if let Some(content_type) = &response.content_type {
1717
assert_eq!(content_type, "text/html; charset=utf-8");
1818
}
1919
}
2020

21-
pub fn _assert_text_bad_request(response: &Response) {
21+
pub fn _assert_text_bad_request(response: &TextResponse) {
2222
assert_eq!(response.status, 400);
2323
if let Some(content_type) = &response.content_type {
2424
assert_eq!(content_type, "text/plain; charset=utf-8");
@@ -27,7 +27,7 @@ pub fn _assert_text_bad_request(response: &Response) {
2727

2828
// JSON responses
2929

30-
pub fn assert_json_ok(response: &Response) {
30+
pub fn assert_json_ok(response: &TextResponse) {
3131
assert_eq!(response.status, 200);
3232
if let Some(content_type) = &response.content_type {
3333
assert_eq!(content_type, "application/json");

tests/e2e/client.rs

Lines changed: 120 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
1+
use reqwest::multipart;
12
use serde::Serialize;
23

34
use super::contexts::category::{AddCategoryForm, DeleteCategoryForm};
45
use super::contexts::settings::UpdateSettingsForm;
6+
use super::contexts::torrent::requests::{TorrentId, UpdateTorrentFrom};
57
use super::contexts::user::{LoginForm, RegistrationForm, TokenRenewalForm, TokenVerificationForm, Username};
8+
use super::responses::{self, BinaryResponse};
69
use crate::e2e::connection_info::ConnectionInfo;
710
use crate::e2e::http::{Query, ReqwestQuery};
8-
use crate::e2e::response::Response;
11+
use crate::e2e::responses::TextResponse;
912

1013
/// API Client
1114
pub struct Client {
@@ -21,71 +24,99 @@ impl Client {
2124

2225
// Context: about
2326

24-
pub async fn about(&self) -> Response {
27+
pub async fn about(&self) -> TextResponse {
2528
self.http_client.get("about", Query::empty()).await
2629
}
2730

28-
pub async fn license(&self) -> Response {
31+
pub async fn license(&self) -> TextResponse {
2932
self.http_client.get("about/license", Query::empty()).await
3033
}
3134

3235
// Context: category
3336

34-
pub async fn get_categories(&self) -> Response {
37+
pub async fn get_categories(&self) -> TextResponse {
3538
self.http_client.get("category", Query::empty()).await
3639
}
3740

38-
pub async fn add_category(&self, add_category_form: AddCategoryForm) -> Response {
41+
pub async fn add_category(&self, add_category_form: AddCategoryForm) -> TextResponse {
3942
self.http_client.post("category", &add_category_form).await
4043
}
4144

42-
pub async fn delete_category(&self, delete_category_form: DeleteCategoryForm) -> Response {
45+
pub async fn delete_category(&self, delete_category_form: DeleteCategoryForm) -> TextResponse {
4346
self.http_client.delete_with_body("category", &delete_category_form).await
4447
}
4548

4649
// Context: root
4750

48-
pub async fn root(&self) -> Response {
51+
pub async fn root(&self) -> TextResponse {
4952
self.http_client.get("", Query::empty()).await
5053
}
5154

5255
// Context: settings
5356

54-
pub async fn get_public_settings(&self) -> Response {
57+
pub async fn get_public_settings(&self) -> TextResponse {
5558
self.http_client.get("settings/public", Query::empty()).await
5659
}
5760

58-
pub async fn get_site_name(&self) -> Response {
61+
pub async fn get_site_name(&self) -> TextResponse {
5962
self.http_client.get("settings/name", Query::empty()).await
6063
}
6164

62-
pub async fn get_settings(&self) -> Response {
65+
pub async fn get_settings(&self) -> TextResponse {
6366
self.http_client.get("settings", Query::empty()).await
6467
}
6568

66-
pub async fn update_settings(&self, update_settings_form: UpdateSettingsForm) -> Response {
69+
pub async fn update_settings(&self, update_settings_form: UpdateSettingsForm) -> TextResponse {
6770
self.http_client.post("settings", &update_settings_form).await
6871
}
6972

73+
// Context: torrent
74+
75+
pub async fn get_torrents(&self) -> TextResponse {
76+
self.http_client.get("torrents", Query::empty()).await
77+
}
78+
79+
pub async fn get_torrent(&self, id: TorrentId) -> TextResponse {
80+
self.http_client.get(&format!("torrent/{id}"), Query::empty()).await
81+
}
82+
83+
pub async fn delete_torrent(&self, id: TorrentId) -> TextResponse {
84+
self.http_client.delete(&format!("torrent/{id}")).await
85+
}
86+
87+
pub async fn update_torrent(&self, id: TorrentId, update_torrent_form: UpdateTorrentFrom) -> TextResponse {
88+
self.http_client.put(&format!("torrent/{id}"), &update_torrent_form).await
89+
}
90+
91+
pub async fn upload_torrent(&self, form: multipart::Form) -> TextResponse {
92+
self.http_client.post_multipart("torrent/upload", form).await
93+
}
94+
95+
pub async fn download_torrent(&self, id: TorrentId) -> responses::BinaryResponse {
96+
self.http_client
97+
.get_binary(&format!("torrent/download/{id}"), Query::empty())
98+
.await
99+
}
100+
70101
// Context: user
71102

72-
pub async fn register_user(&self, registration_form: RegistrationForm) -> Response {
103+
pub async fn register_user(&self, registration_form: RegistrationForm) -> TextResponse {
73104
self.http_client.post("user/register", &registration_form).await
74105
}
75106

76-
pub async fn login_user(&self, registration_form: LoginForm) -> Response {
107+
pub async fn login_user(&self, registration_form: LoginForm) -> TextResponse {
77108
self.http_client.post("user/login", &registration_form).await
78109
}
79110

80-
pub async fn verify_token(&self, token_verification_form: TokenVerificationForm) -> Response {
111+
pub async fn verify_token(&self, token_verification_form: TokenVerificationForm) -> TextResponse {
81112
self.http_client.post("user/token/verify", &token_verification_form).await
82113
}
83114

84-
pub async fn renew_token(&self, token_verification_form: TokenRenewalForm) -> Response {
115+
pub async fn renew_token(&self, token_verification_form: TokenRenewalForm) -> TextResponse {
85116
self.http_client.post("user/token/renew", &token_verification_form).await
86117
}
87118

88-
pub async fn ban_user(&self, username: Username) -> Response {
119+
pub async fn ban_user(&self, username: Username) -> TextResponse {
89120
self.http_client.delete(&format!("user/ban/{}", &username.value)).await
90121
}
91122
}
@@ -104,7 +135,30 @@ impl Http {
104135
}
105136
}
106137

107-
pub async fn get(&self, path: &str, params: Query) -> Response {
138+
pub async fn get(&self, path: &str, params: Query) -> TextResponse {
139+
let response = match &self.connection_info.token {
140+
Some(token) => reqwest::Client::builder()
141+
.build()
142+
.unwrap()
143+
.get(self.base_url(path).clone())
144+
.query(&ReqwestQuery::from(params))
145+
.bearer_auth(token)
146+
.send()
147+
.await
148+
.unwrap(),
149+
None => reqwest::Client::builder()
150+
.build()
151+
.unwrap()
152+
.get(self.base_url(path).clone())
153+
.query(&ReqwestQuery::from(params))
154+
.send()
155+
.await
156+
.unwrap(),
157+
};
158+
TextResponse::from(response).await
159+
}
160+
161+
pub async fn get_binary(&self, path: &str, params: Query) -> BinaryResponse {
108162
let response = match &self.connection_info.token {
109163
Some(token) => reqwest::Client::builder()
110164
.build()
@@ -124,10 +178,10 @@ impl Http {
124178
.await
125179
.unwrap(),
126180
};
127-
Response::from(response).await
181+
BinaryResponse::from(response).await
128182
}
129183

130-
pub async fn post<T: Serialize + ?Sized>(&self, path: &str, form: &T) -> Response {
184+
pub async fn post<T: Serialize + ?Sized>(&self, path: &str, form: &T) -> TextResponse {
131185
let response = match &self.connection_info.token {
132186
Some(token) => reqwest::Client::new()
133187
.post(self.base_url(path).clone())
@@ -143,10 +197,52 @@ impl Http {
143197
.await
144198
.unwrap(),
145199
};
146-
Response::from(response).await
200+
TextResponse::from(response).await
201+
}
202+
203+
pub async fn post_multipart(&self, path: &str, form: multipart::Form) -> TextResponse {
204+
let response = match &self.connection_info.token {
205+
Some(token) => reqwest::Client::builder()
206+
.build()
207+
.unwrap()
208+
.post(self.base_url(path).clone())
209+
.multipart(form)
210+
.bearer_auth(token)
211+
.send()
212+
.await
213+
.unwrap(),
214+
None => reqwest::Client::builder()
215+
.build()
216+
.unwrap()
217+
.post(self.base_url(path).clone())
218+
.multipart(form)
219+
.send()
220+
.await
221+
.unwrap(),
222+
};
223+
TextResponse::from(response).await
224+
}
225+
226+
pub async fn put<T: Serialize + ?Sized>(&self, path: &str, form: &T) -> TextResponse {
227+
let response = match &self.connection_info.token {
228+
Some(token) => reqwest::Client::new()
229+
.put(self.base_url(path).clone())
230+
.bearer_auth(token)
231+
.json(&form)
232+
.send()
233+
.await
234+
.unwrap(),
235+
None => reqwest::Client::new()
236+
.put(self.base_url(path).clone())
237+
.json(&form)
238+
.send()
239+
.await
240+
.unwrap(),
241+
};
242+
TextResponse::from(response).await
147243
}
148244

149-
async fn delete(&self, path: &str) -> Response {
245+
async fn delete(&self, path: &str) -> TextResponse {
150246
let response = match &self.connection_info.token {
151247
Some(token) => reqwest::Client::new()
152248
.delete(self.base_url(path).clone())
@@ -160,10 +256,10 @@ impl Http {
160256
.await
161257
.unwrap(),
162258
};
163-
Response::from(response).await
259+
TextResponse::from(response).await
164260
}
165261

166-
async fn delete_with_body<T: Serialize + ?Sized>(&self, path: &str, form: &T) -> Response {
262+
async fn delete_with_body<T: Serialize + ?Sized>(&self, path: &str, form: &T) -> TextResponse {
167263
let response = match &self.connection_info.token {
168264
Some(token) => reqwest::Client::new()
169265
.delete(self.base_url(path).clone())
@@ -179,7 +275,7 @@ impl Http {
179275
.await
180276
.unwrap(),
181277
};
182-
Response::from(response).await
278+
TextResponse::from(response).await
183279
}
184280

185281
fn base_url(&self, path: &str) -> String {

tests/e2e/contexts/category.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -217,9 +217,17 @@ pub mod fixtures {
217217
use super::AddCategoryForm;
218218
use crate::e2e::contexts::user::fixtures::logged_in_admin;
219219
use crate::e2e::environment::TestEnv;
220-
use crate::e2e::response::Response;
220+
use crate::e2e::responses::TextResponse;
221221

222-
pub async fn add_category(category_name: &str) -> Response {
222+
pub fn software_predefined_category_name() -> String {
223+
"software".to_string()
224+
}
225+
226+
pub fn software_predefined_category_id() -> i64 {
227+
5
228+
}
229+
230+
pub async fn add_category(category_name: &str) -> TextResponse {
223231
let logged_in_admin = logged_in_admin().await;
224232
let client = TestEnv::default().authenticated_client(&logged_in_admin.token);
225233

tests/e2e/contexts/mod.rs

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

0 commit comments

Comments
 (0)