"""Tests for `platform.config`. Covers: - Phase 0 forces filesystem backend even when NEO4J_*/FUSEKI_* are set - `build_ontocast_config` clears Neo4j/Fuseki on filesystem backend - Phase < 4 with non-filesystem storage_backend raises eagerly - working_directory is auto-created """ from __future__ import annotations import sys from pathlib import Path import pytest REPO_ROOT = Path(__file__).resolve().parents[2] PLATFORM_ROOT = REPO_ROOT if str(PLATFORM_ROOT) not in sys.path: sys.path.insert(0, str(PLATFORM_ROOT)) # Importing `platform.config` here works because the `platform/` package # directory has an `__init__.py`. (Avoid the stdlib module of the same name # by relying on the package being on sys.path before site-packages.) import importlib platform_config = importlib.import_module("ont_platform.config") def _clear_settings_env(monkeypatch: pytest.MonkeyPatch) -> None: """Wipe all env vars that PlatformSettings or OntoCast Config might read.""" for var in [ "PHASE", "STORAGE_BACKEND", "ONTOCAST_WORKING_DIRECTORY", "ONTOCAST_ONTOLOGY_DIRECTORY", "HOST", "PORT", "LOG_LEVEL", "ROBOTS_POLICY", "NEO4J_URI", "NEO4J_AUTH", "FUSEKI_URI", "FUSEKI_AUTH", "LLM_PROVIDER", "LLM_API_KEY", "LLM_MODEL_NAME", ]: monkeypatch.delenv(var, raising=False) def test_default_phase_is_base_with_filesystem( monkeypatch: pytest.MonkeyPatch, tmp_path: Path ) -> None: _clear_settings_env(monkeypatch) monkeypatch.setenv("ONTOCAST_WORKING_DIRECTORY", str(tmp_path / "work")) settings = platform_config.PlatformSettings() # type: ignore[call-arg] assert settings.phase == platform_config.Phase.BASE assert settings.storage_backend == "filesystem" assert (tmp_path / "work").exists(), "working_directory should be created" def test_phase0_rejects_neo4j_backend(monkeypatch: pytest.MonkeyPatch) -> None: _clear_settings_env(monkeypatch) monkeypatch.setenv("PHASE", "0") monkeypatch.setenv("STORAGE_BACKEND", "neo4j") with pytest.raises(ValueError, match="requires Phase 4"): platform_config.PlatformSettings() # type: ignore[call-arg] def test_phase0_rejects_fuseki_backend(monkeypatch: pytest.MonkeyPatch) -> None: _clear_settings_env(monkeypatch) monkeypatch.setenv("PHASE", "0") monkeypatch.setenv("STORAGE_BACKEND", "fuseki") with pytest.raises(ValueError, match="requires Phase 4"): platform_config.PlatformSettings() # type: ignore[call-arg] def test_build_ontocast_config_clears_neo4j_and_fuseki( monkeypatch: pytest.MonkeyPatch, tmp_path: Path ) -> None: """Even if NEO4J_*/FUSEKI_* env vars are set, Phase 0 must hand OntoCast a config with both backends disabled.""" _clear_settings_env(monkeypatch) monkeypatch.setenv("ONTOCAST_WORKING_DIRECTORY", str(tmp_path / "work")) # Set fake credentials that would otherwise enable both backends. monkeypatch.setenv("NEO4J_URI", "bolt://localhost:7687") monkeypatch.setenv("NEO4J_AUTH", "neo4j/changeme") monkeypatch.setenv("FUSEKI_URI", "http://localhost:3030") monkeypatch.setenv("FUSEKI_AUTH", "admin:changeme") # LLM must be valid enough for validate_llm_config to pass. monkeypatch.setenv("LLM_PROVIDER", "openai") monkeypatch.setenv("LLM_API_KEY", "test-key") settings = platform_config.PlatformSettings() # type: ignore[call-arg] cfg = platform_config.build_ontocast_config(settings) assert cfg.tool_config.neo4j.uri is None assert cfg.tool_config.neo4j.auth is None assert cfg.tool_config.fuseki.uri is None assert cfg.tool_config.fuseki.auth is None # Paths must be the platform-overridden value. assert cfg.tool_config.path_config.working_directory == (tmp_path / "work") def test_build_ontocast_config_validates_llm_eagerly( monkeypatch: pytest.MonkeyPatch, tmp_path: Path ) -> None: _clear_settings_env(monkeypatch) monkeypatch.setenv("ONTOCAST_WORKING_DIRECTORY", str(tmp_path / "work")) monkeypatch.setenv("LLM_PROVIDER", "openai") # Intentionally omit LLM_API_KEY so validate_llm_config raises. settings = platform_config.PlatformSettings() # type: ignore[call-arg] with pytest.raises(ValueError, match="LLM_API_KEY"): platform_config.build_ontocast_config(settings)