"""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)