@@ -1058,7 +1058,7 @@ class TestDataset:
10581058 multi_model_deployment_model_attributes = [
10591059 {
10601060 "env_var" : {"--test_key_one" : "test_value_one" },
1061- "params" : {} ,
1061+ "params" : None ,
10621062 "gpu_count" : 1 ,
10631063 "model_id" : "ocid1.compartment.oc1..<OCID>" ,
10641064 "model_name" : "model_one" ,
@@ -1068,7 +1068,7 @@ class TestDataset:
10681068 },
10691069 {
10701070 "env_var" : {"--test_key_two" : "test_value_two" },
1071- "params" : {} ,
1071+ "params" : None ,
10721072 "gpu_count" : 1 ,
10731073 "model_id" : "ocid1.compartment.oc1..<OCID>" ,
10741074 "model_name" : "model_two" ,
@@ -1078,7 +1078,7 @@ class TestDataset:
10781078 },
10791079 {
10801080 "env_var" : {"--test_key_three" : "test_value_three" },
1081- "params" : {} ,
1081+ "params" : None ,
10821082 "gpu_count" : 1 ,
10831083 "model_id" : "ocid1.compartment.oc1..<OCID>" ,
10841084 "model_name" : "model_three" ,
@@ -2952,3 +2952,202 @@ def test_from_create_model_deployment_details(self):
29522952 model_group_config_no_ft .model_dump ()
29532953 == TestDataset .multi_model_deployment_group_config_no_ft
29542954 )
2955+
2956+
2957+ # ... [Existing code in test_deployment.py] ...
2958+
2959+
2960+ class TestSingleModelParamResolution (unittest .TestCase ):
2961+ """Tests strictly for the SMM parameter resolution logic in Single Model."""
2962+
2963+ def setUp (self ):
2964+ self .app = AquaDeploymentApp ()
2965+ self .app .region = "us-ashburn-1"
2966+
2967+ # Mock internal helpers to avoid real API calls
2968+ self .app .get_container_config = MagicMock ()
2969+ self .app .get_container_image = MagicMock (return_value = "docker/image:latest" )
2970+ self .app .list_shapes = MagicMock (return_value = [MagicMock (name = "VM.GPU.A10.1" )])
2971+
2972+ # Mock the SMM Defaults (What happens if user sends nothing)
2973+ self .mock_config = MagicMock ()
2974+ # Assume default SMM config is "--default-param 100"
2975+ self .mock_config .configuration .get .return_value .parameters .get .return_value = (
2976+ "--default-param 100"
2977+ )
2978+ self .app .get_deployment_config = MagicMock (return_value = self .mock_config )
2979+
2980+ # Mock Container Defaults (The mandatory left-side params)
2981+ self .mock_container_item = MagicMock ()
2982+ self .mock_container_item .spec .cli_param = "--mandatory-param 1"
2983+ # Mock restricted params to empty list to pass validation
2984+ self .mock_container_item .spec .restricted_params = []
2985+ self .app .get_container_config_item = MagicMock (
2986+ return_value = self .mock_container_item
2987+ )
2988+
2989+ @patch ("ads.aqua.app.ModelDeployment" )
2990+ @patch ("ads.aqua.app.AquaModelApp" )
2991+ def test_case_1_none_loads_defaults (self , mock_model_app , mock_deploy ):
2992+ """Case 1: User input None -> Should load SMM defaults."""
2993+ details = CreateModelDeploymentDetails (
2994+ model_id = "ocid1.model..." ,
2995+ instance_shape = "VM.GPU.A10.1" ,
2996+ # PARAMS is missing (None)
2997+ env_var = {},
2998+ )
2999+
3000+ # Mock the internal call to capture arguments
3001+ with patch .object (self .app , "_create_deployment" ) as mock_create_internal :
3002+ self .app .create (create_deployment_details = details )
3003+
3004+ call_args = mock_create_internal .call_args [1 ]
3005+ final_params = call_args ["env_var" ]["PARAMS" ]
3006+
3007+ # Should have Mandatory + SMM Default
3008+ self .assertIn ("--mandatory-param 1" , final_params )
3009+ self .assertIn ("--default-param 100" , final_params )
3010+
3011+ @patch ("ads.aqua.app.ModelDeployment" )
3012+ @patch ("ads.aqua.app.AquaModelApp" )
3013+ def test_case_2_empty_clears_defaults (self , mock_model_app , mock_deploy ):
3014+ """Case 2: User input Empty String -> Should clear SMM defaults."""
3015+ details = CreateModelDeploymentDetails (
3016+ model_id = "ocid1.model..." ,
3017+ instance_shape = "VM.GPU.A10.1" ,
3018+ # PARAMS is explicitly empty
3019+ env_var = {"PARAMS" : "" },
3020+ )
3021+
3022+ with patch .object (self .app , "_create_deployment" ) as mock_create_internal :
3023+ self .app .create (create_deployment_details = details )
3024+
3025+ call_args = mock_create_internal .call_args [1 ]
3026+ final_params = call_args ["env_var" ]["PARAMS" ]
3027+
3028+ # Should have Mandatory ONLY
3029+ self .assertIn ("--mandatory-param 1" , final_params )
3030+ # SMM Default should be GONE
3031+ self .assertNotIn ("--default-param 100" , final_params )
3032+
3033+ @patch ("ads.aqua.app.ModelDeployment" )
3034+ @patch ("ads.aqua.app.AquaModelApp" )
3035+ def test_case_3_value_overrides_defaults (self , mock_model_app , mock_deploy ):
3036+ """Case 3: User input Value -> Should use exact value (No Merge)."""
3037+ details = CreateModelDeploymentDetails (
3038+ model_id = "ocid1.model..." ,
3039+ instance_shape = "VM.GPU.A10.1" ,
3040+ # PARAMS is a custom value
3041+ env_var = {"PARAMS" : "--user-override 99" },
3042+ )
3043+
3044+ with patch .object (self .app , "_create_deployment" ) as mock_create_internal :
3045+ self .app .create (create_deployment_details = details )
3046+
3047+ call_args = mock_create_internal .call_args [1 ]
3048+ final_params = call_args ["env_var" ]["PARAMS" ]
3049+
3050+ # Should have Mandatory + User Override
3051+ self .assertIn ("--mandatory-param 1" , final_params )
3052+ self .assertIn ("--user-override 99" , final_params )
3053+ # SMM Default should be GONE
3054+ self .assertNotIn ("--default-param 100" , final_params )
3055+
3056+ @patch ("ads.aqua.app.ModelDeployment" )
3057+ @patch ("ads.aqua.app.AquaModelApp" )
3058+ def test_validation_blocks_restricted_params (self , mock_model_app , mock_deploy ):
3059+ """Test that restricted params cause error regardless of input source."""
3060+
3061+ # Setup: Container config has restricted params
3062+ self .mock_container_item .spec .restricted_params = ["--seed" ]
3063+
3064+ # User tries to override restricted param
3065+ details = CreateModelDeploymentDetails (
3066+ model_id = "ocid1.model..." ,
3067+ instance_shape = "VM.GPU.A10.1" ,
3068+ env_var = {"PARAMS" : "--seed 999" },
3069+ )
3070+
3071+ with self .assertRaises (AquaValueError ) as context :
3072+ self .app .create (create_deployment_details = details )
3073+
3074+ self .assertIn ("Parameters ['--seed'] are set by Aqua" , str (context .exception ))
3075+
3076+
3077+ class TestMultiModelParamResolution (unittest .TestCase ):
3078+ """Tests strictly for the SMM parameter resolution logic in Multi-Model."""
3079+
3080+ def setUp (self ):
3081+ # Mock Config Summary structure
3082+ self .mock_config_summary = MagicMock ()
3083+ self .mock_deploy_config = MagicMock ()
3084+
3085+ # Set SMM Default
3086+ self .mock_deploy_config .configuration .get .return_value .parameters .get .return_value = (
3087+ "--smm-default 500"
3088+ )
3089+ self .mock_config_summary .deployment_config .get .return_value = (
3090+ self .mock_deploy_config
3091+ )
3092+
3093+ self .mock_details = MagicMock ()
3094+ self .mock_details .instance_shape = "VM.GPU.A10.2"
3095+
3096+ # Set Container Mandatory Params
3097+ self .container_params = "--mandatory 1"
3098+
3099+ def test_case_1_none_loads_defaults (self ):
3100+ """Case 1: params=None -> Load Defaults"""
3101+ model = AquaMultiModelRef (
3102+ model_id = "ocid1..." , gpu_count = 1 , params = None # User sent nothing
3103+ )
3104+
3105+ result = ModelGroupConfig ._merge_gpu_count_params (
3106+ model ,
3107+ self .mock_config_summary ,
3108+ self .mock_details ,
3109+ "container_key" ,
3110+ self .container_params ,
3111+ )
3112+
3113+ self .assertIn ("--mandatory 1" , result )
3114+ self .assertIn ("--smm-default 500" , result )
3115+
3116+ def test_case_2_empty_clears_defaults (self ):
3117+ """Case 2: params={} -> Clear Defaults"""
3118+ model = AquaMultiModelRef (
3119+ model_id = "ocid1..." , gpu_count = 1 , params = {} # User sent Empty Dict
3120+ )
3121+
3122+ result = ModelGroupConfig ._merge_gpu_count_params (
3123+ model ,
3124+ self .mock_config_summary ,
3125+ self .mock_details ,
3126+ "container_key" ,
3127+ self .container_params ,
3128+ )
3129+
3130+ self .assertIn ("--mandatory 1" , result )
3131+ # SMM Default should be missing
3132+ self .assertNotIn ("--smm-default 500" , result )
3133+
3134+ def test_case_3_value_overrides_defaults (self ):
3135+ """Case 3: params={val} -> Override Defaults"""
3136+ model = AquaMultiModelRef (
3137+ model_id = "ocid1..." ,
3138+ gpu_count = 1 ,
3139+ params = {"--custom" : "99" }, # User sent Value
3140+ )
3141+
3142+ result = ModelGroupConfig ._merge_gpu_count_params (
3143+ model ,
3144+ self .mock_config_summary ,
3145+ self .mock_details ,
3146+ "container_key" ,
3147+ self .container_params ,
3148+ )
3149+
3150+ self .assertIn ("--mandatory 1" , result )
3151+ self .assertIn ("--custom 99" , result )
3152+ # SMM Default should be missing
3153+ self .assertNotIn ("--smm-default 500" , result )
0 commit comments