diff --git a/apps/bright/lib/bright/patreon_entitlements.ex b/apps/bright/lib/bright/patrons/patreon_entitlements.ex similarity index 83% rename from apps/bright/lib/bright/patreon_entitlements.ex rename to apps/bright/lib/bright/patrons/patreon_entitlements.ex index d273cec..ff532b8 100644 --- a/apps/bright/lib/bright/patreon_entitlements.ex +++ b/apps/bright/lib/bright/patrons/patreon_entitlements.ex @@ -1,4 +1,4 @@ -defmodule Bright.PatreonEntitlements do +defmodule Bright.Patrons.PatreonEntitlements do @spec extract_tiers(map()) :: list(String.t()) def extract_tiers(response_body) do response_body diff --git a/apps/bright/lib/bright/tier_mapper.ex b/apps/bright/lib/bright/patrons/tier_mapper.ex similarity index 73% rename from apps/bright/lib/bright/tier_mapper.ex rename to apps/bright/lib/bright/patrons/tier_mapper.ex index bb9d58c..39d672d 100644 --- a/apps/bright/lib/bright/tier_mapper.ex +++ b/apps/bright/lib/bright/patrons/tier_mapper.ex @@ -1,4 +1,4 @@ -defmodule Bright.TierMapper do +defmodule Bright.Patrons.TierMapper do @patreon_tiers %{ "everyone" => 0, "free" => 0, @@ -34,10 +34,16 @@ defmodule Bright.TierMapper do def get_tier_number(_, _), do: 0 + def largest_tier_for_platform(_platform, []), do: 0 + @spec largest_tier_for_platform(any(), nil | maybe_improper_list()) :: any() def largest_tier_for_platform(platform, tier_ids) - when is_list(tier_ids) and is_binary(platform) do + when is_binary(platform) and is_list(tier_ids) do tier_ids |> Enum.map(&get_tier_number(platform, &1)) |> Enum.max() end + + @spec largest_tier_for_platform(any(), list()) :: integer() + def largest_tier_for_platform(_, _), do: 0 + def largest_tier_for_platform(_), do: 0 end diff --git a/apps/bright/lib/bright/users/user.ex b/apps/bright/lib/bright/users/user.ex index e833806..343068b 100644 --- a/apps/bright/lib/bright/users/user.ex +++ b/apps/bright/lib/bright/users/user.ex @@ -35,7 +35,7 @@ defmodule Bright.Users.User do """ def registration_changeset(user, attrs, _opts \\ []) do user - |> cast(attrs, [:patreon_id, :github_id, :role]) + |> cast(attrs, [:patreon_id, :github_id, :role, :patron_tier]) |> validate_provider_id() end diff --git a/apps/bright/lib/bright_web/components/core_components.ex b/apps/bright/lib/bright_web/components/core_components.ex index 5fd536e..f8377e9 100644 --- a/apps/bright/lib/bright_web/components/core_components.ex +++ b/apps/bright/lib/bright_web/components/core_components.ex @@ -19,6 +19,27 @@ defmodule BrightWeb.CoreComponents do alias Phoenix.LiveView.JS + @doc """ + Renders an external link. + + ## Example: + ```heex + <.external_link href="https://example.com">Visit Example + ``` + """ + attr :rest, :global, doc: "the arbitrary HTML attributes to add to the flash container" + attr :href, :string, required: true + slot :inner_block, required: true + + def(external_link(assigns)) do + ~H""" + <.link href={@href} target="_blank" rel="noopener noreferrer" {@rest}> + {render_slot(@inner_block)} + <.icon name="arrow-up-right-from-square" class="ml-0" /> + + """ + end + @doc """ Renders a modal. diff --git a/apps/bright/lib/bright_web/components/layouts/app.html.heex b/apps/bright/lib/bright_web/components/layouts/app.html.heex index 7a4ed6b..6a5f494 100644 --- a/apps/bright/lib/bright_web/components/layouts/app.html.heex +++ b/apps/bright/lib/bright_web/components/layouts/app.html.heex @@ -1,4 +1,81 @@ -
- <.flash_group flash={@flash} /> - {@inner_content} -
+
+ +
+ <.flash_group flash={@flash} /> + {@inner_content} +
+
diff --git a/apps/bright/lib/bright_web/components/layouts/root.html.heex b/apps/bright/lib/bright_web/components/layouts/root.html.heex index f961913..024159c 100644 --- a/apps/bright/lib/bright_web/components/layouts/root.html.heex +++ b/apps/bright/lib/bright_web/components/layouts/root.html.heex @@ -28,81 +28,6 @@ - {@inner_content} diff --git a/apps/bright/lib/bright_web/controllers/auth_controller.ex b/apps/bright/lib/bright_web/controllers/auth_controller.ex index 5caab72..15ec433 100644 --- a/apps/bright/lib/bright_web/controllers/auth_controller.ex +++ b/apps/bright/lib/bright_web/controllers/auth_controller.ex @@ -4,6 +4,7 @@ defmodule BrightWeb.AuthController do alias Bright.Users alias Bright.Users.User + alias Bright.Patrons.TierMapper alias Bright.Repo require Logger @@ -66,12 +67,19 @@ defmodule BrightWeb.AuthController do end end - def callback(conn = %{assigns: %{ueberauth_failure: fails}}, _params) do - Logger.error(inspect(fails)) + def callback( + conn = %{assigns: %{ueberauth_failure: %Ueberauth.Failure{errors: errors}}}, + _params + ) do + error_messages = + errors + |> Enum.map(fn %Ueberauth.Failure.Error{message: message} -> message end) + # Join multiple errors into a single string if needed + |> Enum.join(", ") conn - |> put_flash(:error, "Something went wrong; please try again.") - |> render("new.html", user: nil) + |> put_flash(:error, "Authentication failed: #{error_messages}") + |> redirect(to: ~p"/") end defp params_from_ueberauth(%{provider: :github, info: info, uid: uid}) do @@ -88,14 +96,15 @@ defmodule BrightWeb.AuthController do "getting params_from_ueberauth provider: :patreon. info=#{inspect(info)}, uid=#{inspect(uid)}, user=#{inspect(user)}" ) - currently_entitled_tiers = Bright.PatreonEntitlements.extract_tiers(user) - Logger.debug("currently_entitled_tiers=#{currently_entitled_tiers}") - patron_tier = Bright.TierMapper.largest_tier_for_platform("patreon", currently_entitled_tiers) + currently_entitled_tiers = Bright.Patrons.PatreonEntitlements.extract_tiers(user) + Logger.debug("currently_entitled_tiers=#{inspect(currently_entitled_tiers)}") + + patron_tier = + TierMapper.largest_tier_for_platform("patreon", currently_entitled_tiers) + + Logger.debug(">>>> computed patron_tier=#{inspect(patron_tier)}") %{ - name: info.name, - handle: info.nickname, - patreon_handle: info.nickname, patreon_id: uid, patron_tier: patron_tier } @@ -129,7 +138,7 @@ defmodule BrightWeb.AuthController do user = user_id && Users.get_user!(user_id) Logger.debug( - "fetch_current_user attempting to get user. user_id=#{user_id} user=#{inspect(user)}" + "fetch_current_user attempting to get user. user_id=#{inspect(user_id)} user=#{inspect(user)}" ) assign(conn, :current_user, user) @@ -211,6 +220,40 @@ defmodule BrightWeb.AuthController do end end + def require_patron_tier_1(conn, _), do: require_patron_tier(conn, 1) + def require_patron_tier_2(conn, _), do: require_patron_tier(conn, 2) + def require_patron_tier_3(conn, _), do: require_patron_tier(conn, 3) + + @doc """ + Ensures the user has a patron tier of `tier_level` or higher, + or has the role of "admin". + + Redirects users who don't meet the requirement. + """ + def require_patron_tier(conn, tier_level) when is_integer(tier_level) do + user = conn.assigns[:current_user] || %{} + + Logger.debug( + "require_patron_tier with conn=#{inspect(conn)} and tier_level=#{inspect(tier_level)} and user=#{inspect(user)}" + ) + + case user do + %{role: "admin"} -> + conn + + %{patron_tier: patron_tier} when is_integer(patron_tier) and patron_tier >= tier_level -> + conn + + _ -> + user_return_to = conn.assigns[:user_return_to] || "/" + + conn + |> put_flash(:error, "This route is for tier #{tier_level} or higher.") + |> redirect(to: user_return_to) + |> halt() + end + end + @doc """ Used for routes that require the user to have admin privs. """ @@ -219,7 +262,11 @@ defmodule BrightWeb.AuthController do %{role: "admin"} -> conn - _ -> + user -> + Logger.debug( + "A USER IS DOING A THING THAT REQUIRES ADMIN USER. BUT WE THINK THEY ARE NOT AN ADMIN. user=#{inspect(user)}" + ) + conn |> put_flash(:error, "Only admins can do that.") |> maybe_store_return_to() diff --git a/apps/bright/lib/bright_web/controllers/page_controller.ex b/apps/bright/lib/bright_web/controllers/page_controller.ex index d6474d8..dabebfd 100644 --- a/apps/bright/lib/bright_web/controllers/page_controller.ex +++ b/apps/bright/lib/bright_web/controllers/page_controller.ex @@ -3,33 +3,34 @@ defmodule BrightWeb.PageController do require Logger + @spec home(Plug.Conn.t(), any()) :: Plug.Conn.t() def home(conn, _params) do # The home page is often custom made, # so skip the default app layout. - # render(conn, :home, layout: false) + # render(conn, :home) # render(conn, "index.html", current_user: get_session(conn, :current_user)) # send_resp(conn, 201, "") conn |> put_status(202) - |> render(:home, layout: false, current_user: get_session(conn, :current_user)) + |> render(:home) # redirect(conn, to: ~p"/redirect_test") # redirect(conn, external: "https://elixir-lang.org/") end def about(conn, _params) do - render(conn, :about, layout: false) + render(conn, :about) end def api(conn, _params) do - render(conn, :api, layout: false) + render(conn, :api) end def profile(conn, _params) do sesh = get_session(conn, :current_user) Logger.debug("/profile with sesh=#{inspect(sesh)} conn=#{inspect(conn)}") - render(conn, :profile, layout: false) + render(conn, :profile) end def health(conn, _params) do @@ -38,6 +39,6 @@ defmodule BrightWeb.PageController do end def redirect_test(conn, _params) do - render(conn, :home, layout: false) + render(conn, :home) end end diff --git a/apps/bright/lib/bright_web/controllers/page_html/home.html.heex b/apps/bright/lib/bright_web/controllers/page_html/home.html.heex index 1f6e127..1f0f63e 100644 --- a/apps/bright/lib/bright_web/controllers/page_html/home.html.heex +++ b/apps/bright/lib/bright_web/controllers/page_html/home.html.heex @@ -20,7 +20,7 @@

- Streams Archive + <.link class="mt-5 button is-primary" href={~p"/streams"}>Streams Archive diff --git a/apps/bright/lib/bright_web/controllers/page_html/profile.html.heex b/apps/bright/lib/bright_web/controllers/page_html/profile.html.heex index 09261b2..22c1119 100644 --- a/apps/bright/lib/bright_web/controllers/page_html/profile.html.heex +++ b/apps/bright/lib/bright_web/controllers/page_html/profile.html.heex @@ -30,8 +30,8 @@

Futureporn User {@current_user.id}

n uploads

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus nec - iaculis mauris. @bulmaio. #css - #responsive + iaculis mauris. <.link>@bulmaio. <.link href="#">#css + <.link href="#">#responsive
diff --git a/apps/bright/lib/bright_web/controllers/torrent_html/summary.html.heex b/apps/bright/lib/bright_web/controllers/torrent_html/summary.html.heex index d978269..f9ccc31 100644 --- a/apps/bright/lib/bright_web/controllers/torrent_html/summary.html.heex +++ b/apps/bright/lib/bright_web/controllers/torrent_html/summary.html.heex @@ -2,19 +2,19 @@
<%= if @torrent && @torrent.magnet do %> - <.icon name="magnet" class="icon" /> + <.link target="_blank" href={@torrent.magnet}><.icon name="magnet" class="icon" /> <% end %>
<%= if @torrent && @torrent.cdn_url do %> - + <.link target="_blank" href={@torrent.cdn_url} class="button"> <.icon name="download" /> Download - + <% end %>
diff --git a/apps/bright/lib/bright_web/controllers/vod_html/show.html.heex b/apps/bright/lib/bright_web/controllers/vod_html/show.html.heex index 5eee13e..3d1cd99 100644 --- a/apps/bright/lib/bright_web/controllers/vod_html/show.html.heex +++ b/apps/bright/lib/bright_web/controllers/vod_html/show.html.heex @@ -25,7 +25,9 @@ >

To view this video please enable JavaScript, and consider upgrading to a web browser that - supports HTML5 video + <.link href="http://videojs.com/html5-video-support/" target="_blank"> + supports HTML5 video +

<% else %> @@ -60,7 +62,7 @@ <:item title="Torrent"> <:item title="Source VOD File"> <%= if @vod.s3_cdn_url do %> - - + <% end %> <:item title="HLS Playlist URL">{@vod.playlist_url} diff --git a/apps/bright/lib/bright_web/controllers/vtuber_controller.ex b/apps/bright/lib/bright_web/controllers/vtuber_controller.ex index 37a4ab7..f3adb4f 100644 --- a/apps/bright/lib/bright_web/controllers/vtuber_controller.ex +++ b/apps/bright/lib/bright_web/controllers/vtuber_controller.ex @@ -14,6 +14,7 @@ defmodule BrightWeb.VtuberController do render(conn, :new, changeset: changeset) end + @spec create(Plug.Conn.t(), map()) :: Plug.Conn.t() def create(conn, %{"vtuber" => vtuber_params}) do case Vtubers.create_vtuber(vtuber_params) do {:ok, vtuber} -> diff --git a/apps/bright/lib/bright_web/live/profile/identicon_live.ex b/apps/bright/lib/bright_web/live/profile/identicon_live.ex index cbe847c..3f94118 100644 --- a/apps/bright/lib/bright_web/live/profile/identicon_live.ex +++ b/apps/bright/lib/bright_web/live/profile/identicon_live.ex @@ -4,28 +4,22 @@ defmodule BrightWeb.IdenticonLive do def render(assigns) do ~H""" -
-
-
-
- {raw( - IdenticonSvg.generate(assigns.identicon_seed, 5, :basic, 0.8, 2, - squircle_curvature: 0.8 - ) - )} -
-
-
- -
-
+
+
""" end diff --git a/apps/bright/lib/bright_web/live/profile/profile_live.ex b/apps/bright/lib/bright_web/live/profile/profile_live.ex index a4b368f..91b5c62 100644 --- a/apps/bright/lib/bright_web/live/profile/profile_live.ex +++ b/apps/bright/lib/bright_web/live/profile/profile_live.ex @@ -5,32 +5,134 @@ defmodule BrightWeb.ProfileLive do def render(assigns) do ~H""" + <.header> + Profile + +
-
-
-
-
- <.live_component - module={BrightWeb.IdenticonLive} - id={"user-#{@current_user.id}-identicon"} - identicon_seed={@current_user.identicon_seed} - /> +
+
+
+
+
+
+
+ {raw( + IdenticonSvg.generate(@current_user.identicon_seed, 5, :basic, 0.8, 2, + squircle_curvature: 0.8 + ) + )} +
+
+
+

User {@current_user.id}

+

@{@current_user.id}

+
+
+ +
+

Patron Tier

+

{@current_user.patron_tier}

+ +

Role

+

{@current_user.role}

+
- -
-

User {@current_user.id}

- -

Patron Tier

-

{@current_user.patron_tier}

-

- Change your patron tier at - patreon.com/CJ_Clippy -

+
+
+
+
+

Settings

+
+
+
+ <.live_component + module={BrightWeb.IdenticonLive} + id={"user-#{@current_user.id}-identicon"} + identicon_seed={@current_user.identicon_seed} + /> +
+
+ +
+
+

Care to upgrade your Patron Tier?

+
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FeatureTier 0Tier 1Tier 2
Viewing
RSS
API
Patron List
Uploads
Content Adding
Sponsored Link
+
+ +
+
+

Thank you for your support!

+

+ Change your patron tier at + <.external_link target="_blank" href="https://patreon.com/CJ_Clippy"> + patreon.com/CJ_Clippy + +

+
+
""" end diff --git a/apps/bright/lib/bright_web/router.ex b/apps/bright/lib/bright_web/router.ex index b8843b6..7585711 100644 --- a/apps/bright/lib/bright_web/router.ex +++ b/apps/bright/lib/bright_web/router.ex @@ -6,7 +6,9 @@ defmodule BrightWeb.Router do import BrightWeb.AuthController, only: [ fetch_current_user: 2, - require_admin_user: 2 + require_admin_user: 2, + require_authenticated_user: 2, + require_patron_tier_1: 2 ] pipeline :browser do @@ -36,7 +38,14 @@ defmodule BrightWeb.Router do post("/:provider/callback", AuthController, :callback) end - ## protected routes + ## tier 1 protected routes + scope "/", BrightWeb do + pipe_through [:browser, :require_authenticated_user, :require_patron_tier_1] + + resources("/vtubers", VtuberController, only: [:new, :create, :edit, :update]) + end + + ## admin protected routes ## this section needs to be above the unprotected routes, ## so routes like "/streams/new" take precedence. scope "/", BrightWeb do @@ -67,31 +76,24 @@ defmodule BrightWeb.Router do # end resources("/vods", VodController, only: [:create, :new, :edit, :update, :delete]) - resources("/vtubers", VtuberController, only: [:create, :new, :edit, :update, :delete]) + resources("/vtubers", VtuberController, only: [:delete]) - get("/tags/new", TagController, :new) - post("/tags", TagController, :create) - get("/tags/:id/edit", TagController, :edit) + resources("/tags", TagController, only: [:new, :create, :edit, :update, :delete]) - resources("/torrents", TorrentController, only: [:create, :new, :edit, :update, :delete]) + resources("/torrents", TorrentController, only: [:new, :create, :edit, :update, :delete]) ## !!! DANGER, platforms must only be writable by admins, (unless we implement SVG sanitizing) ## @todo remove SVGs from the database and instead put them in assets resources("/platforms", PlatformController, only: [:new, :create, :edit, :update, :delete]) oban_dashboard("/oban") - - live_session :authenticated, - on_mount: [{BrightWeb.AuthController, :ensure_authenticated}] do - live("/profile", ProfileLive) - end end + ## tier 0 users can access these routes scope "/", BrightWeb do pipe_through(:browser) get("/", PageController, :home) - get("/patrons", PatronController, :index) get("/about", PageController, :about) get("/goals", PageController, :about) @@ -105,8 +107,7 @@ defmodule BrightWeb.Router do get("/vods/:id", VodController, :show) get("/vods", VodController, :index) - get("/tags", TagController, :index) - get("/tags/:id", TagController, :show) + resources("/tags", TagController, only: [:index, :show]) get("/platforms", PlatformController, :index) get("/platforms/:id", PlatformController, :show) @@ -117,6 +118,11 @@ defmodule BrightWeb.Router do get("/vods", VodController, :index) get("/vods/:id", VodController, :show) end + + live_session :authenticated, + on_mount: [{BrightWeb.AuthController, :ensure_authenticated}] do + live("/profile", ProfileLive) + end end scope "/feeds", BrightWeb do diff --git a/apps/bright/test/bright/patreon_entitlements_test.exs b/apps/bright/test/bright/patreon_entitlements_test.exs index e7d61fa..6f362aa 100644 --- a/apps/bright/test/bright/patreon_entitlements_test.exs +++ b/apps/bright/test/bright/patreon_entitlements_test.exs @@ -1,8 +1,16 @@ defmodule Bright.PatreonEntitlementsTest do use ExUnit.Case - alias Bright.PatreonEntitlements + alias Bright.Patrons.PatreonEntitlements describe "extract_tiers/1" do + test "handles empty included list" do + assert PatreonEntitlements.extract_tiers(%{"included" => []}) == [] + end + + test "handles no included list" do + assert PatreonEntitlements.extract_tiers(%{"data" => []}) == [] + end + test "returns a list of tier IDs when included contains tier items" do response_body = %{ "included" => [ diff --git a/apps/bright/test/bright/tier_mapper_test.exs b/apps/bright/test/bright/tier_mapper_test.exs index ba99377..b89277c 100644 --- a/apps/bright/test/bright/tier_mapper_test.exs +++ b/apps/bright/test/bright/tier_mapper_test.exs @@ -1,64 +1,80 @@ defmodule Bright.TierMapperTest do use ExUnit.Case - alias Bright.TierMapper + alias Bright.Patrons.TierMapper describe "largest_tier_for_platform/2" do test "gets the big big" do - assert TierMapper.largest_tier_for_platform("patreon", ["10620388", "8154170"]) == 1 + assert TierMapper.largest_tier_for_platform("patreon", ["10620388", "8154170"]) === 1 + end + + test "handles nil list" do + assert TierMapper.largest_tier_for_platform("patreon", nil) === 0 + end + + test "handles platform with empty list" do + assert TierMapper.largest_tier_for_platform("patreon", []) === 0 + end + + test "handles nil platform" do + assert TierMapper.largest_tier_for_platform(nil) === 0 + end + + test "handles empty list" do + assert TierMapper.largest_tier_for_platform([]) === 0 end end describe "get_tier_number/2" do test "maps Patreon 'everyone' tier to 0" do - assert TierMapper.get_tier_number("patreon", "-1") == 0 + assert TierMapper.get_tier_number("patreon", "-1") === 0 end test "maps Patreon 'free' tier to 0" do - assert TierMapper.get_tier_number("patreon", "10620388") == 0 + assert TierMapper.get_tier_number("patreon", "10620388") === 0 end test "maps Patreon 'archiveSupporter' tier to 1" do - assert TierMapper.get_tier_number("patreon", "8154170") == 1 + assert TierMapper.get_tier_number("patreon", "8154170") === 1 end test "maps Patreon 'stealthSupporter' tier to 1" do - assert TierMapper.get_tier_number("patreon", "9561793") == 1 + assert TierMapper.get_tier_number("patreon", "9561793") === 1 end test "maps Patreon 'tuneItUp' tier to 2" do - assert TierMapper.get_tier_number("patreon", "9184994") == 2 + assert TierMapper.get_tier_number("patreon", "9184994") === 2 end test "maps Patreon 'maxQ' tier to 2" do - assert TierMapper.get_tier_number("patreon", "22529959") == 2 + assert TierMapper.get_tier_number("patreon", "22529959") === 2 end test "maps Patreon 'archiveCollector' tier to 3" do - assert TierMapper.get_tier_number("patreon", "8154171") == 3 + assert TierMapper.get_tier_number("patreon", "8154171") === 3 end test "maps Patreon 'advancedArchiveSupporter' tier to 3" do - assert TierMapper.get_tier_number("patreon", "8686045") == 3 + assert TierMapper.get_tier_number("patreon", "8686045") === 3 end test "maps Patreon 'quantumSupporter' tier to 4" do - assert TierMapper.get_tier_number("patreon", "8694826") == 4 + assert TierMapper.get_tier_number("patreon", "8694826") === 4 end test "maps Patreon 'sneakyQuantumSupporter' tier to 4" do - assert TierMapper.get_tier_number("patreon", "9560538") == 4 + assert TierMapper.get_tier_number("patreon", "9560538") === 4 end test "maps Patreon 'luberPlusPlus' tier to 5" do - assert TierMapper.get_tier_number("patreon", "8686022") == 5 + assert TierMapper.get_tier_number("patreon", "8686022") === 5 end test "returns 0 for unknown Patreon tier" do - assert TierMapper.get_tier_number("patreon", "99999999") == 0 + assert TierMapper.get_tier_number("patreon", "99999999") === 0 end test "returns 0 for non-Patreon platforms" do - assert TierMapper.get_tier_number("someotherplatform", "10620388") == 0 + assert TierMapper.get_tier_number("someotherplatform", "10620388") === 0 end end end diff --git a/apps/bright/test/bright_web/controllers/auth_controller_test.exs b/apps/bright/test/bright_web/controllers/auth_controller_test.exs index b5f2242..3234169 100644 --- a/apps/bright/test/bright_web/controllers/auth_controller_test.exs +++ b/apps/bright/test/bright_web/controllers/auth_controller_test.exs @@ -1,46 +1,193 @@ defmodule BrightWeb.AuthControllerTest do use BrightWeb.ConnCase alias Bright.Users + alias BrightWeb.AuthController + require Logger + + @patron_tier_1_required_error "This route is for tier 1 or higher." + @patron_tier_2_required_error "This route is for tier 2 or higher." + @patron_tier_3_required_error "This route is for tier 3 or higher." describe "patreon auth" do - test "successful auth on existing user signs you in", %{conn: conn} do + test "successful auth on existing user sets the session", %{conn: conn} do + Logger.debug("successful auth on existing user sets the session") user = Bright.Repo.insert!(%Bright.Users.User{patreon_id: 1234}) conn = conn - |> assign(:ueberauth_auth, %{provider: :patreon, info: %{nickname: "joeblow"}}) + |> assign(:ueberauth_auth, %{ + provider: :patreon, + info: %{nickname: "joeblow"}, + uid: 1234, + extra: %{raw_info: %{user: %{}}} + }) |> get("/auth/patreon/callback") - assert redirected_to(conn) == Routes.home_path(conn, :show) assert get_session(conn, "id") == user.id end test "successful auth on new user sends you to profile", %{conn: conn} do + Logger.debug("successful auth on new user sends you to profile") + conn = conn |> assign(:ueberauth_auth, %{ provider: :patreon, info: %{name: "Joe Blow", handle: "joeblow"}, - uid: 1234 + uid: 4321, + extra: %{raw_info: %{user: %{}}} }) |> get("/auth/patreon/callback") - assert redirected_to(conn) == - Routes.user_path(conn, :profile, %{ - name: "Joe Blow", - handle: "joeblow", - patreon_handle: "joeblow" - }) + assert redirected_to(conn) == ~p"/profile" end - test "failed auth doesn't sign you in", %{conn: conn} do + test "failed auth doesn't set the session", %{conn: conn} do + Logger.debug("failed auth doesn't put a user id in the session") + + auth = %Ueberauth.Failure{ + errors: [%Ueberauth.Failure.Error{message: "simulated failure"}], + provider: :patreon, + strategy: Ueberauth.Strategy.Patreon + } + conn = conn - |> assign(:ueberauth_failure, %{}) + |> bypass_through(BrightWeb.Router, :browser) + |> assign(:ueberauth_failure, auth) + |> get("/auth/patreon/callback") + |> BrightWeb.AuthController.callback(%{}) + + assert conn.status == 302 + assert Phoenix.Flash.get(conn.assigns.flash, :error) =~ "simulated failure" + assert get_session(conn, :id) == nil + end + + test "user stays logged in even if they reload the page", %{conn: conn} do + Logger.debug("user stays logged in even if they reload the page") + + user = Bright.Repo.insert!(%Bright.Users.User{patreon_id: 1234}) + + # Simulate login + conn = + conn + |> assign(:ueberauth_auth, %{ + provider: :patreon, + info: %{nickname: "joeblow"}, + uid: 1234, + extra: %{raw_info: %{user: %{}}} + }) |> get("/auth/patreon/callback") - assert conn.status == 200 - assert get_session(conn, "id") == nil + # Verify user is logged in + assert get_session(conn, "id") == user.id + + # Simulate a page reload (new GET request) + conn = get(conn, "/profile") + + # Verify the session is still active + assert get_session(conn, "id") == user.id + end + end + + describe "require_patron_tier/2" do + test "allows access to admins", %{conn: conn} do + conn = + conn + |> bypass_through(BrightWeb.Router, :browser) + |> get("/") + |> assign(:current_user, %{patron_tier: 0, role: "admin"}) + |> AuthController.require_patron_tier(3) + + refute conn.halted + assert Phoenix.Flash.get(conn.assigns.flash, :error) == nil + end + + test "allows access when patron tier is higher than required", %{conn: conn} do + # Setup conn with a user that has higher patron tier + conn = + conn + |> bypass_through(BrightWeb.Router, :browser) + |> get("/") + |> assign(:current_user, %{patron_tier: 3}) + # Call the plug with a lower tier requirement + |> AuthController.require_patron_tier(2) + + # Assert the connection wasn't halted or redirected + refute conn.halted + end + + test "allows access when patron tier equals required tier", %{conn: conn} do + conn = + conn + |> bypass_through(BrightWeb.Router, :browser) + |> assign(:current_user, %{patron_tier: 2}) + |> AuthController.require_patron_tier(2) + + refute conn.halted + end + + test "redirects when patron tier is lower than required", %{conn: conn} do + conn = + conn + |> bypass_through(BrightWeb.Router, :browser) + |> assign(:current_user, %{patron_tier: 1}) + |> get("/") + |> AuthController.require_patron_tier(2) + + assert conn.halted + assert redirected_to(conn) == "/" + + assert Phoenix.Flash.get(conn.assigns.flash, :error) == + "This route is for tier 2 or higher." + end + + test "redirects when user has no patron tier", %{conn: conn} do + conn = + conn + |> bypass_through(BrightWeb.Router, :browser) + |> assign(:current_user, %{}) + |> get("/") + |> AuthController.require_patron_tier(2) + + assert conn.halted + assert redirected_to(conn) == "/" + + assert Phoenix.Flash.get(conn.assigns.flash, :error) == + "This route is for tier 2 or higher." + end + + @tag :unit + test "redirects when no current user exists", %{conn: conn} do + conn = + conn + |> bypass_through(BrightWeb.Router, :browser) + |> assign(:current_user, %{}) + |> get("/") + |> AuthController.require_patron_tier(2) + + assert conn.halted + assert redirected_to(conn) == "/" + + assert Phoenix.Flash.get(conn.assigns.flash, :error) == + "This route is for tier 2 or higher." + end + + @tag :unit + test "respects custom return path", %{conn: conn} do + conn = + conn + |> bypass_through(BrightWeb.Router, :browser) + |> assign(:current_user, %{patron_tier: 1}) + |> assign(:user_return_to, "/custom/path") + |> get("/") + |> AuthController.require_patron_tier(2) + + assert conn.halted + assert redirected_to(conn) == "/custom/path" + + assert Phoenix.Flash.get(conn.assigns.flash, :error) == + "This route is for tier 2 or higher." end end end diff --git a/apps/bright/test/bright_web/controllers/page_controller_test.exs b/apps/bright/test/bright_web/controllers/page_controller_test.exs index 49c3a1d..a2a9d3b 100644 --- a/apps/bright/test/bright_web/controllers/page_controller_test.exs +++ b/apps/bright/test/bright_web/controllers/page_controller_test.exs @@ -3,6 +3,6 @@ defmodule BrightWeb.PageControllerTest do test "GET /", %{conn: conn} do conn = get(conn, ~p"/") - assert html_response(conn, 200) =~ "Futureporn.net" + assert html_response(conn, 202) =~ "Futureporn.net" end end diff --git a/apps/bright/test/bright_web/controllers/platform_controller_test.exs b/apps/bright/test/bright_web/controllers/platform_controller_test.exs index 40ff8e9..84216dc 100644 --- a/apps/bright/test/bright_web/controllers/platform_controller_test.exs +++ b/apps/bright/test/bright_web/controllers/platform_controller_test.exs @@ -15,6 +15,8 @@ defmodule BrightWeb.PlatformControllerTest do end describe "new platform" do + setup [:create_admin] + test "renders form", %{conn: conn} do conn = get(conn, ~p"/platforms/new") assert html_response(conn, 200) =~ "New Platform" @@ -22,6 +24,8 @@ defmodule BrightWeb.PlatformControllerTest do end describe "create platform" do + setup [:create_admin] + test "redirects to show when data is valid", %{conn: conn} do conn = post(conn, ~p"/platforms", platform: @create_attrs) @@ -39,7 +43,7 @@ defmodule BrightWeb.PlatformControllerTest do end describe "edit platform" do - setup [:create_platform] + setup [:create_platform, :create_admin] test "renders form for editing chosen platform", %{conn: conn, platform: platform} do conn = get(conn, ~p"/platforms/#{platform}/edit") @@ -48,7 +52,7 @@ defmodule BrightWeb.PlatformControllerTest do end describe "update platform" do - setup [:create_platform] + setup [:create_platform, :create_admin] test "redirects when data is valid", %{conn: conn, platform: platform} do conn = put(conn, ~p"/platforms/#{platform}", platform: @update_attrs) @@ -65,7 +69,7 @@ defmodule BrightWeb.PlatformControllerTest do end describe "delete platform" do - setup [:create_platform] + setup [:create_platform, :create_admin] test "deletes chosen platform", %{conn: conn, platform: platform} do conn = delete(conn, ~p"/platforms/#{platform}") diff --git a/apps/bright/test/bright_web/controllers/stream_controller_test.exs b/apps/bright/test/bright_web/controllers/stream_controller_test.exs index 63859cc..e0dee13 100644 --- a/apps/bright/test/bright_web/controllers/stream_controller_test.exs +++ b/apps/bright/test/bright_web/controllers/stream_controller_test.exs @@ -2,6 +2,7 @@ defmodule BrightWeb.StreamControllerTest do use BrightWeb.ConnCase import Bright.StreamsFixtures + require Logger @create_attrs %{date: ~U[2024-12-28 03:31:00Z], title: "some title", notes: "some notes"} @update_attrs %{ @@ -19,13 +20,17 @@ defmodule BrightWeb.StreamControllerTest do end describe "new stream" do + setup [:create_admin] + test "renders form", %{conn: conn} do conn = get(conn, ~p"/streams/new") assert html_response(conn, 200) =~ "New Stream" end end - describe "create stream" do + describe "admin create stream" do + setup [:create_admin] + test "redirects to show when data is valid", %{conn: conn} do conn = post(conn, ~p"/streams", stream: @create_attrs) @@ -38,12 +43,13 @@ defmodule BrightWeb.StreamControllerTest do test "renders errors when data is invalid", %{conn: conn} do conn = post(conn, ~p"/streams", stream: @invalid_attrs) + assert Phoenix.Flash.get(conn.assigns.flash, :error) == nil assert html_response(conn, 200) =~ "New Stream" end end describe "edit stream" do - setup [:create_stream] + setup [:create_stream, :create_admin] test "renders form for editing chosen stream", %{conn: conn, stream: stream} do conn = get(conn, ~p"/streams/#{stream}/edit") @@ -51,8 +57,8 @@ defmodule BrightWeb.StreamControllerTest do end end - describe "update stream" do - setup [:create_stream] + describe "admin update stream" do + setup [:create_stream, :create_admin] test "redirects when data is valid", %{conn: conn, stream: stream} do conn = put(conn, ~p"/streams/#{stream}", stream: @update_attrs) @@ -64,12 +70,13 @@ defmodule BrightWeb.StreamControllerTest do test "renders errors when data is invalid", %{conn: conn, stream: stream} do conn = put(conn, ~p"/streams/#{stream}", stream: @invalid_attrs) + Logger.debug("renders errors when data is invalid. conn=#{inspect(conn)}") assert html_response(conn, 200) =~ "Edit Stream" end end - describe "delete stream" do - setup [:create_stream] + describe "admin delete stream" do + setup [:create_stream, :create_admin] test "deletes chosen stream", %{conn: conn, stream: stream} do conn = delete(conn, ~p"/streams/#{stream}") diff --git a/apps/bright/test/bright_web/controllers/tag_controller_test.exs b/apps/bright/test/bright_web/controllers/tag_controller_test.exs index 1536432..71c1fcb 100644 --- a/apps/bright/test/bright_web/controllers/tag_controller_test.exs +++ b/apps/bright/test/bright_web/controllers/tag_controller_test.exs @@ -2,10 +2,11 @@ defmodule BrightWeb.TagControllerTest do use BrightWeb.ConnCase import Bright.TagsFixtures + require Logger @create_attrs %{name: "some name"} @update_attrs %{name: "some updated name"} - @invalid_attrs %{name: nil} + @invalid_attrs %{name: 3} describe "index" do test "lists all tags", %{conn: conn} do @@ -15,6 +16,8 @@ defmodule BrightWeb.TagControllerTest do end describe "new tag" do + setup :create_admin + test "renders form", %{conn: conn} do conn = get(conn, ~p"/tags/new") assert html_response(conn, 200) =~ "New Tag" @@ -22,6 +25,8 @@ defmodule BrightWeb.TagControllerTest do end describe "create tag" do + setup :create_admin + test "redirects to show when data is valid", %{conn: conn} do conn = post(conn, ~p"/tags", tag: @create_attrs) @@ -39,7 +44,7 @@ defmodule BrightWeb.TagControllerTest do end describe "edit tag" do - setup [:create_tag] + setup [:create_tag, :create_admin] test "renders form for editing chosen tag", %{conn: conn, tag: tag} do conn = get(conn, ~p"/tags/#{tag}/edit") @@ -48,10 +53,11 @@ defmodule BrightWeb.TagControllerTest do end describe "update tag" do - setup [:create_tag] + setup [:create_tag, :create_admin] test "redirects when data is valid", %{conn: conn, tag: tag} do conn = put(conn, ~p"/tags/#{tag}", tag: @update_attrs) + Logger.debug("tag update redirect conn=#{inspect(conn)}") assert redirected_to(conn) == ~p"/tags/#{tag}" conn = get(conn, ~p"/tags/#{tag}") @@ -65,7 +71,7 @@ defmodule BrightWeb.TagControllerTest do end describe "delete tag" do - setup [:create_tag] + setup [:create_tag, :create_admin] test "deletes chosen tag", %{conn: conn, tag: tag} do conn = delete(conn, ~p"/tags/#{tag}") diff --git a/apps/bright/test/bright_web/controllers/torrent_controller_test.exs b/apps/bright/test/bright_web/controllers/torrent_controller_test.exs index a0bdf7e..31b7ed4 100644 --- a/apps/bright/test/bright_web/controllers/torrent_controller_test.exs +++ b/apps/bright/test/bright_web/controllers/torrent_controller_test.exs @@ -19,12 +19,14 @@ defmodule BrightWeb.TorrentControllerTest do describe "index" do test "lists all torrent", %{conn: conn} do - conn = get(conn, ~p"/torrent") + conn = get(conn, ~p"/torrents") assert html_response(conn, 200) =~ "Listing Torrent" end end describe "new torrent" do + setup [:create_admin] + test "renders form", %{conn: conn} do conn = get(conn, ~p"/torrents/new") assert html_response(conn, 200) =~ "New Torrent" @@ -32,8 +34,10 @@ defmodule BrightWeb.TorrentControllerTest do end describe "create torrent" do + setup [:create_admin] + test "redirects to show when data is valid", %{conn: conn} do - conn = post(conn, ~p"/torrent", torrent: @create_attrs) + conn = post(conn, ~p"/torrents", torrent: @create_attrs) assert %{id: id} = redirected_params(conn) assert redirected_to(conn) == ~p"/torrents/#{id}" @@ -49,7 +53,7 @@ defmodule BrightWeb.TorrentControllerTest do end describe "edit torrent" do - setup [:create_torrent] + setup [:create_torrent, :create_admin] test "renders form for editing chosen torrent", %{conn: conn, torrent: torrent} do conn = get(conn, ~p"/torrents/#{torrent}/edit") @@ -58,7 +62,7 @@ defmodule BrightWeb.TorrentControllerTest do end describe "update torrent" do - setup [:create_torrent] + setup [:create_torrent, :create_admin] test "redirects when data is valid", %{conn: conn, torrent: torrent} do conn = put(conn, ~p"/torrents/#{torrent}", torrent: @update_attrs) @@ -75,11 +79,11 @@ defmodule BrightWeb.TorrentControllerTest do end describe "delete torrent" do - setup [:create_torrent] + setup [:create_torrent, :create_admin] test "deletes chosen torrent", %{conn: conn, torrent: torrent} do conn = delete(conn, ~p"/torrents/#{torrent}") - assert redirected_to(conn) == ~p"/torrent" + assert redirected_to(conn) == ~p"/torrents" assert_error_sent 404, fn -> get(conn, ~p"/torrents/#{torrent}") diff --git a/apps/bright/test/bright_web/controllers/url_controller_test.exs b/apps/bright/test/bright_web/controllers/url_controller_test.exs deleted file mode 100644 index c86c135..0000000 --- a/apps/bright/test/bright_web/controllers/url_controller_test.exs +++ /dev/null @@ -1,88 +0,0 @@ -defmodule BrightWeb.UrlControllerTest do - use BrightWeb.ConnCase - - import Bright.UrlsFixtures - - alias Bright.Urls.Url - - @create_attrs %{ - link: "some link", - title: "some title" - } - @update_attrs %{ - link: "some updated link", - title: "some updated title" - } - @invalid_attrs %{link: nil, title: nil} - - setup %{conn: conn} do - {:ok, conn: put_req_header(conn, "accept", "application/json")} - end - - describe "index" do - test "lists all urls", %{conn: conn} do - conn = get(conn, ~p"/api/urls") - assert json_response(conn, 200)["data"] == [] - end - end - - describe "create url" do - test "renders url when data is valid", %{conn: conn} do - conn = post(conn, ~p"/api/urls", url: @create_attrs) - assert %{"id" => id} = json_response(conn, 201)["data"] - - conn = get(conn, ~p"/api/urls/#{id}") - - assert %{ - "id" => ^id, - "link" => "some link", - "title" => "some title" - } = json_response(conn, 200)["data"] - end - - test "renders errors when data is invalid", %{conn: conn} do - conn = post(conn, ~p"/api/urls", url: @invalid_attrs) - assert json_response(conn, 422)["errors"] != %{} - end - end - - describe "update url" do - setup [:create_url] - - test "renders url when data is valid", %{conn: conn, url: %Url{id: id} = url} do - conn = put(conn, ~p"/api/urls/#{url}", url: @update_attrs) - assert %{"id" => ^id} = json_response(conn, 200)["data"] - - conn = get(conn, ~p"/api/urls/#{id}") - - assert %{ - "id" => ^id, - "link" => "some updated link", - "title" => "some updated title" - } = json_response(conn, 200)["data"] - end - - test "renders errors when data is invalid", %{conn: conn, url: url} do - conn = put(conn, ~p"/api/urls/#{url}", url: @invalid_attrs) - assert json_response(conn, 422)["errors"] != %{} - end - end - - describe "delete url" do - setup [:create_url] - - test "deletes chosen url", %{conn: conn, url: url} do - conn = delete(conn, ~p"/api/urls/#{url}") - assert response(conn, 204) - - assert_error_sent 404, fn -> - get(conn, ~p"/api/urls/#{url}") - end - end - end - - defp create_url(_) do - url = url_fixture() - %{url: url} - end -end diff --git a/apps/bright/test/bright_web/controllers/vod_controller_test.exs b/apps/bright/test/bright_web/controllers/vod_controller_test.exs index 297bb11..4a8a2d2 100644 --- a/apps/bright/test/bright_web/controllers/vod_controller_test.exs +++ b/apps/bright/test/bright_web/controllers/vod_controller_test.exs @@ -2,14 +2,13 @@ defmodule BrightWeb.VodControllerTest do use BrightWeb.ConnCase import Bright.StreamsFixtures + require Logger @create_attrs %{ s3_cdn_url: "some s3_cdn_url", s3_upload_id: "some s3_upload_id", s3_key: "some s3_key", s3_bucket: "some s3_bucket", - mux_asset_id: "some mux_asset_id", - mux_playback_id: "some mux_playback_id", torrent: "some torrent" } @update_attrs %{ @@ -22,6 +21,7 @@ defmodule BrightWeb.VodControllerTest do torrent: "some updated torrent" } @invalid_attrs %{ + stream_id: "this is obviously invalid", s3_cdn_url: nil, s3_upload_id: nil, s3_key: nil, @@ -39,6 +39,8 @@ defmodule BrightWeb.VodControllerTest do end describe "new vod" do + setup [:create_admin] + test "renders form", %{conn: conn} do conn = get(conn, ~p"/vods/new") assert html_response(conn, 200) =~ "New Vod" @@ -46,8 +48,12 @@ defmodule BrightWeb.VodControllerTest do end describe "create vod" do + setup [:create_admin] + test "redirects to show when data is valid", %{conn: conn} do - conn = post(conn, ~p"/vods", vod: @create_attrs) + stream = stream_fixture() + conn = post(conn, ~p"/vods", vod: Map.put(@create_attrs, :stream_id, stream.id)) + Logger.debug(">>> redirect conn=#{inspect(conn)}") assert %{id: id} = redirected_params(conn) assert redirected_to(conn) == ~p"/vods/#{id}" @@ -63,7 +69,7 @@ defmodule BrightWeb.VodControllerTest do end describe "edit vod" do - setup [:create_vod] + setup [:create_vod, :create_admin] test "renders form for editing chosen vod", %{conn: conn, vod: vod} do conn = get(conn, ~p"/vods/#{vod}/edit") @@ -72,7 +78,7 @@ defmodule BrightWeb.VodControllerTest do end describe "update vod" do - setup [:create_vod] + setup [:create_vod, :create_admin] test "redirects when data is valid", %{conn: conn, vod: vod} do conn = put(conn, ~p"/vods/#{vod}", vod: @update_attrs) @@ -84,12 +90,21 @@ defmodule BrightWeb.VodControllerTest do test "renders errors when data is invalid", %{conn: conn, vod: vod} do conn = put(conn, ~p"/vods/#{vod}", vod: @invalid_attrs) + + Logger.debug("renders errors... conn=#{inspect(conn)}") + assert html_response(conn, 200) =~ "Edit Vod" + + assert conn.assigns.changeset.valid? == false + # kinda weird that there's no flash error for this + assert Phoenix.Flash.get(conn.assigns.flash, :error) == nil + # kinda weird that Phoenix gives this a 200 rather than 401 + assert html_response(conn, 200) =~ "is invalid" end end describe "delete vod" do - setup [:create_vod] + setup [:create_vod, :create_admin] test "deletes chosen vod", %{conn: conn, vod: vod} do conn = delete(conn, ~p"/vods/#{vod}") @@ -102,7 +117,8 @@ defmodule BrightWeb.VodControllerTest do end defp create_vod(_) do - vod = vod_fixture() + stream = stream_fixture() + vod = vod_fixture(%{stream_id: stream.id}) %{vod: vod} end end diff --git a/apps/bright/test/bright_web/controllers/vtuber_controller_test.exs b/apps/bright/test/bright_web/controllers/vtuber_controller_test.exs index 50efb86..21dbc23 100644 --- a/apps/bright/test/bright_web/controllers/vtuber_controller_test.exs +++ b/apps/bright/test/bright_web/controllers/vtuber_controller_test.exs @@ -7,10 +7,18 @@ defmodule BrightWeb.VtuberControllerTest do import Bright.VtubersFixtures require Logger - @create_attrs %{} + @create_attrs %{ + slug: "testvtuber", + display_name: "test", + theme_color: "#813d9c", + image: "https://example.com/image.jpg" + } @update_attrs %{} - @invalid_attrs %{} + @invalid_attrs %{theme_color: 3} @only_admins_error "Only admins can do that." + @patron_tier_1_required_error "This route is for tier 1 or higher." + @patron_tier_2_required_error "This route is for tier 2 or higher." + @patron_tier_3_required_error "This route is for tier 3 or higher." describe "index" do test "lists all vtubers", %{conn: conn} do @@ -34,12 +42,12 @@ defmodule BrightWeb.VtuberControllerTest do test "redirects to /vtubers", %{conn: conn} do conn = get(conn, ~p"/vtubers/new") assert html_response(conn, 302) =~ "" - assert Phoenix.Flash.get(conn.assigns.flash, :error) == @only_admins_error - assert redirected_to(conn) == ~p"/vtubers" + assert Phoenix.Flash.get(conn.assigns.flash, :error) == @patron_tier_1_required_error + assert redirected_to(conn) == ~p"/" end end - describe "create vtuber" do + describe "create vtuber as admin" do setup [:create_admin] test "redirects to :show when data is valid", %{conn: conn} do @@ -47,11 +55,12 @@ defmodule BrightWeb.VtuberControllerTest do conn = post(conn, ~p"/vtubers", vtuber: @create_attrs) - assert %{id: id} = redirected_params(conn) - assert redirected_to(conn) == ~p"/vtubers/#{id}" + Logger.debug("CREATED conn=#{inspect(conn)}") + assert Phoenix.Flash.get(conn.assigns.flash, :error) == nil + assert Phoenix.Flash.get(conn.assigns.flash, :info) =~ "created successfully." - conn = get(conn, ~p"/vtubers/#{id}") - assert html_response(conn, 200) =~ "Vtuber #{id}" + assert %{id: id} = redirected_params(conn, 302) + assert redirected_to(conn) == ~p"/vtubers/#{id}" end test "renders errors when data is invalid", %{conn: conn, user: user} do @@ -60,6 +69,73 @@ defmodule BrightWeb.VtuberControllerTest do end end + @tag :onlyme + describe "create vtuber as tier 0" do + setup [:create_patron_tier_0_user] + + test "user with patron_tier 0 cannot :new", %{conn: conn} do + conn = get(conn, ~p"/vtubers/new") + assert Phoenix.Flash.get(conn.assigns.flash, :error) == @patron_tier_1_required_error + assert conn.status == 302 + assert redirected_to(conn) == ~p"/" + end + + test "user with patron_tier 0 cannot :create", %{conn: conn} do + conn = post(conn, ~p"/vtubers", vtuber: @create_attrs) + assert conn.status == 302 + assert Phoenix.Flash.get(conn.assigns.flash, :error) == @patron_tier_1_required_error + assert redirected_to(conn) == ~p"/" + end + end + + describe "create vtuber as tier 1" do + setup [:create_patron_tier_1_user] + + test "authorized to :new", %{conn: conn} do + conn = get(conn, ~p"/vtubers/new") + assert conn.status == 200 + end + + test "authorized to :create", %{conn: conn} do + Logger.debug("conn=#{inspect(conn)}") + conn = post(conn, ~p"/vtubers", vtuber: @create_attrs) + assert Phoenix.Flash.get(conn.assigns.flash, :info) =~ "success" + assert conn.status == 302 + assert redirected_to(conn) =~ "/vtubers/" + end + end + + describe "create vtuber as tier 2" do + setup [:create_patron_tier_2_user] + + test "user with patron_tier 2 can :new", %{conn: conn} do + conn = get(conn, ~p"/vtubers/new") + assert conn.status == 200 + end + + test "user with patron_tier 2 can :create", %{conn: conn} do + conn = post(conn, ~p"/vtubers", vtuber: @create_attrs) + assert conn.status == 302 + assert Phoenix.Flash.get(conn.assigns.flash, :info) =~ "success" + end + end + + describe "create vtuber as tier 3" do + setup [:create_patron_tier_3_user] + + test "user with patron_tier 3 can :new", %{conn: conn} do + conn = get(conn, ~p"/vtubers/new") + assert conn.status == 200 + end + + test "user with patron_tier 3 can :create", %{conn: conn} do + conn = post(conn, ~p"/vtubers", vtuber: @create_attrs) + assert conn.status == 302 + assert Phoenix.Flash.get(conn.assigns.flash, :info) =~ "created" + end + end + + @tag :onlyme describe "edit vtuber as admin" do setup [:create_vtuber, :create_admin] @@ -95,22 +171,35 @@ defmodule BrightWeb.VtuberControllerTest do test "renders errors when data is invalid", %{conn: conn, vtuber: vtuber} do conn = put(conn, ~p"/vtubers/#{vtuber}", vtuber: @invalid_attrs) - assert html_response(conn, 200) =~ "Edit Vtuber" + Logger.debug(">>> conn=#{inspect(conn)}") + assert conn.assigns.changeset.valid? == false + assert Phoenix.Flash.get(conn.assigns.flash, :error) == nil + # kinda weird that Phoenix gives this a 200 rather than 401 + assert html_response(conn, 200) =~ "is invalid" end end - describe "update vtuber as user" do + describe "update vtuber as user (patron_tier 0)" do setup [:create_vtuber, :create_user] - test "redirects when user is not an admin", %{conn: conn, user: user, vtuber: vtuber} do + # test "redirects when data is valid", %{conn: conn, user: user, vtuber: vtuber} do + # Logger.debug(">>. redirect conn=#{inspect(conn)}, user=#{inspect(user)}") + # conn = put(conn, ~p"/vtubers/#{vtuber}", vtuber: @update_attrs) + # assert redirected_to(conn) == ~p"/vtubers/#{vtuber}" + + # conn = get(conn, ~p"/vtubers/#{vtuber}") + # assert html_response(conn, 200) + # end + + test "redirects", %{conn: conn, user: user, vtuber: vtuber} do Logger.debug("hello here is user=#{inspect(user)}") - conn = get(conn, ~p"/vtubers/#{vtuber}") + conn = put(conn, ~p"/vtubers/#{vtuber}", vtuber: @update_attrs) assert conn.halted - assert redirected_to(conn) == ~p"/vtubers/#{vtuber}" + assert redirected_to(conn) =~ "/" - assert Phoenix.Flash.get(conn.assigns.flash, :error) == @only_admins_error + assert Phoenix.Flash.get(conn.assigns.flash, :error) == @patron_tier_1_required_error end end diff --git a/apps/bright/test/support/conn_case.ex b/apps/bright/test/support/conn_case.ex index 7f5ca5d..f92b0ef 100644 --- a/apps/bright/test/support/conn_case.ex +++ b/apps/bright/test/support/conn_case.ex @@ -16,6 +16,7 @@ defmodule BrightWeb.ConnCase do """ import Bright.UsersFixtures + require Logger use ExUnit.CaseTemplate @@ -50,8 +51,32 @@ defmodule BrightWeb.ConnCase do {:ok, user: user, conn: conn} end - def create_user(%{conn: conn}) do - user = user_fixture(%{role: "user", patreon_id: 4321}) + def create_user(opts) do + create_patron_tier_0_user(opts) + end + + def create_patron_tier_0_user(%{conn: conn}) do + user = user_fixture(%{role: "user", patreon_id: 4321, patron_tier: 0}) + conn = login_user(conn, user) + {:ok, user: user, conn: conn} + end + + def create_patron_tier_1_user(%{conn: conn}) do + user = user_fixture(%{role: "user", patreon_id: 4321, patron_tier: 1}) + Logger.debug("user just created. user=#{inspect(user)}") + conn = login_user(conn, user) + Logger.debug("create_patron_tier_1_user just finished with conn=#{inspect(conn)}") + {:ok, user: user, conn: conn} + end + + def create_patron_tier_2_user(%{conn: conn}) do + user = user_fixture(%{role: "user", patreon_id: 4321, patron_tier: 2}) + conn = login_user(conn, user) + {:ok, user: user, conn: conn} + end + + def create_patron_tier_3_user(%{conn: conn}) do + user = user_fixture(%{role: "user", patreon_id: 4321, patron_tier: 3}) conn = login_user(conn, user) {:ok, user: user, conn: conn} end