Files
stack/tests/conf/test_conf.py
kert 99afcf4717
Some checks failed
CI / lint-test (push) Successful in 5m50s
CI / skinny-install (aco) (push) Successful in 44s
CI / skinny-install (api) (push) Successful in 27s
CI / skinny-install (bcda) (push) Successful in 26s
CI / skinny-install (bib) (push) Successful in 24s
CI / skinny-install (bls) (push) Successful in 23s
CI / skinny-install (ccw) (push) Successful in 23s
CI / skinny-install (cli) (push) Successful in 42s
CI / skinny-install (cms) (push) Successful in 22s
CI / skinny-install (conf) (push) Successful in 24s
CI / skinny-install (pfs) (push) Successful in 27s
CI / skinny-install (rex) (push) Successful in 21s
Infra CI / notebooks (push) Successful in 8s
Infra CI / zotero (push) Failing after 6s
Infra CI / docs (push) Successful in 6s
Infra CI / api (push) Successful in 11s
Infra CI / mc (push) Successful in 6s
Package Supply Chain / pkg-supply-chain (push) Failing after 27s
Deploy / build-scan-report (push) Successful in 3m36s
move zotero/ under data/ — all user data in one gitignored tree
2026-03-24 18:10:40 -04:00

168 lines
5.0 KiB
Python

"""Tests for conf — centralised configuration loader."""
from __future__ import annotations
from pathlib import Path
from conf import ROOT, cfg, context, path, reload, secret
class TestCfg:
def test_root_is_repo_root(self) -> None:
assert (ROOT / "stack.toml").exists()
assert (ROOT / "pyproject.toml").exists()
def test_db_section_exists(self) -> None:
assert "db" in cfg
def test_db_aco_value(self) -> None:
assert cfg.db.aco == "data/aco.duckdb"
def test_db_bib_value(self) -> None:
assert cfg.db.bib == "data/bib.sqlite"
def test_db_zotero_value(self) -> None:
assert cfg.db.zotero == "data/zotero/data/zotero.sqlite"
def test_storage_bcda(self) -> None:
assert cfg.storage.bcda == "data/bcda"
def test_bcda_timeout_is_float(self) -> None:
assert isinstance(cfg.bcda.timeout, float)
def test_bcda_max_retries_is_int(self) -> None:
assert isinstance(cfg.bcda.max_retries, int)
def test_lake_trino_port_is_int(self) -> None:
assert isinstance(cfg.lake.trino.port, int)
def test_dict_access(self) -> None:
val = cfg["db"]["aco"]
assert val == "data/aco.duckdb"
def test_to_dict(self) -> None:
d = cfg.db.to_dict()
assert isinstance(d, dict)
assert "aco" in d
def test_contains(self) -> None:
assert "db" in cfg
assert "nonexistent_key" not in cfg
def test_repr(self) -> None:
r = repr(cfg.db)
assert "aco" in r
def test_missing_attr_raises(self) -> None:
import pytest
with pytest.raises(AttributeError):
cfg.no_such_section
class TestPath:
def test_path_returns_absolute(self) -> None:
p = path("db.aco")
assert isinstance(p, Path)
assert p.is_absolute()
def test_path_resolves_relative_to_root(self) -> None:
p = path("db.aco")
assert p == ROOT / "data/aco.duckdb"
def test_path_nested_key(self) -> None:
p = path("db.bib")
assert p == ROOT / "data/bib.sqlite"
class TestReload:
def test_reload_does_not_raise(self) -> None:
reload()
assert cfg.db.aco == "data/aco.duckdb"
class TestContext:
def test_default_context_is_local(self) -> None:
ctx = context()
assert ctx.db_backend == "duckdb"
assert ctx.storage_backend == "local"
def test_env_var_overrides_active(self, monkeypatch) -> None:
monkeypatch.setenv("STACK_CONTEXT", "lake")
reload()
ctx = context()
assert ctx.db_backend == "iceberg"
assert ctx.storage_backend == "s3"
def test_databricks_context(self, monkeypatch) -> None:
monkeypatch.setenv("STACK_CONTEXT", "databricks")
ctx = context()
assert ctx.db_backend == "databricks"
assert ctx.storage_backend == "dbfs"
def test_trino_context(self, monkeypatch) -> None:
monkeypatch.setenv("STACK_CONTEXT", "trino")
ctx = context()
assert ctx.db_backend == "trino"
assert ctx.storage_backend == "s3"
def test_unknown_context_raises(self, monkeypatch) -> None:
import pytest
monkeypatch.setenv("STACK_CONTEXT", "nonexistent")
with pytest.raises(KeyError):
context()
class TestSecret:
def test_env_var_takes_precedence(self, monkeypatch) -> None:
monkeypatch.setenv("STACK_API_SECRET", "from-env")
assert secret("api.secret", "STACK_API_SECRET") == "from-env"
def test_falls_back_to_config(self, monkeypatch) -> None:
monkeypatch.delenv("STACK_API_SECRET", raising=False)
# config value is empty string by default
assert secret("api.secret", "STACK_API_SECRET") == ""
def test_reads_non_empty_config(self, monkeypatch) -> None:
import conf
monkeypatch.delenv("_CONF_TEST_SECRET_", raising=False)
monkeypatch.setitem(conf.cfg._data["api"], "secret", "from-toml")
result = secret("api.secret", "_CONF_TEST_SECRET_")
assert result == "from-toml"
class TestApiConfig:
def test_api_host(self) -> None:
assert cfg.api.host == "0.0.0.0"
def test_api_port(self) -> None:
assert cfg.api.port == 8000
def test_api_workers(self) -> None:
assert cfg.api.workers == 1
class TestFindRootError:
def test_raises_when_no_stack_toml(self, tmp_path: Path) -> None:
import pytest
# Monkeypatch __file__ won't work easily, but we can test
# the function directly with a path that has no stack.toml
import conf
from conf import _find_root
original = conf.__file__
try:
# Point to a deep tmp path with no stack.toml
deep = tmp_path / "a" / "b" / "c" / "d" / "e"
deep.mkdir(parents=True)
fake_init = deep / "__init__.py"
fake_init.write_text("")
conf.__file__ = str(fake_init)
with pytest.raises(FileNotFoundError, match="stack.toml"):
_find_root()
finally:
conf.__file__ = original