67
crates/application/tests/user/change_password_test.rs
Normal file
67
crates/application/tests/user/change_password_test.rs
Normal file
@@ -0,0 +1,67 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use domain::ports::UserCommandPort;
|
||||
use domain::testing::{FakePasswordHasher, InMemoryStore};
|
||||
use domain::user::{Email, PasswordHash, User, Username};
|
||||
|
||||
use application::user::commands::ChangePasswordCommand;
|
||||
use application::user::use_cases::change_password;
|
||||
|
||||
async fn setup() -> (Arc<InMemoryStore>, change_password::Deps, User) {
|
||||
let store = Arc::new(InMemoryStore::new());
|
||||
let user = User::new(
|
||||
Username::new("alice").unwrap(),
|
||||
Email::new("alice@example.com").unwrap(),
|
||||
PasswordHash::new("hashed:secret".into()),
|
||||
);
|
||||
store.save(&user).await.unwrap();
|
||||
|
||||
let deps = change_password::Deps {
|
||||
user_command: store.clone(),
|
||||
user_query: store.clone(),
|
||||
password_hasher: Arc::new(FakePasswordHasher),
|
||||
};
|
||||
|
||||
(store, deps, user)
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn changes_password_with_correct_current() {
|
||||
let (_store, deps, user) = setup().await;
|
||||
|
||||
let cmd = ChangePasswordCommand {
|
||||
user_id: user.id().clone(),
|
||||
current_password: "secret".into(),
|
||||
new_password: "new-secret".into(),
|
||||
};
|
||||
|
||||
change_password::execute(cmd, &deps).await.unwrap();
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn rejects_wrong_current_password() {
|
||||
let (_store, deps, user) = setup().await;
|
||||
|
||||
let cmd = ChangePasswordCommand {
|
||||
user_id: user.id().clone(),
|
||||
current_password: "wrong".into(),
|
||||
new_password: "new-secret".into(),
|
||||
};
|
||||
|
||||
let result = change_password::execute(cmd, &deps).await;
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn rejects_nonexistent_user() {
|
||||
let (_store, deps, _user) = setup().await;
|
||||
|
||||
let cmd = ChangePasswordCommand {
|
||||
user_id: domain::user::UserId::generate(),
|
||||
current_password: "secret".into(),
|
||||
new_password: "new-secret".into(),
|
||||
};
|
||||
|
||||
let result = change_password::execute(cmd, &deps).await;
|
||||
assert!(result.is_err());
|
||||
}
|
||||
32
crates/application/tests/user/clear_data_test.rs
Normal file
32
crates/application/tests/user/clear_data_test.rs
Normal file
@@ -0,0 +1,32 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use domain::entry::Mood;
|
||||
use domain::ports::{ActivityCommandPort, MoodEntryCommandPort, ReminderCommandPort};
|
||||
use domain::testing::{InMemoryStore, test_activity, test_entry, test_reminder, test_user};
|
||||
|
||||
use application::user::use_cases::clear_data;
|
||||
|
||||
#[tokio::test]
|
||||
async fn clears_all_user_data() {
|
||||
let store = Arc::new(InMemoryStore::new());
|
||||
let user = test_user("alice");
|
||||
let entry = test_entry(user.id().clone(), Mood::Good);
|
||||
let activity = test_activity(user.id().clone(), "gaming");
|
||||
let reminder = test_reminder(user.id().clone());
|
||||
|
||||
MoodEntryCommandPort::save(&*store, &entry).await.unwrap();
|
||||
ActivityCommandPort::save(&*store, &activity).await.unwrap();
|
||||
ReminderCommandPort::save(&*store, &reminder).await.unwrap();
|
||||
|
||||
let deps = clear_data::Deps {
|
||||
entry_query: store.clone(),
|
||||
cascade: store.clone(),
|
||||
media_storage: store.clone(),
|
||||
};
|
||||
|
||||
clear_data::execute(user.id().clone(), &deps).await.unwrap();
|
||||
|
||||
assert_eq!(store.entry_count(), 0);
|
||||
assert_eq!(store.activity_count(), 0);
|
||||
assert_eq!(store.reminder_count(), 0);
|
||||
}
|
||||
50
crates/application/tests/user/delete_user_test.rs
Normal file
50
crates/application/tests/user/delete_user_test.rs
Normal file
@@ -0,0 +1,50 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use domain::entry::Mood;
|
||||
use domain::ports::{
|
||||
ActivityCommandPort, MoodEntryCommandPort, ReminderCommandPort, UserCommandPort,
|
||||
};
|
||||
use domain::testing::{InMemoryStore, test_activity, test_entry, test_reminder, test_user};
|
||||
use domain::user::UserId;
|
||||
|
||||
use application::user::use_cases::delete_user;
|
||||
|
||||
fn deps(store: &Arc<InMemoryStore>) -> delete_user::Deps {
|
||||
delete_user::Deps {
|
||||
user_query: store.clone(),
|
||||
entry_query: store.clone(),
|
||||
cascade: store.clone(),
|
||||
media_storage: store.clone(),
|
||||
events: store.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn deletes_user_and_all_data() {
|
||||
let store = Arc::new(InMemoryStore::new());
|
||||
let user = test_user("alice");
|
||||
let entry = test_entry(user.id().clone(), Mood::Good);
|
||||
let activity = test_activity(user.id().clone(), "gaming");
|
||||
let reminder = test_reminder(user.id().clone());
|
||||
|
||||
UserCommandPort::save(&*store, &user).await.unwrap();
|
||||
MoodEntryCommandPort::save(&*store, &entry).await.unwrap();
|
||||
ActivityCommandPort::save(&*store, &activity).await.unwrap();
|
||||
ReminderCommandPort::save(&*store, &reminder).await.unwrap();
|
||||
|
||||
delete_user::execute(user.id().clone(), &deps(&store))
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(store.entry_count(), 0);
|
||||
assert_eq!(store.activity_count(), 0);
|
||||
assert_eq!(store.reminder_count(), 0);
|
||||
assert_eq!(store.user_count(), 0);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn rejects_nonexistent_user() {
|
||||
let store = Arc::new(InMemoryStore::new());
|
||||
let result = delete_user::execute(UserId::generate(), &deps(&store)).await;
|
||||
assert!(result.is_err());
|
||||
}
|
||||
82
crates/application/tests/user/login_test.rs
Normal file
82
crates/application/tests/user/login_test.rs
Normal file
@@ -0,0 +1,82 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use domain::ports::UserCommandPort;
|
||||
use domain::testing::{FakePasswordHasher, InMemoryStore};
|
||||
use domain::user::{Email, PasswordHash, User, Username};
|
||||
|
||||
use application::auth::use_cases::login;
|
||||
use application::user::commands::LoginCommand;
|
||||
|
||||
async fn setup() -> (Arc<InMemoryStore>, login::Deps) {
|
||||
let store = Arc::new(InMemoryStore::new());
|
||||
let user = User::new(
|
||||
Username::new("alice").unwrap(),
|
||||
Email::new("alice@example.com").unwrap(),
|
||||
PasswordHash::new("hashed:secret".into()),
|
||||
);
|
||||
store.save(&user).await.unwrap();
|
||||
|
||||
let deps = login::Deps {
|
||||
user_query: store.clone(),
|
||||
password_hasher: Arc::new(FakePasswordHasher),
|
||||
auth_service: store.clone(),
|
||||
refresh_session_command: store.clone(),
|
||||
refresh_token_ttl_seconds: 86400,
|
||||
};
|
||||
|
||||
(store, deps)
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn login_with_username() {
|
||||
let (_, deps) = setup().await;
|
||||
|
||||
let cmd = LoginCommand {
|
||||
identifier: "alice".into(),
|
||||
password: "secret".into(),
|
||||
};
|
||||
|
||||
let result = login::execute(cmd, &deps).await.unwrap();
|
||||
assert_eq!(result.user.username().value(), "alice");
|
||||
assert!(!result.access_token.token().is_empty());
|
||||
assert!(!result.refresh_token.is_empty());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn login_with_email() {
|
||||
let (_, deps) = setup().await;
|
||||
|
||||
let cmd = LoginCommand {
|
||||
identifier: "alice@example.com".into(),
|
||||
password: "secret".into(),
|
||||
};
|
||||
|
||||
let result = login::execute(cmd, &deps).await.unwrap();
|
||||
assert_eq!(result.user.username().value(), "alice");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn wrong_password_fails() {
|
||||
let (_, deps) = setup().await;
|
||||
|
||||
let cmd = LoginCommand {
|
||||
identifier: "alice".into(),
|
||||
password: "wrong".into(),
|
||||
};
|
||||
|
||||
let result = login::execute(cmd, &deps).await;
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn nonexistent_user_fails() {
|
||||
let (_, deps) = setup().await;
|
||||
|
||||
let cmd = LoginCommand {
|
||||
identifier: "nobody".into(),
|
||||
password: "secret".into(),
|
||||
};
|
||||
|
||||
let result = login::execute(cmd, &deps).await;
|
||||
assert!(result.is_err());
|
||||
}
|
||||
47
crates/application/tests/user/profile_test.rs
Normal file
47
crates/application/tests/user/profile_test.rs
Normal file
@@ -0,0 +1,47 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use domain::ports::UserCommandPort;
|
||||
use domain::testing::{InMemoryStore, test_user};
|
||||
use domain::user::{DisplayName, Timezone};
|
||||
|
||||
use application::user::commands::UpdateProfileCommand;
|
||||
use application::user::use_cases::{get_profile, update_profile};
|
||||
|
||||
#[tokio::test]
|
||||
async fn get_profile_returns_user() {
|
||||
let store = Arc::new(InMemoryStore::new());
|
||||
let user = test_user("alice");
|
||||
store.save(&user).await.unwrap();
|
||||
|
||||
let deps = get_profile::Deps {
|
||||
user_query: store.clone(),
|
||||
};
|
||||
|
||||
let result = get_profile::execute(user.id().clone(), &deps)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(result.username().value(), "alice");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn updates_display_name_and_timezone() {
|
||||
let store = Arc::new(InMemoryStore::new());
|
||||
let user = test_user("alice");
|
||||
store.save(&user).await.unwrap();
|
||||
|
||||
let deps = update_profile::Deps {
|
||||
user_command: store.clone(),
|
||||
user_query: store.clone(),
|
||||
};
|
||||
|
||||
let cmd = UpdateProfileCommand {
|
||||
user_id: user.id().clone(),
|
||||
display_name: Some(DisplayName::new("Alice K").unwrap()),
|
||||
timezone: Some(Timezone::new("Europe/Warsaw").unwrap()),
|
||||
};
|
||||
|
||||
let updated = update_profile::execute(cmd, &deps).await.unwrap();
|
||||
|
||||
assert_eq!(updated.display_name().map(|d| d.value()), Some("Alice K"));
|
||||
assert_eq!(updated.timezone().map(|t| t.value()), Some("Europe/Warsaw"));
|
||||
}
|
||||
79
crates/application/tests/user/register_test.rs
Normal file
79
crates/application/tests/user/register_test.rs
Normal file
@@ -0,0 +1,79 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use domain::testing::{FakePasswordHasher, InMemoryStore};
|
||||
use domain::user::Username;
|
||||
|
||||
use application::user::commands::RegisterCommand;
|
||||
use application::user::use_cases::register;
|
||||
|
||||
fn deps() -> (Arc<InMemoryStore>, register::Deps) {
|
||||
let store = Arc::new(InMemoryStore::new());
|
||||
let deps = register::Deps {
|
||||
user_command: store.clone(),
|
||||
user_query: store.clone(),
|
||||
activity_command: store.clone(),
|
||||
password_hasher: Arc::new(FakePasswordHasher),
|
||||
events: store.clone(),
|
||||
preset: config::PresetConfig::default(),
|
||||
};
|
||||
(store, deps)
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn registers_user_and_seeds_activities() {
|
||||
let (store, deps) = deps();
|
||||
|
||||
let cmd = RegisterCommand {
|
||||
username: Username::new("alice").unwrap(),
|
||||
email: domain::user::Email::new("alice@example.com").unwrap(),
|
||||
password: "secret123".into(),
|
||||
};
|
||||
|
||||
let user = register::execute(cmd, &deps).await.unwrap();
|
||||
|
||||
assert_eq!(user.username().value(), "alice");
|
||||
assert!(store.activity_count() > 0);
|
||||
assert_eq!(store.published_events().len(), 1);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn rejects_duplicate_username() {
|
||||
let (_, deps) = deps();
|
||||
|
||||
let cmd1 = RegisterCommand {
|
||||
username: Username::new("alice").unwrap(),
|
||||
email: domain::user::Email::new("alice@example.com").unwrap(),
|
||||
password: "secret".into(),
|
||||
};
|
||||
register::execute(cmd1, &deps).await.unwrap();
|
||||
|
||||
let cmd2 = RegisterCommand {
|
||||
username: Username::new("alice").unwrap(),
|
||||
email: domain::user::Email::new("alice2@example.com").unwrap(),
|
||||
password: "secret".into(),
|
||||
};
|
||||
let result = register::execute(cmd2, &deps).await;
|
||||
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn rejects_duplicate_email() {
|
||||
let (_, deps) = deps();
|
||||
|
||||
let cmd1 = RegisterCommand {
|
||||
username: Username::new("alice").unwrap(),
|
||||
email: domain::user::Email::new("alice@example.com").unwrap(),
|
||||
password: "secret".into(),
|
||||
};
|
||||
register::execute(cmd1, &deps).await.unwrap();
|
||||
|
||||
let cmd2 = RegisterCommand {
|
||||
username: Username::new("bob").unwrap(),
|
||||
email: domain::user::Email::new("alice@example.com").unwrap(),
|
||||
password: "secret".into(),
|
||||
};
|
||||
let result = register::execute(cmd2, &deps).await;
|
||||
|
||||
assert!(result.is_err());
|
||||
}
|
||||
Reference in New Issue
Block a user