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,612 @@
|
|
|
1
|
+
"""Integration tests for EdataHelper with service-based architecture."""
|
|
2
|
+
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
from unittest.mock import AsyncMock, Mock, patch
|
|
5
|
+
|
|
6
|
+
import pytest
|
|
7
|
+
from freezegun import freeze_time
|
|
8
|
+
|
|
9
|
+
from edata.const import ATTRIBUTES
|
|
10
|
+
from edata.helpers import EdataHelper
|
|
11
|
+
from edata.models.consumption import Consumption
|
|
12
|
+
from edata.models.contract import Contract
|
|
13
|
+
from edata.models.maximeter import MaxPower
|
|
14
|
+
from edata.models.pricing import PricingRules
|
|
15
|
+
from edata.models.supply import Supply
|
|
16
|
+
|
|
17
|
+
# Test data constants
|
|
18
|
+
TEST_CUPS = "ES1234000000000001JN0F"
|
|
19
|
+
TEST_USERNAME = "testuser"
|
|
20
|
+
TEST_PASSWORD = "testpass"
|
|
21
|
+
TEST_NIF = "12345678Z"
|
|
22
|
+
AT_TIME = "2023-10-15"
|
|
23
|
+
|
|
24
|
+
# Sample pricing rules for testing
|
|
25
|
+
PRICING_RULES_PVPC = PricingRules(
|
|
26
|
+
p1_kw_year_eur=30.67266,
|
|
27
|
+
p2_kw_year_eur=1.4243591,
|
|
28
|
+
meter_month_eur=0.81,
|
|
29
|
+
market_kw_year_eur=3.113,
|
|
30
|
+
electricity_tax=1.0511300560,
|
|
31
|
+
iva_tax=1.05,
|
|
32
|
+
p1_kwh_eur=None, # PVPC mode
|
|
33
|
+
p2_kwh_eur=None,
|
|
34
|
+
p3_kwh_eur=None,
|
|
35
|
+
surplus_p1_kwh_eur=None,
|
|
36
|
+
surplus_p2_kwh_eur=None,
|
|
37
|
+
surplus_p3_kwh_eur=None,
|
|
38
|
+
energy_formula="electricity_tax * iva_tax * kwh_eur * kwh",
|
|
39
|
+
power_formula="electricity_tax * iva_tax * (p1_kw * (p1_kw_year_eur + market_kw_year_eur) + p2_kw * p2_kw_year_eur) / 365 / 24",
|
|
40
|
+
others_formula="iva_tax * meter_month_eur / 30 / 24",
|
|
41
|
+
surplus_formula="electricity_tax * iva_tax * surplus_kwh * surplus_kwh_eur",
|
|
42
|
+
main_formula="energy_term + power_term + others_term",
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
PRICING_RULES_FIXED = PricingRules(
|
|
46
|
+
p1_kw_year_eur=30.67266,
|
|
47
|
+
p2_kw_year_eur=1.4243591,
|
|
48
|
+
meter_month_eur=0.81,
|
|
49
|
+
market_kw_year_eur=3.113,
|
|
50
|
+
electricity_tax=1.0511300560,
|
|
51
|
+
iva_tax=1.05,
|
|
52
|
+
p1_kwh_eur=0.12, # Fixed prices
|
|
53
|
+
p2_kwh_eur=0.10,
|
|
54
|
+
p3_kwh_eur=0.08,
|
|
55
|
+
surplus_p1_kwh_eur=0.05,
|
|
56
|
+
surplus_p2_kwh_eur=0.04,
|
|
57
|
+
surplus_p3_kwh_eur=0.03,
|
|
58
|
+
energy_formula="electricity_tax * iva_tax * kwh_eur * kwh",
|
|
59
|
+
power_formula="electricity_tax * iva_tax * (p1_kw * (p1_kw_year_eur + market_kw_year_eur) + p2_kw * p2_kw_year_eur) / 365 / 24",
|
|
60
|
+
others_formula="iva_tax * meter_month_eur / 30 / 24",
|
|
61
|
+
surplus_formula="electricity_tax * iva_tax * surplus_kwh * surplus_kwh_eur",
|
|
62
|
+
main_formula="energy_term + power_term + others_term",
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
# Sample supply data
|
|
66
|
+
SAMPLE_SUPPLY = Supply(
|
|
67
|
+
cups=TEST_CUPS,
|
|
68
|
+
distributor_code="0031",
|
|
69
|
+
point_type=5,
|
|
70
|
+
date_start=datetime(2020, 1, 1),
|
|
71
|
+
date_end=datetime(2025, 12, 31),
|
|
72
|
+
address="Test Address 123",
|
|
73
|
+
postal_code="28001",
|
|
74
|
+
province="Madrid",
|
|
75
|
+
municipality="Madrid",
|
|
76
|
+
distributor="Test Distributor",
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
# Sample contract data
|
|
80
|
+
SAMPLE_CONTRACT = Contract(
|
|
81
|
+
distributor_code="0031",
|
|
82
|
+
date_start=datetime(2023, 1, 1),
|
|
83
|
+
date_end=datetime(2023, 12, 31),
|
|
84
|
+
power_p1=5.75,
|
|
85
|
+
power_p2=5.75,
|
|
86
|
+
marketer="Test Marketer",
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
# Sample consumption data
|
|
90
|
+
SAMPLE_CONSUMPTIONS = [
|
|
91
|
+
Consumption(
|
|
92
|
+
datetime=datetime(2023, 10, 14, hour),
|
|
93
|
+
delta_h=1.0,
|
|
94
|
+
value_kwh=0.5 + hour * 0.1,
|
|
95
|
+
surplus_kwh=0.0,
|
|
96
|
+
)
|
|
97
|
+
for hour in range(24)
|
|
98
|
+
]
|
|
99
|
+
|
|
100
|
+
# Sample maximeter data
|
|
101
|
+
SAMPLE_MAXPOWER = [
|
|
102
|
+
MaxPower(
|
|
103
|
+
datetime=datetime(2023, 10, day),
|
|
104
|
+
value_kw=4.5 + day * 0.1,
|
|
105
|
+
)
|
|
106
|
+
for day in range(1, 15)
|
|
107
|
+
]
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
class TestEdataHelperIntegration:
|
|
111
|
+
"""Integration tests for EdataHelper with mocked services."""
|
|
112
|
+
|
|
113
|
+
def test_initialization_pvpc(self):
|
|
114
|
+
"""Test EdataHelper initialization with PVPC pricing."""
|
|
115
|
+
helper = EdataHelper(
|
|
116
|
+
datadis_username=TEST_USERNAME,
|
|
117
|
+
datadis_password=TEST_PASSWORD,
|
|
118
|
+
cups=TEST_CUPS,
|
|
119
|
+
datadis_authorized_nif=TEST_NIF,
|
|
120
|
+
pricing_rules=PRICING_RULES_PVPC,
|
|
121
|
+
storage_dir_path=None,
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
# Test basic properties
|
|
125
|
+
assert helper._cups == TEST_CUPS
|
|
126
|
+
assert helper._scups == "1JN0F"
|
|
127
|
+
assert helper._authorized_nif == TEST_NIF
|
|
128
|
+
assert helper.pricing_rules == PRICING_RULES_PVPC
|
|
129
|
+
assert helper.enable_billing is True
|
|
130
|
+
assert helper.is_pvpc is True
|
|
131
|
+
|
|
132
|
+
# Test attributes initialization
|
|
133
|
+
assert len(helper.attributes) == len(ATTRIBUTES)
|
|
134
|
+
for attr in ATTRIBUTES:
|
|
135
|
+
assert helper.attributes[attr] is None
|
|
136
|
+
|
|
137
|
+
# Test that attributes and summary are the same object
|
|
138
|
+
assert helper.attributes is helper.summary
|
|
139
|
+
|
|
140
|
+
# Test services initialization
|
|
141
|
+
assert helper._supply_service is not None
|
|
142
|
+
assert helper._contract_service is not None
|
|
143
|
+
assert helper._consumption_service is not None
|
|
144
|
+
assert helper._maximeter_service is not None
|
|
145
|
+
assert helper._billing_service is not None
|
|
146
|
+
|
|
147
|
+
def test_initialization_fixed_pricing(self):
|
|
148
|
+
"""Test EdataHelper initialization with fixed pricing."""
|
|
149
|
+
helper = EdataHelper(
|
|
150
|
+
datadis_username=TEST_USERNAME,
|
|
151
|
+
datadis_password=TEST_PASSWORD,
|
|
152
|
+
cups=TEST_CUPS,
|
|
153
|
+
pricing_rules=PRICING_RULES_FIXED,
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
assert helper.enable_billing is True
|
|
157
|
+
assert helper.is_pvpc is False
|
|
158
|
+
assert helper._billing_service is not None
|
|
159
|
+
|
|
160
|
+
def test_initialization_no_billing(self):
|
|
161
|
+
"""Test EdataHelper initialization without billing."""
|
|
162
|
+
helper = EdataHelper(
|
|
163
|
+
datadis_username=TEST_USERNAME,
|
|
164
|
+
datadis_password=TEST_PASSWORD,
|
|
165
|
+
cups=TEST_CUPS,
|
|
166
|
+
pricing_rules=None,
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
assert helper.enable_billing is False
|
|
170
|
+
assert helper.is_pvpc is False
|
|
171
|
+
|
|
172
|
+
@freeze_time(AT_TIME)
|
|
173
|
+
@patch("edata.helpers.SupplyService")
|
|
174
|
+
@patch("edata.helpers.ContractService")
|
|
175
|
+
@patch("edata.helpers.ConsumptionService")
|
|
176
|
+
@patch("edata.helpers.MaximeterService")
|
|
177
|
+
@patch("edata.helpers.BillingService")
|
|
178
|
+
@pytest.mark.asyncio
|
|
179
|
+
async def test_update_successful_flow_pvpc(
|
|
180
|
+
self,
|
|
181
|
+
mock_billing_service,
|
|
182
|
+
mock_maximeter_service,
|
|
183
|
+
mock_consumption_service,
|
|
184
|
+
mock_contract_service,
|
|
185
|
+
mock_supply_service,
|
|
186
|
+
):
|
|
187
|
+
"""Test successful update flow with PVPC pricing."""
|
|
188
|
+
|
|
189
|
+
# Setup mocks
|
|
190
|
+
mock_supply_instance = Mock()
|
|
191
|
+
mock_supply_instance.update_supplies = AsyncMock(return_value={"success": True})
|
|
192
|
+
mock_supply_instance.validate_cups = AsyncMock(return_value=True)
|
|
193
|
+
mock_supply_instance.get_supply_by_cups = AsyncMock(return_value=SAMPLE_SUPPLY)
|
|
194
|
+
mock_supply_instance.get_supply_summary = AsyncMock(
|
|
195
|
+
return_value={"cups": TEST_CUPS}
|
|
196
|
+
)
|
|
197
|
+
mock_supply_service.return_value = mock_supply_instance
|
|
198
|
+
|
|
199
|
+
mock_contract_instance = Mock()
|
|
200
|
+
mock_contract_instance.update_contracts = AsyncMock(
|
|
201
|
+
return_value={"success": True}
|
|
202
|
+
)
|
|
203
|
+
mock_contract_instance.get_contract_summary = AsyncMock(
|
|
204
|
+
return_value={
|
|
205
|
+
"contract_p1_kW": 5.75,
|
|
206
|
+
"contract_p2_kW": 5.75,
|
|
207
|
+
}
|
|
208
|
+
)
|
|
209
|
+
mock_contract_service.return_value = mock_contract_instance
|
|
210
|
+
|
|
211
|
+
mock_consumption_instance = Mock()
|
|
212
|
+
mock_consumption_instance.update_consumption_range_by_months = AsyncMock(
|
|
213
|
+
return_value={"success": True}
|
|
214
|
+
)
|
|
215
|
+
mock_consumption_instance.get_consumption_summary = AsyncMock(
|
|
216
|
+
return_value={
|
|
217
|
+
"yesterday_kWh": 12.5,
|
|
218
|
+
"month_kWh": 350.0,
|
|
219
|
+
"last_month_kWh": 340.0,
|
|
220
|
+
"last_registered_date": datetime(2023, 10, 14, 23),
|
|
221
|
+
}
|
|
222
|
+
)
|
|
223
|
+
mock_consumption_service.return_value = mock_consumption_instance
|
|
224
|
+
|
|
225
|
+
mock_maximeter_instance = Mock()
|
|
226
|
+
mock_maximeter_instance.update_maxpower_range_by_months = AsyncMock(
|
|
227
|
+
return_value={"success": True}
|
|
228
|
+
)
|
|
229
|
+
mock_maximeter_instance.get_maximeter_summary = AsyncMock(
|
|
230
|
+
return_value={
|
|
231
|
+
"max_power_kW": 5.8,
|
|
232
|
+
"max_power_date": datetime(2023, 10, 10),
|
|
233
|
+
"max_power_mean_kW": 4.5,
|
|
234
|
+
"max_power_90perc_kW": 5.2,
|
|
235
|
+
}
|
|
236
|
+
)
|
|
237
|
+
mock_maximeter_service.return_value = mock_maximeter_instance
|
|
238
|
+
|
|
239
|
+
mock_billing_instance = Mock()
|
|
240
|
+
mock_billing_instance.update_pvpc_prices = AsyncMock(
|
|
241
|
+
return_value={"success": True}
|
|
242
|
+
)
|
|
243
|
+
mock_billing_instance.update_missing_costs = AsyncMock(
|
|
244
|
+
return_value={"success": True}
|
|
245
|
+
)
|
|
246
|
+
mock_billing_instance.get_billing_summary = AsyncMock(
|
|
247
|
+
return_value={
|
|
248
|
+
"month_€": 45.67,
|
|
249
|
+
"last_month_€": 43.21,
|
|
250
|
+
}
|
|
251
|
+
)
|
|
252
|
+
mock_billing_service.return_value = mock_billing_instance
|
|
253
|
+
|
|
254
|
+
# Test update
|
|
255
|
+
helper = EdataHelper(
|
|
256
|
+
datadis_username=TEST_USERNAME,
|
|
257
|
+
datadis_password=TEST_PASSWORD,
|
|
258
|
+
cups=TEST_CUPS,
|
|
259
|
+
pricing_rules=PRICING_RULES_PVPC,
|
|
260
|
+
)
|
|
261
|
+
|
|
262
|
+
date_from = datetime(2023, 1, 1)
|
|
263
|
+
date_to = datetime(2023, 10, 15)
|
|
264
|
+
result = await helper.update(date_from=date_from, date_to=date_to)
|
|
265
|
+
|
|
266
|
+
# Verify result
|
|
267
|
+
assert result is True
|
|
268
|
+
|
|
269
|
+
# Verify service calls
|
|
270
|
+
mock_supply_instance.update_supplies.assert_called_once_with(
|
|
271
|
+
authorized_nif=None
|
|
272
|
+
)
|
|
273
|
+
mock_supply_instance.validate_cups.assert_called_once_with(TEST_CUPS)
|
|
274
|
+
mock_supply_instance.get_supply_by_cups.assert_called_once_with(TEST_CUPS)
|
|
275
|
+
|
|
276
|
+
mock_contract_instance.update_contracts.assert_called_once_with(
|
|
277
|
+
cups=TEST_CUPS, distributor_code="0031", authorized_nif=None
|
|
278
|
+
)
|
|
279
|
+
|
|
280
|
+
mock_consumption_instance.update_consumption_range_by_months.assert_called_once_with(
|
|
281
|
+
cups=TEST_CUPS,
|
|
282
|
+
distributor_code="0031",
|
|
283
|
+
start_date=date_from, # Use the original date_from since it's after supply start
|
|
284
|
+
end_date=date_to,
|
|
285
|
+
measurement_type="0",
|
|
286
|
+
point_type=5,
|
|
287
|
+
authorized_nif=None,
|
|
288
|
+
)
|
|
289
|
+
|
|
290
|
+
mock_maximeter_instance.update_maxpower_range_by_months.assert_called_once()
|
|
291
|
+
mock_billing_instance.update_pvpc_prices.assert_called_once()
|
|
292
|
+
mock_billing_instance.update_missing_costs.assert_called_once()
|
|
293
|
+
|
|
294
|
+
# Verify summary attributes
|
|
295
|
+
assert helper.attributes["cups"] == TEST_CUPS
|
|
296
|
+
assert helper.attributes["contract_p1_kW"] == 5.75
|
|
297
|
+
assert helper.attributes["contract_p2_kW"] == 5.75
|
|
298
|
+
assert helper.attributes["yesterday_kWh"] == 12.5
|
|
299
|
+
assert helper.attributes["month_kWh"] == 350.0
|
|
300
|
+
assert helper.attributes["last_month_kWh"] == 340.0
|
|
301
|
+
assert helper.attributes["max_power_kW"] == 5.8
|
|
302
|
+
assert helper.attributes["month_€"] == 45.67
|
|
303
|
+
assert helper.attributes["last_month_€"] == 43.21
|
|
304
|
+
|
|
305
|
+
@freeze_time(AT_TIME)
|
|
306
|
+
@patch("edata.helpers.SupplyService")
|
|
307
|
+
@patch("edata.helpers.ContractService")
|
|
308
|
+
@patch("edata.helpers.ConsumptionService")
|
|
309
|
+
@patch("edata.helpers.MaximeterService")
|
|
310
|
+
@patch("edata.helpers.BillingService")
|
|
311
|
+
@pytest.mark.asyncio
|
|
312
|
+
async def test_update_with_service_failures(
|
|
313
|
+
self,
|
|
314
|
+
mock_billing_service,
|
|
315
|
+
mock_maximeter_service,
|
|
316
|
+
mock_consumption_service,
|
|
317
|
+
mock_contract_service,
|
|
318
|
+
mock_supply_service,
|
|
319
|
+
):
|
|
320
|
+
"""Test update flow with some service failures."""
|
|
321
|
+
|
|
322
|
+
# Setup mocks with some failures
|
|
323
|
+
mock_supply_instance = Mock()
|
|
324
|
+
mock_supply_instance.update_supplies = AsyncMock(return_value={"success": True})
|
|
325
|
+
mock_supply_instance.validate_cups = AsyncMock(return_value=True)
|
|
326
|
+
mock_supply_instance.get_supply_by_cups = AsyncMock(return_value=SAMPLE_SUPPLY)
|
|
327
|
+
mock_supply_instance.get_supply_summary = AsyncMock(
|
|
328
|
+
return_value={"cups": TEST_CUPS}
|
|
329
|
+
)
|
|
330
|
+
mock_supply_service.return_value = mock_supply_instance
|
|
331
|
+
|
|
332
|
+
mock_contract_instance = Mock()
|
|
333
|
+
mock_contract_instance.update_contracts = AsyncMock(
|
|
334
|
+
return_value={"success": False, "error": "Contract API down"}
|
|
335
|
+
)
|
|
336
|
+
mock_contract_instance.get_contract_summary = AsyncMock(return_value={})
|
|
337
|
+
mock_contract_service.return_value = mock_contract_instance
|
|
338
|
+
|
|
339
|
+
mock_consumption_instance = Mock()
|
|
340
|
+
mock_consumption_instance.update_consumption_range_by_months = AsyncMock(
|
|
341
|
+
return_value={"success": False}
|
|
342
|
+
)
|
|
343
|
+
mock_consumption_instance.get_consumption_summary = AsyncMock(return_value={})
|
|
344
|
+
mock_consumption_service.return_value = mock_consumption_instance
|
|
345
|
+
|
|
346
|
+
mock_maximeter_instance = Mock()
|
|
347
|
+
mock_maximeter_instance.update_maxpower_range_by_months = AsyncMock(
|
|
348
|
+
return_value={"success": True}
|
|
349
|
+
)
|
|
350
|
+
mock_maximeter_instance.get_maximeter_summary = AsyncMock(
|
|
351
|
+
return_value={"max_power_kW": 5.8}
|
|
352
|
+
)
|
|
353
|
+
mock_maximeter_service.return_value = mock_maximeter_instance
|
|
354
|
+
|
|
355
|
+
mock_billing_instance = Mock()
|
|
356
|
+
mock_billing_instance.update_pvpc_prices = AsyncMock(
|
|
357
|
+
return_value={"success": False, "error": "PVPC API error"}
|
|
358
|
+
)
|
|
359
|
+
mock_billing_instance.get_billing_summary = AsyncMock(return_value={})
|
|
360
|
+
mock_billing_service.return_value = mock_billing_instance
|
|
361
|
+
|
|
362
|
+
# Test update
|
|
363
|
+
helper = EdataHelper(
|
|
364
|
+
datadis_username=TEST_USERNAME,
|
|
365
|
+
datadis_password=TEST_PASSWORD,
|
|
366
|
+
cups=TEST_CUPS,
|
|
367
|
+
pricing_rules=PRICING_RULES_PVPC,
|
|
368
|
+
)
|
|
369
|
+
|
|
370
|
+
result = await helper.update()
|
|
371
|
+
|
|
372
|
+
# Update should still succeed even with some service failures
|
|
373
|
+
assert result is True
|
|
374
|
+
|
|
375
|
+
# Verify summary attributes include successful services
|
|
376
|
+
assert helper.attributes["cups"] == TEST_CUPS
|
|
377
|
+
assert helper.attributes["max_power_kW"] == 5.8
|
|
378
|
+
|
|
379
|
+
# Failed services should have None values
|
|
380
|
+
assert helper.attributes["contract_p1_kW"] is None
|
|
381
|
+
assert helper.attributes["yesterday_kWh"] is None
|
|
382
|
+
|
|
383
|
+
@patch("edata.helpers.SupplyService")
|
|
384
|
+
@pytest.mark.asyncio
|
|
385
|
+
async def test_update_supply_failure(self, mock_supply_service):
|
|
386
|
+
"""Test update with supply service failure."""
|
|
387
|
+
|
|
388
|
+
mock_supply_instance = Mock()
|
|
389
|
+
mock_supply_instance.update_supplies.return_value = {
|
|
390
|
+
"success": False,
|
|
391
|
+
"error": "Authentication failed",
|
|
392
|
+
}
|
|
393
|
+
mock_supply_service.return_value = mock_supply_instance
|
|
394
|
+
|
|
395
|
+
helper = EdataHelper(
|
|
396
|
+
datadis_username=TEST_USERNAME,
|
|
397
|
+
datadis_password=TEST_PASSWORD,
|
|
398
|
+
cups=TEST_CUPS,
|
|
399
|
+
)
|
|
400
|
+
|
|
401
|
+
result = await helper.update()
|
|
402
|
+
|
|
403
|
+
# Should fail if supplies can't be updated
|
|
404
|
+
assert result is False
|
|
405
|
+
|
|
406
|
+
@patch("edata.helpers.SupplyService")
|
|
407
|
+
@pytest.mark.asyncio
|
|
408
|
+
async def test_update_cups_not_found(self, mock_supply_service):
|
|
409
|
+
"""Test update when CUPS is not found in account."""
|
|
410
|
+
|
|
411
|
+
mock_supply_instance = Mock()
|
|
412
|
+
mock_supply_instance.update_supplies.return_value = {"success": True}
|
|
413
|
+
mock_supply_instance.validate_cups.return_value = False
|
|
414
|
+
mock_supply_service.return_value = mock_supply_instance
|
|
415
|
+
|
|
416
|
+
helper = EdataHelper(
|
|
417
|
+
datadis_username=TEST_USERNAME,
|
|
418
|
+
datadis_password=TEST_PASSWORD,
|
|
419
|
+
cups=TEST_CUPS,
|
|
420
|
+
)
|
|
421
|
+
|
|
422
|
+
result = await helper.update()
|
|
423
|
+
|
|
424
|
+
# Should fail if CUPS is not found
|
|
425
|
+
assert result is False
|
|
426
|
+
|
|
427
|
+
@patch("edata.helpers.SupplyService")
|
|
428
|
+
@patch("edata.helpers.ContractService")
|
|
429
|
+
@patch("edata.helpers.ConsumptionService")
|
|
430
|
+
@patch("edata.helpers.MaximeterService")
|
|
431
|
+
def test_calculate_summary_attributes_error_handling(
|
|
432
|
+
self,
|
|
433
|
+
mock_maximeter_service,
|
|
434
|
+
mock_consumption_service,
|
|
435
|
+
mock_contract_service,
|
|
436
|
+
mock_supply_service,
|
|
437
|
+
):
|
|
438
|
+
"""Test error handling in summary calculation."""
|
|
439
|
+
|
|
440
|
+
# Setup mock that raises exception
|
|
441
|
+
mock_supply_instance = Mock()
|
|
442
|
+
mock_supply_instance.get_supply_summary.side_effect = Exception(
|
|
443
|
+
"Database error"
|
|
444
|
+
)
|
|
445
|
+
mock_supply_service.return_value = mock_supply_instance
|
|
446
|
+
|
|
447
|
+
mock_contract_instance = Mock()
|
|
448
|
+
mock_contract_instance.get_contract_summary.return_value = {
|
|
449
|
+
"contract_p1_kW": 5.75
|
|
450
|
+
}
|
|
451
|
+
mock_contract_service.return_value = mock_contract_instance
|
|
452
|
+
|
|
453
|
+
mock_consumption_instance = Mock()
|
|
454
|
+
mock_consumption_instance.get_consumption_summary.return_value = {
|
|
455
|
+
"yesterday_kWh": 12.5
|
|
456
|
+
}
|
|
457
|
+
mock_consumption_service.return_value = mock_consumption_instance
|
|
458
|
+
|
|
459
|
+
mock_maximeter_instance = Mock()
|
|
460
|
+
mock_maximeter_instance.get_maximeter_summary.return_value = {
|
|
461
|
+
"max_power_kW": 5.8
|
|
462
|
+
}
|
|
463
|
+
mock_maximeter_service.return_value = mock_maximeter_instance
|
|
464
|
+
|
|
465
|
+
helper = EdataHelper(
|
|
466
|
+
datadis_username=TEST_USERNAME,
|
|
467
|
+
datadis_password=TEST_PASSWORD,
|
|
468
|
+
cups=TEST_CUPS,
|
|
469
|
+
)
|
|
470
|
+
|
|
471
|
+
# Should not raise exception
|
|
472
|
+
# Note: We can't actually test the exception handling easily in async context
|
|
473
|
+
# but we can test that all attributes are None initially
|
|
474
|
+
for attr in ATTRIBUTES:
|
|
475
|
+
assert helper.attributes[attr] is None
|
|
476
|
+
|
|
477
|
+
@pytest.mark.asyncio
|
|
478
|
+
async def test_numeric_value_rounding(self):
|
|
479
|
+
"""Test that numeric values are properly rounded."""
|
|
480
|
+
with patch("edata.helpers.SupplyService") as mock_supply_service, patch(
|
|
481
|
+
"edata.helpers.ContractService"
|
|
482
|
+
) as mock_contract_service, patch(
|
|
483
|
+
"edata.helpers.ConsumptionService"
|
|
484
|
+
) as mock_consumption_service, patch(
|
|
485
|
+
"edata.helpers.MaximeterService"
|
|
486
|
+
) as mock_maximeter_service:
|
|
487
|
+
|
|
488
|
+
# Setup mocks with unrounded values
|
|
489
|
+
mock_supply_instance = Mock()
|
|
490
|
+
mock_supply_instance.get_supply_summary = AsyncMock(
|
|
491
|
+
return_value={"cups": TEST_CUPS}
|
|
492
|
+
)
|
|
493
|
+
mock_supply_service.return_value = mock_supply_instance
|
|
494
|
+
|
|
495
|
+
mock_contract_instance = Mock()
|
|
496
|
+
mock_contract_instance.get_contract_summary = AsyncMock(
|
|
497
|
+
return_value={"contract_p1_kW": 5.7523456}
|
|
498
|
+
)
|
|
499
|
+
mock_contract_service.return_value = mock_contract_instance
|
|
500
|
+
|
|
501
|
+
mock_consumption_instance = Mock()
|
|
502
|
+
mock_consumption_instance.get_consumption_summary = AsyncMock(
|
|
503
|
+
return_value={"yesterday_kWh": 12.54789}
|
|
504
|
+
)
|
|
505
|
+
mock_consumption_service.return_value = mock_consumption_instance
|
|
506
|
+
|
|
507
|
+
mock_maximeter_instance = Mock()
|
|
508
|
+
mock_maximeter_instance.get_maximeter_summary = AsyncMock(
|
|
509
|
+
return_value={"max_power_kW": 5.87654321}
|
|
510
|
+
)
|
|
511
|
+
mock_maximeter_service.return_value = mock_maximeter_instance
|
|
512
|
+
|
|
513
|
+
helper = EdataHelper(
|
|
514
|
+
datadis_username=TEST_USERNAME,
|
|
515
|
+
datadis_password=TEST_PASSWORD,
|
|
516
|
+
cups=TEST_CUPS,
|
|
517
|
+
)
|
|
518
|
+
|
|
519
|
+
await helper._calculate_summary_attributes()
|
|
520
|
+
|
|
521
|
+
# Check rounding
|
|
522
|
+
assert helper.attributes["contract_p1_kW"] == 5.75
|
|
523
|
+
assert helper.attributes["yesterday_kWh"] == 12.55
|
|
524
|
+
assert helper.attributes["max_power_kW"] == 5.88
|
|
525
|
+
assert (
|
|
526
|
+
helper.attributes["cups"] == TEST_CUPS
|
|
527
|
+
) # String should not be affected
|
|
528
|
+
|
|
529
|
+
@pytest.mark.asyncio
|
|
530
|
+
async def test_date_range_adjustment(self):
|
|
531
|
+
"""Test that date ranges are properly adjusted to supply validity period."""
|
|
532
|
+
with patch("edata.helpers.SupplyService") as mock_supply_service, patch(
|
|
533
|
+
"edata.helpers.ContractService"
|
|
534
|
+
) as mock_contract_service, patch(
|
|
535
|
+
"edata.helpers.ConsumptionService"
|
|
536
|
+
) as mock_consumption_service, patch(
|
|
537
|
+
"edata.helpers.MaximeterService"
|
|
538
|
+
) as mock_maximeter_service:
|
|
539
|
+
|
|
540
|
+
# Supply with limited date range
|
|
541
|
+
limited_supply = Supply(
|
|
542
|
+
cups=TEST_CUPS,
|
|
543
|
+
distributor_code="0031",
|
|
544
|
+
point_type=5,
|
|
545
|
+
date_start=datetime(2023, 6, 1), # Later start
|
|
546
|
+
date_end=datetime(2023, 9, 30), # Earlier end
|
|
547
|
+
address="Test Address",
|
|
548
|
+
postal_code="28001",
|
|
549
|
+
province="Madrid",
|
|
550
|
+
municipality="Madrid",
|
|
551
|
+
distributor="Test Distributor",
|
|
552
|
+
)
|
|
553
|
+
|
|
554
|
+
mock_supply_instance = Mock()
|
|
555
|
+
mock_supply_instance.update_supplies = AsyncMock(
|
|
556
|
+
return_value={"success": True}
|
|
557
|
+
)
|
|
558
|
+
mock_supply_instance.validate_cups = AsyncMock(return_value=True)
|
|
559
|
+
mock_supply_instance.get_supply_by_cups = AsyncMock(
|
|
560
|
+
return_value=limited_supply
|
|
561
|
+
)
|
|
562
|
+
mock_supply_instance.get_supply_summary = AsyncMock(
|
|
563
|
+
return_value={"cups": TEST_CUPS}
|
|
564
|
+
)
|
|
565
|
+
mock_supply_service.return_value = mock_supply_instance
|
|
566
|
+
|
|
567
|
+
mock_contract_instance = Mock()
|
|
568
|
+
mock_contract_instance.update_contracts = AsyncMock(
|
|
569
|
+
return_value={"success": True}
|
|
570
|
+
)
|
|
571
|
+
mock_contract_instance.get_contract_summary = AsyncMock(return_value={})
|
|
572
|
+
mock_contract_service.return_value = mock_contract_instance
|
|
573
|
+
|
|
574
|
+
mock_consumption_instance = Mock()
|
|
575
|
+
mock_consumption_instance.update_consumption_range_by_months = AsyncMock(
|
|
576
|
+
return_value={"success": True}
|
|
577
|
+
)
|
|
578
|
+
mock_consumption_instance.get_consumption_summary = AsyncMock(
|
|
579
|
+
return_value={}
|
|
580
|
+
)
|
|
581
|
+
mock_consumption_service.return_value = mock_consumption_instance
|
|
582
|
+
|
|
583
|
+
mock_maximeter_instance = Mock()
|
|
584
|
+
mock_maximeter_instance.update_maxpower_range_by_months = AsyncMock(
|
|
585
|
+
return_value={"success": True}
|
|
586
|
+
)
|
|
587
|
+
mock_maximeter_instance.get_maximeter_summary = AsyncMock(return_value={})
|
|
588
|
+
mock_maximeter_service.return_value = mock_maximeter_instance
|
|
589
|
+
|
|
590
|
+
helper = EdataHelper(
|
|
591
|
+
datadis_username=TEST_USERNAME,
|
|
592
|
+
datadis_password=TEST_PASSWORD,
|
|
593
|
+
cups=TEST_CUPS,
|
|
594
|
+
)
|
|
595
|
+
|
|
596
|
+
# Request broader date range
|
|
597
|
+
result = await helper.update(
|
|
598
|
+
date_from=datetime(2023, 1, 1), date_to=datetime(2023, 12, 31)
|
|
599
|
+
)
|
|
600
|
+
|
|
601
|
+
assert result is True
|
|
602
|
+
|
|
603
|
+
# Verify that consumption service was called with adjusted dates
|
|
604
|
+
mock_consumption_instance.update_consumption_range_by_months.assert_called_once_with(
|
|
605
|
+
cups=TEST_CUPS,
|
|
606
|
+
distributor_code="0031",
|
|
607
|
+
start_date=datetime(2023, 6, 1), # Adjusted to supply start
|
|
608
|
+
end_date=datetime(2023, 9, 30), # Adjusted to supply end
|
|
609
|
+
measurement_type="0",
|
|
610
|
+
point_type=5,
|
|
611
|
+
authorized_nif=None,
|
|
612
|
+
)
|