test/openagents/voice/open_ai/call_client_test.exs

58e6347eeb72 · 3 KB

defmodule OpenAgents.Voice.OpenAI.CallClientTest do
  use ExUnit.Case, async: true

  alias OpenAgents.Voice.Config
  alias OpenAgents.Voice.OpenAI.CallClient

  setup {Req.Test, :verify_on_exit!}

  test "exchanges a bounded SDP offer through the unified multipart endpoint" do
    safety_identifier = String.duplicate("a", 64)

    contextual_config =
      Config.with_context(enabled_config(), "You are OpenAgents.", [
        %{
          "type" => "function",
          "name" => "memory_list",
          "description" => "List memory",
          "parameters" => %{"type" => "object", "properties" => %{}}
        }
      ])

    Req.Test.expect(__MODULE__, fn conn ->
      assert ["Bearer test-secret"] = Plug.Conn.get_req_header(conn, "authorization")

      assert [^safety_identifier] =
               Plug.Conn.get_req_header(conn, "openai-safety-identifier")

      assert ["multipart/form-data; boundary=" <> _boundary] =
               Plug.Conn.get_req_header(conn, "content-type")

      body = Req.Test.raw_body(conn)
      assert body =~ ~s(name="sdp")
      assert body =~ "v=0\r\no=test-offer"
      assert body =~ ~s(name="session")
      assert body =~ ~s("model":"gpt-realtime-2.1")
      assert body =~ ~s("voice":"marin")
      assert body =~ ~s("instructions":"You are OpenAgents.")
      assert body =~ ~s("name":"memory_list")
      assert body =~ ~s("create_response":false)
      refute body =~ "test-secret"

      conn
      |> Plug.Conn.put_resp_header("location", "/v1/realtime/calls/rtc_123")
      |> Plug.Conn.send_resp(201, "v=0\r\no=test-answer")
    end)

    assert {:ok, admission} =
             CallClient.create("v=0\r\no=test-offer", safety_identifier, contextual_config,
               api_key: "test-secret",
               request_options: [plug: {Req.Test, __MODULE__}, retry: false]
             )

    assert admission.provider_session_id == "rtc_123"
    assert admission.answer_sdp == "v=0\r\no=test-answer"
  end

  test "fails closed on absent credentials and malformed provider identity" do
    assert {:error, :missing_api_key} =
             CallClient.create("v=0\r\n", String.duplicate("b", 64), enabled_config(),
               api_key: ""
             )

    Req.Test.expect(__MODULE__, fn conn ->
      conn
      |> Plug.Conn.put_resp_header("location", "/unexpected/rtc_123")
      |> Plug.Conn.send_resp(200, "v=0\r\no=answer")
    end)

    assert {:error, :invalid_call_id} =
             CallClient.create("v=0\r\n", String.duplicate("b", 64), enabled_config(),
               api_key: "test-secret",
               request_options: [plug: {Req.Test, __MODULE__}, retry: false]
             )
  end

  test "refuses disabled voice and oversized SDP before transport" do
    disabled = %{enabled_config() | enabled?: false}

    assert {:error, :voice_disabled} =
             CallClient.create("v=0\r\n", String.duplicate("c", 64), disabled, api_key: "unused")

    assert {:error, :invalid_sdp} =
             CallClient.create(
               "v=0" <> String.duplicate("x", 65_534),
               String.duplicate("c", 64),
               enabled_config(),
               api_key: "unused"
             )
  end

  test "call creation does not retry a failed POST without an idempotency contract" do
    Req.Test.expect(__MODULE__, fn conn -> Plug.Conn.send_resp(conn, 503, "unavailable") end)

    assert {:error, {:http_status, 503}} =
             CallClient.create(
               "v=0\r\no=no-retry-offer",
               String.duplicate("d", 64),
               enabled_config(),
               api_key: "test-secret",
               request_options: [plug: {Req.Test, __MODULE__}]
             )
  end

  defp enabled_config do
    Config.build!(
      enabled: true,
      architecture: :openai_realtime,
      provider: "openai",
      model: "gpt-realtime-2.1",
      voice: "marin",
      reasoning_effort: "low",
      maximum_session_seconds: 3_000
    )
  end
end