Skip to content

Commit cc07c2e

Browse files
Fix openremote client mocked method signatures and missing types for datapoints in tests
1 parent b254152 commit cc07c2e

File tree

3 files changed

+30
-49
lines changed

3 files changed

+30
-49
lines changed

src/service_ml_forecast/services/openremote_service.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ def get_training_dataset(self, config: ModelConfig) -> TrainingDataSet | None:
6767
end_timestamp = TimeUtil.get_timestamp_ms()
6868

6969
# Retrieve target feature datapoints from OpenRemote with chunking if needed
70-
datapoints = self.__get_historical_datapoints(
70+
datapoints = self._get_historical_datapoints(
7171
config.target.asset_id,
7272
config.target.attribute_name,
7373
start_timestamp,
@@ -94,7 +94,7 @@ def get_training_dataset(self, config: ModelConfig) -> TrainingDataSet | None:
9494
# Get the start timestamp for the regressor historical data
9595
start_timestamp = TimeUtil.get_period_start_timestamp_ms(regressor.training_data_period)
9696

97-
regressor_datapoints = self.__get_historical_datapoints(
97+
regressor_datapoints = self._get_historical_datapoints(
9898
regressor.asset_id,
9999
regressor.attribute_name,
100100
start_timestamp,
@@ -125,7 +125,7 @@ def get_training_dataset(self, config: ModelConfig) -> TrainingDataSet | None:
125125

126126
return training_dataset
127127

128-
def __get_historical_datapoints(
128+
def _get_historical_datapoints(
129129
self, asset_id: str, attribute_name: str, from_timestamp: int, to_timestamp: int
130130
) -> list[AssetDatapoint] | None:
131131
"""Wrapper get_historical_datapoints to split up requests into monthly chunks.
@@ -152,7 +152,7 @@ def __get_historical_datapoints(
152152
logger.info(
153153
f"Chunking datapoint retrieval into {months_diff} monthly chunks for {asset_id} {attribute_name}"
154154
)
155-
155+
156156
# Continue until we've processed a chunk that ends at or after to_timestamp
157157
while current_from < to_timestamp:
158158
# Calculate the end timestamp for this chunk (1 month from current_from)

tests/services/test_openremote_service.py

Lines changed: 26 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from unittest.mock import Mock
22

3+
from service_ml_forecast.clients.openremote.models import AssetDatapoint
34
from service_ml_forecast.services.openremote_service import OpenRemoteService
45

56
# Constants for test values
@@ -27,8 +28,8 @@ def test_get_historical_datapoints_single_month_no_chunking(mock_openremote_serv
2728
mock_openremote_service.client = mock_client
2829

2930
# Mock return value for single month
30-
mock_datapoints = [{"timestamp": JAN_1_2024, "value": 100}]
31-
mock_client.assets.get_historical_datapoints.return_value = mock_datapoints
31+
mock_datapoints = [AssetDatapoint(x=JAN_1_2024, y=100)]
32+
mock_client.get_historical_datapoints.return_value = mock_datapoints
3233

3334
# Test single month (Jan 1 to Feb 1)
3435
from_timestamp = JAN_1_2024 # 2024-01-01 00:00:00 UTC
@@ -39,7 +40,7 @@ def test_get_historical_datapoints_single_month_no_chunking(mock_openremote_serv
3940
)
4041

4142
# Should call client method once (no chunking)
42-
mock_client.assets.get_historical_datapoints.assert_called_once_with(
43+
mock_client.get_historical_datapoints.assert_called_once_with(
4344
"test_asset", "test_attribute", from_timestamp, to_timestamp
4445
)
4546
assert result == mock_datapoints
@@ -60,11 +61,11 @@ def test_get_historical_datapoints_multi_month_chunking(mock_openremote_service:
6061
mock_openremote_service.client = mock_client
6162

6263
# Mock return values for chunks
63-
chunk1_datapoints = [{"timestamp": JAN_1_2024, "value": 100}] # Jan
64-
chunk2_datapoints = [{"timestamp": FEB_1_2024, "value": 200}] # Feb
65-
chunk3_datapoints = [{"timestamp": MAR_1_2024, "value": 300}] # Mar
64+
chunk1_datapoints = [AssetDatapoint(x=JAN_1_2024, y=100)] # Jan
65+
chunk2_datapoints = [AssetDatapoint(x=FEB_1_2024, y=200)] # Feb
66+
chunk3_datapoints = [AssetDatapoint(x=MAR_1_2024, y=300)] # Mar
6667

67-
mock_client.assets.get_historical_datapoints.side_effect = [
68+
mock_client.get_historical_datapoints.side_effect = [
6869
chunk1_datapoints,
6970
chunk2_datapoints,
7071
chunk3_datapoints,
@@ -79,10 +80,10 @@ def test_get_historical_datapoints_multi_month_chunking(mock_openremote_service:
7980
)
8081

8182
# Should call client method 3 times (one for each month)
82-
assert mock_client.assets.get_historical_datapoints.call_count == EXPECTED_CALLS_3_MONTHS
83+
assert mock_client.get_historical_datapoints.call_count == EXPECTED_CALLS_3_MONTHS
8384

8485
# Verify the calls were made with correct timestamps
85-
calls = mock_client.assets.get_historical_datapoints.call_args_list
86+
calls = mock_client.get_historical_datapoints.call_args_list
8687
assert len(calls) == EXPECTED_CALLS_3_MONTHS
8788

8889
# Verify chunk boundaries are correct
@@ -128,12 +129,12 @@ def test_get_historical_datapoints_chunking_partial_months(mock_openremote_servi
128129
mock_openremote_service.client = mock_client
129130

130131
# Mock return values for chunks
131-
jan_datapoints = [{"timestamp": 1705276800000, "value": 100}] # Jan 15-31
132-
feb_datapoints = [{"timestamp": FEB_1_2024, "value": 200}] # Feb 1-29
133-
mar_datapoints = [{"timestamp": MAR_1_2024, "value": 300}] # Mar 1-31
134-
apr_datapoints = [{"timestamp": APR_1_2024, "value": 400}] # Apr 1-30
132+
jan_datapoints = [AssetDatapoint(x=1705276800000, y=100)] # Jan 15-31
133+
feb_datapoints = [AssetDatapoint(x=FEB_1_2024, y=200)] # Feb 1-29
134+
mar_datapoints = [AssetDatapoint(x=MAR_1_2024, y=300)] # Mar 1-31
135+
apr_datapoints = [AssetDatapoint(x=APR_1_2024, y=400)] # Apr 1-30
135136

136-
mock_client.assets.get_historical_datapoints.side_effect = [
137+
mock_client.get_historical_datapoints.side_effect = [
137138
jan_datapoints,
138139
feb_datapoints,
139140
mar_datapoints,
@@ -149,10 +150,10 @@ def test_get_historical_datapoints_chunking_partial_months(mock_openremote_servi
149150
)
150151

151152
# Should call client method 4 times (Jan 15-31, Feb 1-29, Mar 1-31, Apr 1-30)
152-
assert mock_client.assets.get_historical_datapoints.call_count == EXPECTED_CALLS_4_MONTHS
153+
assert mock_client.get_historical_datapoints.call_count == EXPECTED_CALLS_4_MONTHS
153154

154155
# Verify the calls were made with correct timestamps
155-
calls = mock_client.assets.get_historical_datapoints.call_args_list
156+
calls = mock_client.get_historical_datapoints.call_args_list
156157
assert len(calls) == EXPECTED_CALLS_4_MONTHS
157158

158159
# Verify boundaries for partial month scenario
@@ -171,7 +172,7 @@ def test_get_historical_datapoints_chunking_partial_months(mock_openremote_servi
171172

172173
# Reset mock for second test
173174
mock_client.reset_mock()
174-
mock_client.assets.get_historical_datapoints.side_effect = [
175+
mock_client.get_historical_datapoints.side_effect = [
175176
jan_datapoints,
176177
feb_datapoints,
177178
mar_datapoints,
@@ -186,10 +187,10 @@ def test_get_historical_datapoints_chunking_partial_months(mock_openremote_servi
186187
)
187188

188189
# Should call client method 3 times (Jan 1-31, Feb 1-29, Mar 1-31)
189-
assert mock_client.assets.get_historical_datapoints.call_count == EXPECTED_CALLS_3_MONTHS
190+
assert mock_client.get_historical_datapoints.call_count == EXPECTED_CALLS_3_MONTHS
190191

191192
# Verify boundaries for the second test case
192-
calls = mock_client.assets.get_historical_datapoints.call_args_list
193+
calls = mock_client.get_historical_datapoints.call_args_list
193194
first_call_args = calls[0][0]
194195
last_call_args = calls[2][0]
195196

@@ -216,8 +217,8 @@ def test_get_historical_datapoints_chunking_failure_handling(mock_openremote_ser
216217
mock_openremote_service.client = mock_client
217218

218219
# Mock first chunk succeeds, second chunk fails
219-
chunk1_datapoints = [{"timestamp": JAN_1_2024, "value": 100}]
220-
mock_client.assets.get_historical_datapoints.side_effect = [
220+
chunk1_datapoints = [AssetDatapoint(x=JAN_1_2024, y=100)]
221+
mock_client.get_historical_datapoints.side_effect = [
221222
chunk1_datapoints,
222223
None, # Second chunk fails
223224
]
@@ -234,7 +235,7 @@ def test_get_historical_datapoints_chunking_failure_handling(mock_openremote_ser
234235
assert result is None
235236

236237
# Should call client method 2 times (first succeeds, second fails)
237-
assert mock_client.assets.get_historical_datapoints.call_count == EXPECTED_CALLS_2_MONTHS
238+
assert mock_client.get_historical_datapoints.call_count == EXPECTED_CALLS_2_MONTHS
238239

239240

240241
def test_get_historical_datapoints_chunking_edge_case_same_timestamp(
@@ -253,16 +254,14 @@ def test_get_historical_datapoints_chunking_edge_case_same_timestamp(
253254
mock_openremote_service.client = mock_client
254255

255256
# Mock return value
256-
mock_datapoints = [{"timestamp": JAN_1_2024, "value": 100}]
257-
mock_client.assets.get_historical_datapoints.return_value = mock_datapoints
257+
mock_datapoints = [AssetDatapoint(x=JAN_1_2024, y=100)]
258+
mock_client.get_historical_datapoints.return_value = mock_datapoints
258259

259260
# Test same timestamp
260261
timestamp = JAN_1_2024 # 2024-01-01 00:00:00 UTC
261262

262263
result = mock_openremote_service._get_historical_datapoints("test_asset", "test_attribute", timestamp, timestamp)
263264

264265
# Should call client method once (goes through single month path)
265-
mock_client.assets.get_historical_datapoints.assert_called_once_with(
266-
"test_asset", "test_attribute", timestamp, timestamp
267-
)
266+
mock_client.get_historical_datapoints.assert_called_once_with("test_asset", "test_attribute", timestamp, timestamp)
268267
assert result == mock_datapoints

tests/test_time_util.py

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -118,24 +118,6 @@ def test_pd_future_timestamp(self) -> None:
118118
current_ms = fixed_time * MILLISECONDS_PER_SECOND
119119
assert result > current_ms
120120

121-
def test_invalid_iso_duration_handling(self) -> None:
122-
"""Test handling of invalid ISO duration strings.
123-
124-
Verifies that:
125-
- Invalid duration strings raise ValueError
126-
- Empty strings raise ValueError
127-
- None values raise TypeError
128-
- The method fails gracefully with proper error types
129-
"""
130-
with pytest.raises(ValueError):
131-
TimeUtil.parse_iso_duration("invalid")
132-
133-
with pytest.raises(ValueError):
134-
TimeUtil.parse_iso_duration("")
135-
136-
with pytest.raises(TypeError):
137-
TimeUtil.parse_iso_duration(None)
138-
139121
def test_timestamp_edge_cases(self) -> None:
140122
"""Test timestamp methods with extreme edge cases.
141123

0 commit comments

Comments
 (0)