diff --git a/scripts/odysseus-theme b/scripts/odysseus-theme index e434494..c4a3309 100755 --- a/scripts/odysseus-theme +++ b/scripts/odysseus-theme @@ -36,10 +36,14 @@ def _load_prefs() -> dict: return {"_users": {}} try: data = json.loads(_USER_PREFS_PATH.read_text()) - data.setdefault("_users", {}) - return data except json.JSONDecodeError as e: fail(f"user_prefs.json is corrupt: {e}") + if not isinstance(data, dict): + fail("user_prefs.json is corrupt: expected an object") + users = data.setdefault("_users", {}) + if not isinstance(users, dict): + fail("user_prefs.json is corrupt: _users must be an object") + return data def _save_prefs(data: dict) -> None: diff --git a/tests/test_theme_cli_store.py b/tests/test_theme_cli_store.py new file mode 100644 index 0000000..3e0a2d8 --- /dev/null +++ b/tests/test_theme_cli_store.py @@ -0,0 +1,29 @@ +import importlib.machinery +import importlib.util +from pathlib import Path + +import pytest + + +ROOT = Path(__file__).resolve().parents[1] + + +def _load_cli(): + path = ROOT / "scripts" / "odysseus-theme" + loader = importlib.machinery.SourceFileLoader("odysseus_theme_cli", str(path)) + spec = importlib.util.spec_from_loader(loader.name, loader) + module = importlib.util.module_from_spec(spec) + loader.exec_module(module) + return module + + +@pytest.mark.parametrize("payload", ["[]", '{"_users": []}']) +def test_load_prefs_rejects_non_object_user_store(tmp_path, capsys, payload): + cli = _load_cli() + cli._USER_PREFS_PATH = tmp_path / "user_prefs.json" + cli._USER_PREFS_PATH.write_text(payload) + + with pytest.raises(SystemExit): + cli._load_prefs() + + assert "is corrupt" in capsys.readouterr().err