controllers tests are passing
Some checks failed
ci / build (push) Failing after 4m19s
ci / Tests & Checks (push) Failing after 9m47s

This commit is contained in:
CJ_Clippy 2025-02-12 06:09:01 -08:00
parent aa75c224fc
commit 4225bbdf61
28 changed files with 744 additions and 328 deletions

View File

@ -1,4 +1,4 @@
defmodule Bright.PatreonEntitlements do defmodule Bright.Patrons.PatreonEntitlements do
@spec extract_tiers(map()) :: list(String.t()) @spec extract_tiers(map()) :: list(String.t())
def extract_tiers(response_body) do def extract_tiers(response_body) do
response_body response_body

View File

@ -1,4 +1,4 @@
defmodule Bright.TierMapper do defmodule Bright.Patrons.TierMapper do
@patreon_tiers %{ @patreon_tiers %{
"everyone" => 0, "everyone" => 0,
"free" => 0, "free" => 0,
@ -34,10 +34,16 @@ defmodule Bright.TierMapper do
def get_tier_number(_, _), do: 0 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) 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 tier_ids
|> Enum.map(&get_tier_number(platform, &1)) |> Enum.map(&get_tier_number(platform, &1))
|> Enum.max() |> Enum.max()
end end
@spec largest_tier_for_platform(any(), list()) :: integer()
def largest_tier_for_platform(_, _), do: 0
def largest_tier_for_platform(_), do: 0
end end

View File

@ -35,7 +35,7 @@ defmodule Bright.Users.User do
""" """
def registration_changeset(user, attrs, _opts \\ []) do def registration_changeset(user, attrs, _opts \\ []) do
user user
|> cast(attrs, [:patreon_id, :github_id, :role]) |> cast(attrs, [:patreon_id, :github_id, :role, :patron_tier])
|> validate_provider_id() |> validate_provider_id()
end end

View File

@ -19,6 +19,27 @@ defmodule BrightWeb.CoreComponents do
alias Phoenix.LiveView.JS alias Phoenix.LiveView.JS
@doc """
Renders an external link.
## Example:
```heex
<.external_link href="https://example.com">Visit Example</.external_link>
```
"""
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" />
</.link>
"""
end
@doc """ @doc """
Renders a modal. Renders a modal.

View File

@ -1,4 +1,81 @@
<main class="section"> <div>
<.flash_group flash={@flash} /> <nav class="navbar" role="navigation" aria-label="main navigation">
{@inner_content} <div class="navbar-brand">
</main> <.link href={~p"/"} class="navbar-item">
<h1 class="title">🔞💦 Futureporn.net</h1>
</.link>
<.link
role="button"
class="navbar-burger"
aria-label="menu"
aria-expanded="false"
phx-click={JS.toggle(to: ".navbar-menu")}
>
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
</.link>
</div>
<div class="navbar-menu">
<div class="navbar-start">
<.link href={~p"/streams"} class="navbar-item">
Streams Archive
</.link>
<.link href={~p"/vods"} class="navbar-item">
Vods
</.link>
<.link href={~p"/vtubers"} class="navbar-item">
Vtubers
</.link>
<.link href={~p"/about"} class="navbar-item">
About
</.link>
<!--<.link
href={~p"/patrons"}
class="navbar-item">
Patrons
</.link>
<.link
href={~p"/api"}
class="navbar-item">
API
</.link>-->
</div>
<div class="navbar-end">
<div class="navbar-item">
<.icon name="person-digging" class="is-unclickable" />
<.icon name="hammer" class="is-unclickable" />
</div>
<%= if @current_user do %>
<.link href={~p"/profile"} class="navbar-item">
Profile
</.link>
<.link href={~p"/auth/logout"} method="get" class="navbar-item">
Log out
</.link>
<% else %>
<.link href={~p"/auth/patreon"} method="get" class="navbar-item">
Sign in via Patreon
</.link>
<p>hello</p>
<.link href={~p"/auth/patreon"} class="navbar-item">
Log in
</.link>
<% end %>
</div>
</div>
</nav>
<main class="container">
<.flash_group flash={@flash} />
{@inner_content}
</main>
</div>

View File

@ -28,81 +28,6 @@
</script> </script>
</head> </head>
<body class=""> <body class="">
<nav class="navbar" role="navigation" aria-label="main navigation">
<div class="navbar-brand">
<.link href={~p"/"} class="navbar-item">
<h1 class="title">🔞💦 Futureporn.net</h1>
</.link>
<a
role="button"
class="navbar-burger"
aria-label="menu"
aria-expanded="false"
phx-click={JS.toggle(to: ".navbar-menu")}
>
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
</a>
</div>
<div class="navbar-menu">
<div class="navbar-start">
<.link href={~p"/streams"} class="navbar-item">
Streams Archive
</.link>
<.link href={~p"/vods"} class="navbar-item">
Vods
</.link>
<.link href={~p"/vtubers"} class="navbar-item">
Vtubers
</.link>
<.link href={~p"/about"} class="navbar-item">
About
</.link>
<!--<.link
href={~p"/patrons"}
class="navbar-item">
Patrons
</.link>
<.link
href={~p"/api"}
class="navbar-item">
API
</.link>-->
</div>
<div class="navbar-end">
<div class="navbar-item">
<.icon name="person-digging" class="is-unclickable" />
<.icon name="hammer" class="is-unclickable" />
</div>
<%= if @current_user do %>
<.link href={~p"/profile"} class="navbar-item">
Profile
</.link>
<.link href={~p"/auth/logout"} method="get" class="navbar-item">
Log out
</.link>
<% else %>
<.link href={~p"/auth/patreon"} method="get" class="navbar-item">
Sign in via Patreon
</.link>
<p>hello</p>
<.link href={~p"/auth/patreon"} class="navbar-item">
Log in
</.link>
<% end %>
</div>
</div>
</nav>
{@inner_content} {@inner_content}
</body> </body>
</html> </html>

View File

@ -4,6 +4,7 @@ defmodule BrightWeb.AuthController do
alias Bright.Users alias Bright.Users
alias Bright.Users.User alias Bright.Users.User
alias Bright.Patrons.TierMapper
alias Bright.Repo alias Bright.Repo
require Logger require Logger
@ -66,12 +67,19 @@ defmodule BrightWeb.AuthController do
end end
end end
def callback(conn = %{assigns: %{ueberauth_failure: fails}}, _params) do def callback(
Logger.error(inspect(fails)) 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 conn
|> put_flash(:error, "Something went wrong; please try again.") |> put_flash(:error, "Authentication failed: #{error_messages}")
|> render("new.html", user: nil) |> redirect(to: ~p"/")
end end
defp params_from_ueberauth(%{provider: :github, info: info, uid: uid}) do 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)}" "getting params_from_ueberauth provider: :patreon. info=#{inspect(info)}, uid=#{inspect(uid)}, user=#{inspect(user)}"
) )
currently_entitled_tiers = Bright.PatreonEntitlements.extract_tiers(user) currently_entitled_tiers = Bright.Patrons.PatreonEntitlements.extract_tiers(user)
Logger.debug("currently_entitled_tiers=#{currently_entitled_tiers}") Logger.debug("currently_entitled_tiers=#{inspect(currently_entitled_tiers)}")
patron_tier = Bright.TierMapper.largest_tier_for_platform("patreon", 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, patreon_id: uid,
patron_tier: patron_tier patron_tier: patron_tier
} }
@ -129,7 +138,7 @@ defmodule BrightWeb.AuthController do
user = user_id && Users.get_user!(user_id) user = user_id && Users.get_user!(user_id)
Logger.debug( 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) assign(conn, :current_user, user)
@ -211,6 +220,40 @@ defmodule BrightWeb.AuthController do
end end
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 """ @doc """
Used for routes that require the user to have admin privs. Used for routes that require the user to have admin privs.
""" """
@ -219,7 +262,11 @@ defmodule BrightWeb.AuthController do
%{role: "admin"} -> %{role: "admin"} ->
conn 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 conn
|> put_flash(:error, "Only admins can do that.") |> put_flash(:error, "Only admins can do that.")
|> maybe_store_return_to() |> maybe_store_return_to()

View File

@ -3,33 +3,34 @@ defmodule BrightWeb.PageController do
require Logger require Logger
@spec home(Plug.Conn.t(), any()) :: Plug.Conn.t()
def home(conn, _params) do def home(conn, _params) do
# The home page is often custom made, # The home page is often custom made,
# so skip the default app layout. # 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)) # render(conn, "index.html", current_user: get_session(conn, :current_user))
# send_resp(conn, 201, "") # send_resp(conn, 201, "")
conn conn
|> put_status(202) |> put_status(202)
|> render(:home, layout: false, current_user: get_session(conn, :current_user)) |> render(:home)
# redirect(conn, to: ~p"/redirect_test") # redirect(conn, to: ~p"/redirect_test")
# redirect(conn, external: "https://elixir-lang.org/") # redirect(conn, external: "https://elixir-lang.org/")
end end
def about(conn, _params) do def about(conn, _params) do
render(conn, :about, layout: false) render(conn, :about)
end end
def api(conn, _params) do def api(conn, _params) do
render(conn, :api, layout: false) render(conn, :api)
end end
def profile(conn, _params) do def profile(conn, _params) do
sesh = get_session(conn, :current_user) sesh = get_session(conn, :current_user)
Logger.debug("/profile with sesh=#{inspect(sesh)} conn=#{inspect(conn)}") Logger.debug("/profile with sesh=#{inspect(sesh)} conn=#{inspect(conn)}")
render(conn, :profile, layout: false) render(conn, :profile)
end end
def health(conn, _params) do def health(conn, _params) do
@ -38,6 +39,6 @@ defmodule BrightWeb.PageController do
end end
def redirect_test(conn, _params) do def redirect_test(conn, _params) do
render(conn, :home, layout: false) render(conn, :home)
end end
end end

View File

@ -20,7 +20,7 @@
</p> </p>
</i> </i>
<a class="mt-5 button is-primary" href={~p"/streams"}>Streams Archive</a> <.link class="mt-5 button is-primary" href={~p"/streams"}>Streams Archive</.link>
</div> </div>
</div> </div>
</div> </div>

View File

@ -30,8 +30,8 @@
<p class="subtitle is-6">Futureporn User {@current_user.id}</p> <p class="subtitle is-6">Futureporn User {@current_user.id}</p>
<p class="subtitle is-6"><i>n</i> uploads</p> <p class="subtitle is-6"><i>n</i> uploads</p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus nec Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus nec
iaculis mauris. <a>@bulmaio</a>. <a href="#">#css</a> iaculis mauris. <.link>@bulmaio</.link>. <.link href="#">#css</.link>
<a href="#">#responsive</a> <.link href="#">#responsive</.link>
<br /> <br />
<time datetime="2016-1-1">11:09 PM - 1 Jan 2016</time> <time datetime="2016-1-1">11:09 PM - 1 Jan 2016</time>
</div> </div>

View File

@ -2,19 +2,19 @@
<div class="level-left"> <div class="level-left">
<div class="level-item"> <div class="level-item">
<%= if @torrent && @torrent.magnet do %> <%= if @torrent && @torrent.magnet do %>
<a target="_blank" href={@torrent.magnet}><.icon name="magnet" class="icon" /></a> <.link target="_blank" href={@torrent.magnet}><.icon name="magnet" class="icon" /></.link>
<% end %> <% end %>
</div> </div>
<div class="level-item"> <div class="level-item">
<%= if @torrent && @torrent.cdn_url do %> <%= if @torrent && @torrent.cdn_url do %>
<a target="_blank" href={@torrent.cdn_url} class="button"> <.link target="_blank" href={@torrent.cdn_url} class="button">
<span class="icon-text"> <span class="icon-text">
<span class="icon"> <span class="icon">
<.icon name="download" /> <.icon name="download" />
</span> </span>
<span>Download</span> <span>Download</span>
</span> </span>
</a> </.link>
<% end %> <% end %>
</div> </div>
<div class="level-item"> <div class="level-item">

View File

@ -25,7 +25,9 @@
> >
<p class="vjs-no-js"> <p class="vjs-no-js">
To view this video please enable JavaScript, and consider upgrading to a web browser that To view this video please enable JavaScript, and consider upgrading to a web browser that
<a href="http://videojs.com/html5-video-support/" target="_blank">supports HTML5 video</a> <.link href="http://videojs.com/html5-video-support/" target="_blank">
supports HTML5 video
</.link>
</p> </p>
</video> </video>
<% else %> <% else %>
@ -60,7 +62,7 @@
<:item title="Torrent"><TorrentHTML.summary torrent={@vod.torrent} /></:item> <:item title="Torrent"><TorrentHTML.summary torrent={@vod.torrent} /></:item>
<:item title="Source VOD File"> <:item title="Source VOD File">
<%= if @vod.s3_cdn_url do %> <%= if @vod.s3_cdn_url do %>
<a <.link
class="button is-secondary" class="button is-secondary"
href={@vod.s3_cdn_url} href={@vod.s3_cdn_url}
download={Path.basename(@vod.s3_cdn_url)} download={Path.basename(@vod.s3_cdn_url)}
@ -73,7 +75,7 @@
Download Download
</span> </span>
</span> </span>
</a> </.link>
<% end %> <% end %>
</:item> </:item>
<:item title="HLS Playlist URL">{@vod.playlist_url}</:item> <:item title="HLS Playlist URL">{@vod.playlist_url}</:item>

View File

@ -14,6 +14,7 @@ defmodule BrightWeb.VtuberController do
render(conn, :new, changeset: changeset) render(conn, :new, changeset: changeset)
end end
@spec create(Plug.Conn.t(), map()) :: Plug.Conn.t()
def create(conn, %{"vtuber" => vtuber_params}) do def create(conn, %{"vtuber" => vtuber_params}) do
case Vtubers.create_vtuber(vtuber_params) do case Vtubers.create_vtuber(vtuber_params) do
{:ok, vtuber} -> {:ok, vtuber} ->

View File

@ -4,28 +4,22 @@ defmodule BrightWeb.IdenticonLive do
def render(assigns) do def render(assigns) do
~H""" ~H"""
<div class="level"> <div>
<div class="level-left"> <button
<div class="level-item"> class="button"
<figure id="identicon" class="image is-48x48"> phx-click={JS.push("randomize", loading: "#identicon")}
{raw( phx-target={@myself}
IdenticonSvg.generate(assigns.identicon_seed, 5, :basic, 0.8, 2, >
squircle_curvature: 0.8 <span class="icon is-small">
) <.icon name="dice" />
)} </span>
</figure> <figure id="identicon" class="icon image is-24x24 mr-2">
</div> {raw(
<div class="level-item"> IdenticonSvg.generate(assigns.identicon_seed, 5, :basic, 0.8, 2, squircle_curvature: 0.8)
<button )}
class="button" </figure>
phx-click={JS.push("randomize", loading: "#identicon")} <span>Randomize Identicon</span>
phx-target={@myself} </button>
>
<.icon name="dice" class="icon" />
<span>Randomize Identicon</span>
</button>
</div>
</div>
</div> </div>
""" """
end end

View File

@ -5,32 +5,134 @@ defmodule BrightWeb.ProfileLive do
def render(assigns) do def render(assigns) do
~H""" ~H"""
<.header>
Profile
</.header>
<section class="section"> <section class="section">
<div class="card"> <div class="columns">
<div class="card-content"> <div class="column is-half">
<div class="media"> <div class="card">
<div class="media-left"> <div class="card-content">
<.live_component <div class="media">
module={BrightWeb.IdenticonLive} <div class="media-left">
id={"user-#{@current_user.id}-identicon"} <figure class="image is-48x48">
identicon_seed={@current_user.identicon_seed} {raw(
/> IdenticonSvg.generate(@current_user.identicon_seed, 5, :basic, 0.8, 2,
squircle_curvature: 0.8
)
)}
</figure>
</div>
<div class="media-content">
<p class="title is-4">User {@current_user.id}</p>
<p class="subtitle is-6">@{@current_user.id}</p>
</div>
</div>
<div class="content">
<p class="title is-5 mt-5">Patron Tier</p>
<p class="tag subtitle is-6 mt-2">{@current_user.patron_tier}</p>
<p class="title is-5 mt-5">Role</p>
<p class="tag subtitle is-6 mt-2">{@current_user.role}</p>
</div>
</div> </div>
</div> </div>
</div>
<div class="content"> <div class="column is-half">
<p class="title is-4">User {@current_user.id}</p> <div class="card">
<header class="card-header">
<p class="title is-5 mt-5">Patron Tier</p> <p class="card-header-title">Settings</p>
<p class="tag subtitle is-6 mt-2">{@current_user.patron_tier}</p> </header>
<p> <div class="content">
Change your patron tier at <div class="card-content">
<a target="_blank" href="https://patreon.com/CJ_Clippy">patreon.com/CJ_Clippy</a> <.live_component
</p> module={BrightWeb.IdenticonLive}
id={"user-#{@current_user.id}-identicon"}
identicon_seed={@current_user.identicon_seed}
/>
</div>
</div>
</div> </div>
</div> </div>
</div> </div>
</section> </section>
<section class="hero">
<div class="hero-body">
<p class="title">Care to upgrade your Patron Tier?</p>
</div>
</section>
<section class="section">
<table class="table is-striped is-hoverable is-fullwidth">
<thead>
<tr>
<th>Feature</th>
<th>Tier 0</th>
<th>Tier 1</th>
<th>Tier 2</th>
</tr>
</thead>
<tbody>
<tr>
<td>Viewing</td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>RSS</td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>API</td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>Patron List</td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>Uploads</td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>Content Adding</td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>Sponsored Link</td>
<td></td>
<td></td>
<td></td>
</tr>
</tbody>
</table>
</section>
<section class="hero mb-5">
<div class="hero-body">
<p class="title">Thank you for your support!</p>
<p class="subtitle is-6">
Change your patron tier at
<.external_link target="_blank" href="https://patreon.com/CJ_Clippy">
patreon.com/CJ_Clippy
</.external_link>
</p>
</div>
</section>
""" """
end end

View File

@ -6,7 +6,9 @@ defmodule BrightWeb.Router do
import BrightWeb.AuthController, import BrightWeb.AuthController,
only: [ only: [
fetch_current_user: 2, fetch_current_user: 2,
require_admin_user: 2 require_admin_user: 2,
require_authenticated_user: 2,
require_patron_tier_1: 2
] ]
pipeline :browser do pipeline :browser do
@ -36,7 +38,14 @@ defmodule BrightWeb.Router do
post("/:provider/callback", AuthController, :callback) post("/:provider/callback", AuthController, :callback)
end 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, ## this section needs to be above the unprotected routes,
## so routes like "/streams/new" take precedence. ## so routes like "/streams/new" take precedence.
scope "/", BrightWeb do scope "/", BrightWeb do
@ -67,31 +76,24 @@ defmodule BrightWeb.Router do
# end # end
resources("/vods", VodController, only: [:create, :new, :edit, :update, :delete]) 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) resources("/tags", TagController, only: [:new, :create, :edit, :update, :delete])
post("/tags", TagController, :create)
get("/tags/:id/edit", TagController, :edit)
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) ## !!! 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 ## @todo remove SVGs from the database and instead put them in assets
resources("/platforms", PlatformController, only: [:new, :create, :edit, :update, :delete]) resources("/platforms", PlatformController, only: [:new, :create, :edit, :update, :delete])
oban_dashboard("/oban") oban_dashboard("/oban")
live_session :authenticated,
on_mount: [{BrightWeb.AuthController, :ensure_authenticated}] do
live("/profile", ProfileLive)
end
end end
## tier 0 users can access these routes
scope "/", BrightWeb do scope "/", BrightWeb do
pipe_through(:browser) pipe_through(:browser)
get("/", PageController, :home) get("/", PageController, :home)
get("/patrons", PatronController, :index) get("/patrons", PatronController, :index)
get("/about", PageController, :about) get("/about", PageController, :about)
get("/goals", PageController, :about) get("/goals", PageController, :about)
@ -105,8 +107,7 @@ defmodule BrightWeb.Router do
get("/vods/:id", VodController, :show) get("/vods/:id", VodController, :show)
get("/vods", VodController, :index) get("/vods", VodController, :index)
get("/tags", TagController, :index) resources("/tags", TagController, only: [:index, :show])
get("/tags/:id", TagController, :show)
get("/platforms", PlatformController, :index) get("/platforms", PlatformController, :index)
get("/platforms/:id", PlatformController, :show) get("/platforms/:id", PlatformController, :show)
@ -117,6 +118,11 @@ defmodule BrightWeb.Router do
get("/vods", VodController, :index) get("/vods", VodController, :index)
get("/vods/:id", VodController, :show) get("/vods/:id", VodController, :show)
end end
live_session :authenticated,
on_mount: [{BrightWeb.AuthController, :ensure_authenticated}] do
live("/profile", ProfileLive)
end
end end
scope "/feeds", BrightWeb do scope "/feeds", BrightWeb do

View File

@ -1,8 +1,16 @@
defmodule Bright.PatreonEntitlementsTest do defmodule Bright.PatreonEntitlementsTest do
use ExUnit.Case use ExUnit.Case
alias Bright.PatreonEntitlements alias Bright.Patrons.PatreonEntitlements
describe "extract_tiers/1" do 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 test "returns a list of tier IDs when included contains tier items" do
response_body = %{ response_body = %{
"included" => [ "included" => [

View File

@ -1,64 +1,80 @@
defmodule Bright.TierMapperTest do defmodule Bright.TierMapperTest do
use ExUnit.Case use ExUnit.Case
alias Bright.TierMapper alias Bright.Patrons.TierMapper
describe "largest_tier_for_platform/2" do describe "largest_tier_for_platform/2" do
test "gets the big big" 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
end end
describe "get_tier_number/2" do describe "get_tier_number/2" do
test "maps Patreon 'everyone' tier to 0" 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 end
test "maps Patreon 'free' tier to 0" do 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 end
test "maps Patreon 'archiveSupporter' tier to 1" do 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 end
test "maps Patreon 'stealthSupporter' tier to 1" do 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 end
test "maps Patreon 'tuneItUp' tier to 2" do 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 end
test "maps Patreon 'maxQ' tier to 2" do 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 end
test "maps Patreon 'archiveCollector' tier to 3" do 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 end
test "maps Patreon 'advancedArchiveSupporter' tier to 3" do 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 end
test "maps Patreon 'quantumSupporter' tier to 4" do 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 end
test "maps Patreon 'sneakyQuantumSupporter' tier to 4" do 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 end
test "maps Patreon 'luberPlusPlus' tier to 5" do 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 end
test "returns 0 for unknown Patreon tier" do 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 end
test "returns 0 for non-Patreon platforms" do 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 end
end end

View File

@ -1,46 +1,193 @@
defmodule BrightWeb.AuthControllerTest do defmodule BrightWeb.AuthControllerTest do
use BrightWeb.ConnCase use BrightWeb.ConnCase
alias Bright.Users 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 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}) user = Bright.Repo.insert!(%Bright.Users.User{patreon_id: 1234})
conn = conn =
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") |> get("/auth/patreon/callback")
assert redirected_to(conn) == Routes.home_path(conn, :show)
assert get_session(conn, "id") == user.id assert get_session(conn, "id") == user.id
end end
test "successful auth on new user sends you to profile", %{conn: conn} do 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 =
conn conn
|> assign(:ueberauth_auth, %{ |> assign(:ueberauth_auth, %{
provider: :patreon, provider: :patreon,
info: %{name: "Joe Blow", handle: "joeblow"}, info: %{name: "Joe Blow", handle: "joeblow"},
uid: 1234 uid: 4321,
extra: %{raw_info: %{user: %{}}}
}) })
|> get("/auth/patreon/callback") |> get("/auth/patreon/callback")
assert redirected_to(conn) == assert redirected_to(conn) == ~p"/profile"
Routes.user_path(conn, :profile, %{
name: "Joe Blow",
handle: "joeblow",
patreon_handle: "joeblow"
})
end 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 =
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") |> get("/auth/patreon/callback")
assert conn.status == 200 # Verify user is logged in
assert get_session(conn, "id") == nil 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 end
end end

View File

@ -3,6 +3,6 @@ defmodule BrightWeb.PageControllerTest do
test "GET /", %{conn: conn} do test "GET /", %{conn: conn} do
conn = get(conn, ~p"/") conn = get(conn, ~p"/")
assert html_response(conn, 200) =~ "Futureporn.net" assert html_response(conn, 202) =~ "Futureporn.net"
end end
end end

View File

@ -15,6 +15,8 @@ defmodule BrightWeb.PlatformControllerTest do
end end
describe "new platform" do describe "new platform" do
setup [:create_admin]
test "renders form", %{conn: conn} do test "renders form", %{conn: conn} do
conn = get(conn, ~p"/platforms/new") conn = get(conn, ~p"/platforms/new")
assert html_response(conn, 200) =~ "New Platform" assert html_response(conn, 200) =~ "New Platform"
@ -22,6 +24,8 @@ defmodule BrightWeb.PlatformControllerTest do
end end
describe "create platform" do describe "create platform" do
setup [:create_admin]
test "redirects to show when data is valid", %{conn: conn} do test "redirects to show when data is valid", %{conn: conn} do
conn = post(conn, ~p"/platforms", platform: @create_attrs) conn = post(conn, ~p"/platforms", platform: @create_attrs)
@ -39,7 +43,7 @@ defmodule BrightWeb.PlatformControllerTest do
end end
describe "edit platform" do describe "edit platform" do
setup [:create_platform] setup [:create_platform, :create_admin]
test "renders form for editing chosen platform", %{conn: conn, platform: platform} do test "renders form for editing chosen platform", %{conn: conn, platform: platform} do
conn = get(conn, ~p"/platforms/#{platform}/edit") conn = get(conn, ~p"/platforms/#{platform}/edit")
@ -48,7 +52,7 @@ defmodule BrightWeb.PlatformControllerTest do
end end
describe "update platform" do describe "update platform" do
setup [:create_platform] setup [:create_platform, :create_admin]
test "redirects when data is valid", %{conn: conn, platform: platform} do test "redirects when data is valid", %{conn: conn, platform: platform} do
conn = put(conn, ~p"/platforms/#{platform}", platform: @update_attrs) conn = put(conn, ~p"/platforms/#{platform}", platform: @update_attrs)
@ -65,7 +69,7 @@ defmodule BrightWeb.PlatformControllerTest do
end end
describe "delete platform" do describe "delete platform" do
setup [:create_platform] setup [:create_platform, :create_admin]
test "deletes chosen platform", %{conn: conn, platform: platform} do test "deletes chosen platform", %{conn: conn, platform: platform} do
conn = delete(conn, ~p"/platforms/#{platform}") conn = delete(conn, ~p"/platforms/#{platform}")

View File

@ -2,6 +2,7 @@ defmodule BrightWeb.StreamControllerTest do
use BrightWeb.ConnCase use BrightWeb.ConnCase
import Bright.StreamsFixtures import Bright.StreamsFixtures
require Logger
@create_attrs %{date: ~U[2024-12-28 03:31:00Z], title: "some title", notes: "some notes"} @create_attrs %{date: ~U[2024-12-28 03:31:00Z], title: "some title", notes: "some notes"}
@update_attrs %{ @update_attrs %{
@ -19,13 +20,17 @@ defmodule BrightWeb.StreamControllerTest do
end end
describe "new stream" do describe "new stream" do
setup [:create_admin]
test "renders form", %{conn: conn} do test "renders form", %{conn: conn} do
conn = get(conn, ~p"/streams/new") conn = get(conn, ~p"/streams/new")
assert html_response(conn, 200) =~ "New Stream" assert html_response(conn, 200) =~ "New Stream"
end end
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 test "redirects to show when data is valid", %{conn: conn} do
conn = post(conn, ~p"/streams", stream: @create_attrs) 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 test "renders errors when data is invalid", %{conn: conn} do
conn = post(conn, ~p"/streams", stream: @invalid_attrs) conn = post(conn, ~p"/streams", stream: @invalid_attrs)
assert Phoenix.Flash.get(conn.assigns.flash, :error) == nil
assert html_response(conn, 200) =~ "New Stream" assert html_response(conn, 200) =~ "New Stream"
end end
end end
describe "edit stream" do describe "edit stream" do
setup [:create_stream] setup [:create_stream, :create_admin]
test "renders form for editing chosen stream", %{conn: conn, stream: stream} do test "renders form for editing chosen stream", %{conn: conn, stream: stream} do
conn = get(conn, ~p"/streams/#{stream}/edit") conn = get(conn, ~p"/streams/#{stream}/edit")
@ -51,8 +57,8 @@ defmodule BrightWeb.StreamControllerTest do
end end
end end
describe "update stream" do describe "admin update stream" do
setup [:create_stream] setup [:create_stream, :create_admin]
test "redirects when data is valid", %{conn: conn, stream: stream} do test "redirects when data is valid", %{conn: conn, stream: stream} do
conn = put(conn, ~p"/streams/#{stream}", stream: @update_attrs) 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 test "renders errors when data is invalid", %{conn: conn, stream: stream} do
conn = put(conn, ~p"/streams/#{stream}", stream: @invalid_attrs) 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" assert html_response(conn, 200) =~ "Edit Stream"
end end
end end
describe "delete stream" do describe "admin delete stream" do
setup [:create_stream] setup [:create_stream, :create_admin]
test "deletes chosen stream", %{conn: conn, stream: stream} do test "deletes chosen stream", %{conn: conn, stream: stream} do
conn = delete(conn, ~p"/streams/#{stream}") conn = delete(conn, ~p"/streams/#{stream}")

View File

@ -2,10 +2,11 @@ defmodule BrightWeb.TagControllerTest do
use BrightWeb.ConnCase use BrightWeb.ConnCase
import Bright.TagsFixtures import Bright.TagsFixtures
require Logger
@create_attrs %{name: "some name"} @create_attrs %{name: "some name"}
@update_attrs %{name: "some updated name"} @update_attrs %{name: "some updated name"}
@invalid_attrs %{name: nil} @invalid_attrs %{name: 3}
describe "index" do describe "index" do
test "lists all tags", %{conn: conn} do test "lists all tags", %{conn: conn} do
@ -15,6 +16,8 @@ defmodule BrightWeb.TagControllerTest do
end end
describe "new tag" do describe "new tag" do
setup :create_admin
test "renders form", %{conn: conn} do test "renders form", %{conn: conn} do
conn = get(conn, ~p"/tags/new") conn = get(conn, ~p"/tags/new")
assert html_response(conn, 200) =~ "New Tag" assert html_response(conn, 200) =~ "New Tag"
@ -22,6 +25,8 @@ defmodule BrightWeb.TagControllerTest do
end end
describe "create tag" do describe "create tag" do
setup :create_admin
test "redirects to show when data is valid", %{conn: conn} do test "redirects to show when data is valid", %{conn: conn} do
conn = post(conn, ~p"/tags", tag: @create_attrs) conn = post(conn, ~p"/tags", tag: @create_attrs)
@ -39,7 +44,7 @@ defmodule BrightWeb.TagControllerTest do
end end
describe "edit tag" do describe "edit tag" do
setup [:create_tag] setup [:create_tag, :create_admin]
test "renders form for editing chosen tag", %{conn: conn, tag: tag} do test "renders form for editing chosen tag", %{conn: conn, tag: tag} do
conn = get(conn, ~p"/tags/#{tag}/edit") conn = get(conn, ~p"/tags/#{tag}/edit")
@ -48,10 +53,11 @@ defmodule BrightWeb.TagControllerTest do
end end
describe "update tag" do describe "update tag" do
setup [:create_tag] setup [:create_tag, :create_admin]
test "redirects when data is valid", %{conn: conn, tag: tag} do test "redirects when data is valid", %{conn: conn, tag: tag} do
conn = put(conn, ~p"/tags/#{tag}", tag: @update_attrs) conn = put(conn, ~p"/tags/#{tag}", tag: @update_attrs)
Logger.debug("tag update redirect conn=#{inspect(conn)}")
assert redirected_to(conn) == ~p"/tags/#{tag}" assert redirected_to(conn) == ~p"/tags/#{tag}"
conn = get(conn, ~p"/tags/#{tag}") conn = get(conn, ~p"/tags/#{tag}")
@ -65,7 +71,7 @@ defmodule BrightWeb.TagControllerTest do
end end
describe "delete tag" do describe "delete tag" do
setup [:create_tag] setup [:create_tag, :create_admin]
test "deletes chosen tag", %{conn: conn, tag: tag} do test "deletes chosen tag", %{conn: conn, tag: tag} do
conn = delete(conn, ~p"/tags/#{tag}") conn = delete(conn, ~p"/tags/#{tag}")

View File

@ -19,12 +19,14 @@ defmodule BrightWeb.TorrentControllerTest do
describe "index" do describe "index" do
test "lists all torrent", %{conn: conn} 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" assert html_response(conn, 200) =~ "Listing Torrent"
end end
end end
describe "new torrent" do describe "new torrent" do
setup [:create_admin]
test "renders form", %{conn: conn} do test "renders form", %{conn: conn} do
conn = get(conn, ~p"/torrents/new") conn = get(conn, ~p"/torrents/new")
assert html_response(conn, 200) =~ "New Torrent" assert html_response(conn, 200) =~ "New Torrent"
@ -32,8 +34,10 @@ defmodule BrightWeb.TorrentControllerTest do
end end
describe "create torrent" do describe "create torrent" do
setup [:create_admin]
test "redirects to show when data is valid", %{conn: conn} do 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 %{id: id} = redirected_params(conn)
assert redirected_to(conn) == ~p"/torrents/#{id}" assert redirected_to(conn) == ~p"/torrents/#{id}"
@ -49,7 +53,7 @@ defmodule BrightWeb.TorrentControllerTest do
end end
describe "edit torrent" do describe "edit torrent" do
setup [:create_torrent] setup [:create_torrent, :create_admin]
test "renders form for editing chosen torrent", %{conn: conn, torrent: torrent} do test "renders form for editing chosen torrent", %{conn: conn, torrent: torrent} do
conn = get(conn, ~p"/torrents/#{torrent}/edit") conn = get(conn, ~p"/torrents/#{torrent}/edit")
@ -58,7 +62,7 @@ defmodule BrightWeb.TorrentControllerTest do
end end
describe "update torrent" do describe "update torrent" do
setup [:create_torrent] setup [:create_torrent, :create_admin]
test "redirects when data is valid", %{conn: conn, torrent: torrent} do test "redirects when data is valid", %{conn: conn, torrent: torrent} do
conn = put(conn, ~p"/torrents/#{torrent}", torrent: @update_attrs) conn = put(conn, ~p"/torrents/#{torrent}", torrent: @update_attrs)
@ -75,11 +79,11 @@ defmodule BrightWeb.TorrentControllerTest do
end end
describe "delete torrent" do describe "delete torrent" do
setup [:create_torrent] setup [:create_torrent, :create_admin]
test "deletes chosen torrent", %{conn: conn, torrent: torrent} do test "deletes chosen torrent", %{conn: conn, torrent: torrent} do
conn = delete(conn, ~p"/torrents/#{torrent}") conn = delete(conn, ~p"/torrents/#{torrent}")
assert redirected_to(conn) == ~p"/torrent" assert redirected_to(conn) == ~p"/torrents"
assert_error_sent 404, fn -> assert_error_sent 404, fn ->
get(conn, ~p"/torrents/#{torrent}") get(conn, ~p"/torrents/#{torrent}")

View File

@ -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

View File

@ -2,14 +2,13 @@ defmodule BrightWeb.VodControllerTest do
use BrightWeb.ConnCase use BrightWeb.ConnCase
import Bright.StreamsFixtures import Bright.StreamsFixtures
require Logger
@create_attrs %{ @create_attrs %{
s3_cdn_url: "some s3_cdn_url", s3_cdn_url: "some s3_cdn_url",
s3_upload_id: "some s3_upload_id", s3_upload_id: "some s3_upload_id",
s3_key: "some s3_key", s3_key: "some s3_key",
s3_bucket: "some s3_bucket", s3_bucket: "some s3_bucket",
mux_asset_id: "some mux_asset_id",
mux_playback_id: "some mux_playback_id",
torrent: "some torrent" torrent: "some torrent"
} }
@update_attrs %{ @update_attrs %{
@ -22,6 +21,7 @@ defmodule BrightWeb.VodControllerTest do
torrent: "some updated torrent" torrent: "some updated torrent"
} }
@invalid_attrs %{ @invalid_attrs %{
stream_id: "this is obviously invalid",
s3_cdn_url: nil, s3_cdn_url: nil,
s3_upload_id: nil, s3_upload_id: nil,
s3_key: nil, s3_key: nil,
@ -39,6 +39,8 @@ defmodule BrightWeb.VodControllerTest do
end end
describe "new vod" do describe "new vod" do
setup [:create_admin]
test "renders form", %{conn: conn} do test "renders form", %{conn: conn} do
conn = get(conn, ~p"/vods/new") conn = get(conn, ~p"/vods/new")
assert html_response(conn, 200) =~ "New Vod" assert html_response(conn, 200) =~ "New Vod"
@ -46,8 +48,12 @@ defmodule BrightWeb.VodControllerTest do
end end
describe "create vod" do describe "create vod" do
setup [:create_admin]
test "redirects to show when data is valid", %{conn: conn} do 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 %{id: id} = redirected_params(conn)
assert redirected_to(conn) == ~p"/vods/#{id}" assert redirected_to(conn) == ~p"/vods/#{id}"
@ -63,7 +69,7 @@ defmodule BrightWeb.VodControllerTest do
end end
describe "edit vod" do describe "edit vod" do
setup [:create_vod] setup [:create_vod, :create_admin]
test "renders form for editing chosen vod", %{conn: conn, vod: vod} do test "renders form for editing chosen vod", %{conn: conn, vod: vod} do
conn = get(conn, ~p"/vods/#{vod}/edit") conn = get(conn, ~p"/vods/#{vod}/edit")
@ -72,7 +78,7 @@ defmodule BrightWeb.VodControllerTest do
end end
describe "update vod" do describe "update vod" do
setup [:create_vod] setup [:create_vod, :create_admin]
test "redirects when data is valid", %{conn: conn, vod: vod} do test "redirects when data is valid", %{conn: conn, vod: vod} do
conn = put(conn, ~p"/vods/#{vod}", vod: @update_attrs) 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 test "renders errors when data is invalid", %{conn: conn, vod: vod} do
conn = put(conn, ~p"/vods/#{vod}", vod: @invalid_attrs) conn = put(conn, ~p"/vods/#{vod}", vod: @invalid_attrs)
Logger.debug("renders errors... conn=#{inspect(conn)}")
assert html_response(conn, 200) =~ "Edit Vod" 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
end end
describe "delete vod" do describe "delete vod" do
setup [:create_vod] setup [:create_vod, :create_admin]
test "deletes chosen vod", %{conn: conn, vod: vod} do test "deletes chosen vod", %{conn: conn, vod: vod} do
conn = delete(conn, ~p"/vods/#{vod}") conn = delete(conn, ~p"/vods/#{vod}")
@ -102,7 +117,8 @@ defmodule BrightWeb.VodControllerTest do
end end
defp create_vod(_) do defp create_vod(_) do
vod = vod_fixture() stream = stream_fixture()
vod = vod_fixture(%{stream_id: stream.id})
%{vod: vod} %{vod: vod}
end end
end end

View File

@ -7,10 +7,18 @@ defmodule BrightWeb.VtuberControllerTest do
import Bright.VtubersFixtures import Bright.VtubersFixtures
require Logger require Logger
@create_attrs %{} @create_attrs %{
slug: "testvtuber",
display_name: "test",
theme_color: "#813d9c",
image: "https://example.com/image.jpg"
}
@update_attrs %{} @update_attrs %{}
@invalid_attrs %{} @invalid_attrs %{theme_color: 3}
@only_admins_error "Only admins can do that." @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 describe "index" do
test "lists all vtubers", %{conn: conn} do test "lists all vtubers", %{conn: conn} do
@ -34,12 +42,12 @@ defmodule BrightWeb.VtuberControllerTest do
test "redirects to /vtubers", %{conn: conn} do test "redirects to /vtubers", %{conn: conn} do
conn = get(conn, ~p"/vtubers/new") conn = get(conn, ~p"/vtubers/new")
assert html_response(conn, 302) =~ "<html>" assert html_response(conn, 302) =~ "<html>"
assert Phoenix.Flash.get(conn.assigns.flash, :error) == @only_admins_error assert Phoenix.Flash.get(conn.assigns.flash, :error) == @patron_tier_1_required_error
assert redirected_to(conn) == ~p"/vtubers" assert redirected_to(conn) == ~p"/"
end end
end end
describe "create vtuber" do describe "create vtuber as admin" do
setup [:create_admin] setup [:create_admin]
test "redirects to :show when data is valid", %{conn: conn} do 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) conn = post(conn, ~p"/vtubers", vtuber: @create_attrs)
assert %{id: id} = redirected_params(conn) Logger.debug("CREATED conn=#{inspect(conn)}")
assert redirected_to(conn) == ~p"/vtubers/#{id}" 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 %{id: id} = redirected_params(conn, 302)
assert html_response(conn, 200) =~ "Vtuber #{id}" assert redirected_to(conn) == ~p"/vtubers/#{id}"
end end
test "renders errors when data is invalid", %{conn: conn, user: user} do test "renders errors when data is invalid", %{conn: conn, user: user} do
@ -60,6 +69,73 @@ defmodule BrightWeb.VtuberControllerTest do
end end
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 describe "edit vtuber as admin" do
setup [:create_vtuber, :create_admin] 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 test "renders errors when data is invalid", %{conn: conn, vtuber: vtuber} do
conn = put(conn, ~p"/vtubers/#{vtuber}", vtuber: @invalid_attrs) 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
end end
describe "update vtuber as user" do describe "update vtuber as user (patron_tier 0)" do
setup [:create_vtuber, :create_user] 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)}") 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 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
end end

View File

@ -16,6 +16,7 @@ defmodule BrightWeb.ConnCase do
""" """
import Bright.UsersFixtures import Bright.UsersFixtures
require Logger
use ExUnit.CaseTemplate use ExUnit.CaseTemplate
@ -50,8 +51,32 @@ defmodule BrightWeb.ConnCase do
{:ok, user: user, conn: conn} {:ok, user: user, conn: conn}
end end
def create_user(%{conn: conn}) do def create_user(opts) do
user = user_fixture(%{role: "user", patreon_id: 4321}) 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) conn = login_user(conn, user)
{:ok, user: user, conn: conn} {:ok, user: user, conn: conn}
end end