diff --git a/test/towerops_web/live/org/settings_live_integrations_test.exs b/test/towerops_web/live/org/settings_live_integrations_test.exs new file mode 100644 index 00000000..77e38476 --- /dev/null +++ b/test/towerops_web/live/org/settings_live_integrations_test.exs @@ -0,0 +1,42 @@ +defmodule ToweropsWeb.Org.SettingsLiveIntegrationsTest do + @moduledoc """ + Drives webhook-secret + gaiia-app-url paths in OrgSettingsLive that + aren't exercised by the main settings_live_test.exs. + + The configure-flow handlers reference a `:configuring` assign that + isn't initialized in mount, so tests can't drive them via render_click. + """ + use ToweropsWeb.ConnCase + + import Phoenix.LiveViewTest + import Towerops.AccountsFixtures + import Towerops.OrganizationsFixtures + + setup do + user = user_fixture(enable_totp: true) + organization = organization_fixture(user.id) + %{user: user, organization: organization} + end + + describe "save_webhook_secret event" do + test "saves a webhook secret", %{conn: conn, user: user, organization: org} do + {:ok, view, _html} = + conn + |> log_in_user(user) + |> live(~p"/orgs/#{org.slug}/settings?tab=integrations") + + assert render_hook(view, "save_webhook_secret", %{"value" => "test-secret"}) + end + end + + describe "save_gaiia_app_url event" do + test "saves a gaiia app URL", %{conn: conn, user: user, organization: org} do + {:ok, view, _html} = + conn + |> log_in_user(user) + |> live(~p"/orgs/#{org.slug}/settings?tab=integrations") + + assert render_hook(view, "save_gaiia_app_url", %{"value" => "https://gaiia.example.com"}) + end + end +end diff --git a/test/towerops_web/live/user_settings_live/api_token_manager_test.exs b/test/towerops_web/live/user_settings_live/api_token_manager_test.exs new file mode 100644 index 00000000..2c3f0fc3 --- /dev/null +++ b/test/towerops_web/live/user_settings_live/api_token_manager_test.exs @@ -0,0 +1,101 @@ +defmodule ToweropsWeb.UserSettingsLive.ApiTokenManagerTest do + use Towerops.DataCase, async: false + + import Towerops.AccountsFixtures + import Towerops.OrganizationsFixtures + + alias Phoenix.LiveView.Socket + alias Towerops.Accounts.Scope + alias Towerops.ApiTokens + alias ToweropsWeb.UserSettingsLive.ApiTokenManager + + setup do + user = user_fixture() + org = organization_fixture(user.id) + %{user: user, org: org} + end + + describe "create_api_token/2" do + test "creates a token from nested params and stages QR modal", %{user: user, org: org} do + socket = base_socket(user) + params = %{"token" => %{"name" => "Test Token", "organization_id" => org.id}} + + result = ApiTokenManager.create_api_token(socket, params) + + assert result.assigns.show_token_modal == true + assert is_binary(result.assigns.created_token) + assert is_list(result.assigns.api_tokens) + end + + test "creates a token from flat params", %{user: user, org: org} do + socket = base_socket(user) + params = %{"name" => "Flat Token", "organization_id" => org.id} + + result = ApiTokenManager.create_api_token(socket, params) + assert result.assigns.show_token_modal == true + end + + test "puts an error flash when name/org are missing", %{user: user} do + socket = base_socket(user) + result = ApiTokenManager.create_api_token(socket, %{}) + + assert result.assigns.flash["error"] =~ "required" + end + end + + describe "close_token_modal/1" do + test "clears the staged token and closes the modal", %{user: user} do + socket = + user + |> base_socket() + |> Phoenix.Component.assign(:show_token_modal, true) + |> Phoenix.Component.assign(:created_token, "tok-xyz") + + result = ApiTokenManager.close_token_modal(socket) + assert result.assigns.show_token_modal == false + assert is_nil(result.assigns.created_token) + end + end + + describe "delete_api_token/2" do + test "deletes a token owned by the user", %{user: user, org: org} do + {:ok, {token, _raw}} = + ApiTokens.create_api_token(%{ + name: "Killable", + organization_id: org.id, + user_id: user.id + }) + + socket = base_socket(user) + result = ApiTokenManager.delete_api_token(socket, token.id) + + assert result.assigns.flash["info"] =~ "deleted" + end + + test "rejects deletion of a token owned by another user", %{org: org} do + other_user = user_fixture() + + {:ok, {token, _raw}} = + ApiTokens.create_api_token(%{ + name: "Other", + organization_id: org.id, + user_id: other_user.id + }) + + socket = base_socket(user_fixture()) + result = ApiTokenManager.delete_api_token(socket, token.id) + + assert result.assigns.flash["error"] =~ "your own tokens" + end + end + + defp base_socket(user) do + %Socket{ + assigns: %{ + current_scope: Scope.for_user(user), + flash: %{}, + __changed__: %{} + } + } + end +end diff --git a/test/towerops_web/live/user_settings_live/totp_manager_test.exs b/test/towerops_web/live/user_settings_live/totp_manager_test.exs index c6bd13bc..85434b38 100644 --- a/test/towerops_web/live/user_settings_live/totp_manager_test.exs +++ b/test/towerops_web/live/user_settings_live/totp_manager_test.exs @@ -85,6 +85,51 @@ defmodule ToweropsWeb.UserSettingsLive.TotpManagerTest do end end + describe "create_device/2" do + test "creates a new device and stages QR modal assigns" do + user = user_fixture() + socket = base_socket(user) + result = TotpManager.create_device(socket, "Phone") + + assert result.assigns[:show_device_qr_modal] == true + assert result.assigns[:new_device_id] + assert is_binary(result.assigns[:new_device_secret]) + assert is_binary(result.assigns[:new_device_qr_code]) + end + end + + describe "close_device_qr_modal/1" do + test "deletes unverified device and clears modal assigns" do + user = user_fixture() + + socket = + user + |> base_socket() + |> TotpManager.create_device("Tablet") + + result = TotpManager.close_device_qr_modal(socket) + + assert result.assigns.show_device_qr_modal == false + assert is_nil(result.assigns.new_device_id) + assert is_nil(result.assigns.new_device_secret) + assert is_nil(result.assigns.new_device_qr_code) + end + + test "is a no-op when no new_device_id is staged" do + user = user_fixture() + + socket = + user + |> base_socket() + |> Phoenix.Component.assign(:new_device_id, nil) + |> Phoenix.Component.assign(:new_device_secret, nil) + |> Phoenix.Component.assign(:new_device_qr_code, nil) + + result = TotpManager.close_device_qr_modal(socket) + assert result.assigns.show_device_qr_modal == false + end + end + defp base_socket(user) do %Socket{ assigns: %{