test/openagents/threads/event_broadcast_test.exs

58e6347eeb72 · 2 KB

defmodule OpenAgents.Threads.EventBroadcastTest do
  use OpenAgents.DataCase, async: false

  import OpenAgentsWeb.ConnCase, only: [github_user: 1]

  alias OpenAgents.Threads
  alias OpenAgents.Threads.Event

  test "a committed append is broadcast on the thread's topic" do
    user = github_user("thread-broadcast")
    {:ok, thread} = Threads.open(user, "Broadcast the transcript")

    :ok = Threads.subscribe(thread)

    {:ok, _updated} = Threads.record_event(thread, "turn.user", %{"text" => "hello"})

    assert_receive {:thread_event, %Event{event_type: "turn.user", payload: %{"text" => "hello"}}}
  end

  test "a refused append broadcasts nothing" do
    user = github_user("thread-broadcast-terminal")
    {:ok, thread} = Threads.open(user, "Terminal threads stay silent")
    {:ok, cancelled} = Threads.cancel(thread)

    :ok = Threads.subscribe(cancelled)

    assert {:error, :thread_terminal} =
             Threads.record_event(cancelled, "turn.user", %{"text" => "late"})

    refute_receive {:thread_event, _event}
  end

  test "a committed batch broadcasts each event once, in order" do
    user = github_user("thread-broadcast-batch")
    {:ok, thread} = Threads.open(user, "Broadcast the batch")

    :ok = Threads.subscribe(thread)

    {:ok, _updated, events} =
      Threads.record_events(thread, [
        %{event_type: "turn.user", payload: %{"text" => "first"}},
        %{event_type: "tool.ran", payload: %{"tool" => "bash"}},
        %{event_type: "turn.assistant", payload: %{"text" => "third"}}
      ])

    # A subscriber cannot tell a batch from the same events posted one at a
    # time: one message per event, in the order they landed, none repeated.
    for event <- events do
      assert_receive {:thread_event, %Event{} = received}
      assert received.id == event.id
      assert received.event_type == event.event_type
    end

    refute_receive {:thread_event, _event}
  end

  test "a batch that rolls back broadcasts nothing" do
    user = github_user("thread-broadcast-rollback")
    {:ok, thread} = Threads.open(user, "Rolled back batches stay silent")

    :ok = Threads.subscribe(thread)

    assert {:error, {1, %Ecto.Changeset{}}} =
             Threads.record_events(thread, [
               %{event_type: "turn.user", payload: %{"text" => "valid"}},
               %{event_type: String.duplicate("x", 81), payload: %{}}
             ])

    # The first entry was inserted and then rolled back with the second, so a
    # subscriber must never have heard about it.
    refute_receive {:thread_event, _event}
    assert Threads.list_events(thread) |> Enum.map(& &1.event_type) == ["thread.opened"]
  end

  test "another thread's subscriber hears nothing" do
    user = github_user("thread-broadcast-scope")
    {:ok, mine} = Threads.open(user, "Mine")
    {:ok, other} = Threads.open(user, "Other")

    :ok = Threads.subscribe(other)

    {:ok, _updated} = Threads.record_event(mine, "turn.user", %{"text" => "scoped"})

    refute_receive {:thread_event, _event}
  end
end