from pathlib import Path import shutil import pytest from lohup.app import Lohup from lohup.config import ConfigError from lohup.logger import BasicLogger, LogLevel from lohup.util import DeepValidator, ValidationFailed class RepoEnvironment: def __init__(self, tmp_path: Path): self.basepath = tmp_path self.repo = tmp_path / "repo" self.pwfile = tmp_path / "password.txt" self.cfg = tmp_path / "lohup.toml" self.log = BasicLogger(level=LogLevel.DEBUG) def write_password(self, value: str = "1") -> Path: pth = self.basepath / "password.txt" pth.write_text("1") return pth def write_config(self, value: str, vars: dict[str, str]): kwargs = vars.copy() kwargs["base"] = str(self.basepath) kwargs["repo"] = str(self.repo) kwargs["pwfile"] = str(self.pwfile) self.cfg.write_text(value.format_map(kwargs)) @pytest.fixture def repo(tmp_path: Path): env = RepoEnvironment(tmp_path) yield env if env.repo.exists(): shutil.rmtree(env.repo) toml1 = """ [settings.globals] """ def test_error_message(repo): repo.write_config(toml1, {}) app = Lohup(config_path=str(repo.cfg), logger=repo.log) msg = "no repositories defined" with pytest.raises(ConfigError, match="Failed to parse TOML config") as exc: app.load() repo.log.error(exc.value) cause = exc.value.__cause__ assert isinstance(cause, ValidationFailed) and msg in cause.messages toml2 = """ [[hooks.before-all]] kind = "???" foo = "bar" [repos.local] kind = "local" path = "{base}/repo" repo-key-file = "{pwfile}" """ def test_hook_error(repo): repo.write_config(toml2, {}) app = Lohup(config_path=str(repo.cfg), logger=repo.log) msg = "hook: before-all: unsupported kind: ???" with pytest.raises(ConfigError, match="Failed to parse TOML config") as exc: app.load() repo.log.error(exc.value) cause = exc.value.__cause__ assert isinstance(cause, ValidationFailed) and msg in cause.messages toml_vars = """ [settings.globals] foo = "$bar" baz = "1" bag = 2 [repos.local] kind = "local" path = "$foo" repo-key-file = "$unknown" """ def test_invalid_vars(repo): repo.write_config(toml_vars, {}) app = Lohup(config_path=str(repo.cfg), logger=repo.log) lines = [ "settings: variable 'foo': nested variables in globals are not allowed", "settings: variable 'bag': invalid type: int, expected string", "repo 'local': read repo-key-file: variable 'unknown' is not defined", ] with pytest.raises(ConfigError) as exc: app.load() repo.log.error(exc.value) cause = exc.value.__cause__ assert isinstance(cause, ValidationFailed) for exc in cause.exceptions: repo.log.error(exc.value) assert set(lines) == set(cause.messages) def test_validator(): validator = DeepValidator("testing") with validator.inner("read config") as inner: with inner.inner("key 'foo'") as children: children.error("expected string") res = validator.check() assert isinstance(res, ValidationFailed) assert len(res.messages) == 1, "expected error during validation" assert res.messages[0] == "testing: read config: key 'foo': expected string"