strip session-added comments
Some checks failed
CI / Check / Test (push) Has been cancelled
CI / Check / Test (pull_request) Has been cancelled

This commit is contained in:
2026-10-26 10:32:38 +01:00
parent a270632e87
commit f020108024
138 changed files with 0 additions and 719 deletions

View File

@@ -1,5 +1,3 @@
//! Account deletion over HTTP: JSON and classic endpoints on the real wiring.
mod support;
use axum::{
@@ -253,7 +251,6 @@ async fn classic_admin_self_delete_logs_out_unless_last_admin() {
promote(&pool, &alice.user_id).await;
let uri = format!("/users/{}/delete", alice.user_id);
// Sole admin: refused, redirected back with an error, still logged in.
let (status, headers, _) = send(&router, classic_post(&uri, &alice.cookie, PASSWORD)).await;
assert_eq!(status, 303);
assert!(location(&headers).contains("error="), "{headers:?}");
@@ -261,7 +258,6 @@ async fn classic_admin_self_delete_logs_out_unless_last_admin() {
let (status, _, _) = send(&router, get(&format!("/users/{}", alice.user_id), None)).await;
assert_eq!(status, 200);
// With a second admin the self-delete goes through and ends the session.
promote(&pool, &bob.user_id).await;
let (status, headers, _) = send(&router, classic_post(&uri, &alice.cookie, PASSWORD)).await;
assert_eq!(status, 303);
@@ -283,7 +279,6 @@ async fn delete_forms_render_only_where_they_apply() {
let (_, _, body) = send(&router, get("/settings/profile", Some(&alice.cookie))).await;
assert!(body.contains(own_delete), "owner settings page");
// A non-admin viewing someone else, and anonymous viewers, see no form.
let bob_page = format!("/users/{}", bob.user_id);
let (_, _, body) = send(&router, get(&bob_page, None)).await;
assert!(!body.contains(&user_delete(&bob.user_id)), "anonymous");

View File

@@ -1,7 +1,3 @@
//! End to end on the real wiring: a `UserDeleted` event in the outbox makes
//! the worker tell followers, purge the account, and leave the tombstone that
//! the queued deliveries are signed from.
mod support;
use std::time::Duration;
@@ -59,8 +55,6 @@ async fn the_worker_purges_a_deleted_account_and_frees_its_email() {
assert!(!again.user_id.is_empty(), "the email can register again");
}
/// With a follower, the Delete is enqueued for delivery even though the
/// account is already tombstoned, and the keypair outlives the purge.
#[cfg(feature = "federation")]
#[tokio::test]
async fn followers_are_told_before_the_purge_and_the_keypair_survives_it() {
@@ -103,7 +97,6 @@ async fn followers_are_told_before_the_purge_and_the_keypair_survives_it() {
"SELECT COUNT(*) FROM users WHERE username = 'alice' AND purged_at IS NOT NULL",
)
.await;
// Deliveries are enqueued from a spawned task, so wait for them too.
let enqueued = wait_for(
&pool,
"SELECT COUNT(*) FROM event_queue WHERE event_type = 'FederationDeliveryRequested'",

View File

@@ -1,6 +1,3 @@
//! The HTTP surface of the production app, built through `bootstrap` on an
//! in-memory database with real adapters.
mod support;
use axum::{
@@ -16,7 +13,6 @@ async fn test_app() -> Router {
support::server().await.0
}
/// An app with one registered user; returns its pool, id and bearer token.
async fn test_app_with_user() -> (Router, SqlitePool, Uuid, String) {
let (app, pool) = support::server().await;
let session = support::register(&app, "insights", "insights@example.com").await;

View File

@@ -1,5 +1,3 @@
//! The composition root assembles on a fresh database, with every adapter real.
mod support;
#[tokio::test]
@@ -21,9 +19,6 @@ async fn worker_builds_on_a_fresh_database() {
.expect("worker builds");
}
/// Without the federation feature, following another local user still works
/// (the request lands as pending for the target):
/// the local social ports are wired in place of the ActivityPub ones.
#[cfg(not(feature = "federation"))]
#[tokio::test]
async fn federation_off_build_wires_local_social() {
@@ -68,8 +63,6 @@ async fn federation_off_build_wires_local_social() {
);
}
/// The server never talks to the bus: whatever `EVENT_BUS_BACKEND` says, a
/// change and its event land in the outbox table. No NATS server is reachable here.
#[tokio::test]
async fn server_publishes_to_the_outbox_even_when_bus_is_nats() {
let pool = support::memory_pool().await;
@@ -108,8 +101,6 @@ async fn server_publishes_to_the_outbox_even_when_bus_is_nats() {
assert_eq!(queued, 1);
}
/// Handler names key the processed-event ledger, so each must be unique. The
/// check runs over the real wiring, in both federation builds.
#[tokio::test]
async fn handler_names_are_unique() {
let pool = support::memory_pool().await;
@@ -126,8 +117,6 @@ async fn handler_names_are_unique() {
);
let mut sorted = names.clone();
sorted.sort_unstable();
// Names key the ledger: a rename re-runs events inside the retention
// window, so changing this list must be a deliberate edit.
let mut expected = vec![
"account_purge",
"image_cleanup",
@@ -143,7 +132,6 @@ async fn handler_names_are_unique() {
expected.extend(["activitypub", "follow_backfill"]);
}
expected.sort_unstable();
// Image conversion ("image_conversion") is disabled in the test config.
assert_eq!(sorted, expected);
}
@@ -157,8 +145,6 @@ async fn queued(pool: &sqlx::SqlitePool, event_type: &str) -> i64 {
.unwrap()
}
/// A local follow writes both follow rows and its event in one commit, in
/// both federation builds.
#[tokio::test]
async fn a_local_follow_lands_with_its_event_in_the_outbox() {
let (router, pool) = support::server().await;
@@ -189,8 +175,6 @@ async fn a_local_follow_lands_with_its_event_in_the_outbox() {
assert_eq!(queued(&pool, "FollowRequested").await, 1);
}
/// The k_ap path owns its transaction, so the adapter publishes the event
/// itself once the k_ap write has succeeded.
#[cfg(feature = "federation")]
#[tokio::test]
async fn a_remote_block_publishes_its_event_after_the_k_ap_write() {
@@ -217,9 +201,6 @@ async fn a_remote_block_publishes_its_event_after_the_k_ap_write() {
assert_eq!(queued(&pool, "ActorBlocked").await, 1);
}
/// A token outlives the account that owns it, so every authenticated route
/// must re-check the account: a deleted user gets exactly what an anonymous
/// caller gets.
#[tokio::test]
async fn a_deleted_users_token_is_rejected() {
let (router, pool) = support::server().await;
@@ -255,9 +236,6 @@ async fn a_deleted_users_token_is_rejected() {
assert_eq!(headers.get("location").unwrap(), "/login");
}
/// A deleted account stays resolvable inside k-ap (to sign its `Delete`), but
/// every ActivityPub document for it answers 410, while the classic profile
/// page stays 404.
#[cfg(feature = "federation")]
#[tokio::test]
async fn actor_document_for_a_tombstone_is_gone() {
@@ -301,8 +279,6 @@ async fn actor_document_for_a_tombstone_is_gone() {
assert_eq!(status, 404, "the classic profile page stays 404");
}
/// Remote servers posting to a deleted account's inbox are told it is gone,
/// before any signature check.
#[cfg(feature = "federation")]
#[tokio::test]
async fn posting_to_a_tombstones_inbox_is_gone() {

View File

@@ -1,5 +1,3 @@
//! Configuration values reach the running app through the composition root.
mod support;
use axum::{

View File

@@ -395,8 +395,6 @@ async fn following_collection_returns_500_on_federation_error() {
assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR);
}
// --- One refusal shape ---
async fn refusal_body(resp: axum::response::Response) -> serde_json::Value {
let bytes = axum::body::to_bytes(resp.into_body(), usize::MAX)
.await

View File

@@ -246,7 +246,6 @@ async fn admin_api_user_rejects_an_unknown_user() {
)
.await
.unwrap();
// An account the store does not hold is inactive, so it is unauthenticated.
assert_eq!(resp.status(), StatusCode::UNAUTHORIZED);
}
@@ -338,7 +337,6 @@ async fn admin_user_rejects_an_unknown_user() {
)
.await
.unwrap();
// An account the store does not hold is inactive: back to the login page.
assert_eq!(resp.status(), StatusCode::SEE_OTHER);
assert_eq!(resp.headers().get("location").unwrap(), "/login");
}

View File

@@ -1,5 +1,3 @@
//! `AppState` fixtures: `Panic*` stubs everywhere, real prebuilt `deps` built through
//! `bootstrap::build_deps`, with per-port overrides injected BEFORE the deps are built.
#![allow(dead_code)]
use application::config::AppSettings;
@@ -25,8 +23,6 @@ use domain::{
use http_axum::context::AppContext;
use std::sync::Arc;
// --- Local stubs ---
struct PanicReviewLogger;
#[async_trait::async_trait]
@@ -168,9 +164,6 @@ pub fn test_app_state_with_search_port(
build_test_state_from(repos, Arc::new(PanicAuthService))
}
/// Test state whose repositories are adjusted by `adjust` BEFORE `build_deps`
/// runs, for tests that need more than one stub swapped in (e.g. a follow graph
/// plus the live user that authenticated routes now look up).
pub fn test_app_state_with_repos(
adjust: impl FnOnce(&mut Repositories),
auth_service: Arc<dyn AuthService>,

View File

@@ -1,5 +1,3 @@
//! Crash recovery through the real worker dispatch and the real sqlite ledger.
mod support;
use std::sync::atomic::{AtomicU32, Ordering};
@@ -56,9 +54,6 @@ impl EventConsumer for Once {
}
}
/// A claim abandoned mid-handler blocks redeliveries (deferred, not failed)
/// until its lease lapses; then the next delivery takes it over and runs the
/// handler once, and later deliveries skip it.
#[tokio::test]
async fn an_abandoned_claim_defers_deliveries_until_the_lease_lapses_then_runs_once() {
let pool = support::memory_pool().await;
@@ -68,7 +63,6 @@ async fn an_abandoned_claim_defers_deliveries_until_the_lease_lapses_then_runs_o
Duration::from_secs(1),
));
let id = EventId::new();
// The crashed worker: claimed, never completed or released.
assert!(matches!(
ledger.begin(&id, "crashy").await.unwrap(),
Begin::Run(_)

View File

@@ -1,6 +1,3 @@
//! The relay claims outbox rows with the SQLite queue's own atomic claim and
//! settles each row from the sink's verdict, with fakes at the sink boundary.
mod support;
use std::sync::{Arc, Mutex};
@@ -20,9 +17,7 @@ use uuid::Uuid;
#[derive(Default)]
struct FakeSink {
fail: bool,
/// Fail this many calls, then succeed (a bus outage that ends).
fail_first: std::sync::atomic::AtomicUsize,
/// When set, `relay` signals `started` then takes this long.
slow: Option<Duration>,
started: Notify,
seen: Mutex<Vec<Option<EventId>>>,
@@ -133,8 +128,6 @@ async fn relay_marks_rows_done_only_after_publish_succeeds() {
#[tokio::test]
async fn a_failing_bus_never_spends_the_rows_attempts_and_the_row_relays_on_recovery() {
// Budget of 2 attempts, 3 consecutive bus failures: were failures counted
// as attempts the row would be dead-lettered before the bus recovers.
let (pool, publisher, queue) = outbox_with_attempts(2).await;
publisher.publish(&event()).await.unwrap();
let sink = Arc::new(FakeSink {

View File

@@ -1,6 +1,3 @@
//! Characterization of the classic HTML routes, feeds and the OpenAPI surface,
//! recorded before the HTTP layer moves. Statuses are what the app returns today.
mod support;
use axum::{
@@ -13,7 +10,6 @@ use support::{get, json_request, send};
const EMAIL: &str = "owner@example.com";
const USERNAME: &str = "owner";
/// Public classic pages. `{id}`, `{username}`, `{movie}` are replaced by seeded values.
const CLASSIC_VISITOR_PAGES: &[(&str, u16)] = &[
("/", 200),
("/users", 200),
@@ -36,7 +32,6 @@ const CLASSIC_VISITOR_PAGES: &[(&str, u16)] = &[
("/health", 200),
];
/// Pages that need the signed-in owner.
const CLASSIC_OWNER_PAGES: &[(&str, u16)] = &[
("/reviews/new", 200),
("/diary/export", 200),
@@ -64,12 +59,10 @@ const FEDERATION_VISITOR_PAGES: &[(&str, u16)] = &[
const FEDERATION_OWNER_PAGES: &[(&str, u16)] = &[
("/users/{id}/followers", 303),
("/users/{id}/following", 303),
// The registered owner is not an admin.
("/admin/blocked-domains", 403),
("/social/blocked", 200),
];
/// Without federation the collection URLs permanently redirect to the HTML lists.
#[cfg(not(feature = "federation"))]
const FEDERATION_OFF_PAGES: &[(&str, u16)] = &[
("/users/{id}/followers", 308),
@@ -80,11 +73,9 @@ struct Seed {
router: Router,
user_id: String,
movie_id: String,
/// Cookie header for the signed-in owner (csrf + token).
owner_cookie: String,
}
/// One user (registered through the classic form), one movie with a spoiler review, one goal.
async fn seed() -> Seed {
let (router, pool) = support::server().await;
let session = support::register(&router, USERNAME, EMAIL).await;
@@ -149,8 +140,6 @@ async fn seed() -> Seed {
}
}
/// Inserts a READY 2025 wrap-up record (user scope, or global when `user_id` is None),
/// the state the worker leaves behind after generating a report.
async fn seed_ready_wrapup(pool: &sqlx::SqlitePool, user_id: Option<&str>) {
use chrono::NaiveDate;
use domain::models::wrapup::{DateRange, WrapUpScope};
@@ -210,7 +199,6 @@ async fn every_classic_page_renders_for_a_visitor() {
#[cfg(not(feature = "federation"))]
check_table(&seed, FEDERATION_OFF_PAGES, None).await;
// A dropped wrapup route would also 404, so require the template itself.
for template in ["/wrapups/{id}/2025", "/wrapups/global/2025"] {
let (status, _, body) = send(&seed.router, get(&expand(&seed, template), None)).await;
assert_eq!(status, 200, "{template}");
@@ -220,7 +208,6 @@ async fn every_classic_page_renders_for_a_visitor() {
);
}
// The owner embeds this widget in an iframe on their personal website.
let (status, _, body) = send(
&seed.router,
get(&expand(&seed, "/users/{id}?embed=true"), None),
@@ -245,7 +232,6 @@ async fn every_classic_page_renders_for_the_signed_in_owner() {
#[tokio::test]
async fn feeds_and_openapi_are_served() {
let seed = seed().await;
// `/docs` (what users type) redirects to the Swagger UI at `/docs/`.
for (template, expected) in [
("/feed.rss", 200),
("/users/{id}/feed.rss", 200),

View File

@@ -1,5 +1,3 @@
//! Shared helpers for the bootstrap integration tests: a production app built
//! through `bootstrap` on an in-memory database, driven over HTTP.
#![allow(dead_code)]
use axum::{
@@ -29,7 +27,6 @@ pub fn test_config() -> config::AppConfig {
nats: Default::default(),
storage: Default::default(),
image_conversion: Default::default(),
// Required to build; never called (seeding logs reviews with manual titles).
metadata: config::MetadataConfig {
tmdb_api_key: Some("test-key".into()),
..Default::default()
@@ -94,7 +91,6 @@ pub fn json_request(
b.body(Body::from(body.to_string())).unwrap()
}
/// A single-connection in-memory pool (every connection would otherwise get its own database).
pub async fn memory_pool() -> sqlx::SqlitePool {
sqlx::sqlite::SqlitePoolOptions::new()
.max_connections(1)
@@ -103,7 +99,6 @@ pub async fn memory_pool() -> sqlx::SqlitePool {
.unwrap()
}
/// The production server router on a fresh in-memory database, plus its pool.
pub async fn server() -> (Router, sqlx::SqlitePool) {
let pool = memory_pool().await;
let router = bootstrap::build_server_on(&test_config(), pool.clone())
@@ -114,14 +109,10 @@ pub async fn server() -> (Router, sqlx::SqlitePool) {
pub struct Session {
pub user_id: String,
/// Bearer token for the JSON API.
pub token: String,
/// Cookie header for the classic UI (csrf + session).
pub cookie: String,
}
/// Registers through the classic form (GET for the csrf cookie, then POST with
/// the token), then logs in over the JSON API for a bearer token.
pub async fn register(router: &Router, username: &str, email: &str) -> Session {
let (status, headers, _) = send(router, get("/register", None)).await;
assert_eq!(status, 200, "GET /register");