Update Anthropic tests (#182940)

This commit is contained in:
Denis Shulyaka
2026-09-23 11:38:44 +01:00
committed by GitHub
parent c8095a55c9
commit d09f77dfdf
8 changed files with 614 additions and 527 deletions
@@ -201,7 +201,7 @@
}),
])
# ---
# name: test_disabled_thinking[subentry_data0]
# name: test_disabled_thinking[zero_budget]
list([
dict({
'content': '''
@@ -230,7 +230,7 @@
}),
])
# ---
# name: test_disabled_thinking[subentry_data0].1
# name: test_disabled_thinking[zero_budget].1
dict({
'container': None,
'max_tokens': 3000,
@@ -265,7 +265,7 @@
}),
})
# ---
# name: test_disabled_thinking[subentry_data1]
# name: test_disabled_thinking[no_effort]
list([
dict({
'content': '''
@@ -294,7 +294,7 @@
}),
])
# ---
# name: test_disabled_thinking[subentry_data1].1
# name: test_disabled_thinking[no_effort].1
dict({
'container': None,
'max_tokens': 3000,
@@ -513,7 +513,7 @@
}),
])
# ---
# name: test_history_conversion[content0]
# name: test_history_conversion[system_only]
list([
dict({
'content': 'Are you sure?',
@@ -525,7 +525,7 @@
}),
])
# ---
# name: test_history_conversion[content1]
# name: test_history_conversion[single_exchange]
list([
dict({
'content': 'What shape is a donut?',
@@ -545,7 +545,7 @@
}),
])
# ---
# name: test_history_conversion[content2]
# name: test_history_conversion[two_consecutive_messages]
list([
dict({
'content': list([
@@ -583,7 +583,7 @@
}),
])
# ---
# name: test_history_conversion[content3]
# name: test_history_conversion[three_consecutive_messages]
list([
dict({
'content': list([
@@ -629,7 +629,7 @@
}),
])
# ---
# name: test_history_conversion[content4]
# name: test_history_conversion[tool_results]
list([
dict({
'content': 'Turn off the lights and make me coffee',
@@ -694,7 +694,7 @@
}),
])
# ---
# name: test_history_conversion[content5]
# name: test_history_conversion[web_search_citations]
list([
dict({
'content': "What's on the news today?",
@@ -806,7 +806,7 @@
}),
])
# ---
# name: test_history_conversion[content6]
# name: test_history_conversion[web_fetch_citations]
list([
dict({
'content': "What's new in Home Assistant?",
@@ -899,7 +899,7 @@
}),
])
# ---
# name: test_history_conversion[content7]
# name: test_history_conversion[time_in_tool_result]
list([
dict({
'content': 'What time is it?',
@@ -946,7 +946,7 @@
}),
])
# ---
# name: test_history_conversion[content8]
# name: test_history_conversion[tool_search_results]
list([
dict({
'content': 'Set humidity to 50%',
@@ -1088,7 +1088,7 @@
}),
])
# ---
# name: test_text_editor_code_execution[args_parts0-content0]
# name: test_text_editor_code_execution[create_file]
list([
dict({
'attachments': None,
@@ -1147,7 +1147,7 @@
}),
])
# ---
# name: test_text_editor_code_execution[args_parts0-content0].1
# name: test_text_editor_code_execution[create_file].1
list([
dict({
'content': 'Do the needful',
@@ -1186,7 +1186,7 @@
}),
])
# ---
# name: test_text_editor_code_execution[args_parts1-content1]
# name: test_text_editor_code_execution[replace_text]
list([
dict({
'attachments': None,
@@ -1255,7 +1255,7 @@
}),
])
# ---
# name: test_text_editor_code_execution[args_parts1-content1].1
# name: test_text_editor_code_execution[replace_text].1
list([
dict({
'content': 'Do the needful',
@@ -1304,7 +1304,7 @@
}),
])
# ---
# name: test_text_editor_code_execution[args_parts2-content2]
# name: test_text_editor_code_execution[view_file]
list([
dict({
'attachments': None,
@@ -1366,7 +1366,7 @@
}),
])
# ---
# name: test_text_editor_code_execution[args_parts2-content2].1
# name: test_text_editor_code_execution[view_file].1
list([
dict({
'content': 'Do the needful',
@@ -1408,7 +1408,7 @@
}),
])
# ---
# name: test_text_editor_code_execution[args_parts3-content3]
# name: test_text_editor_code_execution[tool_error]
list([
dict({
'attachments': None,
@@ -1467,7 +1467,7 @@
}),
])
# ---
# name: test_text_editor_code_execution[args_parts3-content3].1
# name: test_text_editor_code_execution[tool_error].1
list([
dict({
'content': 'Do the needful',
+13 -23
View File
@@ -24,10 +24,10 @@ from . import create_content_block, create_thinking_block, create_tool_use_block
from tests.common import MockConfigEntry
@pytest.mark.usefixtures("mock_init_component")
async def test_generate_data(
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
mock_init_component,
mock_create_stream: AsyncMock,
entity_registry: er.EntityRegistry,
) -> None:
@@ -59,10 +59,8 @@ async def test_generate_data(
assert result.data == "The test data"
@pytest.mark.usefixtures("mock_init_component")
async def test_translation_key(
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
mock_init_component,
entity_registry: er.EntityRegistry,
) -> None:
"""Test entity translation key."""
@@ -71,10 +69,9 @@ async def test_translation_key(
assert entry.translation_key == "ai_task_data"
@pytest.mark.usefixtures("mock_init_component")
async def test_empty_data(
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
mock_init_component,
mock_create_stream: AsyncMock,
) -> None:
"""Test AI Task data generation but the data returned is empty."""
@@ -91,10 +88,9 @@ async def test_empty_data(
)
@pytest.mark.usefixtures("mock_init_component")
async def test_stream_wrong_type(
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
mock_init_component,
mock_create_stream: AsyncMock,
) -> None:
"""Test error if the response is not a stream."""
@@ -117,10 +113,10 @@ async def test_stream_wrong_type(
@freeze_time("2026-01-01 12:00:00")
@pytest.mark.usefixtures("mock_init_component")
async def test_generate_structured_data_legacy(
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
mock_init_component,
mock_create_stream: AsyncMock,
snapshot: SnapshotAssertion,
) -> None:
@@ -168,10 +164,10 @@ async def test_generate_structured_data_legacy(
@freeze_time("2026-01-01 12:00:00")
@pytest.mark.usefixtures("mock_init_component")
async def test_generate_structured_data_legacy_tools(
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
mock_init_component,
mock_create_stream: AsyncMock,
snapshot: SnapshotAssertion,
) -> None:
@@ -220,10 +216,10 @@ async def test_generate_structured_data_legacy_tools(
@freeze_time("2026-01-01 12:00:00")
@pytest.mark.usefixtures("mock_init_component")
async def test_generate_structured_data_legacy_extended_thinking(
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
mock_init_component,
mock_create_stream: AsyncMock,
snapshot: SnapshotAssertion,
) -> None:
@@ -280,10 +276,10 @@ async def test_generate_structured_data_legacy_extended_thinking(
@freeze_time("2026-01-01 12:00:00")
@pytest.mark.usefixtures("mock_init_component")
async def test_generate_structured_data_legacy_extra_text_block(
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
mock_init_component,
mock_create_stream: AsyncMock,
snapshot: SnapshotAssertion,
) -> None:
@@ -340,10 +336,10 @@ async def test_generate_structured_data_legacy_extra_text_block(
assert mock_create_stream.call_args.kwargs.copy() == snapshot
@pytest.mark.usefixtures("mock_init_component")
async def test_generate_invalid_structured_data_legacy(
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
mock_init_component,
mock_create_stream: AsyncMock,
) -> None:
"""Test AI Task with invalid JSON response with legacy method."""
@@ -389,10 +385,9 @@ async def test_generate_invalid_structured_data_legacy(
@freeze_time("2026-01-01 12:00:00")
@pytest.mark.usefixtures("mock_init_component")
async def test_generate_structured_data(
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
mock_init_component,
mock_create_stream: AsyncMock,
snapshot: SnapshotAssertion,
) -> None:
@@ -423,12 +418,10 @@ async def test_generate_structured_data(
assert mock_create_stream.call_args.kwargs.copy() == snapshot
@pytest.mark.usefixtures("mock_init_component")
async def test_generate_data_with_attachments(
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
mock_init_component,
mock_create_stream: AsyncMock,
entity_registry: er.EntityRegistry,
) -> None:
"""Test AI Task data generation with attachments."""
entity_id = "ai_task.claude_ai_task"
@@ -505,12 +498,10 @@ async def test_generate_data_with_attachments(
assert document_block["source"]["type"] == "base64"
@pytest.mark.usefixtures("mock_init_component")
async def test_generate_data_invalid_attachments(
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
mock_init_component,
mock_create_stream: AsyncMock,
entity_registry: er.EntityRegistry,
) -> None:
"""Test AI Task data generation with attachments of unsupported type."""
entity_id = "ai_task.claude_ai_task"
@@ -581,10 +572,9 @@ async def test_generate_data_invalid_attachments(
)
@pytest.mark.usefixtures("mock_init_component")
async def test_generate_data_with_attachments_whitespace_instructions(
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
mock_init_component,
mock_create_stream: AsyncMock,
) -> None:
"""Test whitespace-only instructions with attachments produce no text block.
+72 -37
View File
@@ -5,6 +5,7 @@ from unittest.mock import AsyncMock, patch
from anthropic import (
APIConnectionError,
APIError,
APIResponseValidationError,
APITimeoutError,
AuthenticationError,
@@ -53,7 +54,10 @@ from homeassistant.data_entry_flow import FlowResultType
from tests.common import MockConfigEntry
async def test_form(hass: HomeAssistant, mock_setup_entry) -> None:
async def test_form(
hass: HomeAssistant,
mock_setup_entry: AsyncMock,
) -> None:
"""Test we get the form."""
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
@@ -94,7 +98,10 @@ async def test_form(hass: HomeAssistant, mock_setup_entry) -> None:
assert len(mock_setup_entry.mock_calls) == 1
async def test_duplicate_entry(hass: HomeAssistant, mock_config_entry) -> None:
async def test_duplicate_entry(
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
) -> None:
"""Test we abort on duplicate config entry."""
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
@@ -117,8 +124,10 @@ async def test_duplicate_entry(hass: HomeAssistant, mock_config_entry) -> None:
assert result["reason"] == "already_configured"
@pytest.mark.usefixtures("mock_init_component")
async def test_creating_conversation_subentry(
hass: HomeAssistant, mock_config_entry, mock_init_component
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
) -> None:
"""Test creating a conversation subentry."""
result = await hass.config_entries.subentries.async_init(
@@ -144,9 +153,9 @@ async def test_creating_conversation_subentry(
assert result2["data"] == processed_options
@pytest.mark.usefixtures("mock_init_component")
async def test_creating_conversation_subentry_not_loaded(
hass: HomeAssistant,
mock_init_component,
mock_config_entry: MockConfigEntry,
) -> None:
"""Test creating a conversation subentry when entry is not loaded."""
@@ -167,9 +176,11 @@ async def test_creating_conversation_subentry_not_loaded(
@pytest.mark.parametrize(
("side_effect", "error"),
[
(APIConnectionError(request=None), "cannot_connect"),
(APITimeoutError(request=None), "timeout_connect"),
(
pytest.param(
APIConnectionError(request=None), "cannot_connect", id="connection_error"
),
pytest.param(APITimeoutError(request=None), "timeout_connect", id="timeout"),
pytest.param(
BadRequestError(
message=(
"Your credit balance is too low to access"
@@ -183,8 +194,9 @@ async def test_creating_conversation_subentry_not_loaded(
body={"type": "error", "error": {"type": "invalid_request_error"}},
),
"unknown",
id="insufficient_credit",
),
(
pytest.param(
AuthenticationError(
message="invalid x-api-key",
response=Response(
@@ -194,8 +206,9 @@ async def test_creating_conversation_subentry_not_loaded(
body={"type": "error", "error": {"type": "authentication_error"}},
),
"authentication_error",
id="authentication_error",
),
(
pytest.param(
InternalServerError(
message=None,
response=Response(
@@ -205,8 +218,9 @@ async def test_creating_conversation_subentry_not_loaded(
body=None,
),
"unknown",
id="server_error",
),
(
pytest.param(
APIResponseValidationError(
response=Response(
status_code=200,
@@ -215,11 +229,16 @@ async def test_creating_conversation_subentry_not_loaded(
body=None,
),
"unknown",
id="invalid_response",
),
],
)
@pytest.mark.usefixtures("mock_setup_entry")
async def test_api_error(hass: HomeAssistant, side_effect, error) -> None:
async def test_api_error(
hass: HomeAssistant,
side_effect: APIError,
error: str,
) -> None:
"""Test that we handle API errors."""
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
@@ -257,8 +276,10 @@ async def test_api_error(hass: HomeAssistant, side_effect, error) -> None:
}
@pytest.mark.usefixtures("mock_init_component")
async def test_subentry_options_thinking_budget_more_than_max(
hass: HomeAssistant, mock_config_entry, mock_init_component
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
) -> None:
"""Test error about thinking budget being more than max tokens."""
subentry = next(iter(mock_config_entry.subentries.values()))
@@ -312,8 +333,10 @@ async def test_subentry_options_thinking_budget_more_than_max(
assert subentry.data["thinking_budget"] == 8192
@pytest.mark.usefixtures("mock_init_component")
async def test_subentry_web_search_user_location(
hass: HomeAssistant, mock_config_entry, mock_init_component
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
) -> None:
"""Test fetching user location."""
subentry = next(iter(mock_config_entry.subentries.values()))
@@ -403,10 +426,10 @@ async def test_subentry_web_search_user_location(
}
@pytest.mark.usefixtures("mock_init_component")
async def test_model_list(
hass: HomeAssistant,
mock_config_entry,
mock_init_component,
mock_config_entry: MockConfigEntry,
snapshot: SnapshotAssertion,
) -> None:
"""Test fetching and processing the list of models."""
@@ -428,8 +451,10 @@ async def test_model_list(
assert options["data_schema"].schema["chat_model"].config["options"] == snapshot
@pytest.mark.usefixtures("mock_init_component")
async def test_invalid_model(
hass: HomeAssistant, mock_config_entry: MockConfigEntry, mock_init_component: None
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
) -> None:
"""Test exceptions during fetching model info."""
options = await hass.config_entries.subentries.async_init(
@@ -534,7 +559,7 @@ async def test_invalid_model(
@pytest.mark.parametrize(
("current_options", "new_options", "expected_options"),
[
( # Test converting single llm api format to list
pytest.param( # Test converting single llm api format to list
{
CONF_RECOMMENDED: True,
CONF_PROMPT: "",
@@ -552,8 +577,9 @@ async def test_invalid_model(
CONF_PROMPT: "",
CONF_LLM_HASS_API: ["assist"],
},
id="recommended_to_recommended",
),
( # Model with web search options
pytest.param( # Model with web search options
{
CONF_RECOMMENDED: False,
CONF_CHAT_MODEL: "claude-sonnet-4-5",
@@ -604,8 +630,9 @@ async def test_invalid_model(
CONF_WEB_SEARCH_USER_LOCATION: False,
CONF_CODE_EXECUTION: False,
},
id="disable_web_tools",
),
( # Model with thinking budget options
pytest.param( # Model with thinking budget options
{
CONF_RECOMMENDED: False,
CONF_CHAT_MODEL: "claude-sonnet-4-5",
@@ -656,8 +683,9 @@ async def test_invalid_model(
CONF_WEB_SEARCH_USER_LOCATION: False,
CONF_CODE_EXECUTION: False,
},
id="update_thinking_budget",
),
( # Model with thinking effort options
pytest.param( # Model with thinking effort options
{
CONF_RECOMMENDED: False,
CONF_CHAT_MODEL: "claude-fable-5",
@@ -709,8 +737,9 @@ async def test_invalid_model(
CONF_WEB_SEARCH_USER_LOCATION: False,
CONF_CODE_EXECUTION: True,
},
id="update_thinking_effort",
),
( # Test switching from recommended to custom options
pytest.param( # Test switching from recommended to custom options
{
CONF_RECOMMENDED: True,
CONF_PROMPT: "bla",
@@ -742,8 +771,9 @@ async def test_invalid_model(
CONF_WEB_FETCH_MAX_USES: 5,
CONF_CODE_EXECUTION: False,
},
id="recommended_to_custom",
),
( # Test switching from custom to recommended options
pytest.param( # Test switching from custom to recommended options
{
CONF_RECOMMENDED: False,
CONF_PROMPT: "Speak like a pirate",
@@ -771,16 +801,17 @@ async def test_invalid_model(
CONF_LLM_HASS_API: ["assist"],
CONF_PROMPT: "",
},
id="custom_to_recommended",
),
],
)
@pytest.mark.usefixtures("mock_init_component")
async def test_subentry_options_switching(
hass: HomeAssistant,
mock_config_entry,
mock_init_component,
current_options,
new_options,
expected_options,
mock_config_entry: MockConfigEntry,
current_options: dict[str, str | int | bool | list[str]],
new_options: tuple[dict[str, str | int | bool | list[str]], ...],
expected_options: dict[str, str | int | bool | list[str]],
) -> None:
"""Test the subentry options form."""
subentry = next(iter(mock_config_entry.subentries.values()))
@@ -822,10 +853,10 @@ async def test_subentry_options_switching(
assert subentry.data == expected_options
@pytest.mark.usefixtures("mock_init_component")
async def test_creating_ai_task_subentry(
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
mock_init_component,
) -> None:
"""Test creating an AI task subentry."""
old_subentries = set(mock_config_entry.subentries)
@@ -880,10 +911,10 @@ async def test_ai_task_subentry_not_loaded(
assert result.get("reason") == "entry_not_loaded"
@pytest.mark.usefixtures("mock_init_component")
async def test_creating_ai_task_subentry_additional(
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
mock_init_component,
) -> None:
"""Test creating an AI task subentry with additional settings."""
result = await hass.config_entries.subentries.async_init(
@@ -945,7 +976,9 @@ async def test_creating_ai_task_subentry_additional(
@pytest.mark.usefixtures("mock_setup_entry")
async def test_reauth(hass: HomeAssistant) -> None:
async def test_reauth(
hass: HomeAssistant,
) -> None:
"""Test we can reauthenticate."""
# Pretend we already set up a config entry.
hass.config.components.add("anthropic")
@@ -980,18 +1013,20 @@ async def test_reauth(hass: HomeAssistant) -> None:
@pytest.mark.parametrize(
("current_llm_apis", "suggested_llm_apis", "expected_options"),
[
("assist", ["assist"], ["assist"]),
(["assist"], ["assist"], ["assist"]),
("non-existent", [], ["assist"]),
(["non-existent"], [], ["assist"]),
(["assist", "non-existent"], ["assist"], ["assist"]),
pytest.param("assist", ["assist"], ["assist"], id="assist_string"),
pytest.param(["assist"], ["assist"], ["assist"], id="assist_list"),
pytest.param("non-existent", [], ["assist"], id="unknown_string"),
pytest.param(["non-existent"], [], ["assist"], id="unknown_list"),
pytest.param(
["assist", "non-existent"], ["assist"], ["assist"], id="mixed_list"
),
],
)
@pytest.mark.usefixtures("mock_init_component")
async def test_reconfigure_conversation_subentry_llm_api_schema(
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
mock_init_component,
current_llm_apis: list[str],
current_llm_apis: str | list[str],
suggested_llm_apis: list[str],
expected_options: list[str],
) -> None:
File diff suppressed because it is too large Load Diff
@@ -5,6 +5,7 @@ from unittest.mock import AsyncMock, patch
from anthropic import APITimeoutError, AuthenticationError, RateLimitError
from freezegun import freeze_time
from httpx import URL, Request, Response
import pytest
from homeassistant.components import conversation
from homeassistant.components.anthropic.const import DOMAIN
@@ -21,11 +22,11 @@ from tests.common import MockConfigEntry, async_fire_time_changed
@patch("anthropic.resources.models.AsyncModels.list", new_callable=AsyncMock)
@pytest.mark.usefixtures("mock_init_component")
async def test_auth_error_handling(
mock_model_list: AsyncMock,
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
mock_init_component,
mock_create_stream: AsyncMock,
) -> None:
"""Test reauth after authentication error during conversation."""
@@ -64,11 +65,10 @@ async def test_auth_error_handling(
@freeze_time("2026-02-27 12:00:00")
@patch("anthropic.resources.models.AsyncModels.list", new_callable=AsyncMock)
@pytest.mark.usefixtures("mock_init_component")
async def test_connection_error_handling(
mock_model_list: AsyncMock,
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
mock_init_component,
mock_create_stream: AsyncMock,
) -> None:
"""Test making entity unavailable on connection error."""
@@ -133,11 +133,11 @@ async def test_connection_error_handling(
@patch("anthropic.resources.models.AsyncModels.list", new_callable=AsyncMock)
@pytest.mark.usefixtures("mock_init_component")
async def test_connection_check_reauth(
mock_model_list: AsyncMock,
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
mock_init_component,
) -> None:
"""Test authentication error during background availability check."""
mock_model_list.side_effect = APITimeoutError(
@@ -197,11 +197,10 @@ async def test_connection_check_reauth(
@patch("anthropic.resources.models.AsyncModels.list", new_callable=AsyncMock)
@pytest.mark.usefixtures("mock_init_component")
async def test_connection_restore(
mock_model_list: AsyncMock,
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
mock_init_component,
mock_create_stream: AsyncMock,
) -> None:
"""Test background availability check restore on non-connectivity error."""
@@ -1,5 +1,6 @@
"""Test Anthropic diagnostics."""
import pytest
from syrupy.assertion import SnapshotAssertion
from homeassistant.core import HomeAssistant
@@ -9,11 +10,11 @@ from tests.components.diagnostics import get_diagnostics_for_config_entry
from tests.typing import ClientSessionGenerator
@pytest.mark.usefixtures("mock_init_component")
async def test_entry_diagnostics(
hass: HomeAssistant,
hass_client: ClientSessionGenerator,
mock_config_entry: MockConfigEntry,
mock_init_component: None,
snapshot: SnapshotAssertion,
) -> None:
"""Test config entry diagnostics."""
+45 -21
View File
@@ -1,10 +1,11 @@
"""Tests for the Anthropic integration."""
from typing import Any
from typing import TypedDict
from unittest.mock import patch
from anthropic import (
APIConnectionError,
APIError,
APITimeoutError,
AuthenticationError,
BadRequestError,
@@ -37,12 +38,23 @@ from tests.common import MockConfigEntry
MINOR_VERSION = AnthropicConfigFlow.MINOR_VERSION
class ConversationSubentryExpectation(TypedDict):
"""Expected registry state for a migrated conversation subentry."""
conversation_entity_id: str
device_disabled_by: DeviceEntryDisabler | None
entity_disabled_by: RegistryEntryDisabler | None
device: int
@pytest.mark.parametrize(
("side_effect", "error"),
[
(APIConnectionError(request=None), "Connection error"),
(APITimeoutError(request=None), "Request timed out"),
(
pytest.param(
APIConnectionError(request=None), "Connection error", id="connection_error"
),
pytest.param(APITimeoutError(request=None), "Request timed out", id="timeout"),
pytest.param(
BadRequestError(
message=(
"Your credit balance is too low to access"
@@ -56,15 +68,16 @@ MINOR_VERSION = AnthropicConfigFlow.MINOR_VERSION
body={"type": "error", "error": {"type": "invalid_request_error"}},
),
"Your credit balance is too low to access the Claude API",
id="insufficient_credit",
),
],
)
@pytest.mark.usefixtures("mock_config_entry")
async def test_init_error(
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
caplog: pytest.LogCaptureFixture,
side_effect,
error,
side_effect: APIError,
error: str,
) -> None:
"""Test initialization errors."""
with patch(
@@ -96,10 +109,10 @@ async def test_init_auth_error(
assert mock_config_entry.state is ConfigEntryState.SETUP_ERROR
@pytest.mark.usefixtures("mock_init_component")
async def test_init_repair_issue(
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
mock_init_component,
issue_registry: ir.IssueRegistry,
) -> None:
"""Test that repair issue is created on deprecated model."""
@@ -261,7 +274,7 @@ async def test_migration_from_v1_to_v2(
"main_config_entry",
),
[
(
pytest.param(
[ConfigEntryDisabler.USER, None],
[DeviceEntryDisabler.CONFIG_ENTRY, None],
[RegistryEntryDisabler.CONFIG_ENTRY, None],
@@ -281,8 +294,9 @@ async def test_migration_from_v1_to_v2(
},
],
1,
id="first_entry_disabled",
),
(
pytest.param(
[None, ConfigEntryDisabler.USER],
[None, DeviceEntryDisabler.CONFIG_ENTRY],
[None, RegistryEntryDisabler.CONFIG_ENTRY],
@@ -302,8 +316,9 @@ async def test_migration_from_v1_to_v2(
},
],
0,
id="second_entry_disabled",
),
(
pytest.param(
[ConfigEntryDisabler.USER, ConfigEntryDisabler.USER],
[DeviceEntryDisabler.CONFIG_ENTRY, DeviceEntryDisabler.CONFIG_ENTRY],
[RegistryEntryDisabler.CONFIG_ENTRY, RegistryEntryDisabler.CONFIG_ENTRY],
@@ -323,6 +338,7 @@ async def test_migration_from_v1_to_v2(
},
],
0,
id="both_entries_disabled",
),
],
)
@@ -335,7 +351,7 @@ async def test_migration_from_v1_disabled(
device_disabled_by: list[DeviceEntryDisabler | None],
entity_disabled_by: list[RegistryEntryDisabler | None],
merged_config_entry_disabled_by: ConfigEntryDisabler | None,
conversation_subentry_data: list[dict[str, Any]],
conversation_subentry_data: list[ConversationSubentryExpectation],
main_config_entry: int,
) -> None:
"""Test migration where the config entries are disabled."""
@@ -807,7 +823,7 @@ async def test_migration_from_v2_1_to_v2_2(
),
[
# Config entry not disabled, update device and entity disabled by config entry
(
pytest.param(
None,
DeviceEntryDisabler.CONFIG_ENTRY,
RegistryEntryDisabler.CONFIG_ENTRY,
@@ -816,8 +832,9 @@ async def test_migration_from_v2_1_to_v2_2(
None,
DeviceEntryDisabler.USER,
RegistryEntryDisabler.DEVICE,
id="enabled_entry_stale_flags",
),
(
pytest.param(
None,
DeviceEntryDisabler.USER,
RegistryEntryDisabler.DEVICE,
@@ -826,8 +843,9 @@ async def test_migration_from_v2_1_to_v2_2(
None,
DeviceEntryDisabler.USER,
RegistryEntryDisabler.DEVICE,
id="enabled_entry_device_disabled",
),
(
pytest.param(
None,
DeviceEntryDisabler.USER,
RegistryEntryDisabler.USER,
@@ -836,8 +854,9 @@ async def test_migration_from_v2_1_to_v2_2(
None,
DeviceEntryDisabler.USER,
RegistryEntryDisabler.USER,
id="enabled_entry_entity_disabled",
),
(
pytest.param(
None,
None,
None,
@@ -846,9 +865,10 @@ async def test_migration_from_v2_1_to_v2_2(
None,
None,
None,
id="enabled_entry_no_disabled_flags",
),
# Config entry disabled, migration does not run
(
pytest.param(
ConfigEntryDisabler.USER,
DeviceEntryDisabler.CONFIG_ENTRY,
RegistryEntryDisabler.CONFIG_ENTRY,
@@ -857,8 +877,9 @@ async def test_migration_from_v2_1_to_v2_2(
ConfigEntryDisabler.USER,
DeviceEntryDisabler.CONFIG_ENTRY,
RegistryEntryDisabler.CONFIG_ENTRY,
id="disabled_entry_config_entry_flags",
),
(
pytest.param(
ConfigEntryDisabler.USER,
DeviceEntryDisabler.USER,
RegistryEntryDisabler.DEVICE,
@@ -867,8 +888,9 @@ async def test_migration_from_v2_1_to_v2_2(
ConfigEntryDisabler.USER,
DeviceEntryDisabler.USER,
RegistryEntryDisabler.DEVICE,
id="disabled_entry_device_disabled",
),
(
pytest.param(
ConfigEntryDisabler.USER,
DeviceEntryDisabler.USER,
RegistryEntryDisabler.USER,
@@ -877,8 +899,9 @@ async def test_migration_from_v2_1_to_v2_2(
ConfigEntryDisabler.USER,
DeviceEntryDisabler.USER,
RegistryEntryDisabler.USER,
id="disabled_entry_entity_disabled",
),
(
pytest.param(
ConfigEntryDisabler.USER,
None,
None,
@@ -887,6 +910,7 @@ async def test_migration_from_v2_1_to_v2_2(
ConfigEntryDisabler.USER,
None,
None,
id="disabled_entry_no_disabled_flags",
),
],
)
@@ -901,7 +925,7 @@ async def test_migrate_entry_to_v2_3(
setup_result: bool,
minor_version_after_migration: int,
config_entry_disabled_by_after_migration: ConfigEntryDisabler | None,
device_disabled_by_after_migration: ConfigEntryDisabler | None,
device_disabled_by_after_migration: DeviceEntryDisabler | None,
entity_disabled_by_after_migration: RegistryEntryDisabler | None,
) -> None:
"""Test migration to version 2.3."""
+6 -3
View File
@@ -1,13 +1,16 @@
"""Tests for the Anthropic repairs flow."""
from types import SimpleNamespace
from typing import Any
from unittest.mock import AsyncMock, MagicMock
from anthropic.pagination import AsyncPage
from homeassistant.components.anthropic.const import CONF_CHAT_MODEL, DOMAIN
from homeassistant.config_entries import ConfigEntryState, ConfigSubentry
from homeassistant.config_entries import (
ConfigEntryState,
ConfigSubentry,
ConfigSubentryData,
)
from homeassistant.core import HomeAssistant
from homeassistant.data_entry_flow import FlowResultType
from homeassistant.helpers import issue_registry as ir
@@ -25,7 +28,7 @@ def _make_entry(
*,
title: str,
api_key: str,
subentries_data: list[dict[str, Any]],
subentries_data: list[ConfigSubentryData],
) -> MockConfigEntry:
"""Create a config entry with subentries and runtime data."""
entry = MockConfigEntry(