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

1"""Tests for Anthropic AI provider.""" 

2 

3from __future__ import annotations 

4 

5from unittest.mock import MagicMock, patch 

6 

7import pytest 

8from assertpy import assert_that 

9 

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 

16 

17 

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() 

22 

23 

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() 

28 

29 assert_that(provider.model_name).is_equal_to( 

30 "claude-sonnet-4-6", 

31 ) 

32 assert_that(provider.name).is_equal_to("anthropic") 

33 

34 

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" 

40 

41 with patch.dict("os.environ", {}, clear=True): 

42 assert_that(provider.is_available()).is_false() 

43 

44 

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" 

50 

51 with patch.dict( 

52 "os.environ", 

53 {"TEST_API_KEY": "sk-test"}, 

54 ): 

55 assert_that(provider.is_available()).is_true() 

56 

57 

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" 

63 

64 with ( 

65 patch.dict("os.environ", {}, clear=True), 

66 pytest.raises(AIAuthenticationError), 

67 ): 

68 provider._get_client() 

69 

70 

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" 

76 

77 mock_block = MagicMock() 

78 mock_block.text = "Hello, world!" 

79 

80 mock_usage = MagicMock() 

81 mock_usage.input_tokens = 100 

82 mock_usage.output_tokens = 50 

83 

84 mock_response = MagicMock() 

85 mock_response.content = [mock_block] 

86 mock_response.usage = mock_usage 

87 

88 mock_client = MagicMock() 

89 mock_client.messages.create.return_value = mock_response 

90 provider._client = mock_client 

91 

92 with patch.dict("os.environ", {"TEST_KEY": "sk-test"}): 

93 result = provider.complete("test prompt", system="be helpful") 

94 

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) 

100 

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 ) 

106 

107 

108def test_anthropic_complete_multiple_text_blocks(): 

109 """complete() concatenates multiple text blocks.""" 

110 with patch.object(mod, "_has_anthropic", True): 

111 provider = AnthropicProvider() 

112 

113 block1 = MagicMock() 

114 block1.text = "Hello, " 

115 block2 = MagicMock() 

116 block2.text = "world!" 

117 

118 mock_usage = MagicMock() 

119 mock_usage.input_tokens = 10 

120 mock_usage.output_tokens = 5 

121 

122 mock_response = MagicMock() 

123 mock_response.content = [block1, block2] 

124 mock_response.usage = mock_usage 

125 

126 mock_client = MagicMock() 

127 mock_client.messages.create.return_value = mock_response 

128 provider._client = mock_client 

129 

130 with patch.dict("os.environ", {"ANTHROPIC_API_KEY": "sk-test"}): 

131 result = provider.complete("prompt") 

132 

133 assert_that(result.content).is_equal_to("Hello, world!") 

134 

135 

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) 

140 

141 mock_usage = MagicMock() 

142 mock_usage.input_tokens = 10 

143 mock_usage.output_tokens = 5 

144 

145 mock_response = MagicMock() 

146 mock_response.content = [MagicMock(text="ok")] 

147 mock_response.usage = mock_usage 

148 

149 mock_client = MagicMock() 

150 mock_client.messages.create.return_value = mock_response 

151 provider._client = mock_client 

152 

153 with patch.dict("os.environ", {"ANTHROPIC_API_KEY": "sk-test"}): 

154 provider.complete("prompt", max_tokens=4096) 

155 

156 call_kwargs = mock_client.messages.create.call_args[1] 

157 assert_that(call_kwargs["max_tokens"]).is_equal_to(2048)