141 lines
4.5 KiB
Rust
141 lines
4.5 KiB
Rust
//! The composition root assembles on a fresh database, with every adapter real.
|
|
|
|
mod support;
|
|
|
|
#[tokio::test]
|
|
async fn server_builds_on_a_fresh_database() {
|
|
let pool = support::memory_pool().await;
|
|
let router = bootstrap::build_server_on(&support::test_config(), pool)
|
|
.await
|
|
.expect("server builds");
|
|
|
|
let (status, _, _) = support::send(&router, support::get("/health", None)).await;
|
|
assert_eq!(status, 200);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn worker_builds_on_a_fresh_database() {
|
|
let pool = support::memory_pool().await;
|
|
bootstrap::build_worker_on(&support::test_config(), pool)
|
|
.await
|
|
.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() {
|
|
use axum::http::Method;
|
|
|
|
let (router, _pool) = support::server().await;
|
|
let alice = support::register(&router, "alice", "alice@example.com").await;
|
|
let bob = support::register(&router, "bob", "bob@example.com").await;
|
|
|
|
let (status, _, body) = support::send(
|
|
&router,
|
|
support::json_request(
|
|
Method::POST,
|
|
"/api/v1/social/follow",
|
|
Some(&alice.token),
|
|
serde_json::json!({ "handle": "bob@localhost:3000" }),
|
|
),
|
|
)
|
|
.await;
|
|
assert_eq!(status, 200, "follow: {body}");
|
|
|
|
let (status, _, body) = support::send(
|
|
&router,
|
|
axum::http::Request::builder()
|
|
.uri("/api/v1/social/followers/pending")
|
|
.header("authorization", format!("Bearer {}", bob.token))
|
|
.body(axum::body::Body::empty())
|
|
.unwrap(),
|
|
)
|
|
.await;
|
|
assert_eq!(status, 200, "pending followers: {body}");
|
|
let followers: serde_json::Value = serde_json::from_str(&body).unwrap();
|
|
let actors = followers["actors"].as_array().unwrap();
|
|
assert_eq!(
|
|
actors.len(),
|
|
1,
|
|
"bob should have one pending follower: {body}"
|
|
);
|
|
assert!(
|
|
body.contains(&alice.user_id) || body.contains("alice"),
|
|
"follower should be alice: {body}"
|
|
);
|
|
}
|
|
|
|
/// 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;
|
|
let mut config = support::test_config();
|
|
config.events.bus_backend = "nats".into();
|
|
config.nats.url = Some("nats://127.0.0.1:1".into());
|
|
let router = bootstrap::build_server_on(&config, pool.clone())
|
|
.await
|
|
.expect("server builds without a reachable NATS");
|
|
let session = support::register(&router, "alice", "alice@example.com").await;
|
|
|
|
let (status, _, body) = support::send(
|
|
&router,
|
|
support::json_request(
|
|
axum::http::Method::POST,
|
|
"/api/v1/reviews",
|
|
Some(&session.token),
|
|
serde_json::json!({
|
|
"manual_title": "Outbox",
|
|
"manual_release_year": 2001,
|
|
"rating": 4,
|
|
"comment": "queued",
|
|
"watched_at": "2024-01-01"
|
|
}),
|
|
),
|
|
)
|
|
.await;
|
|
assert_eq!(status, 201, "log review: {body}");
|
|
|
|
let queued: i64 = sqlx::query_scalar(
|
|
"SELECT COUNT(*) FROM event_queue WHERE event_type = 'ReviewLogged' AND event_id IS NOT NULL",
|
|
)
|
|
.fetch_one(&pool)
|
|
.await
|
|
.unwrap();
|
|
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;
|
|
let worker = bootstrap::build_worker_on(&support::test_config(), pool)
|
|
.await
|
|
.expect("worker builds");
|
|
|
|
let names = worker.handler_names();
|
|
let unique: std::collections::HashSet<_> = names.iter().collect();
|
|
assert_eq!(
|
|
unique.len(),
|
|
names.len(),
|
|
"duplicate handler name in {names:?}"
|
|
);
|
|
for core in [
|
|
"poster_sync",
|
|
"image_cleanup",
|
|
"search_cleanup",
|
|
"search_reindex",
|
|
] {
|
|
assert!(names.contains(&core), "{core} missing from {names:?}");
|
|
}
|
|
assert_eq!(
|
|
names.contains(&"activitypub"),
|
|
cfg!(feature = "federation"),
|
|
"{names:?}"
|
|
);
|
|
}
|