diff --git a/CHANGELOG.md b/CHANGELOG.md index 8d9f7d1b5..da211a1a1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -101,6 +101,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Mastodon API: Change emoji reaction reply format - Notifications: Added `pleroma:emoji_reaction` notification type - Mastodon API: Change emoji reaction reply format once more +- Configuration: `feed.logo` option for tag feed. +- Tag feed: `/tags/:tag.rss` - list public statuses by hashtag. ### Fixed diff --git a/lib/pleroma/emails/admin_email.ex b/lib/pleroma/emails/admin_email.ex index b15e4041b..d7dd4b2e0 100644 --- a/lib/pleroma/emails/admin_email.ex +++ b/lib/pleroma/emails/admin_email.ex @@ -17,7 +17,7 @@ defp instance_notify_email do end defp user_url(user) do - Helpers.feed_url(Pleroma.Web.Endpoint, :feed_redirect, user.id) + Helpers.user_feed_url(Pleroma.Web.Endpoint, :feed_redirect, user.id) end def report(to, reporter, account, statuses, comment) do diff --git a/lib/pleroma/web/controller_helper.ex b/lib/pleroma/web/controller_helper.ex index 9a4e322c9..e3d7a465b 100644 --- a/lib/pleroma/web/controller_helper.ex +++ b/lib/pleroma/web/controller_helper.ex @@ -76,8 +76,7 @@ def assign_account_by_id(%{params: %{"id" => id}} = conn, _) do end end - def try_render(conn, target, params) - when is_binary(target) do + def try_render(conn, target, params) when is_binary(target) do case render(conn, target, params) do nil -> render_error(conn, :not_implemented, "Can't display this activity") res -> res @@ -87,4 +86,8 @@ def try_render(conn, target, params) def try_render(conn, _, _) do render_error(conn, :not_implemented, "Can't display this activity") end + + @spec put_in_if_exist(map(), atom() | String.t(), any) :: map() + def put_in_if_exist(map, _key, nil), do: map + def put_in_if_exist(map, key, value), do: put_in(map, key, value) end diff --git a/lib/pleroma/web/feed/feed_view.ex b/lib/pleroma/web/feed/feed_view.ex index bb1332fd3..334802e0a 100644 --- a/lib/pleroma/web/feed/feed_view.ex +++ b/lib/pleroma/web/feed/feed_view.ex @@ -13,21 +13,53 @@ defmodule Pleroma.Web.Feed.FeedView do require Pleroma.Constants - def prepare_activity(activity) do + @spec pub_date(String.t() | DateTime.t()) :: String.t() + def pub_date(date) when is_binary(date) do + date + |> Timex.parse!("{ISO:Extended}") + |> pub_date + end + + def pub_date(%DateTime{} = date), do: Timex.format!(date, "{RFC822}") + + def prepare_activity(activity, opts \\ []) do object = activity_object(activity) + actor = + if opts[:actor] do + Pleroma.User.get_cached_by_ap_id(activity.actor) + end + %{ activity: activity, data: Map.get(object, :data), - object: object + object: object, + actor: actor } end + def most_recent_update(activities) do + with %{updated_at: updated_at} <- List.first(activities) do + NaiveDateTime.to_iso8601(updated_at) + end + end + def most_recent_update(activities, user) do (List.first(activities) || user).updated_at |> NaiveDateTime.to_iso8601() end + def feed_logo do + case Pleroma.Config.get([:feed, :logo]) do + nil -> + "#{Pleroma.Web.base_url()}/static/logo.png" + + logo -> + "#{Pleroma.Web.base_url()}#{logo}" + end + |> MediaProxy.url() + end + def logo(user) do user |> User.avatar_url() @@ -40,6 +72,8 @@ def activity_object(activity), do: Object.normalize(activity) def activity_title(%{data: %{"content" => content}}, opts \\ %{}) do content + |> Pleroma.Web.Metadata.Utils.scrub_html() + |> Pleroma.Emoji.Formatter.demojify() |> Formatter.truncate(opts[:max_length], opts[:omission]) |> escape() end @@ -50,6 +84,8 @@ def activity_content(%{data: %{"content" => content}}) do |> escape() end + def activity_content(_), do: "" + def activity_context(activity), do: activity.data["context"] def attachment_href(attachment) do diff --git a/lib/pleroma/web/feed/tag_controller.ex b/lib/pleroma/web/feed/tag_controller.ex new file mode 100644 index 000000000..9accd0872 --- /dev/null +++ b/lib/pleroma/web/feed/tag_controller.ex @@ -0,0 +1,41 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.Feed.TagController do + use Pleroma.Web, :controller + + alias Pleroma.Config + alias Pleroma.Web.ActivityPub.ActivityPub + alias Pleroma.Web.Feed.FeedView + + import Pleroma.Web.ControllerHelper, only: [put_in_if_exist: 3] + + def feed(conn, %{"tag" => raw_tag} = params) do + {format, tag} = parse_tag(raw_tag) + + activities = + %{"type" => ["Create"], "tag" => tag} + |> put_in_if_exist("max_id", params["max_id"]) + |> ActivityPub.fetch_public_activities() + + conn + |> put_resp_content_type("application/atom+xml") + |> put_view(FeedView) + |> render("tag.#{format}", + activities: activities, + tag: tag, + feed_config: Config.get([:feed]) + ) + end + + @spec parse_tag(binary() | any()) :: {format :: String.t(), tag :: String.t()} + defp parse_tag(raw_tag) when is_binary(raw_tag) do + case Enum.reverse(String.split(raw_tag, ".")) do + [format | tag] when format in ["atom", "rss"] -> {format, Enum.join(tag, ".")} + _ -> {"rss", raw_tag} + end + end + + defp parse_tag(raw_tag), do: {"rss", raw_tag} +end diff --git a/lib/pleroma/web/feed/feed_controller.ex b/lib/pleroma/web/feed/user_controller.ex similarity index 84% rename from lib/pleroma/web/feed/feed_controller.ex rename to lib/pleroma/web/feed/user_controller.ex index acc0c3e6c..f5096834b 100644 --- a/lib/pleroma/web/feed/feed_controller.ex +++ b/lib/pleroma/web/feed/user_controller.ex @@ -2,13 +2,16 @@ # Copyright © 2017-2019 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only -defmodule Pleroma.Web.Feed.FeedController do +defmodule Pleroma.Web.Feed.UserController do use Pleroma.Web, :controller alias Fallback.RedirectController alias Pleroma.User alias Pleroma.Web.ActivityPub.ActivityPub alias Pleroma.Web.ActivityPub.ActivityPubController + alias Pleroma.Web.Feed.FeedView + + import Pleroma.Web.ControllerHelper, only: [put_in_if_exist: 3] plug(Pleroma.Plugs.SetFormatPlug when action in [:feed_redirect]) @@ -27,7 +30,7 @@ def feed_redirect(%{assigns: %{format: format}} = conn, _params) def feed_redirect(conn, %{"nickname" => nickname}) do with {_, %User{} = user} <- {:fetch_user, User.get_cached_by_nickname(nickname)} do - redirect(conn, external: "#{feed_url(conn, :feed, user.nickname)}.atom") + redirect(conn, external: "#{user_feed_url(conn, :feed, user.nickname)}.atom") end end @@ -38,12 +41,13 @@ def feed(conn, %{"nickname" => nickname} = params) do "type" => ["Create"], "actor_id" => user.ap_id } - |> Map.merge(Map.take(params, ["max_id"])) + |> put_in_if_exist("max_id", params["max_id"]) |> ActivityPub.fetch_public_activities() conn |> put_resp_content_type("application/atom+xml") - |> render("feed.xml", + |> put_view(FeedView) + |> render("user.xml", user: user, activities: activities, feed_config: Pleroma.Config.get([:feed]) diff --git a/lib/pleroma/web/metadata/feed.ex b/lib/pleroma/web/metadata/feed.ex index 8043e6c54..ee48913a7 100644 --- a/lib/pleroma/web/metadata/feed.ex +++ b/lib/pleroma/web/metadata/feed.ex @@ -16,7 +16,7 @@ def build_tags(%{user: user}) do [ rel: "alternate", type: "application/atom+xml", - href: Helpers.feed_path(Endpoint, :feed, user.nickname) <> ".atom" + href: Helpers.user_feed_path(Endpoint, :feed, user.nickname) <> ".atom" ], []} ] end diff --git a/lib/pleroma/web/metadata/utils.ex b/lib/pleroma/web/metadata/utils.ex index 589d11901..000bd9f66 100644 --- a/lib/pleroma/web/metadata/utils.ex +++ b/lib/pleroma/web/metadata/utils.ex @@ -21,15 +21,22 @@ def scrub_html_and_truncate(%{data: %{"content" => content}} = object) do def scrub_html_and_truncate(content, max_length \\ 200) when is_binary(content) do content - # html content comes from DB already encoded, decode first and scrub after - |> HtmlEntities.decode() - |> String.replace(~r//, " ") - |> HTML.strip_tags() + |> scrub_html |> Emoji.Formatter.demojify() |> HtmlEntities.decode() |> Formatter.truncate(max_length) end + def scrub_html(content) when is_binary(content) do + content + # html content comes from DB already encoded, decode first and scrub after + |> HtmlEntities.decode() + |> String.replace(~r//, " ") + |> HTML.strip_tags() + end + + def scrub_html(content), do: content + def attachment_url(url) do MediaProxy.url(url) end diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index ef6e5a565..b5c1d85c7 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -527,8 +527,10 @@ defmodule Pleroma.Web.Router do get("/notice/:id", OStatus.OStatusController, :notice) get("/notice/:id/embed_player", OStatus.OStatusController, :notice_player) - get("/users/:nickname/feed", Feed.FeedController, :feed) - get("/users/:nickname", Feed.FeedController, :feed_redirect) + get("/users/:nickname/feed", Feed.UserController, :feed, as: :user_feed) + get("/users/:nickname", Feed.UserController, :feed_redirect, as: :user_feed) + + get("/tags/:tag", Feed.TagController, :feed, as: :tag_feed) end scope "/", Pleroma.Web do diff --git a/lib/pleroma/web/templates/feed/feed/_activity.xml.eex b/lib/pleroma/web/templates/feed/feed/_activity.xml.eex index 514eacaed..ac8a75009 100644 --- a/lib/pleroma/web/templates/feed/feed/_activity.xml.eex +++ b/lib/pleroma/web/templates/feed/feed/_activity.xml.eex @@ -9,7 +9,7 @@ <%= activity_context(@activity) %> - + <%= if @data["summary"] do %> <%= @data["summary"] %> diff --git a/lib/pleroma/web/templates/feed/feed/_tag_activity.atom.eex b/lib/pleroma/web/templates/feed/feed/_tag_activity.atom.eex new file mode 100644 index 000000000..da4fa6d6c --- /dev/null +++ b/lib/pleroma/web/templates/feed/feed/_tag_activity.atom.eex @@ -0,0 +1,51 @@ + + http://activitystrea.ms/schema/1.0/note + http://activitystrea.ms/schema/1.0/post + + <%= render @view_module, "_tag_author.atom", assigns %> + + <%= @data["id"] %> + <%= activity_title(@object, Keyword.get(@feed_config, :post_title, %{})) %> + <%= activity_content(@object) %> + + <%= if @activity.local do %> + + + <% else %> + + <% end %> + + <%= @data["published"] %> + <%= @data["published"] %> + + + <%= activity_context(@activity) %> + + + + <%= if @data["summary"] do %> + <%= @data["summary"] %> + <% end %> + + <%= for id <- @activity.recipients do %> + <%= if id == Pleroma.Constants.as_public() do %> + + <% else %> + <%= unless Regex.match?(~r/^#{Pleroma.Web.base_url()}.+followers$/, id) do %> + + <% end %> + <% end %> + <% end %> + + <%= for tag <- @data["tag"] || [] do %> + + <% end %> + + <%= for {emoji, file} <- @data["emoji"] || %{} do %> + + <% end %> + diff --git a/lib/pleroma/web/templates/feed/feed/_tag_activity.xml.eex b/lib/pleroma/web/templates/feed/feed/_tag_activity.xml.eex new file mode 100644 index 000000000..295574df1 --- /dev/null +++ b/lib/pleroma/web/templates/feed/feed/_tag_activity.xml.eex @@ -0,0 +1,15 @@ + + <%= activity_title(@object, Keyword.get(@feed_config, :post_title, %{})) %> + + + <%= activity_context(@activity) %> + <%= activity_context(@activity) %> + <%= pub_date(@data["published"]) %> + + <%= activity_content(@object) %> + <%= for attachment <- @data["attachment"] || [] do %> + + <% end %> + + + diff --git a/lib/pleroma/web/templates/feed/feed/_tag_author.atom.eex b/lib/pleroma/web/templates/feed/feed/_tag_author.atom.eex new file mode 100644 index 000000000..997c4936e --- /dev/null +++ b/lib/pleroma/web/templates/feed/feed/_tag_author.atom.eex @@ -0,0 +1,18 @@ + + http://activitystrea.ms/schema/1.0/person + <%= @actor.ap_id %> + <%= @actor.ap_id %> + <%= @actor.nickname %> + <%= escape(@actor.bio) %> + + <%= if User.banner_url(@actor) do %> + + <% end %> + <%= if @actor.local do %> + true + <% end %> + + <%= @actor.nickname %> + <%= @actor.name %> + <%= escape(@actor.bio) %> + diff --git a/lib/pleroma/web/templates/feed/feed/tag.atom.eex b/lib/pleroma/web/templates/feed/feed/tag.atom.eex new file mode 100644 index 000000000..a288539ed --- /dev/null +++ b/lib/pleroma/web/templates/feed/feed/tag.atom.eex @@ -0,0 +1,22 @@ + + + + + <%= '#{tag_feed_url(@conn, :feed, @tag)}.rss' %> + #<%= @tag %> + + These are public toots tagged with #<%= @tag %>. You can interact with them if you have an account anywhere in the fediverse. + <%= feed_logo() %> + <%= most_recent_update(@activities) %> + + <%= for activity <- @activities do %> + <%= render @view_module, "_tag_activity.atom", Map.merge(assigns, prepare_activity(activity, actor: true)) %> + <% end %> + diff --git a/lib/pleroma/web/templates/feed/feed/tag.rss.eex b/lib/pleroma/web/templates/feed/feed/tag.rss.eex new file mode 100644 index 000000000..eeda01a04 --- /dev/null +++ b/lib/pleroma/web/templates/feed/feed/tag.rss.eex @@ -0,0 +1,15 @@ + + + + + + #<%= @tag %> + These are public toots tagged with #<%= @tag %>. You can interact with them if you have an account anywhere in the fediverse. + <%= '#{tag_feed_url(@conn, :feed, @tag)}.rss' %> + <%= feed_logo() %> + 2b90d9 + <%= for activity <- @activities do %> + <%= render @view_module, "_tag_activity.xml", Map.merge(assigns, prepare_activity(activity)) %> + <% end %> + + diff --git a/lib/pleroma/web/templates/feed/feed/feed.xml.eex b/lib/pleroma/web/templates/feed/feed/user.xml.eex similarity index 67% rename from lib/pleroma/web/templates/feed/feed/feed.xml.eex rename to lib/pleroma/web/templates/feed/feed/user.xml.eex index 5ae36d345..d274c08ae 100644 --- a/lib/pleroma/web/templates/feed/feed/feed.xml.eex +++ b/lib/pleroma/web/templates/feed/feed/user.xml.eex @@ -6,16 +6,16 @@ xmlns:poco="http://portablecontacts.net/spec/1.0" xmlns:ostatus="http://ostatus.org/schema/1.0"> - <%= feed_url(@conn, :feed, @user.nickname) <> ".atom" %> + <%= user_feed_url(@conn, :feed, @user.nickname) <> ".atom" %> <%= @user.nickname <> "'s timeline" %> <%= most_recent_update(@activities, @user) %> <%= logo(@user) %> - + <%= render @view_module, "_author.xml", assigns %> <%= if last_activity(@activities) do %> - + <% end %> <%= for activity <- @activities do %> diff --git a/test/emails/admin_email_test.exs b/test/emails/admin_email_test.exs index ad89f9213..383cc3459 100644 --- a/test/emails/admin_email_test.exs +++ b/test/emails/admin_email_test.exs @@ -19,8 +19,8 @@ test "build report email" do AdminEmail.report(to_user, reporter, account, [%{name: "Test", id: "12"}], "Test comment") status_url = Helpers.o_status_url(Pleroma.Web.Endpoint, :notice, "12") - reporter_url = Helpers.feed_url(Pleroma.Web.Endpoint, :feed_redirect, reporter.id) - account_url = Helpers.feed_url(Pleroma.Web.Endpoint, :feed_redirect, account.id) + reporter_url = Helpers.user_feed_url(Pleroma.Web.Endpoint, :feed_redirect, reporter.id) + account_url = Helpers.user_feed_url(Pleroma.Web.Endpoint, :feed_redirect, account.id) assert res.to == [{to_user.name, to_user.email}] assert res.from == {config[:name], config[:notify_email]} diff --git a/test/user_test.exs b/test/user_test.exs index 158f98e66..1b5e63bd4 100644 --- a/test/user_test.exs +++ b/test/user_test.exs @@ -585,7 +585,7 @@ test "returns an ap_id for a user" do user = insert(:user) assert User.ap_id(user) == - Pleroma.Web.Router.Helpers.feed_url( + Pleroma.Web.Router.Helpers.user_feed_url( Pleroma.Web.Endpoint, :feed_redirect, user.nickname @@ -596,7 +596,7 @@ test "returns an ap_followers link for a user" do user = insert(:user) assert User.ap_followers(user) == - Pleroma.Web.Router.Helpers.feed_url( + Pleroma.Web.Router.Helpers.user_feed_url( Pleroma.Web.Endpoint, :feed_redirect, user.nickname diff --git a/test/web/feed/tag_controller_test.exs b/test/web/feed/tag_controller_test.exs new file mode 100644 index 000000000..2aa1b9587 --- /dev/null +++ b/test/web/feed/tag_controller_test.exs @@ -0,0 +1,154 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.Feed.TagControllerTest do + use Pleroma.Web.ConnCase + + import Pleroma.Factory + import SweetXml + + alias Pleroma.Web.Feed.FeedView + + clear_config([:feed]) + + test "gets a feed (ATOM)", %{conn: conn} do + Pleroma.Config.put( + [:feed, :post_title], + %{max_length: 25, omission: "..."} + ) + + user = insert(:user) + {:ok, activity1} = Pleroma.Web.CommonAPI.post(user, %{"status" => "yeah #PleromaArt"}) + + object = Pleroma.Object.normalize(activity1) + + object_data = + Map.put(object.data, "attachment", [ + %{ + "url" => [ + %{ + "href" => + "https://peertube.moe/static/webseed/df5f464b-be8d-46fb-ad81-2d4c2d1630e3-480.mp4", + "mediaType" => "video/mp4", + "type" => "Link" + } + ] + } + ]) + + object + |> Ecto.Changeset.change(data: object_data) + |> Pleroma.Repo.update() + + {:ok, _activity2} = + Pleroma.Web.CommonAPI.post(user, %{"status" => "42 This is :moominmamma #PleromaArt"}) + + {:ok, _activity3} = Pleroma.Web.CommonAPI.post(user, %{"status" => "This is :moominmamma"}) + + response = + conn + |> put_req_header("content-type", "application/atom+xml") + |> get(tag_feed_path(conn, :feed, "pleromaart.atom")) + |> response(200) + + xml = parse(response) + + assert xpath(xml, ~x"//feed/title/text()") == '#pleromaart' + + assert xpath(xml, ~x"//feed/entry/title/text()"l) == [ + '42 This is :moominmamm...', + 'yeah #PleromaArt' + ] + + assert xpath(xml, ~x"//feed/entry/author/name/text()"ls) == [user.nickname, user.nickname] + assert xpath(xml, ~x"//feed/entry/author/id/text()"ls) == [user.ap_id, user.ap_id] + end + + test "gets a feed (RSS)", %{conn: conn} do + Pleroma.Config.put( + [:feed, :post_title], + %{max_length: 25, omission: "..."} + ) + + user = insert(:user) + {:ok, activity1} = Pleroma.Web.CommonAPI.post(user, %{"status" => "yeah #PleromaArt"}) + + object = Pleroma.Object.normalize(activity1) + + object_data = + Map.put(object.data, "attachment", [ + %{ + "url" => [ + %{ + "href" => + "https://peertube.moe/static/webseed/df5f464b-be8d-46fb-ad81-2d4c2d1630e3-480.mp4", + "mediaType" => "video/mp4", + "type" => "Link" + } + ] + } + ]) + + object + |> Ecto.Changeset.change(data: object_data) + |> Pleroma.Repo.update() + + {:ok, activity2} = + Pleroma.Web.CommonAPI.post(user, %{"status" => "42 This is :moominmamma #PleromaArt"}) + + {:ok, _activity3} = Pleroma.Web.CommonAPI.post(user, %{"status" => "This is :moominmamma"}) + + response = + conn + |> put_req_header("content-type", "application/rss+xml") + |> get(tag_feed_path(conn, :feed, "pleromaart.rss")) + |> response(200) + + xml = parse(response) + assert xpath(xml, ~x"//channel/title/text()") == '#pleromaart' + + assert xpath(xml, ~x"//channel/description/text()"s) == + "These are public toots tagged with #pleromaart. You can interact with them if you have an account anywhere in the fediverse." + + assert xpath(xml, ~x"//channel/link/text()") == + '#{Pleroma.Web.base_url()}/tags/pleromaart.rss' + + assert xpath(xml, ~x"//channel/webfeeds:logo/text()") == + '#{Pleroma.Web.base_url()}/static/logo.png' + + assert xpath(xml, ~x"//channel/item/title/text()"l) == [ + '42 This is :moominmamm...', + 'yeah #PleromaArt' + ] + + assert xpath(xml, ~x"//channel/item/pubDate/text()"sl) == [ + FeedView.pub_date(activity1.data["published"]), + FeedView.pub_date(activity2.data["published"]) + ] + + assert xpath(xml, ~x"//channel/item/enclosure/@url"sl) == [ + "https://peertube.moe/static/webseed/df5f464b-be8d-46fb-ad81-2d4c2d1630e3-480.mp4" + ] + + obj1 = Pleroma.Object.normalize(activity1) + obj2 = Pleroma.Object.normalize(activity2) + + assert xpath(xml, ~x"//channel/item/description/text()"sl) == [ + HtmlEntities.decode(FeedView.activity_content(obj2)), + HtmlEntities.decode(FeedView.activity_content(obj1)) + ] + + response = + conn + |> put_req_header("content-type", "application/atom+xml") + |> get(tag_feed_path(conn, :feed, "pleromaart")) + |> response(200) + + xml = parse(response) + assert xpath(xml, ~x"//channel/title/text()") == '#pleromaart' + + assert xpath(xml, ~x"//channel/description/text()"s) == + "These are public toots tagged with #pleromaart. You can interact with them if you have an account anywhere in the fediverse." + end +end diff --git a/test/web/feed/feed_controller_test.exs b/test/web/feed/user_controller_test.exs similarity index 97% rename from test/web/feed/feed_controller_test.exs rename to test/web/feed/user_controller_test.exs index 6f61acf43..41cc9e07e 100644 --- a/test/web/feed/feed_controller_test.exs +++ b/test/web/feed/user_controller_test.exs @@ -2,7 +2,7 @@ # Copyright © 2017-2019 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only -defmodule Pleroma.Web.Feed.FeedControllerTest do +defmodule Pleroma.Web.Feed.UserControllerTest do use Pleroma.Web.ConnCase import Pleroma.Factory @@ -49,7 +49,7 @@ test "gets a feed", %{conn: conn} do resp = conn |> put_req_header("content-type", "application/atom+xml") - |> get("/users/#{user.nickname}/feed.atom") + |> get(user_feed_path(conn, :feed, user.nickname)) |> response(200) activity_titles = @@ -65,7 +65,7 @@ test "returns 404 for a missing feed", %{conn: conn} do conn = conn |> put_req_header("content-type", "application/atom+xml") - |> get("/users/nonexisting/feed.atom") + |> get(user_feed_path(conn, :feed, "nonexisting")) assert response(conn, 404) end @@ -91,7 +91,7 @@ test "undefined format. it returns error when user not found", %{conn: conn} do response = conn |> put_req_header("accept", "application/xml") - |> get("/users/jimm") + |> get(user_feed_path(conn, :feed, "jimm")) |> response(404) assert response == ~S({"error":"Not found"})