mirror of
https://github.com/EDCD/EDMarketConnector.git
synced 2026-10-25 01:06:05 +03:00
194 lines
5.9 KiB
Python
194 lines
5.9 KiB
Python
# flake8: noqa
|
|
# mypy: ignore-errors
|
|
"""Test the Config system."""
|
|
|
|
import sys
|
|
import tomli_w
|
|
import semantic_version
|
|
from unittest.mock import patch, MagicMock, mock_open
|
|
|
|
import config
|
|
from config import (
|
|
Config,
|
|
git_shorthash_from_head,
|
|
appversion,
|
|
appversion_nobuild,
|
|
)
|
|
|
|
|
|
class TestGitFunctions:
|
|
@patch("subprocess.run")
|
|
def test_git_shorthash_success(self, mock_run):
|
|
mock_run.side_effect = [
|
|
MagicMock(stdout="a1b2c3d\n", stderr=None), # rev-parse
|
|
MagicMock(stdout="", stderr=None), # diff (clean)
|
|
]
|
|
assert git_shorthash_from_head() == "a1b2c3d"
|
|
|
|
@patch("subprocess.run")
|
|
@patch("os.path.exists")
|
|
def test_git_shorthash_dirty(self, mock_exists, mock_run):
|
|
mock_exists.return_value = True
|
|
# Simulate the single call to git describe --dirty
|
|
mock_run.return_value = MagicMock(
|
|
stdout="a1b2c3d.DIRTY\n",
|
|
stderr="",
|
|
returncode=0
|
|
)
|
|
assert git_shorthash_from_head() == "a1b2c3d.DIRTY"
|
|
|
|
@patch("subprocess.run")
|
|
def test_git_shorthash_failure(self, mock_run):
|
|
mock_run.side_effect = FileNotFoundError("git not found")
|
|
assert git_shorthash_from_head() is None
|
|
|
|
|
|
class TestAppVersion:
|
|
def setup_method(self):
|
|
# Reset cached version before each test
|
|
config._cached_version = None
|
|
|
|
|
|
class TestConfigClass:
|
|
|
|
def test_init_creates_default_toml(self, mock_app_dir):
|
|
"""Test that initializing Config creates config.toml if missing."""
|
|
toml_file = mock_app_dir / "config.toml"
|
|
assert not toml_file.exists()
|
|
|
|
with patch("config.Config._init_platform"):
|
|
Config(mock_app_dir)
|
|
|
|
assert toml_file.exists()
|
|
assert "generated" in toml_file.read_text()
|
|
|
|
def test_load_existing_settings(self, mock_app_dir):
|
|
toml_file = mock_app_dir / "config.toml"
|
|
data = {
|
|
"settings": {
|
|
"theme": "dark",
|
|
# Need plugin_dir or __init__ will auto-add it
|
|
"plugin_dir": str(mock_app_dir / "plugins"),
|
|
}
|
|
}
|
|
with open(toml_file, "wb") as f:
|
|
tomli_w.dump(data, f)
|
|
|
|
with patch("config.Config._init_platform"):
|
|
cfg = Config(mock_app_dir)
|
|
|
|
assert cfg.get("theme") == "dark"
|
|
|
|
def test_set_saves_to_disk(self, mock_app_dir):
|
|
"""Test that set() updates memory and writes to disk."""
|
|
with patch("config.Config._init_platform"):
|
|
c = Config(mock_app_dir)
|
|
|
|
toml_path = c.toml_path
|
|
c.set("saved_value", "persistence_check")
|
|
|
|
# Read file directly to verify
|
|
with open(toml_path, "rb") as f:
|
|
import tomllib
|
|
|
|
data = tomllib.load(f)
|
|
|
|
assert data["settings"]["saved_value"] == "persistence_check"
|
|
|
|
def test_recovery_broken_toml(self, mock_app_dir):
|
|
"""Test that the system recovers from a corrupted TOML file."""
|
|
toml_path = mock_app_dir / "config.toml"
|
|
|
|
# Write garbage
|
|
with open(toml_path, "w") as f:
|
|
f.write("This is not valid TOML { [ } ]")
|
|
|
|
with patch("config.Config._init_platform"):
|
|
cfg = Config(mock_app_dir)
|
|
|
|
assert "plugin_dir" in cfg.settings
|
|
|
|
# The broken file should be renamed
|
|
broken_files = list(mock_app_dir.glob("config.toml.broken.*"))
|
|
assert len(broken_files) > 0
|
|
|
|
def test_recovery_from_backup(self, mock_app_dir):
|
|
"""Test recovering from .bak if .toml is broken."""
|
|
toml_path = mock_app_dir / "config.toml"
|
|
bak_path = mock_app_dir / "config.toml.bak"
|
|
|
|
with open(toml_path, "w") as f:
|
|
f.write("Garbage")
|
|
|
|
valid_data = {
|
|
"settings": {"recovered": True, "plugin_dir": str(mock_app_dir / "plugins")}
|
|
}
|
|
with open(bak_path, "wb") as f:
|
|
tomli_w.dump(valid_data, f)
|
|
|
|
with patch("config.Config._init_platform"):
|
|
cfg = Config(mock_app_dir)
|
|
|
|
assert cfg.get_bool("recovered") is True
|
|
|
|
# This confirms that the rename `bak.replace(self.toml_path)` inside `_load` worked.
|
|
# If this fails, it means `tomllib.load` on the backup file failed.
|
|
assert not bak_path.exists()
|
|
assert toml_path.exists()
|
|
|
|
def test_reload_from_path(self, mock_app_dir, tmp_path):
|
|
"""Test hot-swapping the config file."""
|
|
with patch("config.Config._init_platform"):
|
|
c = Config(mock_app_dir)
|
|
|
|
# Create a second config file elsewhere
|
|
alt_dir = tmp_path / "alt"
|
|
alt_dir.mkdir()
|
|
alt_config = alt_dir / "alternate.toml"
|
|
|
|
data = {
|
|
"settings": {
|
|
"mode": "alternate",
|
|
"plugin_dir": str(mock_app_dir / "plugins"),
|
|
}
|
|
}
|
|
with open(alt_config, "wb") as f:
|
|
tomli_w.dump(data, f)
|
|
|
|
# Reload
|
|
c.reload_from_path(alt_config)
|
|
|
|
assert c.toml_path == alt_config
|
|
assert c.get("mode") == "alternate"
|
|
|
|
|
|
def test_init_platform_calls_linux_helper(mock_app_dir):
|
|
with patch("sys.platform", "linux"):
|
|
mock_module = sys.modules["config.linux"]
|
|
|
|
# We must NOT patch _init_platform here, because we want it to run
|
|
# and call the mocked module
|
|
Config(mock_app_dir)
|
|
|
|
mock_module.linux_helper.assert_called_once()
|
|
|
|
|
|
def test_set_none_deletes_key(mock_app_dir):
|
|
"""Test that calling set(key, None) deletes the key from config."""
|
|
with patch("config.Config._init_platform"):
|
|
cfg = Config(mock_app_dir)
|
|
|
|
# Set a key first
|
|
cfg.set("some_temp_key", "value")
|
|
assert cfg.get("some_temp_key") == "value"
|
|
|
|
# Set it to None, which should delete it
|
|
cfg.set("some_temp_key", None)
|
|
assert cfg.get("some_temp_key") is None
|
|
|
|
# Verify disk persistence does not have it either
|
|
with open(cfg.toml_path, "rb") as f:
|
|
import tomllib
|
|
data = tomllib.load(f)
|
|
assert "some_temp_key" not in data["settings"]
|