From 366126274742aa187ad5753b2bfa092a3fb09198 Mon Sep 17 00:00:00 2001 From: mydearxym Date: Fri, 25 Jun 2021 17:51:21 +0800 Subject: [PATCH 01/12] chore: remove warning --- lib/groupher_server/cms/delegates/hooks/cite.ex | 2 +- lib/groupher_server/cms/delegates/hooks/mention.ex | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/groupher_server/cms/delegates/hooks/cite.ex b/lib/groupher_server/cms/delegates/hooks/cite.ex index e7623641f..0b6b13d1c 100644 --- a/lib/groupher_server/cms/delegates/hooks/cite.ex +++ b/lib/groupher_server/cms/delegates/hooks/cite.ex @@ -66,7 +66,7 @@ defmodule GroupherServer.CMS.Delegate.Hooks.Cite do end end - def handle(%{document: document} = article) do + def handle(%{document: _document} = article) do body = Repo.preload(article, :document) |> get_in([:document, :body]) article = article |> Map.put(:body, body) handle(article) diff --git a/lib/groupher_server/cms/delegates/hooks/mention.ex b/lib/groupher_server/cms/delegates/hooks/mention.ex index 923afc60c..576911d88 100644 --- a/lib/groupher_server/cms/delegates/hooks/mention.ex +++ b/lib/groupher_server/cms/delegates/hooks/mention.ex @@ -27,7 +27,7 @@ defmodule GroupherServer.CMS.Delegate.Hooks.Mention do end end - def handle(%{document: document} = article) do + def handle(%{document: _document} = article) do body = Repo.preload(article, :document) |> get_in([:document, :body]) article = article |> Map.put(:body, body) handle(article) From 2e3b9ce0e4c5707f62276dc0bbd65eca410f4865 Mon Sep 17 00:00:00 2001 From: mydearxym Date: Fri, 25 Jun 2021 18:18:04 +0800 Subject: [PATCH 02/12] chore(works): create migrate tables --- .../20210625095226_create_works_table.exs | 32 ++++++++++++++++++ .../20210625100126_create_works_document.exs | 13 ++++++++ ...25100519_create_communities_join_works.exs | 12 +++++++ ...625101018_add_works_to_tags_join_table.exs | 9 +++++ ...5101144_add_works_to_other_join_tables.exs | 33 +++++++++++++++++++ 5 files changed, 99 insertions(+) create mode 100644 priv/repo/migrations/20210625095226_create_works_table.exs create mode 100644 priv/repo/migrations/20210625100126_create_works_document.exs create mode 100644 priv/repo/migrations/20210625100519_create_communities_join_works.exs create mode 100644 priv/repo/migrations/20210625101018_add_works_to_tags_join_table.exs create mode 100644 priv/repo/migrations/20210625101144_add_works_to_other_join_tables.exs diff --git a/priv/repo/migrations/20210625095226_create_works_table.exs b/priv/repo/migrations/20210625095226_create_works_table.exs new file mode 100644 index 000000000..053b752c3 --- /dev/null +++ b/priv/repo/migrations/20210625095226_create_works_table.exs @@ -0,0 +1,32 @@ +defmodule GroupherServer.Repo.Migrations.CreateWorksTable do + use Ecto.Migration + + def change do + create table(:cms_works) do + add(:thread, :string) + add(:title, :string) + add(:digest, :string) + add(:views, :integer, default: 0) + add(:mark_delete, :boolean, default: false) + add(:meta, :map) + add(:emotions, :map) + add(:original_community_id, references(:communities, on_delete: :delete_all)) + add(:author_id, references(:cms_authors, on_delete: :delete_all), null: false) + + add(:active_at, :utc_datetime) + + # reaction + add(:upvotes_count, :integer, default: 0) + add(:collects_count, :integer, default: 0) + + # comments + add(:article_comments_participators_count, :integer, default: 0) + add(:article_comments_count, :integer, default: 0) + add(:article_comments_participators, :map) + + timestamps() + end + + create(index(:cms_works, [:author_id])) + end +end diff --git a/priv/repo/migrations/20210625100126_create_works_document.exs b/priv/repo/migrations/20210625100126_create_works_document.exs new file mode 100644 index 000000000..544ae8b55 --- /dev/null +++ b/priv/repo/migrations/20210625100126_create_works_document.exs @@ -0,0 +1,13 @@ +defmodule GroupherServer.Repo.Migrations.CreateWorksDocument do + use Ecto.Migration + + def change do + create table(:works_documents) do + add(:works_id, references(:cms_works, on_delete: :delete_all), null: false) + add(:body, :text) + add(:body_html, :text) + add(:markdown, :text) + add(:toc, :map) + end + end +end diff --git a/priv/repo/migrations/20210625100519_create_communities_join_works.exs b/priv/repo/migrations/20210625100519_create_communities_join_works.exs new file mode 100644 index 000000000..5f0cc8ddb --- /dev/null +++ b/priv/repo/migrations/20210625100519_create_communities_join_works.exs @@ -0,0 +1,12 @@ +defmodule GroupherServer.Repo.Migrations.CreateCommunitiesJoinWorks do + use Ecto.Migration + + def change do + create table(:communities_join_works) do + add(:community_id, references(:communities, on_delete: :delete_all), null: false) + add(:works_id, references(:cms_works, on_delete: :delete_all), null: false) + end + + create(unique_index(:communities_join_works, [:community_id, :works_id])) + end +end diff --git a/priv/repo/migrations/20210625101018_add_works_to_tags_join_table.exs b/priv/repo/migrations/20210625101018_add_works_to_tags_join_table.exs new file mode 100644 index 000000000..557cf9ed8 --- /dev/null +++ b/priv/repo/migrations/20210625101018_add_works_to_tags_join_table.exs @@ -0,0 +1,9 @@ +defmodule GroupherServer.Repo.Migrations.AddWorksToTagsJoinTable do + use Ecto.Migration + + def change do + alter table(:articles_join_tags) do + add(:works_id, references(:cms_works, on_delete: :delete_all)) + end + end +end diff --git a/priv/repo/migrations/20210625101144_add_works_to_other_join_tables.exs b/priv/repo/migrations/20210625101144_add_works_to_other_join_tables.exs new file mode 100644 index 000000000..391d670d9 --- /dev/null +++ b/priv/repo/migrations/20210625101144_add_works_to_other_join_tables.exs @@ -0,0 +1,33 @@ +defmodule GroupherServer.Repo.Migrations.AddWorksToOtherJoinTables do + use Ecto.Migration + + def change do + alter table(:abuse_reports) do + add(:works_id, references(:cms_works, on_delete: :delete_all)) + end + + alter table(:article_collects) do + add(:works_id, references(:cms_works, on_delete: :delete_all)) + end + + alter table(:article_upvotes) do + add(:works_id, references(:cms_works, on_delete: :delete_all)) + end + + alter table(:comments) do + add(:works_id, references(:cms_works, on_delete: :delete_all)) + end + + alter table(:pinned_comments) do + add(:works_id, references(:cms_works, on_delete: :delete_all)) + end + + alter table(:articles_users_emotions) do + add(:works_id, references(:cms_works, on_delete: :delete_all)) + end + + alter table(:pinned_articles) do + add(:works_id, references(:cms_works, on_delete: :delete_all)) + end + end +end From d4f67f00cd9a9f57d74cb0c7811684990e5a6fa6 Mon Sep 17 00:00:00 2001 From: mydearxym Date: Fri, 25 Jun 2021 21:08:23 +0800 Subject: [PATCH 03/12] chore(works): basic setup & test --- config/config.exs | 5 +- lib/groupher_server/cms/models/works.ex | 52 +++++ .../cms/models/works_document.ex | 47 +++++ .../cms/articles/works_test.exs | 197 ++++++++++++++++++ test/support/factory.ex | 20 ++ 5 files changed, 319 insertions(+), 2 deletions(-) create mode 100644 lib/groupher_server/cms/models/works.ex create mode 100644 lib/groupher_server/cms/models/works_document.ex create mode 100644 test/groupher_server/cms/articles/works_test.exs diff --git a/config/config.exs b/config/config.exs index ef90323d5..36cd54005 100644 --- a/config/config.exs +++ b/config/config.exs @@ -69,14 +69,15 @@ config :groupher_server, :article, min_length: 10, max_length: 20_000, # NOTE: do not change unless you know what you are doing - threads: [:post, :job, :repo, :blog], + threads: [:post, :job, :repo, :blog, :works], # in this period, paged articles will sort front if non-article-author commented # 在此时间段内,一旦有非文章作者的用户评论,该文章就会排到前面 active_period_days: %{ post: 10, job: 10, repo: 10, - blog: 10 + blog: 10, + works: 10 }, # NOTE: if you want to add/remove emotion, just edit the list below diff --git a/lib/groupher_server/cms/models/works.ex b/lib/groupher_server/cms/models/works.ex new file mode 100644 index 000000000..c75d0216f --- /dev/null +++ b/lib/groupher_server/cms/models/works.ex @@ -0,0 +1,52 @@ +defmodule GroupherServer.CMS.Model.Works do + @moduledoc false + alias __MODULE__ + + use Ecto.Schema + use Accessible + + import Ecto.Changeset + import GroupherServer.CMS.Helper.Macros + + alias GroupherServer.CMS + alias CMS.Model.Embeds + + alias Helper.HTML + + @timestamps_opts [type: :utc_datetime_usec] + + @required_fields ~w(title digest)a + @article_cast_fields general_article_cast_fields() + @optional_fields @article_cast_fields + + @type t :: %Works{} + schema "cms_works" do + article_tags_field(:works) + article_communities_field(:works) + general_article_fields(:works) + end + + @doc false + def changeset(%Works{} = works, attrs) do + works + |> cast(attrs, @optional_fields ++ @required_fields) + |> validate_required(@required_fields) + |> cast_embed(:meta, required: false, with: &Embeds.ArticleMeta.changeset/2) + |> generl_changeset + end + + @doc false + def update_changeset(%Works{} = works, attrs) do + works + |> cast(attrs, @optional_fields ++ @required_fields) + |> generl_changeset + end + + defp generl_changeset(changeset) do + changeset + |> validate_length(:title, min: 3, max: 50) + |> cast_embed(:emotions, with: &Embeds.ArticleEmotion.changeset/2) + |> validate_length(:link_addr, min: 5, max: 400) + |> HTML.safe_string(:body) + end +end diff --git a/lib/groupher_server/cms/models/works_document.ex b/lib/groupher_server/cms/models/works_document.ex new file mode 100644 index 000000000..260d0ebe8 --- /dev/null +++ b/lib/groupher_server/cms/models/works_document.ex @@ -0,0 +1,47 @@ +defmodule GroupherServer.CMS.Model.WorksDocument do + @moduledoc """ + mainly for full-text search + """ + alias __MODULE__ + + use Ecto.Schema + use Accessible + + import Ecto.Changeset + import Helper.Utils, only: [get_config: 2] + + alias GroupherServer.CMS + alias CMS.Model.Works + + @timestamps_opts [type: :utc_datetime_usec] + + @max_body_length get_config(:article, :max_length) + @min_body_length get_config(:article, :min_length) + + @required_fields ~w(body body_html works_id)a + @optional_fields [] + + @type t :: %WorksDocument{} + schema "works_documents" do + belongs_to(:works, Works, foreign_key: :works_id) + + field(:body, :string) + field(:body_html, :string) + field(:toc, :map) + end + + @doc false + def changeset(%WorksDocument{} = works, attrs) do + works + |> cast(attrs, @optional_fields ++ @required_fields) + |> validate_required(@required_fields) + |> validate_length(:body, min: @min_body_length, max: @max_body_length) + end + + @doc false + def update_changeset(%WorksDocument{} = works, attrs) do + works + |> cast(attrs, @optional_fields ++ @required_fields) + |> validate_length(:body, min: @min_body_length, max: @max_body_length) + end +end diff --git a/test/groupher_server/cms/articles/works_test.exs b/test/groupher_server/cms/articles/works_test.exs new file mode 100644 index 000000000..873b8e0fd --- /dev/null +++ b/test/groupher_server/cms/articles/works_test.exs @@ -0,0 +1,197 @@ +defmodule GroupherServer.Test.Articles.Works do + use GroupherServer.TestTools + + alias GroupherServer.{CMS, Repo} + alias Helper.Converter.{EditorToHTML, HtmlSanitizer} + + alias EditorToHTML.{Class, Validator} + alias CMS.Model.{Author, Works, Community, ArticleDocument, WorksDocument} + alias Helper.ORM + + @root_class Class.article() + @last_year Timex.shift(Timex.beginning_of_year(Timex.now()), days: -3, seconds: -1) + + setup do + {:ok, user} = db_insert(:user) + {:ok, user2} = db_insert(:user) + {:ok, community} = db_insert(:community) + + works_attrs = mock_attrs(:works, %{community_id: community.id}) + + {:ok, ~m(user user2 community works_attrs)a} + end + + describe "[cms workss curd]" do + test "can create works with valid attrs", ~m(user community works_attrs)a do + assert {:error, _} = ORM.find_by(Author, user_id: user.id) + {:ok, works} = CMS.create_article(community, :works, works_attrs, user) + works = Repo.preload(works, :document) + + body_map = Jason.decode!(works.document.body) + + assert works.meta.thread == "WORKS" + + assert works.title == works_attrs.title + assert body_map |> Validator.is_valid() + + assert works.document.body_html + |> String.contains?(~s(
)) + + assert works.document.body_html |> String.contains?(~s(

List.first() |> get_in(["data", "text"]) + assert works.digest == paragraph_text |> HtmlSanitizer.strip_all_tags() + end + + test "created works should have a acitve_at field, same with inserted_at", + ~m(user community works_attrs)a do + {:ok, works} = CMS.create_article(community, :works, works_attrs, user) + + assert works.active_at == works.inserted_at + end + + test "read works should update views and meta viewed_user_list", + ~m(works_attrs community user user2)a do + {:ok, works} = CMS.create_article(community, :works, works_attrs, user) + {:ok, _} = CMS.read_article(:works, works.id, user) + {:ok, _created} = ORM.find(Works, works.id) + + # same user duplicate case + {:ok, _} = CMS.read_article(:works, works.id, user) + {:ok, created} = ORM.find(Works, works.id) + + assert created.meta.viewed_user_ids |> length == 1 + assert user.id in created.meta.viewed_user_ids + + {:ok, _} = CMS.read_article(:works, works.id, user2) + {:ok, created} = ORM.find(Works, works.id) + + assert created.meta.viewed_user_ids |> length == 2 + assert user.id in created.meta.viewed_user_ids + assert user2.id in created.meta.viewed_user_ids + end + + test "read works should contains viewer_has_xxx state", + ~m(works_attrs community user user2)a do + {:ok, works} = CMS.create_article(community, :works, works_attrs, user) + {:ok, works} = CMS.read_article(:works, works.id, user) + + assert not works.viewer_has_collected + assert not works.viewer_has_upvoted + assert not works.viewer_has_reported + + {:ok, works} = CMS.read_article(:works, works.id) + + assert not works.viewer_has_collected + assert not works.viewer_has_upvoted + assert not works.viewer_has_reported + + {:ok, works} = CMS.read_article(:works, works.id, user2) + + assert not works.viewer_has_collected + assert not works.viewer_has_upvoted + assert not works.viewer_has_reported + + {:ok, _} = CMS.upvote_article(:works, works.id, user) + {:ok, _} = CMS.collect_article(:works, works.id, user) + {:ok, _} = CMS.report_article(:works, works.id, "reason", "attr_info", user) + + {:ok, works} = CMS.read_article(:works, works.id, user) + + assert works.viewer_has_collected + assert works.viewer_has_upvoted + assert works.viewer_has_reported + end + + test "create works with an exsit community fails", ~m(user)a do + invalid_attrs = mock_attrs(:works, %{community_id: non_exsit_id()}) + ivalid_community = %Community{id: non_exsit_id()} + + assert {:error, _} = CMS.create_article(ivalid_community, :works, invalid_attrs, user) + end + end + + describe "[cms works sink/undo_sink]" do + test "if a works is too old, read works should update can_undo_sink flag", + ~m(user community works_attrs)a do + {:ok, works} = CMS.create_article(community, :works, works_attrs, user) + + assert works.meta.can_undo_sink + + {:ok, works_last_year} = db_insert(:works, %{title: "last year", inserted_at: @last_year}) + {:ok, works_last_year} = CMS.read_article(:works, works_last_year.id) + assert not works_last_year.meta.can_undo_sink + + {:ok, works_last_year} = CMS.read_article(:works, works_last_year.id, user) + assert not works_last_year.meta.can_undo_sink + end + + test "can sink a works", ~m(user community works_attrs)a do + {:ok, works} = CMS.create_article(community, :works, works_attrs, user) + assert not works.meta.is_sinked + + {:ok, works} = CMS.sink_article(:works, works.id) + assert works.meta.is_sinked + assert works.active_at == works.inserted_at + end + + test "can undo sink works", ~m(user community works_attrs)a do + {:ok, works} = CMS.create_article(community, :works, works_attrs, user) + {:ok, works} = CMS.sink_article(:works, works.id) + assert works.meta.is_sinked + assert works.meta.last_active_at == works.active_at + + {:ok, works} = CMS.undo_sink_article(:works, works.id) + assert not works.meta.is_sinked + assert works.active_at == works.meta.last_active_at + end + + test "can not undo sink to old works", ~m()a do + {:ok, works_last_year} = db_insert(:works, %{title: "last year", inserted_at: @last_year}) + + {:error, reason} = CMS.undo_sink_article(:works, works_last_year.id) + is_error?(reason, :undo_sink_old_article) + end + end + + describe "[cms works document]" do + test "will create related document after create", ~m(user community works_attrs)a do + {:ok, works} = CMS.create_article(community, :works, works_attrs, user) + {:ok, works} = CMS.read_article(:works, works.id) + assert not is_nil(works.document.body_html) + {:ok, works} = CMS.read_article(:works, works.id, user) + assert not is_nil(works.document.body_html) + + {:ok, article_doc} = ORM.find_by(ArticleDocument, %{article_id: works.id, thread: "WORKS"}) + {:ok, works_doc} = ORM.find_by(WorksDocument, %{works_id: works.id}) + + assert works.document.body == works_doc.body + assert article_doc.body == works_doc.body + end + + test "delete works should also delete related document", ~m(user community works_attrs)a do + {:ok, works} = CMS.create_article(community, :works, works_attrs, user) + {:ok, _article_doc} = ORM.find_by(ArticleDocument, %{article_id: works.id, thread: "WORKS"}) + {:ok, _works_doc} = ORM.find_by(WorksDocument, %{works_id: works.id}) + + {:ok, _} = CMS.delete_article(works) + + {:error, _} = ORM.find(Works, works.id) + {:error, _} = ORM.find_by(ArticleDocument, %{article_id: works.id, thread: "WORKS"}) + {:error, _} = ORM.find_by(WorksDocument, %{works_id: works.id}) + end + + test "update works should also update related document", ~m(user community works_attrs)a do + {:ok, works} = CMS.create_article(community, :works, works_attrs, user) + + body = mock_rich_text(~s(new content)) + {:ok, works} = CMS.update_article(works, %{body: body}) + + {:ok, article_doc} = ORM.find_by(ArticleDocument, %{article_id: works.id, thread: "WORKS"}) + {:ok, works_doc} = ORM.find_by(WorksDocument, %{works_id: works.id}) + + assert String.contains?(works_doc.body, "new content") + assert String.contains?(article_doc.body, "new content") + end + end +end diff --git a/test/support/factory.ex b/test/support/factory.ex index 05fe0b582..350202507 100644 --- a/test/support/factory.ex +++ b/test/support/factory.ex @@ -219,6 +219,26 @@ defmodule GroupherServer.Support.Factory do } end + defp mock_meta(:works) do + text = Faker.Lorem.sentence(%Range{first: 80, last: 120}) + + %{ + meta: @default_article_meta |> Map.merge(%{thread: "BLOG"}), + title: String.slice(text, 1, 49), + body: mock_rich_text(), + digest: String.slice(text, 1, 150), + # length: String.length(text), + author: mock(:author), + views: Enum.random(0..2000), + original_community: mock(:community), + communities: [ + mock(:community) + ], + emotions: @default_emotions, + active_at: Timex.shift(Timex.now(), seconds: +1) + } + end + defp mock_meta(:comment) do # text = Faker.Lorem.sentence(%Range{first: 30, last: 80}) From 59e6f8b415ed86facd5a0ec2038389bce0da3bf2 Mon Sep 17 00:00:00 2001 From: mydearxym Date: Fri, 25 Jun 2021 21:24:23 +0800 Subject: [PATCH 04/12] chore(works): missing type --- lib/groupher_server_web/schema/cms/cms_metrics.ex | 9 +++++++++ lib/groupher_server_web/schema/cms/cms_types.ex | 12 ++++++++++++ 2 files changed, 21 insertions(+) diff --git a/lib/groupher_server_web/schema/cms/cms_metrics.ex b/lib/groupher_server_web/schema/cms/cms_metrics.ex index 1d072749e..fd6efb7ab 100644 --- a/lib/groupher_server_web/schema/cms/cms_metrics.ex +++ b/lib/groupher_server_web/schema/cms/cms_metrics.ex @@ -191,12 +191,21 @@ defmodule GroupherServerWeb.Schema.CMS.Metrics do field(:sort, :sort_enum) end + @desc "blog_filter doc" input_object :paged_blogs_filter do pagination_args() article_filter_fields() field(:sort, :sort_enum) end + @desc "works_filter doc" + # TODO: + input_object :paged_workss_filter do + pagination_args() + article_filter_fields() + field(:sort, :sort_enum) + end + @desc "article_filter doc" input_object :paged_repos_filter do @desc "limit of records (default 20), if first > 30, only return 30 at most" diff --git a/lib/groupher_server_web/schema/cms/cms_types.ex b/lib/groupher_server_web/schema/cms/cms_types.ex index a997fc373..902c701db 100644 --- a/lib/groupher_server_web/schema/cms/cms_types.ex +++ b/lib/groupher_server_web/schema/cms/cms_types.ex @@ -93,6 +93,18 @@ defmodule GroupherServerWeb.Schema.CMS.Types do timestamp_fields(:article) end + object :works do + interface(:article) + + general_article_fields() + comments_fields() + + field(:length, :integer) + field(:link_addr, :string) + + timestamp_fields(:article) + end + object :repo do interface(:article) From d6157adfd8dbe11c5c786edbabf831ea3badd764 Mon Sep 17 00:00:00 2001 From: mydearxym Date: Fri, 25 Jun 2021 21:35:07 +0800 Subject: [PATCH 05/12] chore(works): missing schema --- .../schema/Helper/queries.ex | 3 ++ .../schema/cms/mutations/blog.ex | 2 - .../schema/cms/mutations/works.ex | 53 +++++++++++++++++++ lib/helper/utils/utils.ex | 6 +++ 4 files changed, 62 insertions(+), 2 deletions(-) create mode 100644 lib/groupher_server_web/schema/cms/mutations/works.ex diff --git a/lib/groupher_server_web/schema/Helper/queries.ex b/lib/groupher_server_web/schema/Helper/queries.ex index 07062ba1b..c5025542d 100644 --- a/lib/groupher_server_web/schema/Helper/queries.ex +++ b/lib/groupher_server_web/schema/Helper/queries.ex @@ -81,4 +81,7 @@ defmodule GroupherServerWeb.Schema.Helper.Queries do end end end + + def thread_plural(:works), do: :works + def thread_plural(thread), do: :"#{thread}s" end diff --git a/lib/groupher_server_web/schema/cms/mutations/blog.ex b/lib/groupher_server_web/schema/cms/mutations/blog.ex index 01bf3cee4..9870026ef 100644 --- a/lib/groupher_server_web/schema/cms/mutations/blog.ex +++ b/lib/groupher_server_web/schema/cms/mutations/blog.ex @@ -32,8 +32,6 @@ defmodule GroupherServerWeb.Schema.CMS.Mutations.Blog do arg(:length, :integer) arg(:link_addr, :string) - arg(:company, :string) - arg(:company_link, :string) arg(:article_tags, list_of(:id)) # ... diff --git a/lib/groupher_server_web/schema/cms/mutations/works.ex b/lib/groupher_server_web/schema/cms/mutations/works.ex new file mode 100644 index 000000000..791634667 --- /dev/null +++ b/lib/groupher_server_web/schema/cms/mutations/works.ex @@ -0,0 +1,53 @@ +defmodule GroupherServerWeb.Schema.CMS.Mutations.Works do + @moduledoc """ + CMS mutations for works + """ + use Helper.GqlSchemaSuite + import GroupherServerWeb.Schema.Helper.Mutations + + object :cms_works_mutations do + @desc "create a works" + field :create_works, :works do + arg(:title, non_null(:string)) + arg(:body, non_null(:string)) + arg(:digest, non_null(:string)) + arg(:community_id, non_null(:id)) + arg(:link_addr, :string) + arg(:thread, :thread, default_value: :works) + arg(:article_tags, list_of(:id)) + + middleware(M.Authorize, :login) + middleware(M.PublishThrottle) + resolve(&R.CMS.create_article/3) + middleware(M.Statistics.MakeContribute, for: [:user, :community]) + end + + @desc "update a cms/works" + field :update_works, :works do + arg(:id, non_null(:id)) + arg(:title, :string) + arg(:body, :string) + arg(:digest, :string) + + arg(:article_tags, list_of(:id)) + # ... + + middleware(M.Authorize, :login) + middleware(M.PassportLoader, source: :works) + middleware(M.Passport, claim: "owner;cms->c?->works.edit") + + resolve(&R.CMS.update_article/3) + end + + ############# + article_upvote_mutation(:works) + article_pin_mutation(:works) + article_mark_delete_mutation(:works) + article_delete_mutation(:works) + article_emotion_mutation(:works) + article_report_mutation(:works) + article_sink_mutation(:works) + article_lock_comment_mutation(:works) + ############# + end +end diff --git a/lib/helper/utils/utils.ex b/lib/helper/utils/utils.ex index 8d6a27718..6d1eedb44 100644 --- a/lib/helper/utils/utils.ex +++ b/lib/helper/utils/utils.ex @@ -49,6 +49,12 @@ defmodule Helper.Utils do end end + @doc """ + get plural version of a thread + """ + def thread_plural(:works), do: :works + def thread_plural(thread), do: :"#{thread}s" + @doc """ like || in javascript """ From 823b7f3a9e3e37c19c451b1c6ce62af1c09bb5a4 Mon Sep 17 00:00:00 2001 From: mydearxym Date: Fri, 25 Jun 2021 21:51:25 +0800 Subject: [PATCH 06/12] chore(works): plural issue --- .../schema/Helper/objects.ex | 4 ++-- .../schema/Helper/queries.ex | 18 ++++++++---------- .../schema/cms/cms_metrics.ex | 2 +- lib/helper/utils/utils.ex | 6 +++--- ...0625134335_add_works_to_cited_artiments.exs | 9 +++++++++ 5 files changed, 23 insertions(+), 16 deletions(-) create mode 100644 priv/repo/migrations/20210625134335_add_works_to_cited_artiments.exs diff --git a/lib/groupher_server_web/schema/Helper/objects.ex b/lib/groupher_server_web/schema/Helper/objects.ex index d2b6e4e50..d4f16152a 100644 --- a/lib/groupher_server_web/schema/Helper/objects.ex +++ b/lib/groupher_server_web/schema/Helper/objects.ex @@ -2,7 +2,7 @@ defmodule GroupherServerWeb.Schema.Helper.Objects do @moduledoc """ general fields used in schema definition """ - import Helper.Utils, only: [get_config: 2] + import Helper.Utils, only: [get_config: 2, plural: 1] @article_threads get_config(:article, :threads) @@ -19,7 +19,7 @@ defmodule GroupherServerWeb.Schema.Helper.Objects do @article_threads |> Enum.map( "e do - object unquote(:"paged_#{&1}s") do + object unquote(:"paged_#{plural(&1)}") do field(:entries, list_of(unquote(&1))) pagination_fields() end diff --git a/lib/groupher_server_web/schema/Helper/queries.ex b/lib/groupher_server_web/schema/Helper/queries.ex index c5025542d..28e85b700 100644 --- a/lib/groupher_server_web/schema/Helper/queries.ex +++ b/lib/groupher_server_web/schema/Helper/queries.ex @@ -2,7 +2,7 @@ defmodule GroupherServerWeb.Schema.Helper.Queries do @moduledoc """ common fields """ - import Helper.Utils, only: [get_config: 2] + import Helper.Utils, only: [get_config: 2, plural: 1] alias GroupherServerWeb.Middleware, as: M alias GroupherServerWeb.Resolvers, as: R @@ -14,8 +14,9 @@ defmodule GroupherServerWeb.Schema.Helper.Queries do @article_threads |> Enum.map(fn thread -> quote do - @desc unquote("paged published #{thread}s") - field unquote(:"paged_published_#{thread}s"), unquote(:"paged_#{thread}s") do + @desc unquote("paged published #{plural(thread)}") + field unquote(:"paged_published_#{plural(thread)}"), + unquote(:"paged_#{plural(thread)}") do arg(:login, non_null(:string)) arg(:filter, non_null(:paged_filter)) arg(:thread, unquote(:"#{thread}_thread"), default_value: unquote(thread)) @@ -32,7 +33,7 @@ defmodule GroupherServerWeb.Schema.Helper.Queries do |> Enum.map(fn thread -> quote do @desc unquote("get #{thread} by id") - field unquote(:"search_#{thread}s"), unquote(:"paged_#{thread}s") do + field unquote(:"search_#{plural(thread)}"), unquote(:"paged_#{plural(thread)}") do arg(:title, non_null(:string)) arg(:thread, unquote(:"#{thread}_thread"), default_value: unquote(thread)) @@ -57,10 +58,10 @@ defmodule GroupherServerWeb.Schema.Helper.Queries do resolve(&R.CMS.read_article/3) end - @desc unquote("get paged #{thread}s") - field unquote(:"paged_#{thread}s"), unquote(:"paged_#{thread}s") do + @desc unquote("get paged #{plural(thread)}") + field unquote(:"paged_#{plural(thread)}"), unquote(:"paged_#{plural(thread)}") do arg(:thread, unquote(:"#{thread}_thread"), default_value: unquote(thread)) - arg(:filter, non_null(unquote(:"paged_#{thread}s_filter"))) + arg(:filter, non_null(unquote(:"paged_#{plural(thread)}_filter"))) middleware(M.PageSizeProof, default_sort: :desc_active) resolve(&R.CMS.paged_articles/3) @@ -81,7 +82,4 @@ defmodule GroupherServerWeb.Schema.Helper.Queries do end end end - - def thread_plural(:works), do: :works - def thread_plural(thread), do: :"#{thread}s" end diff --git a/lib/groupher_server_web/schema/cms/cms_metrics.ex b/lib/groupher_server_web/schema/cms/cms_metrics.ex index fd6efb7ab..4e4b4c587 100644 --- a/lib/groupher_server_web/schema/cms/cms_metrics.ex +++ b/lib/groupher_server_web/schema/cms/cms_metrics.ex @@ -200,7 +200,7 @@ defmodule GroupherServerWeb.Schema.CMS.Metrics do @desc "works_filter doc" # TODO: - input_object :paged_workss_filter do + input_object :paged_works_filter do pagination_args() article_filter_fields() field(:sort, :sort_enum) diff --git a/lib/helper/utils/utils.ex b/lib/helper/utils/utils.ex index 6d1eedb44..ecbae2158 100644 --- a/lib/helper/utils/utils.ex +++ b/lib/helper/utils/utils.ex @@ -50,10 +50,10 @@ defmodule Helper.Utils do end @doc """ - get plural version of a thread + plural version of the thread """ - def thread_plural(:works), do: :works - def thread_plural(thread), do: :"#{thread}s" + def plural(:works), do: :works + def plural(thread), do: :"#{thread}s" @doc """ like || in javascript diff --git a/priv/repo/migrations/20210625134335_add_works_to_cited_artiments.exs b/priv/repo/migrations/20210625134335_add_works_to_cited_artiments.exs new file mode 100644 index 000000000..1d6de11f2 --- /dev/null +++ b/priv/repo/migrations/20210625134335_add_works_to_cited_artiments.exs @@ -0,0 +1,9 @@ +defmodule GroupherServer.Repo.Migrations.AddWorksToCitedArtiments do + use Ecto.Migration + + def change do + alter table(:cited_artiments) do + add(:works_id, references(:cms_works, on_delete: :delete_all)) + end + end +end From 3e5047c03848916e8f3309cbe7bc3e0b128b7b8b Mon Sep 17 00:00:00 2001 From: mydearxym Date: Fri, 25 Jun 2021 22:11:24 +0800 Subject: [PATCH 07/12] chore(works): plural issue --- lib/groupher_server/cms/helper/macros.ex | 4 ++-- lib/groupher_server/cms/models/works.ex | 4 ---- lib/groupher_server_web/schema/cms/mutations/works.ex | 1 - priv/repo/migrations/20210625095226_create_works_table.exs | 6 +++--- test/groupher_server/cms/articles/works_test.exs | 1 + 5 files changed, 6 insertions(+), 10 deletions(-) diff --git a/lib/groupher_server/cms/helper/macros.ex b/lib/groupher_server/cms/helper/macros.ex index 03788d089..4617fa7ce 100644 --- a/lib/groupher_server/cms/helper/macros.ex +++ b/lib/groupher_server/cms/helper/macros.ex @@ -2,7 +2,7 @@ defmodule GroupherServer.CMS.Helper.Macros do @moduledoc """ macros for define article related fields in CMS models """ - import Helper.Utils, only: [get_config: 2] + import Helper.Utils, only: [get_config: 2, plural: 1] alias GroupherServer.{CMS, Accounts} @@ -220,7 +220,7 @@ defmodule GroupherServer.CMS.Helper.Macros do many_to_many( :communities, Community, - join_through: unquote("communities_join_#{to_string(thread)}s"), + join_through: unquote("communities_join_#{plural(thread)}"), on_replace: :delete ) end diff --git a/lib/groupher_server/cms/models/works.ex b/lib/groupher_server/cms/models/works.ex index c75d0216f..485cf17e8 100644 --- a/lib/groupher_server/cms/models/works.ex +++ b/lib/groupher_server/cms/models/works.ex @@ -11,8 +11,6 @@ defmodule GroupherServer.CMS.Model.Works do alias GroupherServer.CMS alias CMS.Model.Embeds - alias Helper.HTML - @timestamps_opts [type: :utc_datetime_usec] @required_fields ~w(title digest)a @@ -46,7 +44,5 @@ defmodule GroupherServer.CMS.Model.Works do changeset |> validate_length(:title, min: 3, max: 50) |> cast_embed(:emotions, with: &Embeds.ArticleEmotion.changeset/2) - |> validate_length(:link_addr, min: 5, max: 400) - |> HTML.safe_string(:body) end end diff --git a/lib/groupher_server_web/schema/cms/mutations/works.ex b/lib/groupher_server_web/schema/cms/mutations/works.ex index 791634667..8048248dc 100644 --- a/lib/groupher_server_web/schema/cms/mutations/works.ex +++ b/lib/groupher_server_web/schema/cms/mutations/works.ex @@ -12,7 +12,6 @@ defmodule GroupherServerWeb.Schema.CMS.Mutations.Works do arg(:body, non_null(:string)) arg(:digest, non_null(:string)) arg(:community_id, non_null(:id)) - arg(:link_addr, :string) arg(:thread, :thread, default_value: :works) arg(:article_tags, list_of(:id)) diff --git a/priv/repo/migrations/20210625095226_create_works_table.exs b/priv/repo/migrations/20210625095226_create_works_table.exs index 053b752c3..c9690fb35 100644 --- a/priv/repo/migrations/20210625095226_create_works_table.exs +++ b/priv/repo/migrations/20210625095226_create_works_table.exs @@ -20,9 +20,9 @@ defmodule GroupherServer.Repo.Migrations.CreateWorksTable do add(:collects_count, :integer, default: 0) # comments - add(:article_comments_participators_count, :integer, default: 0) - add(:article_comments_count, :integer, default: 0) - add(:article_comments_participators, :map) + add(:comments_participants_count, :integer, default: 0) + add(:comments_count, :integer, default: 0) + add(:comments_participants, :map) timestamps() end diff --git a/test/groupher_server/cms/articles/works_test.exs b/test/groupher_server/cms/articles/works_test.exs index 873b8e0fd..251c7f335 100644 --- a/test/groupher_server/cms/articles/works_test.exs +++ b/test/groupher_server/cms/articles/works_test.exs @@ -169,6 +169,7 @@ defmodule GroupherServer.Test.Articles.Works do assert article_doc.body == works_doc.body end + @tag :wip test "delete works should also delete related document", ~m(user community works_attrs)a do {:ok, works} = CMS.create_article(community, :works, works_attrs, user) {:ok, _article_doc} = ORM.find_by(ArticleDocument, %{article_id: works.id, thread: "WORKS"}) From 0afb2c9b7df231c1b75c6fd59f49dfb6b376f86a Mon Sep 17 00:00:00 2001 From: mydearxym Date: Fri, 25 Jun 2021 22:36:14 +0800 Subject: [PATCH 08/12] chore(works): tests --- .../cms/abuse_reports/works_report_test.exs | 137 ++++ .../cms/article_community/works_test.exs | 85 +++ .../cms/article_tags/works_tag_test.exs | 134 ++++ .../cms/articles/works_pin_test.exs | 54 ++ .../comments/works_comment_emotions_test.exs | 192 +++++ .../comments/works_comment_replies_test.exs | 204 ++++++ .../cms/comments/works_comment_test.exs | 662 ++++++++++++++++++ .../cms/emotions/works_emotions_test.exs | 178 +++++ .../cms/hooks/cite_works_test.exs | 230 ++++++ .../cms/hooks/mention_in_works_test.exs | 98 +++ .../cms/hooks/notify_works_test.exs | 174 +++++ .../cms/upvotes/works_upvote_test.exs | 83 +++ .../cms/abuse_reports/works_report_test.exs | 62 ++ .../cms/article_community/works_test.exs | 148 ++++ .../cms/article_tags/works_tag_test.exs | 91 +++ .../mutation/cms/articles/works_test.exs | 227 ++++++ .../cms/comments/works_comment_test.exs | 329 +++++++++ .../mutation/cms/flags/works_flag_test.exs | 178 +++++ .../mutation/cms/sink/works_sink_test.exs | 79 +++ .../cms/upvotes/works_upvote_test.exs | 64 ++ .../published/published_works_test.exs | 102 +++ .../cms/paged_articles/paged_works_test.exs | 474 +++++++++++++ 22 files changed, 3985 insertions(+) create mode 100644 test/groupher_server/cms/abuse_reports/works_report_test.exs create mode 100644 test/groupher_server/cms/article_community/works_test.exs create mode 100644 test/groupher_server/cms/article_tags/works_tag_test.exs create mode 100644 test/groupher_server/cms/articles/works_pin_test.exs create mode 100644 test/groupher_server/cms/comments/works_comment_emotions_test.exs create mode 100644 test/groupher_server/cms/comments/works_comment_replies_test.exs create mode 100644 test/groupher_server/cms/comments/works_comment_test.exs create mode 100644 test/groupher_server/cms/emotions/works_emotions_test.exs create mode 100644 test/groupher_server/cms/hooks/cite_works_test.exs create mode 100644 test/groupher_server/cms/hooks/mention_in_works_test.exs create mode 100644 test/groupher_server/cms/hooks/notify_works_test.exs create mode 100644 test/groupher_server/cms/upvotes/works_upvote_test.exs create mode 100644 test/groupher_server_web/mutation/cms/abuse_reports/works_report_test.exs create mode 100644 test/groupher_server_web/mutation/cms/article_community/works_test.exs create mode 100644 test/groupher_server_web/mutation/cms/article_tags/works_tag_test.exs create mode 100644 test/groupher_server_web/mutation/cms/articles/works_test.exs create mode 100644 test/groupher_server_web/mutation/cms/comments/works_comment_test.exs create mode 100644 test/groupher_server_web/mutation/cms/flags/works_flag_test.exs create mode 100644 test/groupher_server_web/mutation/cms/sink/works_sink_test.exs create mode 100644 test/groupher_server_web/mutation/cms/upvotes/works_upvote_test.exs create mode 100644 test/groupher_server_web/query/accounts/published/published_works_test.exs create mode 100644 test/groupher_server_web/query/cms/paged_articles/paged_works_test.exs diff --git a/test/groupher_server/cms/abuse_reports/works_report_test.exs b/test/groupher_server/cms/abuse_reports/works_report_test.exs new file mode 100644 index 000000000..ca6062eb5 --- /dev/null +++ b/test/groupher_server/cms/abuse_reports/works_report_test.exs @@ -0,0 +1,137 @@ +defmodule GroupherServer.Test.CMS.AbuseReports.WorksReport do + @moduledoc false + + use GroupherServer.TestTools + + alias Helper.ORM + alias GroupherServer.CMS + alias CMS.Model.Works + + setup do + {:ok, user} = db_insert(:user) + {:ok, user2} = db_insert(:user) + + {:ok, community} = db_insert(:community) + works_attrs = mock_attrs(:works, %{community_id: community.id}) + + {:ok, ~m(user user2 community works_attrs)a} + end + + describe "[article works report/unreport]" do + test "list article reports should work", ~m(community user user2 works_attrs)a do + {:ok, works} = CMS.create_article(community, :works, works_attrs, user) + {:ok, _report} = CMS.report_article(:works, works.id, "reason", "attr_info", user) + {:ok, _report} = CMS.report_article(:works, works.id, "reason", "attr_info", user2) + + filter = %{content_type: :works, content_id: works.id, page: 1, size: 20} + {:ok, all_reports} = CMS.paged_reports(filter) + + report = all_reports.entries |> List.first() + assert report.article.id == works.id + assert report.article.thread == "WORKS" + end + + test "report a works should have a abuse report record", ~m(community user works_attrs)a do + {:ok, works} = CMS.create_article(community, :works, works_attrs, user) + {:ok, _report} = CMS.report_article(:works, works.id, "reason", "attr_info", user) + + filter = %{content_type: :works, content_id: works.id, page: 1, size: 20} + {:ok, all_reports} = CMS.paged_reports(filter) + + report = List.first(all_reports.entries) + report_cases = report.report_cases + + assert report.article.id == works.id + assert all_reports.total_count == 1 + assert report.report_cases_count == 1 + assert List.first(report_cases).user.login == user.login + + {:ok, works} = ORM.find(Works, works.id) + assert works.meta.reported_count == 1 + assert user.id in works.meta.reported_user_ids + end + + test "can undo a report", ~m(community user works_attrs)a do + {:ok, works} = CMS.create_article(community, :works, works_attrs, user) + {:ok, _report} = CMS.report_article(:works, works.id, "reason", "attr_info", user) + {:ok, _report} = CMS.undo_report_article(:works, works.id, user) + + filter = %{content_type: :works, content_id: works.id, page: 1, size: 20} + {:ok, all_reports} = CMS.paged_reports(filter) + assert all_reports.total_count == 0 + + {:ok, works} = ORM.find(Works, works.id) + assert user.id not in works.meta.reported_user_ids + end + + test "can undo a existed report", ~m(community user user2 works_attrs)a do + {:ok, works} = CMS.create_article(community, :works, works_attrs, user) + {:ok, _report} = CMS.report_article(:works, works.id, "reason", "attr_info", user) + {:ok, _report} = CMS.report_article(:works, works.id, "reason", "attr_info", user2) + {:ok, _report} = CMS.undo_report_article(:works, works.id, user) + + filter = %{content_type: :works, content_id: works.id, page: 1, size: 20} + {:ok, all_reports} = CMS.paged_reports(filter) + assert all_reports.total_count == 1 + + {:ok, works} = ORM.find(Works, works.id) + + assert user2.id in works.meta.reported_user_ids + assert user.id not in works.meta.reported_user_ids + end + + test "can undo a report with other user report it too", + ~m(community user user2 works_attrs)a do + {:ok, works} = CMS.create_article(community, :works, works_attrs, user) + {:ok, _report} = CMS.report_article(:works, works.id, "reason", "attr_info", user) + {:ok, _report} = CMS.report_article(:works, works.id, "reason", "attr_info", user2) + + filter = %{content_type: :works, content_id: works.id, page: 1, size: 20} + {:ok, all_reports} = CMS.paged_reports(filter) + assert all_reports.total_count == 1 + + report = all_reports.entries |> List.first() + assert report.report_cases |> length == 2 + assert Enum.any?(report.report_cases, &(&1.user.login == user.login)) + assert Enum.any?(report.report_cases, &(&1.user.login == user2.login)) + + {:ok, _report} = CMS.undo_report_article(:works, works.id, user) + + filter = %{content_type: :works, content_id: works.id, page: 1, size: 20} + {:ok, all_reports} = CMS.paged_reports(filter) + assert all_reports.total_count == 1 + + report = all_reports.entries |> List.first() + assert report.report_cases |> length == 1 + assert Enum.any?(report.report_cases, &(&1.user.login == user2.login)) + end + + test "different user report a comment should have same report with different report cases", + ~m(community user user2 works_attrs)a do + {:ok, works} = CMS.create_article(community, :works, works_attrs, user) + + {:ok, _report} = CMS.report_article(:works, works.id, "reason", "attr_info", user) + {:ok, _report} = CMS.report_article(:works, works.id, "reason2", "attr_info 2", user2) + + filter = %{content_type: :works, content_id: works.id, page: 1, size: 20} + {:ok, all_reports} = CMS.paged_reports(filter) + + report = List.first(all_reports.entries) + report_cases = report.report_cases + + assert all_reports.total_count == 1 + assert length(report_cases) == 2 + assert report.report_cases_count == 2 + + assert List.first(report_cases).user.login == user.login + assert List.last(report_cases).user.login == user2.login + end + + test "same user can not report a comment twice", ~m(community works_attrs user)a do + {:ok, works} = CMS.create_article(community, :works, works_attrs, user) + + {:ok, _report} = CMS.report_article(:works, works.id, "reason", "attr_info", user) + assert {:error, _report} = CMS.report_article(:works, works.id, "reason", "attr_info", user) + end + end +end diff --git a/test/groupher_server/cms/article_community/works_test.exs b/test/groupher_server/cms/article_community/works_test.exs new file mode 100644 index 000000000..b597f410c --- /dev/null +++ b/test/groupher_server/cms/article_community/works_test.exs @@ -0,0 +1,85 @@ +defmodule GroupherServer.Test.CMS.ArticleCommunity.Works do + use GroupherServer.TestTools + + alias Helper.ORM + alias GroupherServer.CMS + alias CMS.Model.Works + + setup do + {:ok, user} = db_insert(:user) + {:ok, user2} = db_insert(:user) + {:ok, works} = db_insert(:works) + {:ok, community} = db_insert(:community) + {:ok, community2} = db_insert(:community) + {:ok, community3} = db_insert(:community) + + works_attrs = mock_attrs(:works, %{community_id: community.id}) + + {:ok, ~m(user user2 community community2 community3 works works_attrs)a} + end + + describe "[article mirror/move]" do + test "created works has origial community info", ~m(user community works_attrs)a do + {:ok, works} = CMS.create_article(community, :works, works_attrs, user) + {:ok, works} = ORM.find(Works, works.id, preload: :original_community) + + assert works.original_community_id == community.id + end + + test "works can be move to other community", ~m(user community community2 works_attrs)a do + {:ok, works} = CMS.create_article(community, :works, works_attrs, user) + assert works.original_community_id == community.id + + {:ok, _} = CMS.move_article(:works, works.id, community2.id) + {:ok, works} = ORM.find(Works, works.id, preload: [:original_community, :communities]) + + assert works.original_community.id == community2.id + assert not is_nil(Enum.find(works.communities, &(&1.id == community2.id))) + end + + test "works can be mirror to other community", ~m(user community community2 works_attrs)a do + {:ok, works} = CMS.create_article(community, :works, works_attrs, user) + + {:ok, works} = ORM.find(Works, works.id, preload: :communities) + assert works.communities |> length == 1 + + assert not is_nil(Enum.find(works.communities, &(&1.id == community.id))) + + {:ok, _} = CMS.mirror_article(:works, works.id, community2.id) + + {:ok, works} = ORM.find(Works, works.id, preload: :communities) + assert works.communities |> length == 2 + assert not is_nil(Enum.find(works.communities, &(&1.id == community.id))) + assert not is_nil(Enum.find(works.communities, &(&1.id == community2.id))) + end + + test "works can be unmirror from community", + ~m(user community community2 community3 works_attrs)a do + {:ok, works} = CMS.create_article(community, :works, works_attrs, user) + {:ok, _} = CMS.mirror_article(:works, works.id, community2.id) + {:ok, _} = CMS.mirror_article(:works, works.id, community3.id) + + {:ok, works} = ORM.find(Works, works.id, preload: :communities) + assert works.communities |> length == 3 + + {:ok, _} = CMS.unmirror_article(:works, works.id, community3.id) + {:ok, works} = ORM.find(Works, works.id, preload: :communities) + assert works.communities |> length == 2 + + assert is_nil(Enum.find(works.communities, &(&1.id == community3.id))) + end + + test "works can not unmirror from original community", + ~m(user community community2 community3 works_attrs)a do + {:ok, works} = CMS.create_article(community, :works, works_attrs, user) + {:ok, _} = CMS.mirror_article(:works, works.id, community2.id) + {:ok, _} = CMS.mirror_article(:works, works.id, community3.id) + + {:ok, works} = ORM.find(Works, works.id, preload: :communities) + assert works.communities |> length == 3 + + {:error, reason} = CMS.unmirror_article(:works, works.id, community.id) + assert reason |> is_error?(:mirror_article) + end + end +end diff --git a/test/groupher_server/cms/article_tags/works_tag_test.exs b/test/groupher_server/cms/article_tags/works_tag_test.exs new file mode 100644 index 000000000..413161523 --- /dev/null +++ b/test/groupher_server/cms/article_tags/works_tag_test.exs @@ -0,0 +1,134 @@ +defmodule GroupherServer.Test.CMS.ArticleTag.WorksTag do + use GroupherServer.TestTools + + alias GroupherServer.CMS + alias CMS.Model.{Community, ArticleTag, Works} + alias Helper.{ORM} + + setup do + {:ok, user} = db_insert(:user) + {:ok, works} = db_insert(:works) + {:ok, community} = db_insert(:community) + article_tag_attrs = mock_attrs(:article_tag) + article_tag_attrs2 = mock_attrs(:article_tag) + + works_attrs = mock_attrs(:works) + + {:ok, ~m(user community works works_attrs article_tag_attrs article_tag_attrs2)a} + end + + describe "[works tag CURD]" do + test "create article tag with valid data", ~m(community article_tag_attrs user)a do + {:ok, article_tag} = CMS.create_article_tag(community, :works, article_tag_attrs, user) + assert article_tag.title == article_tag_attrs.title + assert article_tag.group == article_tag_attrs.group + end + + test "can update an article tag", ~m(community article_tag_attrs user)a do + {:ok, article_tag} = CMS.create_article_tag(community, :works, article_tag_attrs, user) + + new_attrs = article_tag_attrs |> Map.merge(%{title: "new title"}) + + {:ok, article_tag} = CMS.update_article_tag(article_tag.id, new_attrs) + assert article_tag.title == "new title" + end + + test "create article tag with non-exsit community fails", ~m(article_tag_attrs user)a do + assert {:error, _} = + CMS.create_article_tag( + %Community{id: non_exsit_id()}, + :works, + article_tag_attrs, + user + ) + end + + test "tag can be deleted", ~m(community article_tag_attrs user)a do + {:ok, article_tag} = CMS.create_article_tag(community, :works, article_tag_attrs, user) + {:ok, article_tag} = ORM.find(ArticleTag, article_tag.id) + + {:ok, _} = CMS.delete_article_tag(article_tag.id) + + assert {:error, _} = ORM.find(ArticleTag, article_tag.id) + end + + test "assoc tag should be delete after tag deleted", + ~m(community works article_tag_attrs article_tag_attrs2 user)a do + {:ok, article_tag} = CMS.create_article_tag(community, :works, article_tag_attrs, user) + {:ok, article_tag2} = CMS.create_article_tag(community, :works, article_tag_attrs2, user) + + {:ok, works} = CMS.set_article_tag(:works, works.id, article_tag.id) + {:ok, works} = CMS.set_article_tag(:works, works.id, article_tag2.id) + + {:ok, works} = ORM.find(Works, works.id, preload: :article_tags) + assert exist_in?(article_tag, works.article_tags) + assert exist_in?(article_tag2, works.article_tags) + + {:ok, _} = CMS.delete_article_tag(article_tag.id) + + {:ok, works} = ORM.find(Works, works.id, preload: :article_tags) + assert not exist_in?(article_tag, works.article_tags) + assert exist_in?(article_tag2, works.article_tags) + + {:ok, _} = CMS.delete_article_tag(article_tag2.id) + + {:ok, works} = ORM.find(Works, works.id, preload: :article_tags) + assert not exist_in?(article_tag, works.article_tags) + assert not exist_in?(article_tag2, works.article_tags) + end + end + + describe "[create/update works with tags]" do + test "can create works with exsited article tags", + ~m(community user works_attrs article_tag_attrs article_tag_attrs2)a do + {:ok, article_tag} = CMS.create_article_tag(community, :works, article_tag_attrs, user) + {:ok, article_tag2} = CMS.create_article_tag(community, :works, article_tag_attrs2, user) + + works_with_tags = Map.merge(works_attrs, %{article_tags: [article_tag.id, article_tag2.id]}) + + {:ok, created} = CMS.create_article(community, :works, works_with_tags, user) + {:ok, works} = ORM.find(Works, created.id, preload: :article_tags) + + assert exist_in?(article_tag, works.article_tags) + assert exist_in?(article_tag2, works.article_tags) + end + + test "can not create works with other community's article tags", + ~m(community user works_attrs article_tag_attrs article_tag_attrs2)a do + {:ok, community2} = db_insert(:community) + {:ok, article_tag} = CMS.create_article_tag(community, :works, article_tag_attrs, user) + {:ok, article_tag2} = CMS.create_article_tag(community2, :works, article_tag_attrs2, user) + + works_with_tags = Map.merge(works_attrs, %{article_tags: [article_tag.id, article_tag2.id]}) + + {:error, reason} = CMS.create_article(community, :works, works_with_tags, user) + is_error?(reason, :invalid_domain_tag) + end + end + + describe "[works tag set /unset]" do + test "can set a tag ", ~m(community works article_tag_attrs article_tag_attrs2 user)a do + {:ok, article_tag} = CMS.create_article_tag(community, :works, article_tag_attrs, user) + {:ok, article_tag2} = CMS.create_article_tag(community, :works, article_tag_attrs2, user) + + {:ok, works} = CMS.set_article_tag(:works, works.id, article_tag.id) + assert works.article_tags |> length == 1 + assert exist_in?(article_tag, works.article_tags) + + {:ok, works} = CMS.set_article_tag(:works, works.id, article_tag2.id) + assert works.article_tags |> length == 2 + assert exist_in?(article_tag, works.article_tags) + assert exist_in?(article_tag2, works.article_tags) + + {:ok, works} = CMS.unset_article_tag(:works, works.id, article_tag.id) + assert works.article_tags |> length == 1 + assert not exist_in?(article_tag, works.article_tags) + assert exist_in?(article_tag2, works.article_tags) + + {:ok, works} = CMS.unset_article_tag(:works, works.id, article_tag2.id) + assert works.article_tags |> length == 0 + assert not exist_in?(article_tag, works.article_tags) + assert not exist_in?(article_tag2, works.article_tags) + end + end +end diff --git a/test/groupher_server/cms/articles/works_pin_test.exs b/test/groupher_server/cms/articles/works_pin_test.exs new file mode 100644 index 000000000..d5b5b9153 --- /dev/null +++ b/test/groupher_server/cms/articles/works_pin_test.exs @@ -0,0 +1,54 @@ +defmodule GroupherServer.Test.CMS.Artilces.WorksPin do + @moduledoc false + + use GroupherServer.TestTools + + alias Helper.ORM + alias GroupherServer.CMS + + alias CMS.Model.{Community, PinnedArticle} + + @max_pinned_article_count_per_thread Community.max_pinned_article_count_per_thread() + + setup do + {:ok, user} = db_insert(:user) + {:ok, community} = db_insert(:community) + + {:ok, works} = CMS.create_article(community, :works, mock_attrs(:works), user) + + {:ok, ~m(user community works)a} + end + + describe "[cms works pin]" do + test "can pin a works", ~m(community works)a do + {:ok, _} = CMS.pin_article(:works, works.id, community.id) + {:ok, pind_article} = ORM.find_by(PinnedArticle, %{works_id: works.id}) + + assert pind_article.works_id == works.id + end + + test "one community & thread can only pin certern count of works", ~m(community user)a do + Enum.reduce(1..@max_pinned_article_count_per_thread, [], fn _, acc -> + {:ok, new_works} = CMS.create_article(community, :works, mock_attrs(:works), user) + {:ok, _} = CMS.pin_article(:works, new_works.id, community.id) + acc + end) + + {:ok, new_works} = CMS.create_article(community, :works, mock_attrs(:works), user) + {:error, reason} = CMS.pin_article(:works, new_works.id, community.id) + assert reason |> Keyword.get(:code) == ecode(:too_much_pinned_article) + end + + test "can not pin a non-exsit works", ~m(community)a do + assert {:error, _} = CMS.pin_article(:works, 8848, community.id) + end + + test "can undo pin to a works", ~m(community works)a do + {:ok, _} = CMS.pin_article(:works, works.id, community.id) + + assert {:ok, unpinned} = CMS.undo_pin_article(:works, works.id, community.id) + + assert {:error, _} = ORM.find_by(PinnedArticle, %{works_id: works.id}) + end + end +end diff --git a/test/groupher_server/cms/comments/works_comment_emotions_test.exs b/test/groupher_server/cms/comments/works_comment_emotions_test.exs new file mode 100644 index 000000000..3ba13c746 --- /dev/null +++ b/test/groupher_server/cms/comments/works_comment_emotions_test.exs @@ -0,0 +1,192 @@ +defmodule GroupherServer.Test.CMS.Comments.WorksCommentEmotions do + @moduledoc false + + use GroupherServer.TestTools + + alias Helper.ORM + alias GroupherServer.CMS + + alias CMS.Model.{Comment, Embeds, CommentUserEmotion} + + @default_emotions Embeds.CommentEmotion.default_emotions() + + setup do + {:ok, user} = db_insert(:user) + {:ok, user2} = db_insert(:user) + {:ok, user3} = db_insert(:user) + + {:ok, works} = db_insert(:works) + + {:ok, ~m(user user2 user3 works)a} + end + + describe "[emotion in paged article comment]" do + test "login user should got viewer has emotioned status", ~m(works user)a do + total_count = 0 + page_number = 10 + page_size = 20 + + all_comment = + Enum.reduce(0..total_count, [], fn _, acc -> + {:ok, comment} = CMS.create_comment(:works, works.id, mock_comment(), user) + acc ++ [comment] + end) + + first_comment = List.first(all_comment) + + {:ok, _} = CMS.emotion_to_comment(first_comment.id, :downvote, user) + {:ok, _} = CMS.emotion_to_comment(first_comment.id, :beer, user) + {:ok, _} = CMS.emotion_to_comment(first_comment.id, :popcorn, user) + + {:ok, paged_comments} = + CMS.paged_comments( + :works, + works.id, + %{page: page_number, size: page_size}, + :replies, + user + ) + + target = Enum.find(paged_comments.entries, &(&1.id == first_comment.id)) + + assert target.emotions.downvote_count == 1 + assert user_exist_in?(user, target.emotions.latest_downvote_users) + assert target.emotions.viewer_has_downvoteed + + assert target.emotions.beer_count == 1 + assert user_exist_in?(user, target.emotions.latest_beer_users) + assert target.emotions.viewer_has_beered + + assert target.emotions.popcorn_count == 1 + assert user_exist_in?(user, target.emotions.latest_popcorn_users) + assert target.emotions.viewer_has_popcorned + end + end + + describe "[basic article comment emotion]" do + test "comment has default emotions after created", ~m(works user)a do + {:ok, parent_comment} = CMS.create_comment(:works, works.id, mock_comment(), user) + {:ok, parent_comment} = ORM.find(Comment, parent_comment.id) + + emotions = parent_comment.emotions |> Map.from_struct() |> Map.delete(:id) + assert @default_emotions == emotions + end + + test "can make emotion to comment", ~m(works user user2)a do + {:ok, parent_comment} = CMS.create_comment(:works, works.id, mock_comment(), user) + + {:ok, _} = CMS.emotion_to_comment(parent_comment.id, :downvote, user) + {:ok, _} = CMS.emotion_to_comment(parent_comment.id, :downvote, user2) + + {:ok, %{emotions: emotions}} = ORM.find(Comment, parent_comment.id) + + assert emotions.downvote_count == 2 + assert user_exist_in?(user, emotions.latest_downvote_users) + assert user_exist_in?(user2, emotions.latest_downvote_users) + end + + test "can undo emotion to comment", ~m(works user user2)a do + {:ok, parent_comment} = CMS.create_comment(:works, works.id, mock_comment(), user) + + {:ok, _} = CMS.emotion_to_comment(parent_comment.id, :downvote, user) + {:ok, _} = CMS.emotion_to_comment(parent_comment.id, :downvote, user2) + + {:ok, %{emotions: emotions}} = ORM.find(Comment, parent_comment.id) + + assert emotions.downvote_count == 2 + assert user_exist_in?(user, emotions.latest_downvote_users) + assert user_exist_in?(user2, emotions.latest_downvote_users) + + {:ok, _} = CMS.undo_emotion_to_comment(parent_comment.id, :downvote, user) + {:ok, _} = CMS.undo_emotion_to_comment(parent_comment.id, :downvote, user2) + + {:ok, %{emotions: emotions}} = ORM.find(Comment, parent_comment.id) + assert emotions.downvote_count == 0 + assert not user_exist_in?(user, emotions.latest_downvote_users) + assert not user_exist_in?(user2, emotions.latest_downvote_users) + end + + test "same user make same emotion to same comment.", ~m(works user)a do + {:ok, parent_comment} = CMS.create_comment(:works, works.id, mock_comment(), user) + + {:ok, _} = CMS.emotion_to_comment(parent_comment.id, :downvote, user) + {:ok, _} = CMS.emotion_to_comment(parent_comment.id, :downvote, user) + + {:ok, parent_comment} = ORM.find(Comment, parent_comment.id) + + assert parent_comment.emotions.downvote_count == 1 + assert user_exist_in?(user, parent_comment.emotions.latest_downvote_users) + end + + test "same user same emotion to same comment only have one user_emotion record", + ~m(works user)a do + {:ok, parent_comment} = CMS.create_comment(:works, works.id, mock_comment(), user) + + {:ok, _} = CMS.emotion_to_comment(parent_comment.id, :downvote, user) + {:ok, _} = CMS.emotion_to_comment(parent_comment.id, :heart, user) + + {:ok, parent_comment} = ORM.find(Comment, parent_comment.id) + + {:ok, records} = ORM.find_all(CommentUserEmotion, %{page: 1, size: 10}) + assert records.total_count == 1 + + {:ok, record} = + ORM.find_by(CommentUserEmotion, %{ + comment_id: parent_comment.id, + user_id: user.id + }) + + assert record.downvote + assert record.heart + end + + test "different user can make same emotions on same comment", ~m(works user user2 user3)a do + {:ok, parent_comment} = CMS.create_comment(:works, works.id, mock_comment(), user) + + {:ok, _} = CMS.emotion_to_comment(parent_comment.id, :beer, user) + {:ok, _} = CMS.emotion_to_comment(parent_comment.id, :beer, user2) + {:ok, _} = CMS.emotion_to_comment(parent_comment.id, :beer, user3) + + {:ok, %{emotions: emotions}} = ORM.find(Comment, parent_comment.id) + # IO.inspect(emotions, label: "the parent_comment") + + assert emotions.beer_count == 3 + assert user_exist_in?(user, emotions.latest_beer_users) + assert user_exist_in?(user2, emotions.latest_beer_users) + assert user_exist_in?(user3, emotions.latest_beer_users) + end + + test "same user can make differcent emotions on same comment", ~m(works user)a do + {:ok, parent_comment} = CMS.create_comment(:works, works.id, mock_comment(), user) + + {:ok, _} = CMS.emotion_to_comment(parent_comment.id, :downvote, user) + {:ok, _} = CMS.emotion_to_comment(parent_comment.id, :downvote, user) + {:ok, _} = CMS.emotion_to_comment(parent_comment.id, :beer, user) + {:ok, _} = CMS.emotion_to_comment(parent_comment.id, :heart, user) + {:ok, _} = CMS.emotion_to_comment(parent_comment.id, :orz, user) + + {:ok, %{emotions: emotions}} = ORM.find(Comment, parent_comment.id) + + assert emotions.downvote_count == 1 + assert user_exist_in?(user, emotions.latest_downvote_users) + + assert emotions.beer_count == 1 + assert user_exist_in?(user, emotions.latest_beer_users) + + assert emotions.heart_count == 1 + assert user_exist_in?(user, emotions.latest_heart_users) + + assert emotions.orz_count == 1 + assert user_exist_in?(user, emotions.latest_orz_users) + + assert emotions.pill_count == 0 + assert not user_exist_in?(user, emotions.latest_pill_users) + + assert emotions.biceps_count == 0 + assert not user_exist_in?(user, emotions.latest_biceps_users) + + assert emotions.confused_count == 0 + assert not user_exist_in?(user, emotions.latest_confused_users) + end + end +end diff --git a/test/groupher_server/cms/comments/works_comment_replies_test.exs b/test/groupher_server/cms/comments/works_comment_replies_test.exs new file mode 100644 index 000000000..c505f5a8b --- /dev/null +++ b/test/groupher_server/cms/comments/works_comment_replies_test.exs @@ -0,0 +1,204 @@ +defmodule GroupherServer.Test.CMS.Comments.WorksCommentReplies do + @moduledoc false + + use GroupherServer.TestTools + + alias Helper.ORM + alias GroupherServer.CMS + + alias CMS.Model.{Comment, Works} + + @max_parent_replies_count Comment.max_parent_replies_count() + + setup do + {:ok, user} = db_insert(:user) + {:ok, user2} = db_insert(:user) + {:ok, works} = db_insert(:works) + + {:ok, ~m(user user2 works)a} + end + + describe "[basic article comment replies]" do + test "exsit comment can be reply", ~m(works user user2)a do + {:ok, parent_comment} = CMS.create_comment(:works, works.id, mock_comment(), user) + {:ok, replyed_comment} = CMS.reply_comment(parent_comment.id, mock_comment(), user2) + assert replyed_comment.reply_to.id == parent_comment.id + + {:ok, parent_comment} = ORM.find(Comment, parent_comment.id) + + assert exist_in?(replyed_comment, parent_comment.replies) + end + + test "deleted comment can not be reply", ~m(works user user2)a do + {:ok, parent_comment} = CMS.create_comment(:works, works.id, mock_comment(), user) + {:ok, _} = CMS.delete_comment(parent_comment) + + {:error, _} = CMS.reply_comment(parent_comment.id, mock_comment(), user2) + end + + test "multi reply should belong to one parent comment", ~m(works user user2)a do + {:ok, parent_comment} = CMS.create_comment(:works, works.id, mock_comment(), user) + + {:ok, replyed_comment_1} = CMS.reply_comment(parent_comment.id, mock_comment(), user2) + + {:ok, replyed_comment_2} = CMS.reply_comment(parent_comment.id, mock_comment(), user2) + + {:ok, parent_comment} = ORM.find(Comment, parent_comment.id) + + assert exist_in?(replyed_comment_1, parent_comment.replies) + assert exist_in?(replyed_comment_2, parent_comment.replies) + end + + test "reply to reply inside a comment should belong same parent comment", + ~m(works user user2)a do + {:ok, parent_comment} = CMS.create_comment(:works, works.id, mock_comment(), user) + + {:ok, replyed_comment_1} = CMS.reply_comment(parent_comment.id, mock_comment(), user2) + + {:ok, replyed_comment_2} = CMS.reply_comment(replyed_comment_1.id, mock_comment(), user2) + + {:ok, replyed_comment_3} = CMS.reply_comment(replyed_comment_2.id, mock_comment(), user) + + {:ok, parent_comment} = ORM.find(Comment, parent_comment.id) + + # IO.inspect(parent_comment.replies, label: "parent_comment.replies") + + assert exist_in?(replyed_comment_1, parent_comment.replies) + assert exist_in?(replyed_comment_2, parent_comment.replies) + assert exist_in?(replyed_comment_3, parent_comment.replies) + + {:ok, replyed_comment_1} = ORM.find(Comment, replyed_comment_1.id) + {:ok, replyed_comment_2} = ORM.find(Comment, replyed_comment_2.id) + {:ok, replyed_comment_3} = ORM.find(Comment, replyed_comment_3.id) + + assert replyed_comment_1.reply_to_id == parent_comment.id + assert replyed_comment_2.reply_to_id == replyed_comment_1.id + assert replyed_comment_3.reply_to_id == replyed_comment_2.id + end + + test "reply to reply inside a comment should have is_reply_to_others flag in meta", + ~m(works user user2)a do + {:ok, parent_comment} = CMS.create_comment(:works, works.id, mock_comment(), user) + + {:ok, replyed_comment_1} = CMS.reply_comment(parent_comment.id, mock_comment(), user2) + + {:ok, replyed_comment_2} = CMS.reply_comment(replyed_comment_1.id, mock_comment(), user2) + + {:ok, replyed_comment_3} = CMS.reply_comment(replyed_comment_2.id, mock_comment(), user) + + {:ok, _parent_comment} = ORM.find(Comment, parent_comment.id) + + {:ok, replyed_comment_1} = ORM.find(Comment, replyed_comment_1.id) + {:ok, replyed_comment_2} = ORM.find(Comment, replyed_comment_2.id) + {:ok, replyed_comment_3} = ORM.find(Comment, replyed_comment_3.id) + + assert not replyed_comment_1.meta.is_reply_to_others + assert replyed_comment_2.meta.is_reply_to_others + assert replyed_comment_3.meta.is_reply_to_others + end + + test "comment replies only contains @max_parent_replies_count replies", ~m(works user)a do + total_reply_count = @max_parent_replies_count + 1 + + {:ok, parent_comment} = CMS.create_comment(:works, works.id, mock_comment(), user) + + reply_comment_list = + Enum.reduce(1..total_reply_count, [], fn n, acc -> + {:ok, replyed_comment} = + CMS.reply_comment(parent_comment.id, mock_comment("reply_content_#{n}"), user) + + acc ++ [replyed_comment] + end) + + {:ok, parent_comment} = ORM.find(Comment, parent_comment.id) + + assert length(parent_comment.replies) == @max_parent_replies_count + assert exist_in?(Enum.at(reply_comment_list, 0), parent_comment.replies) + assert exist_in?(Enum.at(reply_comment_list, 1), parent_comment.replies) + assert exist_in?(Enum.at(reply_comment_list, 2), parent_comment.replies) + assert not exist_in?(List.last(reply_comment_list), parent_comment.replies) + end + + test "replyed user should appear in article comment participants", ~m(works user user2)a do + {:ok, parent_comment} = CMS.create_comment(:works, works.id, mock_comment(), user) + {:ok, _} = CMS.reply_comment(parent_comment.id, mock_comment(), user2) + + {:ok, article} = ORM.find(Works, works.id) + + assert exist_in?(user, article.comments_participants) + assert exist_in?(user2, article.comments_participants) + end + + test "replies count should inc by 1 after got replyed", ~m(works user user2)a do + {:ok, parent_comment} = CMS.create_comment(:works, works.id, mock_comment(), user) + assert parent_comment.replies_count === 0 + + {:ok, _} = CMS.reply_comment(parent_comment.id, mock_comment(), user2) + {:ok, parent_comment} = ORM.find(Comment, parent_comment.id) + assert parent_comment.replies_count === 1 + + {:ok, _} = CMS.reply_comment(parent_comment.id, mock_comment(), user2) + {:ok, parent_comment} = ORM.find(Comment, parent_comment.id) + assert parent_comment.replies_count === 2 + end + end + + describe "[paged article comment replies]" do + test "can get paged replies of a parent comment", ~m(works user)a do + {:ok, parent_comment} = CMS.create_comment(:works, works.id, mock_comment(), user) + {:ok, paged_replies} = CMS.paged_comment_replies(parent_comment.id, %{page: 1, size: 20}) + assert is_valid_pagination?(paged_replies, :raw, :empty) + + total_reply_count = 30 + + reply_comment_list = + Enum.reduce(1..total_reply_count, [], fn n, acc -> + {:ok, replyed_comment} = + CMS.reply_comment(parent_comment.id, mock_comment("reply_content_#{n}"), user) + + acc ++ [replyed_comment] + end) + + {:ok, paged_replies} = CMS.paged_comment_replies(parent_comment.id, %{page: 1, size: 20}) + + assert total_reply_count == paged_replies.total_count + assert is_valid_pagination?(paged_replies, :raw) + + assert exist_in?(Enum.at(reply_comment_list, 0), paged_replies.entries) + assert exist_in?(Enum.at(reply_comment_list, 1), paged_replies.entries) + assert exist_in?(Enum.at(reply_comment_list, 2), paged_replies.entries) + assert exist_in?(Enum.at(reply_comment_list, 3), paged_replies.entries) + end + + test "can get reply_to info of a parent comment", ~m(works user)a do + page_number = 1 + page_size = 10 + + {:ok, parent_comment} = CMS.create_comment(:works, works.id, mock_comment(), user) + + {:ok, reply_comment} = CMS.reply_comment(parent_comment.id, mock_comment(), user) + + {:ok, reply_comment2} = CMS.reply_comment(parent_comment.id, mock_comment(), user) + + {:ok, paged_comments} = + CMS.paged_comments( + :works, + works.id, + %{page: page_number, size: page_size}, + :timeline + ) + + reply_comment = Enum.find(paged_comments.entries, &(&1.id == reply_comment.id)) + + assert reply_comment.reply_to.id == parent_comment.id + assert reply_comment.reply_to.body_html == parent_comment.body_html + assert reply_comment.reply_to.author.id == parent_comment.author_id + + reply_comment2 = Enum.find(paged_comments.entries, &(&1.id == reply_comment2.id)) + + assert reply_comment2.reply_to.id == parent_comment.id + assert reply_comment2.reply_to.body_html == parent_comment.body_html + assert reply_comment2.reply_to.author.id == parent_comment.author_id + end + end +end diff --git a/test/groupher_server/cms/comments/works_comment_test.exs b/test/groupher_server/cms/comments/works_comment_test.exs new file mode 100644 index 000000000..ade2b696f --- /dev/null +++ b/test/groupher_server/cms/comments/works_comment_test.exs @@ -0,0 +1,662 @@ +defmodule GroupherServer.Test.CMS.Comments.WorksComment do + @moduledoc false + + use GroupherServer.TestTools + import Helper.Utils, only: [get_config: 2] + + alias Helper.ORM + alias GroupherServer.{Accounts, CMS} + alias Accounts.Model.User + alias CMS.Model.{Comment, PinnedComment, Embeds, Works} + + @active_period get_config(:article, :active_period_days) + + @delete_hint Comment.delete_hint() + @report_threshold_for_fold Comment.report_threshold_for_fold() + @default_comment_meta Embeds.CommentMeta.default_meta() + @pinned_comment_limit Comment.pinned_comment_limit() + + setup do + {:ok, user} = db_insert(:user) + {:ok, user2} = db_insert(:user) + {:ok, works} = db_insert(:works) + {:ok, community} = db_insert(:community) + + {:ok, ~m(community user user2 works)a} + end + + describe "[basic article comment]" do + test "works are supported by article comment.", ~m(user works)a do + {:ok, works_comment_1} = CMS.create_comment(:works, works.id, mock_comment(), user) + {:ok, works_comment_2} = CMS.create_comment(:works, works.id, mock_comment(), user) + + {:ok, works} = ORM.find(Works, works.id, preload: :comments) + + assert exist_in?(works_comment_1, works.comments) + assert exist_in?(works_comment_2, works.comments) + end + + test "comment should have default meta after create", ~m(user works)a do + {:ok, comment} = CMS.create_comment(:works, works.id, mock_comment(), user) + assert comment.meta |> Map.from_struct() |> Map.delete(:id) == @default_comment_meta + end + + test "create comment should update active timestamp of works", ~m(user works)a do + Process.sleep(1000) + {:ok, _comment} = CMS.create_comment(:works, works.id, mock_comment(), user) + {:ok, works} = ORM.find(Works, works.id, preload: :comments) + + assert not is_nil(works.active_at) + assert works.active_at > works.inserted_at + end + + test "works author create comment will not update active timestamp", ~m(community user)a do + works_attrs = mock_attrs(:works, %{community_id: community.id}) + {:ok, works} = CMS.create_article(community, :works, works_attrs, user) + {:ok, works} = ORM.find(Works, works.id, preload: [author: :user]) + + Process.sleep(1000) + + {:ok, _comment} = CMS.create_comment(:works, works.id, mock_comment(), works.author.user) + + {:ok, works} = ORM.find(Works, works.id, preload: :comments) + + assert not is_nil(works.active_at) + assert works.active_at == works.inserted_at + end + + test "old works will not update active after comment created", ~m(user)a do + active_period_days = Map.get(@active_period, :works) + + inserted_at = + Timex.shift(Timex.now(), days: -(active_period_days - 1)) |> Timex.to_datetime() + + {:ok, works} = db_insert(:works, %{inserted_at: inserted_at}) + Process.sleep(1000) + {:ok, _comment} = CMS.create_comment(:works, works.id, mock_comment(), user) + {:ok, works} = ORM.find(Works, works.id) + + assert works.active_at |> DateTime.to_date() == DateTime.utc_now() |> DateTime.to_date() + + ##### + inserted_at = + Timex.shift(Timex.now(), days: -(active_period_days + 1)) |> Timex.to_datetime() + + {:ok, works} = db_insert(:works, %{inserted_at: inserted_at}) + Process.sleep(3000) + {:ok, _comment} = CMS.create_comment(:works, works.id, mock_comment(), user) + {:ok, works} = ORM.find(Works, works.id) + + assert works.active_at |> DateTime.to_unix() !== DateTime.utc_now() |> DateTime.to_unix() + end + + test "comment can be updated", ~m(works user)a do + {:ok, comment} = CMS.create_comment(:works, works.id, mock_comment(), user) + + {:ok, updated_comment} = CMS.update_comment(comment, mock_comment("updated content")) + + assert updated_comment.body_html |> String.contains?(~s(updated content

)) + end + end + + describe "[article comment floor]" do + test "comment will have a floor number after created", ~m(user works)a do + {:ok, works_comment} = CMS.create_comment(:works, works.id, mock_comment(), user) + {:ok, works_comment2} = CMS.create_comment(:works, works.id, mock_comment(), user) + + {:ok, works_comment} = ORM.find(Comment, works_comment.id) + {:ok, works_comment2} = ORM.find(Comment, works_comment2.id) + + assert works_comment.floor == 1 + assert works_comment2.floor == 2 + end + end + + describe "[article comment participator for works]" do + test "works will have participator after comment created", ~m(user works)a do + {:ok, _} = CMS.create_comment(:works, works.id, mock_comment(), user) + + {:ok, works} = ORM.find(Works, works.id) + + participator = List.first(works.comments_participants) + assert participator.id == user.id + end + + test "psot participator will not contains same user", ~m(user works)a do + {:ok, _} = CMS.create_comment(:works, works.id, mock_comment(), user) + {:ok, _} = CMS.create_comment(:works, works.id, mock_comment(), user) + + {:ok, works} = ORM.find(Works, works.id) + + assert 1 == length(works.comments_participants) + end + + test "recent comment user should appear at first of the psot participants", + ~m(user user2 works)a do + {:ok, _} = CMS.create_comment(:works, works.id, mock_comment(), user) + {:ok, _} = CMS.create_comment(:works, works.id, mock_comment(), user2) + + {:ok, works} = ORM.find(Works, works.id) + + participator = List.first(works.comments_participants) + + assert participator.id == user2.id + end + end + + describe "[article comment upvotes]" do + test "user can upvote a works comment", ~m(user works)a do + {:ok, comment} = CMS.create_comment(:works, works.id, mock_comment(), user) + + CMS.upvote_comment(comment.id, user) + + {:ok, comment} = ORM.find(Comment, comment.id, preload: :upvotes) + + assert 1 == length(comment.upvotes) + assert List.first(comment.upvotes).user_id == user.id + end + + test "article author upvote works comment will have flag", ~m(works user)a do + {:ok, comment} = CMS.create_comment(:works, works.id, mock_comment(), user) + {:ok, author_user} = ORM.find(User, works.author.user.id) + + CMS.upvote_comment(comment.id, author_user) + + {:ok, comment} = ORM.find(Comment, comment.id, preload: :upvotes) + assert comment.meta.is_article_author_upvoted + end + + test "user upvote works comment will add id to upvoted_user_ids", ~m(works user)a do + {:ok, comment} = CMS.create_comment(:works, works.id, mock_comment(), user) + {:ok, comment} = CMS.upvote_comment(comment.id, user) + + assert user.id in comment.meta.upvoted_user_ids + end + + test "user undo upvote works comment will remove id from upvoted_user_ids", + ~m(works user user2)a do + {:ok, comment} = CMS.create_comment(:works, works.id, mock_comment(), user) + {:ok, _comment} = CMS.upvote_comment(comment.id, user) + {:ok, comment} = CMS.upvote_comment(comment.id, user2) + + assert user2.id in comment.meta.upvoted_user_ids + assert user.id in comment.meta.upvoted_user_ids + + {:ok, comment} = CMS.undo_upvote_comment(comment.id, user2) + + assert user.id in comment.meta.upvoted_user_ids + assert user2.id not in comment.meta.upvoted_user_ids + end + + test "user upvote a already-upvoted comment fails", ~m(user works)a do + {:ok, comment} = CMS.create_comment(:works, works.id, mock_comment(), user) + + CMS.upvote_comment(comment.id, user) + {:error, _} = CMS.upvote_comment(comment.id, user) + end + + test "upvote comment should inc the comment's upvotes_count", ~m(user user2 works)a do + {:ok, comment} = CMS.create_comment(:works, works.id, mock_comment(), user) + {:ok, comment} = ORM.find(Comment, comment.id) + assert comment.upvotes_count == 0 + + {:ok, _} = CMS.upvote_comment(comment.id, user) + {:ok, _} = CMS.upvote_comment(comment.id, user2) + + {:ok, comment} = ORM.find(Comment, comment.id) + assert comment.upvotes_count == 2 + end + + test "user can undo upvote a works comment", ~m(user works)a do + {:ok, comment} = CMS.create_comment(:works, works.id, mock_comment(), user) + CMS.upvote_comment(comment.id, user) + + {:ok, comment} = ORM.find(Comment, comment.id, preload: :upvotes) + assert 1 == length(comment.upvotes) + + {:ok, comment} = CMS.undo_upvote_comment(comment.id, user) + assert 0 == comment.upvotes_count + end + + test "user can undo upvote a works comment with no upvote", ~m(user works)a do + {:ok, comment} = CMS.create_comment(:works, works.id, mock_comment(), user) + {:ok, comment} = CMS.undo_upvote_comment(comment.id, user) + assert 0 == comment.upvotes_count + + {:ok, comment} = CMS.undo_upvote_comment(comment.id, user) + assert 0 == comment.upvotes_count + end + end + + describe "[article comment fold/unfold]" do + test "user can fold a comment", ~m(user works)a do + {:ok, comment} = CMS.create_comment(:works, works.id, mock_comment(), user) + {:ok, comment} = ORM.find(Comment, comment.id) + + assert not comment.is_folded + + {:ok, comment} = CMS.fold_comment(comment.id, user) + {:ok, comment} = ORM.find(Comment, comment.id) + assert comment.is_folded + + {:ok, works} = ORM.find(Works, works.id) + assert works.meta.folded_comment_count == 1 + end + + test "user can unfold a comment", ~m(user works)a do + {:ok, comment} = CMS.create_comment(:works, works.id, mock_comment(), user) + {:ok, _comment} = CMS.fold_comment(comment.id, user) + {:ok, comment} = ORM.find(Comment, comment.id) + + assert comment.is_folded + + {:ok, _comment} = CMS.unfold_comment(comment.id, user) + {:ok, comment} = ORM.find(Comment, comment.id) + assert not comment.is_folded + + {:ok, works} = ORM.find(Works, works.id) + assert works.meta.folded_comment_count == 0 + end + end + + describe "[article comment pin/unpin]" do + test "user can pin a comment", ~m(user works)a do + {:ok, comment} = CMS.create_comment(:works, works.id, mock_comment(), user) + {:ok, comment} = ORM.find(Comment, comment.id) + + assert not comment.is_pinned + + {:ok, comment} = CMS.pin_comment(comment.id) + {:ok, comment} = ORM.find(Comment, comment.id) + + assert comment.is_pinned + + {:ok, pined_record} = PinnedComment |> ORM.find_by(%{works_id: works.id}) + assert pined_record.works_id == works.id + end + + test "user can unpin a comment", ~m(user works)a do + {:ok, comment} = CMS.create_comment(:works, works.id, mock_comment(), user) + + {:ok, _comment} = CMS.pin_comment(comment.id) + {:ok, comment} = CMS.undo_pin_comment(comment.id) + + assert not comment.is_pinned + assert {:error, _} = PinnedComment |> ORM.find_by(%{comment_id: comment.id}) + end + + test "pinned comments has a limit for each article", ~m(user works)a do + {:ok, comment} = CMS.create_comment(:works, works.id, mock_comment(), user) + + Enum.reduce(0..(@pinned_comment_limit - 1), [], fn _, _acc -> + {:ok, _comment} = CMS.pin_comment(comment.id) + end) + + assert {:error, _} = CMS.pin_comment(comment.id) + end + end + + describe "[article comment report/unreport]" do + # + # test "user can report a comment", ~m(user works)a do + # {:ok, comment} = CMS.create_comment(:works, works.id, mock_comment(), user) + # {:ok, comment} = ORM.find(Comment, comment.id) + + # {:ok, comment} = CMS.report_comment(comment.id, mock_comment(), "attr", user) + # {:ok, comment} = ORM.find(Comment, comment.id) + # end + + # + # test "user can unreport a comment", ~m(user works)a do + # {:ok, comment} = CMS.create_comment(:works, works.id, mock_comment(), user) + # {:ok, _comment} = CMS.report_comment(comment.id, mock_comment(), "attr", user) + # {:ok, comment} = ORM.find(Comment, comment.id) + + # {:ok, _comment} = CMS.undo_report_comment(comment.id, user) + # {:ok, comment} = ORM.find(Comment, comment.id) + # end + + test "can undo a report with other user report it too", ~m(user user2 works)a do + {:ok, comment} = CMS.create_comment(:works, works.id, mock_comment(), user) + + {:ok, _comment} = CMS.report_comment(comment.id, mock_comment(), "attr", user) + {:ok, _comment} = CMS.report_comment(comment.id, mock_comment(), "attr", user2) + + filter = %{content_type: :comment, content_id: comment.id, page: 1, size: 20} + {:ok, all_reports} = CMS.paged_reports(filter) + assert all_reports.total_count == 1 + + report = all_reports.entries |> List.first() + assert report.report_cases |> length == 2 + assert Enum.any?(report.report_cases, &(&1.user.login == user.login)) + assert Enum.any?(report.report_cases, &(&1.user.login == user2.login)) + + {:ok, _report} = CMS.undo_report_article(:comment, comment.id, user) + + filter = %{content_type: :comment, content_id: comment.id, page: 1, size: 20} + {:ok, all_reports} = CMS.paged_reports(filter) + + assert all_reports.total_count == 1 + + report = all_reports.entries |> List.first() + assert report.report_cases |> length == 1 + assert Enum.any?(report.report_cases, &(&1.user.login == user2.login)) + end + + test "report user < @report_threshold_for_fold will not fold comment", ~m(user works)a do + {:ok, comment} = CMS.create_comment(:works, works.id, mock_comment(), user) + + assert not comment.is_folded + + Enum.reduce(1..(@report_threshold_for_fold - 1), [], fn _, _acc -> + {:ok, user} = db_insert(:user) + {:ok, _comment} = CMS.report_comment(comment.id, mock_comment(), "attr", user) + end) + + {:ok, comment} = ORM.find(Comment, comment.id) + assert not comment.is_folded + end + + test "report user > @report_threshold_for_fold will cause comment fold", ~m(user works)a do + {:ok, comment} = CMS.create_comment(:works, works.id, mock_comment(), user) + + assert not comment.is_folded + + Enum.reduce(1..(@report_threshold_for_fold + 1), [], fn _, _acc -> + {:ok, user} = db_insert(:user) + {:ok, _comment} = CMS.report_comment(comment.id, mock_comment(), "attr", user) + end) + + {:ok, comment} = ORM.find(Comment, comment.id) + assert comment.is_folded + end + end + + describe "paged article comments" do + test "can load paged comments participants of a article", ~m(user works)a do + total_count = 30 + page_size = 10 + thread = :works + + Enum.reduce(1..total_count, [], fn _, acc -> + {:ok, new_user} = db_insert(:user) + {:ok, comment} = CMS.create_comment(:works, works.id, mock_comment(), new_user) + + acc ++ [comment] + end) + + {:ok, _comment} = CMS.create_comment(:works, works.id, mock_comment(), user) + {:ok, _comment} = CMS.create_comment(:works, works.id, mock_comment(), user) + + {:ok, results} = + CMS.paged_comments_participants(thread, works.id, %{page: 1, size: page_size}) + + assert results |> is_valid_pagination?(:raw) + assert results.total_count == total_count + 1 + end + + test "paged article comments folded flag should be false", ~m(user works)a do + total_count = 30 + page_number = 1 + page_size = 35 + + all_comments = + Enum.reduce(1..total_count, [], fn _, acc -> + {:ok, comment} = CMS.create_comment(:works, works.id, mock_comment(), user) + + acc ++ [comment] + end) + + {:ok, paged_comments} = + CMS.paged_comments( + :works, + works.id, + %{page: page_number, size: page_size}, + :replies + ) + + random_comment = all_comments |> Enum.at(Enum.random(0..(total_count - 1))) + + assert not random_comment.is_folded + + assert page_number == paged_comments.page_number + assert page_size == paged_comments.page_size + assert total_count == paged_comments.total_count + end + + test "paged article comments should contains pinned comments at top position", + ~m(user works)a do + total_count = 20 + page_number = 1 + page_size = 5 + + Enum.reduce(1..total_count, [], fn _, acc -> + {:ok, comment} = CMS.create_comment(:works, works.id, mock_comment(), user) + + acc ++ [comment] + end) + + {:ok, random_comment_1} = CMS.create_comment(:works, works.id, mock_comment(), user) + {:ok, random_comment_2} = CMS.create_comment(:works, works.id, mock_comment(), user) + + {:ok, pined_comment_1} = CMS.pin_comment(random_comment_1.id) + {:ok, pined_comment_2} = CMS.pin_comment(random_comment_2.id) + + {:ok, paged_comments} = + CMS.paged_comments( + :works, + works.id, + %{page: page_number, size: page_size}, + :replies + ) + + 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 + + test "only page 1 have pinned coments", + ~m(user works)a do + total_count = 20 + page_number = 2 + page_size = 5 + + Enum.reduce(1..total_count, [], fn _, acc -> + {:ok, comment} = CMS.create_comment(:works, works.id, mock_comment(), user) + + acc ++ [comment] + end) + + {:ok, random_comment_1} = CMS.create_comment(:works, works.id, mock_comment(), user) + {:ok, random_comment_2} = CMS.create_comment(:works, works.id, mock_comment(), user) + + {:ok, pined_comment_1} = CMS.pin_comment(random_comment_1.id) + {:ok, pined_comment_2} = CMS.pin_comment(random_comment_2.id) + + {:ok, paged_comments} = + CMS.paged_comments( + :works, + works.id, + %{page: page_number, size: page_size}, + :replies + ) + + 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 + + test "paged article comments should not contains folded and repoted comments", + ~m(user works)a do + total_count = 15 + page_number = 1 + page_size = 20 + + all_comments = + Enum.reduce(1..total_count, [], fn _, acc -> + {:ok, comment} = CMS.create_comment(:works, works.id, mock_comment(), user) + + acc ++ [comment] + end) + + random_comment_1 = all_comments |> Enum.at(0) + random_comment_2 = all_comments |> Enum.at(1) + random_comment_3 = all_comments |> Enum.at(3) + + {:ok, _comment} = CMS.fold_comment(random_comment_1.id, user) + {:ok, _comment} = CMS.fold_comment(random_comment_2.id, user) + {:ok, _comment} = CMS.fold_comment(random_comment_3.id, user) + + {:ok, paged_comments} = + CMS.paged_comments( + :works, + works.id, + %{page: page_number, size: page_size}, + :replies + ) + + assert not exist_in?(random_comment_1, paged_comments.entries) + assert not exist_in?(random_comment_2, paged_comments.entries) + assert not exist_in?(random_comment_3, paged_comments.entries) + + assert page_number == paged_comments.page_number + assert page_size == paged_comments.page_size + assert total_count - 3 == paged_comments.total_count + end + + test "can loaded paged folded comment", ~m(user works)a do + total_count = 10 + page_number = 1 + page_size = 20 + + all_folded_comments = + Enum.reduce(1..total_count, [], fn _, acc -> + {:ok, comment} = CMS.create_comment(:works, works.id, mock_comment(), user) + CMS.fold_comment(comment.id, user) + + acc ++ [comment] + end) + + random_comment_1 = all_folded_comments |> Enum.at(1) + random_comment_2 = all_folded_comments |> Enum.at(3) + random_comment_3 = all_folded_comments |> Enum.at(5) + + {:ok, paged_comments} = + CMS.paged_folded_comments(:works, works.id, %{page: page_number, size: page_size}) + + assert exist_in?(random_comment_1, paged_comments.entries) + assert exist_in?(random_comment_2, paged_comments.entries) + assert exist_in?(random_comment_3, paged_comments.entries) + + assert page_number == paged_comments.page_number + assert page_size == paged_comments.page_size + assert total_count == paged_comments.total_count + end + end + + describe "[article comment delete]" do + test "delete comment still exsit in paged list and content is gone", ~m(user works)a do + total_count = 10 + page_number = 1 + page_size = 20 + + all_comments = + Enum.reduce(1..total_count, [], fn _, acc -> + {:ok, comment} = CMS.create_comment(:works, works.id, mock_comment(), user) + + acc ++ [comment] + end) + + random_comment = all_comments |> Enum.at(1) + + {:ok, deleted_comment} = CMS.delete_comment(random_comment) + + {:ok, paged_comments} = + CMS.paged_comments( + :works, + works.id, + %{page: page_number, size: page_size}, + :replies + ) + + assert exist_in?(deleted_comment, paged_comments.entries) + assert deleted_comment.is_deleted + assert deleted_comment.body_html == @delete_hint + end + + test "delete comment still update article's comments_count field", ~m(user works)a do + {:ok, _comment} = CMS.create_comment(:works, works.id, mock_comment(), user) + {:ok, _comment} = CMS.create_comment(:works, works.id, mock_comment(), user) + {:ok, comment} = CMS.create_comment(:works, works.id, mock_comment(), user) + {:ok, _comment} = CMS.create_comment(:works, works.id, mock_comment(), user) + {:ok, _comment} = CMS.create_comment(:works, works.id, mock_comment(), user) + + {:ok, works} = ORM.find(Works, works.id) + + assert works.comments_count == 5 + + {:ok, _} = CMS.delete_comment(comment) + + {:ok, works} = ORM.find(Works, works.id) + assert works.comments_count == 4 + end + + test "delete comment still delete pinned record if needed", ~m(user works)a do + total_count = 10 + + all_comments = + Enum.reduce(1..total_count, [], fn _, acc -> + {:ok, comment} = CMS.create_comment(:works, works.id, mock_comment(), user) + + acc ++ [comment] + end) + + random_comment = all_comments |> Enum.at(1) + + {:ok, _comment} = CMS.pin_comment(random_comment.id) + {:ok, _comment} = ORM.find(Comment, random_comment.id) + + {:ok, _} = CMS.delete_comment(random_comment) + assert {:error, _comment} = ORM.find(PinnedComment, random_comment.id) + end + end + + describe "[article comment info]" do + test "author of the article comment a comment should have flag", ~m(user works)a do + {:ok, comment} = CMS.create_comment(:works, works.id, mock_comment(), user) + assert not comment.is_article_author + + author_user = works.author.user + {:ok, comment} = CMS.create_comment(:works, works.id, mock_comment(), author_user) + assert comment.is_article_author + end + end + + describe "[lock/unlock works comment]" do + test "locked works can not be comment", ~m(user works)a do + {:ok, _} = CMS.create_comment(:works, works.id, mock_comment(), user) + {:ok, _} = CMS.lock_article_comments(:works, works.id) + + {:error, reason} = CMS.create_comment(:works, works.id, mock_comment(), user) + assert reason |> is_error?(:article_comments_locked) + + {:ok, _} = CMS.undo_lock_article_comments(:works, works.id) + {:ok, _} = CMS.create_comment(:works, works.id, mock_comment(), user) + end + + test "locked works can not by reply", ~m(user works)a do + {:ok, parent_comment} = CMS.create_comment(:works, works.id, mock_comment(), user) + {:ok, _} = CMS.reply_comment(parent_comment.id, mock_comment(), user) + + {:ok, _} = CMS.lock_article_comments(:works, works.id) + + {:error, reason} = CMS.reply_comment(parent_comment.id, mock_comment(), user) + assert reason |> is_error?(:article_comments_locked) + + {:ok, _} = CMS.undo_lock_article_comments(:works, works.id) + {:ok, _} = CMS.reply_comment(parent_comment.id, mock_comment(), user) + end + end +end diff --git a/test/groupher_server/cms/emotions/works_emotions_test.exs b/test/groupher_server/cms/emotions/works_emotions_test.exs new file mode 100644 index 000000000..6d3e18df6 --- /dev/null +++ b/test/groupher_server/cms/emotions/works_emotions_test.exs @@ -0,0 +1,178 @@ +defmodule GroupherServer.Test.CMS.Emotions.WorksEmotions do + @moduledoc false + + use GroupherServer.TestTools + + alias Helper.ORM + alias GroupherServer.CMS + + alias CMS.Model.{Works, Embeds, ArticleUserEmotion} + + @default_emotions Embeds.ArticleEmotion.default_emotions() + + setup do + {:ok, user} = db_insert(:user) + {:ok, community} = db_insert(:community) + {:ok, user2} = db_insert(:user) + {:ok, user3} = db_insert(:user) + + works_attrs = mock_attrs(:works, %{community_id: community.id}) + + {:ok, ~m(user user2 user3 community works_attrs)a} + end + + describe "[emotion in paged workss]" do + test "login user should got viewer has emotioned status", + ~m(community works_attrs user)a do + total_count = 10 + page_number = 10 + page_size = 20 + + all_workss = + Enum.reduce(0..total_count, [], fn _, acc -> + {:ok, works} = CMS.create_article(community, :works, works_attrs, user) + acc ++ [works] + end) + + random_works = all_workss |> Enum.at(3) + + {:ok, _} = CMS.emotion_to_article(:works, random_works.id, :downvote, user) + {:ok, _} = CMS.emotion_to_article(:works, random_works.id, :beer, user) + {:ok, _} = CMS.emotion_to_article(:works, random_works.id, :popcorn, user) + + {:ok, paged_articles} = + CMS.paged_articles(:works, %{page: page_number, size: page_size}, user) + + target = Enum.find(paged_articles.entries, &(&1.id == random_works.id)) + + assert target.emotions.downvote_count == 1 + assert user_exist_in?(user, target.emotions.latest_downvote_users) + assert target.emotions.viewer_has_downvoteed + + assert target.emotions.beer_count == 1 + assert user_exist_in?(user, target.emotions.latest_beer_users) + assert target.emotions.viewer_has_beered + + assert target.emotions.popcorn_count == 1 + assert user_exist_in?(user, target.emotions.latest_popcorn_users) + assert target.emotions.viewer_has_popcorned + end + end + + describe "[basic article emotion]" do + test "works has default emotions after created", ~m(community works_attrs user)a do + {:ok, works} = CMS.create_article(community, :works, works_attrs, user) + + emotions = works.emotions |> Map.from_struct() |> Map.delete(:id) + assert @default_emotions == emotions + end + + test "can make emotion to works", ~m(community works_attrs user user2)a do + {:ok, works} = CMS.create_article(community, :works, works_attrs, user) + + {:ok, _} = CMS.emotion_to_article(:works, works.id, :downvote, user) + {:ok, _} = CMS.emotion_to_article(:works, works.id, :downvote, user2) + + {:ok, %{emotions: emotions}} = ORM.find(Works, works.id) + + assert emotions.downvote_count == 2 + assert user_exist_in?(user, emotions.latest_downvote_users) + assert user_exist_in?(user2, emotions.latest_downvote_users) + end + + test "can undo emotion to works", ~m(community works_attrs user user2)a do + {:ok, works} = CMS.create_article(community, :works, works_attrs, user) + + {:ok, _} = CMS.emotion_to_article(:works, works.id, :downvote, user) + {:ok, _} = CMS.emotion_to_article(:works, works.id, :downvote, user2) + + {:ok, _} = CMS.undo_emotion_to_article(:works, works.id, :downvote, user) + {:ok, _} = CMS.undo_emotion_to_article(:works, works.id, :downvote, user2) + + {:ok, %{emotions: emotions}} = ORM.find(Works, works.id) + + assert emotions.downvote_count == 0 + assert not user_exist_in?(user, emotions.latest_downvote_users) + assert not user_exist_in?(user2, emotions.latest_downvote_users) + end + + test "same user make same emotion to same works.", ~m(community works_attrs user)a do + {:ok, works} = CMS.create_article(community, :works, works_attrs, user) + + {:ok, _} = CMS.emotion_to_article(:works, works.id, :downvote, user) + {:ok, _} = CMS.emotion_to_article(:works, works.id, :downvote, user) + + {:ok, works} = ORM.find(Works, works.id) + + assert works.emotions.downvote_count == 1 + assert user_exist_in?(user, works.emotions.latest_downvote_users) + end + + test "same user same emotion to same works only have one user_emotion record", + ~m(community works_attrs user)a do + {:ok, works} = CMS.create_article(community, :works, works_attrs, user) + + {:ok, _} = CMS.emotion_to_article(:works, works.id, :downvote, user) + {:ok, _} = CMS.emotion_to_article(:works, works.id, :heart, user) + + {:ok, works} = ORM.find(Works, works.id) + + {:ok, records} = ORM.find_all(ArticleUserEmotion, %{page: 1, size: 10}) + assert records.total_count == 1 + + {:ok, record} = ORM.find_by(ArticleUserEmotion, %{works_id: works.id, user_id: user.id}) + assert record.downvote + assert record.heart + end + + test "different user can make same emotions on same works", + ~m(community works_attrs user user2 user3)a do + {:ok, works} = CMS.create_article(community, :works, works_attrs, user) + + {:ok, _} = CMS.emotion_to_article(:works, works.id, :beer, user) + {:ok, _} = CMS.emotion_to_article(:works, works.id, :beer, user2) + {:ok, _} = CMS.emotion_to_article(:works, works.id, :beer, user3) + + {:ok, %{emotions: emotions}} = ORM.find(Works, works.id) + + assert emotions.beer_count == 3 + assert user_exist_in?(user, emotions.latest_beer_users) + assert user_exist_in?(user2, emotions.latest_beer_users) + assert user_exist_in?(user3, emotions.latest_beer_users) + end + + test "same user can make differcent emotions on same works", + ~m(community works_attrs user)a do + {:ok, works} = CMS.create_article(community, :works, works_attrs, user) + + {:ok, _} = CMS.emotion_to_article(:works, works.id, :downvote, user) + {:ok, _} = CMS.emotion_to_article(:works, works.id, :downvote, user) + {:ok, _} = CMS.emotion_to_article(:works, works.id, :beer, user) + {:ok, _} = CMS.emotion_to_article(:works, works.id, :heart, user) + {:ok, _} = CMS.emotion_to_article(:works, works.id, :orz, user) + + {:ok, %{emotions: emotions}} = ORM.find(Works, works.id) + + assert emotions.downvote_count == 1 + assert user_exist_in?(user, emotions.latest_downvote_users) + + assert emotions.beer_count == 1 + assert user_exist_in?(user, emotions.latest_beer_users) + + assert emotions.heart_count == 1 + assert user_exist_in?(user, emotions.latest_heart_users) + + assert emotions.orz_count == 1 + assert user_exist_in?(user, emotions.latest_orz_users) + + assert emotions.pill_count == 0 + assert not user_exist_in?(user, emotions.latest_pill_users) + + assert emotions.biceps_count == 0 + assert not user_exist_in?(user, emotions.latest_biceps_users) + + assert emotions.confused_count == 0 + assert not user_exist_in?(user, emotions.latest_confused_users) + end + end +end diff --git a/test/groupher_server/cms/hooks/cite_works_test.exs b/test/groupher_server/cms/hooks/cite_works_test.exs new file mode 100644 index 000000000..16fad510b --- /dev/null +++ b/test/groupher_server/cms/hooks/cite_works_test.exs @@ -0,0 +1,230 @@ +defmodule GroupherServer.Test.CMS.Hooks.CiteWorks do + use GroupherServer.TestTools + + import Helper.Utils, only: [get_config: 2] + + alias Helper.ORM + alias GroupherServer.CMS + + alias CMS.Model.{Works, Comment, CitedArtiment} + alias CMS.Delegate.Hooks + + @site_host get_config(:general, :site_host) + + setup do + {:ok, user} = db_insert(:user) + {:ok, user2} = db_insert(:user) + {:ok, works} = db_insert(:works) + {:ok, works2} = db_insert(:works) + {:ok, works3} = db_insert(:works) + {:ok, works4} = db_insert(:works) + {:ok, works5} = db_insert(:works) + + {:ok, community} = db_insert(:community) + + works_attrs = mock_attrs(:works, %{community_id: community.id}) + + {:ok, ~m(user user2 community works works2 works3 works4 works5 works_attrs)a} + end + + describe "[cite basic]" do + test "cited multi works should work", + ~m(user community works2 works3 works4 works5 works_attrs)a do + body = + mock_rich_text( + ~s(the and same la is awesome, the is awesome too.), + # second paragraph + ~s(the paragraph 2 again, the paragraph 2 again, the paragraph 2 again) + ) + + works_attrs = works_attrs |> Map.merge(%{body: body}) + {:ok, works} = CMS.create_article(community, :works, works_attrs, user) + + body = mock_rich_text(~s(the )) + works_attrs = works_attrs |> Map.merge(%{body: body}) + {:ok, works_n} = CMS.create_article(community, :works, works_attrs, user) + + Hooks.Cite.handle(works) + Hooks.Cite.handle(works_n) + + {:ok, works2} = ORM.find(Works, works2.id) + {:ok, works3} = ORM.find(Works, works3.id) + {:ok, works4} = ORM.find(Works, works4.id) + {:ok, works5} = ORM.find(Works, works5.id) + + assert works2.meta.citing_count == 1 + assert works3.meta.citing_count == 2 + assert works4.meta.citing_count == 1 + assert works5.meta.citing_count == 1 + end + + test "cited works itself should not work", ~m(user community works_attrs)a do + {:ok, works} = CMS.create_article(community, :works, works_attrs, user) + + body = mock_rich_text(~s(the )) + {:ok, works} = CMS.update_article(works, %{body: body}) + + Hooks.Cite.handle(works) + + {:ok, works} = ORM.find(Works, works.id) + assert works.meta.citing_count == 0 + end + + test "cited comment itself should not work", ~m(user works)a do + {:ok, cited_comment} = CMS.create_comment(:works, works.id, mock_rich_text("hello"), user) + + {:ok, comment} = + CMS.update_comment( + cited_comment, + mock_comment( + ~s(the ) + ) + ) + + Hooks.Cite.handle(comment) + + {:ok, cited_comment} = ORM.find(Comment, cited_comment.id) + assert cited_comment.meta.citing_count == 0 + end + + test "can cite works's comment in works", ~m(community user works works2 works_attrs)a do + {:ok, comment} = CMS.create_comment(:works, works.id, mock_rich_text("hello"), user) + + body = + mock_rich_text( + ~s(the ) + ) + + works_attrs = works_attrs |> Map.merge(%{body: body}) + + {:ok, works} = CMS.create_article(community, :works, works_attrs, user) + Hooks.Cite.handle(works) + + {:ok, comment} = ORM.find(Comment, comment.id) + assert comment.meta.citing_count == 1 + + {:ok, cited_content} = ORM.find_by(CitedArtiment, %{cited_by_id: comment.id}) + + # 被 works 以 comment link 的方式引用了 + assert cited_content.works_id == works.id + assert cited_content.cited_by_type == "COMMENT" + end + + test "can cite a comment in a comment", ~m(user works)a do + {:ok, cited_comment} = CMS.create_comment(:works, works.id, mock_rich_text("hello"), user) + + comment_body = + mock_rich_text( + ~s(the ) + ) + + {:ok, comment} = CMS.create_comment(:works, works.id, comment_body, user) + + Hooks.Cite.handle(comment) + + {:ok, cited_comment} = ORM.find(Comment, cited_comment.id) + assert cited_comment.meta.citing_count == 1 + + {:ok, cited_content} = ORM.find_by(CitedArtiment, %{cited_by_id: cited_comment.id}) + assert comment.id == cited_content.comment_id + assert cited_comment.id == cited_content.cited_by_id + assert cited_content.cited_by_type == "COMMENT" + end + + test "can cited works inside a comment", ~m(user works works2 works3 works4 works5)a do + comment_body = + mock_rich_text( + ~s(the and same la is awesome, the is awesome too.), + # second paragraph + ~s(the paragraph 2 again, the paragraph 2 again, the paragraph 2 again) + ) + + {:ok, comment} = CMS.create_comment(:works, works.id, comment_body, user) + Hooks.Cite.handle(comment) + + comment_body = mock_rich_text(~s(the )) + {:ok, comment} = CMS.create_comment(:works, works.id, comment_body, user) + + Hooks.Cite.handle(comment) + + {:ok, works2} = ORM.find(Works, works2.id) + {:ok, works3} = ORM.find(Works, works3.id) + {:ok, works4} = ORM.find(Works, works4.id) + {:ok, works5} = ORM.find(Works, works5.id) + + assert works2.meta.citing_count == 1 + assert works3.meta.citing_count == 2 + assert works4.meta.citing_count == 1 + assert works5.meta.citing_count == 1 + end + end + + describe "[cite pagi]" do + test "can get paged cited articles.", ~m(user community works2 works_attrs)a do + {:ok, comment} = + CMS.create_comment( + :works, + works2.id, + mock_comment(~s(the )), + user + ) + + Process.sleep(1000) + + body = + mock_rich_text( + ~s(the ), + ~s(the ) + ) + + works_attrs = works_attrs |> Map.merge(%{body: body}) + {:ok, works_x} = CMS.create_article(community, :works, works_attrs, user) + + Process.sleep(1000) + body = mock_rich_text(~s(the )) + works_attrs = works_attrs |> Map.merge(%{body: body}) + {:ok, works_y} = CMS.create_article(community, :works, works_attrs, user) + + Hooks.Cite.handle(works_x) + Hooks.Cite.handle(comment) + Hooks.Cite.handle(works_y) + + {:ok, result} = CMS.paged_citing_contents("WORKS", works2.id, %{page: 1, size: 10}) + + entries = result.entries + + result_comment = entries |> List.first() + result_works_x = entries |> Enum.at(1) + result_works_y = entries |> List.last() + + article_map_keys = [:block_linker, :id, :inserted_at, :thread, :title, :user] + + assert result_comment.comment_id == comment.id + assert result_comment.id == works2.id + assert result_comment.title == works2.title + + assert result_works_x.id == works_x.id + assert result_works_x.block_linker |> length == 2 + assert result_works_x |> Map.keys() == article_map_keys + + assert result_works_y.id == works_y.id + assert result_works_y.block_linker |> length == 1 + assert result_works_y |> Map.keys() == article_map_keys + + assert result |> is_valid_pagination?(:raw) + assert result.total_count == 3 + end + end +end diff --git a/test/groupher_server/cms/hooks/mention_in_works_test.exs b/test/groupher_server/cms/hooks/mention_in_works_test.exs new file mode 100644 index 000000000..b84394715 --- /dev/null +++ b/test/groupher_server/cms/hooks/mention_in_works_test.exs @@ -0,0 +1,98 @@ +defmodule GroupherServer.Test.CMS.Hooks.MentionInWorks do + use GroupherServer.TestTools + + import GroupherServer.CMS.Delegate.Helper, only: [preload_author: 1] + + alias GroupherServer.{CMS, Delivery} + alias CMS.Delegate.Hooks + + @article_mention_class "cdx-mention" + + setup do + {:ok, user} = db_insert(:user) + {:ok, user2} = db_insert(:user) + {:ok, user3} = db_insert(:user) + {:ok, works} = db_insert(:works) + + {:ok, community} = db_insert(:community) + + works_attrs = mock_attrs(:works, %{community_id: community.id}) + + {:ok, ~m(user user2 user3 community works works_attrs)a} + end + + describe "[mention in works basic]" do + test "mention multi user in works should work", + ~m(user user2 user3 community works_attrs)a do + body = + mock_rich_text( + ~s(hi
#{user2.login}
, and
#{user3.login}
), + ~s(hi
#{user2.login}
) + ) + + works_attrs = works_attrs |> Map.merge(%{body: body}) + {:ok, works} = CMS.create_article(community, :works, works_attrs, user) + {:ok, works} = preload_author(works) + + {:ok, _result} = Hooks.Mention.handle(works) + + {:ok, result} = Delivery.fetch(:mention, user2, %{page: 1, size: 10}) + + mention = result.entries |> List.first() + assert mention.thread == "WORKS" + assert mention.block_linker |> length == 2 + assert mention.article_id == works.id + assert mention.title == works.title + assert mention.user.login == works.author.user.login + + {:ok, result} = Delivery.fetch(:mention, user3, %{page: 1, size: 10}) + + mention = result.entries |> List.first() + assert mention.thread == "WORKS" + assert mention.block_linker |> length == 1 + assert mention.article_id == works.id + assert mention.title == works.title + assert mention.user.login == works.author.user.login + end + + test "mention in works's comment should work", ~m(user user2 works)a do + comment_body = + mock_rich_text(~s(hi
#{user2.login}
)) + + {:ok, comment} = CMS.create_comment(:works, works.id, comment_body, user) + {:ok, comment} = preload_author(comment) + + {:ok, _result} = Hooks.Mention.handle(comment) + {:ok, result} = Delivery.fetch(:mention, user2, %{page: 1, size: 10}) + + mention = result.entries |> List.first() + assert mention.thread == "WORKS" + assert mention.comment_id == comment.id + assert mention.block_linker |> length == 1 + assert mention.article_id == works.id + assert mention.title == works.title + assert mention.user.login == comment.author.login + end + + test "can not mention author self in works or comment", ~m(community user works_attrs)a do + body = mock_rich_text(~s(hi
#{user.login}
)) + works_attrs = works_attrs |> Map.merge(%{body: body}) + {:ok, works} = CMS.create_article(community, :works, works_attrs, user) + + {:ok, result} = Delivery.fetch(:mention, user, %{page: 1, size: 10}) + assert result.total_count == 0 + + comment_body = + mock_rich_text(~s(hi
#{user.login}
)) + + {:ok, comment} = CMS.create_comment(:works, works.id, comment_body, user) + + {:ok, _result} = Hooks.Mention.handle(comment) + {:ok, result} = Delivery.fetch(:mention, user, %{page: 1, size: 10}) + + assert result.total_count == 0 + end + end +end diff --git a/test/groupher_server/cms/hooks/notify_works_test.exs b/test/groupher_server/cms/hooks/notify_works_test.exs new file mode 100644 index 000000000..bf08535e1 --- /dev/null +++ b/test/groupher_server/cms/hooks/notify_works_test.exs @@ -0,0 +1,174 @@ +defmodule GroupherServer.Test.CMS.Hooks.NotifyWorks do + use GroupherServer.TestTools + + import GroupherServer.CMS.Delegate.Helper, only: [preload_author: 1] + + alias GroupherServer.{CMS, Delivery, Repo} + alias CMS.Delegate.Hooks + + setup do + {:ok, user} = db_insert(:user) + {:ok, user2} = db_insert(:user) + {:ok, user3} = db_insert(:user) + + {:ok, community} = db_insert(:community) + + works_attrs = mock_attrs(:works, %{community_id: community.id}) + {:ok, works} = CMS.create_article(community, :works, works_attrs, user) + {:ok, comment} = CMS.create_comment(:works, works.id, mock_comment(), user) + + {:ok, ~m(user2 user3 works comment)a} + end + + describe "[upvote notify]" do + test "upvote hook should work on works", ~m(user2 works)a do + {:ok, works} = preload_author(works) + + {:ok, article} = CMS.upvote_article(:works, works.id, user2) + Hooks.Notify.handle(:upvote, article, user2) + + {:ok, notifications} = + Delivery.fetch(:notification, works.author.user, %{page: 1, size: 20}) + + assert notifications.total_count == 1 + + notify = notifications.entries |> List.first() + assert notify.action == "UPVOTE" + assert notify.article_id == works.id + assert notify.thread == "WORKS" + assert notify.user_id == works.author.user.id + assert user_exist_in?(user2, notify.from_users) + end + + test "upvote hook should work on works comment", ~m(user2 works comment)a do + {:ok, comment} = CMS.upvote_comment(comment.id, user2) + {:ok, comment} = preload_author(comment) + + Hooks.Notify.handle(:upvote, comment, user2) + + {:ok, notifications} = Delivery.fetch(:notification, comment.author, %{page: 1, size: 20}) + + assert notifications.total_count == 1 + + notify = notifications.entries |> List.first() + assert notify.action == "UPVOTE" + assert notify.article_id == works.id + assert notify.thread == "WORKS" + assert notify.user_id == comment.author.id + assert notify.comment_id == comment.id + assert user_exist_in?(user2, notify.from_users) + end + + test "undo upvote hook should work on works", ~m(user2 works)a do + {:ok, works} = preload_author(works) + + {:ok, article} = CMS.upvote_article(:works, works.id, user2) + Hooks.Notify.handle(:upvote, article, user2) + + {:ok, article} = CMS.undo_upvote_article(:works, works.id, user2) + Hooks.Notify.handle(:undo, :upvote, article, user2) + + {:ok, notifications} = + Delivery.fetch(:notification, works.author.user, %{page: 1, size: 20}) + + assert notifications.total_count == 0 + end + + test "undo upvote hook should work on works comment", ~m(user2 comment)a do + {:ok, comment} = CMS.upvote_comment(comment.id, user2) + + Hooks.Notify.handle(:upvote, comment, user2) + + {:ok, comment} = CMS.undo_upvote_comment(comment.id, user2) + Hooks.Notify.handle(:undo, :upvote, comment, user2) + + {:ok, comment} = preload_author(comment) + + {:ok, notifications} = Delivery.fetch(:notification, comment.author, %{page: 1, size: 20}) + + assert notifications.total_count == 0 + end + end + + describe "[collect notify]" do + test "collect hook should work on works", ~m(user2 works)a do + {:ok, works} = preload_author(works) + + {:ok, _} = CMS.collect_article(:works, works.id, user2) + Hooks.Notify.handle(:collect, works, user2) + + {:ok, notifications} = + Delivery.fetch(:notification, works.author.user, %{page: 1, size: 20}) + + assert notifications.total_count == 1 + + notify = notifications.entries |> List.first() + assert notify.action == "COLLECT" + assert notify.article_id == works.id + assert notify.thread == "WORKS" + assert notify.user_id == works.author.user.id + assert user_exist_in?(user2, notify.from_users) + end + + test "undo collect hook should work on works", ~m(user2 works)a do + {:ok, works} = preload_author(works) + + {:ok, _} = CMS.upvote_article(:works, works.id, user2) + Hooks.Notify.handle(:collect, works, user2) + + {:ok, _} = CMS.undo_upvote_article(:works, works.id, user2) + Hooks.Notify.handle(:undo, :collect, works, user2) + + {:ok, notifications} = + Delivery.fetch(:notification, works.author.user, %{page: 1, size: 20}) + + assert notifications.total_count == 0 + end + end + + describe "[comment notify]" do + test "works author should get notify after some one comment on it", ~m(user2 works)a do + {:ok, works} = preload_author(works) + + {:ok, comment} = CMS.create_comment(:works, works.id, mock_comment(), user2) + Hooks.Notify.handle(:comment, comment, user2) + + {:ok, notifications} = + Delivery.fetch(:notification, works.author.user, %{page: 1, size: 20}) + + assert notifications.total_count == 1 + + notify = notifications.entries |> List.first() + assert notify.action == "COMMENT" + assert notify.thread == "WORKS" + assert notify.article_id == works.id + assert notify.user_id == works.author.user.id + assert user_exist_in?(user2, notify.from_users) + end + + test "works comment author should get notify after some one reply it", + ~m(user2 user3 works)a do + {:ok, works} = preload_author(works) + + {:ok, comment} = CMS.create_comment(:works, works.id, mock_comment(), user2) + {:ok, replyed_comment} = CMS.reply_comment(comment.id, mock_comment(), user3) + + Hooks.Notify.handle(:reply, replyed_comment, user3) + + comment = Repo.preload(comment, :author) + {:ok, notifications} = Delivery.fetch(:notification, comment.author, %{page: 1, size: 20}) + + assert notifications.total_count == 1 + + notify = notifications.entries |> List.first() + + assert notify.action == "REPLY" + assert notify.thread == "WORKS" + assert notify.article_id == works.id + assert notify.comment_id == replyed_comment.id + + assert notify.user_id == comment.author_id + assert user_exist_in?(user3, notify.from_users) + end + end +end diff --git a/test/groupher_server/cms/upvotes/works_upvote_test.exs b/test/groupher_server/cms/upvotes/works_upvote_test.exs new file mode 100644 index 000000000..ec4e34dd8 --- /dev/null +++ b/test/groupher_server/cms/upvotes/works_upvote_test.exs @@ -0,0 +1,83 @@ +defmodule GroupherServer.Test.Upvotes.WorksUpvote do + @moduledoc false + use GroupherServer.TestTools + + alias GroupherServer.CMS + + setup do + {:ok, user} = db_insert(:user) + {:ok, user2} = db_insert(:user) + {:ok, community} = db_insert(:community) + + works_attrs = mock_attrs(:works, %{community_id: community.id}) + + {:ok, ~m(user user2 community works_attrs)a} + end + + describe "[cms works upvote]" do + test "works can be upvote && upvotes_count should inc by 1", + ~m(user user2 community works_attrs)a do + {:ok, works} = CMS.create_article(community, :works, works_attrs, user) + + {:ok, article} = CMS.upvote_article(:works, works.id, user) + assert article.id == works.id + assert article.upvotes_count == 1 + + {:ok, article} = CMS.upvote_article(:works, works.id, user2) + assert article.upvotes_count == 2 + end + + test "works can be undo upvote && upvotes_count should dec by 1", + ~m(user user2 community works_attrs)a do + {:ok, works} = CMS.create_article(community, :works, works_attrs, user) + + {:ok, article} = CMS.upvote_article(:works, works.id, user) + assert article.id == works.id + assert article.upvotes_count == 1 + + {:ok, article} = CMS.undo_upvote_article(:works, works.id, user2) + assert article.upvotes_count == 0 + end + + test "can get upvotes_users", ~m(user user2 community works_attrs)a do + {:ok, works} = CMS.create_article(community, :works, works_attrs, user) + + {:ok, _article} = CMS.upvote_article(:works, works.id, user) + {:ok, _article} = CMS.upvote_article(:works, works.id, user2) + + {:ok, users} = CMS.upvoted_users(:works, works.id, %{page: 1, size: 2}) + + assert users |> is_valid_pagination?(:raw) + assert user_exist_in?(user, users.entries) + assert user_exist_in?(user2, users.entries) + end + + test "works meta history should be updated after upvote", + ~m(user user2 community works_attrs)a do + {:ok, works} = CMS.create_article(community, :works, works_attrs, user) + {:ok, article} = CMS.upvote_article(:works, works.id, user) + assert user.id in article.meta.upvoted_user_ids + + {:ok, article} = CMS.upvote_article(:works, works.id, user2) + assert user.id in article.meta.upvoted_user_ids + assert user2.id in article.meta.upvoted_user_ids + end + + test "works meta history should be updated after undo upvote", + ~m(user user2 community works_attrs)a do + {:ok, works} = CMS.create_article(community, :works, works_attrs, user) + + {:ok, _article} = CMS.upvote_article(:works, works.id, user) + {:ok, article} = CMS.upvote_article(:works, works.id, user2) + + assert user.id in article.meta.upvoted_user_ids + assert user2.id in article.meta.upvoted_user_ids + + {:ok, article} = CMS.undo_upvote_article(:works, works.id, user2) + assert user2.id not in article.meta.upvoted_user_ids + + {:ok, article} = CMS.undo_upvote_article(:works, works.id, user) + assert user.id not in article.meta.upvoted_user_ids + end + end +end diff --git a/test/groupher_server_web/mutation/cms/abuse_reports/works_report_test.exs b/test/groupher_server_web/mutation/cms/abuse_reports/works_report_test.exs new file mode 100644 index 000000000..acc0db54c --- /dev/null +++ b/test/groupher_server_web/mutation/cms/abuse_reports/works_report_test.exs @@ -0,0 +1,62 @@ +defmodule GroupherServer.Test.Mutation.AbuseReports.WorksReport do + use GroupherServer.TestTools + + alias GroupherServer.CMS + + setup do + {:ok, user} = db_insert(:user) + {:ok, community} = db_insert(:community) + + works_attrs = mock_attrs(:works, %{community_id: community.id}) + + guest_conn = simu_conn(:guest) + user_conn = simu_conn(:user) + owner_conn = simu_conn(:user, user) + + {:ok, ~m(user_conn user guest_conn owner_conn community works_attrs)a} + end + + describe "[works report/undo_report]" do + @report_query """ + mutation($id: ID!, $reason: String!, $attr: String) { + reportWorks(id: $id, reason: $reason, attr: $attr) { + id + title + } + } + """ + + test "login user can report a works", ~m(community works_attrs user user_conn)a do + {:ok, works} = CMS.create_article(community, :works, works_attrs, user) + + variables = %{id: works.id, reason: "reason"} + article = user_conn |> mutation_result(@report_query, variables, "reportWorks") + + assert article["id"] == to_string(works.id) + end + + @undo_report_query """ + mutation($id: ID!) { + undoReportWorks(id: $id) { + id + title + } + } + """ + + test "login user can undo report a works", ~m(community works_attrs user user_conn)a do + {:ok, works} = CMS.create_article(community, :works, works_attrs, user) + + variables = %{id: works.id, reason: "reason"} + article = user_conn |> mutation_result(@report_query, variables, "reportWorks") + + assert article["id"] == to_string(works.id) + + variables = %{id: works.id} + + article = user_conn |> mutation_result(@undo_report_query, variables, "undoReportWorks") + + assert article["id"] == to_string(works.id) + end + end +end diff --git a/test/groupher_server_web/mutation/cms/article_community/works_test.exs b/test/groupher_server_web/mutation/cms/article_community/works_test.exs new file mode 100644 index 000000000..b5afea638 --- /dev/null +++ b/test/groupher_server_web/mutation/cms/article_community/works_test.exs @@ -0,0 +1,148 @@ +defmodule GroupherServer.Test.Mutation.ArticleCommunity.Works do + use GroupherServer.TestTools + + alias Helper.ORM + alias GroupherServer.CMS + alias CMS.Model.Works + + setup do + {:ok, works} = db_insert(:works) + {:ok, community} = db_insert(:community) + + guest_conn = simu_conn(:guest) + user_conn = simu_conn(:user) + owner_conn = simu_conn(:owner, works) + + {:ok, ~m(user_conn guest_conn owner_conn community works)a} + end + + describe "[mirror/unmirror/move works to/from community]" do + @mirror_article_query """ + mutation($id: ID!, $thread: Thread, $communityId: ID!) { + mirrorArticle(id: $id, thread: $thread, communityId: $communityId) { + id + } + } + """ + test "auth user can mirror a works to other community", ~m(works)a do + passport_rules = %{"works.community.mirror" => true} + rule_conn = simu_conn(:user, cms: passport_rules) + + {:ok, community} = db_insert(:community) + variables = %{id: works.id, thread: "WORKS", communityId: community.id} + rule_conn |> mutation_result(@mirror_article_query, variables, "mirrorArticle") + {:ok, found} = ORM.find(Works, works.id, preload: :communities) + + assoc_communities = found.communities |> Enum.map(& &1.id) + assert community.id in assoc_communities + end + + test "unauth user cannot mirror a works to a community", ~m(user_conn guest_conn works)a do + {:ok, community} = db_insert(:community) + variables = %{id: works.id, thread: "WORKS", communityId: community.id} + rule_conn = simu_conn(:user, cms: %{"what.ever" => true}) + + assert user_conn + |> mutation_get_error?(@mirror_article_query, variables, ecode(:passport)) + + assert guest_conn + |> mutation_get_error?(@mirror_article_query, variables, ecode(:account_login)) + + assert rule_conn + |> mutation_get_error?(@mirror_article_query, variables, ecode(:passport)) + end + + test "auth user can mirror multi works to other communities", ~m(works)a do + passport_rules = %{"works.community.mirror" => true} + rule_conn = simu_conn(:user, cms: passport_rules) + + {:ok, community} = db_insert(:community) + {:ok, community2} = db_insert(:community) + + variables = %{id: works.id, thread: "WORKS", communityId: community.id} + rule_conn |> mutation_result(@mirror_article_query, variables, "mirrorArticle") + + variables = %{id: works.id, thread: "WORKS", communityId: community2.id} + rule_conn |> mutation_result(@mirror_article_query, variables, "mirrorArticle") + + {:ok, found} = ORM.find(Works, works.id, preload: :communities) + + assoc_communities = found.communities |> Enum.map(& &1.id) + assert community.id in assoc_communities + assert community2.id in assoc_communities + end + + @unmirror_article_query """ + mutation($id: ID!, $thread: Thread, $communityId: ID!) { + unmirrorArticle(id: $id, thread: $thread, communityId: $communityId) { + id + } + } + """ + + test "auth user can unmirror works to a community", ~m(works)a do + passport_rules = %{"works.community.mirror" => true} + rule_conn = simu_conn(:user, cms: passport_rules) + + {:ok, community} = db_insert(:community) + {:ok, community2} = db_insert(:community) + + variables = %{id: works.id, thread: "WORKS", communityId: community.id} + rule_conn |> mutation_result(@mirror_article_query, variables, "mirrorArticle") + + variables2 = %{id: works.id, thread: "WORKS", communityId: community2.id} + rule_conn |> mutation_result(@mirror_article_query, variables2, "mirrorArticle") + + {:ok, found} = ORM.find(Works, works.id, preload: :communities) + + assoc_communities = found.communities |> Enum.map(& &1.id) + assert community.id in assoc_communities + assert community2.id in assoc_communities + + passport_rules = %{"works.community.unmirror" => true} + rule_conn = simu_conn(:user, cms: passport_rules) + + rule_conn |> mutation_result(@unmirror_article_query, variables, "unmirrorArticle") + {:ok, found} = ORM.find(Works, works.id, preload: :communities) + assoc_communities = found.communities |> Enum.map(& &1.id) + assert community.id not in assoc_communities + assert community2.id in assoc_communities + end + + @move_article_query """ + mutation($id: ID!, $thread: Thread, $communityId: ID!) { + moveArticle(id: $id, thread: $thread, communityId: $communityId) { + id + } + } + """ + test "auth user can move works to other community", ~m(works)a do + passport_rules = %{"works.community.mirror" => true} + rule_conn = simu_conn(:user, cms: passport_rules) + + {:ok, community} = db_insert(:community) + {:ok, community2} = db_insert(:community) + + variables = %{id: works.id, thread: "WORKS", communityId: community.id} + rule_conn |> mutation_result(@mirror_article_query, variables, "mirrorArticle") + {:ok, found} = ORM.find(Works, works.id, preload: [:original_community, :communities]) + assoc_communities = found.communities |> Enum.map(& &1.id) + assert community.id in assoc_communities + + passport_rules = %{"works.community.move" => true} + rule_conn = simu_conn(:user, cms: passport_rules) + + pre_original_community_id = found.original_community.id + + variables = %{id: works.id, thread: "WORKS", communityId: community2.id} + rule_conn |> mutation_result(@move_article_query, variables, "moveArticle") + {:ok, found} = ORM.find(Works, works.id, preload: [:original_community, :communities]) + assoc_communities = found.communities |> Enum.map(& &1.id) + assert pre_original_community_id not in assoc_communities + assert community2.id in assoc_communities + assert community2.id == found.original_community_id + + assert found.original_community.id == community2.id + end + end +end diff --git a/test/groupher_server_web/mutation/cms/article_tags/works_tag_test.exs b/test/groupher_server_web/mutation/cms/article_tags/works_tag_test.exs new file mode 100644 index 000000000..7852686fe --- /dev/null +++ b/test/groupher_server_web/mutation/cms/article_tags/works_tag_test.exs @@ -0,0 +1,91 @@ +defmodule GroupherServer.Test.Mutation.ArticleTags.WorksTag do + @moduledoc false + + use GroupherServer.TestTools + + alias Helper.ORM + alias GroupherServer.CMS + alias CMS.Model.Works + + setup do + {:ok, works} = db_insert(:works) + {:ok, user} = db_insert(:user) + {:ok, community} = db_insert(:community) + + guest_conn = simu_conn(:guest) + user_conn = simu_conn(:user) + owner_conn = simu_conn(:owner, works) + + article_tag_attrs = mock_attrs(:article_tag) + article_tag_attrs2 = mock_attrs(:article_tag) + + {:ok, + ~m(user_conn guest_conn owner_conn community works article_tag_attrs article_tag_attrs2 user)a} + end + + describe "[mutation works tag]" do + @set_tag_query """ + mutation($id: ID!, $thread: Thread, $articleTagId: ID!, $communityId: ID!) { + setArticleTag(id: $id, thread: $thread, articleTagId: $articleTagId, communityId: $communityId) { + id + } + } + """ + + test "auth user can set a valid tag to works", ~m(community works article_tag_attrs user)a do + {:ok, article_tag} = CMS.create_article_tag(community, :works, article_tag_attrs, user) + + passport_rules = %{community.title => %{"works.article_tag.set" => true}} + rule_conn = simu_conn(:user, cms: passport_rules) + + variables = %{ + id: works.id, + thread: "WORKS", + articleTagId: article_tag.id, + communityId: community.id + } + + rule_conn |> mutation_result(@set_tag_query, variables, "setArticleTag") + {:ok, found} = ORM.find(Works, works.id, preload: :article_tags) + + assoc_tags = found.article_tags |> Enum.map(& &1.id) + assert article_tag.id in assoc_tags + end + + @unset_tag_query """ + mutation($id: ID!, $thread: Thread, $articleTagId: ID!, $communityId: ID!) { + unsetArticleTag(id: $id, thread: $thread, articleTagId: $articleTagId, communityId: $communityId) { + id + title + } + } + """ + + test "can unset tag to a works", + ~m(community works article_tag_attrs article_tag_attrs2 user)a do + {:ok, article_tag} = CMS.create_article_tag(community, :works, article_tag_attrs, user) + {:ok, article_tag2} = CMS.create_article_tag(community, :works, article_tag_attrs2, user) + + {:ok, _} = CMS.set_article_tag(:works, works.id, article_tag.id) + {:ok, _} = CMS.set_article_tag(:works, works.id, article_tag2.id) + + passport_rules = %{community.title => %{"works.article_tag.unset" => true}} + rule_conn = simu_conn(:user, cms: passport_rules) + + variables = %{ + id: works.id, + thread: "WORKS", + articleTagId: article_tag.id, + communityId: community.id + } + + rule_conn |> mutation_result(@unset_tag_query, variables, "unsetArticleTag") + + {:ok, works} = ORM.find(Works, works.id, preload: :article_tags) + assoc_tags = works.article_tags |> Enum.map(& &1.id) + + assert article_tag.id not in assoc_tags + assert article_tag2.id in assoc_tags + end + end +end diff --git a/test/groupher_server_web/mutation/cms/articles/works_test.exs b/test/groupher_server_web/mutation/cms/articles/works_test.exs new file mode 100644 index 000000000..e3ecdc48d --- /dev/null +++ b/test/groupher_server_web/mutation/cms/articles/works_test.exs @@ -0,0 +1,227 @@ +defmodule GroupherServer.Test.Mutation.Articles.Works do + use GroupherServer.TestTools + + alias Helper.ORM + alias GroupherServer.{CMS, Repo} + + alias CMS.Model.Works + + setup do + {:ok, user} = db_insert(:user) + {:ok, community} = db_insert(:community) + + works_attrs = mock_attrs(:works, %{community_id: community.id}) + {:ok, works} = CMS.create_article(community, :works, works_attrs, user) + + guest_conn = simu_conn(:guest) + user_conn = simu_conn(:user) + owner_conn = simu_conn(:owner, works) + + {:ok, ~m(user_conn guest_conn owner_conn community user works)a} + end + + describe "[mutation works curd]" do + @create_works_query """ + mutation ( + $title: String!, + $body: String, + $digest: String!, + $length: Int, + $communityId: ID!, + $articleTags: [Id] + ) { + createWorks( + title: $title, + body: $body, + digest: $digest, + length: $length, + communityId: $communityId, + articleTags: $articleTags + ) { + id + title + document { + bodyHtml + } + originalCommunity { + id + } + communities { + id + title + } + } + } + """ + test "create works with valid attrs and make sure author exsit" do + {:ok, user} = db_insert(:user) + user_conn = simu_conn(:user, user) + + {:ok, community} = db_insert(:community) + works_attr = mock_attrs(:works) + + variables = works_attr |> Map.merge(%{communityId: community.id}) |> camelize_map_key + + created = user_conn |> mutation_result(@create_works_query, variables, "createWorks") + + {:ok, found} = ORM.find(Works, created["id"]) + + assert created["id"] == to_string(found.id) + assert created["originalCommunity"]["id"] == to_string(community.id) + + assert created["id"] == to_string(found.id) + end + + test "create works with valid tags id list", ~m(user_conn user community)a do + article_tag_attrs = mock_attrs(:article_tag) + {:ok, article_tag} = CMS.create_article_tag(community, :works, article_tag_attrs, user) + + works_attr = mock_attrs(:works) + + variables = + works_attr |> Map.merge(%{communityId: community.id, articleTags: [article_tag.id]}) + + created = user_conn |> mutation_result(@create_works_query, variables, "createWorks") + {:ok, works} = ORM.find(Works, created["id"], preload: :article_tags) + + assert exist_in?(%{id: article_tag.id}, works.article_tags) + end + + test "create works should excape xss attracts" do + {:ok, user} = db_insert(:user) + user_conn = simu_conn(:user, user) + + {:ok, community} = db_insert(:community) + + works_attr = mock_attrs(:works, %{body: mock_xss_string()}) + variables = works_attr |> Map.merge(%{communityId: community.id}) |> camelize_map_key + result = user_conn |> mutation_result(@create_works_query, variables, "createWorks") + + {:ok, works} = ORM.find(Works, result["id"], preload: :document) + body_html = works |> get_in([:document, :body_html]) + + assert not String.contains?(body_html, "script") + end + + test "create works should excape xss attracts 2" do + {:ok, user} = db_insert(:user) + user_conn = simu_conn(:user, user) + + {:ok, community} = db_insert(:community) + + works_attr = mock_attrs(:works, %{body: mock_xss_string(:safe)}) + variables = works_attr |> Map.merge(%{communityId: community.id}) |> camelize_map_key + result = user_conn |> mutation_result(@create_works_query, variables, "createWorks") + {:ok, works} = ORM.find(Works, result["id"], preload: :document) + body_html = works |> get_in([:document, :body_html]) + + assert String.contains?(body_html, "<script>blackmail</script>") + end + + @query """ + mutation($id: ID!, $title: String, $body: String, $articleTags: [Ids]){ + updateWorks(id: $id, title: $title, body: $body, articleTags: $articleTags) { + id + title + document { + bodyHtml + } + articleTags { + id + } + } + } + """ + test "update a works without login user fails", ~m(guest_conn works)a do + unique_num = System.unique_integer([:positive, :monotonic]) + + variables = %{ + id: works.id, + title: "updated title #{unique_num}", + body: mock_rich_text("updated body #{unique_num}") + } + + assert guest_conn |> mutation_get_error?(@query, variables, ecode(:account_login)) + end + + test "works can be update by owner", ~m(owner_conn works)a do + unique_num = System.unique_integer([:positive, :monotonic]) + + variables = %{ + id: works.id, + title: "updated title #{unique_num}", + body: mock_rich_text("updated body #{unique_num}") + } + + result = owner_conn |> mutation_result(@query, variables, "updateWorks") + + assert result["title"] == variables.title + + assert result + |> get_in(["document", "bodyHtml"]) + |> String.contains?(~s(updated body #{unique_num})) + end + + test "login user with auth passport update a works", ~m(works)a do + works = works |> Repo.preload(:communities) + + works_communities_0 = works.communities |> List.first() |> Map.get(:title) + passport_rules = %{works_communities_0 => %{"works.edit" => true}} + rule_conn = simu_conn(:user, cms: passport_rules) + + unique_num = System.unique_integer([:positive, :monotonic]) + + variables = %{ + id: works.id, + title: "updated title #{unique_num}", + body: mock_rich_text("updated body #{unique_num}") + } + + updated = rule_conn |> mutation_result(@query, variables, "updateWorks") + + assert updated["id"] == to_string(works.id) + end + + test "unauth user update works fails", ~m(user_conn guest_conn works)a do + unique_num = System.unique_integer([:positive, :monotonic]) + + variables = %{ + id: works.id, + title: "updated title #{unique_num}", + body: mock_rich_text("updated body #{unique_num}") + } + + rule_conn = simu_conn(:user, cms: %{"what.ever" => true}) + + assert user_conn |> mutation_get_error?(@query, variables, ecode(:passport)) + assert guest_conn |> mutation_get_error?(@query, variables, ecode(:account_login)) + assert rule_conn |> mutation_get_error?(@query, variables, ecode(:passport)) + end + + @query """ + mutation($id: ID!){ + deleteWorks(id: $id) { + id + } + } + """ + + test "can delete a works by works's owner", ~m(owner_conn works)a do + deleted = owner_conn |> mutation_result(@query, %{id: works.id}, "deleteWorks") + + assert deleted["id"] == to_string(works.id) + assert {:error, _} = ORM.find(Works, deleted["id"]) + end + + test "can delete a works by auth user", ~m(works)a do + works = works |> Repo.preload(:communities) + belongs_community_title = works.communities |> List.first() |> Map.get(:title) + rule_conn = simu_conn(:user, cms: %{belongs_community_title => %{"works.delete" => true}}) + + deleted = rule_conn |> mutation_result(@query, %{id: works.id}, "deleteWorks") + + assert deleted["id"] == to_string(works.id) + assert {:error, _} = ORM.find(Works, deleted["id"]) + end + end +end diff --git a/test/groupher_server_web/mutation/cms/comments/works_comment_test.exs b/test/groupher_server_web/mutation/cms/comments/works_comment_test.exs new file mode 100644 index 000000000..dc8bf944c --- /dev/null +++ b/test/groupher_server_web/mutation/cms/comments/works_comment_test.exs @@ -0,0 +1,329 @@ +defmodule GroupherServer.Test.Mutation.Comments.WorksComment do + use GroupherServer.TestTools + + alias GroupherServer.CMS + alias CMS.Model.Works + + alias Helper.ORM + + setup do + {:ok, user} = db_insert(:user) + {:ok, community} = db_insert(:community) + {:ok, works} = CMS.create_article(community, :works, mock_attrs(:works), user) + + guest_conn = simu_conn(:guest) + user_conn = simu_conn(:user) + owner_conn = simu_conn(:user, user) + + {:ok, ~m(user_conn user guest_conn owner_conn community works)a} + end + + describe "[article comment CURD]" do + @write_comment_query """ + mutation($thread: Thread!, $id: ID!, $body: String!) { + createComment(thread: $thread,id: $id, body: $body) { + id + bodyHtml + } + } + """ + test "write article comment to a exsit works", ~m(works user_conn)a do + variables = %{thread: "WORKS", id: works.id, body: mock_comment()} + + result = user_conn |> mutation_result(@write_comment_query, variables, "createComment") + + assert result["bodyHtml"] |> String.contains?(~s(

String.contains?(~s(comment

)) + end + + @reply_comment_query """ + mutation($id: ID!, $body: String!) { + replyComment(id: $id, body: $body) { + id + bodyHtml + } + } + """ + test "login user can reply to a comment", ~m(works user user_conn)a do + {:ok, comment} = CMS.create_comment(:works, works.id, mock_comment(), user) + variables = %{id: comment.id, body: mock_comment("reply comment")} + + result = user_conn |> mutation_result(@reply_comment_query, variables, "replyComment") + + assert result["bodyHtml"] |> String.contains?(~s(

String.contains?(~s(reply comment

)) + end + + @update_comment_query """ + mutation($id: ID!, $body: String!) { + updateComment(id: $id, body: $body) { + id + bodyHtml + } + } + """ + + test "only owner can update a exsit comment", + ~m(works user guest_conn user_conn owner_conn)a do + {:ok, comment} = CMS.create_comment(:works, works.id, mock_comment(), user) + variables = %{id: comment.id, body: mock_comment("updated comment")} + + assert user_conn |> mutation_get_error?(@update_comment_query, variables, ecode(:passport)) + + assert guest_conn + |> mutation_get_error?(@update_comment_query, variables, ecode(:account_login)) + + result = owner_conn |> mutation_result(@update_comment_query, variables, "updateComment") + + assert result["bodyHtml"] |> String.contains?(~s(

String.contains?(~s(updated comment

)) + end + + @delete_comment_query """ + mutation($id: ID!) { + deleteComment(id: $id) { + id + isDeleted + } + } + """ + test "only owner can delete a exsit comment", + ~m(works user guest_conn user_conn owner_conn)a do + {:ok, comment} = CMS.create_comment(:works, works.id, mock_comment(), user) + variables = %{id: comment.id} + + assert user_conn |> mutation_get_error?(@delete_comment_query, variables, ecode(:passport)) + + assert guest_conn + |> mutation_get_error?(@delete_comment_query, variables, ecode(:account_login)) + + deleted = owner_conn |> mutation_result(@delete_comment_query, variables, "deleteComment") + + assert deleted["id"] == to_string(comment.id) + assert deleted["isDeleted"] + end + end + + describe "[article comment upvote]" do + @upvote_comment_query """ + mutation($id: ID!) { + upvoteComment(id: $id) { + id + upvotesCount + viewerHasUpvoted + } + } + """ + + test "login user can upvote a exsit works comment", ~m(works user guest_conn user_conn)a do + {:ok, comment} = CMS.create_comment(:works, works.id, mock_comment(), user) + variables = %{id: comment.id} + + assert guest_conn + |> mutation_get_error?(@upvote_comment_query, variables, ecode(:account_login)) + + result = user_conn |> mutation_result(@upvote_comment_query, variables, "upvoteComment") + + assert result["id"] == to_string(comment.id) + assert result["upvotesCount"] == 1 + assert result["viewerHasUpvoted"] + end + + @undo_upvote_comment_query """ + mutation($id: ID!) { + undoUpvoteComment(id: $id) { + id + upvotesCount + viewerHasUpvoted + } + } + """ + + test "login user can undo upvote a exsit works comment", + ~m(works user guest_conn user_conn)a do + {:ok, comment} = CMS.create_comment(:works, works.id, mock_comment(), user) + variables = %{id: comment.id} + user_conn |> mutation_result(@upvote_comment_query, variables, "upvoteComment") + + assert guest_conn + |> mutation_get_error?(@undo_upvote_comment_query, variables, ecode(:account_login)) + + result = + user_conn + |> mutation_result(@undo_upvote_comment_query, variables, "undoUpvoteComment") + + assert result["upvotesCount"] == 0 + assert not result["viewerHasUpvoted"] + end + end + + describe "[article comment emotion]" do + @emotion_comment_query """ + mutation($id: ID!, $emotion: CommentEmotion!) { + emotionToComment(id: $id, emotion: $emotion) { + id + emotions { + beerCount + viewerHasBeered + latestBeerUsers { + login + nickname + } + } + } + } + """ + test "login user can emotion to a comment", ~m(works user user_conn)a do + {:ok, comment} = CMS.create_comment(:works, works.id, mock_comment(), user) + variables = %{id: comment.id, emotion: "BEER"} + + comment = + user_conn |> mutation_result(@emotion_comment_query, variables, "emotionToComment") + + assert comment |> get_in(["emotions", "beerCount"]) == 1 + assert get_in(comment, ["emotions", "viewerHasBeered"]) + end + + @emotion_comment_query """ + mutation($id: ID!, $emotion: CommentEmotion!) { + undoEmotionToComment(id: $id, emotion: $emotion) { + id + emotions { + beerCount + viewerHasBeered + latestBeerUsers { + login + nickname + } + } + } + } + """ + test "login user can undo emotion to a comment", ~m(works user owner_conn)a do + {:ok, comment} = CMS.create_comment(:works, works.id, mock_comment(), user) + {:ok, _} = CMS.emotion_to_comment(comment.id, :beer, user) + + variables = %{id: comment.id, emotion: "BEER"} + + comment = + owner_conn |> mutation_result(@emotion_comment_query, variables, "undoEmotionToComment") + + assert comment |> get_in(["emotions", "beerCount"]) == 0 + assert not get_in(comment, ["emotions", "viewerHasBeered"]) + end + end + + describe "[article comment lock/unlock]" do + @query """ + mutation($id: ID!, $communityId: ID!){ + lockWorksComment(id: $id, communityId: $communityId) { + id + } + } + """ + + test "can lock a works's comment", ~m(community works)a do + variables = %{id: works.id, communityId: community.id} + passport_rules = %{community.raw => %{"works.lock_comment" => true}} + rule_conn = simu_conn(:user, cms: passport_rules) + + result = rule_conn |> mutation_result(@query, variables, "lockWorksComment") + assert result["id"] == to_string(works.id) + + {:ok, works} = ORM.find(Works, works.id) + assert works.meta.is_comment_locked + end + + test "unauth user fails", ~m(guest_conn community works)a do + variables = %{id: works.id, communityId: community.id} + + assert guest_conn |> mutation_get_error?(@query, variables, ecode(:account_login)) + end + + @query """ + mutation($id: ID!, $communityId: ID!){ + undoLockWorksComment(id: $id, communityId: $communityId) { + id + } + } + """ + + test "can undo lock a works's comment", ~m(community works)a do + {:ok, _} = CMS.lock_article_comments(:works, works.id) + {:ok, works} = ORM.find(Works, works.id) + assert works.meta.is_comment_locked + + variables = %{id: works.id, communityId: community.id} + passport_rules = %{community.raw => %{"works.undo_lock_comment" => true}} + rule_conn = simu_conn(:user, cms: passport_rules) + + result = rule_conn |> mutation_result(@query, variables, "undoLockWorksComment") + assert result["id"] == to_string(works.id) + + {:ok, works} = ORM.find(Works, works.id) + assert not works.meta.is_comment_locked + end + + test "unauth user undo fails", ~m(guest_conn community works)a do + variables = %{id: works.id, communityId: community.id} + + assert guest_conn |> mutation_get_error?(@query, variables, ecode(:account_login)) + end + end + + describe "[article comment pin/unPin]" do + @query """ + mutation($id: ID!){ + pinComment(id: $id) { + id + isPinned + } + } + """ + + test "can pin a works's comment", ~m(owner_conn works user)a do + {:ok, comment} = CMS.create_comment(:works, works.id, mock_comment(), user) + + variables = %{id: comment.id} + result = owner_conn |> mutation_result(@query, variables, "pinComment") + + assert result["id"] == to_string(comment.id) + assert result["isPinned"] + end + + test "unauth user fails.", ~m(guest_conn works user)a do + {:ok, comment} = CMS.create_comment(:works, works.id, mock_comment(), user) + variables = %{id: comment.id} + + assert guest_conn |> mutation_get_error?(@query, variables, ecode(:account_login)) + end + + @query """ + mutation($id: ID!){ + undoPinComment(id: $id) { + id + isPinned + } + } + """ + + test "can undo pin a works's comment", ~m(owner_conn works user)a do + {:ok, comment} = CMS.create_comment(:works, works.id, mock_comment(), user) + {:ok, _} = CMS.pin_comment(comment.id) + + variables = %{id: comment.id} + result = owner_conn |> mutation_result(@query, variables, "undoPinComment") + + assert result["id"] == to_string(comment.id) + assert not result["isPinned"] + end + + test "unauth user undo fails.", ~m(guest_conn works user)a do + {:ok, comment} = CMS.create_comment(:works, works.id, mock_comment(), user) + {:ok, _} = CMS.pin_comment(comment.id) + variables = %{id: comment.id} + + assert guest_conn |> mutation_get_error?(@query, variables, ecode(:account_login)) + end + end +end diff --git a/test/groupher_server_web/mutation/cms/flags/works_flag_test.exs b/test/groupher_server_web/mutation/cms/flags/works_flag_test.exs new file mode 100644 index 000000000..abb31a0bd --- /dev/null +++ b/test/groupher_server_web/mutation/cms/flags/works_flag_test.exs @@ -0,0 +1,178 @@ +defmodule GroupherServer.Test.Mutation.Flags.WorksFlag do + use GroupherServer.TestTools + + alias GroupherServer.CMS + alias CMS.Model.Community + + alias Helper.ORM + + setup do + {:ok, user} = db_insert(:user) + {:ok, community} = db_insert(:community) + + {:ok, works} = CMS.create_article(community, :works, mock_attrs(:works), user) + + guest_conn = simu_conn(:guest) + user_conn = simu_conn(:user) + owner_conn = simu_conn(:user, user) + + {:ok, ~m(user_conn guest_conn owner_conn community user works)a} + end + + describe "[mutation works flag curd]" do + @query """ + mutation($id: ID!){ + markDeleteWorks(id: $id) { + id + markDelete + } + } + """ + test "auth user can markDelete works", ~m(works)a do + variables = %{id: works.id} + + passport_rules = %{"works.mark_delete" => true} + rule_conn = simu_conn(:user, cms: passport_rules) + + updated = rule_conn |> mutation_result(@query, variables, "markDeleteWorks") + + assert updated["id"] == to_string(works.id) + assert updated["markDelete"] == true + end + + test "mark delete works should update works's communities meta count", ~m(user)a do + community_attrs = mock_attrs(:community) |> Map.merge(%{user_id: user.id}) + {:ok, community} = CMS.create_community(community_attrs) + {:ok, works} = CMS.create_article(community, :works, mock_attrs(:works), user) + + {:ok, community} = ORM.find(Community, community.id) + assert community.meta.workss_count == 1 + + variables = %{id: works.id} + passport_rules = %{"works.mark_delete" => true} + rule_conn = simu_conn(:user, cms: passport_rules) + + rule_conn |> mutation_result(@query, variables, "markDeleteWorks") + + {:ok, community} = ORM.find(Community, community.id) + assert community.meta.workss_count == 0 + end + + test "unauth user markDelete works fails", ~m(user_conn guest_conn works)a do + variables = %{id: works.id} + rule_conn = simu_conn(:user, cms: %{"what.ever" => true}) + + assert user_conn |> mutation_get_error?(@query, variables, ecode(:passport)) + assert guest_conn |> mutation_get_error?(@query, variables, ecode(:account_login)) + assert rule_conn |> mutation_get_error?(@query, variables, ecode(:passport)) + end + + @query """ + mutation($id: ID!){ + undoMarkDeleteWorks(id: $id) { + id + markDelete + } + } + """ + test "auth user can undo markDelete works", ~m(works)a do + variables = %{id: works.id} + + {:ok, _} = CMS.mark_delete_article(:works, works.id) + + passport_rules = %{"works.undo_mark_delete" => true} + rule_conn = simu_conn(:user, cms: passport_rules) + + updated = rule_conn |> mutation_result(@query, variables, "undoMarkDeleteWorks") + + assert updated["id"] == to_string(works.id) + assert updated["markDelete"] == false + end + + test "undo mark delete works should update works's communities meta count", ~m(user)a do + community_attrs = mock_attrs(:community) |> Map.merge(%{user_id: user.id}) + {:ok, community} = CMS.create_community(community_attrs) + {:ok, works} = CMS.create_article(community, :works, mock_attrs(:works), user) + + {:ok, _} = CMS.mark_delete_article(:works, works.id) + + {:ok, community} = ORM.find(Community, community.id) + assert community.meta.workss_count == 0 + + variables = %{id: works.id} + passport_rules = %{"works.undo_mark_delete" => true} + rule_conn = simu_conn(:user, cms: passport_rules) + rule_conn |> mutation_result(@query, variables, "undoMarkDeleteWorks") + + {:ok, community} = ORM.find(Community, community.id) + assert community.meta.workss_count == 1 + end + + test "unauth user undo markDelete works fails", ~m(user_conn guest_conn works)a do + variables = %{id: works.id} + rule_conn = simu_conn(:user, cms: %{"what.ever" => true}) + + assert user_conn |> mutation_get_error?(@query, variables, ecode(:passport)) + assert guest_conn |> mutation_get_error?(@query, variables, ecode(:account_login)) + assert rule_conn |> mutation_get_error?(@query, variables, ecode(:passport)) + end + + @query """ + mutation($id: ID!, $communityId: ID!){ + pinWorks(id: $id, communityId: $communityId) { + id + } + } + """ + + test "auth user can pin works", ~m(community works)a do + variables = %{id: works.id, communityId: community.id} + + passport_rules = %{community.raw => %{"works.pin" => true}} + rule_conn = simu_conn(:user, cms: passport_rules) + + updated = rule_conn |> mutation_result(@query, variables, "pinWorks") + + assert updated["id"] == to_string(works.id) + end + + test "unauth user pin works fails", ~m(user_conn guest_conn community works)a do + variables = %{id: works.id, communityId: community.id} + rule_conn = simu_conn(:user, cms: %{"what.ever" => true}) + + assert user_conn |> mutation_get_error?(@query, variables, ecode(:passport)) + assert guest_conn |> mutation_get_error?(@query, variables, ecode(:account_login)) + assert rule_conn |> mutation_get_error?(@query, variables, ecode(:passport)) + end + + @query """ + mutation($id: ID!, $communityId: ID!){ + undoPinWorks(id: $id, communityId: $communityId) { + id + isPinned + } + } + """ + + test "auth user can undo pin works", ~m(community works)a do + variables = %{id: works.id, communityId: community.id} + + passport_rules = %{community.raw => %{"works.undo_pin" => true}} + rule_conn = simu_conn(:user, cms: passport_rules) + + CMS.pin_article(:works, works.id, community.id) + updated = rule_conn |> mutation_result(@query, variables, "undoPinWorks") + + assert updated["id"] == to_string(works.id) + end + + test "unauth user undo pin works fails", ~m(user_conn guest_conn community works)a do + variables = %{id: works.id, communityId: community.id} + rule_conn = simu_conn(:user, cms: %{"what.ever" => true}) + + assert user_conn |> mutation_get_error?(@query, variables, ecode(:passport)) + assert guest_conn |> mutation_get_error?(@query, variables, ecode(:account_login)) + assert rule_conn |> mutation_get_error?(@query, variables, ecode(:passport)) + end + end +end diff --git a/test/groupher_server_web/mutation/cms/sink/works_sink_test.exs b/test/groupher_server_web/mutation/cms/sink/works_sink_test.exs new file mode 100644 index 000000000..46b9e315b --- /dev/null +++ b/test/groupher_server_web/mutation/cms/sink/works_sink_test.exs @@ -0,0 +1,79 @@ +defmodule GroupherServer.Test.Mutation.Sink.WorksSink do + @moduledoc false + use GroupherServer.TestTools + + alias GroupherServer.CMS + alias CMS.Model.Works + + alias Helper.ORM + + setup do + {:ok, user} = db_insert(:user) + {:ok, community} = db_insert(:community) + {:ok, works} = CMS.create_article(community, :works, mock_attrs(:works), user) + + guest_conn = simu_conn(:guest) + user_conn = simu_conn(:user, user) + + {:ok, ~m(user_conn guest_conn community works user)a} + end + + describe "[works sink]" do + @query """ + mutation($id: ID!, $communityId: ID!){ + sinkWorks(id: $id, communityId: $communityId) { + id + } + } + """ + + test "login user can sink a works", ~m(community works)a do + variables = %{id: works.id, communityId: community.id} + passport_rules = %{community.raw => %{"works.sink" => true}} + rule_conn = simu_conn(:user, cms: passport_rules) + + result = rule_conn |> mutation_result(@query, variables, "sinkWorks") + assert result["id"] == to_string(works.id) + + {:ok, works} = ORM.find(Works, works.id) + assert works.meta.is_sinked + assert works.active_at == works.inserted_at + end + + test "unauth user sink a works fails", ~m(guest_conn community works)a do + variables = %{id: works.id, communityId: community.id} + + assert guest_conn |> mutation_get_error?(@query, variables, ecode(:account_login)) + end + + @query """ + mutation($id: ID!, $communityId: ID!){ + undoSinkWorks(id: $id, communityId: $communityId) { + id + } + } + """ + + test "login user can undo sink to a works", ~m(community works)a do + variables = %{id: works.id, communityId: community.id} + + passport_rules = %{community.raw => %{"works.undo_sink" => true}} + rule_conn = simu_conn(:user, cms: passport_rules) + + {:ok, _} = CMS.sink_article(:works, works.id) + updated = rule_conn |> mutation_result(@query, variables, "undoSinkWorks") + assert updated["id"] == to_string(works.id) + + {:ok, works} = ORM.find(Works, works.id) + assert not works.meta.is_sinked + end + + :wip2 + + test "unauth user undo sink a works fails", ~m(guest_conn community works)a do + variables = %{id: works.id, communityId: community.id} + + assert guest_conn |> mutation_get_error?(@query, variables, ecode(:account_login)) + end + end +end diff --git a/test/groupher_server_web/mutation/cms/upvotes/works_upvote_test.exs b/test/groupher_server_web/mutation/cms/upvotes/works_upvote_test.exs new file mode 100644 index 000000000..9eee66630 --- /dev/null +++ b/test/groupher_server_web/mutation/cms/upvotes/works_upvote_test.exs @@ -0,0 +1,64 @@ +defmodule GroupherServer.Test.Mutation.Upvotes.WorksUpvote do + @moduledoc false + use GroupherServer.TestTools + + alias GroupherServer.CMS + + setup do + {:ok, works} = db_insert(:works) + {:ok, user} = db_insert(:user) + + guest_conn = simu_conn(:guest) + user_conn = simu_conn(:user, user) + + {:ok, ~m(user_conn guest_conn works user)a} + end + + describe "[works upvote]" do + @query """ + mutation($id: ID!) { + upvoteWorks(id: $id) { + id + } + } + """ + + test "login user can upvote a works", ~m(user_conn works)a do + variables = %{id: works.id} + created = user_conn |> mutation_result(@query, variables, "upvoteWorks") + + assert created["id"] == to_string(works.id) + end + + test "unauth user upvote a works fails", ~m(guest_conn works)a do + variables = %{id: works.id} + + assert guest_conn + |> mutation_get_error?(@query, variables, ecode(:account_login)) + end + + @query """ + mutation($id: ID!) { + undoUpvoteWorks(id: $id) { + id + } + } + """ + + test "login user can undo upvote to a works", ~m(user_conn works user)a do + {:ok, _} = CMS.upvote_article(:works, works.id, user) + + variables = %{id: works.id} + updated = user_conn |> mutation_result(@query, variables, "undoUpvoteWorks") + + assert updated["id"] == to_string(works.id) + end + + test "unauth user undo upvote a works fails", ~m(guest_conn works)a do + variables = %{id: works.id} + + assert guest_conn + |> mutation_get_error?(@query, variables, ecode(:account_login)) + end + end +end diff --git a/test/groupher_server_web/query/accounts/published/published_works_test.exs b/test/groupher_server_web/query/accounts/published/published_works_test.exs new file mode 100644 index 000000000..f36468840 --- /dev/null +++ b/test/groupher_server_web/query/accounts/published/published_works_test.exs @@ -0,0 +1,102 @@ +defmodule GroupherServer.Test.Query.Accounts.Published.Workss do + use GroupherServer.TestTools + + alias GroupherServer.CMS + + @publish_count 10 + + setup do + {:ok, user} = db_insert(:user) + {:ok, works} = db_insert(:works) + {:ok, community} = db_insert(:community) + + guest_conn = simu_conn(:guest) + user_conn = simu_conn(:user, user) + + {:ok, ~m(guest_conn user_conn community works user)a} + end + + describe "[published workss]" do + @query """ + query($login: String!, $filter: PagedFilter!) { + pagedPublishedWorkss(login: $login, filter: $filter) { + entries { + id + title + author { + id + } + } + totalPages + totalCount + pageSize + pageNumber + } + } + """ + + test "can get published workss", ~m(guest_conn community user)a do + works_attrs = mock_attrs(:works, %{community_id: community.id}) + + {:ok, works} = CMS.create_article(community, :works, works_attrs, user) + {:ok, works2} = CMS.create_article(community, :works, works_attrs, user) + + variables = %{login: user.login, filter: %{page: 1, size: 20}} + results = guest_conn |> query_result(@query, variables, "pagedPublishedWorkss") + + assert results["entries"] |> Enum.any?(&(&1["id"] == to_string(works.id))) + assert results["entries"] |> Enum.any?(&(&1["id"] == to_string(works2.id))) + end + end + + describe "[account published comments on works]" do + @query """ + query($login: String!, $thread: Thread, $filter: PagedFilter!) { + pagedPublishedComments(login: $login, thread: $thread, filter: $filter) { + entries { + id + bodyHtml + author { + id + } + article { + id + title + author { + nickname + login + } + } + } + totalPages + totalCount + pageSize + pageNumber + } + } + """ + test "user can get paged published comments on works", ~m(guest_conn user works)a do + pub_comments = + Enum.reduce(1..@publish_count, [], fn _, acc -> + {:ok, comment} = CMS.create_comment(:works, works.id, mock_comment(), user) + acc ++ [comment] + end) + + random_comment_id = pub_comments |> Enum.random() |> Map.get(:id) |> to_string + + variables = %{login: user.login, thread: "WORKS", filter: %{page: 1, size: 20}} + + results = guest_conn |> query_result(@query, variables, "pagedPublishedComments") + + entries = results["entries"] + assert results |> is_valid_pagination? + assert results["totalCount"] == @publish_count + + assert entries |> Enum.all?(&(not is_nil(&1["article"]["author"]))) + + assert entries |> Enum.all?(&(&1["article"]["id"] == to_string(works.id))) + assert entries |> Enum.all?(&(&1["author"]["id"] == to_string(user.id))) + assert entries |> Enum.any?(&(&1["id"] == random_comment_id)) + end + end +end diff --git a/test/groupher_server_web/query/cms/paged_articles/paged_works_test.exs b/test/groupher_server_web/query/cms/paged_articles/paged_works_test.exs new file mode 100644 index 000000000..fad7d1b80 --- /dev/null +++ b/test/groupher_server_web/query/cms/paged_articles/paged_works_test.exs @@ -0,0 +1,474 @@ +defmodule GroupherServer.Test.Query.PagedArticles.PagedWorkss do + @moduledoc false + use GroupherServer.TestTools + + import Helper.Utils, only: [get_config: 2] + import Ecto.Query, warn: false + + alias GroupherServer.CMS + alias GroupherServer.Repo + + alias CMS.Model.Works + + @page_size get_config(:general, :page_size) + + @cur_date Timex.now() + @last_week Timex.shift(Timex.beginning_of_week(@cur_date), days: -1, seconds: -1) + @last_month Timex.shift(Timex.beginning_of_month(@cur_date), days: -7, seconds: -1) + @last_year Timex.shift(Timex.beginning_of_year(@cur_date), days: -2, seconds: -1) + + @today_count 15 + + @last_week_count 1 + @last_month_count 1 + @last_year_count 1 + + @total_count @today_count + @last_week_count + @last_month_count + @last_year_count + + setup do + {:ok, user} = db_insert(:user) + + {:ok, works_last_week} = + db_insert(:works, %{title: "last week", inserted_at: @last_week, active_at: @last_week}) + + db_insert(:works, %{title: "last month", inserted_at: @last_month}) + + {:ok, works_last_year} = + db_insert(:works, %{title: "last year", inserted_at: @last_year, active_at: @last_year}) + + db_insert_multi(:works, @today_count) + guest_conn = simu_conn(:guest) + + {:ok, ~m(guest_conn user works_last_week works_last_year)a} + end + + describe "[query paged_workss filter pagination]" do + @query """ + query($filter: PagedWorkssFilter!) { + pagedWorkss(filter: $filter) { + entries { + id + document { + bodyHtml + } + communities { + id + raw + } + articleTags { + id + } + } + totalPages + totalCount + pageSize + pageNumber + } + } + """ + test "should get pagination info", ~m(guest_conn)a do + variables = %{filter: %{page: 1, size: 10}} + results = guest_conn |> query_result(@query, variables, "pagedWorkss") + + assert results |> is_valid_pagination? + assert results["pageSize"] == 10 + assert results["totalCount"] == @total_count + assert results["entries"] |> List.first() |> Map.get("articleTags") |> is_list + end + + test "should get valid thread document", ~m(guest_conn)a do + {:ok, user} = db_insert(:user) + {:ok, community} = db_insert(:community) + works_attrs = mock_attrs(:works, %{community_id: community.id}) + Process.sleep(2000) + {:ok, _works} = CMS.create_article(community, :works, works_attrs, user) + + variables = %{filter: %{page: 1, size: 30}} + results = guest_conn |> query_result(@query, variables, "pagedWorkss") + + works = results["entries"] |> List.first() + assert not is_nil(get_in(works, ["document", "bodyHtml"])) + end + + test "support article_tag filter", ~m(guest_conn user)a do + {:ok, community} = db_insert(:community) + works_attrs = mock_attrs(:works, %{community_id: community.id}) + {:ok, works} = CMS.create_article(community, :works, works_attrs, user) + + article_tag_attrs = mock_attrs(:article_tag) + {:ok, article_tag} = CMS.create_article_tag(community, :works, article_tag_attrs, user) + {:ok, _} = CMS.set_article_tag(:works, works.id, article_tag.id) + + variables = %{filter: %{page: 1, size: 10, article_tag: article_tag.title}} + results = guest_conn |> query_result(@query, variables, "pagedWorkss") + + works = results["entries"] |> List.first() + assert results["totalCount"] == 1 + assert exist_in?(article_tag, works["articleTags"], :string_key) + end + + test "support multi-tag (article_tags) filter", ~m(guest_conn user)a do + {:ok, community} = db_insert(:community) + works_attrs = mock_attrs(:works, %{community_id: community.id}) + {:ok, works} = CMS.create_article(community, :works, works_attrs, user) + + article_tag_attrs = mock_attrs(:article_tag) + + {:ok, article_tag} = CMS.create_article_tag(community, :works, article_tag_attrs, user) + {:ok, article_tag2} = CMS.create_article_tag(community, :works, article_tag_attrs, user) + {:ok, article_tag3} = CMS.create_article_tag(community, :works, article_tag_attrs, user) + + {:ok, _} = CMS.set_article_tag(:works, works.id, article_tag.id) + {:ok, _} = CMS.set_article_tag(:works, works.id, article_tag2.id) + + variables = %{ + filter: %{page: 1, size: 10, article_tags: [article_tag.title, article_tag2.title]} + } + + results = guest_conn |> query_result(@query, variables, "pagedWorkss") + + works = results["entries"] |> List.first() + assert results["totalCount"] == 1 + assert exist_in?(article_tag, works["articleTags"], :string_key) + assert exist_in?(article_tag2, works["articleTags"], :string_key) + assert not exist_in?(article_tag3, works["articleTags"], :string_key) + end + + test "should not have pined workss when filter have article_tag or article_tags", + ~m(guest_conn user)a do + {:ok, community} = db_insert(:community) + works_attrs = mock_attrs(:works, %{community_id: community.id}) + {:ok, pinned_works} = CMS.create_article(community, :works, works_attrs, user) + {:ok, works} = CMS.create_article(community, :works, works_attrs, user) + + {:ok, _} = CMS.pin_article(:works, pinned_works.id, community.id) + + article_tag_attrs = mock_attrs(:article_tag) + {:ok, article_tag} = CMS.create_article_tag(community, :works, article_tag_attrs, user) + {:ok, _} = CMS.set_article_tag(:works, works.id, article_tag.id) + + variables = %{ + filter: %{page: 1, size: 10, community: community.raw, article_tag: article_tag.title} + } + + results = guest_conn |> query_result(@query, variables, "pagedWorkss") + + assert not exist_in?(pinned_works, results["entries"], :string_key) + assert exist_in?(works, results["entries"], :string_key) + + variables = %{ + filter: %{page: 1, size: 10, community: community.raw, article_tags: [article_tag.title]} + } + + results = guest_conn |> query_result(@query, variables, "pagedWorkss") + + assert not exist_in?(pinned_works, results["entries"], :string_key) + assert exist_in?(works, results["entries"], :string_key) + end + + test "support community filter", ~m(guest_conn user)a do + {:ok, community} = db_insert(:community) + + works_attrs = mock_attrs(:works, %{community_id: community.id}) + {:ok, _works} = CMS.create_article(community, :works, works_attrs, user) + works_attrs2 = mock_attrs(:works, %{community_id: community.id}) + {:ok, _works} = CMS.create_article(community, :works, works_attrs2, user) + + variables = %{filter: %{page: 1, size: 10, community: community.raw}} + results = guest_conn |> query_result(@query, variables, "pagedWorkss") + + works = results["entries"] |> List.first() + assert results["totalCount"] == 2 + assert exist_in?(%{id: to_string(community.id)}, works["communities"], :string_key) + end + + test "request large size fails", ~m(guest_conn)a do + variables = %{filter: %{page: 1, size: 200}} + assert guest_conn |> query_get_error?(@query, variables, ecode(:pagination)) + end + + test "request 0 or neg-size fails", ~m(guest_conn)a do + variables_0 = %{filter: %{page: 1, size: 0}} + variables_neg_1 = %{filter: %{page: 1, size: -1}} + + assert guest_conn |> query_get_error?(@query, variables_0) + assert guest_conn |> query_get_error?(@query, variables_neg_1) + end + + test "pagination should have default page and size arg", ~m(guest_conn)a do + variables = %{filter: %{}} + results = guest_conn |> query_result(@query, variables, "pagedWorkss") + assert results |> is_valid_pagination? + assert results["pageSize"] == @page_size + assert results["totalCount"] == @total_count + end + end + + describe "[query paged_workss filter has_xxx]" do + @query """ + query($filter: PagedWorkssFilter!) { + pagedWorkss(filter: $filter) { + entries { + id + viewerHasCollected + viewerHasUpvoted + viewerHasViewed + viewerHasReported + } + totalCount + } + } + """ + + test "has_xxx state should work", ~m(user)a do + user_conn = simu_conn(:user, user) + {:ok, community} = db_insert(:community) + + {:ok, works} = CMS.create_article(community, :works, mock_attrs(:works), user) + {:ok, _works} = CMS.create_article(community, :works, mock_attrs(:works), user) + {:ok, _works3} = CMS.create_article(community, :works, mock_attrs(:works), user) + + variables = %{filter: %{community: community.raw}} + results = user_conn |> query_result(@query, variables, "pagedWorkss") + assert results["totalCount"] == 3 + + the_works = Enum.find(results["entries"], &(&1["id"] == to_string(works.id))) + assert not the_works["viewerHasViewed"] + assert not the_works["viewerHasUpvoted"] + assert not the_works["viewerHasCollected"] + assert not the_works["viewerHasReported"] + + {:ok, _} = CMS.read_article(:works, works.id, user) + {:ok, _} = CMS.upvote_article(:works, works.id, user) + {:ok, _} = CMS.collect_article(:works, works.id, user) + {:ok, _} = CMS.report_article(:works, works.id, "reason", "attr_info", user) + + results = user_conn |> query_result(@query, variables, "pagedWorkss") + the_works = Enum.find(results["entries"], &(&1["id"] == to_string(works.id))) + assert the_works["viewerHasViewed"] + assert the_works["viewerHasUpvoted"] + assert the_works["viewerHasCollected"] + assert the_works["viewerHasReported"] + end + end + + describe "[query paged_workss filter sort]" do + @query """ + query($filter: PagedWorkssFilter!) { + pagedWorkss(filter: $filter) { + entries { + id + inserted_at + active_at + author { + id + nickname + avatar + } + } + } + } + """ + + test "filter community should get workss which belongs to that community", + ~m(guest_conn user)a do + {:ok, community} = db_insert(:community) + {:ok, works} = CMS.create_article(community, :works, mock_attrs(:works), user) + + variables = %{filter: %{community: community.raw}} + results = guest_conn |> query_result(@query, variables, "pagedWorkss") + + assert length(results["entries"]) == 1 + assert results["entries"] |> Enum.any?(&(&1["id"] == to_string(works.id))) + end + + test "should have a active_at same with inserted_at", ~m(guest_conn user)a do + {:ok, community} = db_insert(:community) + {:ok, _works} = CMS.create_article(community, :works, mock_attrs(:works), user) + + variables = %{filter: %{community: community.raw}} + results = guest_conn |> query_result(@query, variables, "pagedWorkss") + works = results["entries"] |> List.first() + + assert works["inserted_at"] == works["active_at"] + end + + test "filter sort should have default :desc_active", ~m(guest_conn)a do + variables = %{filter: %{}} + results = guest_conn |> query_result(@query, variables, "pagedWorkss") + active_timestamps = results["entries"] |> Enum.map(& &1["active_at"]) + + {:ok, first_active_time, 0} = active_timestamps |> List.first() |> DateTime.from_iso8601() + {:ok, last_active_time, 0} = active_timestamps |> List.last() |> DateTime.from_iso8601() + + assert :gt = DateTime.compare(first_active_time, last_active_time) + end + + @query """ + query($filter: PagedWorkssFilter!) { + pagedWorkss(filter: $filter) { + entries { + id + views + } + } + } + """ + + test "filter sort MOST_VIEWS should work", ~m(guest_conn)a do + most_views_works = Works |> order_by(desc: :views) |> limit(1) |> Repo.one() + variables = %{filter: %{sort: "MOST_VIEWS"}} + + results = guest_conn |> query_result(@query, variables, "pagedWorkss") + find_works = results |> Map.get("entries") |> hd + + assert find_works["views"] == most_views_works |> Map.get(:views) + end + end + + # TODO test sort, tag, community, when ... + @doc """ + test: FILTER when [TODAY] [THIS_WEEK] [THIS_MONTH] [THIS_YEAR] + """ + describe "[query paged_workss filter when]" do + @query """ + query($filter: PagedWorkssFilter!) { + pagedWorkss(filter: $filter) { + entries { + id + views + inserted_at + } + totalCount + } + } + """ + test "THIS_YEAR option should work", ~m(guest_conn works_last_year)a do + variables = %{filter: %{when: "THIS_YEAR"}} + results = guest_conn |> query_result(@query, variables, "pagedWorkss") + + assert results["entries"] |> Enum.any?(&(&1["id"] != works_last_year.id)) + end + + test "TODAY option should work", ~m(guest_conn)a do + variables = %{filter: %{when: "TODAY"}} + results = guest_conn |> query_result(@query, variables, "pagedWorkss") + + expect_count = @total_count - @last_year_count - @last_month_count - @last_week_count + + assert results |> Map.get("totalCount") == expect_count + end + + test "THIS_WEEK option should work", ~m(guest_conn)a do + variables = %{filter: %{when: "THIS_WEEK"}} + results = guest_conn |> query_result(@query, variables, "pagedWorkss") + + assert results |> Map.get("totalCount") == @today_count + end + + test "THIS_MONTH option should work", ~m(guest_conn)a do + variables = %{filter: %{when: "THIS_MONTH"}} + results = guest_conn |> query_result(@query, variables, "pagedWorkss") + + {_, cur_week_month, _} = @cur_date |> Date.to_erl() + {_, last_week_month, _} = @last_week |> Date.to_erl() + + expect_count = + case cur_week_month == last_week_month do + true -> + @total_count - @last_year_count - @last_month_count + + false -> + @total_count - @last_year_count - @last_month_count - @last_week_count + end + + assert results |> Map.get("totalCount") == expect_count + end + end + + describe "[query paged_workss filter extra]" do + @query """ + query($filter: PagedWorkssFilter!) { + pagedWorkss(filter: $filter) { + entries { + id + title + } + totalCount + } + } + """ + test "basic filter should work", ~m(guest_conn)a do + {:ok, works} = db_insert(:works) + {:ok, works2} = db_insert(:works) + + variables = %{filter: %{page: 1, size: 20}} + results = guest_conn |> query_result(@query, variables, "pagedWorkss") + + assert results["totalCount"] >= 1 + assert results["entries"] |> Enum.any?(&(&1["id"] == to_string(works.id))) + assert results["entries"] |> Enum.any?(&(&1["id"] != to_string(works2.id))) + end + end + + describe "[paged workss active_at]" do + @query """ + query($filter: PagedWorkssFilter!) { + pagedWorkss(filter: $filter) { + entries { + id + insertedAt + activeAt + } + } + } + """ + + test "latest commented works should appear on top", ~m(guest_conn works_last_week user)a do + variables = %{filter: %{page: 1, size: 20}} + results = guest_conn |> query_result(@query, variables, "pagedWorkss") + entries = results["entries"] + first_works = entries |> List.first() + assert first_works["id"] !== to_string(works_last_week.id) + + Process.sleep(1500) + {:ok, _comment} = CMS.create_comment(:works, works_last_week.id, mock_comment(), user) + + results = guest_conn |> query_result(@query, variables, "pagedWorkss") + entries = results["entries"] + first_works = entries |> List.first() + + assert first_works["id"] == to_string(works_last_week.id) + end + + test "comment on very old works have no effect", ~m(guest_conn works_last_year user)a do + variables = %{filter: %{page: 1, size: 20}} + + {:ok, _comment} = CMS.create_comment(:works, works_last_year.id, mock_comment(), user) + + results = guest_conn |> query_result(@query, variables, "pagedWorkss") + entries = results["entries"] + first_works = entries |> List.first() + + assert first_works["id"] !== to_string(works_last_year.id) + end + + test "latest works author commented works have no effect", ~m(guest_conn works_last_week)a do + variables = %{filter: %{page: 1, size: 20}} + + {:ok, _comment} = + CMS.create_comment( + :works, + works_last_week.id, + mock_comment(), + works_last_week.author.user + ) + + results = guest_conn |> query_result(@query, variables, "pagedWorkss") + entries = results["entries"] + first_works = entries |> List.first() + + assert first_works["id"] !== to_string(works_last_week.id) + end + end +end From 4e0aa9febb5d9a626796048708c518bdde4f112b Mon Sep 17 00:00:00 2001 From: mydearxym Date: Fri, 25 Jun 2021 22:50:59 +0800 Subject: [PATCH 09/12] chore(works): pural issue --- .../published/published_works_test.exs | 6 +- .../cms/paged_articles/paged_works_test.exs | 76 +++++++++---------- 2 files changed, 41 insertions(+), 41 deletions(-) diff --git a/test/groupher_server_web/query/accounts/published/published_works_test.exs b/test/groupher_server_web/query/accounts/published/published_works_test.exs index f36468840..c5fb41291 100644 --- a/test/groupher_server_web/query/accounts/published/published_works_test.exs +++ b/test/groupher_server_web/query/accounts/published/published_works_test.exs @@ -1,4 +1,4 @@ -defmodule GroupherServer.Test.Query.Accounts.Published.Workss do +defmodule GroupherServer.Test.Query.Accounts.Published.Works do use GroupherServer.TestTools alias GroupherServer.CMS @@ -19,7 +19,7 @@ defmodule GroupherServer.Test.Query.Accounts.Published.Workss do describe "[published workss]" do @query """ query($login: String!, $filter: PagedFilter!) { - pagedPublishedWorkss(login: $login, filter: $filter) { + pagedPublishedWorks(login: $login, filter: $filter) { entries { id title @@ -42,7 +42,7 @@ defmodule GroupherServer.Test.Query.Accounts.Published.Workss do {:ok, works2} = CMS.create_article(community, :works, works_attrs, user) variables = %{login: user.login, filter: %{page: 1, size: 20}} - results = guest_conn |> query_result(@query, variables, "pagedPublishedWorkss") + results = guest_conn |> query_result(@query, variables, "pagedPublishedWorks") assert results["entries"] |> Enum.any?(&(&1["id"] == to_string(works.id))) assert results["entries"] |> Enum.any?(&(&1["id"] == to_string(works2.id))) diff --git a/test/groupher_server_web/query/cms/paged_articles/paged_works_test.exs b/test/groupher_server_web/query/cms/paged_articles/paged_works_test.exs index fad7d1b80..2e1d722b4 100644 --- a/test/groupher_server_web/query/cms/paged_articles/paged_works_test.exs +++ b/test/groupher_server_web/query/cms/paged_articles/paged_works_test.exs @@ -1,4 +1,4 @@ -defmodule GroupherServer.Test.Query.PagedArticles.PagedWorkss do +defmodule GroupherServer.Test.Query.PagedArticles.PagedWorks do @moduledoc false use GroupherServer.TestTools @@ -44,8 +44,8 @@ defmodule GroupherServer.Test.Query.PagedArticles.PagedWorkss do describe "[query paged_workss filter pagination]" do @query """ - query($filter: PagedWorkssFilter!) { - pagedWorkss(filter: $filter) { + query($filter: PagedWorksFilter!) { + pagedWorks(filter: $filter) { entries { id document { @@ -68,7 +68,7 @@ defmodule GroupherServer.Test.Query.PagedArticles.PagedWorkss do """ test "should get pagination info", ~m(guest_conn)a do variables = %{filter: %{page: 1, size: 10}} - results = guest_conn |> query_result(@query, variables, "pagedWorkss") + results = guest_conn |> query_result(@query, variables, "pagedWorks") assert results |> is_valid_pagination? assert results["pageSize"] == 10 @@ -84,7 +84,7 @@ defmodule GroupherServer.Test.Query.PagedArticles.PagedWorkss do {:ok, _works} = CMS.create_article(community, :works, works_attrs, user) variables = %{filter: %{page: 1, size: 30}} - results = guest_conn |> query_result(@query, variables, "pagedWorkss") + results = guest_conn |> query_result(@query, variables, "pagedWorks") works = results["entries"] |> List.first() assert not is_nil(get_in(works, ["document", "bodyHtml"])) @@ -100,7 +100,7 @@ defmodule GroupherServer.Test.Query.PagedArticles.PagedWorkss do {:ok, _} = CMS.set_article_tag(:works, works.id, article_tag.id) variables = %{filter: %{page: 1, size: 10, article_tag: article_tag.title}} - results = guest_conn |> query_result(@query, variables, "pagedWorkss") + results = guest_conn |> query_result(@query, variables, "pagedWorks") works = results["entries"] |> List.first() assert results["totalCount"] == 1 @@ -125,7 +125,7 @@ defmodule GroupherServer.Test.Query.PagedArticles.PagedWorkss do filter: %{page: 1, size: 10, article_tags: [article_tag.title, article_tag2.title]} } - results = guest_conn |> query_result(@query, variables, "pagedWorkss") + results = guest_conn |> query_result(@query, variables, "pagedWorks") works = results["entries"] |> List.first() assert results["totalCount"] == 1 @@ -151,7 +151,7 @@ defmodule GroupherServer.Test.Query.PagedArticles.PagedWorkss do filter: %{page: 1, size: 10, community: community.raw, article_tag: article_tag.title} } - results = guest_conn |> query_result(@query, variables, "pagedWorkss") + results = guest_conn |> query_result(@query, variables, "pagedWorks") assert not exist_in?(pinned_works, results["entries"], :string_key) assert exist_in?(works, results["entries"], :string_key) @@ -160,7 +160,7 @@ defmodule GroupherServer.Test.Query.PagedArticles.PagedWorkss do filter: %{page: 1, size: 10, community: community.raw, article_tags: [article_tag.title]} } - results = guest_conn |> query_result(@query, variables, "pagedWorkss") + results = guest_conn |> query_result(@query, variables, "pagedWorks") assert not exist_in?(pinned_works, results["entries"], :string_key) assert exist_in?(works, results["entries"], :string_key) @@ -175,7 +175,7 @@ defmodule GroupherServer.Test.Query.PagedArticles.PagedWorkss do {:ok, _works} = CMS.create_article(community, :works, works_attrs2, user) variables = %{filter: %{page: 1, size: 10, community: community.raw}} - results = guest_conn |> query_result(@query, variables, "pagedWorkss") + results = guest_conn |> query_result(@query, variables, "pagedWorks") works = results["entries"] |> List.first() assert results["totalCount"] == 2 @@ -197,7 +197,7 @@ defmodule GroupherServer.Test.Query.PagedArticles.PagedWorkss do test "pagination should have default page and size arg", ~m(guest_conn)a do variables = %{filter: %{}} - results = guest_conn |> query_result(@query, variables, "pagedWorkss") + results = guest_conn |> query_result(@query, variables, "pagedWorks") assert results |> is_valid_pagination? assert results["pageSize"] == @page_size assert results["totalCount"] == @total_count @@ -206,8 +206,8 @@ defmodule GroupherServer.Test.Query.PagedArticles.PagedWorkss do describe "[query paged_workss filter has_xxx]" do @query """ - query($filter: PagedWorkssFilter!) { - pagedWorkss(filter: $filter) { + query($filter: PagedWorksFilter!) { + pagedWorks(filter: $filter) { entries { id viewerHasCollected @@ -229,7 +229,7 @@ defmodule GroupherServer.Test.Query.PagedArticles.PagedWorkss do {:ok, _works3} = CMS.create_article(community, :works, mock_attrs(:works), user) variables = %{filter: %{community: community.raw}} - results = user_conn |> query_result(@query, variables, "pagedWorkss") + results = user_conn |> query_result(@query, variables, "pagedWorks") assert results["totalCount"] == 3 the_works = Enum.find(results["entries"], &(&1["id"] == to_string(works.id))) @@ -243,7 +243,7 @@ defmodule GroupherServer.Test.Query.PagedArticles.PagedWorkss do {:ok, _} = CMS.collect_article(:works, works.id, user) {:ok, _} = CMS.report_article(:works, works.id, "reason", "attr_info", user) - results = user_conn |> query_result(@query, variables, "pagedWorkss") + results = user_conn |> query_result(@query, variables, "pagedWorks") the_works = Enum.find(results["entries"], &(&1["id"] == to_string(works.id))) assert the_works["viewerHasViewed"] assert the_works["viewerHasUpvoted"] @@ -254,8 +254,8 @@ defmodule GroupherServer.Test.Query.PagedArticles.PagedWorkss do describe "[query paged_workss filter sort]" do @query """ - query($filter: PagedWorkssFilter!) { - pagedWorkss(filter: $filter) { + query($filter: PagedWorksFilter!) { + pagedWorks(filter: $filter) { entries { id inserted_at @@ -276,7 +276,7 @@ defmodule GroupherServer.Test.Query.PagedArticles.PagedWorkss do {:ok, works} = CMS.create_article(community, :works, mock_attrs(:works), user) variables = %{filter: %{community: community.raw}} - results = guest_conn |> query_result(@query, variables, "pagedWorkss") + results = guest_conn |> query_result(@query, variables, "pagedWorks") assert length(results["entries"]) == 1 assert results["entries"] |> Enum.any?(&(&1["id"] == to_string(works.id))) @@ -287,7 +287,7 @@ defmodule GroupherServer.Test.Query.PagedArticles.PagedWorkss do {:ok, _works} = CMS.create_article(community, :works, mock_attrs(:works), user) variables = %{filter: %{community: community.raw}} - results = guest_conn |> query_result(@query, variables, "pagedWorkss") + results = guest_conn |> query_result(@query, variables, "pagedWorks") works = results["entries"] |> List.first() assert works["inserted_at"] == works["active_at"] @@ -295,7 +295,7 @@ defmodule GroupherServer.Test.Query.PagedArticles.PagedWorkss do test "filter sort should have default :desc_active", ~m(guest_conn)a do variables = %{filter: %{}} - results = guest_conn |> query_result(@query, variables, "pagedWorkss") + results = guest_conn |> query_result(@query, variables, "pagedWorks") active_timestamps = results["entries"] |> Enum.map(& &1["active_at"]) {:ok, first_active_time, 0} = active_timestamps |> List.first() |> DateTime.from_iso8601() @@ -305,8 +305,8 @@ defmodule GroupherServer.Test.Query.PagedArticles.PagedWorkss do end @query """ - query($filter: PagedWorkssFilter!) { - pagedWorkss(filter: $filter) { + query($filter: PagedWorksFilter!) { + pagedWorks(filter: $filter) { entries { id views @@ -319,7 +319,7 @@ defmodule GroupherServer.Test.Query.PagedArticles.PagedWorkss do most_views_works = Works |> order_by(desc: :views) |> limit(1) |> Repo.one() variables = %{filter: %{sort: "MOST_VIEWS"}} - results = guest_conn |> query_result(@query, variables, "pagedWorkss") + results = guest_conn |> query_result(@query, variables, "pagedWorks") find_works = results |> Map.get("entries") |> hd assert find_works["views"] == most_views_works |> Map.get(:views) @@ -332,8 +332,8 @@ defmodule GroupherServer.Test.Query.PagedArticles.PagedWorkss do """ describe "[query paged_workss filter when]" do @query """ - query($filter: PagedWorkssFilter!) { - pagedWorkss(filter: $filter) { + query($filter: PagedWorksFilter!) { + pagedWorks(filter: $filter) { entries { id views @@ -345,14 +345,14 @@ defmodule GroupherServer.Test.Query.PagedArticles.PagedWorkss do """ test "THIS_YEAR option should work", ~m(guest_conn works_last_year)a do variables = %{filter: %{when: "THIS_YEAR"}} - results = guest_conn |> query_result(@query, variables, "pagedWorkss") + results = guest_conn |> query_result(@query, variables, "pagedWorks") assert results["entries"] |> Enum.any?(&(&1["id"] != works_last_year.id)) end test "TODAY option should work", ~m(guest_conn)a do variables = %{filter: %{when: "TODAY"}} - results = guest_conn |> query_result(@query, variables, "pagedWorkss") + results = guest_conn |> query_result(@query, variables, "pagedWorks") expect_count = @total_count - @last_year_count - @last_month_count - @last_week_count @@ -361,14 +361,14 @@ defmodule GroupherServer.Test.Query.PagedArticles.PagedWorkss do test "THIS_WEEK option should work", ~m(guest_conn)a do variables = %{filter: %{when: "THIS_WEEK"}} - results = guest_conn |> query_result(@query, variables, "pagedWorkss") + results = guest_conn |> query_result(@query, variables, "pagedWorks") assert results |> Map.get("totalCount") == @today_count end test "THIS_MONTH option should work", ~m(guest_conn)a do variables = %{filter: %{when: "THIS_MONTH"}} - results = guest_conn |> query_result(@query, variables, "pagedWorkss") + results = guest_conn |> query_result(@query, variables, "pagedWorks") {_, cur_week_month, _} = @cur_date |> Date.to_erl() {_, last_week_month, _} = @last_week |> Date.to_erl() @@ -388,8 +388,8 @@ defmodule GroupherServer.Test.Query.PagedArticles.PagedWorkss do describe "[query paged_workss filter extra]" do @query """ - query($filter: PagedWorkssFilter!) { - pagedWorkss(filter: $filter) { + query($filter: PagedWorksFilter!) { + pagedWorks(filter: $filter) { entries { id title @@ -403,7 +403,7 @@ defmodule GroupherServer.Test.Query.PagedArticles.PagedWorkss do {:ok, works2} = db_insert(:works) variables = %{filter: %{page: 1, size: 20}} - results = guest_conn |> query_result(@query, variables, "pagedWorkss") + results = guest_conn |> query_result(@query, variables, "pagedWorks") assert results["totalCount"] >= 1 assert results["entries"] |> Enum.any?(&(&1["id"] == to_string(works.id))) @@ -413,8 +413,8 @@ defmodule GroupherServer.Test.Query.PagedArticles.PagedWorkss do describe "[paged workss active_at]" do @query """ - query($filter: PagedWorkssFilter!) { - pagedWorkss(filter: $filter) { + query($filter: PagedWorksFilter!) { + pagedWorks(filter: $filter) { entries { id insertedAt @@ -426,7 +426,7 @@ defmodule GroupherServer.Test.Query.PagedArticles.PagedWorkss do test "latest commented works should appear on top", ~m(guest_conn works_last_week user)a do variables = %{filter: %{page: 1, size: 20}} - results = guest_conn |> query_result(@query, variables, "pagedWorkss") + results = guest_conn |> query_result(@query, variables, "pagedWorks") entries = results["entries"] first_works = entries |> List.first() assert first_works["id"] !== to_string(works_last_week.id) @@ -434,7 +434,7 @@ defmodule GroupherServer.Test.Query.PagedArticles.PagedWorkss do Process.sleep(1500) {:ok, _comment} = CMS.create_comment(:works, works_last_week.id, mock_comment(), user) - results = guest_conn |> query_result(@query, variables, "pagedWorkss") + results = guest_conn |> query_result(@query, variables, "pagedWorks") entries = results["entries"] first_works = entries |> List.first() @@ -446,7 +446,7 @@ defmodule GroupherServer.Test.Query.PagedArticles.PagedWorkss do {:ok, _comment} = CMS.create_comment(:works, works_last_year.id, mock_comment(), user) - results = guest_conn |> query_result(@query, variables, "pagedWorkss") + results = guest_conn |> query_result(@query, variables, "pagedWorks") entries = results["entries"] first_works = entries |> List.first() @@ -464,7 +464,7 @@ defmodule GroupherServer.Test.Query.PagedArticles.PagedWorkss do works_last_week.author.user ) - results = guest_conn |> query_result(@query, variables, "pagedWorkss") + results = guest_conn |> query_result(@query, variables, "pagedWorks") entries = results["entries"] first_works = entries |> List.first() From f50cfc61f4bcce04defd92536d1ae51a410c8096 Mon Sep 17 00:00:00 2001 From: mydearxym Date: Fri, 25 Jun 2021 23:17:28 +0800 Subject: [PATCH 10/12] chore(works): meta thread fix --- test/support/factory.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/support/factory.ex b/test/support/factory.ex index 350202507..af2ddfb98 100644 --- a/test/support/factory.ex +++ b/test/support/factory.ex @@ -223,7 +223,7 @@ defmodule GroupherServer.Support.Factory do text = Faker.Lorem.sentence(%Range{first: 80, last: 120}) %{ - meta: @default_article_meta |> Map.merge(%{thread: "BLOG"}), + meta: @default_article_meta |> Map.merge(%{thread: "WORKS"}), title: String.slice(text, 1, 49), body: mock_rich_text(), digest: String.slice(text, 1, 150), From 29b6eac1b696940190412d12ef600ffc8cdad671 Mon Sep 17 00:00:00 2001 From: mydearxym Date: Fri, 25 Jun 2021 23:34:33 +0800 Subject: [PATCH 11/12] chore(works): adjust article_queries macros --- .../schema/Helper/queries.ex | 33 ++++++++++--------- .../schema/cms/cms_queries.ex | 5 +-- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/lib/groupher_server_web/schema/Helper/queries.ex b/lib/groupher_server_web/schema/Helper/queries.ex index 28e85b700..e55574ba6 100644 --- a/lib/groupher_server_web/schema/Helper/queries.ex +++ b/lib/groupher_server_web/schema/Helper/queries.ex @@ -48,25 +48,28 @@ defmodule GroupherServerWeb.Schema.Helper.Queries do post, page_posts ... """ - defmacro article_queries(thread) do - quote do - @desc unquote("get #{thread} by id") - field unquote(thread), non_null(unquote(thread)) do - arg(:id, non_null(:id)) - arg(:thread, unquote(:"#{thread}_thread"), default_value: unquote(thread)) + defmacro article_queries() do + @article_threads + |> Enum.map(fn thread -> + quote do + @desc unquote("get #{thread} by id") + field unquote(thread), non_null(unquote(thread)) do + arg(:id, non_null(:id)) + arg(:thread, unquote(:"#{thread}_thread"), default_value: unquote(thread)) - resolve(&R.CMS.read_article/3) - end + resolve(&R.CMS.read_article/3) + end - @desc unquote("get paged #{plural(thread)}") - field unquote(:"paged_#{plural(thread)}"), unquote(:"paged_#{plural(thread)}") do - arg(:thread, unquote(:"#{thread}_thread"), default_value: unquote(thread)) - arg(:filter, non_null(unquote(:"paged_#{plural(thread)}_filter"))) + @desc unquote("get paged #{plural(thread)}") + field unquote(:"paged_#{plural(thread)}"), unquote(:"paged_#{plural(thread)}") do + arg(:thread, unquote(:"#{thread}_thread"), default_value: unquote(thread)) + arg(:filter, non_null(unquote(:"paged_#{plural(thread)}_filter"))) - middleware(M.PageSizeProof, default_sort: :desc_active) - resolve(&R.CMS.paged_articles/3) + middleware(M.PageSizeProof, default_sort: :desc_active) + resolve(&R.CMS.paged_articles/3) + end end - end + end) end defmacro article_reacted_users_query(action, resolver) do diff --git a/lib/groupher_server_web/schema/cms/cms_queries.ex b/lib/groupher_server_web/schema/cms/cms_queries.ex index 394c440bd..6ed0a3284 100644 --- a/lib/groupher_server_web/schema/cms/cms_queries.ex +++ b/lib/groupher_server_web/schema/cms/cms_queries.ex @@ -145,9 +145,6 @@ defmodule GroupherServerWeb.Schema.CMS.Queries do article_reacted_users_query(:upvot, &R.CMS.upvoted_users/3) article_reacted_users_query(:collect, &R.CMS.collected_users/3) - article_queries(:post) - article_queries(:job) - article_queries(:blog) - article_queries(:repo) + article_queries() end end From 9bdf37fa210d6b92a6d221e2b7f801c929abc393 Mon Sep 17 00:00:00 2001 From: mydearxym Date: Fri, 25 Jun 2021 23:47:15 +0800 Subject: [PATCH 12/12] chore(works): fix test --- test/groupher_server_web/mutation/cms/articles/works_test.exs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/groupher_server_web/mutation/cms/articles/works_test.exs b/test/groupher_server_web/mutation/cms/articles/works_test.exs index e3ecdc48d..763bc4025 100644 --- a/test/groupher_server_web/mutation/cms/articles/works_test.exs +++ b/test/groupher_server_web/mutation/cms/articles/works_test.exs @@ -26,7 +26,6 @@ defmodule GroupherServer.Test.Mutation.Articles.Works do $title: String!, $body: String, $digest: String!, - $length: Int, $communityId: ID!, $articleTags: [Id] ) { @@ -34,7 +33,6 @@ defmodule GroupherServer.Test.Mutation.Articles.Works do title: $title, body: $body, digest: $digest, - length: $length, communityId: $communityId, articleTags: $articleTags ) { @@ -82,6 +80,7 @@ defmodule GroupherServer.Test.Mutation.Articles.Works do works_attr |> Map.merge(%{communityId: community.id, articleTags: [article_tag.id]}) created = user_conn |> mutation_result(@create_works_query, variables, "createWorks") + {:ok, works} = ORM.find(Works, created["id"], preload: :article_tags) assert exist_in?(%{id: article_tag.id}, works.article_tags)