File: test_bsblan_edge_cases.py

package info (click to toggle)
python-bsblan 3.1.4-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 1,028 kB
  • sloc: python: 4,453; makefile: 3
file content (141 lines) | stat: -rw-r--r-- 5,087 bytes parent folder | download
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()