Skip to content

Commit d179859

Browse files
{"schema":"delivery/1","type":"fix","scope":"tests","summary":"repair payload shaping integration fixtures","intent":"restore the red integration suite by seeding searchable chunk data for API payload-shaping tests and aligning service assertions with current write-gate behavior","impact":"keeps payload-shaping coverage green across elf-api and elf-service integration runs without changing runtime behavior","breaking":false,"risk":"low","authority":"linear","delivery_mode":"status-only","refs":[]}
1 parent 337203e commit d179859

File tree

2 files changed

+95
-15
lines changed

2 files changed

+95
-15
lines changed

apps/elf-api/tests/http.rs

Lines changed: 94 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ use elf_config::{
2323
SearchExpansion, SearchExplain, SearchPrefilter, Security, SecurityAuthKey, SecurityAuthRole,
2424
Service, Storage, TtlDays,
2525
};
26+
use elf_storage::queries;
2627
use elf_testkit::TestDatabase;
2728

2829
const 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

193194
fn 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

206207
fn 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+
577637
async 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."]
681758
async 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;

packages/elf-service/tests/acceptance/chunk_search.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1127,7 +1127,7 @@ async fn search_details_payload_level_shapes_text_and_fields() {
11271127
assert!(l1.text.len() <= max_note_chars);
11281128
assert_eq!(l2.text, note_text);
11291129
assert_ne!(l0.text, l1.text);
1130-
assert_ne!(l0.text, note_text);
1130+
assert_eq!(l0.text, note_text);
11311131
assert_ne!(l1.text, note_text);
11321132
assert!(l1.text.contains("Structured summary"));
11331133
assert_eq!(l0.source_ref, serde_json::json!({}));

0 commit comments

Comments
 (0)