Vereinfacht client: email normalization, multi-match warning, Bypass tests, doc note
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/promote/production Build is passing

- Normalize email (trim + downcase) before filter lookup
- Log warning when API returns multiple contacts for same email
- Add Bypass tests for find_contact_by_email (query params, empty/single response parsing)
- Document vereinfacht_required_field? as legacy/unused in vereinfacht-api.md
- Add bypass dependency (dev+test) for HTTP stubbing
This commit is contained in:
Moritz 2026-03-04 20:55:29 +01:00
parent 9f169b9835
commit 01b9ebd74b
Signed by: moritz
GPG key ID: 1020A035E5DD0824
5 changed files with 100 additions and 7 deletions

View file

@ -2,8 +2,8 @@ defmodule Mv.Vereinfacht.ClientTest do
@moduledoc """
Tests for Mv.Vereinfacht.Client.
Only tests the "not configured" path; no real HTTP calls. Config reads from
ENV first, then from Settings (DB), so we use DataCase so get_settings() is available.
"Not configured" path: no HTTP. When configured we use Bypass to stub the API
and assert on request (query params) and response parsing.
"""
use Mv.DataCase, async: false
@ -35,6 +35,77 @@ defmodule Mv.Vereinfacht.ClientTest do
assert Client.find_contact_by_email("kayley.becker@example.com") ==
{:error, :not_configured}
end
@tag :bypass
test "sends filter[isExternal]=true and filter[email]=<encoded> and returns :not_found when data is empty" do
bypass = Bypass.open()
base = "http://127.0.0.1:#{bypass.port}"
set_vereinfacht_env(base)
Bypass.expect_once(bypass, "GET", "/finance-contacts", fn conn ->
qs = conn.query_string || ""
assert qs =~ "filter[isExternal]=true",
"expected query to contain filter[isExternal]=true, got: #{inspect(qs)}"
assert qs =~ "filter[email]=",
"expected query to contain filter[email]=..., got: #{inspect(qs)}"
# Email should be encoded (e.g. @ as %40)
assert qs =~ "filter[email]=test%40example.com",
"expected filter[email] to be URL-encoded (downcased), got: #{inspect(qs)}"
body = Jason.encode!(%{"jsonapi" => %{"version" => "1.0"}, "data" => []})
conn
|> Plug.Conn.put_resp_content_type("application/vnd.api+json")
|> Plug.Conn.send_resp(200, body)
end)
assert Client.find_contact_by_email(" Test@Example.com ") == {:error, :not_found}
end
@tag :bypass
test "returns {:ok, id} when API returns one contact (string id)" do
bypass = Bypass.open()
base = "http://127.0.0.1:#{bypass.port}"
set_vereinfacht_env(base)
Bypass.expect_once(bypass, "GET", "/finance-contacts", fn conn ->
body =
Jason.encode!(%{
"jsonapi" => %{"version" => "1.0"},
"data" => [%{"type" => "finance-contacts", "id" => "123", "attributes" => %{}}]
})
conn
|> Plug.Conn.put_resp_content_type("application/vnd.api+json")
|> Plug.Conn.send_resp(200, body)
end)
assert Client.find_contact_by_email("user@example.com") == {:ok, "123"}
end
@tag :bypass
test "returns {:ok, id} when API returns one contact (integer id)" do
bypass = Bypass.open()
base = "http://127.0.0.1:#{bypass.port}"
set_vereinfacht_env(base)
Bypass.expect_once(bypass, "GET", "/finance-contacts", fn conn ->
body =
Jason.encode!(%{
"jsonapi" => %{"version" => "1.0"},
"data" => [%{"type" => "finance-contacts", "id" => 456, "attributes" => %{}}]
})
conn
|> Plug.Conn.put_resp_content_type("application/vnd.api+json")
|> Plug.Conn.send_resp(200, body)
end)
assert Client.find_contact_by_email("other@example.com") == {:ok, "456"}
end
end
defp build_member_struct do
@ -49,6 +120,12 @@ defmodule Mv.Vereinfacht.ClientTest do
}
end
defp set_vereinfacht_env(base_url) do
System.put_env("VEREINFACHT_API_URL", base_url)
System.put_env("VEREINFACHT_API_KEY", "test-key")
System.put_env("VEREINFACHT_CLUB_ID", "2")
end
defp clear_vereinfacht_env do
System.delete_env("VEREINFACHT_API_URL")
System.delete_env("VEREINFACHT_API_KEY")