haiku.rag/tests/test_database_scope.py
Yiorgis Gozadinos 34180a0fd1
One reference and one placement for a database
DatabaseRef is a name and a location. The configuration places databases
through lancedb.databases alone; with none configured the default is the
entry haiku.rag under storage.data_dir, selectable like any other.
lancedb.uri is removed, and a config carrying it fails to load with the
replacement spelled out. A path passed from Python is valid where the
configuration places nothing and raises AmbiguousDatabaseError beside
lancedb.databases; haiku-rag --db and haiku-ingester --db construct the
scope directly, so a human's override keeps working. Every database
answers to a name, and a database given as a path keeps its own errors.
2026-09-03 15:12:08 +03:00

220 lines
8.8 KiB
Python

from pathlib import Path
import pytest
from haiku.rag.client.scope import DatabaseRef, DatabaseScope, database_name
from haiku.rag.config.models import AppConfig, LanceDBConfig, StorageConfig
from haiku.rag.store.exceptions import (
AmbiguousDatabaseError,
UnknownDatabaseError,
)
def _config(**kwargs) -> AppConfig:
return AppConfig(lancedb=LanceDBConfig(**kwargs))
class TestResolution:
"""One selector at most, and the same answer wherever it is asked."""
def test_a_name_and_a_path_together_are_refused(self):
config = _config(databases={"alpha": "/data/alpha.lancedb"})
with pytest.raises(AmbiguousDatabaseError, match="pass one of them"):
DatabaseScope.resolve(
config, database_name="alpha", database_path=Path("/data/other.lancedb")
)
def test_a_path_places_the_database_where_the_configuration_places_none(self):
scope = DatabaseScope.resolve(_config(), database_path="/data/other.lancedb")
assert scope.databases == (DatabaseRef.at("/data/other.lancedb"),)
assert scope.names == ("other",)
assert not scope.covers_multiple
def test_a_path_beside_a_configured_placement_is_refused(self):
"""The configuration places databases; a path beside it is a second
placement, and the refusal names both."""
config = _config(databases={"alpha": "/data/alpha.lancedb", "beta": "b://b"})
with pytest.raises(AmbiguousDatabaseError) as raised:
DatabaseScope.resolve(config, database_path=Path("/data/other.lancedb"))
message = str(raised.value)
assert "/data/other.lancedb" in message
assert "alpha" in message and "beta" in message
assert "lancedb.databases" in message
def test_a_named_database_keeps_its_name(self):
config = _config(databases={"alpha": "/data/alpha.lancedb", "beta": "b://b"})
scope = DatabaseScope.resolve(config, database_name="beta")
assert scope.databases == (DatabaseRef("beta", "b://b"),)
assert scope.names == ("beta",)
def test_an_unknown_name_is_refused(self):
config = _config(databases={"alpha": "/data/alpha.lancedb"})
with pytest.raises(UnknownDatabaseError, match="unknown database 'nope'"):
DatabaseScope.resolve(config, database_name="nope")
def test_no_selector_covers_the_configured_set_in_order(self):
config = _config(
databases={"beta": "/data/b.lancedb", "alpha": "/data/a.lancedb"}
)
scope = DatabaseScope.resolve(config)
assert scope.names == ("beta", "alpha")
assert scope.covers_multiple
def test_a_configured_set_of_one_is_still_a_named_database(self):
"""Its name is what results and citations carry, so it survives."""
config = _config(databases={"alpha": "/data/alpha.lancedb"})
scope = DatabaseScope.resolve(config)
assert scope.databases == (DatabaseRef("alpha", Path("/data/alpha.lancedb")),)
assert not scope.covers_multiple
def test_nothing_configured_is_the_default_entry(self, tmp_path):
"""No `databases` reads as one entry, `haiku.rag`, under the data
directory: an ordinary configured database in every respect."""
config = AppConfig(storage=StorageConfig(data_dir=tmp_path))
scope = DatabaseScope.resolve(config)
assert scope.databases == (
DatabaseRef("haiku.rag", tmp_path / "haiku.rag.lancedb"),
)
assert scope.names == ("haiku.rag",)
def test_the_default_entry_is_selectable_by_name(self, tmp_path):
config = AppConfig(storage=StorageConfig(data_dir=tmp_path))
by_name = DatabaseScope.resolve(config, database_name="haiku.rag")
selected = DatabaseScope.resolve(config).select(["haiku.rag"])
assert by_name == selected == DatabaseScope.resolve(config)
def test_the_default_entry_does_not_hide_a_configured_set(self, tmp_path):
"""The default stands in only where nothing is configured."""
config = AppConfig(
storage=StorageConfig(data_dir=tmp_path),
lancedb=LanceDBConfig(databases={"alpha": "/data/alpha.lancedb"}),
)
with pytest.raises(UnknownDatabaseError, match="haiku.rag"):
DatabaseScope.resolve(config, database_name="haiku.rag")
def test_the_environment_is_not_consulted(self, monkeypatch, tmp_path):
"""HAIKU_RAG_DB is honoured by the capability entry point alone;
resolution never reads the environment."""
monkeypatch.setenv("HAIKU_RAG_DB", "/data/from-the-environment.lancedb")
config = _config(databases={"alpha": "/data/alpha.lancedb"})
scope = DatabaseScope.resolve(config)
assert scope.names == ("alpha",)
def test_a_path_is_never_reinterpreted_as_a_uri(self):
"""A caller naming a path means that path, whatever scheme it carries."""
scope = DatabaseScope.resolve(
_config(), database_path="s3://bucket/looks-like-a-uri.lancedb"
)
[ref] = scope.databases
assert ref.location == Path("s3://bucket/looks-like-a-uri.lancedb")
def test_a_configured_location_with_a_scheme_is_a_uri(self):
"""A configured value is a URI or a path depending on its scheme, which is
what makes it different from a path the caller gave."""
config = _config(databases={"alpha": "s3://bucket/alpha.lancedb"})
[ref] = DatabaseScope.resolve(config).databases
assert ref.location == "s3://bucket/alpha.lancedb"
assert ref.db_path is None
def test_a_configured_location_without_a_scheme_is_a_path(self):
config = _config(databases={"alpha": "/data/alpha.lancedb"})
[ref] = DatabaseScope.resolve(config).databases
assert ref.location == Path("/data/alpha.lancedb")
assert ref.db_path == Path("/data/alpha.lancedb")
def test_a_scope_covers_at_least_one_database(self):
"""Every resolution reaches a database, and the sessions built from a
scope have no meaning without one."""
with pytest.raises(ValueError, match="at least one database"):
DatabaseScope(())
class TestTheReference:
"""Constructed directly, a reference still holds what it advertises."""
def test_a_schemeless_string_location_is_a_path(self):
ref = DatabaseRef("x", "local.lancedb")
assert ref.location == Path("local.lancedb")
assert ref.db_path == Path("local.lancedb")
def test_a_location_with_a_scheme_stays_a_uri(self):
ref = DatabaseRef("x", "s3://bucket/x.lancedb")
assert ref.location == "s3://bucket/x.lancedb"
assert ref.db_path is None
def test_a_blank_name_is_refused(self):
with pytest.raises(ValueError, match="no name"):
DatabaseRef("", "/data/x.lancedb")
with pytest.raises(ValueError, match="no name"):
DatabaseRef(" ", "s3://bucket/x.lancedb")
def test_a_blank_location_is_refused(self):
"""A blank string would resolve to the working directory."""
with pytest.raises(ValueError, match="no location"):
DatabaseRef("x", "")
with pytest.raises(ValueError, match="no location"):
DatabaseRef("x", " ")
def test_a_given_database_is_a_local_path(self):
"""Only a path can be given: a given database's errors name its
location, and a URI must never travel that way."""
assert DatabaseRef("x", "local.lancedb", given=True).location == Path(
"local.lancedb"
)
with pytest.raises(ValueError, match="is a URI"):
DatabaseRef("x", "s3://bucket/x.lancedb", given=True)
class TestNamingAPath:
"""A path the caller gave is named by its stem, the one rule for the
default database and for `--db`."""
def test_the_stem_names_the_database(self):
assert database_name(Path("/data/foo.lancedb")) == "foo"
assert database_name(Path("relative.lancedb")) == "relative"
assert database_name(Path("/data/haiku.rag.lancedb")) == "haiku.rag"
def test_a_path_with_no_stem_is_refused(self):
with pytest.raises(ValueError, match="no name"):
database_name(Path("/"))
def test_at_names_the_path_it_is_given(self):
"""A given path is marked as such: its errors may name it, since the
caller already knows where it is."""
assert DatabaseRef.at("/data/other.lancedb") == DatabaseRef(
"other", Path("/data/other.lancedb"), given=True
)
assert not DatabaseRef.configured("other", "/data/other.lancedb").given
def test_a_scope_at_a_path_ignores_the_configuration(self):
"""The CLI's `--db` is a human's explicit override: it constructs the
scope directly and consults no configuration."""
scope = DatabaseScope.at(Path("/data/other.lancedb"))
assert scope.databases == (DatabaseRef.at("/data/other.lancedb"),)
assert scope.names == ("other",)