diff --git a/changelog.d/19987.bugfix b/changelog.d/19987.bugfix new file mode 100644 index 0000000000..73f97c1f7d --- /dev/null +++ b/changelog.d/19987.bugfix @@ -0,0 +1 @@ +Fix config regression around falsy `experimental_features` no longer being accepted. diff --git a/synapse/config/server.py b/synapse/config/server.py index 832183f32b..16479170d0 100644 --- a/synapse/config/server.py +++ b/synapse/config/server.py @@ -966,9 +966,10 @@ class ServerConfig(Config): # The maximum number of delayed events a user may have scheduled at a time. # (Defined here despite being experimental to be near the other MSC4140 config) - self.max_delayed_events_per_user: int = config.get( - "experimental_features", {} - ).get("msc4140_max_delayed_events_per_user", 100) + experimental = config.get("experimental_features") or {} + self.max_delayed_events_per_user: int = experimental.get( + "msc4140_max_delayed_events_per_user", 100 + ) if ( not isinstance(self.max_delayed_events_per_user, int) or self.max_delayed_events_per_user < 0 diff --git a/tests/config/test_experimental.py b/tests/config/test_experimental.py new file mode 100644 index 0000000000..0fede98ee9 --- /dev/null +++ b/tests/config/test_experimental.py @@ -0,0 +1,71 @@ +# +# This file is licensed under the Affero General Public License (AGPL) version 3. +# +# Copyright (C) 2026 Element Creations Ltd. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# See the GNU Affero General Public License for more details: +# . + + +import yaml +from parameterized import parameterized + +from synapse.config._base import RootConfig +from synapse.config.experimental import ExperimentalConfig +from synapse.config.homeserver import HomeServerConfig +from synapse.types import JsonDict + +from tests import unittest + + +class ExperimentalConfigTestCase(unittest.TestCase): + @parameterized.expand( + [ + [ + "single", + { + "experimental_features": { + "msc3575_enabled": True, + } + }, + ], + [ + "multi", + { + "experimental_features": { + "msc3575_enabled": True, + "msc3030_enabled": True, + } + }, + ], + # This has historically worked and this is being added as a regression test + ["none", {"experimental_features": None}], + ] + ) + def test_experimental_features_parsing( + self, test_description: str, config_values: JsonDict + ) -> None: + """ + Test the that `experimental_features` parses with these values + """ + + _read_config(config_values) + + +def _read_config(config_values: JsonDict) -> None: + ExperimentalConfig(RootConfig()).read_config( + yaml.safe_load( + HomeServerConfig().generate_config( + config_dir_path="CONFDIR", + data_dir_path="/data_dir_path", + server_name="che.org", + ) + ) + | config_values, + allow_secrets_in_config=False, + ) diff --git a/tests/config/test_server.py b/tests/config/test_server.py index 41ea8fb5b1..f718e20a2a 100644 --- a/tests/config/test_server.py +++ b/tests/config/test_server.py @@ -22,6 +22,7 @@ from typing import Any import yaml +from parameterized import parameterized from synapse.config._base import ConfigError, RootConfig from synapse.config.homeserver import HomeServerConfig @@ -228,6 +229,29 @@ class ServerConfigTestCase(unittest.TestCase): with self.assertRaises(ConfigError): _read_config(generate_config(disallowed_value)) + @parameterized.expand( + [ + [ + "single", + { + "experimental_features": { + "msc4140_max_delayed_events_per_user": 3, + } + }, + ], + # This has historically worked and this is being added as a regression test + ["none", {"experimental_features": None}], + ] + ) + def test_experimental_features_parsing( + self, test_description: str, config_values: JsonDict + ) -> None: + """ + Test the that `experimental_features` parses with these values + """ + + _read_config(config_values) + def _read_config(config_values: JsonDict) -> None: ServerConfig(RootConfig()).read_config(