towerops/test/towerops_web/plugs/agent_auth_test.exs
Graham McIntire f9b575816a
Fix database ownership issues in agent auth tests
- Update AgentAuth plug to use synchronous heartbeat updates in test env
- Refactor async heartbeat logic to reduce nesting (Credo)
- Remove Process.sleep from tests (no longer needed with sync updates)

This fixes 'owner exited' database errors in the test suite by avoiding
async Task spawns during tests, which caused DB connection ownership
conflicts with Ecto's sandbox mode.
2026-01-13 13:16:54 -06:00

120 lines
3.3 KiB
Elixir

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