Add tests for settings validation and app operations
This commit is contained in:
parent
bb60ad127a
commit
e0da58bb6c
3 changed files with 205 additions and 1 deletions
|
|
@ -9,6 +9,10 @@
|
|||
- Default `None` preserves backwards compatibility (bare state emission)
|
||||
- **Page Image Generation Control**: New `generate_page_images` option in `ConversionOptions` to control PDF page image extraction
|
||||
|
||||
### Changed
|
||||
|
||||
- **CLI Error Handling**: Commands (`rebuild`, `vacuum`, `create-index`, `ask`, `research`) now propagate errors with proper exit codes instead of swallowing exceptions
|
||||
|
||||
### Fixed
|
||||
|
||||
- **Embed-only rebuild with changed vector dimensions**: Fixed `haiku-rag rebuild --embed-only` failing when the configured embedding model has different dimensions than the database
|
||||
|
|
|
|||
|
|
@ -506,3 +506,95 @@ async def test_history_nonexistent_db(tmp_path, monkeypatch):
|
|||
|
||||
calls = [str(c) for c in mock_print.call_args_list]
|
||||
assert any("does not exist" in c for c in calls)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_init_creates_database(tmp_path, monkeypatch):
|
||||
"""Test init creates a new database."""
|
||||
db_path = tmp_path / "new.lancedb"
|
||||
app = HaikuRAGApp(db_path=db_path)
|
||||
mock_print = MagicMock()
|
||||
monkeypatch.setattr(app.console, "print", mock_print)
|
||||
|
||||
assert not db_path.exists()
|
||||
await app.init()
|
||||
|
||||
assert db_path.exists()
|
||||
calls = [str(c) for c in mock_print.call_args_list]
|
||||
assert any("initialized" in c for c in calls)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_init_existing_database(tmp_path, monkeypatch):
|
||||
"""Test init with existing database shows warning."""
|
||||
from haiku.rag.store.engine import Store
|
||||
|
||||
db_path = tmp_path / "existing.lancedb"
|
||||
store = Store(db_path, create=True)
|
||||
store.close()
|
||||
|
||||
app = HaikuRAGApp(db_path=db_path)
|
||||
mock_print = MagicMock()
|
||||
monkeypatch.setattr(app.console, "print", mock_print)
|
||||
|
||||
await app.init()
|
||||
|
||||
calls = [str(c) for c in mock_print.call_args_list]
|
||||
assert any("already exists" in c for c in calls)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_vacuum(tmp_path, monkeypatch):
|
||||
"""Test vacuum operation."""
|
||||
from haiku.rag.store.engine import Store
|
||||
|
||||
db_path = tmp_path / "test.lancedb"
|
||||
store = Store(db_path, create=True)
|
||||
store.close()
|
||||
|
||||
app = HaikuRAGApp(db_path=db_path)
|
||||
mock_print = MagicMock()
|
||||
monkeypatch.setattr(app.console, "print", mock_print)
|
||||
|
||||
await app.vacuum()
|
||||
|
||||
calls = [str(c) for c in mock_print.call_args_list]
|
||||
assert any("Vacuum completed" in c for c in calls)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_create_index_insufficient_chunks(tmp_path, monkeypatch):
|
||||
"""Test create_index with insufficient chunks shows warning."""
|
||||
from haiku.rag.store.engine import Store
|
||||
|
||||
db_path = tmp_path / "test.lancedb"
|
||||
store = Store(db_path, create=True)
|
||||
store.close()
|
||||
|
||||
app = HaikuRAGApp(db_path=db_path)
|
||||
mock_print = MagicMock()
|
||||
monkeypatch.setattr(app.console, "print", mock_print)
|
||||
|
||||
await app.create_index()
|
||||
|
||||
calls = [str(c) for c in mock_print.call_args_list]
|
||||
assert any("Need at least 256 chunks" in c for c in calls)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_rebuild_empty_database(tmp_path, monkeypatch):
|
||||
"""Test rebuild with empty database shows warning."""
|
||||
from haiku.rag.store.engine import Store
|
||||
|
||||
db_path = tmp_path / "test.lancedb"
|
||||
store = Store(db_path, create=True)
|
||||
store.close()
|
||||
|
||||
app = HaikuRAGApp(db_path=db_path)
|
||||
mock_print = MagicMock()
|
||||
monkeypatch.setattr(app.console, "print", mock_print)
|
||||
|
||||
await app.rebuild()
|
||||
|
||||
calls = [str(c) for c in mock_print.call_args_list]
|
||||
assert any("No documents found" in c for c in calls)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,7 @@
|
|||
from haiku.rag.config import Config
|
||||
import pytest
|
||||
|
||||
from haiku.rag.config import AppConfig, Config
|
||||
from haiku.rag.store.repositories.settings import ConfigMismatchError
|
||||
|
||||
|
||||
def test_settings_table_populated_on_store_init(temp_db_path):
|
||||
|
|
@ -48,3 +51,108 @@ def test_monitor_filter_patterns_config():
|
|||
assert isinstance(Config.monitor.ignore_patterns, list)
|
||||
assert isinstance(Config.monitor.include_patterns, list)
|
||||
assert isinstance(Config.monitor.directories, list)
|
||||
|
||||
|
||||
class TestValidateConfigCompatibility:
|
||||
"""Tests for validate_config_compatibility method."""
|
||||
|
||||
def test_empty_settings_saves_config(self, temp_db_path):
|
||||
"""When settings row is missing, validation saves current config."""
|
||||
from haiku.rag.store.engine import Store
|
||||
from haiku.rag.store.repositories.settings import SettingsRepository
|
||||
|
||||
store = Store(temp_db_path, create=True, skip_validation=True)
|
||||
settings_repo = SettingsRepository(store)
|
||||
|
||||
# Clear settings to simulate empty state
|
||||
store.settings_table.delete("id = 'settings'")
|
||||
assert settings_repo.get_current_settings() == {}
|
||||
|
||||
# Validation should save settings
|
||||
settings_repo.validate_config_compatibility()
|
||||
|
||||
# Now settings should exist
|
||||
saved = settings_repo.get_current_settings()
|
||||
assert saved.get("embeddings", {}).get("model", {}).get("provider") is not None
|
||||
store.close()
|
||||
|
||||
def test_compatible_config_no_error(self, temp_db_path):
|
||||
"""Compatible config does not raise error."""
|
||||
from haiku.rag.store.engine import Store
|
||||
from haiku.rag.store.repositories.settings import SettingsRepository
|
||||
|
||||
store = Store(temp_db_path, create=True)
|
||||
settings_repo = SettingsRepository(store)
|
||||
|
||||
# Should not raise - same config
|
||||
settings_repo.validate_config_compatibility()
|
||||
store.close()
|
||||
|
||||
def test_provider_mismatch_raises_error(self, temp_db_path):
|
||||
"""Different embedding provider raises ConfigMismatchError."""
|
||||
from haiku.rag.store.engine import Store
|
||||
from haiku.rag.store.repositories.settings import SettingsRepository
|
||||
|
||||
# Create store with default config (ollama)
|
||||
store = Store(temp_db_path, create=True)
|
||||
store.close()
|
||||
|
||||
# Create new config with different provider
|
||||
new_config = AppConfig()
|
||||
new_config.embeddings.model.provider = "openai"
|
||||
|
||||
store2 = Store(temp_db_path, config=new_config, skip_validation=True)
|
||||
settings_repo = SettingsRepository(store2)
|
||||
|
||||
with pytest.raises(ConfigMismatchError) as exc_info:
|
||||
settings_repo.validate_config_compatibility()
|
||||
|
||||
assert "embedding provider" in str(exc_info.value)
|
||||
assert "ollama" in str(exc_info.value)
|
||||
assert "openai" in str(exc_info.value)
|
||||
store2.close()
|
||||
|
||||
def test_model_mismatch_raises_error(self, temp_db_path):
|
||||
"""Different embedding model raises ConfigMismatchError."""
|
||||
from haiku.rag.store.engine import Store
|
||||
from haiku.rag.store.repositories.settings import SettingsRepository
|
||||
|
||||
# Create store with default config
|
||||
store = Store(temp_db_path, create=True)
|
||||
store.close()
|
||||
|
||||
# Create new config with different model
|
||||
new_config = AppConfig()
|
||||
new_config.embeddings.model.name = "different-model"
|
||||
|
||||
store2 = Store(temp_db_path, config=new_config, skip_validation=True)
|
||||
settings_repo = SettingsRepository(store2)
|
||||
|
||||
with pytest.raises(ConfigMismatchError) as exc_info:
|
||||
settings_repo.validate_config_compatibility()
|
||||
|
||||
assert "embedding model" in str(exc_info.value)
|
||||
store2.close()
|
||||
|
||||
def test_vector_dim_mismatch_raises_error(self, temp_db_path):
|
||||
"""Different vector dimension raises ConfigMismatchError."""
|
||||
from haiku.rag.store.engine import Store
|
||||
from haiku.rag.store.repositories.settings import SettingsRepository
|
||||
|
||||
# Create store with default config
|
||||
store = Store(temp_db_path, create=True)
|
||||
store.close()
|
||||
|
||||
# Create new config with different vector dimension
|
||||
new_config = AppConfig()
|
||||
new_config.embeddings.model.vector_dim = 9999
|
||||
|
||||
store2 = Store(temp_db_path, config=new_config, skip_validation=True)
|
||||
settings_repo = SettingsRepository(store2)
|
||||
|
||||
with pytest.raises(ConfigMismatchError) as exc_info:
|
||||
settings_repo.validate_config_compatibility()
|
||||
|
||||
assert "vector dimension" in str(exc_info.value)
|
||||
assert "9999" in str(exc_info.value)
|
||||
store2.close()
|
||||
|
|
|
|||
Loading…
Reference in a new issue