Added 8 tests covering all plug functionality: - init/1 option passing - Valid token authentication - Invalid authorization header scenarios - Malformed Bearer token handling - Invalid token value rejection - Asynchronous heartbeat updates - Token verification error handling 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
122 lines
3.3 KiB
Elixir
122 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 asynchronously", %{
|
|
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([])
|
|
|
|
# Wait for async task to complete
|
|
Process.sleep(100)
|
|
|
|
# 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
|