230 lines
7.5 KiB
Python
230 lines
7.5 KiB
Python
"""Unit tests for the HourData DTO model."""
|
|
|
|
from datetime import datetime
|
|
from zoneinfo import ZoneInfo
|
|
import unittest
|
|
|
|
import pytest
|
|
from pydantic import ValidationError
|
|
|
|
from elprisenligenu.dto.hour_data import HourData
|
|
|
|
|
|
class TestHourData(unittest.TestCase):
|
|
"""Test cases for the HourData model."""
|
|
|
|
def test_valid_hour_data_creation(self) -> None:
|
|
"""Test creating a valid HourData instance."""
|
|
# Arrange
|
|
tz = ZoneInfo("Europe/Copenhagen")
|
|
start_time = datetime(2024, 10, 29, 12, 0, 0, tzinfo=tz)
|
|
end_time = datetime(2024, 10, 29, 13, 0, 0, tzinfo=tz)
|
|
|
|
# Act
|
|
hour_data = HourData(
|
|
DKK_per_kWh=1.2345,
|
|
EUR_per_kWh=0.1659,
|
|
EXR=7.4371,
|
|
time_start=start_time,
|
|
time_end=end_time,
|
|
)
|
|
|
|
# Assert
|
|
self.assertEqual(hour_data.DKK_per_kWh, 1.2345)
|
|
self.assertEqual(hour_data.EUR_per_kWh, 0.1659)
|
|
self.assertEqual(hour_data.EXR, 7.4371)
|
|
self.assertEqual(hour_data.time_start, start_time)
|
|
self.assertEqual(hour_data.time_end, end_time)
|
|
|
|
def test_hour_data_from_api_response(self) -> None:
|
|
"""Test creating HourData from typical API response data."""
|
|
|
|
# Act - Pydantic can parse ISO datetime strings
|
|
hour_data = HourData(
|
|
DKK_per_kWh=1.3929,
|
|
EUR_per_kWh=0.18729,
|
|
EXR=7.437118,
|
|
time_start=datetime.fromisoformat("2022-11-25T00:00:00+01:00"),
|
|
time_end=datetime.fromisoformat("2022-11-25T01:00:00+01:00"),
|
|
)
|
|
|
|
# Assert
|
|
self.assertEqual(hour_data.DKK_per_kWh, 1.3929)
|
|
self.assertEqual(hour_data.EUR_per_kWh, 0.18729)
|
|
self.assertEqual(hour_data.EXR, 7.437118)
|
|
self.assertEqual(hour_data.time_start.year, 2022)
|
|
self.assertEqual(hour_data.time_start.month, 11)
|
|
self.assertEqual(hour_data.time_start.day, 25)
|
|
self.assertEqual(hour_data.time_start.hour, 0)
|
|
self.assertEqual(hour_data.time_end.hour, 1)
|
|
|
|
def test_hour_data_validation_missing_fields(self) -> None:
|
|
"""Test validation fails when required fields are missing."""
|
|
|
|
# Arrange
|
|
incomplete_data = {
|
|
"DKK_per_kWh": 1.2345,
|
|
"EUR_per_kWh": 0.1659,
|
|
# Missing EXR, time_start, time_end
|
|
}
|
|
|
|
# Act & Assert
|
|
with pytest.raises(ValidationError) as exc_info:
|
|
HourData(**incomplete_data) # type: ignore[arg-type]
|
|
|
|
errors = exc_info.value.errors()
|
|
missing_fields = {error["loc"][0] for error in errors}
|
|
self.assertIn("EXR", missing_fields)
|
|
self.assertIn("time_start", missing_fields)
|
|
self.assertIn("time_end", missing_fields)
|
|
|
|
def test_hour_data_validation_invalid_types(self) -> None:
|
|
"""Test validation fails with invalid data types."""
|
|
# Arrange
|
|
invalid_data = {
|
|
"DKK_per_kWh": "not_a_number", # Should be float
|
|
"EUR_per_kWh": 0.1659,
|
|
"EXR": 7.4371,
|
|
"time_start": "2024-10-29T12:00:00+01:00",
|
|
"time_end": "2024-10-29T13:00:00+01:00",
|
|
}
|
|
|
|
# Act & Assert
|
|
with pytest.raises(ValidationError) as exc_info:
|
|
HourData(**invalid_data) # type: ignore[arg-type]
|
|
|
|
errors = exc_info.value.errors()
|
|
self.assertTrue(any(error["loc"][0] == "DKK_per_kWh" for error in errors))
|
|
|
|
def test_hour_data_negative_prices(self) -> None:
|
|
"""Test HourData accepts negative prices (real markets)."""
|
|
# Arrange
|
|
tz = ZoneInfo("Europe/Copenhagen")
|
|
|
|
# Act
|
|
hour_data = HourData(
|
|
DKK_per_kWh=-0.5, # Negative price
|
|
EUR_per_kWh=-0.067,
|
|
EXR=7.4371,
|
|
time_start=datetime(2024, 10, 29, 12, 0, 0, tzinfo=tz),
|
|
time_end=datetime(2024, 10, 29, 13, 0, 0, tzinfo=tz),
|
|
)
|
|
|
|
# Assert
|
|
self.assertEqual(hour_data.DKK_per_kWh, -0.5)
|
|
self.assertEqual(hour_data.EUR_per_kWh, -0.067)
|
|
|
|
def test_hour_data_zero_prices(self) -> None:
|
|
"""Test HourData accepts zero prices."""
|
|
# Arrange
|
|
tz = ZoneInfo("Europe/Copenhagen")
|
|
|
|
# Act
|
|
hour_data = HourData(
|
|
DKK_per_kWh=0.0,
|
|
EUR_per_kWh=0.0,
|
|
EXR=7.4371,
|
|
time_start=datetime(2024, 10, 29, 12, 0, 0, tzinfo=tz),
|
|
time_end=datetime(2024, 10, 29, 13, 0, 0, tzinfo=tz),
|
|
)
|
|
|
|
# Assert
|
|
self.assertEqual(hour_data.DKK_per_kWh, 0.0)
|
|
self.assertEqual(hour_data.EUR_per_kWh, 0.0)
|
|
|
|
def test_hour_data_serialization(self) -> None:
|
|
"""Test HourData can be serialized to JSON."""
|
|
# Arrange
|
|
tz = ZoneInfo("Europe/Copenhagen")
|
|
start_time = datetime(2024, 10, 29, 12, 0, 0, tzinfo=tz)
|
|
end_time = datetime(2024, 10, 29, 13, 0, 0, tzinfo=tz)
|
|
|
|
hour_data = HourData(
|
|
DKK_per_kWh=1.2345,
|
|
EUR_per_kWh=0.1659,
|
|
EXR=7.4371,
|
|
time_start=start_time,
|
|
time_end=end_time,
|
|
)
|
|
|
|
# Act
|
|
json_data = hour_data.model_dump()
|
|
|
|
# Assert
|
|
self.assertEqual(json_data["DKK_per_kWh"], 1.2345)
|
|
self.assertEqual(json_data["EUR_per_kWh"], 0.1659)
|
|
self.assertEqual(json_data["EXR"], 7.4371)
|
|
self.assertEqual(json_data["time_start"], start_time)
|
|
self.assertEqual(json_data["time_end"], end_time)
|
|
|
|
def test_hour_data_string_representation(self) -> None:
|
|
"""Test HourData string representation is readable."""
|
|
# Arrange
|
|
tz = ZoneInfo("Europe/Copenhagen")
|
|
hour_data = HourData(
|
|
DKK_per_kWh=1.2345,
|
|
EUR_per_kWh=0.1659,
|
|
EXR=7.4371,
|
|
time_start=datetime(2024, 10, 29, 12, 0, 0, tzinfo=tz),
|
|
time_end=datetime(2024, 10, 29, 13, 0, 0, tzinfo=tz),
|
|
)
|
|
|
|
# Act
|
|
str_repr = str(hour_data)
|
|
|
|
# Assert
|
|
self.assertIn("DKK_per_kWh=1.2345", str_repr)
|
|
self.assertIn("EUR_per_kWh=0.1659", str_repr)
|
|
self.assertIn("EXR=7.4371", str_repr)
|
|
|
|
def test_hour_data_equality(self) -> None:
|
|
"""Test HourData equality comparison."""
|
|
# Arrange
|
|
tz = ZoneInfo("Europe/Copenhagen")
|
|
start_time = datetime(2024, 10, 29, 12, 0, 0, tzinfo=tz)
|
|
end_time = datetime(2024, 10, 29, 13, 0, 0, tzinfo=tz)
|
|
|
|
hour_data1 = HourData(
|
|
DKK_per_kWh=1.2345,
|
|
EUR_per_kWh=0.1659,
|
|
EXR=7.4371,
|
|
time_start=start_time,
|
|
time_end=end_time,
|
|
)
|
|
hour_data2 = HourData(
|
|
DKK_per_kWh=1.2345,
|
|
EUR_per_kWh=0.1659,
|
|
EXR=7.4371,
|
|
time_start=start_time,
|
|
time_end=end_time,
|
|
)
|
|
|
|
# Act & Assert
|
|
self.assertEqual(hour_data1, hour_data2)
|
|
|
|
def test_hour_data_inequality(self) -> None:
|
|
"""Test HourData inequality comparison."""
|
|
# Arrange
|
|
tz = ZoneInfo("Europe/Copenhagen")
|
|
start_time = datetime(2024, 10, 29, 12, 0, 0, tzinfo=tz)
|
|
end_time = datetime(2024, 10, 29, 13, 0, 0, tzinfo=tz)
|
|
|
|
hour_data1 = HourData(
|
|
DKK_per_kWh=1.2345,
|
|
EUR_per_kWh=0.1659,
|
|
EXR=7.4371,
|
|
time_start=start_time,
|
|
time_end=end_time,
|
|
)
|
|
|
|
hour_data2 = HourData(
|
|
DKK_per_kWh=2.0000, # Different price
|
|
EUR_per_kWh=0.1659,
|
|
EXR=7.4371,
|
|
time_start=start_time,
|
|
time_end=end_time,
|
|
)
|
|
|
|
# Act & Assert
|
|
self.assertNotEqual(hour_data1, hour_data2)
|