Coverage for tests / unit / ai / providers / test_anthropic.py: 100%
93 statements
« prev ^ index » next coverage.py v7.13.0, created at 2026-04-03 18:53 +0000
« prev ^ index » next coverage.py v7.13.0, created at 2026-04-03 18:53 +0000
1"""Tests for Anthropic AI provider."""
3from __future__ import annotations
5from unittest.mock import MagicMock, patch
7import pytest
8from assertpy import assert_that
10from lintro.ai.exceptions import (
11 AIAuthenticationError,
12 AINotAvailableError,
13)
14from lintro.ai.providers import anthropic as mod
15from lintro.ai.providers.anthropic import AnthropicProvider
18def test_anthropic_provider_raises_when_sdk_missing():
19 """AnthropicProvider raises AINotAvailableError if SDK missing."""
20 with patch.object(mod, "_has_anthropic", False), pytest.raises(AINotAvailableError):
21 AnthropicProvider()
24def test_anthropic_provider_default_model():
25 """AnthropicProvider uses expected default model and name."""
26 with patch.object(mod, "_has_anthropic", True):
27 provider = AnthropicProvider()
29 assert_that(provider.model_name).is_equal_to(
30 "claude-sonnet-4-6",
31 )
32 assert_that(provider.name).is_equal_to("anthropic")
35def test_anthropic_provider_is_available_with_no_key():
36 """Verify that is_available returns False when no API key is set."""
37 with patch.object(mod, "_has_anthropic", True):
38 provider = AnthropicProvider()
39 provider._api_key_env = "NONEXISTENT_KEY_VAR"
41 with patch.dict("os.environ", {}, clear=True):
42 assert_that(provider.is_available()).is_false()
45def test_anthropic_provider_is_available_with_key():
46 """Verify that is_available returns True when a valid API key is present."""
47 with patch.object(mod, "_has_anthropic", True):
48 provider = AnthropicProvider()
49 provider._api_key_env = "TEST_API_KEY"
51 with patch.dict(
52 "os.environ",
53 {"TEST_API_KEY": "sk-test"},
54 ):
55 assert_that(provider.is_available()).is_true()
58def test_anthropic_provider_get_client_no_key_raises():
59 """_get_client raises AIAuthenticationError when key missing."""
60 with patch.object(mod, "_has_anthropic", True):
61 provider = AnthropicProvider()
62 provider._api_key_env = "NONEXISTENT_KEY"
64 with (
65 patch.dict("os.environ", {}, clear=True),
66 pytest.raises(AIAuthenticationError),
67 ):
68 provider._get_client()
71def test_anthropic_complete_parses_response():
72 """complete() extracts content, tokens, and cost from SDK response."""
73 with patch.object(mod, "_has_anthropic", True):
74 provider = AnthropicProvider()
75 provider._api_key_env = "TEST_KEY"
77 mock_block = MagicMock()
78 mock_block.text = "Hello, world!"
80 mock_usage = MagicMock()
81 mock_usage.input_tokens = 100
82 mock_usage.output_tokens = 50
84 mock_response = MagicMock()
85 mock_response.content = [mock_block]
86 mock_response.usage = mock_usage
88 mock_client = MagicMock()
89 mock_client.messages.create.return_value = mock_response
90 provider._client = mock_client
92 with patch.dict("os.environ", {"TEST_KEY": "sk-test"}):
93 result = provider.complete("test prompt", system="be helpful")
95 assert_that(result.content).is_equal_to("Hello, world!")
96 assert_that(result.input_tokens).is_equal_to(100)
97 assert_that(result.output_tokens).is_equal_to(50)
98 assert_that(result.provider).is_equal_to("anthropic")
99 assert_that(result.cost_estimate).is_greater_than_or_equal_to(0.0)
101 call_kwargs = mock_client.messages.create.call_args[1]
102 assert_that(call_kwargs["system"]).is_equal_to("be helpful")
103 assert_that(call_kwargs["messages"]).is_equal_to(
104 [{"role": "user", "content": "test prompt"}],
105 )
108def test_anthropic_complete_multiple_text_blocks():
109 """complete() concatenates multiple text blocks."""
110 with patch.object(mod, "_has_anthropic", True):
111 provider = AnthropicProvider()
113 block1 = MagicMock()
114 block1.text = "Hello, "
115 block2 = MagicMock()
116 block2.text = "world!"
118 mock_usage = MagicMock()
119 mock_usage.input_tokens = 10
120 mock_usage.output_tokens = 5
122 mock_response = MagicMock()
123 mock_response.content = [block1, block2]
124 mock_response.usage = mock_usage
126 mock_client = MagicMock()
127 mock_client.messages.create.return_value = mock_response
128 provider._client = mock_client
130 with patch.dict("os.environ", {"ANTHROPIC_API_KEY": "sk-test"}):
131 result = provider.complete("prompt")
133 assert_that(result.content).is_equal_to("Hello, world!")
136def test_anthropic_complete_respects_max_tokens_cap():
137 """complete() uses the lower of per-call and provider-level max_tokens."""
138 with patch.object(mod, "_has_anthropic", True):
139 provider = AnthropicProvider(max_tokens=2048)
141 mock_usage = MagicMock()
142 mock_usage.input_tokens = 10
143 mock_usage.output_tokens = 5
145 mock_response = MagicMock()
146 mock_response.content = [MagicMock(text="ok")]
147 mock_response.usage = mock_usage
149 mock_client = MagicMock()
150 mock_client.messages.create.return_value = mock_response
151 provider._client = mock_client
153 with patch.dict("os.environ", {"ANTHROPIC_API_KEY": "sk-test"}):
154 provider.complete("prompt", max_tokens=4096)
156 call_kwargs = mock_client.messages.create.call_args[1]
157 assert_that(call_kwargs["max_tokens"]).is_equal_to(2048)