defmodule ToweropsWeb.UserRegistrationControllerTest do @moduledoc """ Tests for UserRegistrationController. Note: This controller is not currently routed (registration uses UserRegistrationLive), but we test the controller module directly to ensure code coverage and correctness. We dispatch through the endpoint to get proper plug pipeline setup. """ use ToweropsWeb.ConnCase, async: true import Towerops.AccountsFixtures import Towerops.OrganizationsFixtures alias Towerops.Accounts alias ToweropsWeb.UserRegistrationController # Helper to prepare conn for direct controller invocation # (controller is unrouted, so we set up params/flash manually) defp prepare_conn(conn) do conn |> Phoenix.ConnTest.init_test_session(%{}) |> Plug.Conn.fetch_query_params() |> Phoenix.Controller.fetch_flash() |> Plug.Conn.put_private(:phoenix_endpoint, ToweropsWeb.Endpoint) |> Plug.Conn.put_private(:phoenix_router, ToweropsWeb.Router) |> Plug.Conn.put_private(:phoenix_format, "html") |> Map.put(:params, %{}) end describe "new/2" do test "renders registration form", %{conn: conn} do conn = conn |> prepare_conn() |> UserRegistrationController.new(%{}) assert conn.status == 200 end test "renders registration form with invitation token", %{conn: conn} do user = user_fixture() org = organization_fixture(user.id) {:ok, invitation} = Towerops.Organizations.create_invitation(%{ organization_id: org.id, invited_by_id: user.id, email: "invited@example.com", role: "member" }) conn = conn |> prepare_conn() |> UserRegistrationController.new(%{"invitation_token" => invitation.token}) assert conn.status == 200 end test "handles non-existent invitation token gracefully", %{conn: conn} do conn = conn |> prepare_conn() |> UserRegistrationController.new(%{"invitation_token" => "bogus-token"}) assert conn.status == 200 end end describe "create/2" do test "creates user with organization on normal registration", %{conn: conn} do email = unique_user_email() conn = conn |> prepare_conn() |> Map.put(:params, %{}) |> UserRegistrationController.create(%{ "user" => %{ "email" => email, "password" => valid_user_password(), "privacy_policy_consent" => "true", "terms_of_service_consent" => "true" } }) # Should redirect (log in user) assert conn.status in [301, 302] # User should exist assert Accounts.get_user_by_email(email) end test "returns errors for invalid registration data", %{conn: conn} do conn = conn |> prepare_conn() |> Map.put(:params, %{}) |> UserRegistrationController.create(%{ "user" => %{ "email" => "bad", "password" => "x" } }) # Should re-render the form with errors assert conn.status == 200 end test "creates user via invitation and adds to organization", %{conn: conn} do inviter = user_fixture() org = organization_fixture(inviter.id) {:ok, invitation} = Towerops.Organizations.create_invitation(%{ organization_id: org.id, invited_by_id: inviter.id, email: "newuser@example.com", role: "member" }) email = unique_user_email() conn = conn |> prepare_conn() |> Map.put(:params, %{"invitation_token" => invitation.token}) |> UserRegistrationController.create(%{ "user" => %{ "email" => email, "password" => valid_user_password(), "invitation_token" => invitation.token, "privacy_policy_consent" => "true", "terms_of_service_consent" => "true" } }) # Should redirect (log in user) assert conn.status in [301, 302] # User should exist assert Accounts.get_user_by_email(email) end test "handles invalid invitation token on create", %{conn: conn} do email = unique_user_email() conn = conn |> prepare_conn() |> Map.put(:params, %{"invitation_token" => "expired-or-bad-token"}) |> UserRegistrationController.create(%{ "user" => %{ "email" => email, "password" => valid_user_password(), "invitation_token" => "expired-or-bad-token", "privacy_policy_consent" => "true", "terms_of_service_consent" => "true" } }) # Should re-render with error flash assert conn.status == 200 end test "prevents duplicate email registration", %{conn: conn} do existing_user = user_fixture() conn = conn |> prepare_conn() |> Map.put(:params, %{}) |> UserRegistrationController.create(%{ "user" => %{ "email" => existing_user.email, "password" => valid_user_password(), "privacy_policy_consent" => "true", "terms_of_service_consent" => "true" } }) # Should re-render with validation errors assert conn.status == 200 end end end