insights-correlations (#22)
Some checks failed
CI / Check / Test (push) Has been cancelled

Reviewed-on: #22
This commit was merged in pull request #22.
This commit is contained in:
2026-10-12 18:56:55 +00:00
parent 4d221e60de
commit e303ae7f08
139 changed files with 2371 additions and 696 deletions

View File

@@ -5,15 +5,6 @@ use uuid::Uuid;
use crate::users::deps::AuthorizeAdminDeps;
/// Whether `user_id` is an admin, for the two extractors (`AdminApiUser`,
/// `AdminUser`) that gate admin-only routes.
///
/// Returns `Ok(None)` when the user row does not exist — a distinct success
/// value, not folded into `DomainError::NotFound`, specifically so callers can
/// tell "row missing" apart from "repository call failed" without depending on
/// which `DomainError` variant a lookup failure happens to produce. Each
/// extractor rejects those two cases differently (missing row is a 401/404,
/// a repository error is a 500) and that distinction must stay exact.
pub async fn execute(
deps: &AuthorizeAdminDeps,
user_id: Uuid,

View File

@@ -7,9 +7,6 @@ use domain::ports::{
};
use domain::value_objects::InstanceIdentity;
/// The local half of the former `GetProfileDeps` split (ADR-0004). `user` and
/// `instance` build the always-populated `ProfileIdentity`; `stats`/`diary`/
/// `social_query` are unchanged from before the split.
pub struct GetLocalProfileDeps {
pub stats: Arc<dyn StatsRepository>,
pub diary: Arc<dyn DiaryQuery>,
@@ -18,18 +15,12 @@ pub struct GetLocalProfileDeps {
pub instance: InstanceIdentity,
}
/// The federated half of the former `GetProfileDeps` split. No `user`/`instance` —
/// the federated handler builds identity from the resolved remote actor and never
/// reads a local user row for this path.
pub struct GetFederatedProfileStatsDeps {
pub stats: Arc<dyn StatsRepository>,
pub diary: Arc<dyn DiaryQuery>,
pub social_query: Arc<dyn FollowGraphQuery>,
}
/// Backs page-chrome data (email/role badge, pending-follow badge). See the error
/// policy note on `get_page_viewer::execute`: this deliberately propagates errors
/// rather than degrading internally — the caller decides chrome degrades.
pub struct GetPageViewerDeps {
pub user: Arc<dyn UserRepository>,
pub follow_graph: Arc<dyn FollowGraphQuery>,
@@ -82,9 +73,6 @@ pub struct AuthorizeAdminDeps {
pub user: Arc<dyn UserRepository>,
}
/// `Option` mirrors `Repositories::federated_profile` — genuine optional
/// configuration (federation on/off), not a container-shape workaround. Same
/// exemption as `Services::person_enrichment`.
pub struct GetFederatedProfileDeps {
pub federated_profile: Option<Arc<dyn FederatedProfileQuery>>,
}

View File

@@ -4,18 +4,6 @@ use uuid::Uuid;
use crate::users::deps::GetFederatedProfileDeps;
/// Looks up a federated (remote) profile by synthetic user id — the fallback
/// path `get_user_profile` (`handlers/users.rs`) takes when the local-profile
/// lookup comes back `NotFound`.
///
/// Federation being disabled (`deps.federated_profile` is `None`) collapses to
/// `Ok(None)`, the same value a present-but-empty lookup would return. That's
/// deliberate: the handler's `if let Ok(Some(fed)) = ...` treats "port absent",
/// "call returned `Ok(None)`", and "call returned `Err`" identically — all three
/// fall through to a 404 — and this use case must not make any of those three
/// distinguishable to a caller that cannot act on the difference. An `Err` from
/// the port is propagated unchanged; the swallow stays at the handler, exactly
/// where it already was.
pub async fn execute(
deps: &GetFederatedProfileDeps,
user_id: Uuid,

View File

@@ -30,10 +30,6 @@ pub struct ProfileIdentity {
pub actor_url: String,
pub avatar_url: Option<String>,
pub banner_url: Option<String>,
/// Only the HTML profile handler reads this (it derives its "display name"
/// from the email's local part, not from `username` — see
/// `handlers/users.rs::get_user_profile_html`). Sourced from the same local
/// `User` row as every other identity field.
pub email: String,
}

View File

@@ -1,12 +1,3 @@
//! Absorbs BOTH of `handlers/users.rs::get_profile_settings`'s repository calls:
//! the user lookup (`repos.user.find_by_id`) and the profile_fields join
//! (`repos.profile_fields.get_fields`) — plus the avatar/banner URL derivation via
//! `InstanceIdentity`, following the same pattern `get_local_profile` uses.
//!
//! Error policy: profile fields are primary content for this page (it's the page
//! whose entire job is editing them), not page chrome, so a failed join
//! propagates as `Err` rather than silently degrading to an empty list.
use domain::{errors::DomainError, value_objects::UserId};
use uuid::Uuid;
@@ -18,10 +9,6 @@ pub struct ProfileSettings {
pub bio: Option<String>,
pub avatar_url: Option<String>,
pub banner_url: Option<String>,
/// Not in the brief's interface sketch, but the deleted handler code read
/// `user.also_known_as()` and rendered it in `ProfileSettingsTemplate` — this
/// use case absorbs that read too, or the settings page would silently stop
/// showing it.
pub also_known_as: Option<String>,
pub fields: Vec<(String, String)>,
}

View File

@@ -3,14 +3,6 @@ use crate::users::deps::GetFederatedProfileStatsDeps;
use crate::users::get_federated_profile_stats;
use crate::users::queries::{GetUserProfileQuery, ProfileView};
/// Mirrors exactly how `build_federated_profile_response`
/// (`crates/presentation/src/handlers/users.rs`) calls `execute`: a `user_id` that
/// has no row in the local `users` table (a federated/remote profile uses a
/// synthetic id — see `FederatedProfileQuery`), `is_own_profile: false`,
/// `include_remote: true`. Before ADR-0004's split this was the tolerant branch of
/// the unified `get_profile`; now it's simply this function's only behavior — it
/// never looks at the local `users` table at all, so there is nothing to be
/// tolerant about.
#[tokio::test]
async fn succeeds_for_a_user_id_with_no_local_row() {
let b = TestContextBuilder::new();

View File

@@ -54,10 +54,6 @@ fn unknown_query(user_id: uuid::Uuid) -> GetUserProfileQuery {
}
}
/// `get_local_profile` is the local-only half of the former `get_profile` split
/// (ADR-0004's closed wart). Unlike the old tolerant behavior, a user id with no
/// local row must be `NotFound` here — the empty-string identity sentinel this
/// replaces cannot be represented in `LocalProfileData` at all.
#[tokio::test]
async fn get_local_profile_is_not_found_for_unknown_user() {
let b = TestContextBuilder::new();
@@ -272,10 +268,6 @@ async fn populates_handle_and_actor_url_for_a_local_user() {
let user = user_repo.find_by_email(&email).await.unwrap().unwrap();
let uid = user.id().value();
// `InMemoryUserRepository::update_profile` now actually persists (previously a
// no-op stub) — use it to give this test a real display_name/bio to assert,
// rather than asserting `None` for a mapping that could just as easily be
// wrong, inverted, or hardcoded.
let update_deps = UpdateProfileDeps {
user: user_repo.clone(),
object_storage: b.object_storage.clone(),

View File

@@ -34,9 +34,6 @@ async fn setup_user(b: &TestContextBuilder, email: &str, username: &str) -> uuid
user.id().value()
}
/// `get_page_viewer` absorbs `helpers.rs::build_page_context`'s inline pending-count
/// computation. It must reuse `FollowGraphQuery::count_pending_followers` rather than
/// re-deriving the count from raw follow rows.
#[tokio::test]
async fn get_page_viewer_reports_pending_follower_count() {
let b = TestContextBuilder::new();

View File

@@ -8,10 +8,6 @@ use crate::test_helpers::TestContextBuilder;
use crate::users::deps::GetProfileSettingsDeps;
use crate::users::get_profile_settings;
/// `get_profile_settings` absorbs BOTH of the handler's calls: the user lookup
/// (`repos.user.find_by_id`) and the profile_fields join
/// (`repos.profile_fields.get_fields`). This proves the join actually moved in,
/// not just the user lookup.
#[tokio::test]
async fn get_profile_settings_includes_profile_fields() {
let b = TestContextBuilder::new();

View File

@@ -4,9 +4,6 @@ use crate::test_helpers::TestContextBuilder;
use crate::users::deps::ResolveUsernameDeps;
use crate::users::resolve_username_to_id;
/// `resolve_username_to_id` absorbs `handlers/users.rs::get_user_by_username`'s
/// `repos.user.find_by_username()` call. Unknown usernames must resolve to `None`,
/// not an error — the handler turns `None` into a 404, same as today.
#[tokio::test]
async fn resolve_username_to_id_returns_none_for_unknown() {
let b = TestContextBuilder::new();