towerops/test/towerops_web/controllers/api/mobile_auth_controller_test.exs
Graham McIntire 2269f38fc5 test: lift coverage 78.42% → 79.59% with focused unit + integration tests
Adds tests across previously-uncovered or under-covered modules:

- Mix tasks: backfill_checks, copy_mibs, import_mibs (new test files)
- Status pages: StatusIncident changeset (new file)
- Webhook signature verification on AgentReleaseWebhookController
- CloudflareBanWorker: HTTP-stubbed success / 5xx / transport-error paths
- FirmwareVersionFetcherWorker: stubbed perform/0 success + non-200 + tx error
- Geoip.Import: production --production code path (HTTP-stubbed)
- AgentLive.Show: superuser restart_agent + update_agent + non-superuser paths
- NetworkMapLive: node_clicked, deep-link node, topology_updated PubSub
- PreseemInsightsLive: toggle_select, deselect_all, filter, dismiss-twice
- AlertQuery: 1-arity defaults variants
- ProfileWatcher: yaml + reload-trigger event passthrough
- MobileAuthController: verify_qr_token success + get/revoke session
- HealthController: redis-configured branch
- RedisHealthCheck: un-tag :integration tests now that Redis is local
- FourOhFourTracker: un-skip module (Redis available)
- PingExecutor: un-tag local-only tests + KeyError surface + clamp branch
- CheckExecutorWorker: dispatch tcp/dns/ssl/ping branches
- UserResetPasswordController: drop dead edit/update + their template

Also removes dead code: edit/update actions on UserResetPasswordController
and the unused edit.html.heex template — both routed via LiveView.
2026-05-07 18:40:43 -05:00

109 lines
3.4 KiB
Elixir

defmodule ToweropsWeb.Api.MobileAuthControllerTest do
use ToweropsWeb.ConnCase, async: true
import Towerops.AccountsFixtures
alias Towerops.MobileSessions
describe "verify_qr_token" do
test "returns error for missing token", %{conn: conn} do
conn = post(conn, ~p"/api/v1/mobile/auth/qr/verify", %{})
assert json_response(conn, 400)["error"] == "Missing token parameter"
end
test "returns error for invalid token", %{conn: conn} do
conn = post(conn, ~p"/api/v1/mobile/auth/qr/verify", %{"token" => "invalid"})
assert json_response(conn, 401)["valid"] == false
end
test "returns valid + user_email for an active QR token", %{conn: conn} do
user = user_fixture()
{:ok, qr_token} = MobileSessions.create_qr_login_token(user.id)
conn = post(conn, ~p"/api/v1/mobile/auth/qr/verify", %{"token" => qr_token.token})
response = json_response(conn, 200)
assert response["valid"] == true
assert response["user_email"] == user.email
end
end
describe "get_session and revoke_session (authenticated)" do
setup do
user = user_fixture()
{:ok, session} =
MobileSessions.create_mobile_session(%{
user_id: user.id,
device_name: "iTest"
})
%{session: session, raw_token: session.raw_token, user: user}
end
test "returns session metadata for an authenticated request",
%{conn: conn, raw_token: raw_token, session: session} do
conn =
conn
|> put_req_header("authorization", "Bearer #{raw_token}")
|> get(~p"/api/v1/mobile/auth/session")
response = json_response(conn, 200)
assert response["id"] == session.id
assert response["device_name"] == "iTest"
end
test "revokes the current session", %{conn: conn, raw_token: raw_token} do
conn =
conn
|> put_req_header("authorization", "Bearer #{raw_token}")
|> delete(~p"/api/v1/mobile/auth/session")
assert json_response(conn, 200)["success"] == true
end
end
describe "complete_qr_login" do
test "returns error for missing token", %{conn: conn} do
conn = post(conn, ~p"/api/v1/mobile/auth/qr/complete", %{})
assert json_response(conn, 400)["error"] == "Missing token parameter"
end
test "returns error for invalid token", %{conn: conn} do
conn =
post(conn, ~p"/api/v1/mobile/auth/qr/complete", %{
"token" => "invalid",
"device_name" => "Test Phone"
})
assert json_response(conn, 401)["error"] == "Invalid or expired token"
end
test "returns a usable session token on success", %{conn: conn} do
user = user_fixture()
{:ok, qr_token} = MobileSessions.create_qr_login_token(user.id)
conn =
post(conn, ~p"/api/v1/mobile/auth/qr/complete", %{
"token" => qr_token.token,
"device_name" => "Test iPhone",
"device_os" => "iOS 17.2",
"app_version" => "1.0.0"
})
assert %{"session_token" => session_token} = json_response(conn, 200)
assert is_binary(session_token)
# The returned token must be usable for subsequent authenticated requests
authed_conn =
build_conn()
|> put_req_header("authorization", "Bearer #{session_token}")
|> get(~p"/api/v1/mobile/organizations")
assert json_response(authed_conn, 200)
end
end
end