1+ # -*- coding: utf-8 -*-
2+
3+ # Licensed under the Apache License, Version 2.0 (the "License"); you may
4+ # not use this file except in compliance with the License. You may obtain
5+ # a copy of the License at
6+ #
7+ # http://www.apache.org/licenses/LICENSE-2.0
8+ #
9+ # Unless required by applicable law or agreed to in writing, software
10+ # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11+ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12+ # License for the specific language governing permissions and limitations
13+ # under the License.
14+
15+ """Tests for `caso._cmd.projects` module."""
16+
17+ import sys
18+ import unittest
19+ from unittest import mock
20+
21+ from oslo_config import cfg
22+
23+ from caso ._cmd import projects
24+ from caso .tests import base
25+
26+ CONF = cfg .CONF
27+
28+
29+ class TestProjectsCommand (base .TestCase ):
30+ """Test case for the projects command module."""
31+
32+ def setUp (self ):
33+ """Set up test fixtures."""
34+ super (TestProjectsCommand , self ).setUp ()
35+
36+ @mock .patch ('caso.config.parse_args' )
37+ @mock .patch ('oslo_log.log.setup' )
38+ @mock .patch ('caso.manager.Manager' )
39+ def test_main_success (self , mock_manager_cls , mock_log_setup , mock_parse_args ):
40+ """Test main function with successful project retrieval."""
41+ # Setup mocks
42+ mock_manager = mock .MagicMock ()
43+ mock_manager_cls .return_value = mock_manager
44+ mock_manager .projects_and_vos .return_value = [('project1' , 'vo1' ), ('project2' , 'vo2' )]
45+
46+ # Mock keystone project retrieval
47+ mock_project = mock .MagicMock ()
48+ mock_project .name = 'Project One'
49+ mock_manager .extractor_manager .keystone .projects .get .return_value = mock_project
50+
51+ with mock .patch ('builtins.print' ) as mock_print :
52+ projects .main ()
53+
54+ # Verify setup calls
55+ mock_parse_args .assert_called_once_with (sys .argv )
56+ mock_log_setup .assert_called_once_with (cfg .CONF , "caso" )
57+ mock_manager_cls .assert_called_once ()
58+
59+ # Verify manager methods called
60+ mock_manager .projects_and_vos .assert_called_once ()
61+
62+ # Verify print was called with expected output format
63+ self .assertTrue (mock_print .called )
64+ print_calls = [call [0 ][0 ] for call in mock_print .call_args_list ]
65+ self .assertIn ("'project1 (Project One) mapped to VO 'vo1'" , print_calls )
66+
67+ @mock .patch ('caso.config.parse_args' )
68+ @mock .patch ('oslo_log.log.setup' )
69+ @mock .patch ('caso.manager.Manager' )
70+ def test_main_with_keystone_error (self , mock_manager_cls , mock_log_setup , mock_parse_args ):
71+ """Test main function when keystone project retrieval fails."""
72+ # Setup mocks
73+ mock_manager = mock .MagicMock ()
74+ mock_manager_cls .return_value = mock_manager
75+ mock_manager .projects_and_vos .return_value = [('project1' , 'vo1' )]
76+
77+ # Mock keystone to raise an exception
78+ mock_manager .extractor_manager .keystone .projects .get .side_effect = Exception ("Keystone error" )
79+
80+ with mock .patch ('builtins.print' ) as mock_print :
81+ projects .main ()
82+
83+ # Verify error messages were printed
84+ print_calls = [call [0 ][0 ] for call in mock_print .call_args_list ]
85+ error_messages = [call for call in print_calls if call .startswith ("ERROR:" )]
86+ self .assertTrue (len (error_messages ) >= 2 ) # Should have at least 2 error messages
87+ self .assertTrue (any ("Could not get project project1" in msg for msg in error_messages ))
88+
89+ @mock .patch ('caso.config.parse_args' )
90+ @mock .patch ('oslo_log.log.setup' )
91+ @mock .patch ('caso.manager.Manager' )
92+ def test_migrate_dry_run_mode (self , mock_manager_cls , mock_log_setup , mock_parse_args ):
93+ """Test migrate function in dry run mode."""
94+ # Setup mocks
95+ mock_manager = mock .MagicMock ()
96+ mock_manager_cls .return_value = mock_manager
97+ mock_manager .extractor_manager .voms_map .items .return_value = [('project1' , 'vo1' )]
98+
99+ # Set dry_run flag
100+ self .flags (dry_run = True , vo_property = 'caso_vo' )
101+ with mock .patch ('builtins.print' ) as mock_print :
102+ projects .migrate ()
103+
104+ # Verify setup calls
105+ mock_parse_args .assert_called_once_with (sys .argv )
106+ mock_log_setup .assert_called_once_with (cfg .CONF , "caso" )
107+ mock_manager ._load_managers .assert_called_once ()
108+
109+ # Verify print statements for dry run
110+ print_calls = [call [0 ][0 ] for call in mock_print .call_args_list ]
111+ warning_printed = any ("WARNING: Running in 'dry-run' mode" in call for call in print_calls )
112+ self .assertTrue (warning_printed )
113+
114+ # Should print the openstack command but not execute it
115+ openstack_cmd_printed = any ("openstack project set --property" in call for call in print_calls )
116+ self .assertTrue (openstack_cmd_printed )
117+
118+ @mock .patch ('caso.config.parse_args' )
119+ @mock .patch ('oslo_log.log.setup' )
120+ @mock .patch ('caso.manager.Manager' )
121+ def test_migrate_with_projects_dry_run (self , mock_manager_cls , mock_log_setup , mock_parse_args ):
122+ """Test migrate function with project migration in dry run mode."""
123+ # Setup mocks
124+ mock_manager = mock .MagicMock ()
125+ mock_manager_cls .return_value = mock_manager
126+ mock_manager .extractor_manager .voms_map .items .return_value = []
127+
128+ # Set flags for project migration
129+ self .flags (dry_run = True , migrate_projects = True , projects = ['project1' ], caso_tag = 'caso' )
130+ with mock .patch ('builtins.print' ) as mock_print :
131+ projects .migrate ()
132+
133+ # Verify print statements for project tagging
134+ print_calls = [call [0 ][0 ] for call in mock_print .call_args_list ]
135+ tag_cmd_printed = any ("openstack project set --tag caso project1" in call for call in print_calls )
136+ self .assertTrue (tag_cmd_printed )
137+
138+ @mock .patch ('caso.config.parse_args' )
139+ @mock .patch ('oslo_log.log.setup' )
140+ @mock .patch ('caso.manager.Manager' )
141+ def test_migrate_actual_execution (self , mock_manager_cls , mock_log_setup , mock_parse_args ):
142+ """Test migrate function with actual execution (not dry run)."""
143+ # Setup mocks
144+ mock_manager = mock .MagicMock ()
145+ mock_manager_cls .return_value = mock_manager
146+ mock_manager .extractor_manager .voms_map .items .return_value = [('project1' , 'vo1' )]
147+
148+ # Mock successful keystone update
149+ mock_manager .extractor_manager .keystone .projects .update .return_value = None
150+
151+ # Set flags for actual execution
152+ self .flags (dry_run = False , vo_property = 'caso_vo' )
153+ with mock .patch ('builtins.print' ) as mock_print :
154+ projects .migrate ()
155+
156+ # Verify keystone update was called
157+ mock_manager .extractor_manager .keystone .projects .update .assert_called_once_with (
158+ 'project1' , caso_vo = 'vo1'
159+ )
160+
161+ # Should not print warning about dry run
162+ print_calls = [call [0 ][0 ] for call in mock_print .call_args_list ]
163+ warning_printed = any ("WARNING: Running in 'dry-run' mode" in call for call in print_calls )
164+ self .assertFalse (warning_printed )
165+
166+ @mock .patch ('caso.config.parse_args' )
167+ @mock .patch ('oslo_log.log.setup' )
168+ @mock .patch ('caso.manager.Manager' )
169+ def test_migrate_keystone_update_error (self , mock_manager_cls , mock_log_setup , mock_parse_args ):
170+ """Test migrate function when keystone update fails."""
171+ # Setup mocks
172+ mock_manager = mock .MagicMock ()
173+ mock_manager_cls .return_value = mock_manager
174+ mock_manager .extractor_manager .voms_map .items .return_value = [('project1' , 'vo1' )]
175+
176+ # Mock keystone update to raise an exception
177+ mock_manager .extractor_manager .keystone .projects .update .side_effect = Exception ("Update failed" )
178+
179+ # Set flags for actual execution
180+ self .flags (dry_run = False , vo_property = 'caso_vo' )
181+ with mock .patch ('builtins.print' ) as mock_print :
182+ projects .migrate ()
183+
184+ # Verify error messages were printed
185+ print_calls = [call [0 ][0 ] for call in mock_print .call_args_list ]
186+ error_messages = [call for call in print_calls if call .startswith ("ERROR:" )]
187+ self .assertTrue (len (error_messages ) >= 2 ) # Should have at least 2 error messages
188+ self .assertTrue (any ("could not add property for project project1" in msg for msg in error_messages ))
189+
190+ @mock .patch ('caso.config.parse_args' )
191+ @mock .patch ('oslo_log.log.setup' )
192+ @mock .patch ('caso.manager.Manager' )
193+ def test_migrate_project_tagging_error (self , mock_manager_cls , mock_log_setup , mock_parse_args ):
194+ """Test migrate function when project tagging fails."""
195+ # Setup mocks
196+ mock_manager = mock .MagicMock ()
197+ mock_manager_cls .return_value = mock_manager
198+ mock_manager .extractor_manager .voms_map .items .return_value = []
199+
200+ # Mock project get and tag operations
201+ mock_project = mock .MagicMock ()
202+ mock_project .add_tag .side_effect = Exception ("Tag failed" )
203+ mock_manager .extractor_manager .keystone .projects .get .return_value = mock_project
204+
205+ # Set flags for project migration
206+ self .flags (dry_run = False , migrate_projects = True , projects = ['project1' ], caso_tag = 'caso' )
207+ with mock .patch ('builtins.print' ) as mock_print :
208+ projects .migrate ()
209+
210+ # Verify error messages were printed
211+ print_calls = [call [0 ][0 ] for call in mock_print .call_args_list ]
212+ error_messages = [call for call in print_calls if call .startswith ("ERROR:" )]
213+ self .assertTrue (len (error_messages ) >= 2 ) # Should have at least 2 error messages
214+ self .assertTrue (any ("could not add tag for project project1" in msg for msg in error_messages ))
0 commit comments