e-data 1.3.1__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.
- connectors/__init__.py +0 -0
- connectors/datadis.py +533 -0
- connectors/redata.py +81 -0
- e_data-1.3.1.dist-info/METADATA +808 -0
- e_data-1.3.1.dist-info/RECORD +19 -0
- e_data-1.3.1.dist-info/WHEEL +5 -0
- e_data-1.3.1.dist-info/licenses/LICENSE +674 -0
- e_data-1.3.1.dist-info/top_level.txt +3 -0
- processors/__init__.py +0 -0
- processors/base.py +27 -0
- processors/billing.py +215 -0
- processors/consumption.py +139 -0
- processors/maximeter.py +56 -0
- processors/utils.py +148 -0
- tests/__init__.py +0 -0
- tests/test_datadis_connector.py +242 -0
- tests/test_helpers.py +52 -0
- tests/test_processors.py +106 -0
- tests/test_redata_connector.py +14 -0
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
"""Tests for DatadisConnector (offline)."""
|
|
2
|
+
|
|
3
|
+
import datetime
|
|
4
|
+
import datetime
|
|
5
|
+
from unittest.mock import patch, AsyncMock, MagicMock
|
|
6
|
+
from ..connectors.datadis import DatadisConnector
|
|
7
|
+
|
|
8
|
+
from ..connectors.datadis import DatadisConnector
|
|
9
|
+
|
|
10
|
+
MOCK_USERNAME = "USERNAME"
|
|
11
|
+
MOCK_PASSWORD = "PASSWORD"
|
|
12
|
+
|
|
13
|
+
SUPPLIES_RESPONSE = [
|
|
14
|
+
{
|
|
15
|
+
"cups": "ESXXXXXXXXXXXXXXXXTEST",
|
|
16
|
+
"validDateFrom": "2022/03/09",
|
|
17
|
+
"validDateTo": "2022/10/28",
|
|
18
|
+
"address": "-",
|
|
19
|
+
"postalCode": "-",
|
|
20
|
+
"province": "-",
|
|
21
|
+
"municipality": "-",
|
|
22
|
+
"distributor": "-",
|
|
23
|
+
"pointType": 5,
|
|
24
|
+
"distributorCode": "2",
|
|
25
|
+
}
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
CONTRACTS_RESPONSE = [
|
|
29
|
+
{
|
|
30
|
+
"startDate": "2022/03/09",
|
|
31
|
+
"endDate": "2022/10/28",
|
|
32
|
+
"marketer": "MARKETER",
|
|
33
|
+
"distributorCode": "2",
|
|
34
|
+
"contractedPowerkW": [4.4, 4.4],
|
|
35
|
+
}
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
CONSUMPTIONS_RESPONSE = [
|
|
39
|
+
{
|
|
40
|
+
"date": "2022/10/22",
|
|
41
|
+
"time": "01:00",
|
|
42
|
+
"consumptionKWh": 0.203,
|
|
43
|
+
"surplusEnergyKWh": 0,
|
|
44
|
+
"obtainMethod": "Real",
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
"date": "2022/10/22",
|
|
48
|
+
"time": "02:00",
|
|
49
|
+
"consumptionKWh": 0.163,
|
|
50
|
+
"surplusEnergyKWh": 0,
|
|
51
|
+
"obtainMethod": "Real",
|
|
52
|
+
},
|
|
53
|
+
]
|
|
54
|
+
|
|
55
|
+
MAXIMETER_RESPONSE = [
|
|
56
|
+
{
|
|
57
|
+
"date": "2022/03/10",
|
|
58
|
+
"time": "14:15",
|
|
59
|
+
"maxPower": 2.436,
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
"date": "2022/03/14",
|
|
63
|
+
"time": "13:15",
|
|
64
|
+
"maxPower": 3.008,
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
"date": "2022/03/27",
|
|
68
|
+
"time": "10:30",
|
|
69
|
+
"maxPower": 3.288,
|
|
70
|
+
},
|
|
71
|
+
]
|
|
72
|
+
|
|
73
|
+
@patch("aiohttp.ClientSession.get")
|
|
74
|
+
@patch.object(DatadisConnector, "_async_get_token", new_callable=AsyncMock, return_value=True)
|
|
75
|
+
def test_get_supplies(mock_token, mock_get, snapshot):
|
|
76
|
+
"""Test a successful 'get_supplies' query (syrupy snapshot)."""
|
|
77
|
+
mock_response = MagicMock()
|
|
78
|
+
mock_response.status = 200
|
|
79
|
+
mock_response.text = AsyncMock(return_value="text")
|
|
80
|
+
mock_response.json = AsyncMock(return_value=SUPPLIES_RESPONSE)
|
|
81
|
+
mock_get.return_value.__aenter__.return_value = mock_response
|
|
82
|
+
connector = DatadisConnector(MOCK_USERNAME, MOCK_PASSWORD)
|
|
83
|
+
assert connector.get_supplies() == snapshot
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
@patch("aiohttp.ClientSession.get")
|
|
88
|
+
@patch.object(DatadisConnector, "_async_get_token", new_callable=AsyncMock, return_value=True)
|
|
89
|
+
def test_get_contract_detail(mock_token, mock_get, snapshot):
|
|
90
|
+
"""Test a successful 'get_contract_detail' query (syrupy snapshot)."""
|
|
91
|
+
mock_response = MagicMock()
|
|
92
|
+
mock_response.status = 200
|
|
93
|
+
mock_response.text = AsyncMock(return_value="text")
|
|
94
|
+
mock_response.json = AsyncMock(return_value=CONTRACTS_RESPONSE)
|
|
95
|
+
mock_get.return_value.__aenter__.return_value = mock_response
|
|
96
|
+
connector = DatadisConnector(MOCK_USERNAME, MOCK_PASSWORD)
|
|
97
|
+
assert connector.get_contract_detail("ESXXXXXXXXXXXXXXXXTEST", "2") == snapshot
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
@patch("aiohttp.ClientSession.get")
|
|
102
|
+
@patch.object(DatadisConnector, "_async_get_token", new_callable=AsyncMock, return_value=True)
|
|
103
|
+
def test_get_consumption_data(mock_token, mock_get, snapshot):
|
|
104
|
+
"""Test a successful 'get_consumption_data' query (syrupy snapshot)."""
|
|
105
|
+
mock_response = MagicMock()
|
|
106
|
+
mock_response.status = 200
|
|
107
|
+
mock_response.text = AsyncMock(return_value="text")
|
|
108
|
+
mock_response.json = AsyncMock(return_value=CONSUMPTIONS_RESPONSE)
|
|
109
|
+
mock_get.return_value.__aenter__.return_value = mock_response
|
|
110
|
+
connector = DatadisConnector(MOCK_USERNAME, MOCK_PASSWORD)
|
|
111
|
+
assert connector.get_consumption_data(
|
|
112
|
+
"ESXXXXXXXXXXXXXXXXTEST",
|
|
113
|
+
"2",
|
|
114
|
+
datetime.datetime(2022, 10, 22, 0, 0, 0),
|
|
115
|
+
datetime.datetime(2022, 10, 22, 2, 0, 0),
|
|
116
|
+
"0",
|
|
117
|
+
5,
|
|
118
|
+
) == snapshot
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
@patch("aiohttp.ClientSession.get")
|
|
123
|
+
@patch.object(DatadisConnector, "_async_get_token", new_callable=AsyncMock, return_value=True)
|
|
124
|
+
def test_get_max_power(mock_token, mock_get, snapshot):
|
|
125
|
+
"""Test a successful 'get_max_power' query (syrupy snapshot)."""
|
|
126
|
+
mock_response = MagicMock()
|
|
127
|
+
mock_response.status = 200
|
|
128
|
+
mock_response.text = AsyncMock(return_value="text")
|
|
129
|
+
mock_response.json = AsyncMock(return_value=MAXIMETER_RESPONSE)
|
|
130
|
+
mock_get.return_value.__aenter__.return_value = mock_response
|
|
131
|
+
connector = DatadisConnector(MOCK_USERNAME, MOCK_PASSWORD)
|
|
132
|
+
assert connector.get_max_power(
|
|
133
|
+
"ESXXXXXXXXXXXXXXXXTEST",
|
|
134
|
+
"2",
|
|
135
|
+
datetime.datetime(2022, 3, 1, 0, 0, 0),
|
|
136
|
+
datetime.datetime(2022, 4, 1, 0, 0, 0),
|
|
137
|
+
None,
|
|
138
|
+
) == snapshot
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
@patch("aiohttp.ClientSession.get")
|
|
142
|
+
@patch.object(DatadisConnector, "_async_get_token", new_callable=AsyncMock, return_value=True)
|
|
143
|
+
def test_get_supplies_empty_response(mock_token, mock_get, snapshot):
|
|
144
|
+
"""Test get_supplies with empty response (syrupy snapshot)."""
|
|
145
|
+
mock_response = MagicMock()
|
|
146
|
+
mock_response.status = 200
|
|
147
|
+
mock_response.text = AsyncMock(return_value="text")
|
|
148
|
+
mock_response.json = AsyncMock(return_value=[])
|
|
149
|
+
mock_get.return_value.__aenter__.return_value = mock_response
|
|
150
|
+
connector = DatadisConnector(MOCK_USERNAME, MOCK_PASSWORD)
|
|
151
|
+
assert connector.get_supplies() == snapshot
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
@patch("aiohttp.ClientSession.get")
|
|
155
|
+
@patch.object(DatadisConnector, "_async_get_token", new_callable=AsyncMock, return_value=True)
|
|
156
|
+
def test_get_supplies_malformed_response(mock_token, mock_get, snapshot):
|
|
157
|
+
"""Test get_supplies with malformed response (missing required fields, syrupy snapshot)."""
|
|
158
|
+
malformed = [{"validDateFrom": "2022/03/09"}] # missing 'cups', etc.
|
|
159
|
+
mock_response = MagicMock()
|
|
160
|
+
mock_response.status = 200
|
|
161
|
+
mock_response.text = AsyncMock(return_value="text")
|
|
162
|
+
mock_response.json = AsyncMock(return_value=malformed)
|
|
163
|
+
mock_get.return_value.__aenter__.return_value = mock_response
|
|
164
|
+
connector = DatadisConnector(MOCK_USERNAME, MOCK_PASSWORD)
|
|
165
|
+
assert connector.get_supplies() == snapshot
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
@patch("aiohttp.ClientSession.get")
|
|
169
|
+
@patch.object(DatadisConnector, "_async_get_token", new_callable=AsyncMock, return_value=True)
|
|
170
|
+
def test_get_supplies_partial_response(mock_token, mock_get, snapshot):
|
|
171
|
+
"""Test get_supplies with partial valid/invalid response (syrupy snapshot)."""
|
|
172
|
+
partial = [
|
|
173
|
+
SUPPLIES_RESPONSE[0],
|
|
174
|
+
{"validDateFrom": "2022/03/09"}, # invalid
|
|
175
|
+
]
|
|
176
|
+
mock_response = MagicMock()
|
|
177
|
+
mock_response.status = 200
|
|
178
|
+
mock_response.text = AsyncMock(return_value="text")
|
|
179
|
+
mock_response.json = AsyncMock(return_value=partial)
|
|
180
|
+
mock_get.return_value.__aenter__.return_value = mock_response
|
|
181
|
+
connector = DatadisConnector(MOCK_USERNAME, MOCK_PASSWORD)
|
|
182
|
+
assert connector.get_supplies() == snapshot
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
@patch("aiohttp.ClientSession.get")
|
|
187
|
+
@patch.object(DatadisConnector, "_async_get_token", new_callable=AsyncMock, return_value=True)
|
|
188
|
+
def test_get_consumption_data_cache(mock_token, mock_get, snapshot):
|
|
189
|
+
"""Test get_consumption_data uses cache on second call (should not call HTTP again, syrupy snapshot)."""
|
|
190
|
+
mock_response = MagicMock()
|
|
191
|
+
mock_response.status = 200
|
|
192
|
+
mock_response.text = AsyncMock(return_value="text")
|
|
193
|
+
mock_response.json = AsyncMock(return_value=CONSUMPTIONS_RESPONSE)
|
|
194
|
+
mock_get.return_value.__aenter__.return_value = mock_response
|
|
195
|
+
connector = DatadisConnector(MOCK_USERNAME, MOCK_PASSWORD)
|
|
196
|
+
# First call populates cache
|
|
197
|
+
assert connector.get_consumption_data(
|
|
198
|
+
"ESXXXXXXXXXXXXXXXXTEST",
|
|
199
|
+
"2",
|
|
200
|
+
datetime.datetime(2022, 10, 22, 0, 0, 0),
|
|
201
|
+
datetime.datetime(2022, 10, 22, 2, 0, 0),
|
|
202
|
+
"0",
|
|
203
|
+
5,
|
|
204
|
+
) == snapshot
|
|
205
|
+
# Second call should use cache, not call HTTP again
|
|
206
|
+
mock_get.reset_mock()
|
|
207
|
+
assert connector.get_consumption_data(
|
|
208
|
+
"ESXXXXXXXXXXXXXXXXTEST",
|
|
209
|
+
"2",
|
|
210
|
+
datetime.datetime(2022, 10, 22, 0, 0, 0),
|
|
211
|
+
datetime.datetime(2022, 10, 22, 2, 0, 0),
|
|
212
|
+
"0",
|
|
213
|
+
5,
|
|
214
|
+
) == snapshot
|
|
215
|
+
mock_get.assert_not_called()
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
@patch("aiohttp.ClientSession.get")
|
|
219
|
+
@patch.object(DatadisConnector, "_async_get_token", new_callable=AsyncMock, return_value=True)
|
|
220
|
+
def test_get_supplies_optional_fields_none(mock_token, mock_get, snapshot):
|
|
221
|
+
"""Test get_supplies with optional fields as None (syrupy snapshot)."""
|
|
222
|
+
response = [
|
|
223
|
+
{
|
|
224
|
+
"cups": "ESXXXXXXXXXXXXXXXXTEST",
|
|
225
|
+
"validDateFrom": "2022/03/09",
|
|
226
|
+
"validDateTo": "2022/10/28",
|
|
227
|
+
"address": None,
|
|
228
|
+
"postalCode": None,
|
|
229
|
+
"province": None,
|
|
230
|
+
"municipality": None,
|
|
231
|
+
"distributor": None,
|
|
232
|
+
"pointType": 5,
|
|
233
|
+
"distributorCode": "2",
|
|
234
|
+
}
|
|
235
|
+
]
|
|
236
|
+
mock_response = MagicMock()
|
|
237
|
+
mock_response.status = 200
|
|
238
|
+
mock_response.text = AsyncMock(return_value="text")
|
|
239
|
+
mock_response.json = AsyncMock(return_value=response)
|
|
240
|
+
mock_get.return_value.__aenter__.return_value = mock_response
|
|
241
|
+
connector = DatadisConnector(MOCK_USERNAME, MOCK_PASSWORD)
|
|
242
|
+
assert connector.get_supplies() == snapshot
|
tests/test_helpers.py
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"""A collection of tests for e-data processors"""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import pathlib
|
|
5
|
+
|
|
6
|
+
from freezegun import freeze_time
|
|
7
|
+
|
|
8
|
+
from ..definitions import PricingRules
|
|
9
|
+
from ..helpers import EdataHelper
|
|
10
|
+
from ..processors import utils
|
|
11
|
+
|
|
12
|
+
AT_TIME = "2022-10-22"
|
|
13
|
+
TESTS_DIR = str(pathlib.Path(__file__).parent.resolve())
|
|
14
|
+
TEST_GOOD_INPUT = TESTS_DIR + "/assets/helpers/edata.storage_TEST"
|
|
15
|
+
TEST_EXPECTATIONS_DATA = TESTS_DIR + "/assets/helpers/data.out"
|
|
16
|
+
TEST_EXPECTATIONS_ATTRIBUTES = (
|
|
17
|
+
TESTS_DIR + f"/assets/helpers/attributes_at_{AT_TIME}.out"
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
PRICING_RULES_PVPC = PricingRules(
|
|
21
|
+
p1_kw_year_eur=30.67266,
|
|
22
|
+
p2_kw_year_eur=1.4243591,
|
|
23
|
+
meter_month_eur=0.81,
|
|
24
|
+
market_kw_year_eur=3.113,
|
|
25
|
+
electricity_tax=1.0511300560,
|
|
26
|
+
iva_tax=1.05,
|
|
27
|
+
p1_kwh_eur=None,
|
|
28
|
+
p2_kwh_eur=None,
|
|
29
|
+
p3_kwh_eur=None,
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
@freeze_time(AT_TIME)
|
|
33
|
+
def test_helper_offline(snapshot) -> None:
|
|
34
|
+
"""Tests EdataHelper (syrupy snapshot)"""
|
|
35
|
+
with open(TEST_GOOD_INPUT, "r", encoding="utf-8") as original_file:
|
|
36
|
+
data = utils.deserialize_dict(json.load(original_file))
|
|
37
|
+
|
|
38
|
+
helper = EdataHelper(
|
|
39
|
+
"USER",
|
|
40
|
+
"PASS",
|
|
41
|
+
"CUPS",
|
|
42
|
+
datadis_authorized_nif=None,
|
|
43
|
+
pricing_rules=PRICING_RULES_PVPC,
|
|
44
|
+
data=data,
|
|
45
|
+
)
|
|
46
|
+
helper.process_data()
|
|
47
|
+
|
|
48
|
+
# Compara ambos outputs con snapshot
|
|
49
|
+
assert {
|
|
50
|
+
"data": utils.serialize_dict(helper.data),
|
|
51
|
+
"attributes": utils.serialize_dict(helper.attributes),
|
|
52
|
+
} == snapshot
|
tests/test_processors.py
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
"""A collection of tests for e-data processors"""
|
|
2
|
+
|
|
3
|
+
import datetime as dt
|
|
4
|
+
import json
|
|
5
|
+
import pathlib
|
|
6
|
+
import typing
|
|
7
|
+
from collections.abc import Iterable
|
|
8
|
+
|
|
9
|
+
import pytest
|
|
10
|
+
|
|
11
|
+
from ..definitions import PricingData, PricingRules
|
|
12
|
+
from ..processors import utils
|
|
13
|
+
from ..processors.base import Processor
|
|
14
|
+
from ..processors.billing import BillingProcessor
|
|
15
|
+
from ..processors.consumption import ConsumptionProcessor
|
|
16
|
+
from ..processors.maximeter import MaximeterProcessor
|
|
17
|
+
|
|
18
|
+
TESTS_DIR = str(pathlib.Path(__file__).parent.resolve())
|
|
19
|
+
TEST_GOOD_INPUT = TESTS_DIR + "/assets/processors/edata.storage_TEST"
|
|
20
|
+
|
|
21
|
+
def _compare_processor_output(
|
|
22
|
+
source_filepath: str,
|
|
23
|
+
processor_class: Processor,
|
|
24
|
+
key: str,
|
|
25
|
+
snapshot,
|
|
26
|
+
):
|
|
27
|
+
with open(source_filepath, encoding="utf-8") as original_file:
|
|
28
|
+
data = utils.deserialize_dict(json.load(original_file))
|
|
29
|
+
if key == "consumptions":
|
|
30
|
+
processor = processor_class({"consumptions": data[key]})
|
|
31
|
+
else:
|
|
32
|
+
processor = processor_class(data[key])
|
|
33
|
+
assert utils.serialize_dict(processor.output) == snapshot
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@pytest.mark.parametrize(
|
|
37
|
+
"processor, key",
|
|
38
|
+
[(ConsumptionProcessor, "consumptions"), (MaximeterProcessor, "maximeter")],
|
|
39
|
+
)
|
|
40
|
+
def test_processor(processor: Processor, key: str, snapshot) -> None:
|
|
41
|
+
"""Tests all processors but billing (syrupy snapshot)"""
|
|
42
|
+
_compare_processor_output(
|
|
43
|
+
TEST_GOOD_INPUT,
|
|
44
|
+
processor,
|
|
45
|
+
key,
|
|
46
|
+
snapshot,
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
@pytest.mark.parametrize(
|
|
51
|
+
"_id, rules, prices",
|
|
52
|
+
[
|
|
53
|
+
(
|
|
54
|
+
"custom_prices",
|
|
55
|
+
PricingRules(
|
|
56
|
+
p1_kw_year_eur=30.67266,
|
|
57
|
+
p2_kw_year_eur=1.4243591,
|
|
58
|
+
meter_month_eur=0.81,
|
|
59
|
+
market_kw_year_eur=3.113,
|
|
60
|
+
electricity_tax=1.0511300560,
|
|
61
|
+
iva_tax=1.1,
|
|
62
|
+
p1_kwh_eur=None,
|
|
63
|
+
p2_kwh_eur=None,
|
|
64
|
+
p3_kwh_eur=None,
|
|
65
|
+
),
|
|
66
|
+
[
|
|
67
|
+
PricingData(
|
|
68
|
+
datetime=dt.datetime(2022, 10, 22, x, 0, 0),
|
|
69
|
+
value_eur_kWh=1,
|
|
70
|
+
delta_h=1,
|
|
71
|
+
)
|
|
72
|
+
for x in range(0, 24)
|
|
73
|
+
],
|
|
74
|
+
),
|
|
75
|
+
(
|
|
76
|
+
"constant_prices",
|
|
77
|
+
PricingRules(
|
|
78
|
+
p1_kw_year_eur=30.67266,
|
|
79
|
+
p2_kw_year_eur=1.4243591,
|
|
80
|
+
meter_month_eur=0.81,
|
|
81
|
+
market_kw_year_eur=3.113,
|
|
82
|
+
electricity_tax=1.0511300560,
|
|
83
|
+
iva_tax=1.1,
|
|
84
|
+
p1_kwh_eur=1,
|
|
85
|
+
p2_kwh_eur=1,
|
|
86
|
+
p3_kwh_eur=1,
|
|
87
|
+
),
|
|
88
|
+
None,
|
|
89
|
+
),
|
|
90
|
+
],
|
|
91
|
+
)
|
|
92
|
+
def test_processor_billing(
|
|
93
|
+
_id: str, rules: PricingRules, prices: typing.Optional[Iterable[PricingData]], snapshot
|
|
94
|
+
):
|
|
95
|
+
"""Tests billing processor (syrupy snapshot)"""
|
|
96
|
+
with open(TEST_GOOD_INPUT, "r", encoding="utf-8") as original_file:
|
|
97
|
+
data = utils.deserialize_dict(json.load(original_file))
|
|
98
|
+
processor = BillingProcessor(
|
|
99
|
+
{
|
|
100
|
+
"consumptions": data["consumptions"],
|
|
101
|
+
"contracts": data["contracts"],
|
|
102
|
+
"prices": prices,
|
|
103
|
+
"rules": rules,
|
|
104
|
+
}
|
|
105
|
+
)
|
|
106
|
+
assert utils.serialize_dict(processor.output) == snapshot
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"""Tests for REData (online)"""
|
|
2
|
+
|
|
3
|
+
from datetime import datetime, timedelta
|
|
4
|
+
|
|
5
|
+
from ..connectors.redata import REDataConnector
|
|
6
|
+
|
|
7
|
+
def test_get_realtime_prices():
|
|
8
|
+
"""Test a successful 'get_realtime_prices' query"""
|
|
9
|
+
connector = REDataConnector()
|
|
10
|
+
yesterday = datetime.now().replace(hour=0, minute=0, second=0) - timedelta(days=1)
|
|
11
|
+
response = connector.get_realtime_prices(
|
|
12
|
+
yesterday, yesterday + timedelta(days=1) - timedelta(minutes=1), False
|
|
13
|
+
)
|
|
14
|
+
assert len(response) == 24
|