Files
romm/backend/tests/config/test_config_loader.py
Georges-Antoine Assi 8210bab806 Tolerate forward-compat values and malformed YAML in config loader
Sample configs shipped with newer releases can reference media types or
options unknown to older versions; previously the loader called
sys.exit(3) and refused to boot. Now drop unknown scan.media entries,
fall back to defaults for unknown gamelist media thumbnail/image values,
and recover from YAML parse errors with a clear critical log instead of
a traceback. Type-mismatch validation still hard-fails since those are
real misconfigurations.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 22:26:43 -04:00

163 lines
5.7 KiB
Python

import os
from pathlib import Path
from config.config_manager import (
DEFAULT_EXCLUDED_DIRS,
DEFAULT_EXCLUDED_EXTENSIONS,
DEFAULT_EXCLUDED_FILES,
ConfigManager,
)
def test_config_loader():
loader = ConfigManager(
os.path.join(Path(__file__).resolve().parent, "fixtures", "config/config.yml")
)
assert loader.config.EXCLUDED_PLATFORMS == sorted({*DEFAULT_EXCLUDED_DIRS, "romm"})
assert loader.config.EXCLUDED_SINGLE_EXT == sorted(
{
*(e.lower() for e in DEFAULT_EXCLUDED_EXTENSIONS),
"xml",
}
)
assert loader.config.EXCLUDED_SINGLE_FILES == sorted(
{*DEFAULT_EXCLUDED_FILES, "info.txt"}
)
assert loader.config.EXCLUDED_MULTI_FILES == sorted(
{
*DEFAULT_EXCLUDED_DIRS,
"my_multi_file_game",
"DLC",
}
)
assert loader.config.EXCLUDED_MULTI_PARTS_EXT == sorted(
{
*(e.lower() for e in DEFAULT_EXCLUDED_EXTENSIONS),
"txt",
}
)
assert loader.config.EXCLUDED_MULTI_PARTS_FILES == sorted(
{
*DEFAULT_EXCLUDED_FILES,
"data.xml",
}
)
assert loader.config.PLATFORMS_BINDING == {"gc": "ngc"}
assert loader.config.PLATFORMS_VERSIONS == {"naomi": "arcade"}
assert loader.config.ROMS_FOLDER_NAME == "ROMS"
assert loader.config.FIRMWARE_FOLDER_NAME == "BIOS"
assert loader.config.SKIP_HASH_CALCULATION
assert loader.config.EJS_DEBUG
assert loader.config.EJS_DISABLE_AUTO_UNLOAD
assert loader.config.EJS_DISABLE_BATCH_BOOTUP
assert loader.config.EJS_CACHE_LIMIT == 1000
assert loader.config.EJS_NETPLAY_ENABLED
assert loader.config.EJS_NETPLAY_ICE_SERVERS == [
{"urls": "stun:stun.relay.metered.ca:80"},
{
"urls": "turn:global.relay.metered.ca:80",
"username": "user",
"credential": "password",
},
]
assert loader.config.EJS_SETTINGS == {
"parallel_n64": {"vsync": "disabled"},
"snes9x": {"snes9x_region": "ntsc"},
}
assert loader.config.EJS_CONTROLS == {
"snes9x": {
"_0": {0: {"value": "x", "value2": "BUTTON_2"}},
"_1": {},
"_2": {},
"_3": {},
},
}
assert loader.config.SCAN_METADATA_PRIORITY == ["ss", "launchbox"]
assert loader.config.SCAN_ARTWORK_PRIORITY == ["igdb", "ss"]
assert loader.config.SCAN_REGION_PRIORITY == ["jp", "eu", "wor"]
assert loader.config.SCAN_LANGUAGE_PRIORITY == ["jp", "es"]
assert loader.config.GAMELIST_MEDIA_THUMBNAIL == "box3d"
assert loader.config.GAMELIST_MEDIA_IMAGE == "title_screen"
def test_empty_config_loader():
loader = ConfigManager(
os.path.join(
Path(__file__).resolve().parent, "fixtures", "config/empty_config.yml"
)
)
assert loader.config.EXCLUDED_PLATFORMS == sorted(DEFAULT_EXCLUDED_DIRS)
assert loader.config.EXCLUDED_SINGLE_EXT == sorted(
{e.lower() for e in DEFAULT_EXCLUDED_EXTENSIONS}
)
assert loader.config.EXCLUDED_SINGLE_FILES == sorted(DEFAULT_EXCLUDED_FILES)
assert loader.config.EXCLUDED_MULTI_FILES == sorted(DEFAULT_EXCLUDED_DIRS)
assert loader.config.EXCLUDED_MULTI_PARTS_EXT == sorted(
{e.lower() for e in DEFAULT_EXCLUDED_EXTENSIONS}
)
assert loader.config.EXCLUDED_MULTI_PARTS_FILES == sorted(DEFAULT_EXCLUDED_FILES)
assert loader.config.PLATFORMS_BINDING == {}
assert loader.config.PLATFORMS_VERSIONS == {}
assert loader.config.ROMS_FOLDER_NAME == "roms"
assert loader.config.FIRMWARE_FOLDER_NAME == "bios"
assert not loader.config.SKIP_HASH_CALCULATION
assert not loader.config.EJS_DEBUG
assert loader.config.EJS_CACHE_LIMIT is None
assert not loader.config.EJS_DISABLE_AUTO_UNLOAD
assert not loader.config.EJS_DISABLE_BATCH_BOOTUP
assert not loader.config.EJS_NETPLAY_ENABLED
assert loader.config.EJS_NETPLAY_ICE_SERVERS == []
assert loader.config.EJS_SETTINGS == {}
assert loader.config.EJS_CONTROLS == {}
assert loader.config.GAMELIST_MEDIA_THUMBNAIL == "box2d"
assert loader.config.GAMELIST_MEDIA_IMAGE == "screenshot"
def test_missing_config_file_is_created(tmp_path):
config_file = tmp_path / "config" / "config.yml"
loader = ConfigManager(str(config_file))
assert config_file.parent.exists()
assert config_file.exists()
assert config_file.read_text() == ""
assert loader.config.CONFIG_FILE_MOUNTED
assert loader.config.CONFIG_FILE_WRITABLE
def test_forward_compat_unknown_values_are_tolerated():
"""A newer release may ship sample configs that reference media types
this version doesn't yet recognize. The loader should drop unknowns and
fall back to defaults rather than exiting."""
loader = ConfigManager(
os.path.join(
Path(__file__).resolve().parent,
"fixtures",
"config/forward_compat_config.yml",
)
)
# Unknown entries in scan.media are filtered out; known ones survive.
assert loader.config.SCAN_MEDIA == ["box2d", "screenshot"]
# Unknown thumbnail/image values fall back to their defaults.
assert loader.config.GAMELIST_MEDIA_THUMBNAIL == "box2d"
assert loader.config.GAMELIST_MEDIA_IMAGE == "screenshot"
def test_malformed_yaml_falls_back_to_defaults():
"""A YAML parse error should log critically and leave the app on
defaults, not crash."""
loader = ConfigManager(
os.path.join(
Path(__file__).resolve().parent,
"fixtures",
"config/malformed_config.yml",
)
)
assert loader.config.ROMS_FOLDER_NAME == "roms"
assert loader.config.FIRMWARE_FOLDER_NAME == "bios"
assert loader.config.SCAN_MEDIA == ["box2d", "screenshot", "manual"]