test/openagents/box_client_runs_test.exs

main at 58e6347eeb72 · 6 KB

defmodule OpenAgents.BoxClientRunsTest do
  use ExUnit.Case, async: false

  alias OpenAgents.Box.Client

  setup {Req.Test, :verify_on_exit!}

  setup do
    original_api = Application.get_env(:openagents, :box_api)
    original_key = Application.get_env(:openagents, :box_api_key)

    Application.put_env(:openagents, :box_api,
      base_url: "https://box-api.internal",
      request_options: [plug: {Req.Test, __MODULE__}]
    )

    Application.put_env(:openagents, :box_api_key, "box-client-test")

    on_exit(fn ->
      restore_env(:box_api, original_api)
      restore_env(:box_api_key, original_key)
    end)
  end

  test "dispatch uses one detached mkdir-and-launch command", do: begin_dispatch()

  test "assignment dispatch carries the credential in the command, never in env" do
    token = "oa_assignment_11111111-1111-4111-8111-111111111111.secret-token"
    run_directory = "/home/box-user/.openagents/box-runs"

    Req.Test.expect(__MODULE__, fn request ->
      command = request.body_params["command"]

      # The provider's CommandRequest schema has no `env`, so a credential sent
      # there is dropped and the wrapper dies on an unbound variable.
      refute Map.has_key?(request.body_params, "env")
      refute command =~ "OPENAGENTS_FORGE_TOKEN"

      # The credential reaches the box inside the command, base64 only so a
      # token cannot break the surrounding shell quoting.
      refute command =~ token
      assert command =~ Base.encode64("https://x:#{token}@openagents.com\n")

      assert command =~ "root=#{run_directory}"
      assert command =~ ~s(> "$root/forge-credential")
      assert command =~ ~s(git config --file="$root/gitconfig")
      assert command =~ ~s(env GIT_CONFIG_GLOBAL="$root/gitconfig")
      assert command =~ "umask 077"
      refute command =~ "credential_setup"

      script_path =
        Path.join(
          System.tmp_dir!(),
          "assignment-dispatch-#{System.unique_integer([:positive])}.sh"
        )

      on_exit(fn -> File.rm(script_path) end)
      assert :ok = File.write(script_path, command)
      assert {_output, 0} = System.cmd("sh", ["-n", script_path])

      Req.Test.json(request, %{"stdout" => "4242\n"})
    end)

    assert {:ok, 4242} =
             Client.dispatch_run(
               "bx_8bhkse3n",
               "11111111-1111-4111-8111-111111111111",
               "git push https://openagents.com/repo.git",
               run_directory,
               token
             )
  end

  test "the credential setup a box executes reconstructs the git credential file" do
    token = "oa_assignment_11111111-1111-4111-8111-111111111111.secret-token"
    encoded = Base.encode64("https://x:#{token}@openagents.com\n")

    assert {output, 0} =
             System.cmd("sh", ["-c", "printf '%s' '#{encoded}' | base64 -d"])

    assert output == "https://x:#{token}@openagents.com\n"
  end

  test "a 2xx without a pid is logged bounded and with the credential struck out" do
    token = "oa_assignment_11111111-1111-4111-8111-111111111111.secret-token"

    Req.Test.expect(__MODULE__, fn request ->
      Req.Test.json(request, %{
        "exit_code" => 1,
        "stdout" => "",
        "stderr" => "bash: line 12: refused with #{token}\nsecond line\n",
        "timed_out" => false
      })
    end)

    log =
      ExUnit.CaptureLog.capture_log(fn ->
        assert {:error, :box_response_invalid} =
                 Client.dispatch_run(
                   "bx_8bhkse3n",
                   "11111111-1111-4111-8111-111111111111",
                   "git push https://openagents.com/repo.git",
                   "/home/box-user/.openagents/box-runs",
                   token
                 )
      end)

    assert log =~ "box_dispatch_response_invalid"
    assert log =~ "box_id=bx_8bhkse3n"
    assert log =~ "run_id=11111111-1111-4111-8111-111111111111"
    assert log =~ "exit_code=1"
    assert log =~ "stdout_bytes=0"
    assert log =~ "[redacted]"
    refute log =~ token
    refute log =~ "second line"
  end

  test "an uncredentialed 2xx without a pid is logged without a credential" do
    Req.Test.expect(__MODULE__, fn request ->
      Req.Test.json(request, %{"exit_code" => 73, "stdout" => "OPENAGENTS_RUN_EXISTS\n"})
    end)

    log =
      ExUnit.CaptureLog.capture_log(fn ->
        assert {:error, :box_response_invalid} =
                 Client.dispatch_run(
                   "bx_8bhkse3n",
                   "11111111-1111-4111-8111-111111111111",
                   "echo detached"
                 )
      end)

    assert log =~ "box_dispatch_response_invalid"
    assert log =~ "exit_code=73"
    assert log =~ "keys=exit_code,stdout"
  end

  test "poll decodes bounded output from a recorded offset" do
    encoded = Base.encode64("hello")

    Req.Test.expect(__MODULE__, fn request ->
      assert request.method == "POST"
      assert request.request_path == "/boxes/bx_8bhkse3n/commands"
      body = request.body_params
      assert body["command"] =~ "OA_SIZE"
      assert body["command"] =~ "dd"

      Req.Test.json(request, %{
        "stdout" => "OA_PRESENT=1\nOA_SIZE=5\nOA_DATA=#{encoded}\nOA_ALIVE=1\n"
      })
    end)

    assert {:ok, %{present: true, log_size: 5, output: "hello", exit_status: nil, alive: true}} =
             Client.poll_run("bx_8bhkse3n", "11111111-1111-4111-8111-111111111111", 0)
  end

  test "an exit sentinel is parsed as an integer status" do
    Req.Test.expect(__MODULE__, fn request ->
      Req.Test.json(request, %{
        "stdout" => "OA_PRESENT=1\nOA_SIZE=0\nOA_DATA=\nOA_EXIT=17\nOA_ALIVE=0\n"
      })
    end)

    assert {:ok, %{exit_status: 17, alive: false}} =
             Client.poll_run("bx_8bhkse3n", "11111111-1111-4111-8111-111111111111", 0)
  end

  defp begin_dispatch do
    Req.Test.expect(__MODULE__, fn request ->
      assert request.method == "POST"
      assert request.request_path == "/boxes/bx_8bhkse3n/commands"
      body = request.body_params
      assert body["timeoutSeconds"] == 30
      assert body["command"] =~ "mkdir"
      assert body["command"] =~ "setsid"
      assert body["command"] =~ "output.log"
      assert body["command"] =~ "exit-code"
      Req.Test.json(request, %{"stdout" => "4242\n"})
    end)

    assert {:ok, 4242} =
             Client.dispatch_run(
               "bx_8bhkse3n",
               "11111111-1111-4111-8111-111111111111",
               "echo detached"
             )
  end

  defp restore_env(key, nil), do: Application.delete_env(:openagents, key)
  defp restore_env(key, value), do: Application.put_env(:openagents, key, value)
end