diff --git a/.sqlx/query-75aa15d4c8898403835cdaf3d27c9973a3f45abc9cf73e171011a2ded4d97dfc.json b/.sqlx/query-75aa15d4c8898403835cdaf3d27c9973a3f45abc9cf73e171011a2ded4d97dfc.json new file mode 100644 index 000000000..60e1078e6 --- /dev/null +++ b/.sqlx/query-75aa15d4c8898403835cdaf3d27c9973a3f45abc9cf73e171011a2ded4d97dfc.json @@ -0,0 +1,15 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE builds\n SET\n build_status = 'failure',\n errors = $2\n WHERE id = $1", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Int4", + "Text" + ] + }, + "nullable": [] + }, + "hash": "75aa15d4c8898403835cdaf3d27c9973a3f45abc9cf73e171011a2ded4d97dfc" +} diff --git a/.sqlx/query-8965c22711d10959507118e8e58f4d76a1b03d3a8ca37c18b7a5b9296b94d4ea.json b/.sqlx/query-8965c22711d10959507118e8e58f4d76a1b03d3a8ca37c18b7a5b9296b94d4ea.json new file mode 100644 index 000000000..c7f51cc0a --- /dev/null +++ b/.sqlx/query-8965c22711d10959507118e8e58f4d76a1b03d3a8ca37c18b7a5b9296b94d4ea.json @@ -0,0 +1,51 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT\n rustc_version,\n docsrs_version,\n build_status as \"build_status: BuildStatus\",\n errors\n FROM builds\n WHERE id = $1", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "rustc_version", + "type_info": "Varchar" + }, + { + "ordinal": 1, + "name": "docsrs_version", + "type_info": "Varchar" + }, + { + "ordinal": 2, + "name": "build_status: BuildStatus", + "type_info": { + "Custom": { + "name": "build_status", + "kind": { + "Enum": [ + "in_progress", + "success", + "failure" + ] + } + } + } + }, + { + "ordinal": 3, + "name": "errors", + "type_info": "Text" + } + ], + "parameters": { + "Left": [ + "Int4" + ] + }, + "nullable": [ + true, + true, + false, + true + ] + }, + "hash": "8965c22711d10959507118e8e58f4d76a1b03d3a8ca37c18b7a5b9296b94d4ea" +} diff --git a/.sqlx/query-db5310437b42896e57cc7de345f64cef8932b6f5e37f9ce9480eb9684b8c88ce.json b/.sqlx/query-db5310437b42896e57cc7de345f64cef8932b6f5e37f9ce9480eb9684b8c88ce.json new file mode 100644 index 000000000..1aae76452 --- /dev/null +++ b/.sqlx/query-db5310437b42896e57cc7de345f64cef8932b6f5e37f9ce9480eb9684b8c88ce.json @@ -0,0 +1,52 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT\n rustc_version,\n docsrs_version,\n build_status as \"build_status: BuildStatus\",\n errors\n FROM\n crates as c\n INNER JOIN releases as r on c.id = r.crate_id\n INNER JOIN builds as b on b.rid = r.id\n WHERE c.name = $1 and r.version = $2", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "rustc_version", + "type_info": "Varchar" + }, + { + "ordinal": 1, + "name": "docsrs_version", + "type_info": "Varchar" + }, + { + "ordinal": 2, + "name": "build_status: BuildStatus", + "type_info": { + "Custom": { + "name": "build_status", + "kind": { + "Enum": [ + "in_progress", + "success", + "failure" + ] + } + } + } + }, + { + "ordinal": 3, + "name": "errors", + "type_info": "Text" + } + ], + "parameters": { + "Left": [ + "Text", + "Text" + ] + }, + "nullable": [ + true, + true, + false, + true + ] + }, + "hash": "db5310437b42896e57cc7de345f64cef8932b6f5e37f9ce9480eb9684b8c88ce" +} diff --git a/.sqlx/query-df2a6468276a174f7993ba7cb5740bd7ce1a9eeae15b48c36a8a5abeaaa4314c.json b/.sqlx/query-e579486925d3d1927232675b2a03d5f8dd826d7136cdbba1331faf6e64c1f5eb.json similarity index 85% rename from .sqlx/query-df2a6468276a174f7993ba7cb5740bd7ce1a9eeae15b48c36a8a5abeaaa4314c.json rename to .sqlx/query-e579486925d3d1927232675b2a03d5f8dd826d7136cdbba1331faf6e64c1f5eb.json index de1ffe1d0..92269065c 100644 --- a/.sqlx/query-df2a6468276a174f7993ba7cb5740bd7ce1a9eeae15b48c36a8a5abeaaa4314c.json +++ b/.sqlx/query-e579486925d3d1927232675b2a03d5f8dd826d7136cdbba1331faf6e64c1f5eb.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT\n builds.id,\n builds.rustc_version,\n builds.docsrs_version,\n builds.build_status as \"build_status: BuildStatus\",\n builds.build_time,\n builds.errors\n FROM builds\n INNER JOIN releases ON releases.id = builds.rid\n INNER JOIN crates ON releases.crate_id = crates.id\n WHERE crates.name = $1 AND releases.version = $2\n ORDER BY id DESC", + "query": "SELECT\n builds.id,\n builds.rustc_version,\n builds.docsrs_version,\n builds.build_status as \"build_status: BuildStatus\",\n builds.build_time,\n builds.errors\n FROM builds\n INNER JOIN releases ON releases.id = builds.rid\n INNER JOIN crates ON releases.crate_id = crates.id\n WHERE\n crates.name = $1 AND\n releases.version = $2 AND\n builds.build_status != 'in_progress'\n ORDER BY id DESC", "describe": { "columns": [ { @@ -60,5 +60,5 @@ true ] }, - "hash": "df2a6468276a174f7993ba7cb5740bd7ce1a9eeae15b48c36a8a5abeaaa4314c" + "hash": "e579486925d3d1927232675b2a03d5f8dd826d7136cdbba1331faf6e64c1f5eb" } diff --git a/migrations/20240313103708_make_release_fields_optional.down.sql b/migrations/20240313103708_make_release_fields_optional.down.sql index 90116a626..bdc4fc8fc 100644 --- a/migrations/20240313103708_make_release_fields_optional.down.sql +++ b/migrations/20240313103708_make_release_fields_optional.down.sql @@ -1,3 +1,18 @@ +DELETE FROM builds WHERE rid in ( + SELECT id FROM releases WHERE ( + release_time IS NULL OR + target_name IS NULL OR + yanked IS NULL OR + is_library IS NULL OR + rustdoc_status IS NULL OR + have_examples IS NULL OR + downloads IS NULL OR + doc_targets IS NULL OR + default_target IS NULL OR + archive_storage IS NULL + ) +); + DELETE FROM releases WHERE ( release_time IS NULL OR target_name IS NULL OR diff --git a/src/db/add_package.rs b/src/db/add_package.rs index ddd115f6f..6807df2b9 100644 --- a/src/db/add_package.rs +++ b/src/db/add_package.rs @@ -608,6 +608,118 @@ mod test { use crate::utils::CargoMetadata; use test_case::test_case; + #[test] + fn test_set_build_to_error() { + async_wrapper(|env| async move { + let mut conn = env.async_db().await.async_conn().await; + let crate_id = initialize_crate(&mut conn, "krate").await?; + let release_id = initialize_release(&mut conn, crate_id, "0.1.0").await?; + let build_id = initialize_build(&mut conn, release_id).await?; + + update_build_with_error(&mut conn, build_id, Some("error message")).await?; + + let row = sqlx::query!( + r#"SELECT + rustc_version, + docsrs_version, + build_status as "build_status: BuildStatus", + errors + FROM builds + WHERE id = $1"#, + build_id + ) + .fetch_one(&mut *conn) + .await?; + + assert!(row.rustc_version.is_none()); + assert!(row.docsrs_version.is_none()); + assert_eq!(row.build_status, BuildStatus::Failure); + assert_eq!(row.errors, Some("error message".into())); + + Ok(()) + }) + } + + #[test] + fn test_finish_build_success() { + async_wrapper(|env| async move { + let mut conn = env.async_db().await.async_conn().await; + let crate_id = initialize_crate(&mut conn, "krate").await?; + let release_id = initialize_release(&mut conn, crate_id, "0.1.0").await?; + let build_id = initialize_build(&mut conn, release_id).await?; + + finish_build( + &mut conn, + build_id, + "rustc_version", + "docsrs_version", + BuildStatus::Success, + None, + ) + .await?; + + let row = sqlx::query!( + r#"SELECT + rustc_version, + docsrs_version, + build_status as "build_status: BuildStatus", + errors + FROM builds + WHERE id = $1"#, + build_id + ) + .fetch_one(&mut *conn) + .await?; + + assert_eq!(row.rustc_version, Some("rustc_version".into())); + assert_eq!(row.docsrs_version, Some("docsrs_version".into())); + assert_eq!(row.build_status, BuildStatus::Success); + assert!(row.errors.is_none()); + + Ok(()) + }) + } + + #[test] + fn test_finish_build_error() { + async_wrapper(|env| async move { + let mut conn = env.async_db().await.async_conn().await; + let crate_id = initialize_crate(&mut conn, "krate").await?; + let release_id = initialize_release(&mut conn, crate_id, "0.1.0").await?; + let build_id = initialize_build(&mut conn, release_id).await?; + + finish_build( + &mut conn, + build_id, + "rustc_version", + "docsrs_version", + BuildStatus::Failure, + Some("error message"), + ) + .await?; + + let row = sqlx::query!( + r#"SELECT + rustc_version, + docsrs_version, + build_status as "build_status: BuildStatus", + errors + FROM builds + WHERE id = $1"#, + build_id + ) + .fetch_one(&mut *conn) + .await?; + + assert_eq!(row.rustc_version, Some("rustc_version".into())); + assert_eq!(row.docsrs_version, Some("docsrs_version".into())); + assert_eq!(row.build_status, BuildStatus::Failure); + assert_eq!(row.errors, Some("error message".into())); + + Ok(()) + }) + } + #[test] fn new_keywords() { wrapper(|env| { diff --git a/src/docbuilder/rustwide_builder.rs b/src/docbuilder/rustwide_builder.rs index 9dc50354a..5535b16bc 100644 --- a/src/docbuilder/rustwide_builder.rs +++ b/src/docbuilder/rustwide_builder.rs @@ -1062,13 +1062,16 @@ mod tests { let mut conn = env.db().conn(); let row = conn .query_one( - "SELECT + r#"SELECT r.rustdoc_status, r.default_target, r.doc_targets, r.archive_storage, cov.total_items, - b.id as build_id + b.id as build_id, + b.build_status::TEXT as build_status, + b.docsrs_version, + b.rustc_version FROM crates as c INNER JOIN releases AS r ON c.id = r.crate_id @@ -1076,7 +1079,7 @@ mod tests { LEFT OUTER JOIN doc_coverage AS cov ON r.id = cov.release_id WHERE c.name = $1 AND - r.version = $2", + r.version = $2"#, &[&crate_, &version], ) .unwrap(); @@ -1085,6 +1088,9 @@ mod tests { assert_eq!(row.get::<_, String>("default_target"), default_target); assert!(row.get::<_, Option>("total_items").is_some()); assert!(row.get::<_, bool>("archive_storage")); + assert!(!row.get::<_, String>("docsrs_version").is_empty()); + assert!(!row.get::<_, String>("rustc_version").is_empty()); + assert_eq!(row.get::<_, String>("build_status"), "success"); let mut targets: Vec = row .get::<_, Value>("doc_targets") @@ -1363,6 +1369,49 @@ mod tests { }); } + #[test] + #[ignore] + fn test_build_failures_before_build() { + wrapper(|env| { + // https://github.com/rust-lang/docs.rs/issues/2491 + // package without Cargo.toml, so fails directly in the fetch stage. + let crate_ = "emheap"; + let version = "0.1.0"; + let mut builder = RustwideBuilder::init(env).unwrap(); + builder.update_toolchain()?; + + // `Result` is `Ok`, but the build-result is `false` + assert!(!builder.build_package(crate_, version, PackageKind::CratesIo)?); + + let row = env.runtime().block_on(async { + let mut conn = env.async_db().await.async_conn().await; + sqlx::query!( + r#"SELECT + rustc_version, + docsrs_version, + build_status as "build_status: BuildStatus", + errors + FROM + crates as c + INNER JOIN releases as r on c.id = r.crate_id + INNER JOIN builds as b on b.rid = r.id + WHERE c.name = $1 and r.version = $2"#, + crate_, + version, + ) + .fetch_one(&mut *conn) + .await + })?; + + assert!(row.rustc_version.is_none()); + assert!(row.docsrs_version.is_none()); + assert_eq!(row.build_status, BuildStatus::Failure); + assert!(row.errors.unwrap().contains("missing Cargo.toml")); + + Ok(()) + }); + } + #[test] #[ignore] fn test_implicit_features_for_optional_dependencies() { diff --git a/src/test/fakes.rs b/src/test/fakes.rs index 5084a2182..b6314c8c0 100644 --- a/src/test/fakes.rs +++ b/src/test/fakes.rs @@ -1,6 +1,7 @@ use super::TestDatabase; use crate::db::types::BuildStatus; +use crate::db::{initialize_build, initialize_crate, initialize_release, update_build_status}; use crate::docbuilder::DocCoverage; use crate::error::Result; use crate::registry_api::{CrateData, CrateOwner, ReleaseData}; @@ -17,6 +18,36 @@ use std::sync::Arc; use tokio::runtime::Runtime; use tracing::debug; +/// Create a fake release in the database that failed before the build. +/// This is a temporary small factory function only until we refactored the +/// `FakeRelelease` and `FakeBuild` factories to be more flexible. +pub(crate) async fn fake_release_that_failed_before_build( + conn: &mut sqlx::PgConnection, + name: &str, + version: &str, + errors: &str, +) -> Result<(i32, i32)> { + let crate_id = initialize_crate(&mut *conn, name).await?; + let release_id = initialize_release(&mut *conn, crate_id, version).await?; + let build_id = initialize_build(&mut *conn, release_id).await?; + + sqlx::query_scalar!( + "UPDATE builds + SET + build_status = 'failure', + errors = $2 + WHERE id = $1", + build_id, + errors, + ) + .execute(&mut *conn) + .await?; + + update_build_status(conn, release_id).await?; + + Ok((release_id, build_id)) +} + #[must_use = "FakeRelease does nothing until you call .create()"] pub(crate) struct FakeRelease<'a> { db: &'a TestDatabase, @@ -613,6 +644,7 @@ impl FakeBuild { default_target: &str, ) -> Result<()> { let build_id = crate::db::initialize_build(&mut *conn, release_id).await?; + crate::db::finish_build( &mut *conn, build_id, @@ -653,6 +685,7 @@ impl FakeBuild { } impl Default for FakeBuild { + /// create a default fake _finished_ build fn default() -> Self { Self { s3_build_log: Some("It works!".into()), diff --git a/src/test/mod.rs b/src/test/mod.rs index 55447c69e..23ca62632 100644 --- a/src/test/mod.rs +++ b/src/test/mod.rs @@ -1,6 +1,6 @@ mod fakes; -pub(crate) use self::fakes::FakeBuild; +pub(crate) use self::fakes::{fake_release_that_failed_before_build, FakeBuild}; use crate::cdn::CdnBackend; use crate::db::{self, AsyncPoolClient, Pool, PoolClient}; use crate::error::Result; diff --git a/src/web/build_details.rs b/src/web/build_details.rs index faf714f19..6e5711c8c 100644 --- a/src/web/build_details.rs +++ b/src/web/build_details.rs @@ -132,7 +132,7 @@ pub(crate) async fn build_details_handler( #[cfg(test)] mod tests { - use crate::test::{wrapper, FakeBuild}; + use crate::test::{fake_release_that_failed_before_build, wrapper, FakeBuild}; use kuchikiki::traits::TendrilSink; use test_case::test_case; @@ -149,6 +149,37 @@ mod tests { .collect() } + #[test] + fn test_partial_build_result() { + wrapper(|env| { + let (_, build_id) = env.runtime().block_on(async { + let mut conn = env.async_db().await.async_conn().await; + fake_release_that_failed_before_build( + &mut conn, + "foo", + "0.1.0", + "some random error", + ) + .await + })?; + + let page = kuchikiki::parse_html().one( + env.frontend() + .get(&format!("/crate/foo/0.1.0/builds/{build_id}")) + .send()? + .error_for_status()? + .text()?, + ); + + let info_text = page.select("pre").unwrap().next().unwrap().text_contents(); + + assert!(info_text.contains("# pre-build errors"), "{}", info_text); + assert!(info_text.contains("some random error"), "{}", info_text); + + Ok(()) + }); + } + #[test] fn db_build_logs() { wrapper(|env| { diff --git a/src/web/builds.rs b/src/web/builds.rs index 40bae507c..a9e7be475 100644 --- a/src/web/builds.rs +++ b/src/web/builds.rs @@ -90,21 +90,21 @@ pub(crate) async fn build_list_json_handler( get_builds(&mut conn, &name, &version) .await? .iter() - .filter_map(|build| { + .map(|build| { // for backwards compatibility in this API, we - // * filter out in-progress builds // * convert the build status to a boolean - if build.build_status != BuildStatus::InProgress { - Some(serde_json::json!({ - "id": build.id, - "rustc_version": build.rustc_version, - "docsrs_version": build.docsrs_version, - "build_status": build.build_status.is_success(), - "build_time": build.build_time, - })) - } else { - None - } + // * already filter out in-progress builds + // + // even when we start showing in-progress builds in the UI, + // we might still not show them here for backwards + // compatibility. + serde_json::json!({ + "id": build.id, + "rustc_version": build.rustc_version, + "docsrs_version": build.docsrs_version, + "build_status": build.build_status.is_success(), + "build_time": build.build_time, + }) }) .collect::>(), ), @@ -129,7 +129,10 @@ async fn get_builds( FROM builds INNER JOIN releases ON releases.id = builds.rid INNER JOIN crates ON releases.crate_id = crates.id - WHERE crates.name = $1 AND releases.version = $2 + WHERE + crates.name = $1 AND + releases.version = $2 AND + builds.build_status != 'in_progress' ORDER BY id DESC"#, name, version.to_string(), @@ -142,13 +145,43 @@ async fn get_builds( mod tests { use super::BuildStatus; use crate::{ - test::{assert_cache_control, wrapper, FakeBuild}, + test::{assert_cache_control, fake_release_that_failed_before_build, wrapper, FakeBuild}, web::cache::CachePolicy, }; use chrono::{DateTime, Duration, Utc}; use kuchikiki::traits::TendrilSink; use reqwest::StatusCode; + #[test] + fn build_list_empty_build() { + wrapper(|env| { + env.runtime().block_on(async { + let mut conn = env.async_db().await.async_conn().await; + fake_release_that_failed_before_build(&mut conn, "foo", "0.1.0", "some errors") + .await + })?; + + let response = env + .frontend() + .get("/crate/foo/0.1.0/builds") + .send()? + .error_for_status()?; + assert_cache_control(&response, CachePolicy::NoCaching, &env.config()); + let page = kuchikiki::parse_html().one(response.text()?); + + let rows: Vec<_> = page + .select("ul > li a.release") + .unwrap() + .map(|row| row.text_contents()) + .collect(); + + assert_eq!(rows.len(), 1); + assert_eq!(rows[0].chars().filter(|&c| c == '—').count(), 3); + + Ok(()) + }); + } + #[test] fn build_list() { wrapper(|env| { @@ -166,6 +199,10 @@ mod tests { FakeBuild::default() .rustc_version("rustc (blabla 2021-01-01)") .docsrs_version("docs.rs 3.0.0"), + FakeBuild::default() + .build_status(BuildStatus::InProgress) + .rustc_version("rustc (blabla 2022-01-01)") + .docsrs_version("docs.rs 4.0.0"), ]) .create()?; diff --git a/src/web/crate_details.rs b/src/web/crate_details.rs index 86fe7e671..33d85bfd6 100644 --- a/src/web/crate_details.rs +++ b/src/web/crate_details.rs @@ -729,8 +729,8 @@ pub(crate) async fn get_all_platforms( mod tests { use super::*; use crate::test::{ - assert_cache_control, assert_redirect, assert_redirect_cached, async_wrapper, wrapper, - FakeBuild, TestDatabase, TestEnvironment, + assert_cache_control, assert_redirect, assert_redirect_cached, async_wrapper, + fake_release_that_failed_before_build, wrapper, FakeBuild, TestDatabase, TestEnvironment, }; use crate::{db::update_build_status, registry_api::CrateOwner}; use anyhow::Error; @@ -1530,6 +1530,53 @@ mod tests { }); } + #[test] + fn test_minimal_failed_release_doesnt_error_features() { + wrapper(|env| { + env.runtime().block_on(async { + let mut conn = env.async_db().await.async_conn().await; + fake_release_that_failed_before_build(&mut conn, "foo", "0.1.0", "some errors") + .await + })?; + + let text_content = env + .frontend() + .get("/crate/foo/0.1.0/features") + .send()? + .error_for_status()? + .text()?; + + assert!(text_content.contains( + "Feature flags are not available for this release because \ + the build failed before we could retrieve them" + )); + + Ok(()) + }); + } + + #[test] + fn test_minimal_failed_release_doesnt_error() { + wrapper(|env| { + env.runtime().block_on(async { + let mut conn = env.async_db().await.async_conn().await; + fake_release_that_failed_before_build(&mut conn, "foo", "0.1.0", "some errors") + .await + })?; + + let text_content = env + .frontend() + .get("/crate/foo/0.1.0") + .send()? + .error_for_status()? + .text()?; + + assert!(text_content.contains("docs.rs failed to build foo")); + + Ok(()) + }); + } + #[test] fn platform_links_are_direct_and_without_nofollow() { fn check_links( diff --git a/src/web/releases.rs b/src/web/releases.rs index 5f9417141..e8f134dea 100644 --- a/src/web/releases.rs +++ b/src/web/releases.rs @@ -786,10 +786,11 @@ pub(crate) async fn build_queue_handler( #[cfg(test)] mod tests { use super::*; + use crate::db::types::BuildStatus; use crate::registry_api::{CrateOwner, OwnerKind}; use crate::test::{ assert_cache_control, assert_redirect, assert_redirect_unchecked, assert_success, wrapper, - TestFrontend, + FakeBuild, TestFrontend, }; use anyhow::Error; use chrono::{Duration, TimeZone}; @@ -814,9 +815,24 @@ mod tests { .version("1.0.0") .github_stats("ghost/bar", 20, 20, 20) .create()?; + env.fake_release() + .name("bar") + .version("1.0.0") + .github_stats("ghost/bar", 20, 20, 20) + .create()?; // release without stars will not be shown env.fake_release().name("baz").version("1.0.0").create()?; + // release with only in-progress build (= in progress release) will not be shown + env.fake_release() + .name("in_progress") + .version("0.1.0") + .builds(vec![FakeBuild::default() + .build_status(BuildStatus::InProgress) + .rustc_version("rustc (blabla 2022-01-01)") + .docsrs_version("docs.rs 4.0.0")]) + .create()?; + let releases = env .runtime() .block_on(async move { @@ -1272,6 +1288,16 @@ mod tests { .yanked(true) .create()?; + // release with only in-progress build (= in progress release) will not be shown + env.fake_release() + .name("in_progress") + .version("0.1.0") + .builds(vec![FakeBuild::default() + .build_status(BuildStatus::InProgress) + .rustc_version("rustc (blabla 2022-01-01)") + .docsrs_version("docs.rs 4.0.0")]) + .create()?; + let _m = crates_io .mock("GET", "/api/v1/crates") .match_query(Matcher::AllOf(vec![ @@ -1286,7 +1312,8 @@ mod tests { { "name": "some_random_crate" }, { "name": "some_other_crate" }, { "name": "and_another_one" }, - { "name": "yet_another_crate" } + { "name": "yet_another_crate" }, + { "name": "in_progress" } ], "meta": { "next_page": null, diff --git a/src/web/source.rs b/src/web/source.rs index 10a768eb0..63bef844a 100644 --- a/src/web/source.rs +++ b/src/web/source.rs @@ -42,7 +42,7 @@ impl File { } /// A list of source files -#[derive(Debug, Clone, PartialEq, Serialize)] +#[derive(Debug, Clone, PartialEq, Serialize, Default)] struct FileList { files: Vec, } @@ -303,7 +303,7 @@ pub(crate) async fn source_browser_handler( current_folder, ) .await? - .ok_or(AxumNope::ResourceNotFound)?; + .unwrap_or_default(); Ok(SourcePage { file_list, @@ -484,7 +484,7 @@ mod tests { &[&release_id], )?; - assert_eq!(web.get(path).send()?.status(), StatusCode::NOT_FOUND); + assert!(web.get(path).send()?.status().is_success()); Ok(()) }); @@ -523,7 +523,7 @@ mod tests { .version("0.2.0") .create()?; let web = env.frontend(); - assert_not_found("/crate/mbedtls/0.2.0/source/test/", web)?; + assert_success("/crate/mbedtls/0.2.0/source/test/", web)?; Ok(()) }) }