refactor: typed errors in rss/import ports

This commit is contained in:
2026-10-25 20:30:28 +01:00
parent 18c179e14c
commit 59238a36d8
17 changed files with 410 additions and 392 deletions

View File

@@ -6,7 +6,7 @@ use axum::{
use uuid::Uuid;
use application::{diary::get_diary, diary::get_user_feed, diary::queries::GetDiaryQuery};
use domain::{errors::DomainError, models::ReviewSortBy, ports::FeedChannel};
use domain::{models::ReviewSortBy, ports::FeedChannel};
use crate::{errors::ApiError, state::AppState};
@@ -29,7 +29,7 @@ pub async fn get_feed(State(state): State<AppState>) -> Result<impl IntoResponse
self_path: "/feed.rss",
},
)
.map_err(|e| ApiError(DomainError::InfrastructureError(e)))?;
.map_err(ApiError)?;
Ok((
[(header::CONTENT_TYPE, "application/xml; charset=utf-8")],
xml,
@@ -61,7 +61,7 @@ pub async fn get_user_feed(
self_path: &self_path,
},
)
.map_err(|e| ApiError(DomainError::InfrastructureError(e)))?;
.map_err(ApiError)?;
Ok((
[(header::CONTENT_TYPE, "application/xml; charset=utf-8")],

View File

@@ -1091,105 +1091,5 @@ pub async fn post_unblock_actor(
}
#[cfg(test)]
mod tests {
use super::*;
use domain::value_objects::{InstanceIdentity, SocialActor, SocialIdentity, UserId};
fn local_actor(uid: &UserId) -> SocialActor {
SocialActor {
identity: SocialIdentity::Local(uid.clone()),
handle: "@gabriel@md.example".into(),
display_name: None,
avatar_url: None,
}
}
#[test]
fn local_actor_dto_url_round_trips_back_to_the_same_identity() {
let instance = InstanceIdentity::new("https://md.example");
let uid = UserId::from_uuid(uuid::Uuid::new_v4());
let dto = social_actor_to_dto(local_actor(&uid), &instance);
assert!(!dto.url.starts_with("local:"), "url was {}", dto.url);
assert_eq!(
instance.identify(&dto.url),
SocialIdentity::Local(uid),
"a local actor's dto url must parse back as Local"
);
}
#[cfg(feature = "federation")]
#[test]
fn local_blocked_dto_url_round_trips_back_to_the_same_identity() {
let instance = InstanceIdentity::new("https://md.example");
let uid = UserId::from_uuid(uuid::Uuid::new_v4());
let dto = social_actor_to_blocked_dto(local_actor(&uid), &instance);
assert!(!dto.url.starts_with("local:"), "url was {}", dto.url);
assert_eq!(instance.identify(&dto.url), SocialIdentity::Local(uid));
}
#[test]
fn local_template_url_round_trips_back_to_the_same_identity() {
let instance = InstanceIdentity::new("https://md.example");
let uid = UserId::from_uuid(uuid::Uuid::new_v4());
let data = social_actor_to_template(local_actor(&uid), &instance);
assert!(!data.url.starts_with("local:"), "url was {}", data.url);
assert_eq!(instance.identify(&data.url), SocialIdentity::Local(uid));
}
#[test]
fn remote_actor_dto_url_is_passed_through_unchanged() {
let instance = InstanceIdentity::new("https://md.example");
let actor = SocialActor {
identity: SocialIdentity::Remote {
actor_url: "https://other.example/users/bob".into(),
},
handle: "@bob@other.example".into(),
display_name: None,
avatar_url: None,
};
let dto = social_actor_to_dto(actor, &instance);
assert_eq!(dto.url, "https://other.example/users/bob");
}
#[test]
fn local_actor_dto_carries_user_id_and_avatar() {
let instance = InstanceIdentity::new("https://md.example");
let uid = UserId::from_uuid(uuid::Uuid::new_v4());
let mut actor = local_actor(&uid);
actor.avatar_url = Some("https://md.example/images/a.webp".into());
let dto = social_actor_to_dto(actor, &instance);
assert_eq!(
dto.user_id,
Some(uid.value()),
"local actors must carry user_id for internal links"
);
assert_eq!(
dto.avatar_url.as_deref(),
Some("https://md.example/images/a.webp")
);
}
#[test]
fn remote_actor_dto_has_no_user_id() {
let instance = InstanceIdentity::new("https://md.example");
let actor = SocialActor {
identity: SocialIdentity::Remote {
actor_url: "https://other.example/users/bob".into(),
},
handle: "@bob@other.example".into(),
display_name: None,
avatar_url: None,
};
assert_eq!(social_actor_to_dto(actor, &instance).user_id, None);
}
}
#[path = "tests/social.rs"]
mod tests;

View File

@@ -0,0 +1,100 @@
use super::*;
use domain::value_objects::{InstanceIdentity, SocialActor, SocialIdentity, UserId};
fn local_actor(uid: &UserId) -> SocialActor {
SocialActor {
identity: SocialIdentity::Local(uid.clone()),
handle: "@gabriel@md.example".into(),
display_name: None,
avatar_url: None,
}
}
#[test]
fn local_actor_dto_url_round_trips_back_to_the_same_identity() {
let instance = InstanceIdentity::new("https://md.example");
let uid = UserId::from_uuid(uuid::Uuid::new_v4());
let dto = social_actor_to_dto(local_actor(&uid), &instance);
assert!(!dto.url.starts_with("local:"), "url was {}", dto.url);
assert_eq!(
instance.identify(&dto.url),
SocialIdentity::Local(uid),
"a local actor's dto url must parse back as Local"
);
}
#[cfg(feature = "federation")]
#[test]
fn local_blocked_dto_url_round_trips_back_to_the_same_identity() {
let instance = InstanceIdentity::new("https://md.example");
let uid = UserId::from_uuid(uuid::Uuid::new_v4());
let dto = social_actor_to_blocked_dto(local_actor(&uid), &instance);
assert!(!dto.url.starts_with("local:"), "url was {}", dto.url);
assert_eq!(instance.identify(&dto.url), SocialIdentity::Local(uid));
}
#[test]
fn local_template_url_round_trips_back_to_the_same_identity() {
let instance = InstanceIdentity::new("https://md.example");
let uid = UserId::from_uuid(uuid::Uuid::new_v4());
let data = social_actor_to_template(local_actor(&uid), &instance);
assert!(!data.url.starts_with("local:"), "url was {}", data.url);
assert_eq!(instance.identify(&data.url), SocialIdentity::Local(uid));
}
#[test]
fn remote_actor_dto_url_is_passed_through_unchanged() {
let instance = InstanceIdentity::new("https://md.example");
let actor = SocialActor {
identity: SocialIdentity::Remote {
actor_url: "https://other.example/users/bob".into(),
},
handle: "@bob@other.example".into(),
display_name: None,
avatar_url: None,
};
let dto = social_actor_to_dto(actor, &instance);
assert_eq!(dto.url, "https://other.example/users/bob");
}
#[test]
fn local_actor_dto_carries_user_id_and_avatar() {
let instance = InstanceIdentity::new("https://md.example");
let uid = UserId::from_uuid(uuid::Uuid::new_v4());
let mut actor = local_actor(&uid);
actor.avatar_url = Some("https://md.example/images/a.webp".into());
let dto = social_actor_to_dto(actor, &instance);
assert_eq!(
dto.user_id,
Some(uid.value()),
"local actors must carry user_id for internal links"
);
assert_eq!(
dto.avatar_url.as_deref(),
Some("https://md.example/images/a.webp")
);
}
#[test]
fn remote_actor_dto_has_no_user_id() {
let instance = InstanceIdentity::new("https://md.example");
let actor = SocialActor {
identity: SocialIdentity::Remote {
actor_url: "https://other.example/users/bob".into(),
},
handle: "@bob@other.example".into(),
display_name: None,
avatar_url: None,
};
assert_eq!(social_actor_to_dto(actor, &instance).user_id, None);
}

View File

@@ -420,7 +420,7 @@ impl domain::ports::RssFeedRenderer for Panic {
&self,
_: &[DiaryEntry],
_: &domain::ports::FeedChannel<'_>,
) -> Result<String, String> {
) -> Result<String, DomainError> {
panic!()
}
}