diff --git a/meilisearch-http/src/routes/indexes/search.rs b/meilisearch-http/src/routes/indexes/search.rs index b6820018e..d266c6f7a 100644 --- a/meilisearch-http/src/routes/indexes/search.rs +++ b/meilisearch-http/src/routes/indexes/search.rs @@ -85,6 +85,11 @@ async fn search_with_url_query( debug!("called with params: {:?}", params); let query = params.into_inner().into(); let search_result = data.search(path.into_inner().index_uid, query).await?; + + // Tests that the nb_hits is always set to false + #[cfg(test)] + assert!(!search_result.exhaustive_nb_hits); + debug!("returns: {:?}", search_result); Ok(HttpResponse::Ok().json(search_result)) } @@ -98,6 +103,11 @@ async fn search_with_post( let search_result = data .search(path.into_inner().index_uid, params.into_inner()) .await?; + + // Tests that the nb_hits is always set to false + #[cfg(test)] + assert!(!search_result.exhaustive_nb_hits); + debug!("returns: {:?}", search_result); Ok(HttpResponse::Ok().json(search_result)) } diff --git a/meilisearch-http/tests/common/index.rs b/meilisearch-http/tests/common/index.rs index 021b11ec4..cb790ba70 100644 --- a/meilisearch-http/tests/common/index.rs +++ b/meilisearch-http/tests/common/index.rs @@ -1,4 +1,7 @@ -use std::{panic::{UnwindSafe, catch_unwind, resume_unwind}, time::Duration}; +use std::{ + panic::{catch_unwind, resume_unwind, UnwindSafe}, + time::Duration, +}; use actix_web::http::StatusCode; use paste::paste; @@ -185,13 +188,17 @@ impl Index<'_> { self.service.get(url).await } - pub async fn search(&self, query: Value, test: impl Fn(Value, StatusCode) + UnwindSafe + Clone) { + /// Performs both GET and POST search queries + pub async fn search( + &self, + query: Value, + test: impl Fn(Value, StatusCode) + UnwindSafe + Clone, + ) { let (response, code) = self.search_post(query.clone()).await; let t = test.clone(); if let Err(e) = catch_unwind(move || t(response, code)) { eprintln!("Error with post search"); resume_unwind(e); - } let (response, code) = self.search_get(query).await; diff --git a/meilisearch-http/tests/search/errors.rs b/meilisearch-http/tests/search/errors.rs new file mode 100644 index 000000000..a09e8f76e --- /dev/null +++ b/meilisearch-http/tests/search/errors.rs @@ -0,0 +1,28 @@ +use crate::common::Server; +use serde_json::json; + +#[actix_rt::test] +async fn search_unexisting_index() { + let server = Server::new().await; + let index = server.index("test"); + + index + .search(json!({"q": "hello"}), |response, code| { + assert_eq!(code, 404, "{}", response); + assert_eq!(response["errorCode"], "index_not_found"); + }) + .await; +} + +#[actix_rt::test] +async fn search_unexisting_parameter() { + let server = Server::new().await; + let index = server.index("test"); + + index + .search(json!({"marin": "hello"}), |response, code| { + assert_eq!(code, 400, "{}", response); + assert_eq!(response["errorCode"], "bad_request"); + }) + .await; +} diff --git a/meilisearch-http/tests/search/mod.rs b/meilisearch-http/tests/search/mod.rs index 56ec6439c..b2a8f760c 100644 --- a/meilisearch-http/tests/search/mod.rs +++ b/meilisearch-http/tests/search/mod.rs @@ -1,2 +1,195 @@ // This modules contains all the test concerning search. Each particular feture of the search // should be tested in its own module to isolate tests and keep the tests readable. + +mod errors; + +use crate::common::Server; +use once_cell::sync::Lazy; +use serde_json::{json, Value}; + +static DOCUMENTS: Lazy = Lazy::new(|| { + json!([ + { + "title": "Shazam!", + "id": "287947" + }, + { + "title": "Captain Marvel", + "id": "299537" + }, + { + "title": "Escape Room", + "id": "522681" + }, + { "title": "How to Train Your Dragon: The Hidden World", "id": "166428" + }, + { + "title": "Glass", + "id": "450465" + } + ]) +}); + +#[actix_rt::test] +async fn simple_placeholder_search() { + let server = Server::new().await; + let index = server.index("test"); + + let documents = DOCUMENTS.clone(); + index.add_documents(documents, None).await; + index.wait_update_id(0).await; + + index + .search(json!({}), |response, code| { + assert_eq!(code, 200, "{}", response); + assert_eq!(response["hits"].as_array().unwrap().len(), 5); + }) + .await; +} + +#[actix_rt::test] +async fn simple_search() { + let server = Server::new().await; + let index = server.index("test"); + + let documents = DOCUMENTS.clone(); + index.add_documents(documents, None).await; + index.wait_update_id(0).await; + + index + .search(json!({"q": "glass"}), |response, code| { + assert_eq!(code, 200, "{}", response); + assert_eq!(response["hits"].as_array().unwrap().len(), 1); + }) + .await; +} + +#[actix_rt::test] +async fn search_multiple_params() { + let server = Server::new().await; + let index = server.index("test"); + + let documents = DOCUMENTS.clone(); + index.add_documents(documents, None).await; + index.wait_update_id(0).await; + + index + .search( + json!({ + "q": "glass", + "attributesToCrop": ["title:2"], + "attributesToHighlight": ["title"], + "limit": 1, + "offset": 0, + }), + |response, code| { + assert_eq!(code, 200, "{}", response); + assert_eq!(response["hits"].as_array().unwrap().len(), 1); + }, + ) + .await; +} + +#[actix_rt::test] +async fn search_with_filter_string_notation() { + let server = Server::new().await; + let index = server.index("test"); + + index + .update_settings(json!({"filterableAttributes": ["title"]})) + .await; + + let documents = DOCUMENTS.clone(); + index.add_documents(documents, None).await; + index.wait_update_id(1).await; + + index + .search( + json!({ + "filter": "title = Glass" + }), + |response, code| { + assert_eq!(code, 200, "{}", response); + assert_eq!(response["hits"].as_array().unwrap().len(), 1); + }, + ) + .await; +} + +#[actix_rt::test] +async fn search_with_filter_array_notation() { + let server = Server::new().await; + let index = server.index("test"); + + index + .update_settings(json!({"filterableAttributes": ["title"]})) + .await; + + let documents = DOCUMENTS.clone(); + index.add_documents(documents, None).await; + index.wait_update_id(1).await; + + let (response, code) = index + .search_post(json!({ + "filter": ["title = Glass"] + })) + .await; + assert_eq!(code, 200, "{}", response); + assert_eq!(response["hits"].as_array().unwrap().len(), 1); + + let (response, code) = index + .search_post(json!({ + "filter": [["title = Glass", "title = \"Shazam!\"", "title = \"Escape Room\""]] + })) + .await; + assert_eq!(code, 200, "{}", response); + assert_eq!(response["hits"].as_array().unwrap().len(), 3); +} + +#[actix_rt::test] +async fn search_facet_distribution() { + let server = Server::new().await; + let index = server.index("test"); + + index + .update_settings(json!({"filterableAttributes": ["title"]})) + .await; + + let documents = DOCUMENTS.clone(); + index.add_documents(documents, None).await; + index.wait_update_id(1).await; + + index + .search( + json!({ + "facetsDistribution": ["title"] + }), + |response, code| { + assert_eq!(code, 200, "{}", response); + let dist = response["facetsDistribution"].as_object().unwrap(); + assert_eq!(dist.len(), 1); + assert!(dist.get("title").is_some()); + }, + ) + .await; +} + +#[actix_rt::test] +async fn displayed_attributes() { + let server = Server::new().await; + let index = server.index("test"); + + index + .update_settings(json!({ "displayedAttributes": ["title"] })) + .await; + + let documents = DOCUMENTS.clone(); + index.add_documents(documents, None).await; + index.wait_update_id(1).await; + + let (response, code) = index + .search_post(json!({ "attributesToRetrieve": ["title", "id"] })) + .await; + assert_eq!(code, 200, "{}", response); + assert!(response["hits"].get("title").is_none()); +}