diff --git a/Cargo.lock b/Cargo.lock index 70c971d5..f85608f4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -427,12 +427,6 @@ dependencies = [ "password-hash 0.5.0", ] -[[package]] -name = "arrayvec" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" - [[package]] name = "arrayvec" version = "0.7.4" @@ -2208,12 +2202,12 @@ dependencies = [ ] [[package]] -name = "iso8601-duration" -version = "0.1.0" +name = "iso8601" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60b51dd97fa24074214b9eb14da518957573f4dec3189112610ae1ccec9ac464" +checksum = "924e5d73ea28f59011fec52a0d12185d496a9b075d360657aed2a5707f701153" dependencies = [ - "nom 5.1.3", + "nom", ] [[package]] @@ -2404,26 +2398,13 @@ dependencies = [ "idna 0.3.0", "mime", "native-tls", - "nom 7.1.3", + "nom", "once_cell", "quoted_printable", "socket2 0.4.10", "tokio", ] -[[package]] -name = "lexical-core" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6607c62aa161d23d17a9072cc5da0be67cdfc89d3afb1e8d9c842bebc2525ffe" -dependencies = [ - "arrayvec 0.5.2", - "bitflags 1.3.2", - "cfg-if", - "ryu", - "static_assertions", -] - [[package]] name = "libc" version = "0.2.150" @@ -2601,9 +2582,9 @@ checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771" [[package]] name = "meilisearch-index-setting-macro" -version = "0.22.1" +version = "0.24.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9fedd7e2fabfbcc91679f3d76f6d648ea7fc9ea87c841b10d26c2a258f408da" +checksum = "b1f2124b55b9cb28e6a08b28854f4e834a51333cbdc2f72935f401efa686c13c" dependencies = [ "convert_case 0.6.0", "proc-macro2", @@ -2613,16 +2594,16 @@ dependencies = [ [[package]] name = "meilisearch-sdk" -version = "0.22.1" +version = "0.24.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e6be928c91e1b23689725586b56f3284f394d93185accfa2771caec3e10015d" +checksum = "2257ea8ed24b079c21570f473e58cccc3de23b46cee331fc513fccdc3f1ae5a1" dependencies = [ "async-trait", "either", "futures", "futures-io", "isahc", - "iso8601-duration", + "iso8601", "js-sys", "jsonwebtoken", "log", @@ -2724,17 +2705,6 @@ dependencies = [ "tempfile", ] -[[package]] -name = "nom" -version = "5.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08959a387a676302eebf4ddbcbc611da04285579f76f88ee0506c63b1a61dd4b" -dependencies = [ - "lexical-core", - "memchr", - "version_check", -] - [[package]] name = "nom" version = "7.1.3" @@ -3060,7 +3030,7 @@ dependencies = [ "fnv", "itertools 0.11.0", "lazy_static", - "nom 7.1.3", + "nom", "quick-xml 0.28.2", "regex", "regex-cache", @@ -3660,7 +3630,7 @@ version = "1.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4c4216490d5a413bc6d10fa4742bd7d4955941d062c0ef873141d6b0e7b30fd" dependencies = [ - "arrayvec 0.7.4", + "arrayvec", "borsh", "bytes", "num-traits", @@ -4246,7 +4216,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6b7b278788e7be4d0d29c0f39497a0eef3fba6bbc8e70d8bf7fde46edeaa9e85" dependencies = [ "itertools 0.11.0", - "nom 7.1.3", + "nom", "unicode_categories", ] diff --git a/Cargo.toml b/Cargo.toml index 83cb9d6a..09f98887 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,7 +29,7 @@ async-trait = "0.1.70" dashmap = "5.4.0" lazy_static = "1.4.0" -meilisearch-sdk = "0.22.0" +meilisearch-sdk = "0.24.3" rust-s3 = "0.33.0" reqwest = { version = "0.11.18", features = ["json", "multipart"] } hyper = { version = "0.14", features = ["full"] } diff --git a/docker-compose.yml b/docker-compose.yml index 1fc25bca..11a4f806 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -12,12 +12,12 @@ services: POSTGRES_PASSWORD: labrinth POSTGRES_HOST_AUTH_METHOD: trust meilisearch: - image: getmeili/meilisearch:v1.0.1 + image: getmeili/meilisearch:v1.5.0 restart: on-failure ports: - "7700:7700" volumes: - - meilisearch-data:/meili_data + - meilisearch-data:/data.ms environment: MEILI_MASTER_KEY: modrinth redis: diff --git a/src/auth/checks.rs b/src/auth/checks.rs index 7794e8f7..75a9e5e1 100644 --- a/src/auth/checks.rs +++ b/src/auth/checks.rs @@ -2,6 +2,7 @@ use crate::database; use crate::database::models::project_item::QueryProject; use crate::database::models::version_item::QueryVersion; use crate::database::models::Collection; +use crate::database::redis::RedisPool; use crate::database::{models, Project, Version}; use crate::models::users::User; use crate::routes::ApiError; @@ -215,17 +216,17 @@ pub async fn is_authorized_version( Ok(authorized) } -impl ValidateAuthorized for crate::database::models::OAuthClient { +impl ValidateAuthorized for models::OAuthClient { fn validate_authorized(&self, user_option: Option<&User>) -> Result<(), ApiError> { if let Some(user) = user_option { - if user.role.is_mod() || user.id == self.created_by.into() { - return Ok(()); + return if user.role.is_mod() || user.id == self.created_by.into() { + Ok(()) } else { - return Err(crate::routes::ApiError::CustomAuthentication( + Err(ApiError::CustomAuthentication( "You don't have sufficient permissions to interact with this OAuth application" .to_string(), - )); - } + )) + }; } Ok(()) @@ -236,9 +237,23 @@ pub async fn filter_authorized_versions( versions: Vec, user_option: &Option, pool: &web::Data, + redis: web::Data, ) -> Result, ApiError> { let mut return_versions = Vec::new(); - let mut check_versions = Vec::new(); + + let project_ids = versions + .iter() + .map(|x| x.inner.project_id) + .collect::>(); + + let authorized_projects = filter_authorized_projects( + Project::get_many_ids(&project_ids, &***pool, &redis).await?, + user_option, + pool, + ) + .await?; + + let authorized_project_ids: Vec<_> = authorized_projects.iter().map(|x| x.id.into()).collect(); for version in versions { if !version.inner.status.is_hidden() @@ -246,48 +261,9 @@ pub async fn filter_authorized_versions( .as_ref() .map(|x| x.role.is_mod()) .unwrap_or(false) + || (user_option.is_some() && authorized_project_ids.contains(&version.inner.project_id)) { return_versions.push(version.into()); - } else if user_option.is_some() { - check_versions.push(version); - } - } - - if !check_versions.is_empty() { - if let Some(user) = user_option { - let user_id: models::ids::UserId = user.id.into(); - - use futures::TryStreamExt; - - sqlx::query!( - " - SELECT m.id FROM mods m - INNER JOIN team_members tm ON tm.team_id = m.team_id AND user_id = $2 - WHERE m.id = ANY($1) - ", - &check_versions - .iter() - .map(|x| x.inner.project_id.0) - .collect::>(), - user_id as database::models::ids::UserId, - ) - .fetch_many(&***pool) - .try_for_each(|e| { - if let Some(row) = e.right() { - check_versions.retain(|x| { - let bool = x.inner.project_id.0 == row.id; - - if bool { - return_versions.push(x.clone().into()); - } - - !bool - }); - } - - futures::future::ready(Ok(())) - }) - .await?; } } diff --git a/src/database/models/project_item.rs b/src/database/models/project_item.rs index b624948c..2cc26a7e 100644 --- a/src/database/models/project_item.rs +++ b/src/database/models/project_item.rs @@ -492,6 +492,12 @@ impl Project { where E: sqlx::Acquire<'a, Database = sqlx::Postgres>, { + let project_strings = project_strings + .iter() + .map(|x| x.to_string()) + .unique() + .collect::>(); + if project_strings.is_empty() { return Ok(Vec::new()); } @@ -500,10 +506,7 @@ impl Project { let mut exec = exec.acquire().await?; let mut found_projects = Vec::new(); - let mut remaining_strings = project_strings - .iter() - .map(|x| x.to_string()) - .collect::>(); + let mut remaining_strings = project_strings.clone(); let mut project_ids = project_strings .iter() diff --git a/src/database/models/version_item.rs b/src/database/models/version_item.rs index 8fc2de5d..c87060e1 100644 --- a/src/database/models/version_item.rs +++ b/src/database/models/version_item.rs @@ -494,6 +494,12 @@ impl Version { where E: sqlx::Acquire<'a, Database = sqlx::Postgres>, { + let version_ids = version_ids + .iter() + .unique() + .copied() + .collect::>(); + use futures::stream::TryStreamExt; if version_ids.is_empty() { diff --git a/src/routes/internal/mod.rs b/src/routes/internal/mod.rs index 358876c4..81ac4c9b 100644 --- a/src/routes/internal/mod.rs +++ b/src/routes/internal/mod.rs @@ -1,4 +1,4 @@ -mod admin; +pub(crate) mod admin; pub mod flows; pub mod pats; pub mod session; diff --git a/src/routes/updates.rs b/src/routes/updates.rs index f4d6d2f8..2d29af8b 100644 --- a/src/routes/updates.rs +++ b/src/routes/updates.rs @@ -75,6 +75,7 @@ pub async fn forge_updates( .collect(), &user_option, &pool, + redis, ) .await?; diff --git a/src/routes/v2/admin.rs b/src/routes/v2/admin.rs deleted file mode 100644 index 07afe836..00000000 --- a/src/routes/v2/admin.rs +++ /dev/null @@ -1,147 +0,0 @@ -use crate::auth::validate::get_user_record_from_bearer_token; -use crate::database::redis::RedisPool; -use crate::models::analytics::Download; -use crate::models::ids::ProjectId; -use crate::models::pats::Scopes; -use crate::queue::analytics::AnalyticsQueue; -use crate::queue::maxmind::MaxMindIndexer; -use crate::queue::session::AuthQueue; -use crate::routes::ApiError; -use crate::search::SearchConfig; -use crate::util::date::get_current_tenths_of_ms; -use crate::util::guards::admin_key_guard; -use actix_web::{patch, post, web, HttpRequest, HttpResponse}; -use serde::Deserialize; -use sqlx::PgPool; -use std::collections::HashMap; -use std::net::Ipv4Addr; -use std::sync::Arc; - -pub fn config(cfg: &mut web::ServiceConfig) { - cfg.service( - web::scope("admin") - .service(count_download) - .service(force_reindex), - ); -} - -#[derive(Deserialize)] -pub struct DownloadBody { - pub url: String, - pub project_id: ProjectId, - pub version_name: String, - - pub ip: String, - pub headers: HashMap, -} - -// This is an internal route, cannot be used without key -#[patch("/_count-download", guard = "admin_key_guard")] -#[allow(clippy::too_many_arguments)] -pub async fn count_download( - req: HttpRequest, - pool: web::Data, - redis: web::Data, - maxmind: web::Data>, - analytics_queue: web::Data>, - session_queue: web::Data, - download_body: web::Json, -) -> Result { - let token = download_body - .headers - .iter() - .find(|x| x.0.to_lowercase() == "authorization") - .map(|x| &**x.1); - - let user = get_user_record_from_bearer_token(&req, token, &**pool, &redis, &session_queue) - .await - .ok() - .flatten(); - - let project_id: crate::database::models::ids::ProjectId = download_body.project_id.into(); - - let id_option = crate::models::ids::base62_impl::parse_base62(&download_body.version_name) - .ok() - .map(|x| x as i64); - - let (version_id, project_id) = if let Some(version) = sqlx::query!( - " - SELECT v.id id, v.mod_id mod_id FROM files f - INNER JOIN versions v ON v.id = f.version_id - WHERE f.url = $1 - ", - download_body.url, - ) - .fetch_optional(pool.as_ref()) - .await? - { - (version.id, version.mod_id) - } else if let Some(version) = sqlx::query!( - " - SELECT id, mod_id FROM versions - WHERE ((version_number = $1 OR id = $3) AND mod_id = $2) - ", - download_body.version_name, - project_id as crate::database::models::ids::ProjectId, - id_option - ) - .fetch_optional(pool.as_ref()) - .await? - { - (version.id, version.mod_id) - } else { - return Err(ApiError::InvalidInput( - "Specified version does not exist!".to_string(), - )); - }; - - let url = url::Url::parse(&download_body.url) - .map_err(|_| ApiError::InvalidInput("invalid download URL specified!".to_string()))?; - - let ip = crate::routes::analytics::convert_to_ip_v6(&download_body.ip) - .unwrap_or_else(|_| Ipv4Addr::new(127, 0, 0, 1).to_ipv6_mapped()); - - analytics_queue.add_download(Download { - recorded: get_current_tenths_of_ms(), - domain: url.host_str().unwrap_or_default().to_string(), - site_path: url.path().to_string(), - user_id: user - .and_then(|(scopes, x)| { - if scopes.contains(Scopes::PERFORM_ANALYTICS) { - Some(x.id.0 as u64) - } else { - None - } - }) - .unwrap_or(0), - project_id: project_id as u64, - version_id: version_id as u64, - ip, - country: maxmind.query(ip).await.unwrap_or_default(), - user_agent: download_body - .headers - .get("user-agent") - .cloned() - .unwrap_or_default(), - headers: download_body - .headers - .clone() - .into_iter() - .filter(|x| !crate::routes::analytics::FILTERED_HEADERS.contains(&&*x.0.to_lowercase())) - .collect(), - }); - - Ok(HttpResponse::NoContent().body("")) -} - -#[post("/_force_reindex", guard = "admin_key_guard")] -pub async fn force_reindex( - pool: web::Data, - redis: web::Data, - config: web::Data, -) -> Result { - use crate::search::indexing::index_projects; - let redis = redis.get_ref(); - index_projects(pool.as_ref().clone(), redis.clone(), &config).await?; - Ok(HttpResponse::NoContent().finish()) -} diff --git a/src/routes/v2/mod.rs b/src/routes/v2/mod.rs index 417308e6..13f823a6 100644 --- a/src/routes/v2/mod.rs +++ b/src/routes/v2/mod.rs @@ -1,4 +1,3 @@ -mod admin; mod moderation; mod notifications; pub(crate) mod project_creation; @@ -20,7 +19,7 @@ pub fn config(cfg: &mut actix_web::web::ServiceConfig) { cfg.service( actix_web::web::scope("v2") .wrap(default_cors()) - .configure(admin::config) + .configure(super::internal::admin::config) // Todo: separate these- they need to also follow v2-v3 conversion .configure(super::internal::session::config) .configure(super::internal::flows::config) diff --git a/src/routes/v2/projects.rs b/src/routes/v2/projects.rs index b107bdb2..c6237cb4 100644 --- a/src/routes/v2/projects.rs +++ b/src/routes/v2/projects.rs @@ -200,7 +200,6 @@ pub async fn project_get( // Call V3 project creation let response = v3::projects::project_get(req, info, pool.clone(), redis.clone(), session_queue) .await - .or_else(v2_reroute::flatten_404_error) .or_else(v2_reroute::flatten_404_error)?; // Convert response to V2 format @@ -227,7 +226,6 @@ pub async fn project_get_check( v3::projects::project_get_check(info, pool, redis) .await .or_else(v2_reroute::flatten_404_error) - .or_else(v2_reroute::flatten_404_error) } #[derive(Serialize)] @@ -248,7 +246,6 @@ pub async fn dependency_list( v3::projects::dependency_list(req, info, pool, redis, session_queue) .await .or_else(v2_reroute::flatten_404_error) - .or_else(v2_reroute::flatten_404_error) } #[derive(Serialize, Deserialize, Validate)] diff --git a/src/routes/v2/tags.rs b/src/routes/v2/tags.rs index 00ba0726..69a3eff2 100644 --- a/src/routes/v2/tags.rs +++ b/src/routes/v2/tags.rs @@ -66,7 +66,13 @@ pub async fn loader_list( .map(|l| LoaderData { icon: l.icon, name: l.name, - supported_project_types: l.supported_project_types, + // Add generic 'project' type to all loaders, which is the v2 representation of + // a project type before any versions are set. + supported_project_types: l + .supported_project_types + .into_iter() + .chain(std::iter::once("project".to_string())) + .collect(), }) .collect::>(); Ok(HttpResponse::Ok().json(loaders)) diff --git a/src/routes/v3/projects.rs b/src/routes/v3/projects.rs index 6cc8b319..c647429b 100644 --- a/src/routes/v3/projects.rs +++ b/src/routes/v3/projects.rs @@ -29,6 +29,7 @@ use crate::util::validate::validation_errors_to_string; use actix_web::{web, HttpRequest, HttpResponse}; use chrono::{DateTime, Utc}; use futures::TryStreamExt; +use itertools::Itertools; use serde::{Deserialize, Serialize}; use serde_json::json; use sqlx::PgPool; @@ -972,7 +973,6 @@ pub async fn dependency_list( let dependencies = database::Project::get_dependencies(project.inner.id, &**pool, &redis).await?; - let project_ids = dependencies .iter() .filter_map(|x| { @@ -986,11 +986,13 @@ pub async fn dependency_list( x.1 } }) + .unique() .collect::>(); let dep_version_ids = dependencies .iter() .filter_map(|x| x.0) + .unique() .collect::>(); let (projects_result, versions_result) = futures::future::try_join( database::Project::get_many_ids(&project_ids, &**pool, &redis), diff --git a/src/routes/v3/version_file.rs b/src/routes/v3/version_file.rs index bbd448a2..3b5b15e6 100644 --- a/src/routes/v3/version_file.rs +++ b/src/routes/v3/version_file.rs @@ -234,6 +234,7 @@ pub async fn get_versions_from_hashes( database::models::Version::get_many(&version_ids, &**pool, &redis).await?, &user_option, &pool, + redis, ) .await?; diff --git a/src/routes/v3/versions.rs b/src/routes/v3/versions.rs index ea40a9cf..32af9eed 100644 --- a/src/routes/v3/versions.rs +++ b/src/routes/v3/versions.rs @@ -134,7 +134,7 @@ pub async fn versions_get( .map(|x| x.1) .ok(); - let versions = filter_authorized_versions(versions_data, &user_option, &pool).await?; + let versions = filter_authorized_versions(versions_data, &user_option, &pool, redis).await?; Ok(HttpResponse::Ok().json(versions)) } @@ -821,7 +821,7 @@ pub async fn version_list( response.sort(); response.dedup_by(|a, b| a.inner.id == b.inner.id); - let response = filter_authorized_versions(response, &user_option, &pool).await?; + let response = filter_authorized_versions(response, &user_option, &pool, redis).await?; Ok(HttpResponse::Ok().json(response)) } else { diff --git a/src/search/indexing/mod.rs b/src/search/indexing/mod.rs index ddc12e7a..c7790301 100644 --- a/src/search/indexing/mod.rs +++ b/src/search/indexing/mod.rs @@ -137,17 +137,99 @@ async fn create_or_update_index( Ok(index) => { info!("Updating index settings."); + let old_settings = index.get_settings().await?; + let mut settings = default_settings(); if let Some(custom_rules) = custom_rules { settings = settings.with_ranking_rules(custom_rules); } - index - .set_settings(&settings) - .await? - .wait_for_completion(client, None, Some(TIMEOUT)) - .await?; + let old_settings = Settings { + synonyms: None, // We don't use synonyms right now + stop_words: if settings.stop_words.is_none() { + None + } else { + old_settings.stop_words.map(|mut x| { + x.sort(); + x + }) + }, + ranking_rules: if settings.ranking_rules.is_none() { + None + } else { + old_settings.ranking_rules + }, + filterable_attributes: if settings.filterable_attributes.is_none() { + None + } else { + old_settings.filterable_attributes.map(|mut x| { + x.sort(); + x + }) + }, + sortable_attributes: if settings.sortable_attributes.is_none() { + None + } else { + old_settings.sortable_attributes.map(|mut x| { + x.sort(); + x + }) + }, + distinct_attribute: if settings.distinct_attribute.is_none() { + None + } else { + old_settings.distinct_attribute + }, + searchable_attributes: if settings.searchable_attributes.is_none() { + None + } else { + old_settings.searchable_attributes + }, + displayed_attributes: if settings.displayed_attributes.is_none() { + None + } else { + old_settings.displayed_attributes.map(|mut x| { + x.sort(); + x + }) + }, + pagination: if settings.pagination.is_none() { + None + } else { + old_settings.pagination + }, + faceting: if settings.faceting.is_none() { + None + } else { + old_settings.faceting + }, + typo_tolerance: None, // We don't use typo tolerance right now + dictionary: None, // We don't use dictionary right now + }; + + if old_settings.synonyms != settings.synonyms + || old_settings.stop_words != settings.stop_words + || old_settings.ranking_rules != settings.ranking_rules + || old_settings.filterable_attributes != settings.filterable_attributes + || old_settings.sortable_attributes != settings.sortable_attributes + || old_settings.distinct_attribute != settings.distinct_attribute + || old_settings.searchable_attributes != settings.searchable_attributes + || old_settings.displayed_attributes != settings.displayed_attributes + || old_settings.pagination != settings.pagination + || old_settings.faceting != settings.faceting + || old_settings.typo_tolerance != settings.typo_tolerance + || old_settings.dictionary != settings.dictionary + { + info!("Performing index settings set."); + index + .set_settings(&settings) + .await? + .wait_for_completion(client, None, Some(TIMEOUT)) + .await?; + info!("Done performing index settings set."); + } + Ok(index) } _ => { @@ -241,12 +323,19 @@ pub async fn add_projects( } fn default_settings() -> Settings { + let mut sorted_display = DEFAULT_DISPLAYED_ATTRIBUTES.to_vec(); + sorted_display.sort(); + let mut sorted_sortable = DEFAULT_SORTABLE_ATTRIBUTES.to_vec(); + sorted_sortable.sort(); + let mut sorted_attrs = DEFAULT_ATTRIBUTES_FOR_FACETING.to_vec(); + sorted_attrs.sort(); + Settings::new() .with_distinct_attribute("project_id") - .with_displayed_attributes(DEFAULT_DISPLAYED_ATTRIBUTES) + .with_displayed_attributes(sorted_display) .with_searchable_attributes(DEFAULT_SEARCHABLE_ATTRIBUTES) - .with_sortable_attributes(DEFAULT_SORTABLE_ATTRIBUTES) - .with_filterable_attributes(DEFAULT_ATTRIBUTES_FOR_FACETING) + .with_sortable_attributes(sorted_sortable) + .with_filterable_attributes(sorted_attrs) .with_pagination(PaginationSetting { max_total_hits: 2147483647, }) diff --git a/src/search/mod.rs b/src/search/mod.rs index d7e7026f..6b9e160c 100644 --- a/src/search/mod.rs +++ b/src/search/mod.rs @@ -67,7 +67,7 @@ pub struct SearchConfig { impl SearchConfig { pub fn make_client(&self) -> Client { - Client::new(self.address.as_str(), self.key.as_str()) + Client::new(self.address.as_str(), Some(self.key.as_str())) } } @@ -187,7 +187,7 @@ pub async fn search_for_project( info: &SearchRequest, config: &SearchConfig, ) -> Result { - let client = Client::new(&*config.address, &*config.key); + let client = Client::new(&*config.address, Some(&*config.key)); let offset = info.offset.as_deref().unwrap_or("0").parse()?; let index = info.index.as_deref().unwrap_or("relevance"); diff --git a/tests/project.rs b/tests/project.rs index 1797892e..2a69280f 100644 --- a/tests/project.rs +++ b/tests/project.rs @@ -1120,7 +1120,7 @@ async fn align_search_projects() { let projects = api .search_deserialized( - Some(&test_name), + Some(&format!("\"&{test_name}\"")), Some(json!([["categories:fabric"]])), USER_USER_PAT, ) @@ -1130,6 +1130,7 @@ async fn align_search_projects() { let project_model = api .get_project(&project.id.to_string(), USER_USER_PAT) .await; + assert_eq!(project_model.status(), 200); let mut project_model: Project = test::read_body_json(project_model).await; // Body/description is huge- don't store it in search, so it's OK if they differ here diff --git a/tests/search.rs b/tests/search.rs index aabacd5b..111dde8a 100644 --- a/tests/search.rs +++ b/tests/search.rs @@ -91,7 +91,11 @@ async fn search_projects() { let test_name = test_name.clone(); async move { let projects = api - .search_deserialized(Some(&test_name), Some(facets.clone()), USER_USER_PAT) + .search_deserialized( + Some(&format!("\"&{test_name}\"")), + Some(facets.clone()), + USER_USER_PAT, + ) .await; let mut found_project_ids: Vec = projects .hits diff --git a/tests/v2/search.rs b/tests/v2/search.rs index abd14993..94ac908b 100644 --- a/tests/v2/search.rs +++ b/tests/v2/search.rs @@ -292,7 +292,11 @@ async fn search_projects() { let test_name = test_name.clone(); async move { let projects = api - .search_deserialized(Some(&test_name), Some(facets.clone()), USER_USER_PAT) + .search_deserialized( + Some(&format!("\"&{test_name}\"")), + Some(facets.clone()), + USER_USER_PAT, + ) .await; let mut found_project_ids: Vec = projects .hits @@ -309,7 +313,7 @@ async fn search_projects() { // A couple additional tests for the saerch type returned, making sure it is properly translated back let client_side_required = api .search_deserialized( - Some(&test_name), + Some(&format!("\"&{test_name}\"")), Some(json!([["client_side:required"]])), USER_USER_PAT, ) @@ -320,7 +324,7 @@ async fn search_projects() { let server_side_required = api .search_deserialized( - Some(&test_name), + Some(&format!("\"&{test_name}\"")), Some(json!([["server_side:required"]])), USER_USER_PAT, ) @@ -331,7 +335,7 @@ async fn search_projects() { let client_side_unsupported = api .search_deserialized( - Some(&test_name), + Some(&format!("\"&{test_name}\"")), Some(json!([["client_side:unsupported"]])), USER_USER_PAT, ) @@ -342,7 +346,7 @@ async fn search_projects() { let game_versions = api .search_deserialized( - Some(&test_name), + Some(&format!("\"&{test_name}\"")), Some(json!([["versions:1.20.5"]])), USER_USER_PAT, )