Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,10 @@ jobs:
run: mix deps.get

- name: Check for outdated dependencies
run: mix hex.outdated --all
run: mix hex.outdated
Comment thread
thanos marked this conversation as resolved.

- name: Audit dependencies for known vulnerabilities
run: mix hex.audit

- name: Run full verification
run: mix verify
run: mix verify
30 changes: 30 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,36 @@

All notable changes to this project will be documented in this file.

## [0.3.2] - 2026-04-19

### Added
- Comprehensive unit tests for Client module: 71 tests covering GenServer callbacks, provider parsing, auth parsing, file validation, search, OpenAPI conversion, and monitoring endpoints
- Comprehensive unit tests for GraphQL transport: 23 new tests covering GenServer callbacks (init, handle_call, handle_info, terminate), state transitions, and type specifications
- Comprehensive unit tests for HTTP transport: SSE streaming helpers, URL parameter substitution, discovery response parsing, tool response parsing, header building, and schema parsing
- Unit tests for GraphQL Connection module: 34 tests covering struct definition, public API, GenServer callbacks, state transitions, and terminate handling
- Unit tests for TCP/UDP Pool: 37 tests covering connection management, cleanup, and lifecycle
- Unit tests for MCP Pool: 29 tests covering connection tracking, cleanup, and provider management
- Extended MCP Connection tests: 25 additional tests covering GenServer callbacks and retry logic
- CI workflows with pinned GitHub Action commit SHAs for supply-chain security
- Behaviour module for WebRTC connection (ExUtcp.Transports.WebRTC.ConnectionBehaviour)
- Testable module for WebRTC with Mox support

### Changed
- Test coverage increased from 52.0% to 55.5% across the codebase
- GraphQL Connection: fixed bug where `handle_call(:get_last_used)` and `handle_call(:update_last_used)` referenced `last_used_at` instead of the struct field `last_used`
- WebRTC provider type spec: removed `url` and `auth` keys that were never present in actual provider maps created by `Providers.new_webrtc_provider/1`
- GitHub Actions: pinned `actions/checkout` to v4.2.2, `erlef/setup-beam` to v1.18.0, and `actions/cache` to v4.2.3 using full commit SHAs
- Test suite now excludes integration tests by default via `ExUnit.start(exclude: [:integration])`
- Removed all emoji from documentation files, replaced with plain text equivalents

### Fixed
- GraphQL Connection: `handle_call(:get_last_used)` and `handle_call(:update_last_used)` used wrong field name (`last_used_at` vs `last_used`)
- WebRTC provider type spec: `url` and `auth` keys marked as required but never created in actual provider maps
- Documentation inconsistencies in TEST_COVERAGE_REPORT.md: corrected test counts for sse_mock_test (19->22) and testable_validation_test (22->30)
- Documentation inconsistency in ZERO_WARNINGS_ACHIEVED.md: Sobelow findings listed as "0" but should be "6 (all mitigated)"
- Documentation inconsistency in WARNINGS_FIXED.md: remaining warnings count said "3" but actual count is "2"
- Documentation error in COMPARISON_STUDY.md: Python comment syntax (`//`) used in Python example block instead of `#`

## [0.3.1] - 2025-10-05

### Added
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ Key characteristics:
* Test configuration with integration test exclusion by default
* Advanced Search: Multiple algorithms with fuzzy matching and semantic search
* Monitoring and Metrics: Telemetry, PromEx, health checks, and performance monitoring
* Comprehensive test suite with 497+ tests
* Comprehensive test suite with 1473+ tests

## Installation

Expand All @@ -43,7 +43,7 @@ Add `ex_utcp` to your list of dependencies in `mix.exs`:
```elixir
def deps do
[
{:ex_utcp, "~> 0.2.0"}
{:ex_utcp, "~> 0.3.2"}
]
end
```
Expand Down
31 changes: 31 additions & 0 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,35 @@

## Release Notes - ExUtcp v0.3.2

### Overview
ExUtcp v0.3.2 is a quality and testing release focused on improving test coverage, fixing bugs, hardening CI security, and correcting documentation. Test coverage increased from 52.0% to 55.5%, and multiple bugs were identified and fixed during testing.

### Changes from v0.3.1

#### Added
- 71 unit tests for the Client module covering GenServer callbacks, provider/auth parsing, file validation, search, OpenAPI conversion, and monitoring.
- 23 new tests for the GraphQL transport covering GenServer callbacks and state management.
- Extended HTTP transport tests with SSE streaming, discovery parsing, header building, and schema parsing.
- 34 tests for GraphQL Connection module: struct definition, public API, GenServer callbacks, and terminate handling.
- 37 tests for TCP/UDP Pool and 29 tests for MCP Pool covering connection management and cleanup.
- 25 additional MCP Connection tests covering callbacks and retry logic.
- WebRTC ConnectionBehaviour module and Testable module with Mox support.
- CI workflows with pinned GitHub Action commit SHAs for supply-chain security.

#### Changed
- Test coverage increased from 52.0% to 55.5%.
- Integration tests now excluded by default (`ExUnit.start(exclude: [:integration])`).
- All emoji removed from documentation, replaced with plain text equivalents.
- 1473 total tests (0 failures, 133 excluded, 81 skipped).

#### Fixed
- GraphQL Connection: `handle_call(:get_last_used)` and `handle_call(:update_last_used)` referenced non-existent field `last_used_at` instead of `last_used`.
- WebRTC provider type spec: removed `url` and `auth` keys that were required by the type but never created in actual provider maps.
- GitHub Actions: pinned all actions to full commit SHAs (`actions/checkout@11bd7190`, `erlef/setup-beam@a6e26b22`, `actions/cache@5a3ec84e`).
Comment thread
thanos marked this conversation as resolved.
- Documentation: corrected test counts, Sobelow findings count, warnings count, and Python comment syntax.

---

## Release Notes - ExUtcp v0.3.1

### Overview
Expand Down
3 changes: 2 additions & 1 deletion mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ defmodule ExUtcp.MixProject do
def project do
[
app: :ex_utcp,
version: "0.3.1",
version: "0.3.2",
elixir: "~> 1.15",
start_permanent: Mix.env() == :prod,
elixirc_paths: elixirc_paths(Mix.env()),
Expand Down Expand Up @@ -137,6 +137,7 @@ defmodule ExUtcp.MixProject do
defp verify(_) do
steps = [
# ["precommit", :dev],
{"hex.outdated", :dev},
{"compile --warnings-as-errors", :dev},
{"format --check-formatted", :dev},
{"credo --strict", :dev},
Expand Down
32 changes: 24 additions & 8 deletions test/ex_utcp/config_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ defmodule ExUtcp.ConfigTest do
test "returns variable from loader module" do
# Use the module atom directly - Config calls loader.get(key)
loader = __MODULE__.TestLoader
config = Config.new(load_variables_from: [loader])
Config.new(load_variables_from: [loader])
# This won't work because TestLoader.get/2 needs a struct
# Skip this test for now - loaders need proper implementation
end
Expand All @@ -100,14 +100,18 @@ defmodule ExUtcp.ConfigTest do

test "returns error when variable not found" do
config = Config.new(variables: %{})
assert {:error, %{variable_name: "NONEXISTENT"}} = Config.get_variable(config, "NONEXISTENT")

assert {:error, %{variable_name: "NONEXISTENT"}} =
Config.get_variable(config, "NONEXISTENT")
end
end

describe "substitute_variables/2" do
test "substitutes ${VAR} pattern" do
config = Config.new(variables: %{"HOST" => "example.com"})
assert Config.substitute_variables(config, "https://${HOST}/api") == "https://example.com/api"

assert Config.substitute_variables(config, "https://${HOST}/api") ==
"https://example.com/api"
end

test "substitutes $VAR pattern" do
Expand All @@ -117,12 +121,16 @@ defmodule ExUtcp.ConfigTest do

test "substitutes multiple variables" do
config = Config.new(variables: %{"HOST" => "example.com", "PORT" => "8080"})
assert Config.substitute_variables(config, "https://${HOST}:${PORT}/api") == "https://example.com:8080/api"

assert Config.substitute_variables(config, "https://${HOST}:${PORT}/api") ==
"https://example.com:8080/api"
end

test "leaves unsubstituted variables intact" do
config = Config.new(variables: %{})
assert Config.substitute_variables(config, "https://${UNKNOWN}/api") == "https://${UNKNOWN}/api"

assert Config.substitute_variables(config, "https://${UNKNOWN}/api") ==
"https://${UNKNOWN}/api"
end

test "substitutes in list values" do
Expand All @@ -132,7 +140,10 @@ defmodule ExUtcp.ConfigTest do

test "substitutes in map values" do
config = Config.new(variables: %{"KEY" => "substituted"})
assert Config.substitute_variables(config, %{"field" => "${KEY}"}) == %{"field" => "substituted"}

assert Config.substitute_variables(config, %{"field" => "${KEY}"}) == %{
"field" => "substituted"
}
end

test "returns non-string, non-map, non-list values unchanged" do
Expand All @@ -144,7 +155,9 @@ defmodule ExUtcp.ConfigTest do

test "handles mixed content in string" do
config = Config.new(variables: %{"VAR" => "replaced"})
assert Config.substitute_variables(config, "prefix_${VAR}_suffix") == "prefix_replaced_suffix"

assert Config.substitute_variables(config, "prefix_${VAR}_suffix") ==
"prefix_replaced_suffix"
end

test "handles $VAR pattern - matches word chars only" do
Expand All @@ -155,7 +168,10 @@ defmodule ExUtcp.ConfigTest do

test "nested map substitution" do
config = Config.new(variables: %{"HOST" => "example.com"})
result = Config.substitute_variables(config, %{"url" => "https://${HOST}", "static" => "value"})

result =
Config.substitute_variables(config, %{"url" => "https://${HOST}", "static" => "value"})

assert result == %{"url" => "https://example.com", "static" => "value"}
end
end
Expand Down
10 changes: 7 additions & 3 deletions test/ex_utcp/transports/graphql/connection_unit_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ defmodule ExUtcp.Transports.Graphql.ConnectionUnitTest do

test "sets default max_retries when not provided" do
provider = %{name: "test", url: "http://example.com/graphql"}
opts = []
# opts = []

# Can't test init directly without HTTP, but struct creation works
state = %Connection{
Expand Down Expand Up @@ -190,7 +190,9 @@ defmodule ExUtcp.Transports.Graphql.ConnectionUnitTest do

from = {self(), :test_ref}

result = Connection.handle_call({:subscription, "subscription { test }", %{}, []}, from, state)
result =
Connection.handle_call({:subscription, "subscription { test }", %{}, []}, from, state)

assert match?({:reply, {:error, _}, ^state}, result)
end

Expand All @@ -209,7 +211,9 @@ defmodule ExUtcp.Transports.Graphql.ConnectionUnitTest do

from = {self(), :test_ref}

result = Connection.handle_call({:subscription, "subscription { test }", %{}, []}, from, state)
result =
Connection.handle_call({:subscription, "subscription { test }", %{}, []}, from, state)

assert match?({:reply, {:error, _}, _}, result)
end
end
Expand Down
24 changes: 18 additions & 6 deletions test/ex_utcp/transports/graphql_unit_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -440,7 +440,7 @@ defmodule ExUtcp.Transports.GraphqlUnitTest do
@tag :skip
test "initializes with default opts" do
result = Graphql.init([])
assert match?({:ok, state}, result)
assert match?({:ok, _state}, result)
{:ok, state} = result
assert %Graphql{} = state
assert state.connection_timeout == 30_000
Expand All @@ -454,7 +454,7 @@ defmodule ExUtcp.Transports.GraphqlUnitTest do
@tag :skip
test "initializes with custom opts" do
result = Graphql.init(connection_timeout: 60_000, max_retries: 5, retry_delay: 2000)
assert match?({:ok, state}, result)
assert match?({:ok, _state}, result)
{:ok, state} = result
assert state.connection_timeout == 60_000
assert state.max_retries == 5
Expand Down Expand Up @@ -528,7 +528,9 @@ defmodule ExUtcp.Transports.GraphqlUnitTest do
from = {self(), :test_ref}
provider = %{type: :graphql, name: "test", url: "http://invalid:9999/graphql"}

result = Graphql.handle_call({:mutation, provider, "mutation { test }", %{}, []}, from, state)
result =
Graphql.handle_call({:mutation, provider, "mutation { test }", %{}, []}, from, state)

assert match?({:reply, {:error, _}, ^state}, result)
end
end
Expand All @@ -540,7 +542,13 @@ defmodule ExUtcp.Transports.GraphqlUnitTest do
from = {self(), :test_ref}
provider = %{type: :graphql, name: "test", url: "http://invalid:9999/graphql"}

result = Graphql.handle_call({:subscription, provider, "subscription { test }", %{}, []}, from, state)
result =
Graphql.handle_call(
{:subscription, provider, "subscription { test }", %{}, []},
from,
state
)

assert match?({:reply, {:error, _}, ^state}, result)
end
end
Expand Down Expand Up @@ -593,15 +601,19 @@ defmodule ExUtcp.Transports.GraphqlUnitTest do

describe "build_graphql_subscription/2" do
test "creates subscription with simple tool name" do
{type, subscription_string, variables} = build_graphql_subscription("updates", %{"filter" => "active"})
{type, subscription_string, variables} =
build_graphql_subscription("updates", %{"filter" => "active"})

assert type == :subscription
assert subscription_string =~ "subscription updates"
assert subscription_string =~ "updates(input:"
assert variables == %{"input" => %{"filter" => "active"}}
end

test "replaces dots in subscription name" do
{type, subscription_string, _variables} = build_graphql_subscription("stream.v2.updates", %{})
{type, subscription_string, _variables} =
build_graphql_subscription("stream.v2.updates", %{})

assert type == :subscription
assert subscription_string =~ "subscription stream_v2_updates"
end
Expand Down
Loading
Loading