From fb7ccad6f9215f4ea9d3e5696093fc41e67432ce Mon Sep 17 00:00:00 2001 From: Claudio Ortolina Date: Thu, 5 Mar 2026 14:43:50 +0000 Subject: [PATCH] Add meaningful tests for ArtistChat and RecordChat Closes #96 --- test/music_library/artist_chat_test.exs | 104 +++++++++++++++++++++++- test/music_library/record_chat_test.exs | 97 +++++++++++++++++++++- 2 files changed, 195 insertions(+), 6 deletions(-) diff --git a/test/music_library/artist_chat_test.exs b/test/music_library/artist_chat_test.exs index bcc5e4bd..0dc26442 100644 --- a/test/music_library/artist_chat_test.exs +++ b/test/music_library/artist_chat_test.exs @@ -2,9 +2,107 @@ defmodule MusicLibrary.ArtistChatTest do use ExUnit.Case alias MusicLibrary.ArtistChat + alias MusicLibrary.Artists.{Artist, ArtistInfo} - test "implements the Chat behaviour" do - Code.ensure_loaded!(ArtistChat) - assert function_exported?(ArtistChat, :stream_response, 3) + defp build_artist(attrs \\ %{}) do + defaults = %{ + musicbrainz_id: Ecto.UUID.generate(), + name: "Radiohead", + sort_name: "Radiohead" + } + + struct!(Artist, Map.merge(defaults, attrs)) + end + + defp build_artist_info(attrs \\ %{}) do + defaults = %{ + musicbrainz_data: %{ + "area" => %{ + "name" => "United Kingdom", + "iso-3166-1-codes" => ["GB"] + } + }, + wikipedia_data: %{ + "description" => "English rock band", + "extract" => + "Radiohead are an English rock band formed in Abingdon, Oxfordshire, in 1985." + } + } + + struct!(ArtistInfo, Map.merge(defaults, attrs)) + end + + defp sse_event(type, data) do + json = JSON.encode!(%{type: type, delta: data}) + "event: #{type}\ndata: #{json}\n\n" + end + + defp completed_response do + sse_event("response.output_text.delta", "Hello") <> + "event: response.completed\ndata: {\"type\":\"response.completed\"}\n\n" + end + + defp stub_and_capture_instructions(test_pid) do + Req.Test.stub(OpenAI.API, fn conn -> + {:ok, body, conn} = Plug.Conn.read_body(conn) + request = JSON.decode!(body) + send(test_pid, {:captured_instructions, request["instructions"]}) + + conn + |> Plug.Conn.put_resp_content_type("text/event-stream") + |> Plug.Conn.send_resp(200, completed_response()) + end) + end + + test "stream_response includes artist context with full info" do + stub_and_capture_instructions(self()) + + artist = build_artist() + artist_info = build_artist_info() + + assert :ok = ArtistChat.stream_response([], {artist, artist_info}, fn _chunk -> :ok end) + + assert_receive {:captured_instructions, instructions} + assert instructions =~ "Name: Radiohead" + assert instructions =~ "Country: United Kingdom" + assert instructions =~ "Description: English rock band" + assert instructions =~ "Biography: Radiohead are an English rock band formed in Abingdon" + end + + test "stream_response excludes nil wikipedia fields" do + stub_and_capture_instructions(self()) + + artist = build_artist() + artist_info = build_artist_info(%{wikipedia_data: %{}}) + + assert :ok = ArtistChat.stream_response([], {artist, artist_info}, fn _chunk -> :ok end) + + assert_receive {:captured_instructions, instructions} + assert instructions =~ "Name: Radiohead" + assert instructions =~ "Country: United Kingdom" + refute instructions =~ "Description:" + refute instructions =~ "Biography:" + refute instructions =~ "nil" + end + + test "stream_response handles missing country gracefully" do + stub_and_capture_instructions(self()) + + artist = build_artist() + + artist_info = + build_artist_info(%{ + musicbrainz_data: %{ + "area" => %{"name" => nil} + }, + wikipedia_data: %{} + }) + + assert :ok = ArtistChat.stream_response([], {artist, artist_info}, fn _chunk -> :ok end) + + assert_receive {:captured_instructions, instructions} + assert instructions =~ "Name: Radiohead" + # Falls back to "World" when area name is nil + refute instructions =~ "Country: \n" end end diff --git a/test/music_library/record_chat_test.exs b/test/music_library/record_chat_test.exs index b75cb4cc..a3f3603f 100644 --- a/test/music_library/record_chat_test.exs +++ b/test/music_library/record_chat_test.exs @@ -1,10 +1,101 @@ defmodule MusicLibrary.RecordChatTest do use ExUnit.Case + alias MusicLibrary.Artists.Artist alias MusicLibrary.RecordChat + alias MusicLibrary.Records.Record - test "implements the Chat behaviour" do - Code.ensure_loaded!(RecordChat) - assert function_exported?(RecordChat, :stream_response, 3) + defp build_record(attrs \\ %{}) do + defaults = %{ + title: "OK Computer", + type: :album, + format: :cd, + genres: ["alternative rock", "art rock"], + release_date: "1997-06-16", + artists: [ + %Artist{ + musicbrainz_id: Ecto.UUID.generate(), + name: "Radiohead", + sort_name: "Radiohead" + } + ] + } + + struct!(Record, Map.merge(defaults, attrs)) + end + + defp sse_event(type, data) do + json = JSON.encode!(%{type: type, delta: data}) + "event: #{type}\ndata: #{json}\n\n" + end + + defp completed_response do + sse_event("response.output_text.delta", "Hello") <> + "event: response.completed\ndata: {\"type\":\"response.completed\"}\n\n" + end + + defp stub_and_capture_instructions(test_pid) do + Req.Test.stub(OpenAI.API, fn conn -> + {:ok, body, conn} = Plug.Conn.read_body(conn) + request = JSON.decode!(body) + send(test_pid, {:captured_instructions, request["instructions"]}) + + conn + |> Plug.Conn.put_resp_content_type("text/event-stream") + |> Plug.Conn.send_resp(200, completed_response()) + end) + end + + test "stream_response uses embedding text when present" do + stub_and_capture_instructions(self()) + + record = build_record() + embedding_text = "Radiohead - OK Computer (1997). Alternative rock masterpiece." + + assert :ok = RecordChat.stream_response([], {record, embedding_text}, fn _chunk -> :ok end) + + assert_receive {:captured_instructions, instructions} + assert instructions =~ embedding_text + refute instructions =~ "Album: OK Computer" + end + + test "stream_response falls back to basic context when embedding text is nil" do + stub_and_capture_instructions(self()) + + record = build_record() + + assert :ok = RecordChat.stream_response([], {record, nil}, fn _chunk -> :ok end) + + assert_receive {:captured_instructions, instructions} + assert instructions =~ "Album: OK Computer" + assert instructions =~ "Artists: Radiohead" + assert instructions =~ "Genres: alternative rock, art rock" + assert instructions =~ "Released: 1997-06-16" + assert instructions =~ "Type: album" + assert instructions =~ "Format: cd" + end + + test "stream_response falls back to basic context when embedding text is empty" do + stub_and_capture_instructions(self()) + + record = build_record() + + assert :ok = RecordChat.stream_response([], {record, ""}, fn _chunk -> :ok end) + + assert_receive {:captured_instructions, instructions} + assert instructions =~ "Album: OK Computer" + assert instructions =~ "Artists: Radiohead" + end + + test "stream_response handles missing optional fields gracefully" do + stub_and_capture_instructions(self()) + + record = build_record(%{genres: nil, release_date: nil}) + + assert :ok = RecordChat.stream_response([], {record, nil}, fn _chunk -> :ok end) + + assert_receive {:captured_instructions, instructions} + assert instructions =~ "Genres: \n" + assert instructions =~ "Released: Unknown" end end