From 35f3f0a3cd1afd0e4aad34ea42bfeaec5990f620 Mon Sep 17 00:00:00 2001 From: mydearxym Date: Mon, 19 Apr 2021 12:04:27 +0800 Subject: [PATCH 1/3] feat(article-comments): pin/unpin --- lib/groupher_server/cms/article_comment.ex | 5 ++- .../cms/article_pined_comment.ex | 44 +++++++++++++++++++ lib/groupher_server/cms/cms.ex | 3 ++ .../cms/delegates/article_comment.ex | 40 ++++++++++++++++- lib/groupher_server/cms/post.ex | 2 + lib/helper/types.ex | 3 ++ ...0419020056_add_pin_to_article_comments.exs | 24 ++++++++++ .../cms/article_comment_test.exs | 34 +++++++++++++- .../mutation/cms/repo_test.exs | 1 - 9 files changed, 152 insertions(+), 4 deletions(-) create mode 100644 lib/groupher_server/cms/article_pined_comment.ex create mode 100644 priv/repo/migrations/20210419020056_add_pin_to_article_comments.exs diff --git a/lib/groupher_server/cms/article_comment.ex b/lib/groupher_server/cms/article_comment.ex index 65d09cb60..61fcf245b 100644 --- a/lib/groupher_server/cms/article_comment.ex +++ b/lib/groupher_server/cms/article_comment.ex @@ -20,7 +20,7 @@ defmodule GroupherServer.CMS.ArticleComment do @required_fields ~w(body_html author_id)a @optional_fields ~w(post_id job_id reply_to_id replies_count is_folded is_reported is_deleted floor is_article_author)a - @updatable_fields ~w(is_folded is_reported is_deleted floor upvotes_count)a + @updatable_fields ~w(is_folded is_reported is_deleted floor upvotes_count is_pined)a @max_participator_count 5 @max_parent_replies_count 3 @@ -63,6 +63,9 @@ defmodule GroupherServer.CMS.ArticleComment do field(:is_article_author, :boolean, default: false) field(:upvotes_count, :integer, default: 0) + # 是否置顶 + field(:is_pined, :boolean, default: false) + belongs_to(:author, Accounts.User, foreign_key: :author_id) belongs_to(:post, Post, foreign_key: :post_id) belongs_to(:job, Job, foreign_key: :job_id) diff --git a/lib/groupher_server/cms/article_pined_comment.ex b/lib/groupher_server/cms/article_pined_comment.ex new file mode 100644 index 000000000..c448eab83 --- /dev/null +++ b/lib/groupher_server/cms/article_pined_comment.ex @@ -0,0 +1,44 @@ +defmodule GroupherServer.CMS.ArticlePinedComment do + @moduledoc false + alias __MODULE__ + + use Ecto.Schema + use Accessible + + import Ecto.Changeset + + alias GroupherServer.CMS + + alias CMS.{ + Post, + Job, + ArticleComment + } + + # alias Helper.HTML + + @required_fields ~w(article_comment_id)a + @optional_fields ~w(post_id job_id)a + + @type t :: %ArticlePinedComment{} + schema "articles_pined_comments" do + belongs_to(:article_comment, ArticleComment, foreign_key: :article_comment_id) + belongs_to(:post, Post, foreign_key: :post_id) + belongs_to(:job, Job, foreign_key: :job_id) + + timestamps(type: :utc_datetime) + end + + @doc false + def changeset(%ArticlePinedComment{} = article_pined_comment, attrs) do + article_pined_comment + |> cast(attrs, @required_fields ++ @optional_fields) + |> validate_required(@required_fields) + end + + # @doc false + def update_changeset(%ArticlePinedComment{} = article_pined_comment, attrs) do + article_pined_comment + |> cast(attrs, @required_fields ++ @updatable_fields) + end +end diff --git a/lib/groupher_server/cms/cms.ex b/lib/groupher_server/cms/cms.ex index 405b7ccfb..d4eea0ecb 100644 --- a/lib/groupher_server/cms/cms.ex +++ b/lib/groupher_server/cms/cms.ex @@ -129,6 +129,9 @@ defmodule GroupherServer.CMS do defdelegate delete_article_comment(comment_id, user), to: ArticleComment defdelegate reply_article_comment(comment_id, args, user), to: ArticleComment + defdelegate pin_article_comment(comment_id), to: ArticleComment + defdelegate undo_pin_article_comment(comment_id), to: ArticleComment + defdelegate make_emotion(comment_id, args, user), to: ArticleComment defdelegate fold_article_comment(comment_id, user), to: ArticleComment defdelegate unfold_article_comment(comment_id, user), to: ArticleComment diff --git a/lib/groupher_server/cms/delegates/article_comment.ex b/lib/groupher_server/cms/delegates/article_comment.ex index f94d972b9..e00356474 100644 --- a/lib/groupher_server/cms/delegates/article_comment.ex +++ b/lib/groupher_server/cms/delegates/article_comment.ex @@ -16,6 +16,7 @@ defmodule GroupherServer.CMS.Delegate.ArticleComment do alias CMS.{ ArticleComment, + ArticlePinedComment, ArticleCommentUpvote, ArticleCommentReply, ArticleCommentUserEmotion, @@ -136,6 +137,42 @@ defmodule GroupherServer.CMS.Delegate.ArticleComment do |> done() end + @doc "pin a comment" + def pin_article_comment(comment_id) do + with {:ok, comment} <- ORM.find(ArticleComment, comment_id), + {:ok, full_comment} <- get_full_comment(comment.id), + {:ok, info} <- match(full_comment.thread) do + Multi.new() + |> Multi.run(:update_comment_flag, fn _, _ -> + ORM.update(comment, %{is_pined: true}) + end) + |> Multi.run(:add_pined_comment, fn _, _ -> + ArticlePinedComment + |> ORM.create( + %{article_comment_id: comment.id} + |> Map.put(info.foreign_key, full_comment.article.id) + ) + end) + |> Repo.transaction() + |> upsert_comment_result() + end + end + + def undo_pin_article_comment(comment_id) do + with {:ok, comment} <- ORM.find(ArticleComment, comment_id) do + Multi.new() + |> Multi.run(:update_comment_flag, fn _, _ -> + ORM.update(comment, %{is_pined: false}) + end) + |> Multi.run(:remove_pined_comment, fn _, _ -> + ORM.findby_delete(ArticlePinedComment, %{article_comment_id: comment.id}) + end) + |> Repo.transaction() + |> upsert_comment_result() + end + end + + # TODO: remove pined record if need def delete_article_comment(comment_id, %User{} = _user) do with {:ok, comment} <- ORM.find(ArticleComment, comment_id) do @@ -514,7 +551,7 @@ defmodule GroupherServer.CMS.Delegate.ArticleComment do with {:ok, article_with_author} <- Repo.preload(article, author: :user) |> done(), article_author <- get_in(article_with_author, [:author, :user]) do # - article_info = %{title: article.title} + article_info = %{title: article.title, id: article.id} author_info = %{ id: article_author.id, @@ -531,6 +568,7 @@ defmodule GroupherServer.CMS.Delegate.ArticleComment do defp upsert_comment_result({:ok, %{check_article_author_upvoted: result}}), do: {:ok, result} defp upsert_comment_result({:ok, %{update_report_flag: result}}), do: {:ok, result} defp upsert_comment_result({:ok, %{update_comment_emotion: result}}), do: {:ok, result} + defp upsert_comment_result({:ok, %{update_comment_flag: result}}), do: {:ok, result} defp upsert_comment_result({:error, :create_comment, result, _steps}) do {:error, result} diff --git a/lib/groupher_server/cms/post.ex b/lib/groupher_server/cms/post.ex index 7408bb277..57277ef0c 100644 --- a/lib/groupher_server/cms/post.ex +++ b/lib/groupher_server/cms/post.ex @@ -13,6 +13,7 @@ defmodule GroupherServer.CMS.Post do Embeds, Author, ArticleComment, + ArticlePinedComment, Community, PostComment, PostCommunityFlag, @@ -58,6 +59,7 @@ defmodule GroupherServer.CMS.Post do has_many(:comments, {"posts_comments", PostComment}) has_many(:article_comments, {"articles_comments", ArticleComment}) + has_many(:article_pined_comments, {"articles_pined_comments", ArticlePinedComment}) has_many(:favorites, {"posts_favorites", PostFavorite}) has_many(:stars, {"posts_stars", PostStar}) diff --git a/lib/helper/types.ex b/lib/helper/types.ex index 469551ad0..cc66ee8ed 100644 --- a/lib/helper/types.ex +++ b/lib/helper/types.ex @@ -29,6 +29,9 @@ defmodule Helper.Types do } @type article_thread :: :post | :job + @type article_common :: %{ + title: String.t() + } @type article_info :: %{ thread: article_thread, article: %{ diff --git a/priv/repo/migrations/20210419020056_add_pin_to_article_comments.exs b/priv/repo/migrations/20210419020056_add_pin_to_article_comments.exs new file mode 100644 index 000000000..7631fd275 --- /dev/null +++ b/priv/repo/migrations/20210419020056_add_pin_to_article_comments.exs @@ -0,0 +1,24 @@ +defmodule GroupherServer.Repo.Migrations.AddPinToArticleComments do + use Ecto.Migration + + def change do + alter table(:articles_comments) do + add(:is_pined, :boolean, default: false) + end + + create table(:articles_pined_comments) do + add(:post_id, references(:cms_posts, on_delete: :delete_all)) + add(:job_id, references(:cms_jobs, on_delete: :delete_all)) + + add(:article_comment_id, references(:articles_comments, on_delete: :delete_all), null: false) + + timestamps() + end + + create(index(:articles_pined_comments, [:post_id])) + create(index(:articles_pined_comments, [:job_id])) + create(index(:articles_pined_comments, [:article_comment_id])) + + create(unique_index(:articles_pined_comments, [:post_id, :job_id, :article_comment_id])) + end +end diff --git a/test/groupher_server/cms/article_comment_test.exs b/test/groupher_server/cms/article_comment_test.exs index 2582a7da3..ffca629cf 100644 --- a/test/groupher_server/cms/article_comment_test.exs +++ b/test/groupher_server/cms/article_comment_test.exs @@ -6,7 +6,7 @@ defmodule GroupherServer.Test.CMS.ArticleComment do alias Helper.ORM alias GroupherServer.{Accounts, CMS} - alias CMS.{ArticleComment, Embeds, Post, Job} + alias CMS.{ArticleComment, ArticlePinedComment, Embeds, Post, Job} @delete_hint CMS.ArticleComment.delete_hint() @report_threshold_for_fold ArticleComment.report_threshold_for_fold() @@ -218,6 +218,38 @@ defmodule GroupherServer.Test.CMS.ArticleComment do end end + describe "[article comment pin/unpin]" do + @tag :wip2 + test "user can pin a comment", ~m(user post)a do + {:ok, comment} = CMS.create_article_comment(:post, post.id, "commment", user) + {:ok, comment} = ORM.find(ArticleComment, comment.id) + + assert not comment.is_pined + + {:ok, comment} = CMS.pin_article_comment(comment.id) + {:ok, comment} = ORM.find(ArticleComment, comment.id) + + assert comment.is_pined + + {:ok, pined_record} = ArticlePinedComment |> ORM.find_by(%{post_id: post.id}) + assert pined_record.post_id == post.id + + {:ok, pined_record} = + ORM.find(ArticlePinedComment, pined_record.id, preload: :article_comment) + end + + @tag :wip2 + test "user can unpin a comment", ~m(user post)a do + {:ok, comment} = CMS.create_article_comment(:post, post.id, "commment", user) + + {:ok, _comment} = CMS.pin_article_comment(comment.id) + {:ok, comment} = CMS.undo_pin_article_comment(comment.id) + + assert not comment.is_pined + assert {:error, _} = ArticlePinedComment |> ORM.find_by(%{article_comment_id: comment.id}) + end + end + describe "[article comment report/unreport]" do @tag :wip test "user can report a comment", ~m(user post)a do diff --git a/test/groupher_server_web/mutation/cms/repo_test.exs b/test/groupher_server_web/mutation/cms/repo_test.exs index 956589f75..a5e18fb42 100644 --- a/test/groupher_server_web/mutation/cms/repo_test.exs +++ b/test/groupher_server_web/mutation/cms/repo_test.exs @@ -132,7 +132,6 @@ defmodule GroupherServer.Test.Mutation.Repo do } } """ - @tag :wip2 test "update git-repo with valid attrs" do {:ok, user} = db_insert(:user) user_conn = simu_conn(:user, user) From f6e31e00db0f3f761d8d18b55ad5a2f69b9b38f8 Mon Sep 17 00:00:00 2001 From: mydearxym Date: Mon, 19 Apr 2021 14:03:36 +0800 Subject: [PATCH 2/3] feat(article-comments): pin/unpin for paged comments --- lib/groupher_server/cms/article_comment.ex | 4 ++ .../cms/delegates/article_comment.ex | 57 +++++++++++++-- .../cms/article_comment_test.exs | 72 ++++++++++++++++++- 3 files changed, 127 insertions(+), 6 deletions(-) diff --git a/lib/groupher_server/cms/article_comment.ex b/lib/groupher_server/cms/article_comment.ex index 61fcf245b..be9b2abc7 100644 --- a/lib/groupher_server/cms/article_comment.ex +++ b/lib/groupher_server/cms/article_comment.ex @@ -32,6 +32,9 @@ defmodule GroupherServer.CMS.ArticleComment do # 举报超过此数评论会被自动折叠 @report_threshold_for_fold 5 + # 每篇文章最多含有置顶评论的条数 + @pined_comment_limit 10 + @doc "latest participators stores in article comment_participators field" def max_participator_count(), do: @max_participator_count @doc "latest replies stores in article_comment replies field, used for frontend display" @@ -44,6 +47,7 @@ defmodule GroupherServer.CMS.ArticleComment do def delete_hint(), do: @delete_hint def report_threshold_for_fold, do: @report_threshold_for_fold + def pined_comment_limit, do: @pined_comment_limit @type t :: %ArticleComment{} schema "articles_comments" do diff --git a/lib/groupher_server/cms/delegates/article_comment.ex b/lib/groupher_server/cms/delegates/article_comment.ex index e00356474..d286c4f92 100644 --- a/lib/groupher_server/cms/delegates/article_comment.ex +++ b/lib/groupher_server/cms/delegates/article_comment.ex @@ -36,6 +36,7 @@ defmodule GroupherServer.CMS.Delegate.ArticleComment do @report_threshold_for_fold ArticleComment.report_threshold_for_fold() @default_comment_meta Embeds.ArticleCommentMeta.default_meta() + @pined_comment_limit ArticleComment.pined_comment_limit() @doc """ list paged article comments @@ -44,9 +45,10 @@ defmodule GroupherServer.CMS.Delegate.ArticleComment do with {:ok, thread_query} <- match(thread, :query, article_id) do ArticleComment |> where(^thread_query) - |> where([c], c.is_folded == false and c.is_reported == false) + |> where([c], c.is_folded == false and c.is_reported == false and c.is_pined == false) |> QueryBuilder.filter_pack(filters) |> ORM.paginater(~m(page size)a) + |> add_pined_comments_ifneed(thread, article_id, filters) |> done() end end @@ -60,19 +62,53 @@ defmodule GroupherServer.CMS.Delegate.ArticleComment do with {:ok, thread_query} <- match(thread, :query, article_id) do ArticleComment |> where(^thread_query) - |> where([c], c.is_folded == false and c.is_reported == false) + |> where([c], c.is_folded == false and c.is_reported == false and c.is_pined == false) |> QueryBuilder.filter_pack(filters) |> ORM.paginater(~m(page size)a) |> check_viewer_has_emotioned(user) + |> add_pined_comments_ifneed(thread, article_id, filters) |> done() end end + # TODO: @pined_comment_limit 10 + defp add_pined_comments_ifneed(%{entries: entries} = paged_comments, thread, article_id, %{ + page: 1 + }) do + with {:ok, info} <- match(thread), + query <- + from(p in ArticlePinedComment, + join: c in ArticleComment, + on: p.article_comment_id == c.id, + where: field(p, ^info.foreign_key) == ^article_id, + select: c + ), + {:ok, pined_comments} <- query |> Repo.all() |> done() do + case pined_comments do + [] -> + paged_comments + + _ -> + updated_entries = + Enum.concat(Enum.slice(pined_comments, 0, @pined_comment_limit), entries) + + pined_comment_count = length(pined_comments) + + Map.merge(paged_comments, %{ + entries: updated_entries, + total_count: paged_comments.total_count + pined_comment_count + }) + end + end + end + + defp add_pined_comments_ifneed(paged_comments, _thread, _article_id, _), do: paged_comments + def list_folded_article_comments(thread, article_id, %{page: page, size: size} = filters) do with {:ok, thread_query} <- match(thread, :query, article_id) do ArticleComment |> where(^thread_query) - |> where([c], c.is_folded == true and c.is_reported == false) + |> where([c], c.is_folded == true and c.is_reported == false and c.is_pined == false) |> QueryBuilder.filter_pack(filters) |> ORM.paginater(~m(page size)a) |> done() @@ -88,7 +124,7 @@ defmodule GroupherServer.CMS.Delegate.ArticleComment do with {:ok, thread_query} <- match(thread, :query, article_id) do ArticleComment |> where(^thread_query) - |> where([c], c.is_folded == true and c.is_reported == false) + |> where([c], c.is_folded == true and c.is_reported == false and c.is_pined == false) |> QueryBuilder.filter_pack(filters) |> ORM.paginater(~m(page size)a) |> check_viewer_has_emotioned(user) @@ -143,6 +179,19 @@ defmodule GroupherServer.CMS.Delegate.ArticleComment do {:ok, full_comment} <- get_full_comment(comment.id), {:ok, info} <- match(full_comment.thread) do Multi.new() + |> Multi.run(:checked_pined_comments_count, fn _, _ -> + count_query = + from(p in ArticlePinedComment, + where: field(p, ^info.foreign_key) == ^full_comment.article.id + ) + + pined_comments_count = Repo.aggregate(count_query, :count) + + case pined_comments_count >= @pined_comment_limit do + true -> {:error, "only support #{@pined_comment_limit} pined comment for each article"} + false -> {:ok, :pass} + end + end) |> Multi.run(:update_comment_flag, fn _, _ -> ORM.update(comment, %{is_pined: true}) end) diff --git a/test/groupher_server/cms/article_comment_test.exs b/test/groupher_server/cms/article_comment_test.exs index ffca629cf..28457acd0 100644 --- a/test/groupher_server/cms/article_comment_test.exs +++ b/test/groupher_server/cms/article_comment_test.exs @@ -11,6 +11,7 @@ defmodule GroupherServer.Test.CMS.ArticleComment do @delete_hint CMS.ArticleComment.delete_hint() @report_threshold_for_fold ArticleComment.report_threshold_for_fold() @default_comment_meta Embeds.ArticleCommentMeta.default_meta() + @pined_comment_limit ArticleComment.pined_comment_limit() setup do {:ok, user} = db_insert(:user) @@ -219,7 +220,6 @@ defmodule GroupherServer.Test.CMS.ArticleComment do end describe "[article comment pin/unpin]" do - @tag :wip2 test "user can pin a comment", ~m(user post)a do {:ok, comment} = CMS.create_article_comment(:post, post.id, "commment", user) {:ok, comment} = ORM.find(ArticleComment, comment.id) @@ -238,7 +238,6 @@ defmodule GroupherServer.Test.CMS.ArticleComment do ORM.find(ArticlePinedComment, pined_record.id, preload: :article_comment) end - @tag :wip2 test "user can unpin a comment", ~m(user post)a do {:ok, comment} = CMS.create_article_comment(:post, post.id, "commment", user) @@ -248,6 +247,17 @@ defmodule GroupherServer.Test.CMS.ArticleComment do assert not comment.is_pined assert {:error, _} = ArticlePinedComment |> ORM.find_by(%{article_comment_id: comment.id}) end + + @tag :wip2 + test "pined comments has a limit for each article", ~m(user post)a do + {:ok, comment} = CMS.create_article_comment(:post, post.id, "commment", user) + + Enum.reduce(0..(@pined_comment_limit - 1), [], fn _, _acc -> + {:ok, _comment} = CMS.pin_article_comment(comment.id) + end) + + assert {:error, _} = CMS.pin_article_comment(comment.id) + end end describe "[article comment report/unreport]" do @@ -337,6 +347,64 @@ defmodule GroupherServer.Test.CMS.ArticleComment do assert total_count == paged_comments.total_count end + @tag :wip2 + test "paged article comments should contains pined comments at top position", + ~m(user post)a do + total_count = 20 + page_number = 1 + page_size = 5 + + all_comments = + Enum.reduce(1..total_count, [], fn _, acc -> + {:ok, comment} = CMS.create_article_comment(:post, post.id, "commment", user) + + acc ++ [comment] + end) + + {:ok, random_comment_1} = CMS.create_article_comment(:post, post.id, "pin commment", user) + {:ok, random_comment_2} = CMS.create_article_comment(:post, post.id, "pin commment2", user) + + {:ok, pined_comment_1} = CMS.pin_article_comment(random_comment_1.id) + {:ok, pined_comment_2} = CMS.pin_article_comment(random_comment_2.id) + + {:ok, paged_comments} = + CMS.list_article_comments(:post, post.id, %{page: page_number, size: page_size}) + + assert pined_comment_1.id == List.first(paged_comments.entries) |> Map.get(:id) + assert pined_comment_2.id == Enum.at(paged_comments.entries, 1) |> Map.get(:id) + + assert paged_comments.total_count == total_count + 2 + end + + @tag :wip2 + test "only page 1 have pined coments", + ~m(user post)a do + total_count = 20 + page_number = 2 + page_size = 5 + + all_comments = + Enum.reduce(1..total_count, [], fn _, acc -> + {:ok, comment} = CMS.create_article_comment(:post, post.id, "commment", user) + + acc ++ [comment] + end) + + {:ok, random_comment_1} = CMS.create_article_comment(:post, post.id, "pin commment", user) + {:ok, random_comment_2} = CMS.create_article_comment(:post, post.id, "pin commment2", user) + + {:ok, pined_comment_1} = CMS.pin_article_comment(random_comment_1.id) + {:ok, pined_comment_2} = CMS.pin_article_comment(random_comment_2.id) + + {:ok, paged_comments} = + CMS.list_article_comments(:post, post.id, %{page: page_number, size: page_size}) + + assert not exist_in?(pined_comment_1, paged_comments.entries) + assert not exist_in?(pined_comment_2, paged_comments.entries) + + assert paged_comments.total_count == total_count + end + @tag :wip test "paged article comments should not contains folded and repoted comments", ~m(user post)a do From fcc9bbff2237aed2ab46000ac1fbb0de6de16968 Mon Sep 17 00:00:00 2001 From: mydearxym Date: Mon, 19 Apr 2021 14:23:57 +0800 Subject: [PATCH 3/3] refactor(article-comments): improve tests --- .../groupher_server/cms/article_comment_test.exs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/test/groupher_server/cms/article_comment_test.exs b/test/groupher_server/cms/article_comment_test.exs index 28457acd0..9baaec6d3 100644 --- a/test/groupher_server/cms/article_comment_test.exs +++ b/test/groupher_server/cms/article_comment_test.exs @@ -23,23 +23,27 @@ defmodule GroupherServer.Test.CMS.ArticleComment do end describe "[basic article comment]" do + @tag :wip2 test "post, job are supported by article comment.", ~m(user post job)a do post_comment_1 = "post_comment 1" post_comment_2 = "post_comment 2" job_comment_1 = "job_comment 1" job_comment_2 = "job_comment 2" - {:ok, _} = CMS.create_article_comment(:post, post.id, post_comment_1, user) - {:ok, _} = CMS.create_article_comment(:post, post.id, post_comment_2, user) + {:ok, post_comment_1} = CMS.create_article_comment(:post, post.id, "post_comment 1", user) + {:ok, post_comment_2} = CMS.create_article_comment(:post, post.id, "post_comment 2", user) - {:ok, _} = CMS.create_article_comment(:job, job.id, job_comment_1, user) - {:ok, _} = CMS.create_article_comment(:job, job.id, job_comment_2, user) + {:ok, job_comment_1} = CMS.create_article_comment(:job, job.id, "job_comment 1", user) + {:ok, job_comment_2} = CMS.create_article_comment(:job, job.id, "job_comment 2", user) {:ok, post} = ORM.find(Post, post.id, preload: :article_comments) {:ok, job} = ORM.find(Job, job.id, preload: :article_comments) - assert List.first(post.article_comments).body_html == post_comment_1 - assert List.first(job.article_comments).body_html == job_comment_1 + assert exist_in?(post_comment_1, post.article_comments) + assert exist_in?(post_comment_2, post.article_comments) + + assert exist_in?(job_comment_1, job.article_comments) + assert exist_in?(job_comment_2, job.article_comments) end @tag :wip