Compare commits

...
Author SHA1 Message Date
Nicolò Boschi ba1b7578e7 ci: ensure docs get created with no extracted facts 2026-02-16 14:19:22 +01:00
@@ -135,3 +135,179 @@ async def test_memory_without_document(memory, request_context):
finally:
await memory.delete_bank(bank_id, request_context=request_context)
@pytest.mark.asyncio
async def test_document_persisted_with_zero_facts(memory, request_context):
"""
Test that documents are persisted even when zero facts are extracted.
This is a regression test for issue #324 where documents with no extractable
facts were reported as disappearing from the system.
"""
bank_id = f"test_zero_facts_{datetime.now(timezone.utc).timestamp()}"
try:
document_id = "doc-zero-facts"
# Retain content that produces zero facts (gibberish/random characters)
units = await memory.retain_async(
bank_id=bank_id,
content="xyzabc123 !!!### @@@ $$$", # Random characters unlikely to produce facts
context="Test zero facts",
document_id=document_id,
request_context=request_context,
)
# Should return empty unit list (no facts extracted)
assert len(units) == 0, "Should extract zero facts from gibberish content"
# But document should still be persisted and retrievable
doc = await memory.get_document(document_id, bank_id, request_context=request_context)
assert doc is not None, "Document should be persisted even with zero facts"
assert doc["id"] == document_id
assert doc["bank_id"] == bank_id
assert doc["memory_unit_count"] == 0, "Should have zero memory units"
assert len(doc["original_text"]) > 0, "Should have non-zero text length"
assert "xyzabc123" in doc["original_text"], "Should contain original content"
# Document should also appear in list
docs_list = await memory.list_documents(
bank_id=bank_id,
search_query=None,
limit=100,
offset=0,
request_context=request_context,
)
assert docs_list["total"] == 1, "Document should appear in list"
assert any(d["id"] == document_id for d in docs_list["items"]), "Document should be in items"
listed_doc = next(d for d in docs_list["items"] if d["id"] == document_id)
assert listed_doc["memory_unit_count"] == 0, "Listed document should show zero memory units"
finally:
await memory.delete_bank(bank_id, request_context=request_context)
@pytest.mark.asyncio
async def test_document_persisted_with_zero_facts_batch(memory, request_context):
"""
Test that documents are persisted with zero facts in batch retain operations.
This tests the async batch code path to ensure it also handles zero facts correctly.
"""
bank_id = f"test_zero_facts_batch_{datetime.now(timezone.utc).timestamp()}"
try:
# Mix of content: some produces facts, some produces zero facts
contents = [
{
"content": "Alice works at Google",
"document_id": "doc-with-facts",
},
{
"content": "!@# $$$ %%% ^^^ &&& ***", # Gibberish - zero facts expected
"document_id": "doc-zero-facts",
},
]
unit_ids = await memory.retain_batch_async(
bank_id=bank_id,
contents=contents,
request_context=request_context,
)
# First content should produce facts, second should not
assert len(unit_ids[0]) > 0, "First content should produce facts"
assert len(unit_ids[1]) == 0, "Second content should produce zero facts"
# Both documents should be persisted
doc_with_facts = await memory.get_document("doc-with-facts", bank_id, request_context=request_context)
assert doc_with_facts is not None
assert doc_with_facts["memory_unit_count"] > 0
doc_zero_facts = await memory.get_document("doc-zero-facts", bank_id, request_context=request_context)
assert doc_zero_facts is not None, "Document with zero facts should be persisted"
assert doc_zero_facts["memory_unit_count"] == 0, "Should have zero memory units"
assert "!@#" in doc_zero_facts["original_text"]
# Both should appear in list
docs_list = await memory.list_documents(
bank_id=bank_id,
search_query=None,
limit=100,
offset=0,
request_context=request_context,
)
assert docs_list["total"] == 2, "Both documents should appear in list"
finally:
await memory.delete_bank(bank_id, request_context=request_context)
@pytest.mark.asyncio
async def test_document_persisted_with_zero_facts_async_submit(memory, request_context):
"""
Test that documents are persisted with zero facts in fire-and-forget async retain.
This tests the submit_async_retain (background task) code path to ensure it also
handles zero facts correctly.
"""
import asyncio
bank_id = f"test_zero_facts_async_{datetime.now(timezone.utc).timestamp()}"
try:
# Submit async retain with gibberish content
result = await memory.submit_async_retain(
bank_id=bank_id,
contents=[
{
"content": "!@# $$$ %%% ^^^ &&& ***", # Gibberish - zero facts expected
"document_id": "doc-async-zero-facts",
}
],
request_context=request_context,
)
operation_id = result["operation_id"]
assert operation_id is not None, "Should return operation_id"
# Wait for background task to complete
max_wait = 60 # 60 seconds max
wait_interval = 0.5
elapsed = 0
while elapsed < max_wait:
await asyncio.sleep(wait_interval)
elapsed += wait_interval
# Check if document exists
doc = await memory.get_document(
"doc-async-zero-facts", bank_id, request_context=request_context
)
if doc is not None:
break
# Document should be persisted even with zero facts
assert doc is not None, "Document should be persisted after async task completes"
assert doc["id"] == "doc-async-zero-facts"
assert doc["memory_unit_count"] == 0, "Should have zero memory units"
assert "!@#" in doc["original_text"]
# Document should appear in list
docs_list = await memory.list_documents(
bank_id=bank_id,
search_query=None,
limit=100,
offset=0,
request_context=request_context,
)
assert docs_list["total"] == 1, "Document should appear in list"
assert any(d["id"] == "doc-async-zero-facts" for d in docs_list["items"])
listed_doc = next(d for d in docs_list["items"] if d["id"] == "doc-async-zero-facts")
assert listed_doc["memory_unit_count"] == 0, "Listed document should show zero memory units"
finally:
await memory.delete_bank(bank_id, request_context=request_context)