nanobot: 2026-09-10 12:33:37
This commit is contained in:
165
skills/wiki/tests/test_wiki_config.py
Normal file
165
skills/wiki/tests/test_wiki_config.py
Normal file
@@ -0,0 +1,165 @@
|
||||
"""Scope precedence (paths -> include -> exclude) and config validation."""
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
SCRIPTS = Path(__file__).parent.parent / "scripts"
|
||||
sys.path.insert(0, str(SCRIPTS))
|
||||
|
||||
import wiki_config
|
||||
from wiki_config import ConfigError, SourceConfig, load_config, source_root
|
||||
|
||||
WORKSPACE_YAML = """
|
||||
embedding:
|
||||
endpoint: http://nvidia.hell:11434/
|
||||
model: qwen3-embedding:0.6b
|
||||
dims: 1024
|
||||
batch: 32
|
||||
keep_alive: -1
|
||||
query_prefix: "Instruct: task\\nQuery: "
|
||||
|
||||
sources:
|
||||
index:
|
||||
kind: git
|
||||
url: git@git.fnet.cz:lachtan/index.git
|
||||
paths: ["**"]
|
||||
include: ["*.md"]
|
||||
exclude:
|
||||
- "**/node_modules/**"
|
||||
- "**/vendor/**"
|
||||
|
||||
workspace:
|
||||
kind: workspace
|
||||
paths:
|
||||
- "notes/**"
|
||||
- "develop/**"
|
||||
include: ["*.md"]
|
||||
exclude:
|
||||
- "**/inbox/**"
|
||||
- "develop/history.md"
|
||||
"""
|
||||
|
||||
|
||||
def _write(tmp_path, text):
|
||||
path = tmp_path / "config.yaml"
|
||||
path.write_text(text, encoding="utf-8")
|
||||
return path
|
||||
|
||||
|
||||
def _source(
|
||||
source_id: str = "ws",
|
||||
kind: str = "workspace",
|
||||
url: str | None = None,
|
||||
paths: tuple[str, ...] = ("**",),
|
||||
include: tuple[str, ...] = ("*.md",),
|
||||
exclude: tuple[str, ...] = (),
|
||||
) -> SourceConfig:
|
||||
return SourceConfig(source_id=source_id, kind=kind, url=url, paths=paths, include=include, exclude=exclude)
|
||||
|
||||
|
||||
def test_loads_embedding_and_sources(tmp_path):
|
||||
config = load_config(_write(tmp_path, WORKSPACE_YAML))
|
||||
assert config.embedding.endpoint == "http://nvidia.hell:11434" # trailing slash trimmed
|
||||
assert config.embedding.model == "qwen3-embedding:0.6b"
|
||||
assert config.embedding.dims == 1024
|
||||
assert config.embedding.keep_alive == -1
|
||||
assert config.embedding.query_prefix.endswith("Query: ")
|
||||
assert [s.source_id for s in config.sources] == ["index", "workspace"]
|
||||
index = config.source("index")
|
||||
assert index is not None and index.kind == "git"
|
||||
assert config.source("nope") is None
|
||||
|
||||
|
||||
def test_string_keep_alive_is_rejected():
|
||||
"""Ollama answers HTTP 400 to a string "-1" — catch it in config, not at runtime."""
|
||||
with pytest.raises(ConfigError, match="keep_alive"):
|
||||
wiki_config._parse_embedding({"endpoint": "http://x", "model": "m", "dims": 1024, "keep_alive": "-1"})
|
||||
|
||||
|
||||
def test_missing_config_file(tmp_path):
|
||||
with pytest.raises(ConfigError, match="missing config"):
|
||||
load_config(tmp_path / "nope.yaml")
|
||||
|
||||
|
||||
def test_git_source_needs_url(tmp_path):
|
||||
yaml_text = WORKSPACE_YAML.replace(" url: git@git.fnet.cz:lachtan/index.git\n", "")
|
||||
with pytest.raises(ConfigError, match="need a url"):
|
||||
load_config(_write(tmp_path, yaml_text))
|
||||
|
||||
|
||||
def test_unknown_kind_is_rejected(tmp_path):
|
||||
yaml_text = WORKSPACE_YAML.replace(" kind: workspace", " kind: mirror")
|
||||
with pytest.raises(ConfigError, match="kind must be one of"):
|
||||
load_config(_write(tmp_path, yaml_text))
|
||||
|
||||
|
||||
def test_paths_is_required(tmp_path):
|
||||
yaml_text = WORKSPACE_YAML.replace(' paths: ["**"]\n', "")
|
||||
with pytest.raises(ConfigError, match="`paths` is required"):
|
||||
load_config(_write(tmp_path, yaml_text))
|
||||
|
||||
|
||||
def test_paths_whitelist_gates_everything():
|
||||
source = _source(paths=("notes/**", "develop/**"))
|
||||
assert source.covers("notes/a.md")
|
||||
assert source.covers("notes/deep/nested/a.md")
|
||||
assert source.covers("develop/knowledge.md")
|
||||
# Not on the whitelist -> does not exist for the index.
|
||||
assert not source.covers("tmp/a.md")
|
||||
assert not source.covers("skills/wiki/SKILL.md")
|
||||
assert not source.covers("AGENTS.md")
|
||||
|
||||
|
||||
def test_include_filters_extensions():
|
||||
source = _source(paths=("**",))
|
||||
assert source.covers("notes/a.md")
|
||||
assert not source.covers("notes/main.py")
|
||||
assert not source.covers("memory/history.jsonl")
|
||||
assert not source.covers("assets/photo.png")
|
||||
|
||||
|
||||
def test_exclude_wins_over_paths_and_include():
|
||||
source = _source(paths=("notes/**", "develop/**"), exclude=("**/inbox/**", "develop/history.md"))
|
||||
assert not source.covers("notes/inbox/raw.md")
|
||||
assert not source.covers("develop/history.md")
|
||||
assert source.covers("develop/knowledge.md")
|
||||
assert source.covers("notes/notes.md")
|
||||
|
||||
|
||||
def test_double_star_matches_whole_repo():
|
||||
source = _source(paths=("**",), exclude=("**/node_modules/**", "**/vendor/**"))
|
||||
assert source.covers("README.md")
|
||||
assert source.covers("japan/tokyo/metro.md")
|
||||
assert not source.covers("node_modules/pkg/README.md")
|
||||
assert not source.covers("web/vendor/lib/CHANGELOG.md")
|
||||
|
||||
|
||||
def test_glob_star_does_not_cross_a_slash():
|
||||
source = _source(paths=("notes/*",))
|
||||
assert source.covers("notes/a.md")
|
||||
assert not source.covers("notes/deep/a.md")
|
||||
|
||||
|
||||
def test_covers_dir_prunes_the_walk():
|
||||
source = _source(paths=("notes/**", "develop/**"), exclude=("**/inbox/**",))
|
||||
assert source.covers_dir("")
|
||||
assert source.covers_dir("notes")
|
||||
assert source.covers_dir("notes/deep")
|
||||
assert source.covers_dir("develop")
|
||||
assert not source.covers_dir("tmp")
|
||||
assert not source.covers_dir("notes/inbox")
|
||||
|
||||
|
||||
def test_covers_dir_never_prunes_a_double_star_source():
|
||||
source = _source(paths=("**",), exclude=("**/node_modules/**",))
|
||||
assert source.covers_dir("anything/deep")
|
||||
assert not source.covers_dir("app/node_modules")
|
||||
|
||||
|
||||
def test_source_root_derives_clone_path_from_id():
|
||||
assert source_root(_source(source_id="travel", kind="git", url="git@x:y.git")) == (
|
||||
wiki_config.REMOTE_DIR / "travel"
|
||||
)
|
||||
assert source_root(_source(kind="workspace")) == wiki_config.WORKSPACE
|
||||
Reference in New Issue
Block a user