mirror of
https://github.com/ChuckBuilds/LEDMatrix.git
synced 2026-06-16 01:38:36 +00:00
The plugin config form posts form-data with dot-notation paths (e.g. "leagues.fifa.world.enabled"). _get_schema_property and _set_nested_value split those paths on every dot, so a schema key that itself contains a dot (soccer league keys like "fifa.world", "eng.1") was mistaken for nested "fifa" -> "world" objects. Per-league edits (enable, favorite_teams, nested booleans) were written to a fabricated "leagues.fifa.world" branch while the real league object was never updated, so saves silently dropped the change and produced a byte-identical config. Both helpers now greedily match the longest path segment that exists in the schema (_get_schema_property) or the config being updated (_set_nested_value), mirroring the frontend's dotted-key handling. Adds regression tests covering schema lookup, value typing, and writes under dotted league keys, plus a guard that plain nested paths still work. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
94 lines
3.6 KiB
Python
94 lines
3.6 KiB
Python
"""
|
|
Regression test for saving plugin config fields whose schema keys contain dots
|
|
(e.g. soccer league keys like "fifa.world", "eng.1", "usa.1").
|
|
|
|
Bug: the web config form posts form-data with dotted paths such as
|
|
"leagues.fifa.world.enabled". The helpers that resolve those paths split on every
|
|
dot, so the dotted league key "fifa.world" was mistaken for nested "fifa" ->
|
|
"world" objects. Per-league edits (enable, favorite_teams, nested booleans) were
|
|
written to a fabricated "leagues.fifa.world" branch while the real league object
|
|
was never updated, so the save silently dropped the change and the saved config
|
|
came out byte-identical.
|
|
"""
|
|
|
|
import unittest
|
|
|
|
from web_interface.blueprints.api_v3 import (
|
|
_get_schema_property,
|
|
_set_nested_value,
|
|
_parse_form_value_with_schema,
|
|
)
|
|
|
|
|
|
SCHEMA = {
|
|
"type": "object",
|
|
"properties": {
|
|
"leagues": {
|
|
"type": "object",
|
|
"properties": {
|
|
"fifa.world": {
|
|
"type": "object",
|
|
"properties": {
|
|
"enabled": {"type": "boolean"},
|
|
"favorite_teams": {
|
|
"type": "array",
|
|
"items": {"type": "string"},
|
|
},
|
|
"display_modes": {
|
|
"type": "object",
|
|
"properties": {"live": {"type": "boolean"}},
|
|
},
|
|
},
|
|
}
|
|
},
|
|
}
|
|
},
|
|
}
|
|
|
|
|
|
class TestDottedLeagueKeys(unittest.TestCase):
|
|
def test_schema_lookup_resolves_dotted_league_key(self):
|
|
prop = _get_schema_property(SCHEMA, "leagues.fifa.world.favorite_teams")
|
|
self.assertIsNotNone(prop, "dotted league key path should resolve")
|
|
self.assertEqual(prop.get("type"), "array")
|
|
|
|
def test_schema_lookup_resolves_nested_object_beneath_dotted_key(self):
|
|
live = _get_schema_property(SCHEMA, "leagues.fifa.world.display_modes.live")
|
|
self.assertIsNotNone(live)
|
|
self.assertEqual(live.get("type"), "boolean")
|
|
|
|
def test_parse_typed_value_for_dotted_key(self):
|
|
# Comma-separated text input "USA" must become an array, not the raw string.
|
|
parsed = _parse_form_value_with_schema(
|
|
"USA", "leagues.fifa.world.favorite_teams", SCHEMA
|
|
)
|
|
self.assertEqual(parsed, ["USA"])
|
|
|
|
def test_set_value_updates_real_league_not_fabricated_branch(self):
|
|
config = {"leagues": {"fifa.world": {"enabled": False, "favorite_teams": []}}}
|
|
_set_nested_value(config, "leagues.fifa.world.enabled", True)
|
|
_set_nested_value(config, "leagues.fifa.world.favorite_teams", ["USA"])
|
|
|
|
self.assertTrue(config["leagues"]["fifa.world"]["enabled"])
|
|
self.assertEqual(config["leagues"]["fifa.world"]["favorite_teams"], ["USA"])
|
|
# The real league must be updated and no fabricated "fifa" branch created.
|
|
self.assertNotIn("fifa", config["leagues"])
|
|
|
|
def test_set_value_into_missing_leaf_lands_in_real_league(self):
|
|
# A leaf that does not exist yet still resolves into the real dotted league.
|
|
config = {"leagues": {"fifa.world": {"enabled": False}}}
|
|
_set_nested_value(config, "leagues.fifa.world.display_modes.live", True)
|
|
self.assertTrue(
|
|
config["leagues"]["fifa.world"]["display_modes"]["live"]
|
|
)
|
|
self.assertNotIn("fifa", config["leagues"])
|
|
|
|
def test_plain_nested_paths_still_work(self):
|
|
config = {}
|
|
_set_nested_value(config, "customization.text.font", "small")
|
|
self.assertEqual(config["customization"]["text"]["font"], "small")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|