From 079f2a5f0fc1e292c419b8d1df564469c5cd6bd8 Mon Sep 17 00:00:00 2001 From: Claudio Ortolina Date: Fri, 17 Apr 2026 13:57:36 +0100 Subject: [PATCH] Add BraveSearch facade tests --- test/brave_search_test.exs | 124 ++++++++++++++++++ .../support/fixtures/brave_search/fixtures.ex | 11 ++ .../brave_search/search_images_response.json | 43 ++++++ 3 files changed, 178 insertions(+) create mode 100644 test/brave_search_test.exs create mode 100644 test/support/fixtures/brave_search/fixtures.ex create mode 100644 test/support/fixtures/brave_search/search_images_response.json diff --git a/test/brave_search_test.exs b/test/brave_search_test.exs new file mode 100644 index 00000000..e8924788 --- /dev/null +++ b/test/brave_search_test.exs @@ -0,0 +1,124 @@ +defmodule BraveSearchTest do + use ExUnit.Case, async: true + + describe "search_images/2" do + test "normalizes results, preserving nil for missing nested fields" do + Req.Test.stub(BraveSearch.API, fn conn -> + Req.Test.json(conn, BraveSearch.Fixtures.search_images_response()) + end) + + assert {:ok, results} = BraveSearch.search_images("steven wilson") + + assert results == [ + %{ + thumbnail_url: "https://thumbnails.example.com/raven-thumb.jpg", + image_url: "https://images.example.com/raven-cover.jpg", + width: 1200, + height: 1200, + title: "The Raven That Refused To Sing - Cover", + source: "https://example.com/page/raven" + }, + %{ + thumbnail_url: nil, + image_url: "https://images.example.com/hce-cover.jpg", + width: 800, + height: 800, + title: "Hand. Cannot. Erase. - Alternate Cover", + source: "https://example.com/page/hce" + }, + %{ + thumbnail_url: "https://thumbnails.example.com/ttb-thumb.jpg", + image_url: nil, + width: nil, + height: nil, + title: "To The Bone - Promo Shot", + source: "https://example.com/page/ttb" + }, + %{ + thumbnail_url: "https://thumbnails.example.com/unknown-thumb.jpg", + image_url: "https://images.example.com/unknown.jpg", + width: 640, + height: 480, + title: "", + source: "" + } + ] + end + + test "forwards the query and explicit count as request params" do + Req.Test.stub(BraveSearch.API, fn conn -> + conn = Plug.Conn.fetch_query_params(conn) + assert conn.request_path == "/res/v1/images/search" + assert conn.query_params["q"] == "porcupine tree" + assert conn.query_params["count"] == "5" + Req.Test.json(conn, %{"results" => []}) + end) + + assert {:ok, []} = BraveSearch.search_images("porcupine tree", count: 5) + end + + test "defaults count to 20 when not supplied" do + Req.Test.stub(BraveSearch.API, fn conn -> + conn = Plug.Conn.fetch_query_params(conn) + assert conn.query_params["count"] == "20" + Req.Test.json(conn, %{"results" => []}) + end) + + assert {:ok, []} = BraveSearch.search_images("marillion") + end + + test "returns an empty list when the response has no results key" do + Req.Test.stub(BraveSearch.API, fn conn -> + Req.Test.json(conn, %{}) + end) + + assert {:ok, []} = BraveSearch.search_images("nothing") + end + + @tag :capture_log + test "returns the decoded error body on non-200 responses" do + Req.Test.stub(BraveSearch.API, fn conn -> + conn + |> Plug.Conn.put_resp_content_type("application/json") + |> Plug.Conn.send_resp(429, ~s({"error":"rate_limited"})) + end) + + assert {:error, %{"error" => "rate_limited"}} = + BraveSearch.search_images("too many requests") + end + end + + describe "download_image/1" do + @jpeg_magic <<0xFF, 0xD8, 0xFF, 0xE0, 0x00, 0x10>> + + test "returns the raw image binary on a 200 response" do + Req.Test.stub(BraveSearch.API, fn conn -> + conn + |> Plug.Conn.put_resp_content_type("image/jpeg") + |> Plug.Conn.send_resp(200, @jpeg_magic) + end) + + assert {:ok, @jpeg_magic} = + BraveSearch.download_image("https://images.example.com/raven-cover.jpg") + end + + @tag :capture_log + test "returns :download_failed on non-200 responses" do + Req.Test.stub(BraveSearch.API, fn conn -> + Plug.Conn.send_resp(conn, 404, "") + end) + + assert {:error, :download_failed} = + BraveSearch.download_image("https://images.example.com/missing.jpg") + end + + test "returns :download_failed on transport errors" do + Req.Test.stub(BraveSearch.API, fn conn -> + Req.Test.transport_error(conn, :timeout) + end) + + assert {:error, :download_failed} = + BraveSearch.download_image("https://images.example.com/slow.jpg") + end + end +end diff --git a/test/support/fixtures/brave_search/fixtures.ex b/test/support/fixtures/brave_search/fixtures.ex new file mode 100644 index 00000000..2fbabbd2 --- /dev/null +++ b/test/support/fixtures/brave_search/fixtures.ex @@ -0,0 +1,11 @@ +defmodule BraveSearch.Fixtures do + @moduledoc false + + @fixtures_folder Path.join([File.cwd!(), "test/support/fixtures/brave_search"]) + + @search_images_response Path.join([@fixtures_folder, "search_images_response.json"]) + |> File.read!() + |> JSON.decode!() + + def search_images_response, do: @search_images_response +end diff --git a/test/support/fixtures/brave_search/search_images_response.json b/test/support/fixtures/brave_search/search_images_response.json new file mode 100644 index 00000000..cadedc27 --- /dev/null +++ b/test/support/fixtures/brave_search/search_images_response.json @@ -0,0 +1,43 @@ +{ + "type": "images", + "results": [ + { + "title": "The Raven That Refused To Sing - Cover", + "source": "https://example.com/page/raven", + "thumbnail": { + "src": "https://thumbnails.example.com/raven-thumb.jpg" + }, + "properties": { + "url": "https://images.example.com/raven-cover.jpg", + "width": 1200, + "height": 1200 + } + }, + { + "title": "Hand. Cannot. Erase. - Alternate Cover", + "source": "https://example.com/page/hce", + "properties": { + "url": "https://images.example.com/hce-cover.jpg", + "width": 800, + "height": 800 + } + }, + { + "title": "To The Bone - Promo Shot", + "source": "https://example.com/page/ttb", + "thumbnail": { + "src": "https://thumbnails.example.com/ttb-thumb.jpg" + } + }, + { + "thumbnail": { + "src": "https://thumbnails.example.com/unknown-thumb.jpg" + }, + "properties": { + "url": "https://images.example.com/unknown.jpg", + "width": 640, + "height": 480 + } + } + ] +}