Beacon submission approval workflow
Non-admin users can now submit beacons but they are held in an unapproved state until an admin approves them. Only approved beacons appear in the public list; pending submissions are shown in a separate admin-only section on /beacons with Approve and Delete actions. The show page surfaces a pending badge and an admin-only Approve button when viewing an unapproved beacon. Also formats EIRP (mW) on the index page without scientific notation.
This commit is contained in:
parent
f8763feb38
commit
80f2725cd5
9 changed files with 304 additions and 42 deletions
|
|
@ -1,8 +1,8 @@
|
|||
defmodule Microwaveprop.Beacons do
|
||||
@moduledoc """
|
||||
The Beacons context. Beacon records are public (anyone can read)
|
||||
but only admin users can create/update/delete them — that gating
|
||||
is enforced by the router's live_session for the form views.
|
||||
The Beacons context. Any authenticated user can submit a beacon,
|
||||
but submissions are held as unapproved until an admin approves
|
||||
them. Only approved beacons appear in the public list.
|
||||
"""
|
||||
|
||||
import Ecto.Query, warn: false
|
||||
|
|
@ -28,9 +28,22 @@ defmodule Microwaveprop.Beacons do
|
|||
Phoenix.PubSub.broadcast(Microwaveprop.PubSub, @topic, message)
|
||||
end
|
||||
|
||||
@doc "Returns all beacons ordered by frequency."
|
||||
@doc "Returns approved beacons ordered by frequency."
|
||||
def list_beacons do
|
||||
Repo.all(from b in Beacon, order_by: [asc: b.frequency_mhz, asc: b.callsign])
|
||||
Repo.all(
|
||||
from b in Beacon,
|
||||
where: b.approved == true,
|
||||
order_by: [asc: b.frequency_mhz, asc: b.callsign]
|
||||
)
|
||||
end
|
||||
|
||||
@doc "Returns unapproved beacons awaiting admin review."
|
||||
def list_pending_beacons do
|
||||
Repo.all(
|
||||
from b in Beacon,
|
||||
where: b.approved == false,
|
||||
order_by: [asc: b.inserted_at]
|
||||
)
|
||||
end
|
||||
|
||||
@doc "Gets a single beacon. Raises if not found."
|
||||
|
|
@ -54,6 +67,14 @@ defmodule Microwaveprop.Beacons do
|
|||
|> broadcast_if_ok(:updated)
|
||||
end
|
||||
|
||||
@doc "Marks a beacon as approved, making it visible in the public list."
|
||||
def approve_beacon(%Beacon{} = beacon) do
|
||||
beacon
|
||||
|> Ecto.Changeset.change(approved: true)
|
||||
|> Repo.update()
|
||||
|> broadcast_if_ok(:updated)
|
||||
end
|
||||
|
||||
@doc "Deletes a beacon."
|
||||
def delete_beacon(%Beacon{} = beacon) do
|
||||
case Repo.delete(beacon) do
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ defmodule Microwaveprop.Beacons.Beacon do
|
|||
field :power_mw, :float
|
||||
field :height_ft, :float
|
||||
field :on_the_air, :boolean, default: true
|
||||
field :approved, :boolean, default: false
|
||||
field :user_id, :binary_id
|
||||
|
||||
timestamps(type: :utc_datetime)
|
||||
|
|
|
|||
|
|
@ -48,8 +48,7 @@ defmodule Microwaveprop.Beacons.RangeEstimate do
|
|||
"""
|
||||
@spec nearest_band_mhz(number()) :: integer()
|
||||
def nearest_band_mhz(freq_mhz) when is_number(freq_mhz) do
|
||||
BandConfig.all_freqs()
|
||||
|> Enum.min_by(fn b -> abs(b - freq_mhz) end)
|
||||
Enum.min_by(BandConfig.all_freqs(), fn b -> abs(b - freq_mhz) end)
|
||||
end
|
||||
|
||||
@doc """
|
||||
|
|
|
|||
|
|
@ -12,8 +12,12 @@ defmodule MicrowavepropWeb.BeaconLive.Index do
|
|||
Beacons
|
||||
<:subtitle>Microwave beacons tracked by NTMS.</:subtitle>
|
||||
<:actions>
|
||||
<.button :if={admin?(@current_scope)} variant="primary" navigate={~p"/beacons/new"}>
|
||||
<.icon name="hero-plus" /> New Beacon
|
||||
<.button
|
||||
:if={authenticated?(@current_scope)}
|
||||
variant="primary"
|
||||
navigate={~p"/beacons/new"}
|
||||
>
|
||||
<.icon name="hero-plus" /> Submit Beacon
|
||||
</.button>
|
||||
</:actions>
|
||||
</.header>
|
||||
|
|
@ -28,10 +32,10 @@ defmodule MicrowavepropWeb.BeaconLive.Index do
|
|||
<:col :let={{_id, beacon}} label="Grid">{beacon.grid}</:col>
|
||||
<:col :let={{_id, beacon}} label="Lat">{beacon.lat}</:col>
|
||||
<:col :let={{_id, beacon}} label="Lon">{beacon.lon}</:col>
|
||||
<:col :let={{_id, beacon}} label="EIRP (mW)">{beacon.power_mw}</:col>
|
||||
<:col :let={{_id, beacon}} label="EIRP (mW)">{format_mw(beacon.power_mw)}</:col>
|
||||
<:col :let={{_id, beacon}} label="Height AGL (ft)">{beacon.height_ft}</:col>
|
||||
<:col :let={{_id, beacon}} label="On air">
|
||||
<span class={["badge badge-sm", beacon.on_the_air && "badge-success" || "badge-ghost"]}>
|
||||
<span class={["badge badge-sm", (beacon.on_the_air && "badge-success") || "badge-ghost"]}>
|
||||
{if beacon.on_the_air, do: "Yes", else: "No"}
|
||||
</span>
|
||||
</:col>
|
||||
|
|
@ -51,6 +55,44 @@ defmodule MicrowavepropWeb.BeaconLive.Index do
|
|||
</.link>
|
||||
</:action>
|
||||
</.table>
|
||||
|
||||
<div :if={admin?(@current_scope) and @pending != []} class="mt-10">
|
||||
<.header>
|
||||
Pending approval
|
||||
<:subtitle>Submitted beacons waiting for admin review.</:subtitle>
|
||||
</.header>
|
||||
|
||||
<.table
|
||||
id="pending-beacons"
|
||||
rows={@streams.pending}
|
||||
row_click={fn {_id, beacon} -> JS.navigate(~p"/beacons/#{beacon}") end}
|
||||
>
|
||||
<:col :let={{_id, beacon}} label="Frequency (MHz)">{beacon.frequency_mhz}</:col>
|
||||
<:col :let={{_id, beacon}} label="Call">{beacon.callsign}</:col>
|
||||
<:col :let={{_id, beacon}} label="Grid">{beacon.grid}</:col>
|
||||
<:col :let={{_id, beacon}} label="EIRP (mW)">{format_mw(beacon.power_mw)}</:col>
|
||||
<:col :let={{_id, beacon}} label="Height AGL (ft)">{beacon.height_ft}</:col>
|
||||
<:col :let={{_id, beacon}} label="Submitted">
|
||||
{Calendar.strftime(beacon.inserted_at, "%Y-%m-%d %H:%M UTC")}
|
||||
</:col>
|
||||
<:action :let={{_id, beacon}}>
|
||||
<.link
|
||||
phx-click={JS.push("approve", value: %{id: beacon.id})}
|
||||
data-confirm="Approve this beacon?"
|
||||
>
|
||||
Approve
|
||||
</.link>
|
||||
</:action>
|
||||
<:action :let={{id, beacon}}>
|
||||
<.link
|
||||
phx-click={JS.push("delete", value: %{id: beacon.id}) |> hide("##{id}")}
|
||||
data-confirm="Delete this beacon?"
|
||||
>
|
||||
Delete
|
||||
</.link>
|
||||
</:action>
|
||||
</.table>
|
||||
</div>
|
||||
</Layouts.app>
|
||||
"""
|
||||
end
|
||||
|
|
@ -59,10 +101,19 @@ defmodule MicrowavepropWeb.BeaconLive.Index do
|
|||
def mount(_params, _session, socket) do
|
||||
if connected?(socket), do: Beacons.subscribe_beacons()
|
||||
|
||||
pending =
|
||||
if admin?(socket.assigns.current_scope) do
|
||||
Beacons.list_pending_beacons()
|
||||
else
|
||||
[]
|
||||
end
|
||||
|
||||
{:ok,
|
||||
socket
|
||||
|> assign(:page_title, "Beacons")
|
||||
|> stream(:beacons, Beacons.list_beacons())}
|
||||
|> assign(:pending, pending)
|
||||
|> stream(:beacons, Beacons.list_beacons())
|
||||
|> stream(:pending, pending)}
|
||||
end
|
||||
|
||||
@impl true
|
||||
|
|
@ -70,7 +121,26 @@ defmodule MicrowavepropWeb.BeaconLive.Index do
|
|||
if admin?(socket.assigns.current_scope) do
|
||||
beacon = Beacons.get_beacon!(id)
|
||||
{:ok, _} = Beacons.delete_beacon(beacon)
|
||||
{:noreply, stream_delete(socket, :beacons, beacon)}
|
||||
|
||||
{:noreply,
|
||||
socket
|
||||
|> stream_delete(:beacons, beacon)
|
||||
|> stream_delete(:pending, beacon)}
|
||||
else
|
||||
{:noreply, put_flash(socket, :error, "Admins only.")}
|
||||
end
|
||||
end
|
||||
|
||||
def handle_event("approve", %{"id" => id}, socket) do
|
||||
if admin?(socket.assigns.current_scope) do
|
||||
beacon = Beacons.get_beacon!(id)
|
||||
{:ok, approved} = Beacons.approve_beacon(beacon)
|
||||
|
||||
{:noreply,
|
||||
socket
|
||||
|> put_flash(:info, "Approved #{approved.callsign}.")
|
||||
|> stream_delete(:pending, beacon)
|
||||
|> stream_insert(:beacons, approved)}
|
||||
else
|
||||
{:noreply, put_flash(socket, :error, "Admins only.")}
|
||||
end
|
||||
|
|
@ -78,9 +148,45 @@ defmodule MicrowavepropWeb.BeaconLive.Index do
|
|||
|
||||
@impl true
|
||||
def handle_info({type, %Microwaveprop.Beacons.Beacon{}}, socket) when type in [:created, :updated, :deleted] do
|
||||
{:noreply, stream(socket, :beacons, Beacons.list_beacons(), reset: true)}
|
||||
pending =
|
||||
if admin?(socket.assigns.current_scope) do
|
||||
Beacons.list_pending_beacons()
|
||||
else
|
||||
[]
|
||||
end
|
||||
|
||||
{:noreply,
|
||||
socket
|
||||
|> assign(:pending, pending)
|
||||
|> stream(:beacons, Beacons.list_beacons(), reset: true)
|
||||
|> stream(:pending, pending, reset: true)}
|
||||
end
|
||||
|
||||
defp admin?(%{user: %{is_admin: true}}), do: true
|
||||
defp admin?(_), do: false
|
||||
|
||||
defp authenticated?(%{user: %{}}), do: true
|
||||
defp authenticated?(_), do: false
|
||||
|
||||
# Format a milliwatt power value without scientific notation.
|
||||
# Keeps up to 3 decimal places, drops trailing zeros.
|
||||
defp format_mw(nil), do: ""
|
||||
|
||||
defp format_mw(mw) when is_float(mw) do
|
||||
if mw == trunc(mw) do
|
||||
Integer.to_string(trunc(mw))
|
||||
else
|
||||
mw |> :erlang.float_to_binary(decimals: 3) |> trim_trailing_zeros()
|
||||
end
|
||||
end
|
||||
|
||||
defp format_mw(mw), do: to_string(mw)
|
||||
|
||||
defp trim_trailing_zeros(str) do
|
||||
if String.contains?(str, ".") do
|
||||
str |> String.trim_trailing("0") |> String.trim_trailing(".")
|
||||
else
|
||||
str
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -12,8 +12,20 @@ defmodule MicrowavepropWeb.BeaconLive.Show do
|
|||
<Layouts.app flash={@flash} current_scope={@current_scope}>
|
||||
<.header>
|
||||
{@beacon.callsign}
|
||||
<:subtitle>{@beacon.frequency_mhz} MHz · {@beacon.grid}</:subtitle>
|
||||
<:subtitle>
|
||||
{@beacon.frequency_mhz} MHz · {@beacon.grid}
|
||||
<span :if={not @beacon.approved} class="badge badge-warning badge-sm ml-2">
|
||||
Pending approval
|
||||
</span>
|
||||
</:subtitle>
|
||||
<:actions>
|
||||
<.button
|
||||
:if={admin?(@current_scope) and not @beacon.approved}
|
||||
variant="primary"
|
||||
phx-click="approve"
|
||||
>
|
||||
<.icon name="hero-check" /> Approve
|
||||
</.button>
|
||||
<.button navigate={~p"/beacons"}>
|
||||
<.icon name="hero-arrow-left" />
|
||||
</.button>
|
||||
|
|
@ -54,7 +66,8 @@ defmodule MicrowavepropWeb.BeaconLive.Show do
|
|||
<% else %>
|
||||
(no HRRR data — defaulting cells to 50)
|
||||
<% end %>
|
||||
· <strong>{length(@estimate.cells)}</strong> cells rendered
|
||||
· <strong>{length(@estimate.cells)}</strong>
|
||||
cells rendered
|
||||
</span>
|
||||
</div>
|
||||
|
||||
|
|
@ -67,8 +80,7 @@ defmodule MicrowavepropWeb.BeaconLive.Show do
|
|||
>
|
||||
</span>
|
||||
<span>
|
||||
<strong>{tier.label}</strong>
|
||||
(≥ {tier.min_dbm} dBm)
|
||||
<strong>{tier.label}</strong> (≥ {tier.min_dbm} dBm)
|
||||
</span>
|
||||
</div>
|
||||
<% end %>
|
||||
|
|
@ -101,6 +113,20 @@ defmodule MicrowavepropWeb.BeaconLive.Show do
|
|||
|> assign(:estimate, RangeEstimate.estimate(beacon))}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_event("approve", _params, socket) do
|
||||
if admin?(socket.assigns.current_scope) do
|
||||
{:ok, approved} = Beacons.approve_beacon(socket.assigns.beacon)
|
||||
|
||||
{:noreply,
|
||||
socket
|
||||
|> assign(:beacon, approved)
|
||||
|> put_flash(:info, "Beacon approved.")}
|
||||
else
|
||||
{:noreply, put_flash(socket, :error, "Admins only.")}
|
||||
end
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_info({:updated, %Beacon{id: id} = beacon}, %{assigns: %{beacon: %{id: id}}} = socket) do
|
||||
{:noreply,
|
||||
|
|
|
|||
|
|
@ -27,13 +27,17 @@ defmodule MicrowavepropWeb.Router do
|
|||
# Health check — no pipeline, minimal overhead
|
||||
get "/health", MicrowavepropWeb.HealthController, :check, log: false
|
||||
|
||||
# Admin-only routes must come first so that literal segments like
|
||||
# `/beacons/new` are registered before the public `/beacons/:id`.
|
||||
# Authenticated (non-admin) and admin-only routes must come first so that
|
||||
# literal segments like `/beacons/new` are registered before the public
|
||||
# `/beacons/:id`.
|
||||
scope "/", MicrowavepropWeb do
|
||||
pipe_through [:browser, :require_authenticated_user]
|
||||
|
||||
live_session :admin, on_mount: [{MicrowavepropWeb.UserAuth, :require_admin}] do
|
||||
live_session :authenticated, on_mount: [{MicrowavepropWeb.UserAuth, :default}] do
|
||||
live "/beacons/new", BeaconLive.Form, :new
|
||||
end
|
||||
|
||||
live_session :admin, on_mount: [{MicrowavepropWeb.UserAuth, :require_admin}] do
|
||||
live "/beacons/:id/edit", BeaconLive.Form, :edit
|
||||
|
||||
live "/users", UserManagementLive.Index, :index
|
||||
|
|
|
|||
|
|
@ -0,0 +1,12 @@
|
|||
defmodule Microwaveprop.Repo.Migrations.AddApprovedToBeacons do
|
||||
use Ecto.Migration
|
||||
|
||||
def change do
|
||||
alter table(:beacons) do
|
||||
add :approved, :boolean, null: false, default: false
|
||||
end
|
||||
|
||||
# Existing beacons were admin-created, so mark them approved.
|
||||
execute "UPDATE beacons SET approved = true", ""
|
||||
end
|
||||
end
|
||||
|
|
@ -18,12 +18,36 @@ defmodule Microwaveprop.BeaconsTest do
|
|||
}
|
||||
|
||||
describe "list_beacons/0" do
|
||||
test "returns all beacons regardless of creator" do
|
||||
test "returns only approved beacons" do
|
||||
u1 = user_fixture()
|
||||
u2 = user_fixture()
|
||||
b1 = beacon_fixture(u1)
|
||||
b2 = beacon_fixture(u2, callsign: "W5TX")
|
||||
assert [^b1, ^b2] = Enum.sort_by(Beacons.list_beacons(), & &1.callsign)
|
||||
{:ok, b1} = Beacons.create_beacon(u1, valid_beacon_attrs())
|
||||
{:ok, b1} = Beacons.approve_beacon(b1)
|
||||
_pending = beacon_fixture(u2, callsign: "W5TX")
|
||||
assert [^b1] = Beacons.list_beacons()
|
||||
end
|
||||
end
|
||||
|
||||
describe "list_pending_beacons/0" do
|
||||
test "returns only unapproved beacons" do
|
||||
u1 = user_fixture()
|
||||
u2 = user_fixture()
|
||||
{:ok, approved} = Beacons.create_beacon(u1, valid_beacon_attrs())
|
||||
{:ok, _approved} = Beacons.approve_beacon(approved)
|
||||
pending = beacon_fixture(u2, callsign: "W5TX")
|
||||
assert [^pending] = Beacons.list_pending_beacons()
|
||||
end
|
||||
end
|
||||
|
||||
describe "approve_beacon/1" do
|
||||
test "marks the beacon as approved and broadcasts an update" do
|
||||
user = user_fixture()
|
||||
beacon = beacon_fixture(user)
|
||||
refute beacon.approved
|
||||
Beacons.subscribe_beacons()
|
||||
assert {:ok, approved} = Beacons.approve_beacon(beacon)
|
||||
assert approved.approved == true
|
||||
assert_receive {:updated, %Beacon{approved: true}}
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -50,6 +74,19 @@ defmodule Microwaveprop.BeaconsTest do
|
|||
assert beacon.user_id == user.id
|
||||
end
|
||||
|
||||
test "new beacons default to unapproved" do
|
||||
user = user_fixture()
|
||||
assert {:ok, beacon} = Beacons.create_beacon(user, valid_beacon_attrs())
|
||||
assert beacon.approved == false
|
||||
end
|
||||
|
||||
test "ignores client-supplied approved value" do
|
||||
user = user_fixture()
|
||||
attrs = Map.put(valid_beacon_attrs(), :approved, true)
|
||||
assert {:ok, beacon} = Beacons.create_beacon(user, attrs)
|
||||
assert beacon.approved == false
|
||||
end
|
||||
|
||||
test "derives grid from lat/lon when grid is omitted" do
|
||||
user = user_fixture()
|
||||
# DFW is EM12
|
||||
|
|
@ -61,7 +98,8 @@ defmodule Microwaveprop.BeaconsTest do
|
|||
user = user_fixture()
|
||||
|
||||
attrs =
|
||||
valid_beacon_attrs(grid: "EM12kp")
|
||||
[grid: "EM12kp"]
|
||||
|> valid_beacon_attrs()
|
||||
|> Map.drop([:lat, :lon])
|
||||
|
||||
assert {:ok, beacon} = Beacons.create_beacon(user, attrs)
|
||||
|
|
|
|||
|
|
@ -16,63 +16,112 @@ defmodule MicrowavepropWeb.BeaconLiveTest do
|
|||
promote_to_admin(user_fixture())
|
||||
end
|
||||
|
||||
defp approved_beacon_fixture(user, attrs \\ %{}) do
|
||||
beacon = beacon_fixture(user, attrs)
|
||||
{:ok, beacon} = Microwaveprop.Beacons.approve_beacon(beacon)
|
||||
beacon
|
||||
end
|
||||
|
||||
describe "Index (public)" do
|
||||
test "lists beacons without logging in", %{conn: conn} do
|
||||
beacon = beacon_fixture(user_fixture())
|
||||
test "lists approved beacons without logging in", %{conn: conn} do
|
||||
beacon = approved_beacon_fixture(user_fixture())
|
||||
{:ok, _view, html} = live(conn, ~p"/beacons")
|
||||
assert html =~ "Beacons"
|
||||
assert html =~ beacon.callsign
|
||||
end
|
||||
|
||||
test "non-admin does not see the New Beacon button", %{conn: conn} do
|
||||
test "hides unapproved beacons from the public list", %{conn: conn} do
|
||||
_pending = beacon_fixture(user_fixture(), callsign: "W5PEN")
|
||||
{:ok, _view, html} = live(conn, ~p"/beacons")
|
||||
refute html =~ "W5PEN"
|
||||
end
|
||||
|
||||
test "anonymous does not see the Submit Beacon button", %{conn: conn} do
|
||||
{:ok, _view, html} = live(conn, ~p"/beacons")
|
||||
refute html =~ "Submit Beacon"
|
||||
end
|
||||
|
||||
test "authenticated user sees the Submit Beacon button", %{conn: conn} do
|
||||
user = user_fixture()
|
||||
conn = log_in_user(conn, user)
|
||||
{:ok, _view, html} = live(conn, ~p"/beacons")
|
||||
refute html =~ "New Beacon"
|
||||
assert html =~ "Submit Beacon"
|
||||
end
|
||||
|
||||
test "admin sees the New Beacon button", %{conn: conn} do
|
||||
test "admin sees pending beacons section", %{conn: conn} do
|
||||
pending = beacon_fixture(user_fixture(), callsign: "W5PEN")
|
||||
conn = log_in_user(conn, admin_user_fixture())
|
||||
{:ok, _view, html} = live(conn, ~p"/beacons")
|
||||
assert html =~ "New Beacon"
|
||||
assert html =~ "Pending approval"
|
||||
assert html =~ pending.callsign
|
||||
end
|
||||
|
||||
test "non-admin does not see pending beacons section", %{conn: conn} do
|
||||
_pending = beacon_fixture(user_fixture(), callsign: "W5PEN")
|
||||
conn = log_in_user(conn, user_fixture())
|
||||
{:ok, _view, html} = live(conn, ~p"/beacons")
|
||||
refute html =~ "Pending approval"
|
||||
refute html =~ "W5PEN"
|
||||
end
|
||||
|
||||
test "admin can approve a pending beacon", %{conn: conn} do
|
||||
pending = beacon_fixture(user_fixture(), callsign: "W5PEN")
|
||||
conn = log_in_user(conn, admin_user_fixture())
|
||||
{:ok, view, _html} = live(conn, ~p"/beacons")
|
||||
|
||||
html =
|
||||
view
|
||||
|> element("a[phx-click][data-confirm='Approve this beacon?']")
|
||||
|> render_click()
|
||||
|
||||
assert html =~ "Approved #{pending.callsign}"
|
||||
assert Microwaveprop.Beacons.get_beacon!(pending.id).approved == true
|
||||
end
|
||||
end
|
||||
|
||||
describe "Show (public)" do
|
||||
test "renders beacon details without logging in", %{conn: conn} do
|
||||
beacon = beacon_fixture(user_fixture())
|
||||
beacon = approved_beacon_fixture(user_fixture())
|
||||
{:ok, _view, html} = live(conn, ~p"/beacons/#{beacon}")
|
||||
assert html =~ beacon.callsign
|
||||
assert html =~ "#{beacon.frequency_mhz}"
|
||||
end
|
||||
|
||||
test "shows pending badge on unapproved beacons", %{conn: conn} do
|
||||
beacon = beacon_fixture(user_fixture())
|
||||
{:ok, _view, html} = live(conn, ~p"/beacons/#{beacon}")
|
||||
assert html =~ "Pending approval"
|
||||
end
|
||||
end
|
||||
|
||||
describe "Form (admin only)" do
|
||||
test "redirects non-admin away from /beacons/new", %{conn: conn} do
|
||||
describe "Form" do
|
||||
test "authenticated non-admin can access /beacons/new", %{conn: conn} do
|
||||
user = user_fixture()
|
||||
conn = log_in_user(conn, user)
|
||||
|
||||
assert {:error, {:redirect, %{to: "/"}}} = live(conn, ~p"/beacons/new")
|
||||
{:ok, _view, html} = live(conn, ~p"/beacons/new")
|
||||
assert html =~ "New beacon"
|
||||
end
|
||||
|
||||
test "redirects anonymous user away from /beacons/new", %{conn: conn} do
|
||||
assert {:error, {:redirect, %{to: "/users/log-in"}}} = live(conn, ~p"/beacons/new")
|
||||
end
|
||||
|
||||
test "admin creates a beacon", %{conn: conn} do
|
||||
conn = log_in_user(conn, admin_user_fixture())
|
||||
test "non-admin submission creates an unapproved beacon", %{conn: conn} do
|
||||
user = user_fixture()
|
||||
conn = log_in_user(conn, user)
|
||||
{:ok, form_live, _html} = live(conn, ~p"/beacons/new")
|
||||
|
||||
attrs = valid_beacon_attrs()
|
||||
|
||||
assert {:ok, _view, html} =
|
||||
assert {:ok, _view, _html} =
|
||||
form_live
|
||||
|> form("#beacon-form", beacon: attrs)
|
||||
|> render_submit()
|
||||
|> follow_redirect(conn, ~p"/beacons")
|
||||
|
||||
assert html =~ "Beacon created"
|
||||
assert html =~ attrs.callsign
|
||||
beacon = List.first(Microwaveprop.Beacons.list_pending_beacons())
|
||||
assert beacon.callsign == attrs.callsign
|
||||
assert beacon.approved == false
|
||||
end
|
||||
|
||||
test "admin edits a beacon", %{conn: conn} do
|
||||
|
|
@ -90,5 +139,11 @@ defmodule MicrowavepropWeb.BeaconLiveTest do
|
|||
|
||||
assert html =~ "Beacon updated"
|
||||
end
|
||||
|
||||
test "non-admin cannot access /beacons/:id/edit", %{conn: conn} do
|
||||
beacon = beacon_fixture(user_fixture())
|
||||
conn = log_in_user(conn, user_fixture())
|
||||
assert {:error, {:redirect, %{to: "/"}}} = live(conn, ~p"/beacons/#{beacon}/edit")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue