defmodule MvWeb.JoinConfirmControllerTest do @moduledoc """ Unit tests for JoinConfirmController (Subtask 2). Stubs the join-confirm callback via Application config so no DB or domain is used. Uses unauthenticated conn; route is public (/confirm*). """ use MvWeb.ConnCase, async: false # Stub modules for configurable callback (unit test: no real Membership calls) defmodule JoinConfirmValidStub do def confirm_join_request(_token, _opts), do: {:ok, %{}} end defmodule JoinConfirmExpiredStub do def confirm_join_request(_token, _opts), do: {:error, :token_expired} end defmodule JoinConfirmInvalidStub do def confirm_join_request(_token, _opts) do {:error, Ash.Error.Query.NotFound.exception(resource: Mv.Membership.JoinRequest)} end end setup %{conn: conn} do # Restore callback after each test so env does not leak on_exit(fn -> Application.delete_env(:mv, :join_confirm_callback) end) # Build unauthenticated conn for public confirm route unauth_conn = build_conn() |> init_test_session(%{}) |> fetch_flash() |> Plug.Conn.put_private(:ecto_sandbox, conn.private[:ecto_sandbox]) {:ok, conn: unauth_conn} end describe "GET /confirm_join/:token" do @tag role: :unauthenticated test "valid token returns 200 and success message", %{conn: conn} do Application.put_env(:mv, :join_confirm_callback, JoinConfirmValidStub) conn = get(conn, "/confirm_join/any-valid-token") assert response(conn, 200) =~ "received your request" end @tag role: :unauthenticated test "second request with same token still returns 200 (idempotent)", %{conn: conn} do Application.put_env(:mv, :join_confirm_callback, JoinConfirmValidStub) first = get(conn, "/confirm_join/same-token") second = get(conn, "/confirm_join/same-token") assert response(first, 200) =~ "received your request" assert response(second, 200) =~ "received your request" end @tag role: :unauthenticated test "expired token returns 200 with expired message", %{conn: conn} do Application.put_env(:mv, :join_confirm_callback, JoinConfirmExpiredStub) conn = get(conn, "/confirm_join/expired-token") assert response(conn, 200) =~ "expired" assert response(conn, 200) =~ "submit" end @tag role: :unauthenticated test "unknown or invalid token returns 404 with error message", %{conn: conn} do Application.put_env(:mv, :join_confirm_callback, JoinConfirmInvalidStub) conn = get(conn, "/confirm_join/nonexistent-token") assert response(conn, 404) =~ "Invalid" end @tag role: :unauthenticated test "route is public (unauthenticated request returns 200, not redirect to sign-in)", %{ conn: conn } do Application.put_env(:mv, :join_confirm_callback, JoinConfirmValidStub) conn = get(conn, "/confirm_join/public-test-token") assert conn.status == 200 refute redirected_to(conn) =~ "/sign-in" end end end