/ tests / unit / common / test_feature_core.py
test_feature_core.py
  1  """
  2  Unit tests for the feature flag service module.
  3  
  4  Each test class is isolated: the autouse fixture resets module state and
  5  clears the OpenFeature global provider before and after every test so that
  6  state never leaks between tests or test files.
  7  
  8  Synthetic flag keys (never real production flag names) are used throughout
  9  so that tests do not couple to specific feature flag definitions.
 10  """
 11  
 12  import os
 13  import threading
 14  from unittest.mock import patch
 15  
 16  import pytest
 17  from openfeature import api as openfeature_api
 18  
 19  from solace_agent_mesh.common.features.provider import SamFeatureProvider
 20  
 21  from sam_test_infrastructure.feature_flags import mock_flags
 22  from solace_agent_mesh.common.features.core import (
 23      _reset_for_testing,
 24      get_registry,
 25      has_env_override,
 26      initialize,
 27      is_known_flag,
 28      load_flags_from_yaml,
 29  )
 30  import solace_agent_mesh.common.features.core as feature_core_module
 31  
 32  
 33  @pytest.fixture(autouse=True)
 34  def _reset():
 35      _reset_for_testing()
 36      yield
 37      _reset_for_testing()
 38  
 39  
 40  class TestLazyInitialization:
 41      """Module does not initialize on import; first use triggers init."""
 42  
 43      def test_not_initialized_at_start(self):
 44          assert feature_core_module._initialized is False
 45          assert feature_core_module._checker is None
 46  
 47      def test_is_known_flag_triggers_init(self):
 48          is_known_flag("any_key")
 49          assert feature_core_module._initialized is True
 50  
 51      def test_get_registry_triggers_init(self):
 52          get_registry()
 53          assert feature_core_module._initialized is True
 54  
 55  
 56  class TestIdempotentInitialization:
 57      """initialize() is safe to call multiple times and from multiple threads."""
 58  
 59      def test_double_initialize_reuses_checker(self):
 60          initialize()
 61          checker_first = feature_core_module._checker
 62          initialize()
 63          assert feature_core_module._checker is checker_first
 64  
 65      def test_concurrent_initialization_no_errors(self):
 66          errors = []
 67  
 68          def _init():
 69              try:
 70                  initialize()
 71              except Exception as exc:  # pylint: disable=broad-except
 72                  errors.append(exc)
 73  
 74          threads = [threading.Thread(target=_init) for _ in range(10)]
 75          for t in threads:
 76              t.start()
 77          for t in threads:
 78              t.join()
 79  
 80          assert not errors
 81          assert feature_core_module._initialized is True
 82  
 83      def test_concurrent_init_produces_single_checker(self):
 84          checkers = []
 85  
 86          def _capture():
 87              initialize()
 88              checkers.append(feature_core_module._checker)
 89  
 90          threads = [threading.Thread(target=_capture) for _ in range(10)]
 91          for t in threads:
 92              t.start()
 93          for t in threads:
 94              t.join()
 95  
 96          assert all(c is checkers[0] for c in checkers)
 97  
 98  
 99  class TestCommunityYamlLoading:
100      """Community features.yaml loads cleanly and produces a valid registry."""
101  
102      def test_community_yaml_loads_without_error(self):
103          initialize()
104          flags = get_registry().all()
105          assert len(flags) > 0
106          for defn in flags:
107              assert defn.key != ""
108              assert defn.jira != ""
109  
110      def test_unknown_flag_returns_false(self):
111          initialize()
112          assert openfeature_api.get_client().get_boolean_value("__nonexistent_flag_xyz__", False) is False
113  
114  
115  class TestLoadFlagsFromYaml:
116      """load_flags_from_yaml() merges additional definitions without removing existing ones."""
117  
118      def test_extra_yaml_merged_preserves_community_flags(self, tmp_path):
119          extra_yaml = tmp_path / "extra.yaml"
120          extra_yaml.write_text(
121              "features:\n"
122              "  - key: test_alpha_flag\n"
123              "    name: Test Alpha\n"
124              "    release_phase: experimental\n"
125              "    default: true\n"
126              "    jira: DATAGO-99999\n"
127          )
128  
129          initialize()
130          community_count = len(get_registry().all())
131          load_flags_from_yaml(str(extra_yaml))
132  
133          client = openfeature_api.get_client()
134          assert is_known_flag("test_alpha_flag")
135          assert client.get_boolean_value("test_alpha_flag", False) is True
136          assert len(get_registry().all()) == community_count + 1
137  
138      def test_extra_yaml_with_multiple_flags(self, tmp_path):
139          extra_yaml = tmp_path / "extra.yaml"
140          extra_yaml.write_text(
141              "features:\n"
142              "  - key: test_beta_flag\n"
143              "    name: Test Beta\n"
144              "    release_phase: beta\n"
145              "    default: false\n"
146              "    jira: DATAGO-99999\n"
147              "  - key: test_gamma_flag\n"
148              "    name: Test Gamma\n"
149              "    release_phase: early_access\n"
150              "    default: true\n"
151              "    jira: DATAGO-99999\n"
152          )
153  
154          initialize()
155          load_flags_from_yaml(str(extra_yaml))
156  
157          client = openfeature_api.get_client()
158          assert is_known_flag("test_beta_flag")
159          assert is_known_flag("test_gamma_flag")
160          assert client.get_boolean_value("test_beta_flag", False) is False
161          assert client.get_boolean_value("test_gamma_flag", False) is True
162  
163  
164  class TestOpenFeatureIntegration:
165      """OpenFeature provider is correctly registered and evaluation goes through it."""
166  
167      def test_provider_is_sam_feature_provider_after_init(self):
168          initialize()
169          provider = openfeature_api.provider_registry.get_default_provider()
170          assert isinstance(provider, SamFeatureProvider)
171  
172      def test_openfeature_client_evaluates_registered_flag(self, tmp_path):
173          extra_yaml = tmp_path / "extra.yaml"
174          extra_yaml.write_text(
175              "features:\n"
176              "  - key: test_of_flag\n"
177              "    name: Test OF Flag\n"
178              "    release_phase: general_availability\n"
179              "    default: true\n"
180              "    jira: DATAGO-99999\n"
181          )
182          initialize()
183          load_flags_from_yaml(str(extra_yaml))
184  
185          assert openfeature_api.get_client().get_boolean_value("test_of_flag", False) is True
186  
187  
188  class TestEnvOverride:
189      """has_env_override() and env var evaluation via the OpenFeature path."""
190  
191      @pytest.fixture()
192      def _registered_key(self, tmp_path):
193          yaml_path = tmp_path / "test_flags.yaml"
194          yaml_path.write_text(
195              "features:\n"
196              "  - key: test_env_flag\n"
197              "    name: Test Env Flag\n"
198              "    release_phase: general_availability\n"
199              "    default: false\n"
200              "    jira: DATAGO-99999\n"
201          )
202          initialize()
203          load_flags_from_yaml(str(yaml_path))
204          return "test_env_flag"
205  
206      def test_no_env_var_returns_false(self, _registered_key):
207          assert has_env_override(_registered_key) is False
208  
209      def test_env_var_present_returns_true(self, _registered_key):
210          with patch.dict(os.environ, {"SAM_FEATURE_TEST_ENV_FLAG": "true"}):
211              assert has_env_override(_registered_key) is True
212  
213      def test_env_var_overrides_default_to_enabled(self, _registered_key):
214          client = openfeature_api.get_client()
215          with patch.dict(os.environ, {"SAM_FEATURE_TEST_ENV_FLAG": "true"}):
216              assert client.get_boolean_value(_registered_key, False) is True
217  
218      def test_flag_disabled_without_env_var(self, _registered_key):
219          assert openfeature_api.get_client().get_boolean_value(_registered_key, False) is False
220  
221  
222  class TestEnterpriseIntegration:
223      """initialize() loads enterprise flags when the enterprise package is available."""
224  
225      def test_enterprise_flags_loaded_during_initialize(self, monkeypatch, tmp_path):
226          import sys
227          import types
228  
229          yaml_path = tmp_path / "enterprise.yaml"
230          yaml_path.write_text(
231              "features:\n"
232              "  - key: test_enterprise_flag\n"
233              "    name: Test Enterprise Flag\n"
234              "    release_phase: early_access\n"
235              "    default: false\n"
236              "    jira: DATAGO-99999\n"
237          )
238  
239          def _fake_register():
240              load_flags_from_yaml(str(yaml_path))
241  
242          fake_module = types.SimpleNamespace(
243              _register_enterprise_feature_flags=_fake_register
244          )
245          monkeypatch.setitem(
246              sys.modules,
247              "solace_agent_mesh_enterprise.init_enterprise",
248              fake_module,
249          )
250  
251          initialize()
252  
253          assert is_known_flag("test_enterprise_flag")
254  
255      def test_enterprise_flags_registered_after_provider(self, monkeypatch, tmp_path):
256          import sys
257          import types
258          from openfeature import api as openfeature_api
259          from solace_agent_mesh.common.features.provider import SamFeatureProvider
260  
261          provider_at_call_time = []
262  
263          def _fake_register():
264              provider_at_call_time.append(
265                  openfeature_api.provider_registry.get_default_provider()
266              )
267  
268          fake_module = types.SimpleNamespace(
269              _register_enterprise_feature_flags=_fake_register
270          )
271          monkeypatch.setitem(
272              sys.modules,
273              "solace_agent_mesh_enterprise.init_enterprise",
274              fake_module,
275          )
276  
277          initialize()
278  
279          assert len(provider_at_call_time) == 1
280          assert isinstance(provider_at_call_time[0], SamFeatureProvider)
281  
282      def test_initialize_succeeds_when_enterprise_not_installed(self, monkeypatch):
283          import sys
284  
285          monkeypatch.setitem(
286              sys.modules, "solace_agent_mesh_enterprise.init_enterprise", None
287          )
288  
289          initialize()
290  
291          assert feature_core_module._initialized is True
292          assert len(get_registry().all()) > 0
293  
294  
295  class TestResetForTesting:
296      """_reset_for_testing() clears module state and allows full re-initialisation."""
297  
298      def test_reset_clears_state(self):
299          initialize()
300          _reset_for_testing()
301          assert feature_core_module._initialized is False
302          assert feature_core_module._checker is None
303  
304      def test_reset_clears_openfeature_provider(self):
305          initialize()
306          _reset_for_testing()
307          provider = openfeature_api.provider_registry.get_default_provider()
308          assert not isinstance(provider, SamFeatureProvider)
309  
310      def test_can_reinitialize_after_reset(self):
311          initialize()
312          _reset_for_testing()
313          initialize()
314          assert feature_core_module._initialized is True
315          assert len(get_registry().all()) > 0
316  
317  
318  class TestMockFlags:
319      """mock_flags() context manager sets env-var overrides for the test scope."""
320  
321      def _register_flag(self, tmp_path, key: str, default: bool):
322          """Write a single-flag YAML and load it into the initialised registry."""
323          yaml_path = tmp_path / f"{key}.yaml"
324          yaml_path.write_text(
325              "features:\n"
326              f"  - key: {key}\n"
327              f"    name: Test {key}\n"
328              "    release_phase: beta\n"
329              f"    default: {str(default).lower()}\n"
330              "    jira: DATAGO-99999\n"
331          )
332          initialize()
333          load_flags_from_yaml(str(yaml_path))
334  
335      def test_false_default_overridden_to_true(self, tmp_path):
336          self._register_flag(tmp_path, "test_mock_flag_a", False)
337          client = openfeature_api.get_client()
338  
339          assert client.get_boolean_value("test_mock_flag_a", False) is False
340          with mock_flags(test_mock_flag_a=True):
341              assert client.get_boolean_value("test_mock_flag_a", False) is True
342          assert client.get_boolean_value("test_mock_flag_a", False) is False
343  
344      def test_true_default_overridden_to_false(self, tmp_path):
345          self._register_flag(tmp_path, "test_mock_flag_b", True)
346          client = openfeature_api.get_client()
347  
348          assert client.get_boolean_value("test_mock_flag_b", True) is True
349          with mock_flags(test_mock_flag_b=False):
350              assert client.get_boolean_value("test_mock_flag_b", True) is False
351          assert client.get_boolean_value("test_mock_flag_b", True) is True
352  
353      def test_unspecified_flags_unaffected(self, tmp_path):
354          self._register_flag(tmp_path, "test_mock_flag_c", False)
355          self._register_flag(tmp_path, "test_mock_flag_d", False)
356          client = openfeature_api.get_client()
357  
358          with mock_flags(test_mock_flag_c=True):
359              assert client.get_boolean_value("test_mock_flag_c", False) is True
360              assert client.get_boolean_value("test_mock_flag_d", False) is False
361  
362      def test_env_var_absent_after_context_exits(self, tmp_path):
363          self._register_flag(tmp_path, "test_mock_flag_e", False)
364          env_key = "SAM_FEATURE_TEST_MOCK_FLAG_E"
365  
366          assert env_key not in os.environ
367          with mock_flags(test_mock_flag_e=True):
368              assert os.environ.get(env_key) == "true"
369          assert env_key not in os.environ
370  
371      def test_pre_existing_env_var_restored_after_context(self, tmp_path):
372          self._register_flag(tmp_path, "test_mock_flag_f", False)
373          env_key = "SAM_FEATURE_TEST_MOCK_FLAG_F"
374  
375          os.environ[env_key] = "true"
376          try:
377              with mock_flags(test_mock_flag_f=False):
378                  assert os.environ.get(env_key) == "false"
379              assert os.environ.get(env_key) == "true"
380          finally:
381              os.environ.pop(env_key, None)
382  
383      def test_multiple_flags_controlled_independently(self, tmp_path):
384          self._register_flag(tmp_path, "test_mock_flag_g", False)
385          self._register_flag(tmp_path, "test_mock_flag_h", True)
386          client = openfeature_api.get_client()
387  
388          with mock_flags(test_mock_flag_g=True, test_mock_flag_h=False):
389              assert client.get_boolean_value("test_mock_flag_g", False) is True
390              assert client.get_boolean_value("test_mock_flag_h", True) is False