e-data 2.0.0b1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- e_data-2.0.0b1.dist-info/METADATA +214 -0
- e_data-2.0.0b1.dist-info/RECORD +21 -0
- e_data-2.0.0b1.dist-info/WHEEL +5 -0
- e_data-2.0.0b1.dist-info/licenses/LICENSE +674 -0
- e_data-2.0.0b1.dist-info/top_level.txt +1 -0
- edata/__init__.py +0 -0
- edata/const.py +56 -0
- edata/helpers.py +312 -0
- edata/tests/__init__.py +0 -0
- edata/tests/connectors/__init__.py +0 -0
- edata/tests/connectors/test_datadis_connector.py +178 -0
- edata/tests/connectors/test_redata_connector.py +29 -0
- edata/tests/services/__init__.py +0 -0
- edata/tests/services/test_billing_service.py +1169 -0
- edata/tests/services/test_consumption_service.py +955 -0
- edata/tests/services/test_contract_service.py +216 -0
- edata/tests/services/test_database_service.py +415 -0
- edata/tests/services/test_maximeter_service.py +83 -0
- edata/tests/services/test_supply_service.py +251 -0
- edata/tests/test_helpers.py +612 -0
- edata/utils.py +164 -0
|
@@ -0,0 +1,955 @@
|
|
|
1
|
+
"""Tests for ConsumptionService."""
|
|
2
|
+
|
|
3
|
+
import shutil
|
|
4
|
+
import tempfile
|
|
5
|
+
from datetime import date, datetime, timedelta
|
|
6
|
+
from unittest.mock import AsyncMock, Mock, patch
|
|
7
|
+
|
|
8
|
+
import pytest
|
|
9
|
+
import pytest_asyncio
|
|
10
|
+
|
|
11
|
+
from edata.connectors.datadis import DatadisConnector
|
|
12
|
+
from edata.models.consumption import Consumption, ConsumptionAggregated
|
|
13
|
+
from edata.services.consumption import ConsumptionService
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class TestConsumptionService:
|
|
17
|
+
"""Test suite for ConsumptionService."""
|
|
18
|
+
|
|
19
|
+
@pytest.fixture
|
|
20
|
+
def temp_dir(self):
|
|
21
|
+
"""Create a temporary directory for tests."""
|
|
22
|
+
temp_dir = tempfile.mkdtemp()
|
|
23
|
+
yield temp_dir
|
|
24
|
+
shutil.rmtree(temp_dir)
|
|
25
|
+
|
|
26
|
+
@pytest.fixture
|
|
27
|
+
def mock_datadis_connector(self):
|
|
28
|
+
"""Mock DatadisConnector for testing."""
|
|
29
|
+
with patch(
|
|
30
|
+
"edata.services.consumption.DatadisConnector"
|
|
31
|
+
) as mock_connector_class:
|
|
32
|
+
mock_connector = Mock(spec=DatadisConnector)
|
|
33
|
+
mock_connector_class.return_value = mock_connector
|
|
34
|
+
yield mock_connector, mock_connector_class
|
|
35
|
+
|
|
36
|
+
@pytest.fixture
|
|
37
|
+
def mock_database_service(self):
|
|
38
|
+
"""Mock DatabaseService for testing."""
|
|
39
|
+
with patch("edata.services.consumption.get_database_service") as mock_get_db:
|
|
40
|
+
mock_db = Mock()
|
|
41
|
+
# Hacer que los métodos async retornen AsyncMock
|
|
42
|
+
mock_db.get_consumptions = AsyncMock(return_value=[])
|
|
43
|
+
mock_db.save_consumption = AsyncMock(return_value=Mock())
|
|
44
|
+
mock_db.get_latest_consumption = AsyncMock(return_value=None)
|
|
45
|
+
mock_get_db.return_value = mock_db
|
|
46
|
+
yield mock_db
|
|
47
|
+
|
|
48
|
+
@pytest_asyncio.fixture
|
|
49
|
+
async def consumption_service(
|
|
50
|
+
self, temp_dir, mock_datadis_connector, mock_database_service
|
|
51
|
+
):
|
|
52
|
+
"""Create a ConsumptionService instance for testing."""
|
|
53
|
+
mock_connector, mock_connector_class = mock_datadis_connector
|
|
54
|
+
return ConsumptionService(
|
|
55
|
+
datadis_connector=mock_connector,
|
|
56
|
+
storage_dir=temp_dir,
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
@pytest.fixture
|
|
60
|
+
def sample_consumptions(self):
|
|
61
|
+
"""Sample consumption data for testing."""
|
|
62
|
+
return [
|
|
63
|
+
Consumption(
|
|
64
|
+
datetime=datetime(2024, 6, 15, 10, 0),
|
|
65
|
+
delta_h=1.0,
|
|
66
|
+
value_kwh=0.5,
|
|
67
|
+
surplus_kwh=0.0,
|
|
68
|
+
real=True,
|
|
69
|
+
),
|
|
70
|
+
Consumption(
|
|
71
|
+
datetime=datetime(2024, 6, 15, 11, 0),
|
|
72
|
+
delta_h=1.0,
|
|
73
|
+
value_kwh=0.7,
|
|
74
|
+
surplus_kwh=0.0,
|
|
75
|
+
real=True,
|
|
76
|
+
),
|
|
77
|
+
Consumption(
|
|
78
|
+
datetime=datetime(2024, 6, 15, 12, 0),
|
|
79
|
+
delta_h=1.0,
|
|
80
|
+
value_kwh=0.6,
|
|
81
|
+
surplus_kwh=0.0,
|
|
82
|
+
real=True,
|
|
83
|
+
),
|
|
84
|
+
]
|
|
85
|
+
|
|
86
|
+
@pytest.mark.asyncio
|
|
87
|
+
async def test_initialization(
|
|
88
|
+
self, temp_dir, mock_datadis_connector, mock_database_service
|
|
89
|
+
):
|
|
90
|
+
"""Test ConsumptionService initialization."""
|
|
91
|
+
mock_connector, mock_connector_class = mock_datadis_connector
|
|
92
|
+
|
|
93
|
+
service = ConsumptionService(
|
|
94
|
+
datadis_connector=mock_connector,
|
|
95
|
+
storage_dir=temp_dir,
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
# Verify service stores the connector and storage directory
|
|
99
|
+
assert service._datadis == mock_connector
|
|
100
|
+
assert service._storage_dir == temp_dir
|
|
101
|
+
|
|
102
|
+
# Verify database service is obtained lazily by calling _get_db_service
|
|
103
|
+
db_service = await service._get_db_service()
|
|
104
|
+
assert db_service is mock_database_service
|
|
105
|
+
|
|
106
|
+
@pytest.mark.asyncio
|
|
107
|
+
async def test_update_consumptions_success(
|
|
108
|
+
self,
|
|
109
|
+
consumption_service,
|
|
110
|
+
mock_datadis_connector,
|
|
111
|
+
mock_database_service,
|
|
112
|
+
sample_consumptions,
|
|
113
|
+
):
|
|
114
|
+
"""Test successful consumption update."""
|
|
115
|
+
mock_connector, mock_connector_class = mock_datadis_connector
|
|
116
|
+
cups = "ES1234567890123456789"
|
|
117
|
+
distributor_code = "123"
|
|
118
|
+
start_date = datetime(2024, 6, 15, 0, 0)
|
|
119
|
+
end_date = datetime(2024, 6, 15, 23, 59)
|
|
120
|
+
|
|
121
|
+
# Mock datadis connector response (now returns Pydantic models)
|
|
122
|
+
mock_connector.get_consumption_data.return_value = sample_consumptions
|
|
123
|
+
|
|
124
|
+
# Mock database service responses - no existing consumptions
|
|
125
|
+
mock_database_service.get_consumptions.return_value = []
|
|
126
|
+
|
|
127
|
+
# Execute update
|
|
128
|
+
result = await consumption_service.update_consumptions(
|
|
129
|
+
cups=cups,
|
|
130
|
+
distributor_code=distributor_code,
|
|
131
|
+
start_date=start_date,
|
|
132
|
+
end_date=end_date,
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
# Verify datadis connector was called correctly
|
|
136
|
+
mock_connector.get_consumption_data.assert_called_once_with(
|
|
137
|
+
cups=cups,
|
|
138
|
+
distributor_code=distributor_code,
|
|
139
|
+
start_date=start_date,
|
|
140
|
+
end_date=end_date,
|
|
141
|
+
measurement_type="0",
|
|
142
|
+
point_type=5,
|
|
143
|
+
authorized_nif=None,
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
# Verify database service was called for each consumption
|
|
147
|
+
assert mock_database_service.save_consumption.call_count == len(
|
|
148
|
+
sample_consumptions
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
# Verify result structure
|
|
152
|
+
assert result["success"] is True
|
|
153
|
+
assert result["cups"] == cups
|
|
154
|
+
assert result["period"]["start"] == start_date.isoformat()
|
|
155
|
+
assert result["period"]["end"] == end_date.isoformat()
|
|
156
|
+
assert result["stats"]["fetched"] == len(sample_consumptions)
|
|
157
|
+
assert result["stats"]["saved"] == len(sample_consumptions)
|
|
158
|
+
assert result["stats"]["updated"] == 0
|
|
159
|
+
|
|
160
|
+
@pytest.mark.asyncio
|
|
161
|
+
async def test_update_consumptions_with_existing_data(
|
|
162
|
+
self,
|
|
163
|
+
consumption_service,
|
|
164
|
+
mock_datadis_connector,
|
|
165
|
+
mock_database_service,
|
|
166
|
+
sample_consumptions,
|
|
167
|
+
):
|
|
168
|
+
"""Test consumption update with some existing data."""
|
|
169
|
+
mock_connector, mock_connector_class = mock_datadis_connector
|
|
170
|
+
cups = "ES1234567890123456789"
|
|
171
|
+
distributor_code = "123"
|
|
172
|
+
start_date = datetime(2024, 6, 15, 0, 0)
|
|
173
|
+
end_date = datetime(2024, 6, 15, 23, 59)
|
|
174
|
+
|
|
175
|
+
# Mock datadis connector response (now returns Pydantic models)
|
|
176
|
+
mock_connector.get_consumption_data.return_value = sample_consumptions
|
|
177
|
+
|
|
178
|
+
# Mock get_latest_consumption to return an existing consumption before the start date
|
|
179
|
+
mock_latest = Mock()
|
|
180
|
+
mock_latest.datetime = datetime(2024, 6, 14, 23, 0) # Day before start_date
|
|
181
|
+
mock_database_service.get_latest_consumption.return_value = mock_latest
|
|
182
|
+
|
|
183
|
+
# Mock database service responses - first consumption exists, others don't
|
|
184
|
+
def mock_get_consumptions(cups, start_date, end_date):
|
|
185
|
+
if start_date == sample_consumptions[0].datetime:
|
|
186
|
+
return [Mock()] # Existing consumption
|
|
187
|
+
return [] # No existing consumption
|
|
188
|
+
|
|
189
|
+
mock_database_service.get_consumptions.side_effect = mock_get_consumptions
|
|
190
|
+
|
|
191
|
+
# Execute update
|
|
192
|
+
result = await consumption_service.update_consumptions(
|
|
193
|
+
cups=cups,
|
|
194
|
+
distributor_code=distributor_code,
|
|
195
|
+
start_date=start_date,
|
|
196
|
+
end_date=end_date,
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
# Verify result
|
|
200
|
+
assert result["success"] is True
|
|
201
|
+
assert result["stats"]["fetched"] == len(sample_consumptions)
|
|
202
|
+
assert result["stats"]["saved"] == 2 # Two new consumptions
|
|
203
|
+
assert result["stats"]["updated"] == 1 # One updated consumption
|
|
204
|
+
|
|
205
|
+
@pytest.mark.asyncio
|
|
206
|
+
async def test_update_consumptions_with_optional_parameters(
|
|
207
|
+
self,
|
|
208
|
+
consumption_service,
|
|
209
|
+
mock_datadis_connector,
|
|
210
|
+
mock_database_service,
|
|
211
|
+
sample_consumptions,
|
|
212
|
+
):
|
|
213
|
+
"""Test consumption update with optional parameters."""
|
|
214
|
+
mock_connector, mock_connector_class = mock_datadis_connector
|
|
215
|
+
cups = "ES1234567890123456789"
|
|
216
|
+
distributor_code = "123"
|
|
217
|
+
start_date = datetime(2024, 6, 15, 0, 0)
|
|
218
|
+
end_date = datetime(2024, 6, 15, 23, 59)
|
|
219
|
+
measurement_type = "1"
|
|
220
|
+
point_type = 3
|
|
221
|
+
authorized_nif = "12345678A"
|
|
222
|
+
|
|
223
|
+
# Mock datadis connector response (now returns Pydantic models)
|
|
224
|
+
mock_connector.get_consumption_data.return_value = sample_consumptions
|
|
225
|
+
mock_database_service.get_consumptions.return_value = []
|
|
226
|
+
|
|
227
|
+
# Execute update with optional parameters
|
|
228
|
+
result = await consumption_service.update_consumptions(
|
|
229
|
+
cups=cups,
|
|
230
|
+
distributor_code=distributor_code,
|
|
231
|
+
start_date=start_date,
|
|
232
|
+
end_date=end_date,
|
|
233
|
+
measurement_type=measurement_type,
|
|
234
|
+
point_type=point_type,
|
|
235
|
+
authorized_nif=authorized_nif,
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
# Verify datadis connector was called with optional parameters
|
|
239
|
+
mock_connector.get_consumption_data.assert_called_once_with(
|
|
240
|
+
cups=cups,
|
|
241
|
+
distributor_code=distributor_code,
|
|
242
|
+
start_date=start_date,
|
|
243
|
+
end_date=end_date,
|
|
244
|
+
measurement_type=measurement_type,
|
|
245
|
+
point_type=point_type,
|
|
246
|
+
authorized_nif=authorized_nif,
|
|
247
|
+
)
|
|
248
|
+
|
|
249
|
+
assert result["success"] is True
|
|
250
|
+
|
|
251
|
+
@pytest.mark.asyncio
|
|
252
|
+
async def test_update_consumptions_error_handling(
|
|
253
|
+
self, consumption_service, mock_datadis_connector, mock_database_service
|
|
254
|
+
):
|
|
255
|
+
"""Test consumption update error handling."""
|
|
256
|
+
mock_connector, mock_connector_class = mock_datadis_connector
|
|
257
|
+
cups = "ES1234567890123456789"
|
|
258
|
+
distributor_code = "123"
|
|
259
|
+
start_date = datetime(2024, 6, 15, 0, 0)
|
|
260
|
+
end_date = datetime(2024, 6, 15, 23, 59)
|
|
261
|
+
|
|
262
|
+
# Mock datadis connector to raise an exception
|
|
263
|
+
error_message = "API connection failed"
|
|
264
|
+
mock_connector.get_consumption_data.side_effect = Exception(error_message)
|
|
265
|
+
|
|
266
|
+
# Mock database service to return None for get_latest_consumption (no existing data)
|
|
267
|
+
mock_database_service.get_latest_consumption.return_value = None
|
|
268
|
+
|
|
269
|
+
# Execute update
|
|
270
|
+
result = await consumption_service.update_consumptions(
|
|
271
|
+
cups=cups,
|
|
272
|
+
distributor_code=distributor_code,
|
|
273
|
+
start_date=start_date,
|
|
274
|
+
end_date=end_date,
|
|
275
|
+
)
|
|
276
|
+
|
|
277
|
+
# Verify error result
|
|
278
|
+
assert result["success"] is False
|
|
279
|
+
assert result["cups"] == cups
|
|
280
|
+
assert result["error"] == error_message
|
|
281
|
+
assert result["period"]["start"] == start_date.isoformat()
|
|
282
|
+
assert result["period"]["end"] == end_date.isoformat()
|
|
283
|
+
|
|
284
|
+
# Verify database service was not called
|
|
285
|
+
mock_database_service.save_consumption.assert_not_called()
|
|
286
|
+
|
|
287
|
+
@pytest.mark.asyncio
|
|
288
|
+
async def test_update_consumptions_with_force_full_update(
|
|
289
|
+
self,
|
|
290
|
+
consumption_service,
|
|
291
|
+
mock_datadis_connector,
|
|
292
|
+
mock_database_service,
|
|
293
|
+
sample_consumptions,
|
|
294
|
+
):
|
|
295
|
+
"""Test consumption update with force_full_update=True ignores existing data."""
|
|
296
|
+
mock_connector, mock_connector_class = mock_datadis_connector
|
|
297
|
+
cups = "ES1234567890123456789"
|
|
298
|
+
distributor_code = "123"
|
|
299
|
+
start_date = datetime(2024, 6, 15, 0, 0)
|
|
300
|
+
end_date = datetime(2024, 6, 15, 23, 59)
|
|
301
|
+
|
|
302
|
+
# Mock datadis connector response
|
|
303
|
+
mock_connector.get_consumption_data.return_value = sample_consumptions
|
|
304
|
+
|
|
305
|
+
# Mock get_latest_consumption to return existing data
|
|
306
|
+
mock_latest = Mock()
|
|
307
|
+
mock_latest.datetime = datetime(
|
|
308
|
+
2024, 6, 15, 12, 0
|
|
309
|
+
) # Within the requested range
|
|
310
|
+
mock_database_service.get_latest_consumption.return_value = mock_latest
|
|
311
|
+
|
|
312
|
+
# Mock database service responses - no existing consumptions
|
|
313
|
+
mock_database_service.get_consumptions.return_value = []
|
|
314
|
+
|
|
315
|
+
# Execute update with force_full_update=True
|
|
316
|
+
result = await consumption_service.update_consumptions(
|
|
317
|
+
cups=cups,
|
|
318
|
+
distributor_code=distributor_code,
|
|
319
|
+
start_date=start_date,
|
|
320
|
+
end_date=end_date,
|
|
321
|
+
force_full_update=True,
|
|
322
|
+
)
|
|
323
|
+
|
|
324
|
+
# Verify datadis connector was called with original start_date (not optimized)
|
|
325
|
+
mock_connector.get_consumption_data.assert_called_once_with(
|
|
326
|
+
cups=cups,
|
|
327
|
+
distributor_code=distributor_code,
|
|
328
|
+
start_date=start_date, # Should use original start_date, not optimized
|
|
329
|
+
end_date=end_date,
|
|
330
|
+
measurement_type="0",
|
|
331
|
+
point_type=5,
|
|
332
|
+
authorized_nif=None,
|
|
333
|
+
)
|
|
334
|
+
|
|
335
|
+
# Verify result
|
|
336
|
+
assert result["success"] is True
|
|
337
|
+
assert result["stats"]["fetched"] == len(sample_consumptions)
|
|
338
|
+
|
|
339
|
+
@pytest.mark.asyncio
|
|
340
|
+
async def test_update_consumptions_incremental_optimization(
|
|
341
|
+
self,
|
|
342
|
+
consumption_service,
|
|
343
|
+
mock_datadis_connector,
|
|
344
|
+
mock_database_service,
|
|
345
|
+
sample_consumptions,
|
|
346
|
+
):
|
|
347
|
+
"""Test that consumption update optimizes by starting from last consumption date."""
|
|
348
|
+
mock_connector, mock_connector_class = mock_datadis_connector
|
|
349
|
+
cups = "ES1234567890123456789"
|
|
350
|
+
distributor_code = "123"
|
|
351
|
+
start_date = datetime(2024, 6, 15, 0, 0)
|
|
352
|
+
end_date = datetime(2024, 6, 15, 23, 59)
|
|
353
|
+
|
|
354
|
+
# Mock datadis connector response
|
|
355
|
+
mock_connector.get_consumption_data.return_value = sample_consumptions
|
|
356
|
+
|
|
357
|
+
# Mock get_latest_consumption to return existing data
|
|
358
|
+
mock_latest = Mock()
|
|
359
|
+
mock_latest.datetime = datetime(2024, 6, 15, 8, 0) # 8 AM on same day
|
|
360
|
+
mock_database_service.get_latest_consumption.return_value = mock_latest
|
|
361
|
+
|
|
362
|
+
# Mock database service responses - no existing consumptions for the new range
|
|
363
|
+
mock_database_service.get_consumptions.return_value = []
|
|
364
|
+
|
|
365
|
+
# Execute update
|
|
366
|
+
result = await consumption_service.update_consumptions(
|
|
367
|
+
cups=cups,
|
|
368
|
+
distributor_code=distributor_code,
|
|
369
|
+
start_date=start_date,
|
|
370
|
+
end_date=end_date,
|
|
371
|
+
)
|
|
372
|
+
|
|
373
|
+
# Verify datadis connector was called with optimized start_date (9 AM)
|
|
374
|
+
expected_optimized_start = datetime(2024, 6, 15, 9, 0) # last + 1 hour
|
|
375
|
+
mock_connector.get_consumption_data.assert_called_once_with(
|
|
376
|
+
cups=cups,
|
|
377
|
+
distributor_code=distributor_code,
|
|
378
|
+
start_date=expected_optimized_start, # Should be optimized
|
|
379
|
+
end_date=end_date,
|
|
380
|
+
measurement_type="0",
|
|
381
|
+
point_type=5,
|
|
382
|
+
authorized_nif=None,
|
|
383
|
+
)
|
|
384
|
+
|
|
385
|
+
# Verify result includes message about optimization
|
|
386
|
+
assert result["success"] is True
|
|
387
|
+
assert "message" in result
|
|
388
|
+
assert "missing data" in result["message"]
|
|
389
|
+
|
|
390
|
+
@pytest.mark.asyncio
|
|
391
|
+
async def test_update_consumptions_up_to_date(
|
|
392
|
+
self,
|
|
393
|
+
consumption_service,
|
|
394
|
+
mock_datadis_connector,
|
|
395
|
+
mock_database_service,
|
|
396
|
+
):
|
|
397
|
+
"""Test consumption update when data is already up to date."""
|
|
398
|
+
mock_connector, mock_connector_class = mock_datadis_connector
|
|
399
|
+
cups = "ES1234567890123456789"
|
|
400
|
+
distributor_code = "123"
|
|
401
|
+
start_date = datetime(2024, 6, 15, 0, 0)
|
|
402
|
+
end_date = datetime(2024, 6, 15, 23, 59)
|
|
403
|
+
|
|
404
|
+
# Mock get_latest_consumption to return data beyond end_date
|
|
405
|
+
mock_latest = Mock()
|
|
406
|
+
mock_latest.datetime = datetime(2024, 6, 16, 1, 0) # After end_date
|
|
407
|
+
mock_database_service.get_latest_consumption.return_value = mock_latest
|
|
408
|
+
|
|
409
|
+
# Execute update
|
|
410
|
+
result = await consumption_service.update_consumptions(
|
|
411
|
+
cups=cups,
|
|
412
|
+
distributor_code=distributor_code,
|
|
413
|
+
start_date=start_date,
|
|
414
|
+
end_date=end_date,
|
|
415
|
+
)
|
|
416
|
+
|
|
417
|
+
# Verify datadis connector was NOT called (data is up to date)
|
|
418
|
+
mock_connector.get_consumption_data.assert_not_called()
|
|
419
|
+
|
|
420
|
+
# Verify result indicates no new data needed
|
|
421
|
+
assert result["success"] is True
|
|
422
|
+
assert result["stats"]["fetched"] == 0
|
|
423
|
+
assert result["stats"]["skipped"] == "up_to_date"
|
|
424
|
+
assert "up to date" in result["message"]
|
|
425
|
+
|
|
426
|
+
@pytest.mark.asyncio
|
|
427
|
+
async def test_update_consumption_range_by_months_single_month(
|
|
428
|
+
self,
|
|
429
|
+
consumption_service,
|
|
430
|
+
mock_datadis_connector,
|
|
431
|
+
mock_database_service,
|
|
432
|
+
sample_consumptions,
|
|
433
|
+
):
|
|
434
|
+
"""Test consumption range update for a single month."""
|
|
435
|
+
mock_connector, mock_connector_class = mock_datadis_connector
|
|
436
|
+
cups = "ES1234567890123456789"
|
|
437
|
+
distributor_code = "123"
|
|
438
|
+
start_date = datetime(2024, 6, 1, 0, 0)
|
|
439
|
+
end_date = datetime(2024, 6, 30, 23, 59)
|
|
440
|
+
|
|
441
|
+
# Mock datadis connector response (now returns Pydantic models)
|
|
442
|
+
mock_connector.get_consumption_data.return_value = sample_consumptions
|
|
443
|
+
mock_database_service.get_consumptions.return_value = []
|
|
444
|
+
|
|
445
|
+
# Execute range update
|
|
446
|
+
result = await consumption_service.update_consumption_range_by_months(
|
|
447
|
+
cups=cups,
|
|
448
|
+
distributor_code=distributor_code,
|
|
449
|
+
start_date=start_date,
|
|
450
|
+
end_date=end_date,
|
|
451
|
+
)
|
|
452
|
+
|
|
453
|
+
# Verify result structure
|
|
454
|
+
assert result["success"] is True
|
|
455
|
+
assert result["cups"] == cups
|
|
456
|
+
assert result["months_processed"] == 1
|
|
457
|
+
assert result["total_stats"]["consumptions_fetched"] == len(sample_consumptions)
|
|
458
|
+
assert result["total_stats"]["consumptions_saved"] == len(sample_consumptions)
|
|
459
|
+
assert result["total_stats"]["consumptions_updated"] == 0
|
|
460
|
+
assert len(result["monthly_results"]) == 1
|
|
461
|
+
|
|
462
|
+
# Verify monthly result
|
|
463
|
+
monthly_result = result["monthly_results"][0]
|
|
464
|
+
assert monthly_result["month"] == "2024-06"
|
|
465
|
+
assert monthly_result["consumption"]["success"] is True
|
|
466
|
+
|
|
467
|
+
@pytest.mark.asyncio
|
|
468
|
+
async def test_update_consumption_range_by_months_multiple_months(
|
|
469
|
+
self,
|
|
470
|
+
consumption_service,
|
|
471
|
+
mock_datadis_connector,
|
|
472
|
+
mock_database_service,
|
|
473
|
+
sample_consumptions,
|
|
474
|
+
):
|
|
475
|
+
"""Test consumption range update for multiple months."""
|
|
476
|
+
mock_connector, mock_connector_class = mock_datadis_connector
|
|
477
|
+
cups = "ES1234567890123456789"
|
|
478
|
+
distributor_code = "123"
|
|
479
|
+
start_date = datetime(2024, 5, 15, 0, 0)
|
|
480
|
+
end_date = datetime(2024, 7, 15, 23, 59)
|
|
481
|
+
|
|
482
|
+
# Mock datadis connector response (now returns Pydantic models)
|
|
483
|
+
mock_connector.get_consumption_data.return_value = sample_consumptions
|
|
484
|
+
mock_database_service.get_consumptions.return_value = []
|
|
485
|
+
|
|
486
|
+
# Execute range update
|
|
487
|
+
result = await consumption_service.update_consumption_range_by_months(
|
|
488
|
+
cups=cups,
|
|
489
|
+
distributor_code=distributor_code,
|
|
490
|
+
start_date=start_date,
|
|
491
|
+
end_date=end_date,
|
|
492
|
+
)
|
|
493
|
+
|
|
494
|
+
# Should process 3 months: May (partial), June (full), July (partial)
|
|
495
|
+
assert result["months_processed"] == 3
|
|
496
|
+
assert len(result["monthly_results"]) == 3
|
|
497
|
+
|
|
498
|
+
# Verify month identifiers
|
|
499
|
+
months = [r["month"] for r in result["monthly_results"]]
|
|
500
|
+
assert "2024-05" in months
|
|
501
|
+
assert "2024-06" in months
|
|
502
|
+
assert "2024-07" in months
|
|
503
|
+
|
|
504
|
+
# Verify total stats
|
|
505
|
+
expected_total_fetched = len(sample_consumptions) * 3
|
|
506
|
+
assert result["total_stats"]["consumptions_fetched"] == expected_total_fetched
|
|
507
|
+
|
|
508
|
+
@pytest.mark.asyncio
|
|
509
|
+
async def test_update_consumption_range_by_months_with_errors(
|
|
510
|
+
self,
|
|
511
|
+
consumption_service,
|
|
512
|
+
mock_datadis_connector,
|
|
513
|
+
mock_database_service,
|
|
514
|
+
sample_consumptions,
|
|
515
|
+
):
|
|
516
|
+
"""Test consumption range update with some months failing."""
|
|
517
|
+
mock_connector, mock_connector_class = mock_datadis_connector
|
|
518
|
+
cups = "ES1234567890123456789"
|
|
519
|
+
distributor_code = "123"
|
|
520
|
+
start_date = datetime(2024, 6, 1, 0, 0)
|
|
521
|
+
end_date = datetime(2024, 8, 31, 23, 59)
|
|
522
|
+
|
|
523
|
+
# Mock datadis connector to fail on second call
|
|
524
|
+
call_count = 0
|
|
525
|
+
|
|
526
|
+
def mock_get_consumption_data(*args, **kwargs):
|
|
527
|
+
nonlocal call_count
|
|
528
|
+
call_count += 1
|
|
529
|
+
if call_count == 2: # Second month fails
|
|
530
|
+
raise Exception("API rate limit exceeded")
|
|
531
|
+
return sample_consumptions
|
|
532
|
+
|
|
533
|
+
mock_connector.get_consumption_data.side_effect = mock_get_consumption_data
|
|
534
|
+
mock_database_service.get_consumptions.return_value = []
|
|
535
|
+
|
|
536
|
+
# Execute range update
|
|
537
|
+
result = await consumption_service.update_consumption_range_by_months(
|
|
538
|
+
cups=cups,
|
|
539
|
+
distributor_code=distributor_code,
|
|
540
|
+
start_date=start_date,
|
|
541
|
+
end_date=end_date,
|
|
542
|
+
)
|
|
543
|
+
|
|
544
|
+
# Should process 3 months but with one failure
|
|
545
|
+
assert result["months_processed"] == 3
|
|
546
|
+
assert result["success"] is False # Overall failure due to one failed month
|
|
547
|
+
|
|
548
|
+
# Check individual month results
|
|
549
|
+
successful_months = [
|
|
550
|
+
r for r in result["monthly_results"] if r["consumption"]["success"]
|
|
551
|
+
]
|
|
552
|
+
failed_months = [
|
|
553
|
+
r for r in result["monthly_results"] if not r["consumption"]["success"]
|
|
554
|
+
]
|
|
555
|
+
|
|
556
|
+
assert len(successful_months) == 2
|
|
557
|
+
assert len(failed_months) == 1
|
|
558
|
+
|
|
559
|
+
@pytest.mark.asyncio
|
|
560
|
+
async def test_update_consumption_range_year_boundary(
|
|
561
|
+
self,
|
|
562
|
+
consumption_service,
|
|
563
|
+
mock_datadis_connector,
|
|
564
|
+
mock_database_service,
|
|
565
|
+
sample_consumptions,
|
|
566
|
+
):
|
|
567
|
+
"""Test consumption range update across year boundary."""
|
|
568
|
+
mock_connector, mock_connector_class = mock_datadis_connector
|
|
569
|
+
cups = "ES1234567890123456789"
|
|
570
|
+
distributor_code = "123"
|
|
571
|
+
start_date = datetime(2023, 12, 1, 0, 0)
|
|
572
|
+
end_date = datetime(2024, 2, 28, 23, 59)
|
|
573
|
+
|
|
574
|
+
# Mock datadis connector response (now returns Pydantic models)
|
|
575
|
+
mock_connector.get_consumption_data.return_value = sample_consumptions
|
|
576
|
+
mock_database_service.get_consumptions.return_value = []
|
|
577
|
+
|
|
578
|
+
# Execute range update
|
|
579
|
+
result = await consumption_service.update_consumption_range_by_months(
|
|
580
|
+
cups=cups,
|
|
581
|
+
distributor_code=distributor_code,
|
|
582
|
+
start_date=start_date,
|
|
583
|
+
end_date=end_date,
|
|
584
|
+
)
|
|
585
|
+
|
|
586
|
+
# Should process 3 months: December 2023, January 2024, February 2024
|
|
587
|
+
assert result["months_processed"] == 3
|
|
588
|
+
|
|
589
|
+
# Verify month identifiers
|
|
590
|
+
months = [r["month"] for r in result["monthly_results"]]
|
|
591
|
+
assert "2023-12" in months
|
|
592
|
+
assert "2024-01" in months
|
|
593
|
+
assert "2024-02" in months
|
|
594
|
+
|
|
595
|
+
@pytest.mark.asyncio
|
|
596
|
+
async def test_get_stored_consumptions_no_filters(
|
|
597
|
+
self, consumption_service, mock_database_service, sample_consumptions
|
|
598
|
+
):
|
|
599
|
+
"""Test getting stored consumptions without date filters."""
|
|
600
|
+
cups = "ES1234567890123456789"
|
|
601
|
+
|
|
602
|
+
# Mock database service response
|
|
603
|
+
mock_database_service.get_consumptions.return_value = sample_consumptions
|
|
604
|
+
|
|
605
|
+
# Execute get stored consumptions
|
|
606
|
+
result = await consumption_service.get_stored_consumptions(cups)
|
|
607
|
+
|
|
608
|
+
# Verify database service was called correctly
|
|
609
|
+
mock_database_service.get_consumptions.assert_called_once_with(cups, None, None)
|
|
610
|
+
|
|
611
|
+
# Verify result
|
|
612
|
+
assert result == sample_consumptions
|
|
613
|
+
|
|
614
|
+
@pytest.mark.asyncio
|
|
615
|
+
async def test_get_stored_consumptions_with_filters(
|
|
616
|
+
self, consumption_service, mock_database_service, sample_consumptions
|
|
617
|
+
):
|
|
618
|
+
"""Test getting stored consumptions with date filters."""
|
|
619
|
+
cups = "ES1234567890123456789"
|
|
620
|
+
start_date = datetime(2024, 6, 15, 0, 0)
|
|
621
|
+
end_date = datetime(2024, 6, 15, 23, 59)
|
|
622
|
+
|
|
623
|
+
# Mock database service response
|
|
624
|
+
filtered_consumptions = sample_consumptions[:2] # Return first two
|
|
625
|
+
mock_database_service.get_consumptions.return_value = filtered_consumptions
|
|
626
|
+
|
|
627
|
+
# Execute get stored consumptions with filters
|
|
628
|
+
result = await consumption_service.get_stored_consumptions(
|
|
629
|
+
cups=cups, start_date=start_date, end_date=end_date
|
|
630
|
+
)
|
|
631
|
+
|
|
632
|
+
# Verify database service was called correctly
|
|
633
|
+
mock_database_service.get_consumptions.assert_called_once_with(
|
|
634
|
+
cups, start_date, end_date
|
|
635
|
+
)
|
|
636
|
+
|
|
637
|
+
# Verify result
|
|
638
|
+
assert result == filtered_consumptions
|
|
639
|
+
|
|
640
|
+
@pytest.mark.asyncio
|
|
641
|
+
async def test_initialization_default_parameters(
|
|
642
|
+
self, temp_dir, mock_datadis_connector, mock_database_service
|
|
643
|
+
):
|
|
644
|
+
"""Test ConsumptionService initialization with default parameters."""
|
|
645
|
+
mock_connector, mock_connector_class = mock_datadis_connector
|
|
646
|
+
|
|
647
|
+
service = ConsumptionService(datadis_connector=mock_connector)
|
|
648
|
+
|
|
649
|
+
# Verify service stores the connector with default storage directory
|
|
650
|
+
assert service._datadis == mock_connector
|
|
651
|
+
assert service._storage_dir is None
|
|
652
|
+
|
|
653
|
+
@patch("edata.services.consumption._LOGGER")
|
|
654
|
+
@pytest.mark.asyncio
|
|
655
|
+
async def test_logging_during_operations(
|
|
656
|
+
self,
|
|
657
|
+
mock_logger,
|
|
658
|
+
consumption_service,
|
|
659
|
+
mock_datadis_connector,
|
|
660
|
+
mock_database_service,
|
|
661
|
+
sample_consumptions,
|
|
662
|
+
):
|
|
663
|
+
"""Test that appropriate logging occurs during operations."""
|
|
664
|
+
mock_connector, mock_connector_class = mock_datadis_connector
|
|
665
|
+
cups = "ES1234567890123456789"
|
|
666
|
+
distributor_code = "123"
|
|
667
|
+
start_date = datetime(2024, 6, 15, 0, 0)
|
|
668
|
+
end_date = datetime(2024, 6, 15, 23, 59)
|
|
669
|
+
|
|
670
|
+
# Mock datadis connector response (now returns Pydantic models)
|
|
671
|
+
mock_connector.get_consumption_data.return_value = sample_consumptions
|
|
672
|
+
mock_database_service.get_consumptions.return_value = []
|
|
673
|
+
|
|
674
|
+
# Execute update
|
|
675
|
+
await consumption_service.update_consumptions(
|
|
676
|
+
cups=cups,
|
|
677
|
+
distributor_code=distributor_code,
|
|
678
|
+
start_date=start_date,
|
|
679
|
+
end_date=end_date,
|
|
680
|
+
)
|
|
681
|
+
|
|
682
|
+
# Verify logging calls
|
|
683
|
+
assert mock_logger.info.call_count >= 2 # Start and completion logs
|
|
684
|
+
|
|
685
|
+
# Verify log messages contain expected information
|
|
686
|
+
log_calls = [call.args[0] for call in mock_logger.info.call_args_list]
|
|
687
|
+
assert any("Updating consumptions" in msg for msg in log_calls)
|
|
688
|
+
assert any("Consumption update completed" in msg for msg in log_calls)
|
|
689
|
+
|
|
690
|
+
@pytest.fixture
|
|
691
|
+
def sample_db_consumptions(self):
|
|
692
|
+
"""Sample database consumption data for aggregation testing."""
|
|
693
|
+
from edata.services.database import ConsumptionModel as DbConsumption
|
|
694
|
+
|
|
695
|
+
# Use Monday (weekday 0) instead of Saturday for proper tariff testing
|
|
696
|
+
base_date = datetime(2024, 6, 17) # Monday, June 17, 2024
|
|
697
|
+
|
|
698
|
+
# Create 48 hours of hourly data (2 days: Monday and Tuesday)
|
|
699
|
+
db_consumptions = []
|
|
700
|
+
for hour in range(48):
|
|
701
|
+
dt = base_date + timedelta(hours=hour)
|
|
702
|
+
# Vary consumption by hour to test tariff periods
|
|
703
|
+
if 10 <= dt.hour <= 13 or 18 <= dt.hour <= 21: # P1 hours
|
|
704
|
+
kwh = 1.5
|
|
705
|
+
elif dt.hour in [8, 9, 14, 15, 16, 17, 22, 23]: # P2 hours
|
|
706
|
+
kwh = 1.0
|
|
707
|
+
else: # P3 hours
|
|
708
|
+
kwh = 0.5
|
|
709
|
+
|
|
710
|
+
db_cons = Mock(spec=DbConsumption)
|
|
711
|
+
db_cons.datetime = dt
|
|
712
|
+
db_cons.delta_h = 1.0
|
|
713
|
+
db_cons.value_kwh = kwh
|
|
714
|
+
db_cons.surplus_kwh = (
|
|
715
|
+
0.1 if hour % 10 == 0 else 0.0
|
|
716
|
+
) # Some surplus every 10 hours
|
|
717
|
+
db_cons.real = True
|
|
718
|
+
|
|
719
|
+
db_consumptions.append(db_cons)
|
|
720
|
+
|
|
721
|
+
return db_consumptions
|
|
722
|
+
|
|
723
|
+
@pytest.mark.asyncio
|
|
724
|
+
async def test_get_daily_consumptions(
|
|
725
|
+
self,
|
|
726
|
+
consumption_service,
|
|
727
|
+
mock_datadis_connector,
|
|
728
|
+
mock_database_service,
|
|
729
|
+
sample_db_consumptions,
|
|
730
|
+
):
|
|
731
|
+
"""Test daily consumption aggregation."""
|
|
732
|
+
mock_connector, mock_connector_class = mock_datadis_connector
|
|
733
|
+
cups = "ES1234567890123456789"
|
|
734
|
+
start_date = datetime(2024, 6, 17, 0, 0) # Monday
|
|
735
|
+
end_date = datetime(2024, 6, 18, 23, 59) # Tuesday
|
|
736
|
+
|
|
737
|
+
# Mock database service to return sample data
|
|
738
|
+
mock_database_service.get_consumptions.return_value = sample_db_consumptions
|
|
739
|
+
|
|
740
|
+
# Execute daily aggregation
|
|
741
|
+
daily_consumptions = await consumption_service.get_daily_consumptions(
|
|
742
|
+
cups=cups, start_date=start_date, end_date=end_date
|
|
743
|
+
)
|
|
744
|
+
|
|
745
|
+
# Verify database service was called correctly
|
|
746
|
+
mock_database_service.get_consumptions.assert_called_once_with(
|
|
747
|
+
cups, start_date, end_date
|
|
748
|
+
)
|
|
749
|
+
|
|
750
|
+
# Should have 2 days of data
|
|
751
|
+
assert len(daily_consumptions) == 2
|
|
752
|
+
|
|
753
|
+
# Verify first day aggregation
|
|
754
|
+
day1 = daily_consumptions[0]
|
|
755
|
+
assert isinstance(day1, ConsumptionAggregated)
|
|
756
|
+
assert day1.datetime.date() == date(2024, 6, 17) # Monday
|
|
757
|
+
assert day1.delta_h == 24.0 # 24 hours
|
|
758
|
+
|
|
759
|
+
# Verify total consumption (should be sum of all hourly values)
|
|
760
|
+
expected_day1_total = (8 * 1.5) + (8 * 1.0) + (8 * 0.5) # P1 + P2 + P3 hours
|
|
761
|
+
assert day1.value_kwh == expected_day1_total
|
|
762
|
+
|
|
763
|
+
# Verify P1 consumption (hours 10-13, 18-21)
|
|
764
|
+
expected_p1 = 8 * 1.5 # 8 P1 hours at 1.5 kWh each
|
|
765
|
+
assert day1.value_p1_kwh == expected_p1
|
|
766
|
+
|
|
767
|
+
# Verify some surplus was recorded
|
|
768
|
+
assert day1.surplus_kwh > 0
|
|
769
|
+
|
|
770
|
+
# Verify second day
|
|
771
|
+
day2 = daily_consumptions[1]
|
|
772
|
+
assert day2.datetime.date() == date(2024, 6, 18) # Tuesday
|
|
773
|
+
assert day2.delta_h == 24.0
|
|
774
|
+
|
|
775
|
+
@pytest.mark.asyncio
|
|
776
|
+
async def test_get_monthly_consumptions(
|
|
777
|
+
self,
|
|
778
|
+
consumption_service,
|
|
779
|
+
mock_datadis_connector,
|
|
780
|
+
mock_database_service,
|
|
781
|
+
sample_db_consumptions,
|
|
782
|
+
):
|
|
783
|
+
"""Test monthly consumption aggregation."""
|
|
784
|
+
mock_connector, mock_connector_class = mock_datadis_connector
|
|
785
|
+
cups = "ES1234567890123456789"
|
|
786
|
+
start_date = datetime(2024, 6, 17, 0, 0) # Monday
|
|
787
|
+
end_date = datetime(2024, 6, 18, 23, 59) # Tuesday
|
|
788
|
+
|
|
789
|
+
# Mock database service to return sample data
|
|
790
|
+
mock_database_service.get_consumptions.return_value = sample_db_consumptions
|
|
791
|
+
|
|
792
|
+
# Execute monthly aggregation
|
|
793
|
+
monthly_consumptions = await consumption_service.get_monthly_consumptions(
|
|
794
|
+
cups=cups, start_date=start_date, end_date=end_date
|
|
795
|
+
)
|
|
796
|
+
|
|
797
|
+
# Verify database service was called correctly
|
|
798
|
+
mock_database_service.get_consumptions.assert_called_once_with(
|
|
799
|
+
cups, start_date, end_date
|
|
800
|
+
)
|
|
801
|
+
|
|
802
|
+
# Should have 1 month of data (both days in same month)
|
|
803
|
+
assert len(monthly_consumptions) == 1
|
|
804
|
+
|
|
805
|
+
# Verify monthly aggregation
|
|
806
|
+
month = monthly_consumptions[0]
|
|
807
|
+
assert isinstance(month, ConsumptionAggregated)
|
|
808
|
+
assert month.datetime.replace(day=1).date() == date(2024, 6, 1)
|
|
809
|
+
assert month.delta_h == 48.0 # 48 hours total
|
|
810
|
+
|
|
811
|
+
# Verify total consumption (should be sum of both days)
|
|
812
|
+
expected_total = 2 * ((8 * 1.5) + (8 * 1.0) + (8 * 0.5))
|
|
813
|
+
assert month.value_kwh == expected_total
|
|
814
|
+
|
|
815
|
+
# Verify P1 consumption
|
|
816
|
+
expected_p1 = 2 * (8 * 1.5) # 2 days * 8 P1 hours * 1.5 kWh
|
|
817
|
+
assert month.value_p1_kwh == expected_p1
|
|
818
|
+
|
|
819
|
+
@pytest.mark.asyncio
|
|
820
|
+
async def test_get_monthly_consumptions_with_cycle_start_day(
|
|
821
|
+
self, consumption_service, mock_datadis_connector, mock_database_service
|
|
822
|
+
):
|
|
823
|
+
"""Test monthly consumption aggregation with custom cycle start day."""
|
|
824
|
+
mock_connector, mock_connector_class = mock_datadis_connector
|
|
825
|
+
cups = "ES1234567890123456789"
|
|
826
|
+
start_date = datetime(2024, 6, 1, 0, 0)
|
|
827
|
+
end_date = datetime(2024, 6, 30, 23, 59)
|
|
828
|
+
|
|
829
|
+
# Create sample data spanning across billing cycle boundary
|
|
830
|
+
from edata.services.database import ConsumptionModel as DbConsumption
|
|
831
|
+
|
|
832
|
+
db_consumptions = []
|
|
833
|
+
|
|
834
|
+
# Data on June 14th (before cycle start)
|
|
835
|
+
dt1 = datetime(2024, 6, 14, 12, 0)
|
|
836
|
+
db_cons1 = Mock(spec=DbConsumption)
|
|
837
|
+
db_cons1.datetime = dt1
|
|
838
|
+
db_cons1.delta_h = 1.0
|
|
839
|
+
db_cons1.value_kwh = 2.0
|
|
840
|
+
db_cons1.surplus_kwh = 0.0
|
|
841
|
+
db_cons1.real = True
|
|
842
|
+
db_consumptions.append(db_cons1)
|
|
843
|
+
|
|
844
|
+
# Data on June 16th (after cycle start)
|
|
845
|
+
dt2 = datetime(2024, 6, 16, 12, 0)
|
|
846
|
+
db_cons2 = Mock(spec=DbConsumption)
|
|
847
|
+
db_cons2.datetime = dt2
|
|
848
|
+
db_cons2.delta_h = 1.0
|
|
849
|
+
db_cons2.value_kwh = 3.0
|
|
850
|
+
db_cons2.surplus_kwh = 0.0
|
|
851
|
+
db_cons2.real = True
|
|
852
|
+
db_consumptions.append(db_cons2)
|
|
853
|
+
|
|
854
|
+
mock_database_service.get_consumptions.return_value = db_consumptions
|
|
855
|
+
|
|
856
|
+
# Execute with cycle start day = 15
|
|
857
|
+
monthly_consumptions = await consumption_service.get_monthly_consumptions(
|
|
858
|
+
cups=cups, start_date=start_date, end_date=end_date, cycle_start_day=15
|
|
859
|
+
)
|
|
860
|
+
|
|
861
|
+
# Should have 2 months (May billing period and June billing period)
|
|
862
|
+
assert len(monthly_consumptions) == 2
|
|
863
|
+
|
|
864
|
+
# Verify the months
|
|
865
|
+
months = sorted([m.datetime for m in monthly_consumptions])
|
|
866
|
+
assert months[0].month == 5 # May billing period (for June 14th data)
|
|
867
|
+
assert months[1].month == 6 # June billing period (for June 16th data)
|
|
868
|
+
|
|
869
|
+
@pytest.mark.asyncio
|
|
870
|
+
async def test_get_daily_consumptions_empty_data(
|
|
871
|
+
self, consumption_service, mock_datadis_connector, mock_database_service
|
|
872
|
+
):
|
|
873
|
+
"""Test daily consumption aggregation with no data."""
|
|
874
|
+
mock_connector, mock_connector_class = mock_datadis_connector
|
|
875
|
+
cups = "ES1234567890123456789"
|
|
876
|
+
start_date = datetime(2024, 6, 17, 0, 0) # Monday
|
|
877
|
+
end_date = datetime(2024, 6, 17, 23, 59) # Monday
|
|
878
|
+
|
|
879
|
+
# Mock database service to return empty data
|
|
880
|
+
mock_database_service.get_consumptions.return_value = []
|
|
881
|
+
|
|
882
|
+
# Execute daily aggregation
|
|
883
|
+
daily_consumptions = await consumption_service.get_daily_consumptions(
|
|
884
|
+
cups=cups, start_date=start_date, end_date=end_date
|
|
885
|
+
)
|
|
886
|
+
|
|
887
|
+
# Should return empty list
|
|
888
|
+
assert len(daily_consumptions) == 0
|
|
889
|
+
|
|
890
|
+
@pytest.mark.asyncio
|
|
891
|
+
async def test_get_monthly_consumptions_empty_data(
|
|
892
|
+
self, consumption_service, mock_datadis_connector, mock_database_service
|
|
893
|
+
):
|
|
894
|
+
"""Test monthly consumption aggregation with no data."""
|
|
895
|
+
mock_connector, mock_connector_class = mock_datadis_connector
|
|
896
|
+
cups = "ES1234567890123456789"
|
|
897
|
+
start_date = datetime(2024, 6, 15, 0, 0)
|
|
898
|
+
end_date = datetime(2024, 6, 15, 23, 59)
|
|
899
|
+
|
|
900
|
+
# Mock database service to return empty data
|
|
901
|
+
mock_database_service.get_consumptions.return_value = []
|
|
902
|
+
|
|
903
|
+
# Execute monthly aggregation
|
|
904
|
+
monthly_consumptions = await consumption_service.get_monthly_consumptions(
|
|
905
|
+
cups=cups, start_date=start_date, end_date=end_date
|
|
906
|
+
)
|
|
907
|
+
|
|
908
|
+
# Should return empty list
|
|
909
|
+
assert len(monthly_consumptions) == 0
|
|
910
|
+
|
|
911
|
+
@pytest.mark.asyncio
|
|
912
|
+
@patch("edata.services.consumption.get_pvpc_tariff")
|
|
913
|
+
async def test_tariff_calculation_in_aggregations(
|
|
914
|
+
self,
|
|
915
|
+
mock_get_pvpc_tariff,
|
|
916
|
+
consumption_service,
|
|
917
|
+
mock_datadis_connector,
|
|
918
|
+
mock_database_service,
|
|
919
|
+
):
|
|
920
|
+
"""Test that tariff calculation is used correctly in aggregations."""
|
|
921
|
+
mock_connector, mock_connector_class = mock_datadis_connector
|
|
922
|
+
cups = "ES1234567890123456789"
|
|
923
|
+
start_date = datetime(2024, 6, 17, 0, 0) # Monday
|
|
924
|
+
end_date = datetime(2024, 6, 17, 23, 59) # Monday
|
|
925
|
+
|
|
926
|
+
# Create single consumption data
|
|
927
|
+
from edata.services.database import ConsumptionModel as DbConsumption
|
|
928
|
+
|
|
929
|
+
db_cons = Mock(spec=DbConsumption)
|
|
930
|
+
db_cons.datetime = datetime(2024, 6, 17, 12, 0) # Monday noon
|
|
931
|
+
db_cons.delta_h = 1.0
|
|
932
|
+
db_cons.value_kwh = 2.0
|
|
933
|
+
db_cons.surplus_kwh = 0.1
|
|
934
|
+
db_cons.real = True
|
|
935
|
+
|
|
936
|
+
mock_database_service.get_consumptions.return_value = [db_cons]
|
|
937
|
+
|
|
938
|
+
# Mock tariff calculation to return P2
|
|
939
|
+
mock_get_pvpc_tariff.return_value = "p2"
|
|
940
|
+
|
|
941
|
+
# Execute daily aggregation with await
|
|
942
|
+
daily_consumptions = await consumption_service.get_daily_consumptions(
|
|
943
|
+
cups=cups, start_date=start_date, end_date=end_date
|
|
944
|
+
)
|
|
945
|
+
|
|
946
|
+
# Verify tariff function was called
|
|
947
|
+
mock_get_pvpc_tariff.assert_called_with(datetime(2024, 6, 17, 12, 0))
|
|
948
|
+
|
|
949
|
+
# Verify P2 values were set correctly
|
|
950
|
+
assert len(daily_consumptions) == 1
|
|
951
|
+
day = daily_consumptions[0]
|
|
952
|
+
assert day.value_p2_kwh == 2.0
|
|
953
|
+
assert day.surplus_p2_kwh == 0.1
|
|
954
|
+
assert day.value_p1_kwh == 0.0
|
|
955
|
+
assert day.value_p3_kwh == 0.0
|