Skip to content

Commit 2b1e8b9

Browse files
Copilotalvarolopez
andcommitted
Refactor test_prometheus.py to use fixtures and reduce code repetition
- Created pytest fixtures for common test setup: - extract_dates: fixture for date range - mock_server: fixture for mock server object - mock_flavors: fixture for mock flavors dictionary - configured_extractor: fixture for pre-configured extractor instance - prometheus_success_response: fixture for successful Prometheus response - prometheus_error_response: fixture for failed Prometheus response - Removed duplicated CONF configuration code - Removed duplicated extractor setup code - Removed duplicated mock setup code - Tests are now more concise and maintainable - All tests still pass (4/4) Co-authored-by: alvarolopez <468751+alvarolopez@users.noreply.github.com>
1 parent dfd5a52 commit 2b1e8b9

File tree

1 file changed

+118
-147
lines changed

1 file changed

+118
-147
lines changed

caso/tests/extract/test_prometheus.py

Lines changed: 118 additions & 147 deletions
Original file line numberDiff line numberDiff line change
@@ -27,207 +27,178 @@
2727
CONF = cfg.CONF
2828

2929

30+
@pytest.fixture
31+
def extract_dates():
32+
"""Fixture for extraction date range."""
33+
return {
34+
"extract_from": datetime.datetime(2023, 5, 25, 0, 0, 0),
35+
"extract_to": datetime.datetime(2023, 5, 25, 23, 59, 59),
36+
}
37+
38+
39+
@pytest.fixture
40+
def mock_server():
41+
"""Fixture for a mock server."""
42+
server = mock.Mock()
43+
server.id = "e3c5aeef-37b8-4332-ad9f-9d068f156dc2"
44+
server.name = "test-vm-1"
45+
server.status = "ACTIVE"
46+
server.created = "2023-05-25T12:00:00Z"
47+
server.flavor = {"id": "flavor-1"}
48+
return server
49+
50+
51+
@pytest.fixture
52+
def mock_flavors():
53+
"""Fixture for mock flavors."""
54+
return {"flavor-1": {"vcpus": 2, "id": "flavor-1"}}
55+
56+
57+
@pytest.fixture
58+
def configured_extractor(mock_flavors):
59+
"""Fixture for a configured EnergyConsumptionExtractor."""
60+
# Configure CONF
61+
CONF.set_override("site_name", "TEST-Site")
62+
CONF.set_override("service_name", "TEST-Service")
63+
64+
with mock.patch(
65+
"caso.extract.openstack.base.BaseOpenStackExtractor.__init__",
66+
return_value=None,
67+
), mock.patch(
68+
"caso.extract.prometheus.EnergyConsumptionExtractor._get_flavors",
69+
return_value=mock_flavors,
70+
), mock.patch(
71+
"caso.extract.openstack.base.BaseOpenStackExtractor._get_nova_client"
72+
):
73+
extractor = EnergyConsumptionExtractor("test-project", "test-vo")
74+
extractor.project = "test-project"
75+
extractor.vo = "test-vo"
76+
extractor.project_id = "test-project-id"
77+
extractor.cloud_type = "openstack"
78+
yield extractor
79+
80+
81+
@pytest.fixture
82+
def prometheus_success_response():
83+
"""Fixture for a successful Prometheus response."""
84+
response = mock.Mock()
85+
response.json.return_value = {
86+
"status": "success",
87+
"data": {
88+
"result": [
89+
{
90+
"metric": {"instance": "test"},
91+
"value": [1685051946, "5.0"],
92+
}
93+
]
94+
},
95+
}
96+
response.raise_for_status = mock.Mock()
97+
return response
98+
99+
100+
@pytest.fixture
101+
def prometheus_error_response():
102+
"""Fixture for a failed Prometheus response."""
103+
response = mock.Mock()
104+
response.json.return_value = {
105+
"status": "error",
106+
"error": "query failed",
107+
}
108+
response.raise_for_status = mock.Mock()
109+
return response
110+
111+
30112
class TestEnergyConsumptionExtractor:
31113
"""Test the energy consumption extractor."""
32114

33-
@mock.patch("caso.extract.prometheus.EnergyConsumptionExtractor._get_flavors")
34-
@mock.patch("caso.extract.openstack.base.BaseOpenStackExtractor._get_nova_client")
35-
@mock.patch("caso.extract.openstack.base.BaseOpenStackExtractor.__init__")
36115
@mock.patch("caso.extract.prometheus.requests.get")
37116
def test_extract_with_results(
38-
self, mock_get, mock_base_init, mock_get_nova, mock_get_flavors
117+
self,
118+
mock_get,
119+
configured_extractor,
120+
mock_server,
121+
extract_dates,
122+
prometheus_success_response,
39123
):
40124
"""Test extraction with successful Prometheus query."""
41-
# Configure CONF
42-
CONF.set_override("site_name", "TEST-Site")
43-
CONF.set_override("service_name", "TEST-Service")
44-
45-
# Mock the base class __init__ to do nothing
46-
mock_base_init.return_value = None
47-
48-
# Mock flavors
49-
mock_get_flavors.return_value = {"flavor-1": {"vcpus": 2, "id": "flavor-1"}}
50-
51-
# Mock Nova client and servers
52-
mock_server1 = mock.Mock()
53-
mock_server1.id = "e3c5aeef-37b8-4332-ad9f-9d068f156dc2"
54-
mock_server1.name = "test-vm-1"
55-
mock_server1.status = "ACTIVE"
56-
mock_server1.created = "2023-05-25T12:00:00Z"
57-
mock_server1.flavor = {"id": "flavor-1"}
58-
125+
# Create a second server
59126
mock_server2 = mock.Mock()
60127
mock_server2.id = "f4d6bedf-48c9-5f2f-b043-ebb4f9e65d73"
61128
mock_server2.name = "test-vm-2"
62129
mock_server2.status = "ACTIVE"
63130
mock_server2.created = "2023-05-25T12:00:00Z"
64131
mock_server2.flavor = {"id": "flavor-1"}
65132

66-
mock_nova = mock.Mock()
67-
mock_nova.servers.list.return_value = [mock_server1, mock_server2]
68-
mock_get_nova.return_value = mock_nova
69-
70-
# Create extractor and manually set required attributes
71-
extractor = EnergyConsumptionExtractor("test-project", "test-vo")
72-
extractor.project = "test-project"
73-
extractor.vo = "test-vo"
74-
extractor.project_id = "test-project-id"
75-
extractor.cloud_type = "openstack"
133+
# Mock Nova client with servers
134+
configured_extractor.nova = mock.Mock()
135+
configured_extractor.nova.servers.list.return_value = [
136+
mock_server,
137+
mock_server2,
138+
]
76139

77140
# Mock Prometheus response
78-
mock_response = mock.Mock()
79-
mock_response.json.return_value = {
80-
"status": "success",
81-
"data": {
82-
"result": [
83-
{
84-
"metric": {"instance": "test"},
85-
"value": [1685051946, "5.0"],
86-
}
87-
]
88-
},
89-
}
90-
mock_response.raise_for_status = mock.Mock()
91-
mock_get.return_value = mock_response
141+
mock_get.return_value = prometheus_success_response
92142

93143
# Extract records
94-
extract_from = datetime.datetime(2023, 5, 25, 0, 0, 0)
95-
extract_to = datetime.datetime(2023, 5, 25, 23, 59, 59)
96-
records = extractor.extract(extract_from, extract_to)
144+
records = configured_extractor.extract(**extract_dates)
97145

98146
# Verify - should create 2 records (one per VM)
99147
assert len(records) == 2
100148
assert records[0].energy_wh == 5.0
101149
assert records[0].owner == "test-vo"
102150
assert records[0].status == "active"
103151

104-
@mock.patch("caso.extract.prometheus.EnergyConsumptionExtractor._get_flavors")
105-
@mock.patch("caso.extract.openstack.base.BaseOpenStackExtractor._get_nova_client")
106-
@mock.patch("caso.extract.openstack.base.BaseOpenStackExtractor.__init__")
107-
def test_extract_with_no_vms(self, mock_base_init, mock_get_nova, mock_get_flavors):
152+
def test_extract_with_no_vms(self, configured_extractor, extract_dates):
108153
"""Test extraction when there are no VMs."""
109-
# Configure CONF
110-
CONF.set_override("site_name", "TEST-Site")
111-
CONF.set_override("service_name", "TEST-Service")
112-
113-
# Mock the base class __init__ to do nothing
114-
mock_base_init.return_value = None
115-
mock_get_flavors.return_value = {}
116-
117154
# Mock Nova client with no servers
118-
mock_nova = mock.Mock()
119-
mock_nova.servers.list.return_value = []
120-
mock_get_nova.return_value = mock_nova
121-
122-
# Create extractor and manually set required attributes
123-
extractor = EnergyConsumptionExtractor("test-project", "test-vo")
124-
extractor.project = "test-project"
125-
extractor.vo = "test-vo"
126-
extractor.project_id = "test-project-id"
155+
configured_extractor.nova = mock.Mock()
156+
configured_extractor.nova.servers.list.return_value = []
127157

128158
# Extract records
129-
extract_from = datetime.datetime(2023, 5, 25, 0, 0, 0)
130-
extract_to = datetime.datetime(2023, 5, 25, 23, 59, 59)
131-
records = extractor.extract(extract_from, extract_to)
159+
records = configured_extractor.extract(**extract_dates)
132160

133161
# Verify - no VMs, no records
134162
assert len(records) == 0
135163

136-
@mock.patch("caso.extract.prometheus.EnergyConsumptionExtractor._get_flavors")
137-
@mock.patch("caso.extract.openstack.base.BaseOpenStackExtractor._get_nova_client")
138-
@mock.patch("caso.extract.openstack.base.BaseOpenStackExtractor.__init__")
139164
@mock.patch("caso.extract.prometheus.requests.get")
140165
def test_extract_with_failed_query(
141-
self, mock_get, mock_base_init, mock_get_nova, mock_get_flavors
166+
self,
167+
mock_get,
168+
configured_extractor,
169+
mock_server,
170+
extract_dates,
171+
prometheus_error_response,
142172
):
143173
"""Test extraction when Prometheus query fails."""
144-
# Configure CONF
145-
CONF.set_override("site_name", "TEST-Site")
146-
CONF.set_override("service_name", "TEST-Service")
147-
148-
# Mock the base class __init__ to do nothing
149-
mock_base_init.return_value = None
150-
mock_get_flavors.return_value = {}
151-
152-
# Mock Nova client and servers
153-
mock_server = mock.Mock()
154-
mock_server.id = "vm-uuid-1"
155-
mock_server.name = "test-vm-1"
156-
mock_server.status = "ACTIVE"
157-
mock_server.created = "2023-05-25T12:00:00Z"
158-
mock_server.flavor = {"id": "flavor-1"}
159-
160-
mock_nova = mock.Mock()
161-
mock_nova.servers.list.return_value = [mock_server]
162-
mock_get_nova.return_value = mock_nova
163-
164-
# Create extractor and manually set required attributes
165-
extractor = EnergyConsumptionExtractor("test-project", "test-vo")
166-
extractor.project = "test-project"
167-
extractor.vo = "test-vo"
168-
extractor.project_id = "test-project-id"
169-
extractor.cloud_type = "openstack"
174+
# Mock Nova client with servers
175+
configured_extractor.nova = mock.Mock()
176+
configured_extractor.nova.servers.list.return_value = [mock_server]
170177

171178
# Mock Prometheus error response
172-
mock_response = mock.Mock()
173-
mock_response.json.return_value = {
174-
"status": "error",
175-
"error": "query failed",
176-
}
177-
mock_response.raise_for_status = mock.Mock()
178-
mock_get.return_value = mock_response
179+
mock_get.return_value = prometheus_error_response
179180

180181
# Extract records
181-
extract_from = datetime.datetime(2023, 5, 25, 0, 0, 0)
182-
extract_to = datetime.datetime(2023, 5, 25, 23, 59, 59)
183-
records = extractor.extract(extract_from, extract_to)
182+
records = configured_extractor.extract(**extract_dates)
184183

185184
# Verify - query failed, no records
186185
assert len(records) == 0
187186

188-
@mock.patch("caso.extract.prometheus.EnergyConsumptionExtractor._get_flavors")
189-
@mock.patch("caso.extract.openstack.base.BaseOpenStackExtractor._get_nova_client")
190-
@mock.patch("caso.extract.openstack.base.BaseOpenStackExtractor.__init__")
191-
@mock.patch("caso.extract.prometheus.requests.get")
192187
@mock.patch("caso.extract.prometheus.LOG")
188+
@mock.patch("caso.extract.prometheus.requests.get")
193189
def test_extract_with_request_exception(
194-
self, mock_log, mock_get, mock_base_init, mock_get_nova, mock_get_flavors
190+
self, mock_get, mock_log, configured_extractor, mock_server, extract_dates
195191
):
196192
"""Test extraction when request to Prometheus fails."""
197-
# Configure CONF
198-
CONF.set_override("site_name", "TEST-Site")
199-
CONF.set_override("service_name", "TEST-Service")
200-
201-
# Mock the base class __init__ to do nothing
202-
mock_base_init.return_value = None
203-
mock_get_flavors.return_value = {}
204-
205-
# Mock Nova client and servers
206-
mock_server = mock.Mock()
207-
mock_server.id = "vm-uuid-1"
208-
mock_server.name = "test-vm-1"
209-
mock_server.status = "ACTIVE"
210-
mock_server.created = "2023-05-25T12:00:00Z"
211-
mock_server.flavor = {"id": "flavor-1"}
212-
213-
mock_nova = mock.Mock()
214-
mock_nova.servers.list.return_value = [mock_server]
215-
mock_get_nova.return_value = mock_nova
216-
217-
# Create extractor and manually set required attributes
218-
extractor = EnergyConsumptionExtractor("test-project", "test-vo")
219-
extractor.project = "test-project"
220-
extractor.vo = "test-vo"
221-
extractor.project_id = "test-project-id"
222-
extractor.cloud_type = "openstack"
193+
# Mock Nova client with servers
194+
configured_extractor.nova = mock.Mock()
195+
configured_extractor.nova.servers.list.return_value = [mock_server]
223196

224197
# Mock request exception
225198
mock_get.side_effect = Exception("Connection error")
226199

227200
# Extract records
228-
extract_from = datetime.datetime(2023, 5, 25, 0, 0, 0)
229-
extract_to = datetime.datetime(2023, 5, 25, 23, 59, 59)
230-
records = extractor.extract(extract_from, extract_to)
201+
records = configured_extractor.extract(**extract_dates)
231202

232203
# Verify - exception caught, no records
233204
assert len(records) == 0

0 commit comments

Comments
 (0)