init
Some checks failed
CI / ci (push) Failing after 1m48s

This commit is contained in:
2026-08-25 23:24:36 +02:00
commit 95739892de
466 changed files with 33918 additions and 0 deletions

View 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());
}

View 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);
}

View 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());
}

View 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());
}

View 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"));
}

View 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());
}