use search::{self, SearchClient, SearchDocument}; fn meili() -> (String, String) { ( std::env::var("MEILI_URL").expect("MEILI_URL must be set"), std::env::var("MEILI_MASTER_KEY").expect("MEILI_MASTER_KEY must be set"), ) } fn unique_index() -> String { format!("objects_test_{}", uuid::Uuid::new_v4().simple()) } fn doc(id: &str, object_name: &str, fields_text: &[&str]) -> SearchDocument { SearchDocument { id: id.to_string(), object_number: format!("N-{id}"), object_name: object_name.to_string(), brief_description: None, current_owner: None, recorder: None, visibility: "draft".to_string(), fields_text: fields_text.iter().map(|s| s.to_string()).collect(), } } #[tokio::test] async fn index_search_and_remove() { let (url, key) = meili(); let client = SearchClient::connect(&url, &key, &unique_index()).unwrap(); client.ensure_index().await.unwrap(); let vase = domain::ObjectId::new(); let chair = domain::ObjectId::new(); client .index_object(&doc(&vase.to_string(), "vase", &["wood", "trä"])) .await .unwrap(); client .index_object(&doc(&chair.to_string(), "chair", &["oak"])) .await .unwrap(); let hits = client.search("wood").await.unwrap(); assert_eq!(hits, vec![vase]); let hits = client.search("chair").await.unwrap(); assert_eq!(hits, vec![chair]); client.remove_object(vase).await.unwrap(); assert!(client.search("wood").await.unwrap().is_empty()); } #[tokio::test] async fn search_objects_returns_hits_with_highlight_filter_and_paging() { let (url, key) = meili(); let client = SearchClient::connect(&url, &key, &unique_index()).unwrap(); client.ensure_index().await.unwrap(); let a = domain::ObjectId::new(); let b = domain::ObjectId::new(); let c = domain::ObjectId::new(); let mut bronze_a = doc( &a.to_string(), "Bronze figurine", &["cast bronze with green patina"], ); bronze_a.visibility = "public".to_string(); let mut bronze_b = doc(&b.to_string(), "Ceremonial bowl", &["bronze alloy rim"]); bronze_b.visibility = "public".to_string(); let mut bronze_c = doc(&c.to_string(), "Door fitting", &["bronze hinge"]); bronze_c.visibility = "draft".to_string(); client.index_object(&bronze_a).await.unwrap(); client.index_object(&bronze_b).await.unwrap(); client.index_object(&bronze_c).await.unwrap(); let results = client.search_objects("bronze", None, 0, 20).await.unwrap(); assert_eq!(results.estimated_total, 3); assert_eq!(results.hits.len(), 3); let hit = results.hits.iter().find(|h| h.id == a.to_string()).unwrap(); assert_eq!(hit.object_name, "Bronze figurine"); assert_eq!(hit.object_number, format!("N-{a}")); let snippet = hit.snippet.as_ref().expect("a matched snippet"); assert!( snippet.contains(search::HL_PRE), "snippet must mark the match" ); assert!(snippet.contains(search::HL_POST)); let public = client .search_objects("bronze", Some("public"), 0, 20) .await .unwrap(); assert_eq!(public.estimated_total, 2); assert!(public.hits.iter().all(|h| h.visibility == "public")); let page = client.search_objects("bronze", None, 0, 1).await.unwrap(); assert_eq!(page.hits.len(), 1); assert_eq!(page.estimated_total, 3); } #[tokio::test] async fn ensure_index_is_idempotent() { let (url, key) = meili(); let index = unique_index(); let client = SearchClient::connect(&url, &key, &index).unwrap(); client.ensure_index().await.unwrap(); // second call against the now-existing index must succeed client.ensure_index().await.unwrap(); // and the client still works let id = domain::ObjectId::new(); client .index_object(&doc(&id.to_string(), "lamp", &[])) .await .unwrap(); assert_eq!(client.search("lamp").await.unwrap(), vec![id]); }