Skip to content

Commit f993107

Browse files
committed
tests: [#56] for users table in upgrader
1 parent 6188b10 commit f993107

File tree

5 files changed

+184
-26
lines changed

5 files changed

+184
-26
lines changed

src/upgrades/from_v1_0_0_to_v2_0_0/upgrader.rs

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -71,10 +71,11 @@ fn parse_args() -> Arguments {
7171
}
7272

7373
pub async fn run_upgrader() {
74-
upgrade(&parse_args()).await
74+
let now = datetime_iso_8601();
75+
upgrade(&parse_args(), &now).await;
7576
}
7677

77-
pub async fn upgrade(args: &Arguments) {
78+
pub async fn upgrade(args: &Arguments, date_imported: &str) {
7879
// Get connection to source database (current DB in settings)
7980
let source_database = current_db(&args.source_database_file).await;
8081

@@ -86,7 +87,12 @@ pub async fn upgrade(args: &Arguments) {
8687
migrate_destiny_database(dest_database.clone()).await;
8788
reset_destiny_database(dest_database.clone()).await;
8889
transfer_categories(source_database.clone(), dest_database.clone()).await;
89-
transfer_user_data(source_database.clone(), dest_database.clone()).await;
90+
transfer_user_data(
91+
source_database.clone(),
92+
dest_database.clone(),
93+
date_imported,
94+
)
95+
.await;
9096
transfer_tracker_keys(source_database.clone(), dest_database.clone()).await;
9197
transfer_torrents(
9298
source_database.clone(),
@@ -158,6 +164,7 @@ async fn transfer_categories(
158164
async fn transfer_user_data(
159165
source_database: Arc<SqliteDatabaseV1_0_0>,
160166
dest_database: Arc<SqliteDatabaseV2_0_0>,
167+
date_imported: &str,
161168
) {
162169
println!("Transferring users ...");
163170

@@ -173,8 +180,6 @@ async fn transfer_user_data(
173180
&user.username, &user.user_id
174181
);
175182

176-
let date_imported = today_iso8601();
177-
178183
let id = dest_database
179184
.insert_imported_user(user.user_id, &date_imported, user.administrator)
180185
.await
@@ -238,7 +243,9 @@ async fn transfer_user_data(
238243
}
239244
}
240245

241-
fn today_iso8601() -> String {
246+
/// Current datetime in ISO8601 without time zone.
247+
/// For example: 2022-11-10 10:35:15
248+
pub fn datetime_iso_8601() -> String {
242249
let dt: DateTime<Utc> = SystemTime::now().into();
243250
format!("{}", dt.format("%Y-%m-%d %H:%M:%S"))
244251
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
pub mod sqlite_v1_0_0;
2+
pub mod sqlite_v2_0_0;
23
pub mod tests;

tests/upgrades/from_v1_0_0_to_v2_0_0/sqlite_v1_0_0.rs

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
11
use sqlx::sqlite::SqlitePoolOptions;
2-
use sqlx::SqlitePool;
2+
use sqlx::{query, SqlitePool};
33
use std::fs;
4+
use torrust_index_backend::upgrades::from_v1_0_0_to_v2_0_0::databases::sqlite_v1_0_0::UserRecordV1;
45

56
pub struct SqliteDatabaseV1_0_0 {
67
pub pool: SqlitePool,
78
}
89

910
impl SqliteDatabaseV1_0_0 {
10-
pub async fn db_connection(source_database_file: &str) -> Self {
11-
let source_database_connect_url = format!("sqlite://{}?mode=rwc", source_database_file);
12-
SqliteDatabaseV1_0_0::new(&source_database_connect_url).await
11+
pub async fn db_connection(database_file: &str) -> Self {
12+
let connect_url = format!("sqlite://{}?mode=rwc", database_file);
13+
Self::new(&connect_url).await
1314
}
1415

1516
pub async fn new(database_url: &str) -> Self {
@@ -24,6 +25,7 @@ impl SqliteDatabaseV1_0_0 {
2425
pub async fn migrate(&self, fixtures_dir: &str) {
2526
let migrations_dir = format!("{}database/v1.0.0/migrations/", fixtures_dir);
2627

28+
// TODO: read files from dir
2729
let migrations = vec![
2830
"20210831113004_torrust_users.sql",
2931
"20210904135524_torrust_tracker_keys.sql",
@@ -50,4 +52,17 @@ impl SqliteDatabaseV1_0_0 {
5052

5153
println!("Migration result {:?}", res);
5254
}
55+
56+
pub async fn insert_user(&self, user: &UserRecordV1) -> Result<i64, sqlx::Error> {
57+
query("INSERT INTO torrust_users (user_id, username, email, email_verified, password, administrator) VALUES (?, ?, ?, ?, ?, ?)")
58+
.bind(user.user_id)
59+
.bind(user.username.clone())
60+
.bind(user.email.clone())
61+
.bind(user.email_verified)
62+
.bind(user.password.clone())
63+
.bind(user.administrator)
64+
.execute(&self.pool)
65+
.await
66+
.map(|v| v.last_insert_rowid())
67+
}
5368
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
use serde::{Deserialize, Serialize};
2+
use sqlx::sqlite::SqlitePoolOptions;
3+
use sqlx::{query_as, SqlitePool};
4+
5+
#[derive(Debug, Serialize, Deserialize, sqlx::FromRow)]
6+
pub struct UserRecordV2 {
7+
pub user_id: i64,
8+
pub date_registered: Option<String>,
9+
pub date_imported: Option<String>,
10+
pub administrator: bool,
11+
}
12+
13+
pub struct SqliteDatabaseV2_0_0 {
14+
pub pool: SqlitePool,
15+
}
16+
17+
impl SqliteDatabaseV2_0_0 {
18+
pub async fn db_connection(database_file: &str) -> Self {
19+
let connect_url = format!("sqlite://{}?mode=rwc", database_file);
20+
Self::new(&connect_url).await
21+
}
22+
23+
pub async fn new(database_url: &str) -> Self {
24+
let db = SqlitePoolOptions::new()
25+
.connect(database_url)
26+
.await
27+
.expect("Unable to create database pool.");
28+
Self { pool: db }
29+
}
30+
31+
pub async fn get_user(&self, user_id: i64) -> Result<UserRecordV2, sqlx::Error> {
32+
query_as::<_, UserRecordV2>("SELECT * FROM torrust_users WHERE user_id = ?")
33+
.bind(user_id)
34+
.fetch_one(&self.pool)
35+
.await
36+
}
37+
}

tests/upgrades/from_v1_0_0_to_v2_0_0/tests.rs

Lines changed: 114 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,39 +4,137 @@
44
//! cargo test upgrade_data_from_version_v1_0_0_to_v2_0_0 -- --nocapture
55
//! ```
66
use crate::upgrades::from_v1_0_0_to_v2_0_0::sqlite_v1_0_0::SqliteDatabaseV1_0_0;
7+
use crate::upgrades::from_v1_0_0_to_v2_0_0::sqlite_v2_0_0::SqliteDatabaseV2_0_0;
8+
use argon2::password_hash::SaltString;
9+
use argon2::{Argon2, PasswordHasher};
10+
use rand_core::OsRng;
711
use std::fs;
812
use std::sync::Arc;
9-
use torrust_index_backend::upgrades::from_v1_0_0_to_v2_0_0::upgrader::{upgrade, Arguments};
13+
use torrust_index_backend::upgrades::from_v1_0_0_to_v2_0_0::databases::sqlite_v1_0_0::UserRecordV1;
14+
use torrust_index_backend::upgrades::from_v1_0_0_to_v2_0_0::upgrader::{
15+
datetime_iso_8601, upgrade, Arguments,
16+
};
1017

1118
#[tokio::test]
1219
async fn upgrade_data_from_version_v1_0_0_to_v2_0_0() {
13-
/* TODO:
14-
* - Insert data: user, tracker key and torrent
15-
* - Assertions
16-
*/
20+
// Directories
1721
let fixtures_dir = "./tests/upgrades/from_v1_0_0_to_v2_0_0/fixtures/".to_string();
18-
let debug_output_dir = "./tests/upgrades/from_v1_0_0_to_v2_0_0/output/".to_string();
22+
let output_dir = "./tests/upgrades/from_v1_0_0_to_v2_0_0/output/".to_string();
1923

20-
let source_database_file = format!("{}source.db", debug_output_dir);
21-
let destiny_database_file = format!("{}destiny.db", debug_output_dir);
22-
23-
// TODO: use a unique temporary dir
24-
fs::remove_file(&source_database_file).expect("Can't remove source DB file.");
25-
fs::remove_file(&destiny_database_file).expect("Can't remove destiny DB file.");
24+
// Files
25+
let source_database_file = format!("{}source.db", output_dir);
26+
let destiny_database_file = format!("{}destiny.db", output_dir);
2627

28+
// Set up clean database
29+
reset_databases(&source_database_file, &destiny_database_file);
2730
let source_database = source_db_connection(&source_database_file).await;
28-
2931
source_database.migrate(&fixtures_dir).await;
3032

33+
// Load data into database v1
34+
35+
// `torrust_users` table
36+
37+
let user = UserRecordV1 {
38+
user_id: 1,
39+
username: "user01".to_string(),
40+
email: "[email protected]".to_string(),
41+
email_verified: true,
42+
password: hashed_valid_password(),
43+
administrator: true,
44+
};
45+
let user_id = source_database.insert_user(&user).await.unwrap();
46+
47+
// `torrust_tracker_keys` table
48+
49+
// TODO
50+
51+
// `torrust_torrents` table
52+
53+
// TODO
54+
55+
// Run the upgrader
3156
let args = Arguments {
32-
source_database_file,
33-
destiny_database_file,
57+
source_database_file: source_database_file.clone(),
58+
destiny_database_file: destiny_database_file.clone(),
3459
upload_path: format!("{}uploads/", fixtures_dir),
3560
};
61+
let now = datetime_iso_8601();
62+
upgrade(&args, &now).await;
63+
64+
// Assertions in database v2
65+
66+
let destiny_database = destiny_db_connection(&destiny_database_file).await;
67+
68+
// `torrust_users` table
69+
70+
let imported_user = destiny_database.get_user(user_id).await.unwrap();
71+
72+
assert_eq!(imported_user.user_id, user.user_id);
73+
assert!(imported_user.date_registered.is_none());
74+
assert_eq!(imported_user.date_imported.unwrap(), now);
75+
assert_eq!(imported_user.administrator, user.administrator);
76+
77+
// `torrust_user_authentication` table
78+
79+
// TODO
80+
81+
// `torrust_user_profiles` table
82+
83+
// TODO
84+
85+
// `torrust_tracker_keys` table
3686

37-
upgrade(&args).await;
87+
// TODO
88+
89+
// `torrust_torrents` table
90+
91+
// TODO
92+
93+
// `torrust_torrent_files` table
94+
95+
// TODO
96+
97+
// `torrust_torrent_info` table
98+
99+
// TODO
100+
101+
// `torrust_torrent_announce_urls` table
102+
103+
// TODO
38104
}
39105

40106
async fn source_db_connection(source_database_file: &str) -> Arc<SqliteDatabaseV1_0_0> {
41107
Arc::new(SqliteDatabaseV1_0_0::db_connection(&source_database_file).await)
42108
}
109+
110+
async fn destiny_db_connection(destiny_database_file: &str) -> Arc<SqliteDatabaseV2_0_0> {
111+
Arc::new(SqliteDatabaseV2_0_0::db_connection(&destiny_database_file).await)
112+
}
113+
114+
/// Reset databases from previous executions
115+
fn reset_databases(source_database_file: &str, destiny_database_file: &str) {
116+
// TODO: use a unique temporary dir
117+
fs::remove_file(&source_database_file).expect("Can't remove source DB file.");
118+
fs::remove_file(&destiny_database_file).expect("Can't remove destiny DB file.");
119+
}
120+
121+
fn hashed_valid_password() -> String {
122+
hash_password(&valid_password())
123+
}
124+
125+
fn valid_password() -> String {
126+
"123456".to_string()
127+
}
128+
129+
fn hash_password(plain_password: &str) -> String {
130+
let salt = SaltString::generate(&mut OsRng);
131+
132+
// Argon2 with default params (Argon2id v19)
133+
let argon2 = Argon2::default();
134+
135+
// Hash password to PHC string ($argon2id$v=19$...)
136+
argon2
137+
.hash_password(plain_password.as_bytes(), &salt)
138+
.unwrap()
139+
.to_string()
140+
}

0 commit comments

Comments
 (0)