Adds end-to-end Linux compatibility work for the dedicated server: new CI workflow (Ubuntu + Arch), line-ending/executable safeguards, and a local venv-first startup flow with split dependency files for server vs optional host GUI tooling. Improves runtime resilience with better bind error messages, stricter config validation, writable-state checks, cleaner socket/thread shutdown behavior, and SIGTERM-aware graceful stop handling for headless/systemd use. Updates deployment/startup docs and adds portability/runtime integration tests to lock in these behaviors.
71 lines
2.0 KiB
Python
71 lines
2.0 KiB
Python
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,
|
|
)
|
|
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
|
|
|
|
|
|
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_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))
|