import os from pathlib import Path import pytest from knoe import knoe_conf def _symlink_supported(tmp_path: Path) -> bool: try: target = tmp_path / "target" link = tmp_path / "link" target.write_text("x") os.symlink(str(target), str(link)) return link.is_symlink() except Exception: return False def test_layered_cfg_files_order_and_merge(tmp_path: Path): if not _symlink_supported(tmp_path): pytest.skip("symlinks not supported") conf_dir = tmp_path / "conf" env_dir = conf_dir / "dev" env_dir.mkdir(parents=True) (env_dir / "knoe.cfg").write_text( "[Global]\nNAMESPACE = base\nSERVICE_NAMESPACE = base-svc\n" ) (env_dir / "10-first.cfg").write_text("[Global]\nNAMESPACE = first\n") (env_dir / "20-second.cfg").write_text("[Global]\nNAMESPACE = second\n") (env_dir / "gcp.cfg").write_text('project_id = "demo"\n') (env_dir / "prod.cfg").write_text("[Global]\nNAMESPACE = ignored\n") entry = conf_dir / "knoe.cfg" os.symlink(str(env_dir / "knoe.cfg"), str(entry)) files = knoe_conf.layered_cfg_files(entry) assert files == [ env_dir / "knoe.cfg", env_dir / "10-first.cfg", env_dir / "20-second.cfg", ] cfg = knoe_conf.load_layered_config(entry) assert cfg.get("Global", "NAMESPACE") == "second" assert cfg.get("Global", "SERVICE_NAMESPACE") == "base-svc" def test_activate_environment_updates_symlink_and_preserves_other_env(tmp_path: Path): if not _symlink_supported(tmp_path): pytest.skip("symlinks not supported") conf_dir = tmp_path / "conf" (conf_dir / "dev").mkdir(parents=True) (conf_dir / "service").mkdir(parents=True) dev_base = conf_dir / "dev" / "knoe.cfg" svc_base = conf_dir / "service" / "knoe.cfg" dev_base.write_text("[Global]\nNAMESPACE = dev-ns\n") svc_base.write_text("[Global]\nNAMESPACE = service-ns\n") knoe_conf.activate_environment(conf_dir, "dev", create_base_if_missing=False) entry = conf_dir / "knoe.cfg" assert entry.is_symlink() assert entry.resolve() == dev_base knoe_conf.activate_environment(conf_dir, "service", create_base_if_missing=False) assert entry.is_symlink() assert entry.resolve() == svc_base # Ensure switching does not mutate other environments. assert dev_base.read_text() == "[Global]\nNAMESPACE = dev-ns\n" cfg = knoe_conf.load_layered_config(entry) assert cfg.get("Global", "NAMESPACE") == "service-ns" def test_test_environment_defaults_not_default_namespace(tmp_path: Path): if not _symlink_supported(tmp_path): pytest.skip("symlinks not supported") conf_dir = tmp_path / "conf" knoe_conf.activate_environment(conf_dir, "test") entry = conf_dir / "knoe.cfg" cfg = knoe_conf.load_layered_config(entry) ns = cfg.get("Global", "NAMESPACE", fallback="") assert ns assert ns != "default" assert ns == "knoe-test" @pytest.mark.parametrize( "mode,expected_env", [ ("k3d", "dev"), ("k3s", "service"), ("k8s", "prod"), ("test", "test"), ], ) def test_ensure_entrypoint_migrates_legacy_regular_file_based_on_mode( tmp_path: Path, mode: str, expected_env: str ): if not _symlink_supported(tmp_path): pytest.skip("symlinks not supported") conf_dir = tmp_path / "conf" conf_dir.mkdir(parents=True) legacy_text = ( f"[Global]\nDEPLOYMENT_MODE = {mode}\nCLUSTER_ENV = {mode}-example\nNAMESPACE = ns\n" ) entry = conf_dir / "knoe.cfg" entry.write_text(legacy_text, encoding="utf-8") knoe_conf.ensure_entrypoint(conf_dir) # Entry should now be a symlink to the inferred env base. assert entry.is_symlink() target = entry.resolve() assert target == conf_dir / expected_env / "knoe.cfg" assert target.read_text(encoding="utf-8") == legacy_text # Legacy should be preserved as a backup. backups = list(conf_dir.glob("knoe.cfg.legacy.*")) assert backups, "expected legacy backup to be created" assert any(p.read_text(encoding="utf-8") == legacy_text for p in backups) # All env dirs should exist after bootstrap. for env in knoe_conf.ENVIRONMENTS: assert (conf_dir / env).is_dir()