11import sys
2- from unittest import mock
2+ from unittest . mock import MagicMock , patch
33
44from click .testing import CliRunner
55
99
1010class TestShowMACsec (object ):
1111 def test_plugin_registration (self ):
12- cli = mock . MagicMock ()
12+ cli = MagicMock ()
1313 show_macsec .register (cli )
1414 cli .add_command .assert_called_once_with (show_macsec .macsec )
1515
@@ -27,3 +27,250 @@ def test_show_profile(self):
2727 runner = CliRunner ()
2828 result = runner .invoke (show_macsec .macsec ,["--profile" ])
2929 assert result .exit_code == 0 , "exit code: {}, Exception: {}, Traceback: {}" .format (result .exit_code , result .exception , result .exc_info )
30+
31+ @patch ('show_macsec.SonicV2Connector' )
32+ def test_post_status_success (self , mock_connector ):
33+ """Test --post-status command with successful data"""
34+ with patch ('show_macsec.multi_asic_util.MultiAsic' ) as mock_multi_asic_class :
35+ # Setup MultiAsic mock
36+ mock_multi_asic_instance = MagicMock ()
37+ mock_multi_asic_instance .is_multi_asic .return_value = False
38+ mock_multi_asic_instance .get_ns_list_based_on_options .return_value = ['' ]
39+ mock_multi_asic_class .return_value = mock_multi_asic_instance
40+
41+ # Setup database mock
42+ def mock_connector_side_effect (use_unix_socket_path = True , namespace = None ):
43+ mock_db = MagicMock ()
44+
45+ if not namespace or namespace == '' :
46+ mock_db .keys .return_value = ['FIPS_MACSEC_POST_TABLE|crypto' , 'FIPS_MACSEC_POST_TABLE|sai' ]
47+ # Mock get_all for different modules
48+ def mock_get_all (db_name , key ):
49+ if key == "FIPS_MACSEC_POST_TABLE|crypto" :
50+ return {
51+ 'status' : 'pass' ,
52+ 'timestamp' : '2025-09-15 10:30:00 UTC' ,
53+ }
54+ elif key == "FIPS_MACSEC_POST_TABLE|sai" :
55+ return {
56+ 'status' : 'fail' ,
57+ 'timestamp' : '2025-09-15 10:31:30 UTC' ,
58+ }
59+ return {}
60+ mock_db .get_all .side_effect = mock_get_all
61+ else :
62+ mock_db .keys .return_value = []
63+ mock_db .get_all .return_value = {}
64+
65+ return mock_db
66+ mock_connector .side_effect = mock_connector_side_effect
67+
68+ # Test the CLI
69+ runner = CliRunner ()
70+ result = runner .invoke (show_macsec .macsec , ["--post-status" ])
71+
72+ # Assertions
73+ assert result .exit_code == 0
74+ assert "Module : crypto" in result .output
75+ assert "Module : sai" in result .output
76+ assert "Status : pass" in result .output
77+ assert "Status : fail" in result .output
78+
79+ @patch ('show_macsec.SonicV2Connector' )
80+ def test_post_status_no_entries (self , mock_connector ):
81+ """Test --post-status command when no POST entries exist"""
82+ with patch ('show_macsec.multi_asic_util.MultiAsic' ) as mock_multi_asic_class :
83+ # Mock the database connection
84+ # Setup MultiAsic mock
85+ mock_multi_asic_instance = MagicMock ()
86+ mock_multi_asic_instance .is_multi_asic .return_value = False
87+ mock_multi_asic_instance .get_ns_list_based_on_options .return_value = ['' ]
88+ mock_multi_asic_class .return_value = mock_multi_asic_instance
89+
90+ # Create separate mock instances for different database connections
91+ def mock_connector_side_effect (use_unix_socket_path = True , namespace = None ):
92+ mock_db = MagicMock ()
93+ mock_db .keys .return_value = []
94+ mock_connector .return_value = mock_db
95+ # Return empty dict for any get_all call (no POST data)
96+ mock_db .get_all .return_value = {}
97+ return mock_db
98+
99+ mock_connector .side_effect = mock_connector_side_effect
100+
101+ runner = CliRunner ()
102+ result = runner .invoke (show_macsec .macsec , ["--post-status" ])
103+
104+ assert result .exit_code == 0
105+ assert "No entries found" in result .output
106+
107+ def test_post_status_mutual_exclusivity (self ):
108+ """Test that --post-status and other option/argument are mutually exclusive"""
109+ runner = CliRunner ()
110+ result = runner .invoke (show_macsec .macsec , ["Ethernet0" , "--post-status" ])
111+
112+ assert result .exit_code == 0
113+ assert "POST status is not valid with other options/arguments" in result .output
114+
115+ result = runner .invoke (show_macsec .macsec , ["--profile" , "--post-status" ])
116+
117+ assert result .exit_code == 0
118+ assert "POST status is not valid with other options/arguments" in result .output
119+
120+ result = runner .invoke (show_macsec .macsec , ["--dump-file" , "--post-status" ])
121+
122+ assert result .exit_code == 0
123+ assert "POST status is not valid with other options/arguments" in result .output
124+
125+ result = runner .invoke (show_macsec .macsec , ["--profile" , "--post-status" ])
126+
127+ assert result .exit_code == 0
128+ assert "POST status is not valid with other options/arguments" in result .output
129+
130+ result = runner .invoke (show_macsec .macsec , ["Ethernet0" , "--profile" , "--post-status" ])
131+
132+ assert result .exit_code == 0
133+ assert "POST status is not valid with other options/arguments" in result .output
134+
135+ @patch ('show_macsec.SonicV2Connector' )
136+ def test_post_status_multi_asic_success (self , mock_connector ):
137+ """Test --post-status command in multi-ASIC environment with successful data"""
138+ with patch ('show_macsec.multi_asic_util.MultiAsic' ) as mock_multi_asic_class :
139+ mock_multi_asic_instance = MagicMock ()
140+ mock_multi_asic_instance .is_multi_asic .return_value = True
141+ mock_multi_asic_instance .get_ns_list_based_on_options .return_value = ['' , 'asic0' , 'asic1' ]
142+ mock_multi_asic_class .return_value = mock_multi_asic_instance
143+
144+ # Setup database mock
145+ def mock_connector_side_effect (use_unix_socket_path = True , namespace = None ):
146+ mock_db = MagicMock ()
147+
148+ if namespace == 'asic0' :
149+ mock_db .keys .return_value = ['FIPS_MACSEC_POST_TABLE|sai' ]
150+ mock_db .get_all .return_value = {'status' : 'fail' , 'timestamp' : '2025-09-15 10:31:00 UTC' }
151+ elif namespace == 'asic1' :
152+ mock_db .keys .return_value = ['FIPS_MACSEC_POST_TABLE|sai' ]
153+ mock_db .get_all .return_value = {'status' : 'inprogress' , 'timestamp' : '2025-09-15 10:31:30 UTC' }
154+ elif not namespace or namespace == '' :
155+ mock_db .keys .return_value = ['FIPS_MACSEC_POST_TABLE|crypto' , 'FIPS_MACSEC_POST_TABLE|sai' ]
156+ # Mock get_all for different modules
157+ def mock_get_all (db_name , key ):
158+ if key == "FIPS_MACSEC_POST_TABLE|crypto" :
159+ return {
160+ 'status' : 'pass' ,
161+ 'timestamp' : '2025-09-15 10:30:00 UTC' ,
162+ }
163+ elif key == "FIPS_MACSEC_POST_TABLE|sai" :
164+ return {
165+ 'status' : 'pass' ,
166+ 'timestamp' : '2025-09-15 10:31:30 UTC' ,
167+ }
168+ return {}
169+ mock_db .get_all .side_effect = mock_get_all
170+ else :
171+ mock_db .keys .return_value = []
172+ mock_db .get_all .return_value = {}
173+
174+ return mock_db
175+
176+ mock_connector .side_effect = mock_connector_side_effect
177+
178+ # Test the CLI
179+ runner = CliRunner ()
180+ result = runner .invoke (show_macsec .macsec , ["--post-status" ])
181+
182+ # Assertions
183+ assert result .exit_code == 0
184+ assert "Module : crypto" in result .output
185+ assert "Module : sai" in result .output
186+ assert "Namespace (asic0)" in result .output
187+ assert "Namespace (asic1)" in result .output
188+ assert "Status : pass" in result .output
189+ assert "Status : fail" in result .output
190+ assert "Status : inprogress" in result .output
191+
192+ @patch ('show_macsec.SonicV2Connector' )
193+ def test_post_status_multi_asic_specific_namespace (self , mock_connector ):
194+ """Test --post-status command with specific namespace filter"""
195+ with patch ('show_macsec.multi_asic_util.MultiAsic' ) as mock_multi_asic_class :
196+ mock_multi_asic_instance = MagicMock ()
197+ mock_multi_asic_instance .is_multi_asic .return_value = True
198+ mock_multi_asic_instance .get_ns_list_based_on_options .return_value = ['asic1' ]
199+ mock_multi_asic_class .return_value = mock_multi_asic_instance
200+
201+ # Mock database to return keys and data only for asic1
202+ def mock_connector_side_effect (use_unix_socket_path = True , namespace = None ):
203+ mock_db = MagicMock ()
204+
205+ if namespace == 'asic1' :
206+ # Only asic1 should have data when filtered
207+ mock_db .keys .return_value = ['FIPS_MACSEC_POST_TABLE|sai' ]
208+ # Mock get_all for different modules
209+ def mock_get_all (db_name , key ):
210+ if key == "FIPS_MACSEC_POST_TABLE|sai" :
211+ return {
212+ 'status' : 'pass' ,
213+ 'timestamp' : '2025-09-15 10:31:00 UTC' ,
214+ }
215+ return {}
216+ mock_db .get_all .side_effect = mock_get_all
217+ else :
218+ # No keys for other namespaces (they shouldn't be called with filtering)
219+ mock_db .keys .return_value = []
220+ mock_db .get_all .return_value = {}
221+
222+ return mock_db
223+
224+ mock_connector .side_effect = mock_connector_side_effect
225+
226+ # Test the new approach with namespace filtering
227+ runner = CliRunner ()
228+ result = runner .invoke (show_macsec .macsec , ["--post-status" ])
229+
230+ assert result .exit_code == 0
231+ # Check that modules are displayed for asic1 only
232+ assert "Module : sai" in result .output
233+ assert "Status : pass" in result .output
234+ assert "Namespace (asic1)" in result .output
235+
236+ @patch ('show_macsec.SonicV2Connector' )
237+ def test_post_status_multi_asic_partial_entries (self , mock_connector ):
238+ """Test --post-status command when no POST data is available"""
239+ # Setup MultiAsic mock
240+ with patch ('show_macsec.multi_asic_util.MultiAsic' ) as mock_multi_asic_class :
241+ mock_multi_asic_instance = MagicMock ()
242+ mock_multi_asic_instance .is_multi_asic .return_value = True
243+ mock_multi_asic_instance .get_ns_list_based_on_options .return_value = ['' , 'asic0' , 'asic1' ]
244+ mock_multi_asic_class .return_value = mock_multi_asic_instance
245+ def mock_connector_side_effect (use_unix_socket_path = True , namespace = None ):
246+ mock_db = MagicMock ()
247+ if namespace == 'asic0' :
248+ # Only asic1 should have data when filtered
249+ mock_db .keys .return_value = ['FIPS_MACSEC_POST_TABLE|sai' ]
250+ # Mock get_all for different modules
251+ def mock_get_all (db_name , key ):
252+ if key == "FIPS_MACSEC_POST_TABLE|sai" :
253+ return {
254+ 'status' : 'pass' ,
255+ 'timestamp' : '2025-09-15 10:31:00 UTC' ,
256+ }
257+ return {}
258+ mock_db .get_all .side_effect = mock_get_all
259+ else :
260+ # No keys for other namespaces (they shouldn't be called with filtering)
261+ mock_db .keys .return_value = []
262+ mock_db .get_all .return_value = {}
263+
264+ return mock_db
265+
266+ mock_connector .side_effect = mock_connector_side_effect
267+
268+ # Test the CLI command
269+ runner = CliRunner ()
270+ result = runner .invoke (show_macsec .macsec , ["--post-status" ])
271+
272+ assert result .exit_code == 0
273+ assert "No entries found" in result .output
274+ assert "Module : sai" in result .output
275+ assert "Status : pass" in result .output
276+ assert "Namespace (asic1)" in result .output
0 commit comments