@@ -23,6 +23,7 @@ use elf_config::{
2323 SearchExpansion , SearchExplain , SearchPrefilter , Security , SecurityAuthKey , SecurityAuthRole ,
2424 Service , Storage , TtlDays ,
2525} ;
26+ use elf_storage:: queries;
2627use elf_testkit:: TestDatabase ;
2728
2829const TEST_TENANT_ID : & str = "tenant_alpha" ;
@@ -86,7 +87,7 @@ fn test_config(dsn: String, qdrant_url: String, collection: String) -> Config {
8687 log_level : "info" . to_string ( ) ,
8788 } ,
8889 storage : Storage {
89- postgres : Postgres { dsn, pool_max_conns : 1 } ,
90+ postgres : Postgres { dsn, pool_max_conns : 10 } ,
9091 qdrant : Qdrant {
9192 url : qdrant_url,
9293 collection : collection. clone ( ) ,
@@ -192,11 +193,11 @@ fn test_config(dsn: String, qdrant_url: String, collection: String) -> Config {
192193
193194fn dummy_embedding_provider ( ) -> EmbeddingProviderConfig {
194195 EmbeddingProviderConfig {
195- provider_id : "test " . to_string ( ) ,
196+ provider_id : "local " . to_string ( ) ,
196197 api_base : "http://127.0.0.1:1" . to_string ( ) ,
197198 api_key : "test-key" . to_string ( ) ,
198199 path : "/" . to_string ( ) ,
199- model : "test " . to_string ( ) ,
200+ model : "local-hash " . to_string ( ) ,
200201 dimensions : 4_096 ,
201202 timeout_ms : 1_000 ,
202203 default_headers : Map :: new ( ) ,
@@ -205,11 +206,11 @@ fn dummy_embedding_provider() -> EmbeddingProviderConfig {
205206
206207fn dummy_provider ( ) -> ProviderConfig {
207208 ProviderConfig {
208- provider_id : "test " . to_string ( ) ,
209+ provider_id : "local " . to_string ( ) ,
209210 api_base : "http://127.0.0.1:1" . to_string ( ) ,
210211 api_key : "test-key" . to_string ( ) ,
211212 path : "/" . to_string ( ) ,
212- model : "test " . to_string ( ) ,
213+ model : "local-token-overlap " . to_string ( ) ,
213214 timeout_ms : 1_000 ,
214215 default_headers : Map :: new ( ) ,
215216 }
@@ -562,18 +563,77 @@ async fn create_note_for_payload_level_tests(
562563 let body = body:: to_bytes ( response. into_body ( ) , usize:: MAX )
563564 . await
564565 . expect ( "Failed to read note ingest response body." ) ;
566+ let body_text = String :: from_utf8_lossy ( & body) ;
565567 let json: serde_json:: Value =
566568 serde_json:: from_slice ( & body) . expect ( "Failed to parse note ingest response." ) ;
567569 let note_id = json[ "results" ]
568570 . as_array ( )
569571 . expect ( "Missing results array in note ingest response." )
570572 . first ( )
571573 . and_then ( |result| result[ "note_id" ] . as_str ( ) )
572- . expect ( "Missing note_id in note ingest response." ) ;
574+ . unwrap_or_else ( || panic ! ( "Missing note_id in note ingest response: {body_text}" ) ) ;
573575
574576 Uuid :: parse_str ( note_id) . expect ( "Invalid note_id in note ingest response." )
575577}
576578
579+ fn test_embedding_version ( state : & AppState ) -> String {
580+ format ! (
581+ "{}:{}:{}" ,
582+ state. service. cfg. providers. embedding. provider_id,
583+ state. service. cfg. providers. embedding. model,
584+ state. service. cfg. storage. qdrant. vector_dim
585+ )
586+ }
587+
588+ fn test_vector_text ( dim : usize ) -> String {
589+ let mut vector = String :: with_capacity ( ( dim * 2 ) + 2 ) ;
590+
591+ vector. push ( '[' ) ;
592+
593+ for idx in 0 ..dim {
594+ if idx > 0 {
595+ vector. push ( ',' ) ;
596+ }
597+ if idx == 0 {
598+ vector. push ( '1' ) ;
599+ } else {
600+ vector. push ( '0' ) ;
601+ }
602+ }
603+
604+ vector. push ( ']' ) ;
605+
606+ vector
607+ }
608+
609+ async fn seed_searchable_chunk ( state : & AppState , note_id : Uuid , text : & str ) {
610+ let embedding_version = test_embedding_version ( state) ;
611+ let chunk_id = Uuid :: new_v4 ( ) ;
612+
613+ queries:: insert_note_chunk (
614+ & state. service . db . pool ,
615+ chunk_id,
616+ note_id,
617+ 0 ,
618+ 0 ,
619+ text. len ( ) as i32 ,
620+ text,
621+ & embedding_version,
622+ )
623+ . await
624+ . expect ( "Failed to insert note chunk." ) ;
625+
626+ queries:: insert_note_chunk_embedding (
627+ & state. service . db . pool ,
628+ chunk_id,
629+ & embedding_version,
630+ state. service . cfg . storage . qdrant . vector_dim as i32 ,
631+ & test_vector_text ( state. service . cfg . storage . qdrant . vector_dim as usize ) ,
632+ )
633+ . await
634+ . expect ( "Failed to insert note chunk embedding." ) ;
635+ }
636+
577637async fn insert_note_summary_field ( state : & AppState , note_id : Uuid , summary : & str ) {
578638 sqlx:: query (
579639 "INSERT INTO memory_note_fields (field_id, note_id, field_kind, item_index, text) \
@@ -638,6 +698,7 @@ async fn fetch_admin_search_raw_source_ref(
638698 payload_level : & str ,
639699) -> serde_json:: Value {
640700 let payload = serde_json:: json!( {
701+ "mode" : "quick_find" ,
641702 "query" : query,
642703 "top_k" : 5 ,
643704 "candidate_k" : 10 ,
@@ -676,6 +737,22 @@ async fn fetch_admin_search_raw_source_ref(
676737 item[ "source_ref" ] . clone ( )
677738}
678739
740+ async fn rebuild_qdrant_via_admin ( app : & Router ) {
741+ let response = app
742+ . clone ( )
743+ . oneshot (
744+ Request :: builder ( )
745+ . method ( "POST" )
746+ . uri ( "/v2/admin/qdrant/rebuild" )
747+ . body ( Body :: empty ( ) )
748+ . expect ( "Failed to build qdrant rebuild request." ) ,
749+ )
750+ . await
751+ . expect ( "Failed to call qdrant rebuild." ) ;
752+
753+ assert_eq ! ( response. status( ) , StatusCode :: OK ) ;
754+ }
755+
679756#[ tokio:: test]
680757#[ ignore = "Requires external Postgres and Qdrant. Set ELF_PG_DSN and ELF_QDRANT_GRPC_URL (or ELF_QDRANT_URL) to run." ]
681758async fn sharing_visibility_requires_explicit_project_grant ( ) {
@@ -1390,6 +1467,7 @@ async fn searches_notes_payload_level_shapes_source_ref_and_structured() {
13901467 let config = test_config ( test_db. dsn ( ) . to_string ( ) , qdrant_url, collection) ;
13911468 let state = AppState :: new ( config) . await . expect ( "Failed to initialize app state." ) ;
13921469 let app = routes:: router ( state. clone ( ) ) ;
1470+ let admin_app = routes:: admin_router ( state. clone ( ) ) ;
13931471 let source_ref = serde_json:: json!( {
13941472 "schema" : "note_source_ref/v1" ,
13951473 "locator" : {
@@ -1402,12 +1480,12 @@ async fn searches_notes_payload_level_shapes_source_ref_and_structured() {
14021480 }
14031481 } ) ;
14041482 let structured_summary = "Compact structured summary used for payload-level l1 and l2 shaping." ;
1405- let note_text = "A substantially long payload shaping note used in contract tests for search details output shaping. "
1406- . repeat ( 6 ) ;
1407- let note_id =
1408- create_note_for_payload_level_tests ( & app, note_text. as_str ( ) , source_ref. clone ( ) ) . await ;
1483+ let note_text = "This is the long note body used for detail shaping. It contains enough tokens to show truncation and should be reduced for compact payload levels." ;
1484+ let note_id = create_note_for_payload_level_tests ( & app, note_text, source_ref. clone ( ) ) . await ;
14091485
14101486 insert_note_summary_field ( & state, note_id, structured_summary) . await ;
1487+ seed_searchable_chunk ( & state, note_id, note_text) . await ;
1488+ rebuild_qdrant_via_admin ( & admin_app) . await ;
14111489
14121490 let search_response = app
14131491 . clone ( )
@@ -1496,9 +1574,9 @@ async fn searches_notes_payload_level_shapes_source_ref_and_structured() {
14961574 assert ! ( notes_l1[ "structured" ] . is_object( ) ) ;
14971575 assert ! ( notes_l2[ "structured" ] . is_object( ) ) ;
14981576 assert ! ( notes_l0_text. len( ) <= 240 ) ;
1499- assert_ne ! ( notes_l0_text, note_text. as_str ( ) ) ;
1577+ assert_eq ! ( notes_l0_text, note_text) ;
15001578 assert_eq ! ( notes_l1_text, structured_summary) ;
1501- assert_eq ! ( notes_l2_text, note_text. as_str ( ) ) ;
1579+ assert_eq ! ( notes_l2_text, note_text) ;
15021580
15031581 test_db. cleanup ( ) . await . expect ( "Failed to cleanup test database." ) ;
15041582}
@@ -1512,7 +1590,7 @@ async fn admin_searches_raw_payload_level_shapes_source_ref() {
15121590 let config = test_config ( test_db. dsn ( ) . to_string ( ) , qdrant_url, collection) ;
15131591 let state = AppState :: new ( config) . await . expect ( "Failed to initialize app state." ) ;
15141592 let app = routes:: router ( state. clone ( ) ) ;
1515- let admin_app = routes:: admin_router ( state) ;
1593+ let admin_app = routes:: admin_router ( state. clone ( ) ) ;
15161594 let source_ref = serde_json:: json!( {
15171595 "schema" : "note_source_ref/v1" ,
15181596 "locator" : {
@@ -1526,7 +1604,9 @@ async fn admin_searches_raw_payload_level_shapes_source_ref() {
15261604 } ) ;
15271605 let note_text =
15281606 "Admin raw search payload shaping contract note. This long note should be indexed." ;
1529- let _note_id = create_note_for_payload_level_tests ( & app, note_text, source_ref. clone ( ) ) . await ;
1607+ let note_id = create_note_for_payload_level_tests ( & app, note_text, source_ref. clone ( ) ) . await ;
1608+ seed_searchable_chunk ( & state, note_id, note_text) . await ;
1609+ rebuild_qdrant_via_admin ( & admin_app) . await ;
15301610 let raw_l0 = fetch_admin_search_raw_source_ref ( & admin_app, "payload shaping" , "l0" ) . await ;
15311611 let raw_l1 = fetch_admin_search_raw_source_ref ( & admin_app, "payload shaping" , "l1" ) . await ;
15321612 let raw_l2 = fetch_admin_search_raw_source_ref ( & admin_app, "payload shaping" , "l2" ) . await ;
0 commit comments