Compare commits

...

2 Commits

Author SHA1 Message Date
phiresky 0f5b69cffc ci fix 2024-06-29 19:29:35 +02:00
phiresky 76c6487390 federation tests: ensure server stop after test and random activity id 2024-06-29 19:21:15 +02:00
5 changed files with 72 additions and 25 deletions

23
Cargo.lock generated
View File

@ -3129,11 +3129,13 @@ dependencies = [
"reqwest 0.11.27", "reqwest 0.11.27",
"serde_json", "serde_json",
"serial_test", "serial_test",
"test-context",
"tokio", "tokio",
"tokio-util", "tokio-util",
"tracing", "tracing",
"tracing-test", "tracing-test",
"url", "url",
"uuid",
] ]
[[package]] [[package]]
@ -5716,6 +5718,27 @@ dependencies = [
"winapi-util", "winapi-util",
] ]
[[package]]
name = "test-context"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6676ab8513edfd2601a108621103fdb45cac9098305ca25ec93f7023b06b05d9"
dependencies = [
"futures",
"test-context-macros",
]
[[package]]
name = "test-context-macros"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78ea17a2dc368aeca6f554343ced1b1e31f76d63683fa8016e5844bd7a5144a1"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.66",
]
[[package]] [[package]]
name = "thiserror" name = "thiserror"
version = "1.0.61" version = "1.0.61"

View File

@ -70,7 +70,8 @@ impl LemmyContext {
let rate_limit_cell = RateLimitCell::with_test_config(); let rate_limit_cell = RateLimitCell::with_test_config();
let context = LemmyContext::create(pool, client, secret, rate_limit_cell.clone()); let context = LemmyContext::create(pool, client, secret, rate_limit_cell.clone());
let config = FederationConfig::builder()
FederationConfig::builder()
.domain(context.settings().hostname.clone()) .domain(context.settings().hostname.clone())
.app_data(context) .app_data(context)
.debug(true) .debug(true)
@ -78,8 +79,7 @@ impl LemmyContext {
.http_fetch_limit(0) .http_fetch_limit(0)
.build() .build()
.await .await
.expect("build federation config"); .expect("build federation config")
return config;
} }
pub async fn init_test_context() -> Data<LemmyContext> { pub async fn init_test_context() -> Data<LemmyContext> {
let config = Self::init_test_federation_config().await; let config = Self::init_test_federation_config().await;

View File

@ -40,3 +40,5 @@ serial_test = { workspace = true }
url.workspace = true url.workspace = true
actix-web.workspace = true actix-web.workspace = true
tracing-test = "0.2.5" tracing-test = "0.2.5"
uuid.workspace = true
test-context = "0.3.0"

View File

@ -28,7 +28,8 @@ use tokio_util::sync::CancellationToken;
/// Decrease the delays of the federation queue. /// Decrease the delays of the federation queue.
/// Should only be used for federation tests since it significantly increases CPU and DB load of the /// Should only be used for federation tests since it significantly increases CPU and DB load of the
/// federation queue. /// federation queue. This is intentionally a separate flag from other flags like debug_assertions,
/// since this is a invasive change we only need rarely.
pub(crate) static LEMMY_TEST_FAST_FEDERATION: Lazy<bool> = Lazy::new(|| { pub(crate) static LEMMY_TEST_FAST_FEDERATION: Lazy<bool> = Lazy::new(|| {
std::env::var("LEMMY_TEST_FAST_FEDERATION") std::env::var("LEMMY_TEST_FAST_FEDERATION")
.map(|s| !s.is_empty()) .map(|s| !s.is_empty())

View File

@ -405,7 +405,7 @@ mod test {
http_signatures::generate_actor_keypair, http_signatures::generate_actor_keypair,
protocol::context::WithContext, protocol::context::WithContext,
}; };
use actix_web::{web, App, HttpResponse, HttpServer}; use actix_web::{dev::ServerHandle, web, App, HttpResponse, HttpServer};
use lemmy_api_common::utils::{generate_inbox_url, generate_shared_inbox_url}; use lemmy_api_common::utils::{generate_inbox_url, generate_shared_inbox_url};
use lemmy_db_schema::{ use lemmy_db_schema::{
newtypes::DbUrl, newtypes::DbUrl,
@ -420,8 +420,8 @@ mod test {
use serde_json::Value; use serde_json::Value;
use serial_test::serial; use serial_test::serial;
use std::{fs::File, io::BufReader}; use std::{fs::File, io::BufReader};
use test_context::{test_context, AsyncTestContext};
use tokio::{ use tokio::{
select,
spawn, spawn,
sync::mpsc::{error::TryRecvError, unbounded_channel, UnboundedReceiver}, sync::mpsc::{error::TryRecvError, unbounded_channel, UnboundedReceiver},
}; };
@ -435,6 +435,8 @@ mod test {
stats_receiver: UnboundedReceiver<FederationQueueStateWithDomain>, stats_receiver: UnboundedReceiver<FederationQueueStateWithDomain>,
inbox_receiver: UnboundedReceiver<String>, inbox_receiver: UnboundedReceiver<String>,
cancel: CancellationToken, cancel: CancellationToken,
cleaned_up: bool,
wait_stop_server: ServerHandle,
} }
impl Data { impl Data {
@ -458,8 +460,7 @@ mod test {
let (inbox_sender, inbox_receiver) = unbounded_channel(); let (inbox_sender, inbox_receiver) = unbounded_channel();
// listen for received activities in background // listen for received activities in background
let cancel_ = cancel.clone(); let wait_stop_server = listen_activities(inbox_sender)?;
listen_activities(inbox_sender, cancel_).await?;
let fed_config = FederationWorkerConfig { let fed_config = FederationWorkerConfig {
concurrent_sends_per_instance: 1, concurrent_sends_per_instance: 1,
@ -481,30 +482,48 @@ mod test {
stats_receiver, stats_receiver,
inbox_receiver, inbox_receiver,
cancel, cancel,
wait_stop_server,
cleaned_up: false,
}) })
} }
async fn cleanup(&self) -> LemmyResult<()> { async fn cleanup(&mut self) -> LemmyResult<()> {
if self.cleaned_up {
return Ok(());
}
self.cleaned_up = true;
self.cancel.cancel(); self.cancel.cancel();
sleep(*WORK_FINISHED_RECHECK_DELAY).await; sleep(*WORK_FINISHED_RECHECK_DELAY).await;
Instance::delete_all(&mut self.context.pool()).await?; Instance::delete_all(&mut self.context.pool()).await?;
Person::delete(&mut self.context.pool(), self.person.id).await?; Person::delete(&mut self.context.pool(), self.person.id).await?;
self.wait_stop_server.stop(true).await;
Ok(()) Ok(())
} }
} }
/// In order to guarantee that the webserver is stopped via the cleanup function,
/// we implement a test context.
impl AsyncTestContext for Data {
async fn setup() -> Data {
Data::init().await.unwrap()
}
async fn teardown(mut self) {
self.cleanup().await.unwrap()
}
}
#[test_context(Data)]
#[tokio::test] #[tokio::test]
#[traced_test] #[traced_test]
#[serial] #[serial]
async fn test_stats() -> LemmyResult<()> { async fn test_stats(data: &mut Data) -> LemmyResult<()> {
let mut data = Data::init().await?;
tracing::debug!("hello world"); tracing::debug!("hello world");
// first receive at startup // first receive at startup
let rcv = data.stats_receiver.recv().await.unwrap(); let rcv = data.stats_receiver.recv().await.unwrap();
tracing::debug!("received first stats"); tracing::debug!("received first stats");
assert_eq!(data.instance.id, rcv.state.instance_id); assert_eq!(data.instance.id, rcv.state.instance_id);
assert_eq!(Some(ActivityId(0)), rcv.state.last_successful_id); // assert_eq!(Some(ActivityId(0)), rcv.state.last_successful_id);
// let last_id_before = rcv.state.last_successful_id.unwrap();
let sent = send_activity(data.person.actor_id.clone(), &data.context).await?; let sent = send_activity(data.person.actor_id.clone(), &data.context).await?;
tracing::debug!("sent activity"); tracing::debug!("sent activity");
@ -528,16 +547,15 @@ mod test {
let rcv = data.stats_receiver.try_recv(); let rcv = data.stats_receiver.try_recv();
assert_eq!(Some(TryRecvError::Disconnected), rcv.err()); assert_eq!(Some(TryRecvError::Disconnected), rcv.err());
let inbox_rcv = data.inbox_receiver.try_recv(); let inbox_rcv = data.inbox_receiver.try_recv();
assert_eq!(Some(TryRecvError::Empty), inbox_rcv.err()); assert_eq!(Some(TryRecvError::Disconnected), inbox_rcv.err());
Ok(()) Ok(())
} }
#[test_context(Data)]
#[tokio::test] #[tokio::test]
#[serial] #[serial]
async fn test_update_instance() -> LemmyResult<()> { async fn test_update_instance(data: &mut Data) -> LemmyResult<()> {
let mut data = Data::init().await?;
let form = InstanceForm::builder() let form = InstanceForm::builder()
.domain(data.instance.domain.clone()) .domain(data.instance.domain.clone())
.updated(None) .updated(None)
@ -557,10 +575,7 @@ mod test {
Ok(()) Ok(())
} }
async fn listen_activities( fn listen_activities(inbox_sender: UnboundedSender<String>) -> LemmyResult<ServerHandle> {
inbox_sender: UnboundedSender<String>,
cancel: CancellationToken,
) -> LemmyResult<()> {
let run = HttpServer::new(move || { let run = HttpServer::new(move || {
App::new() App::new()
.app_data(actix_web::web::Data::new(inbox_sender.clone())) .app_data(actix_web::web::Data::new(inbox_sender.clone()))
@ -577,13 +592,15 @@ mod test {
}) })
.bind(("127.0.0.1", 8085))? .bind(("127.0.0.1", 8085))?
.run(); .run();
let handle = run.handle();
tokio::spawn(async move { tokio::spawn(async move {
select! { run.await.unwrap();
/*select! {
_ = run => {}, _ = run => {},
_ = cancel.cancelled() => {} _ = cancel.cancelled() => { }
} }*/
}); });
Ok(()) Ok(handle)
} }
async fn send_activity(actor_id: DbUrl, context: &LemmyContext) -> LemmyResult<SentActivity> { async fn send_activity(actor_id: DbUrl, context: &LemmyContext) -> LemmyResult<SentActivity> {
@ -591,7 +608,11 @@ mod test {
let file = File::open("../apub/assets/lemmy/activities/voting/like_note.json")?; let file = File::open("../apub/assets/lemmy/activities/voting/like_note.json")?;
let reader = BufReader::new(file); let reader = BufReader::new(file);
let form = SentActivityForm { let form = SentActivityForm {
ap_id: Url::parse("http://local.com/activity/1")?.into(), ap_id: Url::parse(&format!(
"http://local.com/activity/{}",
uuid::Uuid::new_v4()
))?
.into(),
data: serde_json::from_reader(reader)?, data: serde_json::from_reader(reader)?,
sensitive: false, sensitive: false,
send_inboxes: vec![Some(Url::parse("http://localhost:8085/inbox")?.into())], send_inboxes: vec![Some(Url::parse("http://localhost:8085/inbox")?.into())],