handlers: image/reindex/auth via use cases; auth::authenticate replaces ensure_active; check-handler-services guard
All checks were successful
CI / Check / Test (push) Successful in 1h10m22s

This commit is contained in:
2026-10-26 10:51:32 +01:00
parent 017b58d6fa
commit 00395357ac
30 changed files with 275 additions and 96 deletions

View File

@@ -1,5 +1,7 @@
mod support;
#[path = "auth/authenticate_test.rs"]
mod authenticate_test;
#[path = "auth/login_test.rs"]
mod login_test;
#[path = "auth/logout_test.rs"]

View File

@@ -7,15 +7,16 @@ use domain::{
value_objects::{Email, UserId},
};
use application::{
auth::{deps::RegisterDeps, register},
users::{deps::EnsureActiveDeps, ensure_active},
use application::auth::{
authenticate,
deps::{AuthenticateDeps, RegisterDeps},
register,
};
use crate::support::{TestContextBuilder, register_command};
#[tokio::test]
async fn a_live_user_is_active() {
async fn a_live_users_token_authenticates() {
let store = InMemoryStore::new();
let b = TestContextBuilder::new().with_users(Arc::clone(&store) as _);
let reg = RegisterDeps {
@@ -33,15 +34,20 @@ async fn a_live_user_is_active() {
.unwrap()
.id()
.clone();
let deps = EnsureActiveDeps {
let deps = AuthenticateDeps {
auth: b.auth_service.clone(),
user: b.user_repo.clone(),
};
ensure_active::execute(&deps, &id).await.unwrap();
let authenticated = authenticate::execute(&deps, &id.value().to_string())
.await
.unwrap();
assert_eq!(authenticated, id);
}
#[tokio::test]
async fn a_deleted_user_is_rejected() {
async fn a_deleted_users_token_is_rejected() {
let store = InMemoryStore::new();
let b = TestContextBuilder::new().with_users(Arc::clone(&store) as _);
let reg = RegisterDeps {
@@ -60,23 +66,42 @@ async fn a_deleted_user_is_rejected() {
.id()
.clone();
store.mark_deleted(&id, &[]).await.unwrap();
let deps = EnsureActiveDeps {
let deps = AuthenticateDeps {
auth: b.auth_service.clone(),
user: b.user_repo.clone(),
};
let err = ensure_active::execute(&deps, &id).await.unwrap_err();
assert!(matches!(err, DomainError::Unauthorized(_)), "got {err:?}");
}
#[tokio::test]
async fn an_unknown_user_is_rejected() {
let b = TestContextBuilder::new();
let deps = EnsureActiveDeps {
user: b.user_repo.clone(),
};
let err = ensure_active::execute(&deps, &UserId::generate())
let err = authenticate::execute(&deps, &id.value().to_string())
.await
.unwrap_err();
assert!(matches!(err, DomainError::Unauthorized(_)), "got {err:?}");
}
#[tokio::test]
async fn an_unknown_users_token_is_rejected() {
let b = TestContextBuilder::new();
let deps = AuthenticateDeps {
auth: b.auth_service.clone(),
user: b.user_repo.clone(),
};
let err = authenticate::execute(&deps, &UserId::generate().value().to_string())
.await
.unwrap_err();
assert!(matches!(err, DomainError::Unauthorized(_)), "got {err:?}");
}
#[tokio::test]
async fn an_invalid_token_is_rejected() {
let b = TestContextBuilder::new();
let deps = AuthenticateDeps {
auth: b.auth_service.clone(),
user: b.user_repo.clone(),
};
let err = authenticate::execute(&deps, "not-a-token")
.await
.unwrap_err();

View File

@@ -0,0 +1,2 @@
#[path = "images/get_image_test.rs"]
mod get_image_test;

View File

@@ -0,0 +1,76 @@
use std::{
collections::HashMap,
sync::{Arc, Mutex},
};
use async_trait::async_trait;
use domain::{errors::DomainError, ports::ObjectStorage};
use application::images::{deps::GetImageDeps, get_image};
#[derive(Default)]
struct Storage {
objects: Mutex<HashMap<String, Vec<u8>>>,
}
#[async_trait]
impl ObjectStorage for Storage {
async fn store(&self, key: &str, image_bytes: &[u8]) -> Result<String, DomainError> {
self.objects
.lock()
.unwrap()
.insert(key.to_string(), image_bytes.to_vec());
Ok(key.to_string())
}
async fn get(&self, key: &str) -> Result<Vec<u8>, DomainError> {
self.objects
.lock()
.unwrap()
.get(key)
.cloned()
.ok_or_else(|| DomainError::NotFound(key.to_string()))
}
async fn get_stream(
&self,
_key: &str,
) -> Result<futures::stream::BoxStream<'static, Result<bytes::Bytes, DomainError>>, DomainError>
{
unimplemented!()
}
async fn delete(&self, _key: &str) -> Result<(), DomainError> {
unimplemented!()
}
async fn exists(&self, key: &str) -> Result<bool, DomainError> {
Ok(self.objects.lock().unwrap().contains_key(key))
}
}
#[tokio::test]
async fn returns_the_stored_bytes() {
let storage = Arc::new(Storage::default());
storage.store("posters/1.webp", b"image").await.unwrap();
let deps = GetImageDeps {
object_storage: storage,
};
let bytes = get_image::execute(&deps, "posters/1.webp").await.unwrap();
assert_eq!(bytes, b"image");
}
#[tokio::test]
async fn a_missing_key_is_not_found() {
let deps = GetImageDeps {
object_storage: Arc::new(Storage::default()),
};
let err = get_image::execute(&deps, "posters/missing.webp")
.await
.unwrap_err();
assert!(matches!(err, DomainError::NotFound(_)));
}

View File

@@ -2,3 +2,6 @@ mod support;
#[path = "search/execute_test.rs"]
mod execute_test;
#[path = "search/request_reindex_test.rs"]
mod request_reindex_test;

View File

@@ -0,0 +1,33 @@
use domain::{
errors::DomainError,
events::DomainEvent,
testing::{NoopEventPublisher, RefusingEventPublisher},
};
use application::search::{deps::RequestReindexDeps, request_reindex};
#[tokio::test]
async fn publishes_a_reindex_request() {
let publisher = NoopEventPublisher::new();
let deps = RequestReindexDeps {
event_publisher: publisher.clone(),
};
request_reindex::execute(&deps).await.unwrap();
assert!(matches!(
publisher.published().as_slice(),
[DomainEvent::SearchReindexRequested]
));
}
#[tokio::test]
async fn a_publish_failure_is_returned() {
let deps = RequestReindexDeps {
event_publisher: std::sync::Arc::new(RefusingEventPublisher),
};
let err = request_reindex::execute(&deps).await.unwrap_err();
assert!(matches!(err, DomainError::InfrastructureError(_)));
}

View File

@@ -2,8 +2,6 @@ mod support;
#[path = "users/authorize_admin_test.rs"]
mod authorize_admin_test;
#[path = "users/ensure_active_test.rs"]
mod ensure_active_test;
#[path = "users/get_current_profile_test.rs"]
mod get_current_profile_test;
#[path = "users/get_federated_profile_stats_test.rs"]