Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions lib/tilex/posts.ex
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ defmodule Tilex.Posts do
end
end

def delete_post(post) do
Repo.delete(post)
end

def published(query \\ Post) do
from(p in query, where: not is_nil(p.published_at) and p.published_at <= fragment("now()"))
end
Expand Down
28 changes: 26 additions & 2 deletions lib/tilex_web/controllers/post_controller.ex
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@ defmodule TilexWeb.PostController do
alias Tilex.Posts

plug(:load_channels when action in [:new, :create, :edit, :update])
plug(:extract_slug when action in [:show, :edit, :update])
plug(:extract_slug when action in [:show, :edit, :update, :delete])

plug(
Guardian.Plug.EnsureAuthenticated,
[error_handler: __MODULE__]
when action in ~w(new create edit update)a
when action in ~w(new create edit update delete)a
)

@behaviour Guardian.Plug.ErrorHandler
Expand Down Expand Up @@ -196,6 +196,30 @@ defmodule TilexWeb.PostController do
end
end

def delete(conn, _params) do
current_user = Guardian.Plug.current_resource(conn)

post =
case current_user.admin do
false -> assoc(current_user, :posts)
true -> Post
end
|> Repo.get_by!(slug: conn.assigns.slug)
|> Repo.preload([:developer])

case Posts.delete_post(post) do
{:ok, _post} ->
conn
|> put_flash(:info, "Post deleted successfully")
|> redirect(to: Routes.developer_path(conn, :show, post.developer))

{:error, _changeset} ->
conn
|> put_flash(:error, "Failed to delete post")
|> redirect(to: Routes.post_path(conn, :show, post))
end
end

defp load_channels(conn, _) do
query =
Channel
Expand Down
3 changes: 3 additions & 0 deletions lib/tilex_web/templates/shared/post.html.eex
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@
<li>
<%= link("edit", to: Routes.post_path(@conn, :edit, @post), class: "post__permalink") %>
</li>
<li>
<%= link("delete", to: Routes.post_path(@conn, :delete, @post), method: :delete, class: "post__delete-link", data: [confirm: "Are you sure you want to delete this post?"]) %>
</li>
<% end %>
<li>
<%= link to: "#", class: "js-like-action post__like-link", id: @post.slug do %>
Expand Down
2 changes: 1 addition & 1 deletion mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ defmodule Tilex.Mixfile do
{:tzdata, "~> 1.1.0"},
{:ueberauth_google, "~> 0.5"},
{:usage_rules, "~> 0.1", only: [:dev]},
{:wallaby, "~>0.30.1", [runtime: false, only: :test]}
{:wallaby, "~>0.30.1", runtime: false, only: :test}
]
end

Expand Down
14 changes: 7 additions & 7 deletions mix.lock

Large diffs are not rendered by default.

87 changes: 77 additions & 10 deletions test/controllers/post_controller_test.exs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
defmodule Tilex.PostControllerTest do
use TilexWeb.ConnCase, async: true
alias Tilex.Factory

alias Tilex.Auth
alias Tilex.Blog.Post
alias Tilex.Factory
alias Tilex.Repo

test "lists all entries on index", %{conn: conn} do
conn = get(conn, Routes.post_path(conn, :index))
Expand Down Expand Up @@ -55,7 +58,7 @@ defmodule Tilex.PostControllerTest do
post(conn, Routes.post_path(conn, :create, params))

til =
Tilex.Blog.Post
Post
|> Tilex.Repo.all()
|> List.first()

Expand All @@ -79,7 +82,7 @@ defmodule Tilex.PostControllerTest do
post(conn, Routes.post_path(conn, :create, params))

til =
Tilex.Blog.Post
Post
|> Tilex.Repo.all()
|> List.first()

Expand All @@ -102,7 +105,7 @@ defmodule Tilex.PostControllerTest do
post(conn, Routes.post_path(conn, :create, params))

til =
Tilex.Blog.Post
Post
|> Tilex.Repo.all()
|> List.first()

Expand Down Expand Up @@ -133,7 +136,7 @@ defmodule Tilex.PostControllerTest do
put(conn, Routes.post_path(conn, :update, til.slug, params))

til =
Tilex.Blog.Post
Post
|> Tilex.Repo.get(til.id)

assert til.title == "New Title"
Expand Down Expand Up @@ -162,7 +165,7 @@ defmodule Tilex.PostControllerTest do
put(conn, Routes.post_path(conn, :update, til.slug, params))

til =
Tilex.Blog.Post
Post
|> Tilex.Repo.get(til.id)

assert til.title == "New Title"
Expand Down Expand Up @@ -190,18 +193,82 @@ defmodule Tilex.PostControllerTest do
put(conn, Routes.post_path(conn, :update, til.slug, params))

til =
Tilex.Blog.Post
Post
|> Tilex.Repo.get(til.id)

assert til.title == "New Title"
assert til.max_likes == 1
end

test "author can delete their own post", %{
conn: conn,
current_user: current_user
} do
post = Factory.insert!(:post, developer: current_user)

conn = delete(conn, Routes.post_path(conn, :delete, post.slug))

assert redirected_to(conn) == Routes.developer_path(conn, :show, current_user)
assert get_flash(conn, :info) == "Post deleted successfully"
assert Repo.all(Post) == []
end

test "author cannot delete another developer's post", %{
conn: conn
} do
other_user = Factory.insert!(:developer, username: "other-dev")
post = Factory.insert!(:post, developer: other_user)
post_id = post.id

assert_raise Ecto.NoResultsError, fn ->
delete(conn, Routes.post_path(conn, :delete, post.slug))
end

assert [%Post{id: ^post_id}] = Repo.all(Post)
end
end

defp authenticated_conn(%{conn: conn}) do
current_user = Factory.insert!(:developer, email: "[email protected]", username: "current")
channel = Factory.insert!(:channel, name: "git")
describe "when authenticated as admin" do
setup :authenticated_admin_conn

test "admin can delete any post", %{
conn: conn,
current_user: _admin
} do
other_user = Factory.insert!(:developer, username: "other-dev")
post = Factory.insert!(:post, developer: other_user)

conn = delete(conn, Routes.post_path(conn, :delete, post.slug))

assert redirected_to(conn) == Routes.developer_path(conn, :show, other_user)
assert get_flash(conn, :info) == "Post deleted successfully"
assert Repo.all(Post) == []
end
end

test "unauthenticated user cannot delete post", %{conn: conn} do
post = Factory.insert!(:post)
post_id = post.id

conn = delete(conn, Routes.post_path(conn, :delete, post.slug))

assert html_response(conn, 302)
assert get_flash(conn, :info) == "Authentication required"
assert [%Post{id: ^post_id}] = Repo.all(Post)
end

defp authenticated_conn(context) do
Factory.insert!(:developer, email: "[email protected]", username: "current")
|> do_authenticated_conn(context)
end

defp authenticated_admin_conn(context) do
Factory.insert!(:developer, email: "[email protected]", username: "admin", admin: true)
|> do_authenticated_conn(context)
end

defp do_authenticated_conn(current_user, %{conn: conn}) do
channel = Factory.insert!(:channel, name: "git")
conn = Auth.Guardian.Plug.sign_in(conn, current_user)
{:ok, conn: conn, current_user: current_user, channel: channel}
end
Expand Down
6 changes: 3 additions & 3 deletions test/features/admin_edits_post_test.exs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
defmodule AdminEditsPostTest do
use Tilex.IntegrationCase, async: true
use Tilex.IntegrationCase, async: false

alias Tilex.Integration.Pages.PostForm
alias Tilex.Integration.Pages.PostShowPage

test "fills out form and updates post from post show", %{session: session} do
feature "fills out form and updates post from post show", %{session: session} do
Factory.insert!(:channel, name: "phoenix")
developer = Factory.insert!(:developer, %{username: "luke-skywalker"})
admin = Factory.insert!(:developer, %{admin: true, username: "darth-vader"})
Expand All @@ -29,7 +29,7 @@ defmodule AdminEditsPostTest do
|> PostShowPage.ensure_page_loaded("Even Awesomer Post!")
end

test "enters a title that is too long", %{session: session} do
feature "enters a title that is too long", %{session: session} do
Factory.insert!(:channel, name: "phoenix")
developer = Factory.insert!(:developer)

Expand Down
18 changes: 8 additions & 10 deletions test/features/developer_creates_post_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@ defmodule DeveloperCreatesPostTest do
alias Tilex.Integration.Pages.PostShowPage
alias Tilex.Integration.Pages.PostForm

test "fills out form and save", %{session: session} do
Ecto.Adapters.SQL.Sandbox.allow(Tilex.Repo, self(), Process.whereis(Tilex.Notifications))
feature "fills out form and save", %{session: session} do
Factory.insert!(:channel, name: "phoenix")
developer = Factory.insert!(:developer)

Expand Down Expand Up @@ -48,8 +47,7 @@ defmodule DeveloperCreatesPostTest do
|> Navigation.ensure_heading("TODAY I LEARNED")
end

test "fills out form and save & publish", %{session: session} do
Ecto.Adapters.SQL.Sandbox.allow(Tilex.Repo, self(), Process.whereis(Tilex.Notifications))
feature "fills out form and save & publish", %{session: session} do
Factory.insert!(:channel, name: "phoenix")
developer = Factory.insert!(:developer)

Expand Down Expand Up @@ -88,7 +86,7 @@ defmodule DeveloperCreatesPostTest do
|> Navigation.ensure_heading("TODAY I LEARNED")
end

test "cancels submission", %{session: session} do
feature "cancels submission", %{session: session} do
developer = Factory.insert!(:developer)

session
Expand All @@ -99,7 +97,7 @@ defmodule DeveloperCreatesPostTest do
|> IndexPage.ensure_page_loaded()
end

test "fails to enter things", %{session: session} do
feature "fails to enter things", %{session: session} do
developer = Factory.insert!(:developer)

session
Expand All @@ -113,7 +111,7 @@ defmodule DeveloperCreatesPostTest do
|> CreatePostPage.expect_form_has_error("Channel can't be blank")
end

test "enters a title that is too long", %{session: session} do
feature "enters a title that is too long", %{session: session} do
Factory.insert!(:channel, name: "phoenix")
developer = Factory.insert!(:developer)

Expand All @@ -131,7 +129,7 @@ defmodule DeveloperCreatesPostTest do
|> CreatePostPage.expect_form_has_error("Title should be at most 50 character(s)")
end

test "enters a body that is too long", %{session: session} do
feature "enters a body that is too long", %{session: session} do
Factory.insert!(:channel, name: "phoenix")
developer = Factory.insert!(:developer)

Expand All @@ -149,7 +147,7 @@ defmodule DeveloperCreatesPostTest do
|> CreatePostPage.expect_form_has_error("Body should be at most 200 word(s)")
end

test "enters markdown code into the body", %{session: session} do
feature "enters markdown code into the body", %{session: session} do
Factory.insert!(:channel, name: "phoenix")
developer = Factory.insert!(:developer)

Expand All @@ -166,7 +164,7 @@ defmodule DeveloperCreatesPostTest do
assert find(session, Query.css("strong", text: "bold powerup"))
end

test "views parsed markdown preview", %{session: session} do
feature "views parsed markdown preview", %{session: session} do
Factory.insert!(:channel, name: "phoenix")
developer = Factory.insert!(:developer)

Expand Down
47 changes: 47 additions & 0 deletions test/features/developer_deletes_post_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
defmodule DeveloperDeletesPostTest do
use Tilex.IntegrationCase, async: false

alias Tilex.Blog.Post
alias Tilex.Integration.Pages.PostShowPage
alias Tilex.Repo

feature "developer can delete their own post", %{session: session} do
developer = Factory.insert!(:developer)
post = Factory.insert!(:post, developer: developer)

session
|> sign_in(developer)
|> PostShowPage.navigate(post)
|> click_and_confirm(Query.link("delete"))
|> assert_flash(:info, "Post deleted successfully")
|> assert_path(developer_path(TilexWeb.Endpoint, :show, developer))

assert Repo.all(Post) == []
end

feature "admin can delete other developer's post", %{session: session} do
developer = Factory.insert!(:developer, username: "regular-dev")
admin = Factory.insert!(:developer, username: "admin-user", admin: true)
post = Factory.insert!(:post, developer: developer)

session
|> sign_in(admin)
|> PostShowPage.navigate(post)
|> click_and_confirm(Query.link("delete"))
|> assert_flash(:info, "Post deleted successfully")
|> assert_path(developer_path(TilexWeb.Endpoint, :show, developer))

assert Repo.all(Post) == []
end

feature "non-owner cannot see delete button", %{session: session} do
developer = Factory.insert!(:developer, username: "post-owner")
other_developer = Factory.insert!(:developer, username: "other-dev")
post = Factory.insert!(:post, developer: developer)

session
|> sign_in(other_developer)
|> PostShowPage.navigate(post)
|> refute_has(Query.link("delete"))
end
end
4 changes: 2 additions & 2 deletions test/features/developer_edits_post_test.exs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
defmodule DeveloperEditsPostTest do
use Tilex.IntegrationCase, async: true
use Tilex.IntegrationCase, async: false

alias Tilex.Integration.Pages.PostForm
alias Tilex.Integration.Pages.PostShowPage

test "fills out form and updates post from post show", %{session: session} do
feature "fills out form and updates post from post show", %{session: session} do
Factory.insert!(:channel, name: "phoenix")
developer = Factory.insert!(:developer)

Expand Down
2 changes: 1 addition & 1 deletion test/features/developer_edits_profile_test.exs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
defmodule DeveloperEditsProfileTest do
use Tilex.IntegrationCase, async: false

test "fills out form and updates post from post show", %{session: session} do
feature "fills out form and updates post from post show", %{session: session} do
developer = Factory.insert!(:developer, email: "[email protected]")

sign_in(session, developer)
Expand Down
Loading
Loading