from __future__ import annotations from pathlib import Path import pytest from config import ( Config, ensure_writable_directory, load_config, save_config, validate_config, ) def test_save_and_load_utf8_lf(tmp_path: Path) -> None: path = tmp_path / "commonwealth-server.json" cfg = Config( host="0.0.0.0", port=7777, server_name="Café Server", server_description="résumé", max_players=8, log_verbosity="info", admin_port=7779, enable_gns_transport=True, gns_bridge_path="/opt/commonwealth/libcommonwealth_online_gns_bridge.so", ) save_config(cfg, str(path)) raw = path.read_bytes() assert b"\r\n" not in raw assert "Café Server".encode("utf-8") in raw loaded = load_config(str(path)) assert loaded.server_name == "Café Server" assert loaded.server_description == "résumé" assert loaded.admin_port == 7779 assert loaded.enable_gns_transport is True assert loaded.gns_bridge_path == "/opt/commonwealth/libcommonwealth_online_gns_bridge.so" def test_gns_boolean_config_parsing_does_not_treat_false_string_as_true() -> None: assert Config.from_dict({"enable_gns_transport": "false"}).enable_gns_transport is False assert Config.from_dict({"enable_gns_transport": "off"}).enable_gns_transport is False assert Config.from_dict({"enable_gns_transport": "0"}).enable_gns_transport is False assert Config.from_dict({"enable_gns_transport": "true"}).enable_gns_transport is True assert Config.from_dict({"enable_gns_transport": "yes"}).enable_gns_transport is True assert Config.from_dict({"enable_gns_transport": 1}).enable_gns_transport is True assert Config.from_dict({"enable_gns_transport": 0}).enable_gns_transport is False with pytest.raises(ValueError, match="enable_gns_transport"): Config.from_dict({"enable_gns_transport": "sometimes"}) def test_gns_bridge_path_normalizes_blank_to_none_and_rejects_nonstring() -> None: assert Config.from_dict({"gns_bridge_path": ""}).gns_bridge_path is None assert Config.from_dict({"gns_bridge_path": " "}).gns_bridge_path is None assert Config.from_dict({"gns_bridge_path": None}).gns_bridge_path is None with pytest.raises(ValueError, match="gns_bridge_path"): Config.from_dict({"gns_bridge_path": 123}) def test_validate_rejects_bad_ports_and_max_players() -> None: cfg = Config(port=70000, admin_port=7777, max_players=0) ok, errors = validate_config(cfg) assert not ok assert any("port must be 1-65535" in error for error in errors) assert any("max_players must be >= 1" in error for error in errors) collide = Config(port=7777, admin_port=7777) ok, errors = validate_config(collide) assert not ok assert any("admin_port must differ" in error for error in errors) def test_validate_rejects_unresolvable_host() -> None: cfg = Config(host="this-host-should-not-resolve.invalid") ok, errors = validate_config(cfg) assert not ok assert any("not a valid IPv4" in error for error in errors) def test_gns_requires_explicit_ipv4_bind_host() -> None: explicit = Config(host="127.0.0.1", enable_gns_transport=True) ok, errors = validate_config(explicit) assert ok, errors wildcard = Config(host="0.0.0.0", enable_gns_transport=True) ok, errors = validate_config(wildcard) assert ok, errors hostname = Config(host="localhost", enable_gns_transport=True) ok, errors = validate_config(hostname) assert not ok assert any("explicit IPv4 bind address" in error for error in errors) def test_ensure_writable_directory(tmp_path: Path) -> None: target = tmp_path / "state" ensure_writable_directory(target) assert target.is_dir() assert not (target / ".commonwealth-write-probe").exists() def test_load_invalid_json(tmp_path: Path) -> None: path = tmp_path / "bad.json" path.write_text("{not json", encoding="utf-8") with pytest.raises(Exception): load_config(str(path))