haiku.rag/tests/test_cascade_delete.py
Yiorgis Gozadinos d367b1eb5a
Resolve the databases a command works on, once
The CLI decides only what it knows — that --db and --database are the same
thing said twice, and whether a command reads more than one — and hands the
resolved scope down. Nothing rewrites the configuration, so a named database
keeps the name results and citations carry, and a remote one opens the URI
it was configured with rather than the local path standing in for it.

HaikuRAGApp, ChatApp and InspectorApp take that scope and nothing else.
Selection reaches the client through a private constructor, so the public
signature still takes a path or names.
2026-08-25 15:48:47 +03:00

161 lines
5.7 KiB
Python

from haiku.rag.app import HaikuRAGApp
from haiku.rag.client import HaikuRAG
from haiku.rag.client.documents import parent_uri_filter
from haiku.rag.config.models import AppConfig
from haiku.rag.store.models.document import Document
from tests.conftest import for_path
def test_parent_uri_filter_simple():
f = parent_uri_filter("file:///path/to/parent.pdf")
assert f == 'metadata LIKE \'%"parent_uri": "file:///path/to/parent.pdf"%\''
def test_parent_uri_filter_escapes_single_quote():
f = parent_uri_filter("file:///x's.pdf")
assert "''" in f
def test_parent_uri_filter_escapes_backslash():
f = parent_uri_filter("file:///x\\y.pdf")
assert "\\\\" in f
async def test_delete_cascades_to_children(temp_db_path):
async with HaikuRAG(temp_db_path, create=True) as client:
parent_uri = "file:///path/to/parent.pdf"
parent = await client.document_repository.create(
Document(content="parent body", uri=parent_uri, metadata={})
)
child_a = await client.document_repository.create(
Document(
content="child A body",
uri=f"{parent_uri}#attachment=a.pdf",
metadata={"parent_uri": parent_uri},
)
)
child_b = await client.document_repository.create(
Document(
content="child B body",
uri=f"{parent_uri}#attachment=b.pdf",
metadata={"parent_uri": parent_uri},
)
)
deleted = await client.delete_document(parent.id)
assert deleted is True
assert await client.get_document_by_id(parent.id) is None
assert await client.get_document_by_id(child_a.id) is None
assert await client.get_document_by_id(child_b.id) is None
async def test_delete_leaves_unrelated_documents(temp_db_path):
async with HaikuRAG(temp_db_path, create=True) as client:
parent_uri = "file:///path/to/parent.pdf"
parent = await client.document_repository.create(
Document(content="parent", uri=parent_uri, metadata={})
)
child = await client.document_repository.create(
Document(
content="child",
uri=f"{parent_uri}#attachment=a.pdf",
metadata={"parent_uri": parent_uri},
)
)
unrelated = await client.document_repository.create(
Document(
content="unrelated",
uri="file:///path/to/other.pdf",
metadata={},
)
)
await client.delete_document(parent.id)
assert await client.get_document_by_id(child.id) is None
survivor = await client.get_document_by_id(unrelated.id)
assert survivor is not None
assert survivor.id == unrelated.id
async def test_delete_cascades_recursively(temp_db_path):
async with HaikuRAG(temp_db_path, create=True) as client:
gp_uri = "file:///path/to/grandparent.pdf"
parent_uri = f"{gp_uri}#attachment=parent.pdf"
grandparent = await client.document_repository.create(
Document(content="gp", uri=gp_uri, metadata={})
)
parent = await client.document_repository.create(
Document(
content="p",
uri=parent_uri,
metadata={"parent_uri": gp_uri},
)
)
child = await client.document_repository.create(
Document(
content="c",
uri=f"{parent_uri}#attachment=leaf.pdf",
metadata={"parent_uri": parent_uri},
)
)
await client.delete_document(grandparent.id)
assert await client.get_document_by_id(grandparent.id) is None
assert await client.get_document_by_id(parent.id) is None
assert await client.get_document_by_id(child.id) is None
async def test_delete_nonexistent_returns_false(temp_db_path):
async with HaikuRAG(temp_db_path, create=True) as client:
result = await client.delete_document("does-not-exist")
assert result is False
async def test_delete_handles_self_referential_parent(temp_db_path):
"""A document whose metadata.parent_uri points at its own uri must not
cascade into infinite recursion."""
async with HaikuRAG(temp_db_path, create=True) as client:
uri = "file:///path/to/self.pdf"
doc = await client.document_repository.create(
Document(content="self-loop", uri=uri, metadata={"parent_uri": uri})
)
deleted = await client.delete_document(doc.id)
assert deleted is True
assert await client.get_document_by_id(doc.id) is None
async def test_delete_succeeds_with_embedding_dim_mismatch(temp_db_path):
"""Deletion touches no embeddings, so a vector_dim mismatch must not block it."""
async with HaikuRAG(temp_db_path, create=True) as client:
doc = await client.document_repository.create(
Document(content="body", uri="file:///doc.pdf", metadata={})
)
mismatched = AppConfig()
mismatched.embeddings.model.vector_dim = 9999
app = HaikuRAGApp(scope=for_path(temp_db_path, mismatched), config=mismatched)
await app.delete_document(doc.id)
async with HaikuRAG(
temp_db_path, config=mismatched, skip_validation=True
) as client:
assert await client.get_document_by_id(doc.id) is None
def test_processing_config_extract_pdf_attachments_default_true():
from haiku.rag.config.models import ProcessingConfig
assert ProcessingConfig().extract_pdf_attachments is True
def test_processing_config_extract_pdf_attachments_overridable():
from haiku.rag.config.models import ProcessingConfig
cfg = ProcessingConfig(extract_pdf_attachments=False)
assert cfg.extract_pdf_attachments is False