"""Guard tests keeping the live-settings schema and the config builder in lock-step. `_live_processing_config` builds ProcessingLiveConfig by reading every widget through `_WEB_LIVE_SCHEMA` (the same map the embedded web form renders from), plus a small set of fields set explicitly (`_NON_WIDGET_LIVE_FIELDS`). These tests fail the moment a new config field is added without being placed in one of those two sets — which is exactly the drift that previously hid GPR settings from the web form. """ from __future__ import annotations import dataclasses as dc import unittest from python_app.gui.controllers.app_window_config.live_processing_mixin import ( _NON_WIDGET_LIVE_FIELDS, _WEB_DISPLAY_SCHEMA, _WEB_LIVE_GETTERS, _WEB_STABLE_SCHEMA, web_apply_field_names, ) from python_app.orchestration.live_processing_config import ProcessingLiveConfig class LiveSettingsSchemaCoverageTest(unittest.TestCase): @staticmethod def _config_fields() -> set[str]: return {field.name for field in dc.fields(ProcessingLiveConfig)} def test_schema_plus_non_widget_covers_every_config_field(self) -> None: schema_fields = set(_WEB_LIVE_GETTERS) covered = schema_fields | _NON_WIDGET_LIVE_FIELDS config_fields = self._config_fields() self.assertEqual( covered, config_fields, msg=( f"uncovered config fields: {sorted(config_fields - covered)}; " f"stray names: {sorted(covered - config_fields)}" ), ) def test_widget_and_non_widget_sets_are_disjoint(self) -> None: # A field is either widget-backed (in the schema) or explicitly non-widget — never both. self.assertEqual(set(_WEB_LIVE_GETTERS) & _NON_WIDGET_LIVE_FIELDS, set()) def test_display_and_stable_fields_are_not_live_config(self) -> None: # Display toggles (GUI profile) and stable fields (run_config) must never collide # with live ProcessingLiveConfig fields — they take different write paths. extra = {f for f, *_ in _WEB_DISPLAY_SCHEMA} | {f for f, *_ in _WEB_STABLE_SCHEMA} self.assertEqual(extra & self._config_fields(), set()) self.assertEqual(extra & set(_WEB_LIVE_GETTERS), set()) def test_web_apply_names_cover_the_whole_form(self) -> None: form_fields = ( set(_WEB_LIVE_GETTERS) | {f for f, *_ in _WEB_DISPLAY_SCHEMA} | {f for f, *_ in _WEB_STABLE_SCHEMA} ) # The web may apply every form field plus the history command, and nothing else. self.assertEqual(web_apply_field_names(), form_fields | {"history_command"}) if __name__ == "__main__": unittest.main()