defmodule ToweropsWeb.Plugs.AgentAuthTest do use ToweropsWeb.ConnCase, async: false import Towerops.AccountsFixtures alias Towerops.Agents alias ToweropsWeb.Plugs.AgentAuth setup %{conn: conn} do user = user_fixture() {:ok, organization} = Towerops.Organizations.create_organization(%{name: "Test Org"}, user.id) {:ok, agent_token, token_string} = Agents.create_agent_token(organization.id, "Test Agent") # Set format to json for API endpoint conn = conn |> Plug.Conn.fetch_query_params() |> Phoenix.Controller.put_format("json") %{conn: conn, organization: organization, agent_token: agent_token, token_string: token_string} end describe "init/1" do test "returns options unchanged" do opts = [some: :option] assert AgentAuth.init(opts) == opts end end describe "call/2" do test "authenticates valid Bearer token", %{conn: conn, agent_token: agent_token, token_string: token_string} do conn = conn |> put_req_header("authorization", "Bearer #{token_string}") |> AgentAuth.call([]) assert conn.assigns.current_agent_token.id == agent_token.id refute conn.halted end test "rejects missing authorization header", %{conn: conn} do conn = AgentAuth.call(conn, []) assert conn.status == 401 assert conn.halted end test "rejects invalid Bearer token format", %{conn: conn} do conn = conn |> put_req_header("authorization", "InvalidFormat token123") |> AgentAuth.call([]) assert conn.status == 401 assert conn.halted end test "rejects malformed Bearer token", %{conn: conn} do conn = conn |> put_req_header("authorization", "Bearer") |> AgentAuth.call([]) assert conn.status == 401 assert conn.halted end test "rejects invalid token value", %{conn: conn} do conn = conn |> put_req_header("authorization", "Bearer invalid_token_value") |> AgentAuth.call([]) assert conn.status == 401 assert conn.halted end test "updates agent token heartbeat", %{ conn: conn, agent_token: agent_token, token_string: token_string } do # Get initial last_seen_at initial_agent_token = Agents.get_agent_token!(agent_token.id) initial_last_seen = initial_agent_token.last_seen_at conn = conn |> put_req_header("authorization", "Bearer #{token_string}") |> AgentAuth.call([]) # In test environment, heartbeat update is synchronous # Verify heartbeat was updated updated_agent_token = Agents.get_agent_token!(agent_token.id) if initial_last_seen do assert DateTime.compare(updated_agent_token.last_seen_at, initial_last_seen) in [:gt, :eq] else assert updated_agent_token.last_seen_at end assert updated_agent_token.last_ip == "127.0.0.1" refute conn.halted end test "handles token verification error gracefully", %{conn: conn} do # Use a token that looks valid but doesn't exist fake_token = String.duplicate("a", 32) conn = conn |> put_req_header("authorization", "Bearer #{fake_token}") |> AgentAuth.call([]) assert conn.status == 401 assert conn.halted end end end