From 520d37983c74f22a88af53420011d60d4948137e Mon Sep 17 00:00:00 2001 From: mpostma Date: Tue, 6 Jul 2021 11:54:09 +0200 Subject: [PATCH 1/2] implement index search methods --- meilisearch-http/Cargo.toml | 2 +- meilisearch-http/tests/common/index.rs | 29 +++++++++++++++++++++++++- 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/meilisearch-http/Cargo.toml b/meilisearch-http/Cargo.toml index aa0f2a974..5931d8d06 100644 --- a/meilisearch-http/Cargo.toml +++ b/meilisearch-http/Cargo.toml @@ -97,7 +97,7 @@ actix-rt = "2.1.0" assert-json-diff = { branch = "master", git = "https://github.com/qdequele/assert-json-diff" } mockall = "0.9.1" paste = "1.0.5" -serde_url_params = "0.2.0" +serde_url_params = "0.2.1" tempdir = "0.3.7" urlencoding = "1.1.1" diff --git a/meilisearch-http/tests/common/index.rs b/meilisearch-http/tests/common/index.rs index 7d98d0733..021b11ec4 100644 --- a/meilisearch-http/tests/common/index.rs +++ b/meilisearch-http/tests/common/index.rs @@ -1,4 +1,4 @@ -use std::time::Duration; +use std::{panic::{UnwindSafe, catch_unwind, resume_unwind}, time::Duration}; use actix_web::http::StatusCode; use paste::paste; @@ -185,6 +185,33 @@ impl Index<'_> { self.service.get(url).await } + 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; + if let Err(e) = catch_unwind(move || test(response, code)) { + eprintln!("Error with get search"); + resume_unwind(e); + } + } + + pub async fn search_post(&self, query: Value) -> (Value, StatusCode) { + let url = format!("/indexes/{}/search", self.uid); + self.service.post(url, query).await + } + + pub async fn search_get(&self, query: Value) -> (Value, StatusCode) { + let params = serde_url_params::to_string(&query).unwrap(); + let url = format!("/indexes/{}/search?{}", self.uid, params); + self.service.get(url).await + } + make_settings_test_routes!(distinct_attribute); } From 41e271974a905be99308bb1359e4541cf41f91f0 Mon Sep 17 00:00:00 2001 From: mpostma Date: Tue, 6 Jul 2021 11:54:37 +0200 Subject: [PATCH 2/2] add tests --- meilisearch-http/src/routes/indexes/search.rs | 10 + meilisearch-http/tests/common/index.rs | 13 +- meilisearch-http/tests/search/errors.rs | 28 +++ meilisearch-http/tests/search/mod.rs | 193 ++++++++++++++++++ 4 files changed, 241 insertions(+), 3 deletions(-) create mode 100644 meilisearch-http/tests/search/errors.rs 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()); +}