Packages
A well-typed, idiomatic Gleam client for Anthropic's Claude API with streaming support and tool use
Current section
Files
Jump to
Current section
Files
src/anthropic@testing.erl
-module(anthropic@testing).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/anthropic/testing.gleam").
-export([mock_text_response_body/2, mock_text_response/1, mock_tool_use_response_body/4, mock_tool_use_response/3, mock_error_body/2, mock_error_response/3, mock_auth_error/0, mock_rate_limit_error/0, mock_overloaded_error/0, mock_invalid_request_error/1, fixture_simple_response/0, fixture_conversation_response/0, fixture_tool_use_response/0, fixture_max_tokens_response/0, fixture_stop_sequence_response/0, has_api_key/0, mock_response/1, mock_response_with/5]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
?MODULEDOC(
" Testing utilities for Anthropic API client\n"
"\n"
" This module provides mock responses, test fixtures, and helpers\n"
" for testing code that uses the anthropic_gleam library.\n"
"\n"
" ## Two Categories of Test Helpers\n"
"\n"
" This module provides two distinct categories of testing utilities:\n"
"\n"
" ### 1. HTTP Response Mocks (`mock_*` functions)\n"
"\n"
" These functions return `Response(String)` - raw HTTP responses with JSON bodies.\n"
" Use these when testing code that handles HTTP responses directly, such as\n"
" custom HTTP clients or sans-io patterns.\n"
"\n"
" ```gleam\n"
" // Returns Response(String) - an HTTP response\n"
" let http_response = mock_text_response(\"Hello!\")\n"
"\n"
" // To get a CreateMessageResponse, parse it:\n"
" let assert Ok(parsed) = http.parse_messages_response(http_response)\n"
" ```\n"
"\n"
" ### 2. Fixture Responses (`fixture_*` functions)\n"
"\n"
" These functions return `CreateMessageResponse` - pre-parsed domain objects.\n"
" Use these when testing business logic that works with parsed responses.\n"
"\n"
" ```gleam\n"
" // Returns CreateMessageResponse directly\n"
" let response = fixture_simple_response()\n"
" let text = request.response_text(response)\n"
" ```\n"
"\n"
" ## Quick Reference\n"
"\n"
" | Function | Returns | Use Case |\n"
" |----------|---------|----------|\n"
" | `mock_response(text)` | `CreateMessageResponse` | Simple testing with custom text |\n"
" | `mock_text_response(text)` | `Response(String)` | HTTP layer testing |\n"
" | `mock_tool_use_response(...)` | `Response(String)` | HTTP layer testing |\n"
" | `mock_error_response(...)` | `Response(String)` | HTTP error handling |\n"
" | `fixture_simple_response()` | `CreateMessageResponse` | Business logic testing |\n"
" | `fixture_tool_use_response()` | `CreateMessageResponse` | Tool use testing |\n"
"\n"
" ## Example: Testing Business Logic\n"
"\n"
" ```gleam\n"
" import anthropic/testing.{mock_response, fixture_tool_use_response}\n"
" import anthropic/request\n"
"\n"
" pub fn test_response_handling() {\n"
" // Use mock_response for simple custom text\n"
" let response = mock_response(\"The answer is 42\")\n"
" let text = request.response_text(response)\n"
" assert text == \"The answer is 42\"\n"
"\n"
" // Use fixtures for specific scenarios\n"
" let tool_response = fixture_tool_use_response()\n"
" assert request.needs_tool_execution(tool_response) == True\n"
" }\n"
" ```\n"
"\n"
" ## Example: Testing HTTP Handling\n"
"\n"
" ```gleam\n"
" import anthropic/testing.{mock_text_response, mock_rate_limit_error}\n"
" import anthropic/http\n"
"\n"
" pub fn test_http_parsing() {\n"
" // Test successful response parsing\n"
" let http_response = mock_text_response(\"Hello!\")\n"
" let assert Ok(parsed) = http.parse_messages_response(http_response)\n"
"\n"
" // Test error handling\n"
" let error_response = mock_rate_limit_error()\n"
" let assert Error(err) = http.parse_messages_response(error_response)\n"
" }\n"
" ```\n"
).
-file("src/anthropic/testing.gleam", 269).
?DOC(
" Build a mock text response body JSON\n"
"\n"
" The generated JSON includes all fields that the Anthropic API returns,\n"
" including `stop_sequence` (set to null for text responses).\n"
).
-spec mock_text_response_body(binary(), binary()) -> binary().
mock_text_response_body(Id, Text) ->
gleam@json:to_string(
gleam@json:object(
[{<<"id"/utf8>>, gleam@json:string(Id)},
{<<"type"/utf8>>, gleam@json:string(<<"message"/utf8>>)},
{<<"role"/utf8>>, gleam@json:string(<<"assistant"/utf8>>)},
{<<"content"/utf8>>,
gleam@json:array(
[gleam@json:object(
[{<<"type"/utf8>>,
gleam@json:string(<<"text"/utf8>>)},
{<<"text"/utf8>>, gleam@json:string(Text)}]
)],
fun(X) -> X end
)},
{<<"model"/utf8>>,
gleam@json:string(<<"claude-sonnet-4-20250514"/utf8>>)},
{<<"stop_reason"/utf8>>, gleam@json:string(<<"end_turn"/utf8>>)},
{<<"stop_sequence"/utf8>>, gleam@json:null()},
{<<"usage"/utf8>>,
gleam@json:object(
[{<<"input_tokens"/utf8>>, gleam@json:int(10)},
{<<"output_tokens"/utf8>>, gleam@json:int(20)}]
)}]
)
).
-file("src/anthropic/testing.gleam", 185).
?DOC(
" Create a mock HTTP response with text content\n"
"\n"
" Returns a `Response(String)` representing an HTTP response from the API.\n"
" Use this when testing HTTP handling code. To get a `CreateMessageResponse`,\n"
" parse it with `http.parse_messages_response()`.\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" let http_response = mock_text_response(\"Hello!\")\n"
" // http_response.status == 200\n"
" // http_response.body contains JSON\n"
"\n"
" // To parse into CreateMessageResponse:\n"
" let assert Ok(parsed) = http.parse_messages_response(http_response)\n"
" ```\n"
"\n"
" For a simpler API that returns `CreateMessageResponse` directly,\n"
" use `mock_response()` instead.\n"
).
-spec mock_text_response(binary()) -> gleam@http@response:response(binary()).
mock_text_response(Text) ->
_pipe = gleam@http@response:new(200),
gleam@http@response:set_body(
_pipe,
mock_text_response_body(<<"msg_mock_123"/utf8>>, Text)
).
-file("src/anthropic/testing.gleam", 305).
?DOC(
" Build a mock tool use response body JSON\n"
"\n"
" The generated JSON includes all fields that the Anthropic API returns,\n"
" including `stop_sequence` (set to null for tool use responses).\n"
).
-spec mock_tool_use_response_body(binary(), binary(), binary(), binary()) -> binary().
mock_tool_use_response_body(Id, Tool_id, Tool_name, _) ->
gleam@json:to_string(
gleam@json:object(
[{<<"id"/utf8>>, gleam@json:string(Id)},
{<<"type"/utf8>>, gleam@json:string(<<"message"/utf8>>)},
{<<"role"/utf8>>, gleam@json:string(<<"assistant"/utf8>>)},
{<<"content"/utf8>>,
gleam@json:array(
[gleam@json:object(
[{<<"type"/utf8>>,
gleam@json:string(<<"tool_use"/utf8>>)},
{<<"id"/utf8>>, gleam@json:string(Tool_id)},
{<<"name"/utf8>>,
gleam@json:string(Tool_name)},
{<<"input"/utf8>>, gleam@json:object([])}]
)],
fun(X) -> X end
)},
{<<"model"/utf8>>,
gleam@json:string(<<"claude-sonnet-4-20250514"/utf8>>)},
{<<"stop_reason"/utf8>>, gleam@json:string(<<"tool_use"/utf8>>)},
{<<"stop_sequence"/utf8>>, gleam@json:null()},
{<<"usage"/utf8>>,
gleam@json:object(
[{<<"input_tokens"/utf8>>, gleam@json:int(15)},
{<<"output_tokens"/utf8>>, gleam@json:int(25)}]
)}]
)
).
-file("src/anthropic/testing.gleam", 207).
?DOC(
" Create a mock HTTP response with tool use content\n"
"\n"
" Returns a `Response(String)` representing an HTTP response with tool use.\n"
" Use this when testing HTTP handling code for tool use scenarios.\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" let http_response = mock_tool_use_response(\n"
" \"toolu_123\",\n"
" \"get_weather\",\n"
" \"{\\\"location\\\": \\\"Paris\\\"}\",\n"
" )\n"
" let assert Ok(parsed) = http.parse_messages_response(http_response)\n"
" ```\n"
"\n"
" For pre-parsed tool use responses, use `fixture_tool_use_response()`.\n"
).
-spec mock_tool_use_response(binary(), binary(), binary()) -> gleam@http@response:response(binary()).
mock_tool_use_response(Tool_id, Tool_name, Tool_input) ->
_pipe = gleam@http@response:new(200),
gleam@http@response:set_body(
_pipe,
mock_tool_use_response_body(
<<"msg_mock_456"/utf8>>,
Tool_id,
Tool_name,
Tool_input
)
).
-file("src/anthropic/testing.gleam", 347).
?DOC(" Build a mock error body JSON\n").
-spec mock_error_body(binary(), binary()) -> binary().
mock_error_body(Error_type, Message) ->
gleam@json:to_string(
gleam@json:object(
[{<<"type"/utf8>>, gleam@json:string(<<"error"/utf8>>)},
{<<"error"/utf8>>,
gleam@json:object(
[{<<"type"/utf8>>, gleam@json:string(Error_type)},
{<<"message"/utf8>>, gleam@json:string(Message)}]
)}]
)
).
-file("src/anthropic/testing.gleam", 232).
?DOC(
" Create a mock HTTP error response\n"
"\n"
" Returns a `Response(String)` representing an HTTP error response.\n"
" Use this when testing error handling code.\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" let http_response = mock_error_response(400, \"invalid_request_error\", \"Bad request\")\n"
" let assert Error(err) = http.parse_messages_response(http_response)\n"
" ```\n"
).
-spec mock_error_response(integer(), binary(), binary()) -> gleam@http@response:response(binary()).
mock_error_response(Status_code, Error_type, Error_message) ->
_pipe = gleam@http@response:new(Status_code),
gleam@http@response:set_body(
_pipe,
mock_error_body(Error_type, Error_message)
).
-file("src/anthropic/testing.gleam", 242).
?DOC(" Create a mock HTTP authentication error response (401)\n").
-spec mock_auth_error() -> gleam@http@response:response(binary()).
mock_auth_error() ->
mock_error_response(
401,
<<"authentication_error"/utf8>>,
<<"Invalid API key"/utf8>>
).
-file("src/anthropic/testing.gleam", 247).
?DOC(" Create a mock HTTP rate limit error response (429)\n").
-spec mock_rate_limit_error() -> gleam@http@response:response(binary()).
mock_rate_limit_error() ->
mock_error_response(
429,
<<"rate_limit_error"/utf8>>,
<<"Rate limit exceeded"/utf8>>
).
-file("src/anthropic/testing.gleam", 252).
?DOC(" Create a mock HTTP overloaded error response (529)\n").
-spec mock_overloaded_error() -> gleam@http@response:response(binary()).
mock_overloaded_error() ->
mock_error_response(
529,
<<"overloaded_error"/utf8>>,
<<"API is temporarily overloaded"/utf8>>
).
-file("src/anthropic/testing.gleam", 257).
?DOC(" Create a mock HTTP invalid request error response (400)\n").
-spec mock_invalid_request_error(binary()) -> gleam@http@response:response(binary()).
mock_invalid_request_error(Error_message) ->
mock_error_response(400, <<"invalid_request_error"/utf8>>, Error_message).
-file("src/anthropic/testing.gleam", 385).
?DOC(
" A simple text response fixture\n"
"\n"
" Returns a `CreateMessageResponse` with a simple greeting text.\n"
" Use this for basic response handling tests.\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" let response = fixture_simple_response()\n"
" let text = request.response_text(response)\n"
" // text == \"Hello! How can I help you today?\"\n"
" ```\n"
"\n"
" For custom text, use `mock_response(text)` instead.\n"
).
-spec fixture_simple_response() -> anthropic@request:create_message_response().
fixture_simple_response() ->
{create_message_response,
<<"msg_fixture_001"/utf8>>,
<<"message"/utf8>>,
assistant,
[{text_block, <<"Hello! How can I help you today?"/utf8>>}],
<<"claude-sonnet-4-20250514"/utf8>>,
{some, end_turn},
none,
{usage, 12, 8}}.
-file("src/anthropic/testing.gleam", 401).
?DOC(
" A multi-turn conversation response fixture\n"
"\n"
" Returns a `CreateMessageResponse` simulating a response in an ongoing conversation.\n"
).
-spec fixture_conversation_response() -> anthropic@request:create_message_response().
fixture_conversation_response() ->
{create_message_response,
<<"msg_fixture_002"/utf8>>,
<<"message"/utf8>>,
assistant,
[{text_block,
<<"Based on our previous conversation, I understand you're asking about Gleam programming."/utf8>>}],
<<"claude-sonnet-4-20250514"/utf8>>,
{some, end_turn},
none,
{usage, 150, 45}}.
-file("src/anthropic/testing.gleam", 430).
?DOC(
" A tool use response fixture\n"
"\n"
" Returns a `CreateMessageResponse` with tool use content.\n"
" Use this when testing tool execution workflows.\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" let response = fixture_tool_use_response()\n"
" assert request.needs_tool_execution(response) == True\n"
" let calls = request.get_pending_tool_calls(response)\n"
" ```\n"
).
-spec fixture_tool_use_response() -> anthropic@request:create_message_response().
fixture_tool_use_response() ->
{create_message_response,
<<"msg_fixture_003"/utf8>>,
<<"message"/utf8>>,
assistant,
[{text_block, <<"Let me check the weather for you."/utf8>>},
{tool_use_block,
<<"toolu_fixture_001"/utf8>>,
<<"get_weather"/utf8>>,
<<"{\"location\":\"San Francisco\",\"unit\":\"celsius\"}"/utf8>>}],
<<"claude-sonnet-4-20250514"/utf8>>,
{some, tool_use},
none,
{usage, 25, 35}}.
-file("src/anthropic/testing.gleam", 454).
?DOC(
" A max tokens response fixture\n"
"\n"
" Returns a `CreateMessageResponse` that was truncated due to max_tokens limit.\n"
" Use this when testing handling of truncated responses.\n"
).
-spec fixture_max_tokens_response() -> anthropic@request:create_message_response().
fixture_max_tokens_response() ->
{create_message_response,
<<"msg_fixture_004"/utf8>>,
<<"message"/utf8>>,
assistant,
[{text_block,
<<"This response was truncated because it reached the maximum token limit..."/utf8>>}],
<<"claude-sonnet-4-20250514"/utf8>>,
{some, max_tokens},
none,
{usage, 20, 100}}.
-file("src/anthropic/testing.gleam", 475).
?DOC(
" A stop sequence response fixture\n"
"\n"
" Returns a `CreateMessageResponse` that stopped due to a custom stop sequence.\n"
" Use this when testing stop sequence handling.\n"
).
-spec fixture_stop_sequence_response() -> anthropic@request:create_message_response().
fixture_stop_sequence_response() ->
{create_message_response,
<<"msg_fixture_005"/utf8>>,
<<"message"/utf8>>,
assistant,
[{text_block, <<"The answer is 42"/utf8>>}],
<<"claude-sonnet-4-20250514"/utf8>>,
{some, stop_sequence},
{some, <<"END"/utf8>>},
{usage, 15, 5}}.
-file("src/anthropic/testing.gleam", 507).
-spec get_env(binary()) -> {ok, binary()} | {error, nil}.
get_env(Name) ->
Value = begin
_pipe = os:getenv(
unicode:characters_to_list(Name),
unicode:characters_to_list(<<""/utf8>>)
),
unicode:characters_to_binary(_pipe)
end,
case Value of
<<""/utf8>> ->
{error, nil};
V ->
{ok, V}
end.
-file("src/anthropic/testing.gleam", 493).
?DOC(" Check if an API key is available for integration tests\n").
-spec has_api_key() -> boolean().
has_api_key() ->
case get_env(<<"ANTHROPIC_API_KEY"/utf8>>) of
{ok, Key} ->
Key /= <<""/utf8>>;
{error, _} ->
false
end.
-file("src/anthropic/testing.gleam", 522).
?DOC(" Generate a unique ID for mock responses\n").
-spec unique_id() -> integer().
unique_id() ->
anthropic_testing_ffi:unique_integer().
-file("src/anthropic/testing.gleam", 109).
?DOC(
" Create a mock CreateMessageResponse with custom text\n"
"\n"
" This is the simplest way to create a test response. Returns a parsed\n"
" `CreateMessageResponse` directly, ready to use with functions like\n"
" `request.response_text()`.\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" let response = mock_response(\"Hello from Claude!\")\n"
" let text = request.response_text(response)\n"
" assert text == \"Hello from Claude!\"\n"
" ```\n"
"\n"
" For more control over the response, use `mock_response_with()` or\n"
" the `fixture_*` functions.\n"
).
-spec mock_response(binary()) -> anthropic@request:create_message_response().
mock_response(Text) ->
{create_message_response,
<<"msg_mock_"/utf8, (erlang:integer_to_binary(unique_id()))/binary>>,
<<"message"/utf8>>,
assistant,
[{text_block, Text}],
<<"claude-sonnet-4-20250514"/utf8>>,
{some, end_turn},
none,
{usage, 10, 20}}.
-file("src/anthropic/testing.gleam", 137).
?DOC(
" Create a mock CreateMessageResponse with custom options\n"
"\n"
" Allows customizing the model, stop reason, and token usage.\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" let response = mock_response_with(\n"
" \"Generated text\",\n"
" model: \"claude-opus-4-20250514\",\n"
" stop_reason: Some(request.MaxTokens),\n"
" input_tokens: 100,\n"
" output_tokens: 500,\n"
" )\n"
" ```\n"
).
-spec mock_response_with(
binary(),
binary(),
gleam@option:option(anthropic@request:stop_reason()),
integer(),
integer()
) -> anthropic@request:create_message_response().
mock_response_with(Text, Model, Stop_reason, Input_tokens, Output_tokens) ->
{create_message_response,
<<"msg_mock_"/utf8, (erlang:integer_to_binary(unique_id()))/binary>>,
<<"message"/utf8>>,
assistant,
[{text_block, Text}],
Model,
Stop_reason,
none,
{usage, Input_tokens, Output_tokens}}.