-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}}.