1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141
|
"""Test edge cases in bsblan.py for 100% coverage."""
from __future__ import annotations
from typing import Any
from unittest.mock import AsyncMock, MagicMock
import aiohttp
import pytest
from bsblan import BSBLAN, BSBLANConfig
from bsblan.constants import API_DATA_NOT_INITIALIZED_ERROR_MSG
from bsblan.exceptions import BSBLANConnectionError, BSBLANError
@pytest.mark.asyncio
async def test_initialize_api_data_edge_case() -> None:
"""Test _initialize_api_data when API data is None after version setting."""
config = BSBLANConfig(host="example.com")
bsblan = BSBLAN(config)
# Force API version to be set but data to be None
bsblan._api_version = "v3"
bsblan._api_data = None
# This should trigger the defensive check in _initialize_api_data
api_data = await bsblan._initialize_api_data()
assert api_data is not None
@pytest.mark.asyncio
async def test_validate_api_section_key_error(monkeypatch: Any) -> None:
"""Test validate_api_section when section is not found in API data."""
async with aiohttp.ClientSession() as session:
config = BSBLANConfig(host="example.com")
bsblan = BSBLAN(config, session=session)
# Set up basic initialization
bsblan._firmware_version = "1.0.38-20200730234859"
bsblan._api_version = "v3"
bsblan._api_data = {"other_section": {}} # type: ignore[assignment]
# Mock API validator
mock_validator = MagicMock()
mock_validator.is_section_validated.return_value = False
bsblan._api_validator = mock_validator
# Mock request to avoid network calls
request_mock = AsyncMock(return_value={})
monkeypatch.setattr(bsblan, "_request", request_mock)
# This should trigger the KeyError path
with pytest.raises(
BSBLANError,
match="Section 'nonexistent' not found in API data",
):
await bsblan._validate_api_section("nonexistent") # type: ignore[arg-type]
@pytest.mark.asyncio
async def test_client_response_error_path(monkeypatch: Any) -> None:
"""Test aiohttp.ClientError handling in _request."""
async with aiohttp.ClientSession() as session:
config = BSBLANConfig(host="example.com")
bsblan = BSBLAN(config, session=session)
# Mock session.request to raise ClientError
mock_response = MagicMock()
mock_response.__aenter__ = AsyncMock(
side_effect=aiohttp.ClientError("Connection failed")
)
mock_response.__aexit__ = AsyncMock(return_value=None)
monkeypatch.setattr(session, "request", MagicMock(return_value=mock_response))
with pytest.raises(BSBLANConnectionError):
await bsblan._request()
@pytest.mark.asyncio
async def test_value_error_path(monkeypatch: Any) -> None:
"""Test ValueError handling in _request."""
async with aiohttp.ClientSession() as session:
config = BSBLANConfig(host="example.com")
bsblan = BSBLAN(config, session=session)
# Mock a successful response but with invalid JSON processing
mock_response = MagicMock()
mock_response.raise_for_status = MagicMock()
mock_response.json = AsyncMock(side_effect=ValueError("Invalid JSON"))
mock_response.__aenter__ = AsyncMock(return_value=mock_response)
mock_response.__aexit__ = AsyncMock(return_value=None)
monkeypatch.setattr(session, "request", MagicMock(return_value=mock_response))
with pytest.raises(BSBLANError, match="Invalid JSON"):
await bsblan._request()
def test_bsblan_config_initialization_edge_cases() -> None:
"""Test edge cases in BSBLAN initialization."""
# Test that we can create a BSBLAN instance without session
config = BSBLANConfig(host="example.com")
bsblan = BSBLAN(config)
# Verify initial state
assert bsblan.session is None
assert bsblan._initialized is False
assert len(bsblan._hot_water_param_cache) == 0
@pytest.mark.asyncio
async def test_initialize_api_data_none_after_init(monkeypatch: Any) -> None:
"""Test _initialize_api_data raises error when api_data remains None.
This covers the defensive check at line 391 in bsblan.py.
"""
config = BSBLANConfig(host="example.com")
bsblan = BSBLAN(config)
# Set API version so we pass the first check
bsblan._api_version = "v3"
# Monkeypatch dict comprehension to return None (simulating failure)
original_items = dict.items
def mock_items(self: dict[str, Any]) -> Any:
# Return empty to prevent assignment
return original_items(self)
# Force _api_data to stay None by patching the assignment
def mock_setattr(obj: Any, name: str, value: Any) -> None:
if name == "_api_data":
object.__setattr__(obj, name, None)
else:
object.__setattr__(obj, name, value)
monkeypatch.setattr(BSBLAN, "__setattr__", mock_setattr)
with pytest.raises(BSBLANError, match=API_DATA_NOT_INITIALIZED_ERROR_MSG):
await bsblan._initialize_api_data()
|