Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
684 changes: 21 additions & 663 deletions packages/elf-service/tests/acceptance/docs_extension_v1/helpers.rs

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
use qdrant_client::qdrant::value;
use uuid::Uuid;

use elf_service::{
DocsExcerptsGetRequest, DocsGetRequest, DocsSearchL0Request, ElfService, TextQuoteSelector,
docs::DocRetrievalTrajectory,
};

pub(crate) fn payload_string(payload_value: &qdrant_client::qdrant::Value) -> Option<&str> {
match payload_value.kind.as_ref() {
Some(value::Kind::StringValue(value)) => Some(value.as_str()),
_ => None,
}
}

pub(crate) fn trajectory_stage_stats<'a>(
trajectory: &'a DocRetrievalTrajectory,
stage_name: &str,
) -> Option<&'a serde_json::Value> {
trajectory.stages.iter().find(|stage| stage.stage_name == stage_name).map(|stage| &stage.stats)
}

pub(crate) async fn assert_doc_get(service: &ElfService, doc_id: Uuid) {
let get_as_owner = service
.docs_get(DocsGetRequest {
tenant_id: "t".to_string(),
project_id: "p".to_string(),
agent_id: "owner".to_string(),
read_profile: "private_plus_project".to_string(),
doc_id,
})
.await
.expect("Failed to get doc as owner.");

assert_eq!(get_as_owner.scope, "project_shared");
assert_eq!(get_as_owner.doc_type, "knowledge");
assert_eq!(get_as_owner.agent_id, "owner");
assert_eq!(get_as_owner.title.as_deref(), Some("Docs v1"));

let get_as_reader = service
.docs_get(DocsGetRequest {
tenant_id: "t".to_string(),
project_id: "p".to_string(),
agent_id: "reader".to_string(),
read_profile: "private_plus_project".to_string(),
doc_id,
})
.await
.expect("Failed to get doc as reader (expected project grant).");

assert_eq!(get_as_reader.doc_id, doc_id);
}

pub(crate) async fn assert_doc_excerpt(service: &ElfService, doc_id: Uuid, content_hash: &str) {
let excerpts = service
.docs_excerpts_get(DocsExcerptsGetRequest {
tenant_id: "t".to_string(),
project_id: "p".to_string(),
agent_id: "reader".to_string(),
read_profile: "private_plus_project".to_string(),
doc_id,
level: "L1".to_string(),
chunk_id: None,
quote: Some(TextQuoteSelector {
exact: "Keyword: peregrine.".to_string(),
prefix: Some("evidence. ".to_string()),
suffix: Some("\nSecond".to_string()),
}),
position: None,
explain: None,
})
.await
.expect("Failed to get excerpt.");

assert!(excerpts.verification.verified);
assert!(excerpts.excerpt.contains("Keyword: peregrine."));
assert_eq!(excerpts.verification.content_hash, content_hash);
}

pub(crate) async fn assert_docs_search_l0(service: &ElfService, doc_id: Uuid) {
let results = service
.docs_search_l0(DocsSearchL0Request {
tenant_id: "t".to_string(),
project_id: "p".to_string(),
caller_agent_id: "reader".to_string(),
scope: None,
status: None,
doc_type: None,
sparse_mode: None,
domain: None,
repo: None,
agent_id: None,
thread_id: None,
updated_after: None,
updated_before: None,
ts_gte: None,
ts_lte: None,
read_profile: "private_plus_project".to_string(),
query: "peregrine".to_string(),
top_k: Some(5),
candidate_k: Some(20),
explain: None,
})
.await
.expect("Failed to search docs.");

assert!(!results.items.is_empty());
assert_eq!(results.items[0].doc_id, doc_id);
assert_eq!(results.items[0].doc_type, "knowledge");
assert!(results.items[0].snippet.contains("peregrine"));
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
use std::{string::ToString, sync::Arc};

use serde_json::Value;

use crate::acceptance::{self, SpyExtractor, StubEmbedding, StubRerank};
use elf_service::{DocsPutRequest, DocsPutResponse, ElfService, Providers};
use elf_testkit::TestDatabase;

pub(crate) const TEST_CONTENT: &str =
"ELF docs extension v1 stores evidence. Keyword: peregrine.\nSecond sentence for chunking.";

pub(crate) struct DocsContext {
pub(crate) test_db: TestDatabase,
pub(crate) service: ElfService,
}

pub(crate) async fn setup_docs_context() -> Option<DocsContext> {
let Some(test_db) = acceptance::test_db().await else {
eprintln!("Skipping docs_extension_v1; set ELF_PG_DSN to run this test.");

return None;
};
let Some(qdrant_url) = acceptance::test_qdrant_url() else {
eprintln!(
"Skipping docs_extension_v1; set ELF_QDRANT_URL (or ELF_QDRANT_GRPC_URL) to run this test."
);

return None;
};
let collection = test_db.collection_name("elf_acceptance");
let docs_collection = test_db.collection_name("elf_acceptance_docs");
let cfg = acceptance::test_config(
test_db.dsn().to_string(),
qdrant_url,
4_096,
collection,
docs_collection,
);
let providers = Providers::new(
Arc::new(StubEmbedding { vector_dim: 4_096 }),
Arc::new(StubRerank),
Arc::new(SpyExtractor {
calls: Arc::new(Default::default()),
payload: serde_json::json!({ "notes": [] }),
}),
);
let service =
acceptance::build_service(cfg, providers).await.expect("Failed to build service.");

acceptance::reset_db(&service.db.pool).await.expect("Failed to reset test database.");
acceptance::reset_qdrant_collection(
&service.qdrant.client,
&service.qdrant.collection,
service.qdrant.vector_dim,
)
.await
.expect("Failed to reset Qdrant memory collection.");
acceptance::reset_qdrant_collection(
&service.qdrant.client,
&service.cfg.storage.qdrant.docs_collection,
service.qdrant.vector_dim,
)
.await
.expect("Failed to reset Qdrant docs collection.");

Some(DocsContext { test_db, service })
}

pub(crate) async fn put_test_doc(service: &ElfService) -> DocsPutResponse {
put_test_doc_with(
service,
"owner",
"project_shared",
None,
"Docs v1",
serde_json::json!({
"schema": "doc_source_ref/v1",
"doc_type": "knowledge",
"ts": "2026-02-25T12:00:00Z",
"uri": "acceptance://knowledge/v1"
}),
TEST_CONTENT,
)
.await
}

pub(crate) async fn put_test_doc_with(
service: &ElfService,
agent_id: &str,
scope: &str,
doc_type: Option<&str>,
title: &str,
source_ref: Value,
content: &str,
) -> DocsPutResponse {
service
.docs_put(DocsPutRequest {
tenant_id: "t".to_string(),
project_id: "p".to_string(),
agent_id: agent_id.to_string(),
scope: scope.to_string(),
doc_type: doc_type.map(ToString::to_string),
title: Some(title.to_string()),
write_policy: None,
source_ref,
content: content.to_string(),
})
.await
.expect("Failed to put doc.")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
use std::{collections::HashSet, time::Duration};

use tokio::{sync::oneshot::Sender, task::JoinHandle};
use uuid::Uuid;

use crate::acceptance::docs_extension_v1::helpers::{
context::{self, DocsContext, TEST_CONTENT},
outbox, worker,
};
use elf_service::{DocsSearchL0Request, ElfService};
use elf_testkit::TestDatabase;

pub(crate) async fn create_docs_search_filter_fixture(
ctx: DocsContext,
) -> (TestDatabase, ElfService, Uuid, Uuid, Uuid, JoinHandle<()>, Sender<()>) {
let DocsContext { test_db, service } = ctx;
let shared_knowledge_doc = context::put_test_doc_with(
&service,
"owner",
"project_shared",
None,
"Docs filter sample",
serde_json::json!({
"schema": "doc_source_ref/v1",
"doc_type": "knowledge",
"ts": "2026-02-25T12:00:00Z",
}),
TEST_CONTENT,
)
.await;
let older_shared_knowledge_doc = context::put_test_doc_with(
&service,
"owner",
"project_shared",
None,
"Docs old filter sample",
serde_json::json!({
"schema": "doc_source_ref/v1",
"doc_type": "knowledge",
"ts": "2025-01-01T10:00:00Z",
}),
TEST_CONTENT,
)
.await;
let private_chat_doc = context::put_test_doc_with(
&service,
"assistant",
"agent_private",
Some("chat"),
"Docs chat sample",
serde_json::json!({
"schema": "doc_source_ref/v1",
"doc_type": "chat",
"ts": "2026-02-25T12:00:00Z",
"thread_id": "shared-chat-thread",
"role": "assistant"
}),
TEST_CONTENT,
)
.await;
let (handle, shutdown) = worker::spawn_doc_worker(&service).await;

assert!(
outbox::wait_for_doc_outbox_done(
&service.db.pool,
shared_knowledge_doc.doc_id,
Duration::from_secs(15)
)
.await,
"Expected shared docs outbox to reach DONE."
);
assert!(
outbox::wait_for_doc_outbox_done(
&service.db.pool,
older_shared_knowledge_doc.doc_id,
Duration::from_secs(15)
)
.await,
"Expected older shared docs outbox to reach DONE."
);
assert!(
outbox::wait_for_doc_outbox_done(
&service.db.pool,
private_chat_doc.doc_id,
Duration::from_secs(15)
)
.await,
"Expected private docs outbox to reach DONE."
);

(
test_db,
service,
shared_knowledge_doc.doc_id,
older_shared_knowledge_doc.doc_id,
private_chat_doc.doc_id,
handle,
shutdown,
)
}

pub(crate) async fn cleanup_docs_filter_fixture(
test_db: TestDatabase,
_handle: JoinHandle<()>,
shutdown: Sender<()>,
) {
let _ = shutdown.send(());

_handle.abort();

let _ = _handle.await;

test_db.cleanup().await.expect("Failed to cleanup test database.");
}

pub(crate) async fn search_doc_ids_with_filters(
service: &ElfService,
scope: Option<&str>,
doc_type: Option<&str>,
agent_id: Option<&str>,
updated_after: Option<&str>,
updated_before: Option<&str>,
caller_agent_id: &str,
) -> HashSet<Uuid> {
let results = service
.docs_search_l0(DocsSearchL0Request {
tenant_id: "t".to_string(),
project_id: "p".to_string(),
caller_agent_id: caller_agent_id.to_string(),
scope: scope.map(str::to_string),
status: None,
doc_type: doc_type.map(str::to_string),
sparse_mode: None,
domain: None,
repo: None,
agent_id: agent_id.map(str::to_string),
thread_id: None,
updated_after: updated_after.map(str::to_string),
updated_before: updated_before.map(str::to_string),
ts_gte: None,
ts_lte: None,
read_profile: "all_scopes".to_string(),
query: "peregrine".to_string(),
top_k: Some(20),
candidate_k: Some(50),
explain: None,
})
.await
.expect("Failed to search docs.");

results.items.into_iter().map(|item| item.doc_id).collect()
}
Loading