|
2 | 2 |
|
3 | 3 | from unittest.mock import Mock |
4 | 4 |
|
| 5 | +import pytest |
| 6 | + |
5 | 7 | from openstack_mcp_server.tools.image_tools import ImageTools |
6 | 8 | from openstack_mcp_server.tools.request.image import CreateImage |
7 | 9 | from openstack_mcp_server.tools.response.image import Image |
@@ -46,80 +48,135 @@ def image_factory(**overrides): |
46 | 48 |
|
47 | 49 | return defaults |
48 | 50 |
|
49 | | - def test_get_image_images_success(self, mock_get_openstack_conn_image): |
| 51 | + def test_get_images_success(self, mock_get_openstack_conn_image): |
50 | 52 | """Test getting image images successfully.""" |
51 | 53 | mock_conn = mock_get_openstack_conn_image |
52 | 54 |
|
53 | | - # Create mock image objects |
54 | | - mock_image1 = Mock() |
55 | | - mock_image1.name = "ubuntu-20.04-server" |
56 | | - mock_image1.id = "img-123-abc-def" |
57 | | - mock_image1.status = "active" |
| 55 | + mock_image1 = self.image_factory( |
| 56 | + id="img-123-abc-def", |
| 57 | + name="ubuntu-20.04-server", |
| 58 | + status="active", |
| 59 | + visibility="public", |
| 60 | + checksum="abc123", |
| 61 | + size=1073741824, |
| 62 | + ) |
58 | 63 |
|
59 | | - mock_image2 = Mock() |
60 | | - mock_image2.name = "centos-8-stream" |
61 | | - mock_image2.id = "img-456-ghi-jkl" |
62 | | - mock_image2.status = "active" |
| 64 | + mock_image2 = self.image_factory( |
| 65 | + id="img-456-ghi-jkl", |
| 66 | + name="centos-8-stream", |
| 67 | + status="active", |
| 68 | + visibility="public", |
| 69 | + checksum="def456", |
| 70 | + size=2147483648, |
| 71 | + ) |
63 | 72 |
|
64 | | - # Configure mock image.images() |
65 | 73 | mock_conn.image.images.return_value = [mock_image1, mock_image2] |
66 | 74 |
|
67 | | - # Test ImageTools |
68 | | - image_tools = ImageTools() |
69 | | - result = image_tools.get_image_images() |
| 75 | + result = ImageTools().get_images() |
70 | 76 |
|
71 | | - # Verify results |
72 | | - expected_output = ( |
73 | | - "ubuntu-20.04-server (img-123-abc-def) - Status: active\n" |
74 | | - "centos-8-stream (img-456-ghi-jkl) - Status: active" |
75 | | - ) |
| 77 | + expected_output = [ |
| 78 | + Image(**mock_image1), |
| 79 | + Image(**mock_image2), |
| 80 | + ] |
76 | 81 | assert result == expected_output |
77 | 82 |
|
78 | | - # Verify mock calls |
79 | 83 | mock_conn.image.images.assert_called_once() |
80 | 84 |
|
81 | | - def test_get_image_images_empty_list(self, mock_get_openstack_conn_image): |
| 85 | + def test_get_images_empty_list(self, mock_get_openstack_conn_image): |
82 | 86 | """Test getting image images when no images exist.""" |
83 | 87 | mock_conn = mock_get_openstack_conn_image |
84 | | - |
85 | | - # Empty image list |
86 | 88 | mock_conn.image.images.return_value = [] |
87 | 89 |
|
88 | | - image_tools = ImageTools() |
89 | | - result = image_tools.get_image_images() |
90 | | - |
91 | | - # Verify empty string |
92 | | - assert result == "" |
| 90 | + result = ImageTools().get_images() |
93 | 91 |
|
| 92 | + assert result == [] |
94 | 93 | mock_conn.image.images.assert_called_once() |
95 | 94 |
|
96 | | - def test_get_image_images_with_empty_name( |
| 95 | + @pytest.mark.parametrize( |
| 96 | + "filter_name,filter_value,expected_count", |
| 97 | + [ |
| 98 | + ("name", "ubuntu-20.04-server", 1), # exact name match |
| 99 | + ("name", "ubuntu", 0), # partial match not supported |
| 100 | + ("name", "nonexistent", 0), # non-existent name |
| 101 | + ("name", "", 2), # empty filter value |
| 102 | + ("name", " ", 2), # whitespace only |
| 103 | + ("status", "active", 2), |
| 104 | + ("visibility", "public", 2), |
| 105 | + ("status", "deleted", 0), |
| 106 | + ("visibility", "private", 0), |
| 107 | + ], |
| 108 | + ) |
| 109 | + def test_get_images_with_filters( |
97 | 110 | self, |
98 | 111 | mock_get_openstack_conn_image, |
| 112 | + filter_name, |
| 113 | + filter_value, |
| 114 | + expected_count, |
99 | 115 | ): |
100 | | - """Test images with empty or None names.""" |
| 116 | + """Test getting images with various filters.""" |
101 | 117 | mock_conn = mock_get_openstack_conn_image |
102 | 118 |
|
103 | | - # Images with empty name (edge case) |
104 | | - mock_image1 = Mock() |
105 | | - mock_image1.name = "normal-image" |
106 | | - mock_image1.id = "img-normal" |
107 | | - mock_image1.status = "active" |
108 | | - |
109 | | - mock_image2 = Mock() |
110 | | - mock_image2.name = "" # Empty name |
111 | | - mock_image2.id = "img-empty-name" |
112 | | - mock_image2.status = "active" |
113 | | - |
114 | | - mock_conn.image.images.return_value = [mock_image1, mock_image2] |
115 | | - |
116 | | - image_tools = ImageTools() |
117 | | - result = image_tools.get_image_images() |
| 119 | + mock_image1 = self.image_factory( |
| 120 | + id="img-123-abc-def", |
| 121 | + name="ubuntu-20.04-server", |
| 122 | + status="active", |
| 123 | + visibility="public", |
| 124 | + checksum="abc123", |
| 125 | + size=1073741824, |
| 126 | + ) |
118 | 127 |
|
119 | | - assert "normal-image (img-normal) - Status: active" in result |
120 | | - assert " (img-empty-name) - Status: active" in result # Empty name |
| 128 | + mock_image2 = self.image_factory( |
| 129 | + id="img-456-ghi-jkl", |
| 130 | + name="centos-8-stream", |
| 131 | + status="active", |
| 132 | + visibility="public", |
| 133 | + checksum="def456", |
| 134 | + size=2147483648, |
| 135 | + ) |
121 | 136 |
|
122 | | - mock_conn.image.images.assert_called_once() |
| 137 | + if filter_name == "name": |
| 138 | + if filter_value == "ubuntu-20.04-server": |
| 139 | + mock_conn.image.images.return_value = [mock_image1] |
| 140 | + elif filter_value in ["", " "]: |
| 141 | + mock_conn.image.images.return_value = [ |
| 142 | + mock_image1, |
| 143 | + mock_image2, |
| 144 | + ] |
| 145 | + else: |
| 146 | + mock_conn.image.images.return_value = [] |
| 147 | + elif filter_name == "status": |
| 148 | + if filter_value == "active": |
| 149 | + mock_conn.image.images.return_value = [ |
| 150 | + mock_image1, |
| 151 | + mock_image2, |
| 152 | + ] |
| 153 | + else: |
| 154 | + mock_conn.image.images.return_value = [] |
| 155 | + elif filter_name == "visibility": |
| 156 | + if filter_value == "public": |
| 157 | + mock_conn.image.images.return_value = [ |
| 158 | + mock_image1, |
| 159 | + mock_image2, |
| 160 | + ] |
| 161 | + else: |
| 162 | + mock_conn.image.images.return_value = [] |
| 163 | + |
| 164 | + result = ImageTools().get_images(**{filter_name: filter_value}) |
| 165 | + |
| 166 | + if expected_count == 0: |
| 167 | + assert result == [] |
| 168 | + elif expected_count == 1: |
| 169 | + assert result == [Image(**mock_image1)] |
| 170 | + else: |
| 171 | + assert result == [Image(**mock_image1), Image(**mock_image2)] |
| 172 | + |
| 173 | + # For empty/whitespace filters, no filter should be applied |
| 174 | + if filter_value in ["", " "]: |
| 175 | + mock_conn.image.images.assert_called_once_with() |
| 176 | + else: |
| 177 | + mock_conn.image.images.assert_called_once_with( |
| 178 | + **{filter_name: filter_value} |
| 179 | + ) |
123 | 180 |
|
124 | 181 | def test_create_image_success_with_volume_id( |
125 | 182 | self, |
|
0 commit comments