106 lines
3.9 KiB
Python
106 lines
3.9 KiB
Python
"""AI layout awareness and path expansion."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
import thicket.layout as layout
|
|
from thicket.pipeline_core import IngestConfig
|
|
|
|
|
|
@pytest.fixture
|
|
def fake_ai(tmp_path: Path, monkeypatch):
|
|
(tmp_path / "corpus" / "cold").mkdir(parents=True)
|
|
(tmp_path / "corpus" / "hot").mkdir(parents=True)
|
|
(tmp_path / "corpus" / "books").mkdir(parents=True)
|
|
monkeypatch.setattr(layout, "AI_ROOT", tmp_path)
|
|
return tmp_path
|
|
|
|
|
|
def test_layout_detected_with_corpus_taxonomy(fake_ai):
|
|
profile = layout.detect_layout()
|
|
assert profile is not None
|
|
assert profile.corpus_cold == fake_ai / "corpus" / "cold"
|
|
assert profile.corpus_hot == fake_ai / "corpus" / "hot"
|
|
assert profile.books_present is True
|
|
assert profile.archive == fake_ai / "corpus" / "archive"
|
|
|
|
|
|
def test_layout_absent_without_corpus(tmp_path, monkeypatch):
|
|
monkeypatch.setattr(layout, "AI_ROOT", tmp_path)
|
|
assert layout.detect_layout() is None
|
|
|
|
|
|
def test_defaults_follow_the_layout(fake_ai):
|
|
assert layout.default_input() == fake_ai / "corpus" / "cold"
|
|
assert layout.default_vault() == fake_ai / "corpus" / "hot"
|
|
assert layout.default_archive(Path("/anywhere/in")) == fake_ai / "corpus" / "archive"
|
|
|
|
|
|
def test_defaults_fall_home_without_layout(tmp_path, monkeypatch):
|
|
monkeypatch.setattr(layout, "AI_ROOT", tmp_path)
|
|
assert layout.default_input() == Path.home() / "Downloads" / "Raw_Books_And_Papers"
|
|
assert layout.default_vault() == Path.home() / "Documents" / "ObsidianVault"
|
|
assert layout.default_archive(Path("/data/in")) == Path("/data/ingested-archive")
|
|
|
|
|
|
def test_archive_falls_back_to_sibling_without_layout(tmp_path, monkeypatch):
|
|
monkeypatch.setattr(layout, "AI_ROOT", tmp_path)
|
|
assert layout.default_archive(Path("/data/in")) == Path("/data/ingested-archive")
|
|
|
|
|
|
def test_expand_path_tilde_and_env(monkeypatch):
|
|
monkeypatch.setenv("THICKET_TEST_DIR", "/tmp/thicket-expanded")
|
|
assert layout.expand_path("~/books") == Path.home() / "books"
|
|
assert layout.expand_path("$THICKET_TEST_DIR/x") == Path("/tmp/thicket-expanded/x")
|
|
assert layout.expand_path("/plain/path") == Path("/plain/path")
|
|
|
|
|
|
def test_ingest_config_expands_paths(monkeypatch):
|
|
monkeypatch.setenv("THICKET_TEST_DIR", "/tmp/thicket-expanded")
|
|
config = IngestConfig(input_dir="~/in", vault_dir="$THICKET_TEST_DIR/vault",
|
|
archive_dir="$THICKET_TEST_DIR/archive",
|
|
use_qdrant=False)
|
|
assert config.input_dir == Path.home() / "in"
|
|
assert config.vault_dir == Path("/tmp/thicket-expanded/vault")
|
|
assert config.archive_dir == Path("/tmp/thicket-expanded/archive")
|
|
|
|
|
|
def test_backend_profiles_fill_gaps_env_wins(tmp_path, monkeypatch):
|
|
import os
|
|
|
|
import thicket.layout as layout
|
|
|
|
backends = tmp_path / "backends"
|
|
backends.mkdir()
|
|
(backends / "thicket.env").write_text(
|
|
"# profile\n"
|
|
"PGHOST=db.local\n"
|
|
"PGUSER='fileuser'\n"
|
|
"MINIO_ENDPOINT=\"127.0.0.1:9000\"\n"
|
|
"NOT_A_LINE\n", encoding="utf-8")
|
|
monkeypatch.setattr(layout, "AI_ROOT", tmp_path)
|
|
for key in ("PGHOST", "PGUSER", "MINIO_ENDPOINT"):
|
|
monkeypatch.delenv(key, raising=False)
|
|
|
|
applied = layout.apply_backend_profiles()
|
|
assert applied == {"PGHOST": "db.local", "PGUSER": "fileuser",
|
|
"MINIO_ENDPOINT": "127.0.0.1:9000"}
|
|
assert os.environ["PGUSER"] == "fileuser"
|
|
|
|
monkeypatch.setenv("PGUSER", "shelluser")
|
|
monkeypatch.delenv("PGHOST", raising=False)
|
|
applied = layout.apply_backend_profiles()
|
|
assert "PGUSER" not in applied # environment wins
|
|
assert applied["PGHOST"] == "db.local" # gaps still filled
|
|
assert os.environ["PGUSER"] == "shelluser"
|
|
|
|
|
|
def test_backend_profiles_absent_file_is_noop(tmp_path, monkeypatch):
|
|
import thicket.layout as layout
|
|
|
|
monkeypatch.setattr(layout, "AI_ROOT", tmp_path)
|
|
assert layout.apply_backend_profiles() == {}
|