2727
2828import config .main as config
2929import config .validated_config_db_connector as validated_config_db_connector
30+ from config .main import config_file_yang_validation
3031
3132# Add Test, module and script path.
3233test_path = os .path .dirname (os .path .abspath (__file__ ))
@@ -966,6 +967,16 @@ def setup_class(cls):
966967 import config .main
967968 importlib .reload (config .main )
968969
970+ def read_json_file_side_effect (self , filename ):
971+ return {
972+ 'DEVICE_METADATA' : {
973+ 'localhost' : {
974+ 'platform' : 'x86_64-mlnx_msn2700-r0' ,
975+ 'mac' : '00:02:03:04:05:07'
976+ }
977+ }
978+ }
979+
969980 @mock .patch ('sonic_py_common.device_info.get_paths_to_platform_and_hwsku_dirs' , mock .MagicMock (return_value = ("dummy_path" , None )))
970981 @mock .patch ('config.main.subprocess.check_call' )
971982 def test_load_minigraph (self , mock_check_call , get_cmd_module , setup_single_broadcom_asic ):
@@ -1135,12 +1146,9 @@ def test_load_minigraph_with_specified_golden_config_path(self, get_cmd_module):
11351146 def is_file_side_effect (filename ):
11361147 return True if 'golden_config' in filename else False
11371148
1138- def read_json_file_side_effect (filename ):
1139- return {}
1140-
11411149 with mock .patch ("utilities_common.cli.run_command" , mock .MagicMock (side_effect = mock_run_command_side_effect )) as mock_run_command , \
11421150 mock .patch ('os.path.isfile' , mock .MagicMock (side_effect = is_file_side_effect )), \
1143- mock .patch ('config.main.read_json_file' , mock .MagicMock (side_effect = read_json_file_side_effect )):
1151+ mock .patch ('config.main.read_json_file' , mock .MagicMock (side_effect = self . read_json_file_side_effect )):
11441152 (config , show ) = get_cmd_module
11451153 runner = CliRunner ()
11461154 result = runner .invoke (config .config .commands ["load_minigraph" ], ["--override_config" , "--golden_config_path" , "golden_config.json" , "-y" ])
@@ -1152,18 +1160,32 @@ def test_load_minigraph_with_default_golden_config_path(self, get_cmd_module):
11521160 def is_file_side_effect (filename ):
11531161 return True if 'golden_config' in filename else False
11541162
1155- def read_json_file_side_effect (filename ):
1156- return {}
1157-
11581163 with mock .patch ("utilities_common.cli.run_command" , mock .MagicMock (side_effect = mock_run_command_side_effect )) as mock_run_command , \
11591164 mock .patch ('os.path.isfile' , mock .MagicMock (side_effect = is_file_side_effect )), \
1160- mock .patch ('config.main.read_json_file' , mock .MagicMock (side_effect = read_json_file_side_effect )):
1165+ mock .patch ('config.main.read_json_file' , mock .MagicMock (side_effect = self . read_json_file_side_effect )):
11611166 (config , show ) = get_cmd_module
11621167 runner = CliRunner ()
11631168 result = runner .invoke (config .config .commands ["load_minigraph" ], ["--override_config" , "-y" ])
11641169 assert result .exit_code == 0
11651170 assert "config override-config-table /etc/sonic/golden_config_db.json" in result .output
11661171
1172+ @mock .patch (
1173+ 'sonic_py_common.device_info.get_paths_to_platform_and_hwsku_dirs' ,
1174+ mock .MagicMock (return_value = ("dummy_path" , None )))
1175+ def test_load_minigraph_with_invalid_golden_config (self , get_cmd_module ):
1176+ def is_file_side_effect (filename ):
1177+ return True if 'golden_config' in filename else False
1178+
1179+ with mock .patch ("utilities_common.cli.run_command" ,
1180+ mock .MagicMock (side_effect = mock_run_command_side_effect )), \
1181+ mock .patch ('os.path.isfile' , mock .MagicMock (side_effect = is_file_side_effect )), \
1182+ mock .patch ('config.main.read_json_file' , mock .MagicMock (return_value = [])):
1183+ (config , show ) = get_cmd_module
1184+ runner = CliRunner ()
1185+ result = runner .invoke (config .config .commands ["load_minigraph" ], ["--override_config" , "-y" ])
1186+ assert result .exit_code != 0
1187+ assert "Invalid golden config file:" in result .output
1188+
11671189 @mock .patch ('sonic_py_common.device_info.get_paths_to_platform_and_hwsku_dirs' ,
11681190 mock .MagicMock (return_value = ("dummy_path" , None )))
11691191 def test_load_minigraph_hard_dependency_check (self , get_cmd_module ):
@@ -1234,11 +1256,9 @@ def test_load_minigraph_with_traffic_shift_away_with_golden_config(self, get_cmd
12341256 def is_file_side_effect (filename ):
12351257 return True if 'golden_config' in filename else False
12361258
1237- def read_json_file_side_effect (filename ):
1238- return {}
1239-
12401259 with mock .patch ('os.path.isfile' , mock .MagicMock (side_effect = is_file_side_effect )), \
1241- mock .patch ('config.main.read_json_file' , mock .MagicMock (side_effect = read_json_file_side_effect )):
1260+ mock .patch ('config.main.read_json_file' , mock .MagicMock (
1261+ side_effect = self .read_json_file_side_effect )):
12421262 (config , show ) = get_cmd_module
12431263 db = Db ()
12441264 golden_config = {}
@@ -1251,6 +1271,51 @@ def read_json_file_side_effect(filename):
12511271 assert "TSA" in result .output
12521272 assert "[WARNING] Golden configuration may override Traffic-shift-away state" in result .output
12531273
1274+ def test_config_file_yang_validation (self ):
1275+ # Test with empty config
1276+ with mock .patch ('config.main.read_json_file' , return_value = "" ) as mock_read_json_file :
1277+ with mock .patch ('config.main.sonic_yang.SonicYang.loadYangModel' ) as mock_load_yang_model :
1278+ assert not config_file_yang_validation ('dummy_file.json' )
1279+ mock_read_json_file .assert_called_once_with ('dummy_file.json' )
1280+ mock_load_yang_model .assert_not_called ()
1281+
1282+ # Test with non-dict config
1283+ with mock .patch ('config.main.read_json_file' , return_value = []) as mock_read_json_file :
1284+ with mock .patch ('config.main.sonic_yang.SonicYang.loadYangModel' ) as mock_load_yang_model :
1285+ assert not config_file_yang_validation ('dummy_file.json' )
1286+ mock_read_json_file .assert_called_once_with ('dummy_file.json' )
1287+ mock_load_yang_model .assert_not_called ()
1288+
1289+ # Test with missing namespaces in multi-ASIC config
1290+ with mock .patch ('config.main.read_json_file' , return_value = {'localhost' : {}}) as mock_read_json_file :
1291+ with mock .patch ('config.main.multi_asic.is_multi_asic' , return_value = True ):
1292+ with mock .patch ('config.main.multi_asic.get_namespace_list' , return_value = ['asic0' , 'asic1' ]):
1293+ with mock .patch ('config.main.sonic_yang.SonicYang.loadYangModel' ) as mock_load_yang_model :
1294+ assert not config_file_yang_validation ('dummy_file.json' )
1295+ mock_read_json_file .assert_called_once_with ('dummy_file.json' )
1296+ mock_load_yang_model .assert_not_called ()
1297+
1298+ # Test with valid config
1299+ valid_config = {
1300+ 'DEVICE_METADATA' : {
1301+ 'localhost' : {
1302+ 'platform' : 'x86_64-mlnx_msn2700-r0' ,
1303+ 'mac' : '00:02:03:04:05:07'
1304+ }
1305+ }
1306+ }
1307+
1308+ with mock .patch ('config.main.read_json_file' , return_value = valid_config ) as mock_read_json_file , \
1309+ mock .patch ('config.main.multi_asic.is_multi_asic' , return_value = False ), \
1310+ mock .patch ('config.main.sonic_yang.SonicYang.loadYangModel' ) as mock_load_yang_model , \
1311+ mock .patch ('config.main.sonic_yang.SonicYang.loadData' ) as mock_load_data , \
1312+ mock .patch ('config.main.sonic_yang.SonicYang.validate_data_tree' ) as mock_validate_data_tree :
1313+ assert config_file_yang_validation ('dummy_file.json' )
1314+ mock_read_json_file .assert_called_once_with ('dummy_file.json' )
1315+ mock_load_yang_model .assert_called_once ()
1316+ mock_load_data .assert_called_once_with (configdbJson = valid_config )
1317+ mock_validate_data_tree .assert_called_once ()
1318+
12541319 @classmethod
12551320 def teardown_class (cls ):
12561321 os .environ ['UTILITIES_UNIT_TESTING' ] = "0"
0 commit comments