# `ExMCP.Testing.Assertions`
[🔗](https://github.com/azmaveth/ex_mcp/blob/v1.0.0/lib/ex_mcp/testing/assertions.ex#L1)

Custom assertions for MCP protocol testing.

This module provides MCP-specific assertions that go beyond basic ExUnit
assertions, offering detailed validation for protocol compliance, content
structure, and common MCP patterns.

## Features

- **Protocol Assertions**: Validate MCP message structure and compliance
- **Content Assertions**: Type-safe content validation and inspection
- **Tool Assertions**: Validate tool definitions and results
- **Resource Assertions**: Validate resource definitions and responses
- **Error Assertions**: Structured error validation
- **Performance Assertions**: Response time and throughput validation

## Usage

    use ExMCP.TestCase

    test "tool call returns valid content" do
      result = call_tool(client, "sample_tool", %{input: "test"})

      assert_success(result)
      assert_valid_tool_result(result)
      assert_content_type(result, :text)
      assert_content_contains(result, "expected output")
    end

# `assertion_opts`

```elixir
@type assertion_opts() :: [
  strict: boolean(),
  timeout: pos_integer(),
  retry_count: pos_integer(),
  message: String.t()
]
```

Assertion options for customizing behavior

# `mcp_message_type`

```elixir
@type mcp_message_type() ::
  :request
  | :response
  | :notification
  | :error
  | :initialize
  | :list_tools
  | :call_tool
  | :list_resources
  | :read_resource
  | :list_prompts
  | :get_prompt
```

MCP message types for assertion validation

# `assert_all_valid`

```elixir
@spec assert_all_valid([any()], (any() -&gt; any())) :: [any()]
```

Asserts that all items in a list pass a validation function.

## Examples

    assert_all_valid(tools, &assert_valid_tool/1)
    assert_all_valid(content_list, fn content ->
      assert_content_type(content, :text)
      assert_content_contains(content, "required")
    end)

# `assert_content_contains`

```elixir
@spec assert_content_contains(ExMCP.Content.Protocol.content() | map(), String.t()) ::
  any()
```

Asserts that text content contains a specific string.

## Examples

    assert_content_contains(text_content, "hello")
    assert_content_contains(tool_result, "expected output")

# `assert_content_matches`

```elixir
@spec assert_content_matches(ExMCP.Content.Protocol.content() | map(), Regex.t()) ::
  any()
```

Asserts that content matches a regular expression.

## Examples

    assert_content_matches(content, ~r/hello \w+/)
    assert_content_matches(tool_result, ~r/\d{4}-\d{2}-\d{2}/)

# `assert_content_type`

```elixir
@spec assert_content_type(ExMCP.Content.Protocol.content() | map() | [map()], atom()) ::
  any()
```

Asserts that content is of a specific type.

## Examples

    assert_content_type(content, :text)
    assert_content_type(result, :image)
    assert_content_type(result["content"], :text)  # For tool results

# `assert_error`

```elixir
@spec assert_error(any(), (any() -&gt; any()) | nil, String.t() | nil) :: any()
```

Asserts that a response indicates an error.

## Examples

    assert_error({:error, :timeout})
    assert_error(%{"error" => %{"code" => -1}})

    # With specific error checking
    assert_error(result, fn error ->
      assert error.code == -32601
      assert error.message =~ "Method not found"
    end)

# `assert_eventually`

```elixir
@spec assert_eventually((-&gt; boolean()), assertion_opts()) :: :ok
```

Asserts that a value becomes true within a timeout period.

## Examples

    assert_eventually fn ->
      Process.alive?(pid)
    end, timeout: 1000

    assert_eventually fn ->
      GenServer.call(server, :is_ready)
    end, timeout: 5000, interval: 100

# `assert_has_resource`

```elixir
@spec assert_has_resource([map()], String.t()) :: map()
```

Asserts that a list of resources contains a specific resource.

## Examples

    resources = [%{"uri" => "file://a.txt"}, %{"uri" => "file://b.txt"}]
    assert_has_resource(resources, "file://a.txt")

# `assert_has_tool`

```elixir
@spec assert_has_tool([map()], String.t()) :: map()
```

Asserts that a list of tools contains a specific tool.

## Examples

    tools = [%{"name" => "tool1"}, %{"name" => "tool2"}]
    assert_has_tool(tools, "tool1")

# `assert_mcp_message`

```elixir
@spec assert_mcp_message(map(), mcp_message_type(), keyword()) :: map()
```

Asserts that a value matches MCP message structure.

## Examples

    assert_mcp_message(message, :request)
    assert_mcp_message(message, :response, id: 123)
    assert_mcp_message(message, :notification, method: "notifications/message")

# `assert_performance`

```elixir
@spec assert_performance((-&gt; any()), assertion_opts()) :: any()
```

Asserts that an operation completes within a time limit.

## Examples

    assert_performance fn ->
      slow_operation()
    end, max_time: 1000

    assert_performance fn ->
      call_tool(client, "tool", %{})
    end, max_time: 500, message: "Tool call should be fast"

# `assert_sequence`
*macro* 

Asserts that events occur in a specific order.

## Examples

    assert_sequence([
      fn -> send(self(), :event1) end,
      fn -> send(self(), :event2) end,
      fn -> send(self(), :event3) end
    ]) do
      assert_received :event1
      assert_received :event2
      assert_received :event3
    end

# `assert_success`

```elixir
@spec assert_success(any(), String.t() | nil) :: any()
```

Asserts that a response indicates success.

## Examples

    assert_success({:ok, result})
    assert_success(%{"status" => "success"})

    # With custom message
    assert_success(result, "Tool call should succeed")

# `assert_throughput`

```elixir
@spec assert_throughput((-&gt; any()), assertion_opts()) :: [any()]
```

Asserts that multiple operations maintain average performance.

## Examples

    assert_throughput fn ->
      call_tool(client, "tool", %{})
    end, iterations: 10, max_avg_time: 100

# `assert_valid_content`

```elixir
@spec assert_valid_content(ExMCP.Content.Protocol.content() | map()) ::
  ExMCP.Content.Protocol.content() | map()
```

Asserts that content is valid according to MCP content protocol.

## Examples

    assert_valid_content(Protocol.text("Hello"))
    assert_valid_content(%{"type" => "text", "text" => "Hello"})

# `assert_valid_prompt`

```elixir
@spec assert_valid_prompt(map()) :: map()
```

Asserts that a prompt definition is valid.

## Examples

    prompt = %{
      "name" => "sample_prompt",
      "description" => "A sample prompt",
      "arguments" => [
        %{"name" => "topic", "description" => "The topic", "required" => true}
      ]
    }
    assert_valid_prompt(prompt)

# `assert_valid_resource`

```elixir
@spec assert_valid_resource(map()) :: map()
```

Asserts that a resource definition is valid.

## Examples

    resource = %{
      "uri" => "file://data.txt",
      "name" => "Sample Data",
      "description" => "Sample data file",
      "mimeType" => "text/plain"
    }
    assert_valid_resource(resource)

# `assert_valid_tool`

```elixir
@spec assert_valid_tool(map()) :: map()
```

Asserts that a tool definition is valid.

## Examples

    tool = %{
      "name" => "sample_tool",
      "description" => "A sample tool",
      "inputSchema" => %{"type" => "object"}
    }
    assert_valid_tool(tool)

# `assert_valid_tool_result`

```elixir
@spec assert_valid_tool_result(map()) :: map()
```

Asserts that a tool result contains valid content.

## Examples

    result = %{"content" => [%{"type" => "text", "text" => "Hello"}]}
    assert_valid_tool_result(result)

---

*Consult [api-reference.md](api-reference.md) for complete listing*
