1414from common .schema import Settings , Configuration , Database , Model , OracleCloudSettings
1515
1616
17- class TestSettings :
18- """Test settings module functionality"""
19-
20- def __init__ (self ):
21- """Setup test data for all tests"""
22- self .default_settings = Settings (client = "default" )
23- self .test_client_settings = Settings (client = "test_client" )
24- self .sample_config_data = {
25- "database_configs" : [{"name" : "test_db" , "user" : "user" , "password" : "pass" , "dsn" : "dsn" }],
26- "model_configs" : [{"id" : "test-model" , "provider" : "openai" , "type" : "ll" }],
27- "oci_configs" : [{"auth_profile" : "DEFAULT" , "compartment_id" : "ocid1.compartment.oc1..test" }],
28- "prompt_overrides" : {"optimizer_basic-default" : "You are helpful" },
29- "client_settings" : {"client" : "default" , "max_tokens" : 1000 , "temperature" : 0.7 },
30- }
17+ #####################################################
18+ # Helper functions for test data
19+ #####################################################
20+ def make_default_settings ():
21+ """Create default settings for tests"""
22+ return Settings (client = "default" )
23+
24+
25+ def make_test_client_settings ():
26+ """Create test client settings for tests"""
27+ return Settings (client = "test_client" )
28+
29+
30+ def make_sample_config_data ():
31+ """Create sample configuration data for tests"""
32+ return {
33+ "database_configs" : [{"name" : "test_db" , "user" : "user" , "password" : "pass" , "dsn" : "dsn" }],
34+ "model_configs" : [{"id" : "test-model" , "provider" : "openai" , "type" : "ll" }],
35+ "oci_configs" : [{"auth_profile" : "DEFAULT" , "compartment_id" : "ocid1.compartment.oc1..test" }],
36+ "prompt_overrides" : {"optimizer_basic-default" : "You are helpful" },
37+ "client_settings" : {"client" : "default" , "max_tokens" : 1000 , "temperature" : 0.7 },
38+ }
39+
40+
41+ #####################################################
42+ # Client Settings Tests
43+ #####################################################
44+ class TestClientSettings :
45+ """Test client settings CRUD operations"""
3146
3247 @patch ("server.api.utils.settings.bootstrap" )
3348 def test_create_client_success (self , mock_bootstrap ):
3449 """Test successful client settings creation"""
35- # Create a list that includes the default settings and will be appended to
36- settings_list = [self . default_settings ]
50+ default_cfg = make_default_settings ()
51+ settings_list = [default_cfg ]
3752 mock_bootstrap .SETTINGS_OBJECTS = settings_list
3853
3954 result = settings .create_client ("new_client" )
4055
4156 assert result .client == "new_client"
42- assert result .ll_model .max_tokens == self .default_settings .ll_model .max_tokens # pylint: disable=no-member
43- # Check that a new client was added to the list
57+ # Verify ll_model settings are copied from default
58+ result_ll_model = result .model_dump ()["ll_model" ]
59+ default_ll_model = default_cfg .model_dump ()["ll_model" ]
60+ assert result_ll_model ["max_tokens" ] == default_ll_model ["max_tokens" ]
4461 assert len (settings_list ) == 2
4562 assert settings_list [- 1 ].client == "new_client"
4663
4764 @patch ("server.api.utils.settings.bootstrap.SETTINGS_OBJECTS" )
4865 def test_create_client_already_exists (self , mock_settings_objects ):
4966 """Test creating client settings when client already exists"""
50- mock_settings_objects .__iter__ = MagicMock (return_value = iter ([self .test_client_settings ]))
67+ test_cfg = make_test_client_settings ()
68+ mock_settings_objects .__iter__ = MagicMock (return_value = iter ([test_cfg ]))
5169
5270 with pytest .raises (ValueError , match = "client test_client already exists" ):
5371 settings .create_client ("test_client" )
5472
5573 @patch ("server.api.utils.settings.bootstrap.SETTINGS_OBJECTS" )
5674 def test_get_client_found (self , mock_settings_objects ):
5775 """Test getting client settings when client exists"""
58- mock_settings_objects .__iter__ = MagicMock (return_value = iter ([self .test_client_settings ]))
76+ test_cfg = make_test_client_settings ()
77+ mock_settings_objects .__iter__ = MagicMock (return_value = iter ([test_cfg ]))
5978
6079 result = settings .get_client ("test_client" )
6180
62- assert result == self . test_client_settings
81+ assert result == test_cfg
6382
6483 @patch ("server.api.utils.settings.bootstrap.SETTINGS_OBJECTS" )
6584 def test_get_client_not_found (self , mock_settings_objects ):
6685 """Test getting client settings when client doesn't exist"""
67- mock_settings_objects .__iter__ = MagicMock (return_value = iter ([self .default_settings ]))
86+ default_cfg = make_default_settings ()
87+ mock_settings_objects .__iter__ = MagicMock (return_value = iter ([default_cfg ]))
6888
6989 with pytest .raises (ValueError , match = "client nonexistent not found" ):
7090 settings .get_client ("nonexistent" )
7191
92+ @patch ("server.api.utils.settings.bootstrap.SETTINGS_OBJECTS" )
93+ @patch ("server.api.utils.settings.get_client" )
94+ def test_update_client (self , mock_get_settings , mock_settings_objects ):
95+ """Test updating client settings"""
96+ test_cfg = make_test_client_settings ()
97+ mock_get_settings .return_value = test_cfg
98+ mock_settings_objects .remove = MagicMock ()
99+ mock_settings_objects .append = MagicMock ()
100+ mock_settings_objects .__iter__ = MagicMock (return_value = iter ([test_cfg ]))
101+
102+ new_settings = Settings (client = "test_client" , max_tokens = 800 , temperature = 0.9 )
103+ result = settings .update_client (new_settings , "test_client" )
104+
105+ assert result .client == "test_client"
106+ mock_settings_objects .remove .assert_called_once_with (test_cfg )
107+ mock_settings_objects .append .assert_called_once ()
108+
109+
110+ #####################################################
111+ # Server Configuration Tests
112+ #####################################################
113+ class TestServerConfiguration :
114+ """Test server configuration operations"""
115+
72116 @pytest .mark .asyncio
73117 @patch ("server.api.utils.settings.get_mcp_prompts_with_overrides" )
74118 @patch ("server.api.utils.settings.bootstrap.DATABASE_OBJECTS" )
@@ -81,7 +125,7 @@ async def test_get_server(self, mock_oci, mock_models, mock_databases, mock_get_
81125 )
82126 mock_models .__iter__ = MagicMock (return_value = iter ([Model (id = "test" , provider = "openai" , type = "ll" )]))
83127 mock_oci .__iter__ = MagicMock (return_value = iter ([OracleCloudSettings (auth_profile = "DEFAULT" )]))
84- mock_get_prompts .return_value = [] # Return empty list of prompts
128+ mock_get_prompts .return_value = []
85129
86130 mock_mcp_engine = MagicMock ()
87131 result = await settings .get_server (mock_mcp_engine )
@@ -91,54 +135,79 @@ async def test_get_server(self, mock_oci, mock_models, mock_databases, mock_get_
91135 assert "oci_configs" in result
92136 assert "prompt_configs" in result
93137
94- @patch ("server.api.utils.settings.bootstrap.SETTINGS_OBJECTS" )
95- @patch ("server.api.utils.settings.get_client" )
96- def test_update_client (self , mock_get_settings , mock_settings_objects ):
97- """Test updating client settings"""
98- mock_get_settings .return_value = self .test_client_settings
99- mock_settings_objects .remove = MagicMock ()
100- mock_settings_objects .append = MagicMock ()
101- mock_settings_objects .__iter__ = MagicMock (return_value = iter ([self .test_client_settings ]))
102-
103- new_settings = Settings (client = "test_client" , max_tokens = 800 , temperature = 0.9 )
104- result = settings .update_client (new_settings , "test_client" )
105-
106- assert result .client == "test_client"
107- mock_settings_objects .remove .assert_called_once_with (self .test_client_settings )
108- mock_settings_objects .append .assert_called_once ()
109-
110138 @patch ("server.api.utils.settings.bootstrap" )
111139 def test_update_server (self , mock_bootstrap ):
112140 """Test updating server configuration"""
113- # Use the valid sample config data that includes client_settings
114- settings .update_server (self .sample_config_data )
141+ mock_bootstrap .DATABASE_OBJECTS = []
142+ mock_bootstrap .MODEL_OBJECTS = []
143+ mock_bootstrap .OCI_OBJECTS = []
144+
145+ settings .update_server (make_sample_config_data ())
115146
116147 assert hasattr (mock_bootstrap , "DATABASE_OBJECTS" )
117148 assert hasattr (mock_bootstrap , "MODEL_OBJECTS" )
118149
150+ @patch ("server.api.utils.settings.bootstrap" )
151+ def test_update_server_mutates_lists_not_replaces (self , mock_bootstrap ):
152+ """Test that update_server mutates existing lists rather than replacing them.
153+
154+ This is critical because other modules import these lists directly
155+ (e.g., `from server.bootstrap.bootstrap import DATABASE_OBJECTS`).
156+ If we replace the list, those modules would hold stale references.
157+ """
158+ original_db_list = []
159+ original_model_list = []
160+ original_oci_list = []
161+
162+ mock_bootstrap .DATABASE_OBJECTS = original_db_list
163+ mock_bootstrap .MODEL_OBJECTS = original_model_list
164+ mock_bootstrap .OCI_OBJECTS = original_oci_list
165+
166+ settings .update_server (make_sample_config_data ())
167+
168+ # Verify the lists are the SAME objects (mutated, not replaced)
169+ assert mock_bootstrap .DATABASE_OBJECTS is original_db_list , "DATABASE_OBJECTS was replaced instead of mutated"
170+ assert mock_bootstrap .MODEL_OBJECTS is original_model_list , "MODEL_OBJECTS was replaced instead of mutated"
171+ assert mock_bootstrap .OCI_OBJECTS is original_oci_list , "OCI_OBJECTS was replaced instead of mutated"
172+
173+ # Verify the lists now contain the new data
174+ assert len (original_db_list ) == 1
175+ assert original_db_list [0 ].name == "test_db"
176+ assert len (original_model_list ) == 1
177+ assert original_model_list [0 ].id == "test-model"
178+ assert len (original_oci_list ) == 1
179+ assert original_oci_list [0 ].auth_profile == "DEFAULT"
180+
181+
182+ #####################################################
183+ # Config Loading Tests
184+ #####################################################
185+ class TestConfigLoading :
186+ """Test configuration loading operations"""
187+
119188 @patch ("server.api.utils.settings.update_server" )
120189 @patch ("server.api.utils.settings.update_client" )
121190 def test_load_config_from_json_data_with_client (self , mock_update_client , mock_update_server ):
122191 """Test loading config from JSON data with specific client"""
123- settings .load_config_from_json_data (self .sample_config_data , client = "test_client" )
192+ config_data = make_sample_config_data ()
193+ settings .load_config_from_json_data (config_data , client = "test_client" )
124194
125- mock_update_server .assert_called_once_with (self . sample_config_data )
195+ mock_update_server .assert_called_once_with (config_data )
126196 mock_update_client .assert_called_once ()
127197
128198 @patch ("server.api.utils.settings.update_server" )
129199 @patch ("server.api.utils.settings.update_client" )
130200 def test_load_config_from_json_data_without_client (self , mock_update_client , mock_update_server ):
131201 """Test loading config from JSON data without specific client"""
132- settings .load_config_from_json_data (self .sample_config_data )
202+ config_data = make_sample_config_data ()
203+ settings .load_config_from_json_data (config_data )
133204
134- mock_update_server .assert_called_once_with (self .sample_config_data )
135- # Should be called twice: once for "server" and once for "default"
205+ mock_update_server .assert_called_once_with (config_data )
136206 assert mock_update_client .call_count == 2
137207
138208 @patch ("server.api.utils.settings.update_server" )
139209 def test_load_config_from_json_data_missing_client_settings (self , _mock_update_server ):
140210 """Test loading config from JSON data without client_settings"""
141- # Create config without client_settings
142211 invalid_config = {"database_configs" : [], "model_configs" : [], "oci_configs" : [], "prompt_configs" : []}
143212
144213 with pytest .raises (KeyError , match = "Missing client_settings in config file" ):
@@ -153,7 +222,7 @@ def test_read_config_from_json_file_success(self, mock_json_load, mock_access, m
153222 """Test successful reading of config file"""
154223 mock_isfile .return_value = True
155224 mock_access .return_value = True
156- mock_json_load .return_value = self . sample_config_data
225+ mock_json_load .return_value = make_sample_config_data ()
157226
158227 result = settings .read_config_from_json_file ()
159228
@@ -166,19 +235,22 @@ def test_read_config_from_json_file_not_exists(self, mock_isfile):
166235 """Test reading config file that doesn't exist"""
167236 mock_isfile .return_value = False
168237
169- # This should still attempt to process, but will log a warning
170- # The actual behavior depends on the implementation
171-
172238 @patch .dict (os .environ , {"CONFIG_FILE" : "/path/to/config.txt" })
173239 def test_read_config_from_json_file_wrong_extension (self ):
174240 """Test reading config file with wrong extension"""
175- # This should log a warning about the file extension
176241
177242 def test_logger_exists (self ):
178243 """Test that logger is properly configured"""
179244 assert hasattr (settings , "logger" )
180245 assert settings .logger .name == "api.core.settings"
181246
247+
248+ #####################################################
249+ # Prompt Override Tests
250+ #####################################################
251+ class TestPromptOverrides :
252+ """Test prompt override operations"""
253+
182254 @patch ("server.api.utils.settings.cache" )
183255 def test_load_prompt_override_with_text (self , mock_cache ):
184256 """Test loading prompt override when text is provided"""
0 commit comments