207 lines
6.4 KiB
Python
207 lines
6.4 KiB
Python
"""Tests for the PV Microinverter API client."""
|
|
|
|
import json
|
|
from unittest.mock import AsyncMock, MagicMock
|
|
|
|
import aiohttp
|
|
import pytest
|
|
from aiohttp import ClientResponseError, ClientSession
|
|
from custom_components.pv_microinverter.api import (
|
|
PVMicroinverterApiClient,
|
|
PVMicroinverterApiClientError,
|
|
)
|
|
|
|
|
|
@pytest.fixture
|
|
def mock_session():
|
|
"""Return a mocked aiohttp client session."""
|
|
session = MagicMock(spec=ClientSession)
|
|
session.get = AsyncMock()
|
|
return session
|
|
|
|
|
|
@pytest.fixture
|
|
def api_client(mock_session):
|
|
"""Return a new API client with a mocked session."""
|
|
return PVMicroinverterApiClient(
|
|
session=mock_session,
|
|
station_id="test_station_id",
|
|
base_url="https://api.example.com/v1",
|
|
)
|
|
|
|
|
|
@pytest.fixture
|
|
def mock_response():
|
|
"""Return a mocked API response."""
|
|
mock = MagicMock()
|
|
mock.raise_for_status = MagicMock()
|
|
mock.json = AsyncMock(
|
|
return_value={
|
|
"current_power": 500.5,
|
|
"today_energy": 3.75,
|
|
"lifetime_energy": 1250.25,
|
|
"last_updated": "2023-04-01T12:00:00Z",
|
|
}
|
|
)
|
|
return mock
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_async_get_data_success(api_client, mock_session, mock_response):
|
|
"""Test successful data retrieval."""
|
|
# Setup the mock response
|
|
mock_session.get.return_value = mock_response
|
|
|
|
# Call the method
|
|
data = await api_client.async_get_data()
|
|
|
|
# Verify the API call
|
|
mock_session.get.assert_called_once_with(
|
|
"https://api.example.com/v1/systems/test_system_id/stats",
|
|
headers={
|
|
"Authorization": "Bearer test_api_key",
|
|
"Content-Type": "application/json",
|
|
},
|
|
)
|
|
|
|
# Verify the response processing
|
|
assert data.current_power == 500.5
|
|
assert data.today_energy == 3.75
|
|
assert data.lifetime_energy == 1250.25
|
|
assert data.last_updated == "2023-04-01T12:00:00Z"
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_async_get_data_http_error(api_client, mock_session):
|
|
"""Test handling of HTTP errors."""
|
|
# Setup the mock to raise an error
|
|
error_response = MagicMock()
|
|
error_response.raise_for_status.side_effect = ClientResponseError(
|
|
request_info=MagicMock(),
|
|
history=None,
|
|
status=401,
|
|
message="Unauthorized",
|
|
headers=None,
|
|
)
|
|
mock_session.get.return_value = error_response
|
|
|
|
# Call the method and expect an exception
|
|
with pytest.raises(PVMicroinverterApiClientError) as excinfo:
|
|
await api_client.async_get_data()
|
|
|
|
# Verify the error message
|
|
assert "Error fetching data from API" in str(excinfo.value)
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_async_get_data_connection_error(api_client, mock_session):
|
|
"""Test handling of connection errors."""
|
|
# Setup the mock to raise a connection error
|
|
mock_session.get.side_effect = aiohttp.ClientConnectionError("Connection refused")
|
|
|
|
# Call the method and expect an exception
|
|
with pytest.raises(PVMicroinverterApiClientError) as excinfo:
|
|
await api_client.async_get_data()
|
|
|
|
# Verify the error message
|
|
assert "Error fetching data from API" in str(excinfo.value)
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_async_get_data_invalid_json(api_client, mock_session, mock_response):
|
|
"""Test handling of invalid JSON responses."""
|
|
# Setup the mock to return invalid JSON
|
|
mock_response.json.side_effect = json.JSONDecodeError("Invalid JSON", "", 0)
|
|
mock_session.get.return_value = mock_response
|
|
|
|
# Call the method and expect an exception
|
|
with pytest.raises(PVMicroinverterApiClientError) as excinfo:
|
|
await api_client.async_get_data()
|
|
|
|
# Verify the error message
|
|
assert "Unexpected error occurred" in str(excinfo.value)
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_async_get_data_missing_fields(api_client, mock_session, mock_response):
|
|
"""Test handling of responses with missing fields."""
|
|
# Setup the mock to return a response with missing fields
|
|
mock_response.json.return_value = {"some_other_field": "value"}
|
|
mock_session.get.return_value = mock_response
|
|
|
|
# Call the method - it should handle missing fields gracefully
|
|
data = await api_client.async_get_data()
|
|
|
|
# Verify default values are used
|
|
assert data.current_power == 0
|
|
assert data.today_energy == 0
|
|
assert data.lifetime_energy == 0
|
|
assert data.last_updated is not None # Should default to current time
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_async_check_connection_success(api_client, mock_session):
|
|
"""Test successful connection check."""
|
|
# Setup the mock response
|
|
mock_response = MagicMock()
|
|
mock_response.raise_for_status = MagicMock()
|
|
mock_session.get.return_value = mock_response
|
|
|
|
# Call the method
|
|
result = await api_client.async_check_connection()
|
|
|
|
# Verify the API call
|
|
mock_session.post.assert_called_once_with(
|
|
f"{api_client._base_url}/GetStationInfo",
|
|
headers={
|
|
"Authorization": "Bearer test_api_key",
|
|
"Content-Type": "application/json",
|
|
},
|
|
)
|
|
|
|
# Verify the result
|
|
assert result is True
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_async_check_connection_failure(api_client, mock_session):
|
|
"""Test failed connection check."""
|
|
# Setup the mock to raise an error
|
|
mock_session.get.side_effect = aiohttp.ClientError("Connection error")
|
|
|
|
# Call the method
|
|
result = await api_client.async_check_connection()
|
|
|
|
# Verify the result
|
|
assert result is False
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_process_data_with_various_types(api_client):
|
|
"""Test data processing with various data types."""
|
|
# Test with string values that should be converted to float
|
|
data_with_strings = {
|
|
"current_power": "450.75",
|
|
"today_energy": "2.5",
|
|
"lifetime_energy": "1000",
|
|
"last_updated": "2023-04-01T14:30:00Z",
|
|
}
|
|
result = api_client._process_data(data_with_strings)
|
|
assert result.current_power == 450.75
|
|
assert result.today_energy == 2.5
|
|
assert result.lifetime_energy == 1000.0
|
|
assert result.last_updated == "2023-04-01T14:30:00Z"
|
|
|
|
# Test with mixed types
|
|
data_mixed = {
|
|
"current_power": 300,
|
|
"today_energy": 1.5,
|
|
"lifetime_energy": "750.5",
|
|
"last_updated": "2023-04-01T14:30:00Z",
|
|
}
|
|
result = api_client._process_data(data_mixed)
|
|
assert result.current_power == 300.0
|
|
assert result.today_energy == 1.5
|
|
assert result.lifetime_energy == 750.5
|
|
assert result.last_updated == "2023-04-01T14:30:00Z"
|