chore: initialize EverOS 1.0.0
md-first memory extraction framework for AI agents. Markdown is the single source of truth; SQLite holds state and LanceDB provides the rebuildable vector + BM25 + scalar index. The codebase follows a single-direction DDD layering (entrypoints -> service -> memory -> infra, with component / core / config cross-cutting) enforced by import-linter. Engineering surface: - Coding conventions in .claude/rules/ (path-scoped) and workflows in .claude/skills/ (/commit, /new-branch, /pr). - GitHub Actions CI runs make lint + test + integration; pre-commit mirrors the gates locally (ruff, hygiene hooks, gitlint commit-msg). - Commit messages follow Conventional Commits, enforced by gitlint. - make lint also enforces datetime two-zone discipline and OpenAPI drift.
This commit is contained in:
0
tests/unit/test_component/test_config/__init__.py
Normal file
0
tests/unit/test_component/test_config/__init__.py
Normal file
167
tests/unit/test_component/test_config/test_loader.py
Normal file
167
tests/unit/test_component/test_config/test_loader.py
Normal file
@ -0,0 +1,167 @@
|
||||
"""Unit tests for YamlConfigLoader."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from everos.component.config import YamlConfigLoader
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def config_root(tmp_path: Path) -> Path:
|
||||
"""Build a fixture config tree::
|
||||
|
||||
tmp_path/
|
||||
prompt_slots/
|
||||
episode.yaml
|
||||
atomic_fact.yaml
|
||||
custom_dir/
|
||||
alpha.yaml
|
||||
"""
|
||||
(tmp_path / "prompt_slots").mkdir()
|
||||
(tmp_path / "prompt_slots" / "episode.yaml").write_text(
|
||||
"template: extract episode\nvariables:\n memcell: input memcell\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
(tmp_path / "prompt_slots" / "atomic_fact.yaml").write_text(
|
||||
"template: extract atomic fact\n", encoding="utf-8"
|
||||
)
|
||||
(tmp_path / "custom_dir").mkdir()
|
||||
(tmp_path / "custom_dir" / "alpha.yaml").write_text(
|
||||
"value: alpha\n", encoding="utf-8"
|
||||
)
|
||||
return tmp_path
|
||||
|
||||
|
||||
def test_register_default_subdir(config_root: Path) -> None:
|
||||
loader = YamlConfigLoader(root=config_root)
|
||||
loader.register_category("prompt_slots")
|
||||
meta = loader.find("prompt_slots", "episode")
|
||||
assert meta == {
|
||||
"template": "extract episode",
|
||||
"variables": {"memcell": "input memcell"},
|
||||
}
|
||||
|
||||
|
||||
def test_register_custom_subdir(config_root: Path) -> None:
|
||||
loader = YamlConfigLoader(root=config_root)
|
||||
loader.register_category("alphas", subdir="custom_dir")
|
||||
meta = loader.find("alphas", "alpha")
|
||||
assert meta == {"value": "alpha"}
|
||||
|
||||
|
||||
def test_constructor_categories_dict(config_root: Path) -> None:
|
||||
loader = YamlConfigLoader(
|
||||
root=config_root,
|
||||
categories={"prompt_slots": None, "alphas": "custom_dir"},
|
||||
)
|
||||
assert sorted(loader.categories()) == ["alphas", "prompt_slots"]
|
||||
assert loader.find("alphas", "alpha") == {"value": "alpha"}
|
||||
|
||||
|
||||
def test_find_unregistered_category_raises(config_root: Path) -> None:
|
||||
loader = YamlConfigLoader(root=config_root)
|
||||
with pytest.raises(KeyError, match="not registered"):
|
||||
loader.find("ghost", "x")
|
||||
|
||||
|
||||
def test_find_missing_file_raises(config_root: Path) -> None:
|
||||
loader = YamlConfigLoader(root=config_root)
|
||||
loader.register_category("prompt_slots")
|
||||
with pytest.raises(FileNotFoundError):
|
||||
loader.find("prompt_slots", "no_such")
|
||||
|
||||
|
||||
def test_find_non_mapping_top_level_raises(tmp_path: Path) -> None:
|
||||
(tmp_path / "prompt_slots").mkdir()
|
||||
# Top-level is a list, not a mapping — must be rejected.
|
||||
(tmp_path / "prompt_slots" / "bad.yaml").write_text(
|
||||
"- one\n- two\n", encoding="utf-8"
|
||||
)
|
||||
loader = YamlConfigLoader(root=tmp_path)
|
||||
loader.register_category("prompt_slots")
|
||||
with pytest.raises(TypeError, match="must be a mapping"):
|
||||
loader.find("prompt_slots", "bad")
|
||||
|
||||
|
||||
def test_find_empty_file_yields_empty_dict(tmp_path: Path) -> None:
|
||||
(tmp_path / "prompt_slots").mkdir()
|
||||
(tmp_path / "prompt_slots" / "blank.yaml").write_text("", encoding="utf-8")
|
||||
loader = YamlConfigLoader(root=tmp_path)
|
||||
loader.register_category("prompt_slots")
|
||||
assert loader.find("prompt_slots", "blank") == {}
|
||||
|
||||
|
||||
def test_list_returns_sorted_stems(config_root: Path) -> None:
|
||||
loader = YamlConfigLoader(root=config_root)
|
||||
loader.register_category("prompt_slots")
|
||||
assert loader.list("prompt_slots") == ["atomic_fact", "episode"]
|
||||
|
||||
|
||||
def test_list_unregistered_category_raises(config_root: Path) -> None:
|
||||
loader = YamlConfigLoader(root=config_root)
|
||||
with pytest.raises(KeyError):
|
||||
loader.list("ghost")
|
||||
|
||||
|
||||
def test_list_empty_directory(tmp_path: Path) -> None:
|
||||
loader = YamlConfigLoader(root=tmp_path)
|
||||
loader.register_category("nope")
|
||||
assert loader.list("nope") == [] # missing directory → empty
|
||||
|
||||
|
||||
def test_cache_returns_same_object(config_root: Path) -> None:
|
||||
loader = YamlConfigLoader(root=config_root)
|
||||
loader.register_category("prompt_slots")
|
||||
a = loader.find("prompt_slots", "episode")
|
||||
b = loader.find("prompt_slots", "episode")
|
||||
assert a is b # cached, same dict reference
|
||||
|
||||
|
||||
def test_refresh_invalidates_cache_and_reloads(config_root: Path) -> None:
|
||||
loader = YamlConfigLoader(root=config_root)
|
||||
loader.register_category("prompt_slots")
|
||||
a = loader.find("prompt_slots", "episode")
|
||||
|
||||
# Modify the file on disk; without refresh the loader still returns
|
||||
# the cached value.
|
||||
(config_root / "prompt_slots" / "episode.yaml").write_text(
|
||||
"template: MODIFIED\n", encoding="utf-8"
|
||||
)
|
||||
cached = loader.find("prompt_slots", "episode")
|
||||
assert cached is a # still the cached object
|
||||
|
||||
loader.refresh()
|
||||
fresh = loader.find("prompt_slots", "episode")
|
||||
assert fresh is not a
|
||||
assert fresh == {"template": "MODIFIED"}
|
||||
|
||||
|
||||
def test_refresh_specific_entry(config_root: Path) -> None:
|
||||
loader = YamlConfigLoader(root=config_root)
|
||||
loader.register_category("prompt_slots")
|
||||
e = loader.find("prompt_slots", "episode")
|
||||
a = loader.find("prompt_slots", "atomic_fact")
|
||||
|
||||
(config_root / "prompt_slots" / "episode.yaml").write_text(
|
||||
"template: NEW\n", encoding="utf-8"
|
||||
)
|
||||
loader.refresh("prompt_slots", "episode")
|
||||
|
||||
assert loader.find("prompt_slots", "episode") != e # reloaded
|
||||
assert loader.find("prompt_slots", "atomic_fact") is a # untouched
|
||||
|
||||
|
||||
def test_refresh_full_category(config_root: Path) -> None:
|
||||
loader = YamlConfigLoader(
|
||||
root=config_root,
|
||||
categories={"prompt_slots": None, "alphas": "custom_dir"},
|
||||
)
|
||||
loader.find("prompt_slots", "episode")
|
||||
a = loader.find("alphas", "alpha")
|
||||
|
||||
loader.refresh("prompt_slots")
|
||||
# alphas cache survives the prompt_slots refresh
|
||||
assert loader.find("alphas", "alpha") is a
|
||||
Reference in New Issue
Block a user