diff --git a/src/tests/worker/mod.rs b/src/tests/worker/mod.rs index 07eb5cb02f4..f6f21e41382 100644 --- a/src/tests/worker/mod.rs +++ b/src/tests/worker/mod.rs @@ -1,3 +1,6 @@ mod git; +mod readmes; mod rss; +mod send_publish_notifications; mod sync_admins; +mod update_default_version; diff --git a/src/tests/worker/readmes.rs b/src/tests/worker/readmes.rs new file mode 100644 index 00000000000..81d091ebbbf --- /dev/null +++ b/src/tests/worker/readmes.rs @@ -0,0 +1,17 @@ +use crate::tests::util::TestApp; +use crate::worker::jobs; +use crates_io_worker::BackgroundJob; + +#[tokio::test(flavor = "multi_thread")] +async fn skips_when_crate_deleted() -> anyhow::Result<()> { + let (app, _) = TestApp::full().empty().await; + let mut conn = app.db_conn().await; + + let job = + jobs::RenderAndUploadReadme::new(-1, "deleted".to_string(), ".".to_string(), None, None); + + job.enqueue(&mut conn).await?; + app.run_pending_background_jobs().await; + + Ok(()) +} diff --git a/src/tests/worker/send_publish_notifications.rs b/src/tests/worker/send_publish_notifications.rs new file mode 100644 index 00000000000..98dd4faef17 --- /dev/null +++ b/src/tests/worker/send_publish_notifications.rs @@ -0,0 +1,16 @@ +use crate::tests::util::TestApp; +use crate::worker::jobs; +use crates_io_worker::BackgroundJob; + +#[tokio::test(flavor = "multi_thread")] +async fn skips_when_crate_deleted() -> anyhow::Result<()> { + let (app, _) = TestApp::full().empty().await; + let mut conn = app.db_conn().await; + + let job = jobs::SendPublishNotificationsJob::new(-1); + + job.enqueue(&mut conn).await?; + app.run_pending_background_jobs().await; + + Ok(()) +} diff --git a/src/tests/worker/update_default_version.rs b/src/tests/worker/update_default_version.rs new file mode 100644 index 00000000000..ca38a6fee90 --- /dev/null +++ b/src/tests/worker/update_default_version.rs @@ -0,0 +1,16 @@ +use crate::tests::util::TestApp; +use crate::worker::jobs; +use crates_io_worker::BackgroundJob; + +#[tokio::test(flavor = "multi_thread")] +async fn skips_when_crate_deleted() -> anyhow::Result<()> { + let (app, _) = TestApp::full().empty().await; + let mut conn = app.db_conn().await; + + let job = jobs::UpdateDefaultVersion::new(-1); + + job.enqueue(&mut conn).await?; + app.run_pending_background_jobs().await; + + Ok(()) +} diff --git a/src/worker/jobs/readmes.rs b/src/worker/jobs/readmes.rs index b3cdfcdf9c5..86c2fbce112 100644 --- a/src/worker/jobs/readmes.rs +++ b/src/worker/jobs/readmes.rs @@ -9,7 +9,7 @@ use diesel_async::AsyncConnection; use diesel_async::scoped_futures::ScopedFutureExt; use serde::{Deserialize, Serialize}; use std::sync::Arc; -use tracing::{info, instrument}; +use tracing::{info, instrument, warn}; #[derive(Clone, Serialize, Deserialize)] pub struct RenderAndUploadReadme { @@ -70,13 +70,31 @@ impl BackgroundJob for RenderAndUploadReadme { let mut conn = env.deadpool.get().await?; conn.transaction(|conn| { async move { - Version::record_readme_rendering(job.version_id, conn).await?; - let (crate_name, vers): (String, String) = versions::table - .find(job.version_id) - .inner_join(crates::table) - .select((crates::name, versions::num)) - .first(conn) - .await?; + let (crate_name, vers): (String, String) = + match Version::record_readme_rendering(job.version_id, conn) + .await + .and( + versions::table + .find(job.version_id) + .inner_join(crates::table) + .select((crates::name, versions::num)) + .first(conn) + .await, + ) { + Ok(r) => r, + Err(diesel::result::Error::DatabaseError( + diesel::result::DatabaseErrorKind::ForeignKeyViolation, + .., + )) + | Err(diesel::result::Error::NotFound) => { + warn!( + "Skipping rendering README for version {}: no version found", + job.version_id + ); + return Ok(()); + } + Err(err) => return Err(err.into()), + }; tracing::Span::current().record("krate.name", tracing::field::display(&crate_name)); diff --git a/src/worker/jobs/send_publish_notifications.rs b/src/worker/jobs/send_publish_notifications.rs index fea389271c6..5fb733a1ed5 100644 --- a/src/worker/jobs/send_publish_notifications.rs +++ b/src/worker/jobs/send_publish_notifications.rs @@ -39,7 +39,12 @@ impl BackgroundJob for SendPublishNotificationsJob { let mut conn = ctx.deadpool.get().await?; // Get crate name, version and other publish details - let publish_details = PublishDetails::for_version(version_id, &mut conn).await?; + let Some(publish_details) = PublishDetails::for_version(version_id, &mut conn).await? + else { + warn!("Skipping publish notifications for {version_id}: no version found"); + + return Ok(()); + }; let publish_time = publish_details .publish_time @@ -157,7 +162,10 @@ struct PublishDetails { } impl PublishDetails { - async fn for_version(version_id: i32, conn: &mut AsyncPgConnection) -> QueryResult { + async fn for_version( + version_id: i32, + conn: &mut AsyncPgConnection, + ) -> QueryResult> { versions::table .find(version_id) .inner_join(crates::table) @@ -165,5 +173,6 @@ impl PublishDetails { .select(PublishDetails::as_select()) .first(conn) .await + .optional() } } diff --git a/src/worker/jobs/update_default_version.rs b/src/worker/jobs/update_default_version.rs index 2f620ccec61..e2174152ecd 100644 --- a/src/worker/jobs/update_default_version.rs +++ b/src/worker/jobs/update_default_version.rs @@ -7,7 +7,7 @@ use diesel::prelude::*; use diesel_async::RunQueryDsl; use serde::{Deserialize, Serialize}; use std::sync::Arc; -use tracing::info; +use tracing::{info, warn}; #[derive(Serialize, Deserialize)] pub struct UpdateDefaultVersion { @@ -32,16 +32,28 @@ impl BackgroundJob for UpdateDefaultVersion { info!("Updating default version for crate {crate_id}"); let mut conn = ctx.deadpool.get().await?; - update_default_version(crate_id, &mut conn).await?; - - // Get the crate name for OG image generation - let crate_name: String = crates::table - .filter(crates::id.eq(crate_id)) - .select(crates::name) - .first(&mut conn) - .await?; + let crate_name = update_default_version(crate_id, &mut conn).await.and( + // Get the crate name for OG image generation + crates::table + .filter(crates::id.eq(crate_id)) + .select(crates::name) + .first::(&mut conn) + .await, + ); + + if matches!( + crate_name, + Err(diesel::result::Error::DatabaseError( + diesel::result::DatabaseErrorKind::ForeignKeyViolation, + .., + )) | Err(diesel::result::Error::NotFound) + ) { + warn!("Skipping update default version for crate for {crate_id}: no crate found"); + return Ok(()); + } // Generate OG image after updating default version + let crate_name = crate_name?; info!("Enqueueing OG image generation for crate {crate_name}"); GenerateOgImage::new(crate_name).enqueue(&mut conn).await?;