Files
DocsMCP/tests/test_db.py
T
2026-06-05 23:02:55 +01:00

317 lines
11 KiB
Python

"""
Tests for backend/app/db.py
These tests verify SQLite database operations including:
- Table creation (init_db)
- Library CRUD operations
- Document chunk storage and retrieval
- Full-text search functionality
All tests use a temporary test database file.
"""
import pytest
from datetime import datetime
class TestInitDatabase:
"""Tests for init_db() - table creation."""
def test_init_db_creates_tables(self, test_database):
"""Database should have libraries and documents tables after init."""
import sqlite3
from backend.app.db import get_connection, get_db_path
conn = get_connection()
cursor = conn.execute("SELECT name FROM sqlite_master WHERE type='table' ORDER BY name")
tables = [row[0] for row in cursor.fetchall()]
# Should have libraries, documents, and FTS virtual table
assert "libraries" in tables or any("libraries" in t.lower() for t in tables)
conn.close()
def test_init_db_returns_success(self, test_database):
"""init_db should return success indicator."""
from backend.app.db import init_db
result = init_db()
assert result["success"] is True
class TestLibraryOperations:
"""Tests for library CRUD operations."""
def test_upsert_library_new(self, test_database):
"""Upsert should create new library."""
from backend.app.db import upsert_library
result = upsert_library(
library_id="/local/testlib",
name="Test Library",
description="A test library for unit tests"
)
assert result["success"] is True
assert result["id"] == "/local/testlib"
def test_upsert_library_update(self, test_database):
"""Upsert should update existing library."""
from backend.app.db import upsert_library
# Insert first library
upsert_library(
library_id="/local/upsertlib",
name="Original Name",
description="Original description"
)
# Update it
result = upsert_library(
library_id="/local/upsertlib",
name="Updated Name",
description="Updated description"
)
assert result["success"] is True
def test_upsert_library_id_normalization(self, test_database):
"""Library ID normalization - /local/ prefix should be preserved."""
from backend.app.db import upsert_library
# Test various ID formats
test_ids = [
"/local/foundryvtt",
"foundryvtt",
"/local/mydocs",
]
for lib_id in test_ids:
result = upsert_library(library_id=lib_id, name="Test", description="Desc")
assert result["success"] is True
# Verify we can retrieve it back
from backend.app.db import get_chunks_for_library
# Just ensure no errors occur
def test_list_libraries(self, test_database):
"""list_libraries should return list of libraries."""
from backend.app.db import upsert_library, list_libraries
# Create some libraries
for i in range(3):
upsert_library(
library_id=f"/local/lib{i}",
name=f"Library {i}",
description=f"Description {i}"
)
libs = list_libraries()
assert isinstance(libs, list)
assert len(libs) >= 3
def test_search_libraries(self, test_database):
"""search_libraries should find libraries by name/description."""
from backend.app.db import upsert_library, search_libraries
# Create libraries with searchable names
upsert_library(library_id="/local/foo1", name="Foo Library", description="Bar baz")
upsert_library(library_id="/local/foo2", name="Other Library", description="Different content")
results = search_libraries("foo")
assert isinstance(results, list)
class TestDocumentChunkOperations:
"""Tests for document chunk storage and retrieval."""
def test_insert_document_chunk_new(self, test_database):
"""insert_document_chunk should create new chunk record."""
from backend.app.db import insert_document_chunk
result = insert_document_chunk(
doc_id="doc-1",
library_id="/local/testlib",
path="docs/example.md",
title="Example Document",
content="# Example\n\nThis is the content.",
chunk_index=0,
token_estimate=100
)
assert result["success"] is True
def test_insert_document_chunk_update(self, test_database):
"""insert_document_chunk should update existing record."""
from backend.app.db import insert_document_chunk
# Insert first
insert_document_chunk(
doc_id="doc-update-test",
library_id="/local/uplib",
path="old-path.md",
title="Old Title",
content="# Old\nContent here.",
chunk_index=0,
token_estimate=50
)
# Update it
result = insert_document_chunk(
doc_id="doc-update-test",
library_id="/local/uplib",
path="new-path.md",
title="New Title",
content="# New\nUpdated content.",
chunk_index=1,
token_estimate=75
)
assert result["success"] is True
def test_get_document_by_id(self, test_database):
"""get_document_by_id should retrieve document by ID."""
from backend.app.db import insert_document_chunk, get_document_by_id
# Insert document
doc_id = "unique-doc-id-12345"
insert_document_chunk(
doc_id=doc_id,
library_id="/local/testlib",
path="docs/test.md",
title="Test Document",
content="# Test\n\nTest content here.",
chunk_index=None,
token_estimate=200
)
# Retrieve it
doc = get_document_by_id(doc_id)
assert doc is not None
assert doc["id"] == doc_id
def test_get_chunks_for_library(self, test_database):
"""get_chunks_for_library should return all chunks for a library."""
from backend.app.db import upsert_library, insert_document_chunk, get_chunks_for_library
# Create library
upsert_library(library_id="/local/chunktest", name="Chunk Test", description="Test")
# Add some chunks
for i in range(3):
insert_document_chunk(
doc_id=f"chunk-{i}",
library_id="/local/chunktest",
path=f"path{i}.md",
title=f"Section {i}",
content=f"Content section {i}.",
chunk_index=i,
token_estimate=50
)
chunks = get_chunks_for_library("/local/chunktest")
assert isinstance(chunks, list)
assert len(chunks) >= 3
def test_clear_library_documents(self, test_database):
"""clear_library_documents should delete all docs for a library."""
from backend.app.db import upsert_library, insert_document_chunk, clear_library_documents, get_chunks_for_library
# Create and populate library
upsert_library(library_id="/local/cleartest", name="Clear Test", description="Test")
for i in range(5):
insert_document_chunk(
doc_id=f"clear-{i}",
library_id="/local/cleartest",
path=f"path{i}.md",
content=f"Content {i}.",
token_estimate=20
)
# Clear it
result = clear_library_documents("/local/cleartest")
assert result["success"] is True
# Verify cleared
remaining = get_chunks_for_library("/local/cleartest")
assert len(remaining) == 0
class TestDatabaseEdgeCases:
"""Tests for edge cases and error handling."""
def test_empty_library_id(self, test_database):
"""Operations with empty ID should handle gracefully."""
from backend.app.db import upsert_library
result = upsert_library(library_id="", name="Test", description="Desc")
# Should not crash, though may not be a valid operation
def test_special_characters_in_content(self, test_database):
"""Content with special characters should be stored."""
from backend.app.db import insert_document_chunk
content = "Hello \"world\" <tag /> & amp; 'apostrophe'"
result = insert_document_chunk(
doc_id="special-test",
library_id="/local/speciallib",
path="special.md",
content=content,
token_estimate=100
)
assert result["success"] is True
def test_very_long_content(self, test_database):
"""Long content should be stored."""
from backend.app.db import insert_document_chunk
long_content = "a" * 5000
result = insert_document_chunk(
doc_id="long-test",
library_id="/local/longlib",
path="long.md",
content=long_content,
token_estimate=1000
)
assert result["success"] is True
def test_none_description(self, test_database):
"""Library with None description should work."""
from backend.app.db import upsert_library
result = upsert_library(
library_id="/local/nonedesc",
name="No Description Lib",
description=None
)
assert result["success"] is True
class TestDatabaseInitialization:
"""Tests for database initialization state."""
def test_database_is_empty_after_init(self, test_database):
"""Database should be empty right after init."""
from backend.app.db import list_libraries
libs = list_libraries()
assert isinstance(libs, list)
# =============================================================================
# FIXTURES
# =============================================================================
@pytest.fixture
def sample_doc():
"""Sample document chunk for testing."""
return {
"doc_id": "sample-doc-1",
"library_id": "/local/samplelib",
"path": "docs/guide.md",
"title": "Getting Started Guide",
"content": "# Getting Started\n\nWelcome to the guide. This is a sample document for testing.\n\n## Installation\n\nInstall with pip.",
"chunk_index": 0,
"token_estimate": 500
}