from pathlib import Path import pytest from pve_vm_setup.errors import SettingsError from pve_vm_setup.settings import AppSettings, resolve_dotenv_paths, write_config_dotenv def test_settings_load_defaults_and_normalize_api_base() -> None: settings = AppSettings.from_env( { "PROXMOX_URL": "https://proxmox.example.invalid:8006/", "PROXMOX_USER": "root", "PROXMOX_PASSWORD": "secret", "PROXMOX_REALM": "pam", "PROXMOX_API_BASE": "api2/json", }, load_dotenv_file=False, ) assert settings.proxmox_url == "https://proxmox.example.invalid:8006" assert settings.proxmox_api_base == "/api2/json" assert settings.proxmox_verify_tls is False assert settings.request_timeout_seconds == 15 assert settings.default_iso_selector is None assert settings.effective_username == "root@pam" assert settings.safety_policy.prevent_create is False assert settings.safety_policy.enable_test_mode is False assert settings.safety_policy.test_tag == "codex-e2e" assert settings.safety_policy.test_vm_name_prefix == "codex-e2e-" def test_settings_reject_test_mode_without_required_scope() -> None: with pytest.raises(SettingsError): AppSettings.from_env( { "PROXMOX_ENABLE_TEST_MODE": "true", }, load_dotenv_file=False, ) def test_settings_allow_create_without_test_scope_when_test_mode_disabled() -> None: settings = AppSettings.from_env( { "PROXMOX_PREVENT_CREATE": "false", }, load_dotenv_file=False, ) assert settings.safety_policy.allow_create is True assert settings.safety_policy.enable_test_mode is False def test_settings_allow_create_by_default_when_prevent_flag_is_unset() -> None: settings = AppSettings.from_env({}, load_dotenv_file=False) assert settings.safety_policy.prevent_create is False assert settings.safety_policy.allow_create is True def test_settings_treat_url_only_as_live_capable_for_interactive_login() -> None: settings = AppSettings.from_env( { "PROXMOX_URL": "https://proxmox.example.invalid:8006", }, load_dotenv_file=False, ) assert settings.is_live_configured is True def test_settings_validate_live_requirements_still_needs_login_defaults_for_doctor() -> None: settings = AppSettings.from_env( { "PROXMOX_URL": "https://proxmox.example.invalid:8006", }, load_dotenv_file=False, ) with pytest.raises(SettingsError): settings.validate_live_requirements() def test_settings_reject_invalid_default_iso_regex_selector() -> None: with pytest.raises(SettingsError): AppSettings.from_env( { "PROXMOX_DEFAULT_ISO_SELECTOR": "regex:[unterminated", }, load_dotenv_file=False, ) def test_settings_loads_current_directory_dotenv_when_present(tmp_path: Path, monkeypatch) -> None: monkeypatch.chdir(tmp_path) monkeypatch.setenv("HOME", str(tmp_path / "home")) (tmp_path / ".env").write_text( "PROXMOX_URL=https://cwd.example.invalid:8006\n", encoding="utf-8", ) settings = AppSettings.from_env({}, load_dotenv_file=True) assert settings.proxmox_url == "https://cwd.example.invalid:8006" def test_settings_prefers_config_dotenv_over_current_directory_dotenv( tmp_path: Path, monkeypatch ) -> None: home = tmp_path / "home" config_dir = home / ".config" / "pve-vm-setup" config_dir.mkdir(parents=True) monkeypatch.chdir(tmp_path) monkeypatch.setenv("HOME", str(home)) (tmp_path / ".env").write_text( "PROXMOX_URL=https://cwd.example.invalid:8006\n", encoding="utf-8", ) (config_dir / ".env").write_text( "PROXMOX_URL=https://config.example.invalid:8006\n", encoding="utf-8", ) settings = AppSettings.from_env({}, load_dotenv_file=True) assert settings.proxmox_url == "https://config.example.invalid:8006" def test_settings_prefers_environment_over_config_and_current_directory_dotenv( tmp_path: Path, monkeypatch ) -> None: home = tmp_path / "home" config_dir = home / ".config" / "pve-vm-setup" config_dir.mkdir(parents=True) monkeypatch.chdir(tmp_path) monkeypatch.setenv("HOME", str(home)) (tmp_path / ".env").write_text( "PROXMOX_URL=https://cwd.example.invalid:8006\n", encoding="utf-8", ) (config_dir / ".env").write_text( "PROXMOX_URL=https://config.example.invalid:8006\n", encoding="utf-8", ) settings = AppSettings.from_env( { "PROXMOX_URL": "https://env.example.invalid:8006", }, load_dotenv_file=True, ) assert settings.proxmox_url == "https://env.example.invalid:8006" def test_resolve_dotenv_paths_uses_standard_config_location(tmp_path: Path, monkeypatch) -> None: home = tmp_path / "home" monkeypatch.setenv("HOME", str(home)) paths = resolve_dotenv_paths() assert paths.cwd == Path(".env") assert paths.config == home / ".config" / "pve-vm-setup" / ".env" assert paths.any_exists is False def test_write_config_dotenv_creates_parent_directory_and_skips_blank_values( tmp_path: Path, ) -> None: target = tmp_path / "home" / ".config" / "pve-vm-setup" / ".env" written_path = write_config_dotenv( { "PROXMOX_URL": "https://pve.example.invalid:8006", "PROXMOX_USER": "root", "PROXMOX_PASSWORD": "", }, config_dotenv_path=target, ) assert written_path == target assert target.exists() assert target.read_text(encoding="utf-8") == ( "# Generated by pve-vm-setup\n" "PROXMOX_URL=https://pve.example.invalid:8006\n" "PROXMOX_USER=root\n" )