summaryrefslogtreecommitdiff
path: root/vmmsclient/tests
diff options
context:
space:
mode:
Diffstat (limited to 'vmmsclient/tests')
-rw-r--r--vmmsclient/tests/__init__.py14
-rw-r--r--vmmsclient/tests/base.py48
-rw-r--r--vmmsclient/tests/v2/__init__.py14
-rw-r--r--vmmsclient/tests/v2/fakes.py40
-rw-r--r--vmmsclient/tests/v2/test_client.py278
-rw-r--r--vmmsclient/tests/v2/test_plugin.py53
6 files changed, 447 insertions, 0 deletions
diff --git a/vmmsclient/tests/__init__.py b/vmmsclient/tests/__init__.py
new file mode 100644
index 0000000..a25a7f2
--- /dev/null
+++ b/vmmsclient/tests/__init__.py
@@ -0,0 +1,14 @@
+# Copyright (c) 2025 Thomas Goirand
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
diff --git a/vmmsclient/tests/base.py b/vmmsclient/tests/base.py
new file mode 100644
index 0000000..e2e8da6
--- /dev/null
+++ b/vmmsclient/tests/base.py
@@ -0,0 +1,48 @@
+# Copyright (c) 2025 Thomas Goirand
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+try:
+ from unittest import mock
+except ImportError:
+ import mock
+import testtools
+
+from keystoneauth1 import fixture as ksa_fixture
+from osc_lib import shell
+from osc_lib.tests import utils
+
+
+class FakeApp(object):
+ def __init__(self):
+ self.client_manager = None
+
+
+class TestCase(testtools.TestCase):
+ """Test case base class for all unit tests."""
+
+ def setUp(self):
+ super(TestCase, self).setUp()
+ self.app = FakeApp()
+ self.shell = shell.OpenStackShell()
+
+
+class TestCommand(TestCase):
+ """Test case base class for command tests."""
+
+ def setUp(self):
+ super(TestCommand, self).setUp()
+ self.app = mock.Mock()
+ self.app.client_manager = mock.Mock()
+ self.app.client_manager.vmms = mock.Mock()
diff --git a/vmmsclient/tests/v2/__init__.py b/vmmsclient/tests/v2/__init__.py
new file mode 100644
index 0000000..a25a7f2
--- /dev/null
+++ b/vmmsclient/tests/v2/__init__.py
@@ -0,0 +1,14 @@
+# Copyright (c) 2025 Thomas Goirand
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
diff --git a/vmmsclient/tests/v2/fakes.py b/vmmsclient/tests/v2/fakes.py
new file mode 100644
index 0000000..9db59a0
--- /dev/null
+++ b/vmmsclient/tests/v2/fakes.py
@@ -0,0 +1,40 @@
+# Copyright (c) 2025 Thomas Goirand
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import datetime
+import json
+import uuid
+
+
+def make_fake_migration(**kwargs):
+ """Create a fake migration object."""
+ fake_migration = {
+ 'id': kwargs.get('id', str(uuid.uuid4())),
+ 'vm_id': kwargs.get('vm_id', str(uuid.uuid4())),
+ 'vm_name': kwargs.get('vm_name', 'test-vm'),
+ 'scheduled_time': kwargs.get('scheduled_time', None),
+ 'state': kwargs.get('state', 'SCHEDULED'),
+ 'created_at': kwargs.get('created_at', datetime.datetime.now().isoformat()),
+ 'updated_at': kwargs.get('updated_at', datetime.datetime.now().isoformat()),
+ }
+ return fake_migration
+
+
+FAKE_MIGRATIONS = [
+ make_fake_migration(vm_name='test-vm-1', state='SCHEDULED'),
+ make_fake_migration(vm_name='test-vm-2', state='MIGRATED'),
+ make_fake_migration(vm_name='scheduled-vm', state='SCHEDULED',
+ scheduled_time='2025-10-07T22:00:00'),
+]
diff --git a/vmmsclient/tests/v2/test_client.py b/vmmsclient/tests/v2/test_client.py
new file mode 100644
index 0000000..ac2b972
--- /dev/null
+++ b/vmmsclient/tests/v2/test_client.py
@@ -0,0 +1,278 @@
+# Copyright (c) 2025 Thomas Goirand
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import datetime
+try:
+ from unittest import mock
+except ImportError:
+ import mock
+import testtools
+import uuid
+
+from osc_lib import exceptions as osc_lib_exceptions
+
+from vmmsclient.tests import base
+from vmmsclient.tests.v2.fakes import FAKE_MIGRATIONS, make_fake_migration
+from vmmsclient.v2 import client
+
+
+class TestClient(base.TestCase):
+ """Test cases for the Client class."""
+
+ def setUp(self):
+ super(TestClient, self).setUp()
+ self.client = client.Client(
+ session=mock.Mock(),
+ service_type='vmms',
+ interface='public'
+ )
+
+ def test_add_vm(self):
+ """Test adding a VM migration."""
+ fake_migration = make_fake_migration()
+
+ # Mock the post method to return our fake data
+ with mock.patch.object(self.client, 'post') as mock_post:
+ mock_response = mock.Mock()
+ mock_response.json.return_value = fake_migration
+ mock_post.return_value = mock_response
+
+ result = self.client.add_vm(
+ vm_identifier='test-vm-id-or-name'
+ )
+
+ self.assertEqual(fake_migration, result)
+ # Check that post was called with correct arguments
+ expected_data = {
+ 'vm_identifier': 'test-vm-id-or-name'
+ }
+ mock_post.assert_called_once_with('/vms', json=expected_data)
+
+ def test_add_vm_with_schedule_time(self):
+ """Test adding a VM migration with scheduled time."""
+ fake_migration = make_fake_migration(
+ scheduled_time='2025-10-07T22:00:00'
+ )
+
+ # Mock the post method to return our fake data
+ with mock.patch.object(self.client, 'post') as mock_post:
+ mock_response = mock.Mock()
+ mock_response.json.return_value = fake_migration
+ mock_post.return_value = mock_response
+
+ result = self.client.add_vm(
+ vm_identifier='test-vm-id-or-name',
+ scheduled_time=fake_migration['scheduled_time']
+ )
+
+ self.assertEqual(fake_migration, result)
+ # Check that post was called with correct arguments
+ expected_data = {
+ 'vm_identifier': 'test-vm-id-or-name',
+ 'scheduled_time': fake_migration['scheduled_time']
+ }
+ mock_post.assert_called_once_with('/vms', json=expected_data)
+
+ def test_list_vms(self):
+ """Test listing VM migrations."""
+ # Mock the get method to return our fake data
+ with mock.patch.object(self.client, 'get') as mock_get:
+ mock_response = mock.Mock()
+ mock_response.json.return_value = FAKE_MIGRATIONS
+ mock_get.return_value = mock_response
+
+ result = self.client.list_vms()
+
+ self.assertEqual(FAKE_MIGRATIONS, result)
+ mock_get.assert_called_once_with('/vms')
+
+ def test_remove_vm(self):
+ """Test removing a VM migration."""
+ migration_id = 'test-migration-id'
+
+ # Mock the delete method to return success
+ with mock.patch.object(self.client, 'delete') as mock_delete:
+ mock_response = mock.Mock()
+ mock_response.status_code = 200
+ mock_delete.return_value = mock_response
+
+ result = self.client.remove_vm(migration_id)
+
+ self.assertTrue(result)
+ mock_delete.assert_called_once_with('/vms/{0}'.format(migration_id))
+
+ def test_remove_vm_not_found(self):
+ """Test removing a VM migration that doesn't exist."""
+ migration_id = 'non-existent-id'
+
+ # Mock the delete method to return 404
+ with mock.patch.object(self.client, 'delete') as mock_delete:
+ mock_delete.side_effect = osc_lib_exceptions.NotFound('Migration not found (404).')
+
+ # Expect NotFound to be raised for 404
+ self.assertRaises(
+ osc_lib_exceptions.NotFound,
+ self.client.remove_vm,
+ migration_id
+ )
+
+
+class TestAddVMCommand(base.TestCommand):
+ """Test cases for AddVMCommand."""
+
+ def setUp(self):
+ super(TestAddVMCommand, self).setUp()
+ self.cmd = client.AddVMCommand(self.app, None)
+
+ def test_get_parser(self):
+ """Test parser creation."""
+ parser = self.cmd.get_parser('test')
+ parsed_args = parser.parse_args([
+ 'test-vm-id-or-name'
+ ])
+ self.assertEqual('test-vm-id-or-name', parsed_args.vm_identifier)
+
+ def test_get_parser_with_schedule_time(self):
+ """Test parser creation with schedule time."""
+ parser = self.cmd.get_parser('test')
+ parsed_args = parser.parse_args([
+ 'test-vm-id-or-name',
+ '--schedule-time', '2025-10-07T22:00:00'
+ ])
+ self.assertEqual('test-vm-id-or-name', parsed_args.vm_identifier)
+ self.assertEqual('2025-10-07T22:00:00', parsed_args.schedule_time)
+
+ def test_take_action(self):
+ """Test taking action."""
+ fake_migration = make_fake_migration()
+ self.app.client_manager.vmms.add_vm.return_value = fake_migration
+
+ parsed_args = mock.Mock()
+ parsed_args.vm_identifier = 'test-vm-id-or-name'
+ parsed_args.schedule_time = None
+
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.app.client_manager.vmms.add_vm.assert_called_once_with(
+ vm_identifier='test-vm-id-or-name',
+ scheduled_time=None
+ )
+ self.assertEqual(('id', 'vm_id', 'vm_name', 'scheduled_time', 'state',
+ 'workflow_exec', 'created_at', 'updated_at'), columns)
+
+
+class TestListVMsCommand(base.TestCommand):
+ """Test cases for ListVMsCommand."""
+
+ def setUp(self):
+ super(TestListVMsCommand, self).setUp()
+ self.cmd = client.ListVMsCommand(self.app, None)
+
+ def test_take_action(self):
+ """Test taking action."""
+ self.app.client_manager.vmms.list_vms.return_value = FAKE_MIGRATIONS
+
+ # Test without --state parameter
+ parsed_args = mock.Mock()
+ parsed_args.state = None
+ parsed_args.long = False
+
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.app.client_manager.vmms.list_vms.assert_called_once_with(state=None)
+ self.assertEqual(('id', 'vm_id', 'vm_name', 'scheduled_time', 'state',
+ 'workflow_exec'), columns)
+ self.assertEqual(len(FAKE_MIGRATIONS), len(list(data)))
+
+ def test_take_action_with_state_filter(self):
+ """Test taking action with state filter."""
+ self.app.client_manager.vmms.list_vms.return_value = FAKE_MIGRATIONS
+
+ parsed_args = mock.Mock()
+ parsed_args.state = 'ERROR'
+ parsed_args.long = False
+
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.app.client_manager.vmms.list_vms.assert_called_once_with(state='ERROR')
+ self.assertEqual(('id', 'vm_id', 'vm_name', 'scheduled_time', 'state',
+ 'workflow_exec'), columns)
+
+ def test_take_action_long_format(self):
+ """Test taking action with long format."""
+ self.app.client_manager.vmms.list_vms.return_value = FAKE_MIGRATIONS
+
+ parsed_args = mock.Mock()
+ parsed_args.state = None
+ parsed_args.long = True
+
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.app.client_manager.vmms.list_vms.assert_called_once_with(state=None)
+ self.assertEqual(('id', 'vm_id', 'vm_name', 'scheduled_time', 'state',
+ 'workflow_exec', 'created_at', 'updated_at'), columns)
+ self.assertEqual(len(FAKE_MIGRATIONS), len(list(data)))
+
+ def test_get_parser(self):
+ """Test parser creation."""
+ parser = self.cmd.get_parser('test')
+ # Test that parser accepts --state option
+ parsed_args = parser.parse_args(['--state', 'ERROR'])
+ self.assertEqual('ERROR', parsed_args.state)
+
+ # Test that parser accepts --long option
+ parsed_args = parser.parse_args(['--long'])
+ self.assertTrue(parsed_args.long)
+
+
+class TestRemoveVMCommand(base.TestCommand):
+ """Test cases for RemoveVMCommand."""
+
+ def setUp(self):
+ super(TestRemoveVMCommand, self).setUp()
+ self.cmd = client.RemoveVMCommand(self.app, None)
+
+ def test_get_parser(self):
+ """Test parser creation."""
+ parser = self.cmd.get_parser('test')
+ parsed_args = parser.parse_args(['test-migration-id'])
+ self.assertEqual('test-migration-id', parsed_args.migration_id)
+
+ def test_take_action_success(self):
+ """Test successful removal."""
+ self.app.client_manager.vmms.remove_vm.return_value = True
+
+ parsed_args = mock.Mock()
+ parsed_args.migration_id = 'test-migration-id'
+
+ self.cmd.take_action(parsed_args)
+
+ self.app.client_manager.vmms.remove_vm.assert_called_once_with(
+ 'test-migration-id'
+ )
+
+ def test_take_action_failure(self):
+ """Test failed removal."""
+ # Mock remove_vm to raise NotFound
+ self.app.client_manager.vmms.remove_vm.side_effect = osc_lib_exceptions.NotFound("Migration not found")
+
+ parsed_args = mock.Mock()
+ parsed_args.migration_id = 'test-migration-id'
+
+ self.assertRaises(
+ osc_lib_exceptions.CommandError,
+ self.cmd.take_action,
+ parsed_args
+ )
diff --git a/vmmsclient/tests/v2/test_plugin.py b/vmmsclient/tests/v2/test_plugin.py
new file mode 100644
index 0000000..53443d7
--- /dev/null
+++ b/vmmsclient/tests/v2/test_plugin.py
@@ -0,0 +1,53 @@
+# Copyright (c) 2025 Thomas Goirand
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import argparse
+try:
+ from unittest import mock
+except ImportError:
+ import mock
+import testtools
+
+from vmmsclient import plugin
+from vmmsclient.tests import base
+
+
+class TestPlugin(base.TestCase):
+ """Test cases for the plugin module."""
+
+ def test_make_client(self):
+ """Test client creation."""
+ instance = mock.Mock()
+ instance.get_configuration.return_value = {
+ plugin.API_VERSION_OPTION: '2'
+ }
+ instance.session = mock.Mock()
+ instance.region_name = 'RegionOne'
+ instance.interface = 'public'
+
+ client = plugin.make_client(instance)
+
+ self.assertIsNotNone(client)
+
+ def test_build_option_parser(self):
+ """Test option parser creation."""
+ parser = argparse.ArgumentParser()
+ result_parser = plugin.build_option_parser(parser)
+
+ self.assertEqual(parser, result_parser)
+
+ # Test that the option is added
+ args = parser.parse_args([])
+ self.assertTrue(hasattr(args, 'os_vmms_api_version'))