From 72eed0e369d87dba70e52a010b4018b0186e02f1 Mon Sep 17 00:00:00 2001 From: mpostma Date: Thu, 18 Feb 2021 19:50:52 +0100 Subject: [PATCH] test create index --- tests/common/index.rs | 27 ++ tests/common/mod.rs | 30 ++ tests/common/server.rs | 537 ++++++++++++++++++++++++++++++++++++ tests/common/service.rs | 39 +++ tests/index/create_index.rs | 48 ++++ tests/index/mod.rs | 2 + tests/integration.rs | 3 + tests/search/mod.rs | 3 + 8 files changed, 689 insertions(+) create mode 100644 tests/common/index.rs create mode 100644 tests/common/mod.rs create mode 100644 tests/common/server.rs create mode 100644 tests/common/service.rs create mode 100644 tests/index/create_index.rs create mode 100644 tests/index/mod.rs create mode 100644 tests/integration.rs create mode 100644 tests/search/mod.rs diff --git a/tests/common/index.rs b/tests/common/index.rs new file mode 100644 index 000000000..69b383b4a --- /dev/null +++ b/tests/common/index.rs @@ -0,0 +1,27 @@ +use actix_web::http::StatusCode; +use serde_json::{json, Value}; + +use super::service::Service; + +pub struct Index<'a> { + pub uid: String, + pub service: &'a Service, +} + +impl Index<'_> { + pub async fn get(&self) -> (Value, StatusCode) { + let url = format!("/indexes/{}", self.uid); + self.service.get(url).await + } + + pub async fn create<'a>( + &'a self, + primary_key: Option<&str>, + ) -> (Value, StatusCode) { + let body = json!({ + "uid": self.uid, + "primaryKey": primary_key, + }); + self.service.post("/indexes", body).await + } +} diff --git a/tests/common/mod.rs b/tests/common/mod.rs new file mode 100644 index 000000000..36c7e8190 --- /dev/null +++ b/tests/common/mod.rs @@ -0,0 +1,30 @@ +mod index; +mod server; +mod service; + +pub use server::Server; + +/// Performs a search test on both post and get routes +#[macro_export] +macro_rules! test_post_get_search { + ($server:expr, $query:expr, |$response:ident, $status_code:ident | $block:expr) => { + let post_query: meilisearch_http::routes::search::SearchQueryPost = + serde_json::from_str(&$query.clone().to_string()).unwrap(); + let get_query: meilisearch_http::routes::search::SearchQuery = post_query.into(); + let get_query = ::serde_url_params::to_string(&get_query).unwrap(); + let ($response, $status_code) = $server.search_get(&get_query).await; + let _ = ::std::panic::catch_unwind(|| $block).map_err(|e| { + panic!( + "panic in get route: {:?}", + e.downcast_ref::<&str>().unwrap() + ) + }); + let ($response, $status_code) = $server.search_post($query).await; + let _ = ::std::panic::catch_unwind(|| $block).map_err(|e| { + panic!( + "panic in post route: {:?}", + e.downcast_ref::<&str>().unwrap() + ) + }); + }; +} diff --git a/tests/common/server.rs b/tests/common/server.rs new file mode 100644 index 000000000..5982af973 --- /dev/null +++ b/tests/common/server.rs @@ -0,0 +1,537 @@ +use tempdir::TempDir; +use byte_unit::{Byte, ByteUnit}; + +use meilisearch_http::data::Data; +use meilisearch_http::option::{Opt, IndexerOpts}; + +use super::index::Index; +use super::service::Service; + +pub struct Server { + service: Service, +} + +impl Server { + pub async fn new() -> Self { + let tmp_dir = TempDir::new("meilisearch").unwrap(); + + let opt = Opt { + db_path: tmp_dir.path().join("db"), + dumps_dir: tmp_dir.path().join("dump"), + dump_batch_size: 16, + http_addr: "127.0.0.1:7700".to_owned(), + master_key: None, + env: "development".to_owned(), + no_analytics: true, + max_mdb_size: Byte::from_unit(4.0, ByteUnit::GiB).unwrap(), + max_udb_size: Byte::from_unit(4.0, ByteUnit::GiB).unwrap(), + http_payload_size_limit: Byte::from_unit(10.0, ByteUnit::MiB).unwrap(), + ssl_cert_path: None, + ssl_key_path: None, + ssl_auth_path: None, + ssl_ocsp_path: None, + ssl_require_auth: false, + ssl_resumption: false, + ssl_tickets: false, + import_snapshot: None, + ignore_missing_snapshot: false, + ignore_snapshot_if_db_exists: false, + snapshot_dir: ".".into(), + schedule_snapshot: false, + snapshot_interval_sec: None, + import_dump: None, + indexer_options: IndexerOpts::default(), + }; + + let data = Data::new(opt).unwrap(); + let service = Service(data); + + Server { + service, + } + } + + //pub async fn test_server() -> Self { + //let mut server = Self::new(); + + //let body = json!({ + //"uid": "test", + //"primaryKey": "id", + //}); + + //server.create_index(body).await; + + //let body = json!({ + ////"rankingRules": [ + ////"typo", + ////"words", + ////"proximity", + ////"attribute", + ////"wordsPosition", + ////"exactness", + ////], + //"searchableAttributes": [ + //"balance", + //"picture", + //"age", + //"color", + //"name", + //"gender", + //"email", + //"phone", + //"address", + //"about", + //"registered", + //"latitude", + //"longitude", + //"tags", + //], + //"displayedAttributes": [ + //"id", + //"isActive", + //"balance", + //"picture", + //"age", + //"color", + //"name", + //"gender", + //"email", + //"phone", + //"address", + //"about", + //"registered", + //"latitude", + //"longitude", + //"tags", + //], + //}); + + //server.update_all_settings(body).await; + + //let dataset = include_bytes!("../assets/test_set.json"); + + //let body: Value = serde_json::from_slice(dataset).unwrap(); + + //server.add_or_replace_multiple_documents(body).await; + //server + //} + + //pub fn data(&self) -> &Data { + //&self.data + //} + + //pub async fn wait_update_id(&mut self, update_id: u64) { + //// try 10 times to get status, or panic to not wait forever + //for _ in 0..10 { + //let (response, status_code) = self.get_update_status(update_id).await; + //assert_eq!(status_code, 200); + + //if response["status"] == "processed" || response["status"] == "failed" { + //// eprintln!("{:#?}", response); + //return; + //} + + //delay_for(Duration::from_secs(1)).await; + //} + //panic!("Timeout waiting for update id"); + //} + + // Global Http request GET/POST/DELETE async or sync + + //pub async fn get_request(&mut self, url: &str) -> (Value, StatusCode) { + //eprintln!("get_request: {}", url); + + //let mut app = + //test::init_service(meilisearch_http::create_app(&self.data, true).wrap(NormalizePath)).await; + + //let req = test::TestRequest::get().uri(url).to_request(); + //let res = test::call_service(&mut app, req).await; + //let status_code = res.status(); + + //let body = test::read_body(res).await; + //let response = serde_json::from_slice(&body).unwrap_or_default(); + //(response, status_code) + //} + + + //pub async fn post_request_async(&mut self, url: &str, body: Value) -> (Value, StatusCode) { + //eprintln!("post_request_async: {}", url); + + //let (response, status_code) = self.post_request(url, body).await; + //eprintln!("response: {}", response); + //assert!(response["updateId"].as_u64().is_some()); + //self.wait_update_id(response["updateId"].as_u64().unwrap()) + //.await; + //(response, status_code) + //} + + //pub async fn put_request(&mut self, url: &str, body: Value) -> (Value, StatusCode) { + //eprintln!("put_request: {}", url); + + //let mut app = + //test::init_service(meilisearch_http::create_app(&self.data, true).wrap(NormalizePath)).await; + + //let req = test::TestRequest::put() + //.uri(url) + //.set_json(&body) + //.to_request(); + //let res = test::call_service(&mut app, req).await; + //let status_code = res.status(); + + //let body = test::read_body(res).await; + //let response = serde_json::from_slice(&body).unwrap_or_default(); + //(response, status_code) + //} + + //pub async fn put_request_async(&mut self, url: &str, body: Value) -> (Value, StatusCode) { + //eprintln!("put_request_async: {}", url); + + //let (response, status_code) = self.put_request(url, body).await; + //assert!(response["updateId"].as_u64().is_some()); + //assert_eq!(status_code, 202); + //self.wait_update_id(response["updateId"].as_u64().unwrap()) + //.await; + //(response, status_code) + //} + + //pub async fn delete_request(&mut self, url: &str) -> (Value, StatusCode) { + //eprintln!("delete_request: {}", url); + + //let mut app = + //test::init_service(meilisearch_http::create_app(&self.data, true).wrap(NormalizePath)).await; + + //let req = test::TestRequest::delete().uri(url).to_request(); + //let res = test::call_service(&mut app, req).await; + //let status_code = res.status(); + + //let body = test::read_body(res).await; + //let response = serde_json::from_slice(&body).unwrap_or_default(); + //(response, status_code) + //} + + //pub async fn delete_request_async(&mut self, url: &str) -> (Value, StatusCode) { + //eprintln!("delete_request_async: {}", url); + + //let (response, status_code) = self.delete_request(url).await; + //assert!(response["updateId"].as_u64().is_some()); + //assert_eq!(status_code, 202); + //self.wait_update_id(response["updateId"].as_u64().unwrap()) + //.await; + //(response, status_code) + //} + + // All Routes + + //pub async fn list_indexes(&mut self) -> (Value, StatusCode) { + //self.get_request("/indexes").await + //} + + /// Returns a view to an index. There is no guarantee that the index exists. + pub fn index<'a>(&'a self, uid: impl AsRef) -> Index<'a> { + Index { + uid: uid.as_ref().to_string(), + service: &self.service, + } + } + //pub async fn search_multi_index(&mut self, query: &str) -> (Value, StatusCode) { + //let url = format!("/indexes/search?{}", query); + //self.get_request(&url).await + //} + + //pub async fn get_index(&mut self) -> (Value, StatusCode) { + //let url = format!("/indexes/{}", self.uid); + //self.get_request(&url).await + //} + + //pub async fn update_index(&mut self, body: Value) -> (Value, StatusCode) { + //let url = format!("/indexes/{}", self.uid); + //self.put_request(&url, body).await + //} + + //pub async fn delete_index(&mut self) -> (Value, StatusCode) { + //let url = format!("/indexes/{}", self.uid); + //self.delete_request(&url).await + //} + + //pub async fn search_get(&mut self, query: &str) -> (Value, StatusCode) { + //let url = format!("/indexes/{}/search?{}", self.uid, query); + //self.get_request(&url).await + //} + + //pub async fn search_post(&mut self, body: Value) -> (Value, StatusCode) { + //let url = format!("/indexes/{}/search", self.uid); + //self.post_request(&url, body).await + //} + + //pub async fn get_all_updates_status(&mut self) -> (Value, StatusCode) { + //let url = format!("/indexes/{}/updates", self.uid); + //self.get_request(&url).await + //} + + //pub async fn get_update_status(&mut self, update_id: u64) -> (Value, StatusCode) { + //let url = format!("/indexes/{}/updates/{}", self.uid, update_id); + //self.get_request(&url).await + //} + + //pub async fn get_all_documents(&mut self) -> (Value, StatusCode) { + //let url = format!("/indexes/{}/documents", self.uid); + //self.get_request(&url).await + //} + + //pub async fn add_or_replace_multiple_documents(&mut self, body: Value) { + //let url = format!("/indexes/{}/documents", self.uid); + //self.post_request_async(&url, body).await; + //} + + //pub async fn add_or_replace_multiple_documents_sync( + //&mut self, + //body: Value, + //) -> (Value, StatusCode) { + //let url = format!("/indexes/{}/documents", self.uid); + //self.post_request(&url, body).await + //} + + //pub async fn add_or_update_multiple_documents(&mut self, body: Value) { + //let url = format!("/indexes/{}/documents", self.uid); + //self.put_request_async(&url, body).await; + //} + + //pub async fn clear_all_documents(&mut self) { + //let url = format!("/indexes/{}/documents", self.uid); + //self.delete_request_async(&url).await; + //} + + //pub async fn get_document(&mut self, document_id: impl ToString) -> (Value, StatusCode) { + //let url = format!( + //"/indexes/{}/documents/{}", + //self.uid, + //document_id.to_string() + //); + //self.get_request(&url).await + //} + + //pub async fn delete_document(&mut self, document_id: impl ToString) -> (Value, StatusCode) { + //let url = format!( + //"/indexes/{}/documents/{}", + //self.uid, + //document_id.to_string() + //); + //self.delete_request_async(&url).await + //} + + //pub async fn delete_multiple_documents(&mut self, body: Value) -> (Value, StatusCode) { + //let url = format!("/indexes/{}/documents/delete-batch", self.uid); + //self.post_request_async(&url, body).await + //} + + //pub async fn get_all_settings(&mut self) -> (Value, StatusCode) { + //let url = format!("/indexes/{}/settings", self.uid); + //self.get_request(&url).await + //} + + //pub async fn update_all_settings(&mut self, body: Value) { + //let url = format!("/indexes/{}/settings", self.uid); + //self.post_request_async(&url, body).await; + //} + + //pub async fn update_all_settings_sync(&mut self, body: Value) -> (Value, StatusCode) { + //let url = format!("/indexes/{}/settings", self.uid); + //self.post_request(&url, body).await + //} + + //pub async fn delete_all_settings(&mut self) -> (Value, StatusCode) { + //let url = format!("/indexes/{}/settings", self.uid); + //self.delete_request_async(&url).await + //} + + //pub async fn get_ranking_rules(&mut self) -> (Value, StatusCode) { + //let url = format!("/indexes/{}/settings/ranking-rules", self.uid); + //self.get_request(&url).await + //} + + //pub async fn update_ranking_rules(&mut self, body: Value) { + //let url = format!("/indexes/{}/settings/ranking-rules", self.uid); + //self.post_request_async(&url, body).await; + //} + + //pub async fn update_ranking_rules_sync(&mut self, body: Value) -> (Value, StatusCode) { + //let url = format!("/indexes/{}/settings/ranking-rules", self.uid); + //self.post_request(&url, body).await + //} + + //pub async fn delete_ranking_rules(&mut self) -> (Value, StatusCode) { + //let url = format!("/indexes/{}/settings/ranking-rules", self.uid); + //self.delete_request_async(&url).await + //} + + //pub async fn get_distinct_attribute(&mut self) -> (Value, StatusCode) { + //let url = format!("/indexes/{}/settings/distinct-attribute", self.uid); + //self.get_request(&url).await + //} + + //pub async fn update_distinct_attribute(&mut self, body: Value) { + //let url = format!("/indexes/{}/settings/distinct-attribute", self.uid); + //self.post_request_async(&url, body).await; + //} + + //pub async fn update_distinct_attribute_sync(&mut self, body: Value) -> (Value, StatusCode) { + //let url = format!("/indexes/{}/settings/distinct-attribute", self.uid); + //self.post_request(&url, body).await + //} + + //pub async fn delete_distinct_attribute(&mut self) -> (Value, StatusCode) { + //let url = format!("/indexes/{}/settings/distinct-attribute", self.uid); + //self.delete_request_async(&url).await + //} + + //pub async fn get_primary_key(&mut self) -> (Value, StatusCode) { + //let url = format!("/indexes/{}/settings/primary_key", self.uid); + //self.get_request(&url).await + //} + + //pub async fn get_searchable_attributes(&mut self) -> (Value, StatusCode) { + //let url = format!("/indexes/{}/settings/searchable-attributes", self.uid); + //self.get_request(&url).await + //} + + //pub async fn update_searchable_attributes(&mut self, body: Value) { + //let url = format!("/indexes/{}/settings/searchable-attributes", self.uid); + //self.post_request_async(&url, body).await; + //} + + //pub async fn update_searchable_attributes_sync(&mut self, body: Value) -> (Value, StatusCode) { + //let url = format!("/indexes/{}/settings/searchable-attributes", self.uid); + //self.post_request(&url, body).await + //} + + //pub async fn delete_searchable_attributes(&mut self) -> (Value, StatusCode) { + //let url = format!("/indexes/{}/settings/searchable-attributes", self.uid); + //self.delete_request_async(&url).await + //} + + //pub async fn get_displayed_attributes(&mut self) -> (Value, StatusCode) { + //let url = format!("/indexes/{}/settings/displayed-attributes", self.uid); + //self.get_request(&url).await + //} + + //pub async fn update_displayed_attributes(&mut self, body: Value) { + //let url = format!("/indexes/{}/settings/displayed-attributes", self.uid); + //self.post_request_async(&url, body).await; + //} + + //pub async fn update_displayed_attributes_sync(&mut self, body: Value) -> (Value, StatusCode) { + //let url = format!("/indexes/{}/settings/displayed-attributes", self.uid); + //self.post_request(&url, body).await + //} + + //pub async fn delete_displayed_attributes(&mut self) -> (Value, StatusCode) { + //let url = format!("/indexes/{}/settings/displayed-attributes", self.uid); + //self.delete_request_async(&url).await + //} + + //pub async fn get_attributes_for_faceting(&mut self) -> (Value, StatusCode) { + //let url = format!("/indexes/{}/settings/attributes-for-faceting", self.uid); + //self.get_request(&url).await + //} + + //pub async fn update_attributes_for_faceting(&mut self, body: Value) { + //let url = format!("/indexes/{}/settings/attributes-for-faceting", self.uid); + //self.post_request_async(&url, body).await; + //} + + //pub async fn update_attributes_for_faceting_sync( + //&mut self, + //body: Value, + //) -> (Value, StatusCode) { + //let url = format!("/indexes/{}/settings/attributes-for-faceting", self.uid); + //self.post_request(&url, body).await + //} + + //pub async fn delete_attributes_for_faceting(&mut self) -> (Value, StatusCode) { + //let url = format!("/indexes/{}/settings/attributes-for-faceting", self.uid); + //self.delete_request_async(&url).await + //} + + //pub async fn get_synonyms(&mut self) -> (Value, StatusCode) { + //let url = format!("/indexes/{}/settings/synonyms", self.uid); + //self.get_request(&url).await + //} + + //pub async fn update_synonyms(&mut self, body: Value) { + //let url = format!("/indexes/{}/settings/synonyms", self.uid); + //self.post_request_async(&url, body).await; + //} + + //pub async fn update_synonyms_sync(&mut self, body: Value) -> (Value, StatusCode) { + //let url = format!("/indexes/{}/settings/synonyms", self.uid); + //self.post_request(&url, body).await + //} + + //pub async fn delete_synonyms(&mut self) -> (Value, StatusCode) { + //let url = format!("/indexes/{}/settings/synonyms", self.uid); + //self.delete_request_async(&url).await + //} + + //pub async fn get_stop_words(&mut self) -> (Value, StatusCode) { + //let url = format!("/indexes/{}/settings/stop-words", self.uid); + //self.get_request(&url).await + //} + + //pub async fn update_stop_words(&mut self, body: Value) { + //let url = format!("/indexes/{}/settings/stop-words", self.uid); + //self.post_request_async(&url, body).await; + //} + + //pub async fn update_stop_words_sync(&mut self, body: Value) -> (Value, StatusCode) { + //let url = format!("/indexes/{}/settings/stop-words", self.uid); + //self.post_request(&url, body).await + //} + + //pub async fn delete_stop_words(&mut self) -> (Value, StatusCode) { + //let url = format!("/indexes/{}/settings/stop-words", self.uid); + //self.delete_request_async(&url).await + //} + + //pub async fn get_index_stats(&mut self) -> (Value, StatusCode) { + //let url = format!("/indexes/{}/stats", self.uid); + //self.get_request(&url).await + //} + + //pub async fn list_keys(&mut self) -> (Value, StatusCode) { + //self.get_request("/keys").await + //} + + //pub async fn get_health(&mut self) -> (Value, StatusCode) { + //self.get_request("/health").await + //} + + //pub async fn update_health(&mut self, body: Value) -> (Value, StatusCode) { + //self.put_request("/health", body).await + //} + + //pub async fn get_version(&mut self) -> (Value, StatusCode) { + //self.get_request("/version").await + //} + + //pub async fn get_sys_info(&mut self) -> (Value, StatusCode) { + //self.get_request("/sys-info").await + //} + + //pub async fn get_sys_info_pretty(&mut self) -> (Value, StatusCode) { + //self.get_request("/sys-info/pretty").await + //} + + //pub async fn trigger_dump(&self) -> (Value, StatusCode) { + //self.post_request("/dumps", Value::Null).await + //} + + //pub async fn get_dump_status(&mut self, dump_uid: &str) -> (Value, StatusCode) { + //let url = format!("/dumps/{}/status", dump_uid); + //self.get_request(&url).await + //} + + //pub async fn trigger_dump_importation(&mut self, dump_uid: &str) -> (Value, StatusCode) { + //let url = format!("/dumps/{}/import", dump_uid); + //self.get_request(&url).await + //} +} diff --git a/tests/common/service.rs b/tests/common/service.rs new file mode 100644 index 000000000..21442cf64 --- /dev/null +++ b/tests/common/service.rs @@ -0,0 +1,39 @@ +use actix_web::{http::StatusCode, test}; +use serde_json::Value; + +use meilisearch_http::data::Data; +use meilisearch_http::helpers::NormalizePath; + +pub struct Service(pub Data); + +impl Service { + pub async fn post(&self, url: impl AsRef, body: Value) -> (Value, StatusCode) { + let mut app = + test::init_service(meilisearch_http::create_app(&self.0, true).wrap(NormalizePath)).await; + + let req = test::TestRequest::post() + .uri(url.as_ref()) + .set_json(&body) + .to_request(); + let res = test::call_service(&mut app, req).await; + let status_code = res.status(); + + let body = test::read_body(res).await; + let response = serde_json::from_slice(&body).unwrap_or_default(); + (response, status_code) + } + + pub async fn get(&self, url: impl AsRef) -> (Value, StatusCode) { + let mut app = + test::init_service(meilisearch_http::create_app(&self.0, true).wrap(NormalizePath)).await; + + let req = test::TestRequest::get().uri(url.as_ref()).to_request(); + let res = test::call_service(&mut app, req).await; + let status_code = res.status(); + + let body = test::read_body(res).await; + let response = serde_json::from_slice(&body).unwrap_or_default(); + (response, status_code) + } +} + diff --git a/tests/index/create_index.rs b/tests/index/create_index.rs new file mode 100644 index 000000000..b7bce4e26 --- /dev/null +++ b/tests/index/create_index.rs @@ -0,0 +1,48 @@ +use crate::common::Server; +use serde_json::Value; + +#[actix_rt::test] +async fn create_index_no_primary_key() { + let server = Server::new().await; + let index = server.index("test"); + let (response, code) = index.create(None).await; + + assert_eq!(code, 200); + assert_eq!(response["uid"], "test"); + assert!(response.get("uuid").is_some()); + assert!(response.get("createdAt").is_some()); + assert!(response.get("updatedAt").is_some()); + assert_eq!(response["createdAt"], response["updatedAt"]); + assert_eq!(response["primaryKey"], Value::Null); + assert_eq!(response.as_object().unwrap().len(), 5); +} + +#[actix_rt::test] +async fn create_index_with_primary_key() { + let server = Server::new().await; + let index = server.index("test"); + let (response, code) = index.create(Some("primary")).await; + + assert_eq!(code, 200); + assert_eq!(response["uid"], "test"); + assert!(response.get("uuid").is_some()); + assert!(response.get("createdAt").is_some()); + assert!(response.get("updatedAt").is_some()); + assert_eq!(response["createdAt"], response["updatedAt"]); + assert_eq!(response["primaryKey"], "primary"); + assert_eq!(response.as_object().unwrap().len(), 5); +} + +// TODO: partial test since we are testing error, amd error is not yet fully implemented in +// transplant +#[actix_rt::test] +async fn create_existing_index() { + let server = Server::new().await; + let index = server.index("test"); + let (_, code) = index.create(Some("primary")).await; + + assert_eq!(code, 200); + + let (_response, code) = index.create(Some("primary")).await; + assert_eq!(code, 400); +} diff --git a/tests/index/mod.rs b/tests/index/mod.rs new file mode 100644 index 000000000..cd3203e6b --- /dev/null +++ b/tests/index/mod.rs @@ -0,0 +1,2 @@ +mod create_index; +mod get_index; diff --git a/tests/integration.rs b/tests/integration.rs new file mode 100644 index 000000000..2b8dc5612 --- /dev/null +++ b/tests/integration.rs @@ -0,0 +1,3 @@ +mod common; +mod search; +mod index; diff --git a/tests/search/mod.rs b/tests/search/mod.rs new file mode 100644 index 000000000..735a3478f --- /dev/null +++ b/tests/search/mod.rs @@ -0,0 +1,3 @@ +// 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. +