@@ -925,3 +925,85 @@ def side_effect(config_key, tenant_id=None):
925925 service_mocks ['logger' ].warning .assert_called_with (
926926 "Failed to get config for EMBEDDING_ID: Database timeout"
927927 )
928+
929+
930+ @pytest .mark .asyncio
931+ async def test_save_config_impl_persist_model_engine_key_failure (service_mocks ):
932+ """When persisting MODEL_ENGINE_API_KEY fails, a warning should be logged but function should continue."""
933+ config = MagicMock ()
934+ # Provide a simple config dict that includes modelengine apiKey
935+ config .model_dump .return_value = {"app" : {}, "models" : {}, "modelengine" : {"apiKey" : "me-key" }}
936+
937+ tenant_id = "test_tenant_id"
938+ user_id = "test_user_id"
939+
940+ # tenant_config_manager.load_config should return something reasonable
941+ service_mocks ['tenant_config_manager' ].load_config .return_value = {}
942+
943+ # Make set_single_config raise when persisting MODEL_ENGINE_API_KEY
944+ service_mocks ['tenant_config_manager' ].set_single_config .side_effect = Exception ("persist failed" )
945+
946+ # Execute
947+ result = await save_config_impl (config , tenant_id , user_id )
948+
949+ # Function should not raise; it should log a warning
950+ assert result is None
951+ service_mocks ['logger' ].warning .assert_called ()
952+
953+
954+ @pytest .mark .asyncio
955+ async def test_save_config_impl_modelengine_deletion_paths (service_mocks ):
956+ """When MODEL_ENGINE_API_KEY is empty, deletion logic should attempt to remove records and handle failures."""
957+ config = MagicMock ()
958+ # modelengine apiKey empty -> triggers deletion flow
959+ config .model_dump .return_value = {"app" : {}, "models" : {}, "modelengine" : {"apiKey" : "" }}
960+
961+ tenant_id = "test_tenant_id"
962+ user_id = "test_user_id"
963+
964+ # tenant config load
965+ service_mocks ['tenant_config_manager' ].load_config .return_value = {}
966+
967+ # Case A: get_model_records returns models where one has no id (should be skipped) and one has id which delete_model_record will raise on
968+ from backend .services import config_sync_service
969+
970+ async def _run_case_a ():
971+ with patch ('backend.services.config_sync_service.get_model_records' , return_value = [{"model_id" : None }, {"model_id" : "7" }]), \
972+ patch ('backend.services.config_sync_service.delete_model_record' , side_effect = Exception ("delete failed" )):
973+ res = await save_config_impl (config , tenant_id , user_id )
974+ assert res is None
975+ # A warning should be logged for the failed delete
976+ service_mocks ['logger' ].warning .assert_called ()
977+
978+ await _run_case_a ()
979+
980+ # Case B: get_model_records itself raises -> should be caught and logged
981+ async def _run_case_b ():
982+ with patch ('backend.services.config_sync_service.get_model_records' , side_effect = Exception ("query failed" )):
983+ res = await save_config_impl (config , tenant_id , user_id )
984+ assert res is None
985+ service_mocks ['logger' ].warning .assert_called ()
986+
987+ await _run_case_b ()
988+
989+
990+ def test_build_model_config_embedding_direct (service_mocks ):
991+ """Direct test of build_model_config to ensure embedding dimension is included when model_type contains 'embedding'."""
992+ from backend .services .config_sync_service import build_model_config
993+
994+ model_config = {
995+ "display_name" : "Emb" ,
996+ "model_type" : "embedding" ,
997+ "max_tokens" : 2048 ,
998+ "base_url" : "http://test" ,
999+ "api_key" : "k"
1000+ }
1001+
1002+ # get_model_name_from_config is patched by service_mocks fixture; ensure it returns a value
1003+ service_mocks ['get_model_name' ].return_value = "emb-name"
1004+
1005+ result = build_model_config (model_config )
1006+ assert result ["displayName" ] == "Emb"
1007+ assert result ["name" ] == "emb-name"
1008+ # dimension should be set when 'embedding' in model_type
1009+ assert result .get ("dimension" ) == 2048
0 commit comments