diff --git a/config/config.exs b/config/config.exs index d523bfa37..7c01d66fa 100644 --- a/config/config.exs +++ b/config/config.exs @@ -69,7 +69,7 @@ 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, :works, :radar, :guide, :meetup], + threads: [:post, :job, :repo, :blog, :works, :radar, :guide, :meetup, :drink], # in this period, paged articles will sort front if non-article-author commented # 在此时间段内,一旦有非文章作者的用户评论,该文章就会排到前面 active_period_days: %{ @@ -80,7 +80,8 @@ config :groupher_server, :article, works: 10, radar: 10, guide: 10, - meetup: 10 + meetup: 10, + drink: 10 }, # NOTE: if you want to add/remove emotion, just edit the list below diff --git a/lib/groupher_server/cms/delegates/article_curd.ex b/lib/groupher_server/cms/delegates/article_curd.ex index 5227b21b2..e784f6b4d 100644 --- a/lib/groupher_server/cms/delegates/article_curd.ex +++ b/lib/groupher_server/cms/delegates/article_curd.ex @@ -526,7 +526,7 @@ defmodule GroupherServer.CMS.Delegate.ArticleCURD do defp result({:ok, %{set_viewer_has_states: result}}), do: result |> done() defp result({:ok, %{update_article_meta: result}}), do: {:ok, result} - defp result({:error, :create_article, _result, _steps}) do + defp result({:error, :create_article, result, _steps}) do {:error, [message: "create article", code: ecode(:create_fails)]} end diff --git a/lib/groupher_server/cms/models/blog.ex b/lib/groupher_server/cms/models/blog.ex index e7520eb26..13a4bc638 100644 --- a/lib/groupher_server/cms/models/blog.ex +++ b/lib/groupher_server/cms/models/blog.ex @@ -15,12 +15,11 @@ defmodule GroupherServer.CMS.Model.Blog do @required_fields ~w(title digest)a @article_cast_fields general_article_cast_fields() - @optional_fields ~w(link_addr digest length)a ++ @article_cast_fields + @optional_fields ~w(link_addr digest)a ++ @article_cast_fields @type t :: %Blog{} schema "cms_blogs" do field(:link_addr, :string) - field(:length, :integer) article_tags_field(:blog) article_communities_field(:blog) diff --git a/lib/groupher_server/cms/models/drink.ex b/lib/groupher_server/cms/models/drink.ex new file mode 100644 index 000000000..27fd211f8 --- /dev/null +++ b/lib/groupher_server/cms/models/drink.ex @@ -0,0 +1,48 @@ +defmodule GroupherServer.CMS.Model.Drink 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 + + @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 :: %Drink{} + schema "cms_drinks" do + article_tags_field(:drink) + article_communities_field(:drink) + general_article_fields(:drink) + end + + @doc false + def changeset(%Drink{} = drink, attrs) do + drink + |> 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(%Drink{} = drink, attrs) do + drink + |> 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) + end +end diff --git a/lib/groupher_server/cms/models/drink_document.ex b/lib/groupher_server/cms/models/drink_document.ex new file mode 100644 index 000000000..c5a0f2710 --- /dev/null +++ b/lib/groupher_server/cms/models/drink_document.ex @@ -0,0 +1,47 @@ +defmodule GroupherServer.CMS.Model.DrinkDocument 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.Drink + + @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 drink_id)a + @optional_fields [] + + @type t :: %DrinkDocument{} + schema "drink_documents" do + belongs_to(:drink, Drink, foreign_key: :drink_id) + + field(:body, :string) + field(:body_html, :string) + field(:toc, :map) + end + + @doc false + def changeset(%DrinkDocument{} = drink, attrs) do + drink + |> 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(%DrinkDocument{} = drink, attrs) do + drink + |> cast(attrs, @optional_fields ++ @required_fields) + |> validate_length(:body, min: @min_body_length, max: @max_body_length) + end +end diff --git a/lib/groupher_server/cms/models/job.ex b/lib/groupher_server/cms/models/job.ex index 142a3ffb9..2a4745026 100644 --- a/lib/groupher_server/cms/models/job.ex +++ b/lib/groupher_server/cms/models/job.ex @@ -12,7 +12,7 @@ defmodule GroupherServer.CMS.Model.Job do alias CMS.Model.Embeds @timestamps_opts [type: :utc_datetime_usec] - @required_fields ~w(title company digest length)a + @required_fields ~w(title company digest)a @article_cast_fields general_article_cast_fields() @optional_fields @article_cast_fields ++ ~w(desc company_link link_addr copy_right)a @@ -24,7 +24,6 @@ defmodule GroupherServer.CMS.Model.Job do field(:link_addr, :string) field(:copy_right, :string) - field(:length, :integer) article_tags_field(:job) article_communities_field(:job) diff --git a/lib/groupher_server/cms/models/post.ex b/lib/groupher_server/cms/models/post.ex index 6bdd15a07..b9a6d8ffb 100644 --- a/lib/groupher_server/cms/models/post.ex +++ b/lib/groupher_server/cms/models/post.ex @@ -15,7 +15,7 @@ defmodule GroupherServer.CMS.Model.Post do @timestamps_opts [type: :utc_datetime_usec] - @required_fields ~w(title digest length)a + @required_fields ~w(title digest)a @article_cast_fields general_article_cast_fields() @optional_fields ~w(link_addr copy_right is_question is_solved solution_digest)a ++ @article_cast_fields @@ -24,7 +24,6 @@ defmodule GroupherServer.CMS.Model.Post do schema "cms_posts" do field(:link_addr, :string) field(:copy_right, :string) - field(:length, :integer) field(:is_question, :boolean, default: false) field(:is_solved, :boolean, default: false) diff --git a/lib/groupher_server_web/schema/Helper/fields.ex b/lib/groupher_server_web/schema/Helper/fields.ex index 1ba8b9da8..703889a4a 100644 --- a/lib/groupher_server_web/schema/Helper/fields.ex +++ b/lib/groupher_server_web/schema/Helper/fields.ex @@ -168,7 +168,6 @@ defmodule GroupherServerWeb.Schema.Helper.Fields do defmacro article_filter_fields do quote do field(:when, :when_enum) - field(:length, :length_enum) field(:article_tag, :string) field(:article_tags, list_of(:string)) field(:community, :string) diff --git a/lib/groupher_server_web/schema/cms/cms_metrics.ex b/lib/groupher_server_web/schema/cms/cms_metrics.ex index d35d887f8..b5928becf 100644 --- a/lib/groupher_server_web/schema/cms/cms_metrics.ex +++ b/lib/groupher_server_web/schema/cms/cms_metrics.ex @@ -161,7 +161,6 @@ defmodule GroupherServerWeb.Schema.CMS.Metrics do # field(:sort, :sort_input) field(:when, :when_enum) field(:sort, :sort_enum) - field(:length, :length_enum) # @desc "Matching a tag" # @desc "Added to the menu after this date" # field(:added_after, :datetime) @@ -226,6 +225,13 @@ defmodule GroupherServerWeb.Schema.CMS.Metrics do field(:sort, :sort_enum) end + @desc "drink_filter doc" + input_object :paged_drinks_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 3207e2b46..cee08fab0 100644 --- a/lib/groupher_server_web/schema/cms/cms_types.ex +++ b/lib/groupher_server_web/schema/cms/cms_types.ex @@ -58,7 +58,6 @@ defmodule GroupherServerWeb.Schema.CMS.Types do general_article_fields() comments_fields() - field(:length, :integer) field(:link_addr, :string) field(:copy_right, :string) @@ -74,7 +73,6 @@ defmodule GroupherServerWeb.Schema.CMS.Types do field(:desc, :string) field(:company, :string) field(:company_link, :string) - field(:length, :integer) field(:link_addr, :string) field(:copy_right, :string) @@ -87,7 +85,6 @@ defmodule GroupherServerWeb.Schema.CMS.Types do general_article_fields() comments_fields() - field(:length, :integer) field(:link_addr, :string) timestamp_fields(:article) @@ -99,7 +96,6 @@ defmodule GroupherServerWeb.Schema.CMS.Types do general_article_fields() comments_fields() - field(:length, :integer) field(:link_addr, :string) timestamp_fields(:article) @@ -111,7 +107,6 @@ defmodule GroupherServerWeb.Schema.CMS.Types do general_article_fields() comments_fields() - field(:length, :integer) field(:link_addr, :string) timestamp_fields(:article) @@ -123,7 +118,6 @@ defmodule GroupherServerWeb.Schema.CMS.Types do general_article_fields() comments_fields() - field(:length, :integer) field(:link_addr, :string) timestamp_fields(:article) @@ -135,8 +129,14 @@ defmodule GroupherServerWeb.Schema.CMS.Types do general_article_fields() comments_fields() - field(:length, :integer) - field(:link_addr, :string) + timestamp_fields(:article) + end + + object :drink do + interface(:article) + + general_article_fields() + comments_fields() timestamp_fields(:article) end diff --git a/lib/groupher_server_web/schema/cms/mutations/blog.ex b/lib/groupher_server_web/schema/cms/mutations/blog.ex index 395e3a327..942b6c331 100644 --- a/lib/groupher_server_web/schema/cms/mutations/blog.ex +++ b/lib/groupher_server_web/schema/cms/mutations/blog.ex @@ -11,7 +11,6 @@ defmodule GroupherServerWeb.Schema.CMS.Mutations.Blog do arg(:title, non_null(:string)) arg(:body, non_null(:string)) arg(:digest, non_null(:string)) - arg(:length, :integer) arg(:community_id, non_null(:id)) arg(:link_addr, :string) arg(:thread, :thread, default_value: :blog) @@ -29,7 +28,6 @@ defmodule GroupherServerWeb.Schema.CMS.Mutations.Blog do arg(:title, :string) arg(:body, :string) arg(:digest, :string) - arg(:length, :integer) arg(:link_addr, :string) arg(:article_tags, list_of(:id)) diff --git a/lib/groupher_server_web/schema/cms/mutations/drink.ex b/lib/groupher_server_web/schema/cms/mutations/drink.ex new file mode 100644 index 000000000..ec25adb28 --- /dev/null +++ b/lib/groupher_server_web/schema/cms/mutations/drink.ex @@ -0,0 +1,52 @@ +defmodule GroupherServerWeb.Schema.CMS.Mutations.Drink do + @moduledoc """ + CMS mutations for drink + """ + use Helper.GqlSchemaSuite + import GroupherServerWeb.Schema.Helper.Mutations + + object :cms_drink_mutations do + @desc "create a drink" + field :create_drink, :drink do + arg(:title, non_null(:string)) + arg(:body, non_null(:string)) + arg(:digest, non_null(:string)) + arg(:community_id, non_null(:id)) + arg(:thread, :thread, default_value: :drink) + 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/drink" + field :update_drink, :drink 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: :drink) + middleware(M.Passport, claim: "owner;cms->c?->drink.edit") + + resolve(&R.CMS.update_article/3) + end + + article_react_mutations(:drink, [ + :upvote, + :pin, + :mark_delete, + :delete, + :emotion, + :report, + :sink, + :lock_comment + ]) + end +end diff --git a/lib/groupher_server_web/schema/cms/mutations/job.ex b/lib/groupher_server_web/schema/cms/mutations/job.ex index 44d714ef6..6af88db1c 100644 --- a/lib/groupher_server_web/schema/cms/mutations/job.ex +++ b/lib/groupher_server_web/schema/cms/mutations/job.ex @@ -13,7 +13,6 @@ defmodule GroupherServerWeb.Schema.CMS.Mutations.Job do arg(:company_link, :string) arg(:body, non_null(:string)) arg(:digest, non_null(:string)) - arg(:length, non_null(:integer)) arg(:community_id, non_null(:id)) arg(:desc, :string) @@ -35,7 +34,6 @@ defmodule GroupherServerWeb.Schema.CMS.Mutations.Job do arg(:title, :string) arg(:body, :string) arg(:digest, :string) - arg(:length, :integer) arg(:copy_right, :string) arg(:desc, :string) arg(:link_addr, :string) diff --git a/lib/groupher_server_web/schema/cms/mutations/post.ex b/lib/groupher_server_web/schema/cms/mutations/post.ex index 2b01a8e5d..5429be9ca 100644 --- a/lib/groupher_server_web/schema/cms/mutations/post.ex +++ b/lib/groupher_server_web/schema/cms/mutations/post.ex @@ -12,7 +12,6 @@ defmodule GroupherServerWeb.Schema.CMS.Mutations.Post do arg(:title, non_null(:string)) arg(:body, non_null(:string)) arg(:digest, non_null(:string)) - arg(:length, non_null(:integer)) arg(:link_addr, :string) arg(:copy_right, :string) arg(:community_id, non_null(:id)) diff --git a/priv/repo/migrations/20210626100316_create_drink.exs b/priv/repo/migrations/20210626100316_create_drink.exs new file mode 100644 index 000000000..b9f9f5c1d --- /dev/null +++ b/priv/repo/migrations/20210626100316_create_drink.exs @@ -0,0 +1,32 @@ +defmodule GroupherServer.Repo.Migrations.CreateDrink do + use Ecto.Migration + + def change do + create table(:cms_drinks) 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(:comments_participants_count, :integer, default: 0) + add(:comments_count, :integer, default: 0) + add(:comments_participants, :map) + + timestamps() + end + + create(index(:cms_drinks, [:author_id])) + end +end diff --git a/priv/repo/migrations/20210626101523_create_communities_join_drinks.exs b/priv/repo/migrations/20210626101523_create_communities_join_drinks.exs new file mode 100644 index 000000000..fe4604a8f --- /dev/null +++ b/priv/repo/migrations/20210626101523_create_communities_join_drinks.exs @@ -0,0 +1,13 @@ +defmodule GroupherServer.Repo.Migrations.CreateCommunitiesJoinDrinks do + use Ecto.Migration + + def change do + create table(:drink_documents) do + add(:drink_id, references(:cms_drinks, 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/20210626101620_add_drink_to_others.exs b/priv/repo/migrations/20210626101620_add_drink_to_others.exs new file mode 100644 index 000000000..a2cfab042 --- /dev/null +++ b/priv/repo/migrations/20210626101620_add_drink_to_others.exs @@ -0,0 +1,41 @@ +defmodule GroupherServer.Repo.Migrations.AddDrinkToOthers do + use Ecto.Migration + + def change do + alter table(:articles_join_tags) do + add(:drink_id, references(:cms_drinks, on_delete: :delete_all)) + end + + alter table(:abuse_reports) do + add(:drink_id, references(:cms_drinks, on_delete: :delete_all)) + end + + alter table(:article_collects) do + add(:drink_id, references(:cms_drinks, on_delete: :delete_all)) + end + + alter table(:article_upvotes) do + add(:drink_id, references(:cms_drinks, on_delete: :delete_all)) + end + + alter table(:comments) do + add(:drink_id, references(:cms_drinks, on_delete: :delete_all)) + end + + alter table(:pinned_comments) do + add(:drink_id, references(:cms_drinks, on_delete: :delete_all)) + end + + alter table(:articles_users_emotions) do + add(:drink_id, references(:cms_drinks, on_delete: :delete_all)) + end + + alter table(:pinned_articles) do + add(:drink_id, references(:cms_drinks, on_delete: :delete_all)) + end + + alter table(:cited_artiments) do + add(:drink_id, references(:cms_drinks, on_delete: :delete_all)) + end + end +end diff --git a/priv/repo/migrations/20210626124149_add_drink_community_join_table.exs b/priv/repo/migrations/20210626124149_add_drink_community_join_table.exs new file mode 100644 index 000000000..f5ef5ed85 --- /dev/null +++ b/priv/repo/migrations/20210626124149_add_drink_community_join_table.exs @@ -0,0 +1,12 @@ +defmodule GroupherServer.Repo.Migrations.AddDrinkCommunityJoinTable do + use Ecto.Migration + + def change do + create table(:communities_join_drinks) do + add(:community_id, references(:communities, on_delete: :delete_all), null: false) + add(:drink_id, references(:cms_drinks, on_delete: :delete_all), null: false) + end + + create(unique_index(:communities_join_drinks, [:community_id, :drink_id])) + end +end diff --git a/test/groupher_server/accounts/published/published_drink_test.exs b/test/groupher_server/accounts/published/published_drink_test.exs new file mode 100644 index 000000000..0abead783 --- /dev/null +++ b/test/groupher_server/accounts/published/published_drink_test.exs @@ -0,0 +1,93 @@ +defmodule GroupherServer.Test.Accounts.Published.Drink do + use GroupherServer.TestTools + + alias GroupherServer.{Accounts, CMS} + alias Accounts.Model.User + alias Helper.ORM + + @publish_count 10 + + setup do + {:ok, user} = db_insert(:user) + {:ok, user2} = db_insert(:user) + {:ok, drink} = db_insert(:drink) + {:ok, community} = db_insert(:community) + {:ok, community2} = db_insert(:community) + + {:ok, ~m(user user2 drink community community2)a} + end + + describe "[publised drinks]" do + test "create drink should update user published meta", ~m(community user)a do + drink_attrs = mock_attrs(:drink, %{community_id: community.id}) + {:ok, _drink} = CMS.create_article(community, :drink, drink_attrs, user) + {:ok, _drink} = CMS.create_article(community, :drink, drink_attrs, user) + + {:ok, user} = ORM.find(User, user.id) + assert user.meta.published_drinks_count == 2 + end + + test "fresh user get empty paged published drinks", ~m(user)a do + {:ok, results} = Accounts.paged_published_articles(user, :drink, %{page: 1, size: 20}) + + assert results |> is_valid_pagination?(:raw) + assert results.total_count == 0 + end + + test "user can get paged published drinks", ~m(user user2 community community2)a do + pub_drinks = + Enum.reduce(1..@publish_count, [], fn _, acc -> + drink_attrs = mock_attrs(:drink, %{community_id: community.id}) + {:ok, drink} = CMS.create_article(community, :drink, drink_attrs, user) + + acc ++ [drink] + end) + + pub_drinks2 = + Enum.reduce(1..@publish_count, [], fn _, acc -> + drink_attrs = mock_attrs(:drink, %{community_id: community2.id}) + {:ok, drink} = CMS.create_article(community, :drink, drink_attrs, user) + + acc ++ [drink] + end) + + # unrelated other user + Enum.reduce(1..5, [], fn _, acc -> + drink_attrs = mock_attrs(:drink, %{community_id: community.id}) + {:ok, drink} = CMS.create_article(community, :drink, drink_attrs, user2) + + acc ++ [drink] + end) + + {:ok, results} = Accounts.paged_published_articles(user, :drink, %{page: 1, size: 20}) + + assert results |> is_valid_pagination?(:raw) + assert results.total_count == @publish_count * 2 + + random_drink_id = pub_drinks |> Enum.random() |> Map.get(:id) + random_drink_id2 = pub_drinks2 |> Enum.random() |> Map.get(:id) + assert results.entries |> Enum.any?(&(&1.id == random_drink_id)) + assert results.entries |> Enum.any?(&(&1.id == random_drink_id2)) + end + end + + describe "[publised drink comments]" do + test "can get published article comments", ~m(drink user)a do + total_count = 10 + + Enum.reduce(1..total_count, [], fn _, acc -> + {:ok, comment} = CMS.create_comment(:drink, drink.id, mock_comment(), user) + acc ++ [comment] + end) + + filter = %{page: 1, size: 20} + {:ok, articles} = Accounts.paged_published_comments(user, :drink, filter) + + entries = articles.entries + article = entries |> List.first() + + assert article.article.id == drink.id + assert article.article.title == drink.title + end + end +end diff --git a/test/groupher_server/cms/abuse_reports/drink_report_test.exs b/test/groupher_server/cms/abuse_reports/drink_report_test.exs new file mode 100644 index 000000000..0b4f8d89c --- /dev/null +++ b/test/groupher_server/cms/abuse_reports/drink_report_test.exs @@ -0,0 +1,137 @@ +defmodule GroupherServer.Test.CMS.AbuseReports.DrinkReport do + @moduledoc false + + use GroupherServer.TestTools + + alias Helper.ORM + alias GroupherServer.CMS + alias CMS.Model.Drink + + setup do + {:ok, user} = db_insert(:user) + {:ok, user2} = db_insert(:user) + + {:ok, community} = db_insert(:community) + drink_attrs = mock_attrs(:drink, %{community_id: community.id}) + + {:ok, ~m(user user2 community drink_attrs)a} + end + + describe "[article drink report/unreport]" do + test "list article reports should work", ~m(community user user2 drink_attrs)a do + {:ok, drink} = CMS.create_article(community, :drink, drink_attrs, user) + {:ok, _report} = CMS.report_article(:drink, drink.id, "reason", "attr_info", user) + {:ok, _report} = CMS.report_article(:drink, drink.id, "reason", "attr_info", user2) + + filter = %{content_type: :drink, content_id: drink.id, page: 1, size: 20} + {:ok, all_reports} = CMS.paged_reports(filter) + + report = all_reports.entries |> List.first() + assert report.article.id == drink.id + assert report.article.thread == "DRINK" + end + + test "report a drink should have a abuse report record", ~m(community user drink_attrs)a do + {:ok, drink} = CMS.create_article(community, :drink, drink_attrs, user) + {:ok, _report} = CMS.report_article(:drink, drink.id, "reason", "attr_info", user) + + filter = %{content_type: :drink, content_id: drink.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 == drink.id + assert all_reports.total_count == 1 + assert report.report_cases_count == 1 + assert List.first(report_cases).user.login == user.login + + {:ok, drink} = ORM.find(Drink, drink.id) + assert drink.meta.reported_count == 1 + assert user.id in drink.meta.reported_user_ids + end + + test "can undo a report", ~m(community user drink_attrs)a do + {:ok, drink} = CMS.create_article(community, :drink, drink_attrs, user) + {:ok, _report} = CMS.report_article(:drink, drink.id, "reason", "attr_info", user) + {:ok, _report} = CMS.undo_report_article(:drink, drink.id, user) + + filter = %{content_type: :drink, content_id: drink.id, page: 1, size: 20} + {:ok, all_reports} = CMS.paged_reports(filter) + assert all_reports.total_count == 0 + + {:ok, drink} = ORM.find(Drink, drink.id) + assert user.id not in drink.meta.reported_user_ids + end + + test "can undo a existed report", ~m(community user user2 drink_attrs)a do + {:ok, drink} = CMS.create_article(community, :drink, drink_attrs, user) + {:ok, _report} = CMS.report_article(:drink, drink.id, "reason", "attr_info", user) + {:ok, _report} = CMS.report_article(:drink, drink.id, "reason", "attr_info", user2) + {:ok, _report} = CMS.undo_report_article(:drink, drink.id, user) + + filter = %{content_type: :drink, content_id: drink.id, page: 1, size: 20} + {:ok, all_reports} = CMS.paged_reports(filter) + assert all_reports.total_count == 1 + + {:ok, drink} = ORM.find(Drink, drink.id) + + assert user2.id in drink.meta.reported_user_ids + assert user.id not in drink.meta.reported_user_ids + end + + test "can undo a report with other user report it too", + ~m(community user user2 drink_attrs)a do + {:ok, drink} = CMS.create_article(community, :drink, drink_attrs, user) + {:ok, _report} = CMS.report_article(:drink, drink.id, "reason", "attr_info", user) + {:ok, _report} = CMS.report_article(:drink, drink.id, "reason", "attr_info", user2) + + filter = %{content_type: :drink, content_id: drink.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(:drink, drink.id, user) + + filter = %{content_type: :drink, content_id: drink.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 drink_attrs)a do + {:ok, drink} = CMS.create_article(community, :drink, drink_attrs, user) + + {:ok, _report} = CMS.report_article(:drink, drink.id, "reason", "attr_info", user) + {:ok, _report} = CMS.report_article(:drink, drink.id, "reason2", "attr_info 2", user2) + + filter = %{content_type: :drink, content_id: drink.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 drink_attrs user)a do + {:ok, drink} = CMS.create_article(community, :drink, drink_attrs, user) + + {:ok, _report} = CMS.report_article(:drink, drink.id, "reason", "attr_info", user) + assert {:error, _report} = CMS.report_article(:drink, drink.id, "reason", "attr_info", user) + end + end +end diff --git a/test/groupher_server/cms/article_community/drink_test.exs b/test/groupher_server/cms/article_community/drink_test.exs new file mode 100644 index 000000000..58ffa8d09 --- /dev/null +++ b/test/groupher_server/cms/article_community/drink_test.exs @@ -0,0 +1,85 @@ +defmodule GroupherServer.Test.CMS.ArticleCommunity.Drink do + use GroupherServer.TestTools + + alias Helper.ORM + alias GroupherServer.CMS + alias CMS.Model.Drink + + setup do + {:ok, user} = db_insert(:user) + {:ok, user2} = db_insert(:user) + {:ok, drink} = db_insert(:drink) + {:ok, community} = db_insert(:community) + {:ok, community2} = db_insert(:community) + {:ok, community3} = db_insert(:community) + + drink_attrs = mock_attrs(:drink, %{community_id: community.id}) + + {:ok, ~m(user user2 community community2 community3 drink drink_attrs)a} + end + + describe "[article mirror/move]" do + test "created drink has origial community info", ~m(user community drink_attrs)a do + {:ok, drink} = CMS.create_article(community, :drink, drink_attrs, user) + {:ok, drink} = ORM.find(Drink, drink.id, preload: :original_community) + + assert drink.original_community_id == community.id + end + + test "drink can be move to other community", ~m(user community community2 drink_attrs)a do + {:ok, drink} = CMS.create_article(community, :drink, drink_attrs, user) + assert drink.original_community_id == community.id + + {:ok, _} = CMS.move_article(:drink, drink.id, community2.id) + {:ok, drink} = ORM.find(Drink, drink.id, preload: [:original_community, :communities]) + + assert drink.original_community.id == community2.id + assert not is_nil(Enum.find(drink.communities, &(&1.id == community2.id))) + end + + test "drink can be mirror to other community", ~m(user community community2 drink_attrs)a do + {:ok, drink} = CMS.create_article(community, :drink, drink_attrs, user) + + {:ok, drink} = ORM.find(Drink, drink.id, preload: :communities) + assert drink.communities |> length == 1 + + assert not is_nil(Enum.find(drink.communities, &(&1.id == community.id))) + + {:ok, _} = CMS.mirror_article(:drink, drink.id, community2.id) + + {:ok, drink} = ORM.find(Drink, drink.id, preload: :communities) + assert drink.communities |> length == 2 + assert not is_nil(Enum.find(drink.communities, &(&1.id == community.id))) + assert not is_nil(Enum.find(drink.communities, &(&1.id == community2.id))) + end + + test "drink can be unmirror from community", + ~m(user community community2 community3 drink_attrs)a do + {:ok, drink} = CMS.create_article(community, :drink, drink_attrs, user) + {:ok, _} = CMS.mirror_article(:drink, drink.id, community2.id) + {:ok, _} = CMS.mirror_article(:drink, drink.id, community3.id) + + {:ok, drink} = ORM.find(Drink, drink.id, preload: :communities) + assert drink.communities |> length == 3 + + {:ok, _} = CMS.unmirror_article(:drink, drink.id, community3.id) + {:ok, drink} = ORM.find(Drink, drink.id, preload: :communities) + assert drink.communities |> length == 2 + + assert is_nil(Enum.find(drink.communities, &(&1.id == community3.id))) + end + + test "drink can not unmirror from original community", + ~m(user community community2 community3 drink_attrs)a do + {:ok, drink} = CMS.create_article(community, :drink, drink_attrs, user) + {:ok, _} = CMS.mirror_article(:drink, drink.id, community2.id) + {:ok, _} = CMS.mirror_article(:drink, drink.id, community3.id) + + {:ok, drink} = ORM.find(Drink, drink.id, preload: :communities) + assert drink.communities |> length == 3 + + {:error, reason} = CMS.unmirror_article(:drink, drink.id, community.id) + assert reason |> is_error?(:mirror_article) + end + end +end diff --git a/test/groupher_server/cms/article_tags/drink_tag_test.exs b/test/groupher_server/cms/article_tags/drink_tag_test.exs new file mode 100644 index 000000000..1e2fff1c2 --- /dev/null +++ b/test/groupher_server/cms/article_tags/drink_tag_test.exs @@ -0,0 +1,134 @@ +defmodule GroupherServer.Test.CMS.ArticleTag.DrinkTag do + use GroupherServer.TestTools + + alias GroupherServer.CMS + alias CMS.Model.{Community, ArticleTag, Drink} + alias Helper.{ORM} + + setup do + {:ok, user} = db_insert(:user) + {:ok, drink} = db_insert(:drink) + {:ok, community} = db_insert(:community) + article_tag_attrs = mock_attrs(:article_tag) + article_tag_attrs2 = mock_attrs(:article_tag) + + drink_attrs = mock_attrs(:drink) + + {:ok, ~m(user community drink drink_attrs article_tag_attrs article_tag_attrs2)a} + end + + describe "[drink 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, :drink, 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, :drink, 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()}, + :drink, + 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, :drink, 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 drink article_tag_attrs article_tag_attrs2 user)a do + {:ok, article_tag} = CMS.create_article_tag(community, :drink, article_tag_attrs, user) + {:ok, article_tag2} = CMS.create_article_tag(community, :drink, article_tag_attrs2, user) + + {:ok, drink} = CMS.set_article_tag(:drink, drink.id, article_tag.id) + {:ok, drink} = CMS.set_article_tag(:drink, drink.id, article_tag2.id) + + {:ok, drink} = ORM.find(Drink, drink.id, preload: :article_tags) + assert exist_in?(article_tag, drink.article_tags) + assert exist_in?(article_tag2, drink.article_tags) + + {:ok, _} = CMS.delete_article_tag(article_tag.id) + + {:ok, drink} = ORM.find(Drink, drink.id, preload: :article_tags) + assert not exist_in?(article_tag, drink.article_tags) + assert exist_in?(article_tag2, drink.article_tags) + + {:ok, _} = CMS.delete_article_tag(article_tag2.id) + + {:ok, drink} = ORM.find(Drink, drink.id, preload: :article_tags) + assert not exist_in?(article_tag, drink.article_tags) + assert not exist_in?(article_tag2, drink.article_tags) + end + end + + describe "[create/update drink with tags]" do + test "can create drink with exsited article tags", + ~m(community user drink_attrs article_tag_attrs article_tag_attrs2)a do + {:ok, article_tag} = CMS.create_article_tag(community, :drink, article_tag_attrs, user) + {:ok, article_tag2} = CMS.create_article_tag(community, :drink, article_tag_attrs2, user) + + drink_with_tags = Map.merge(drink_attrs, %{article_tags: [article_tag.id, article_tag2.id]}) + + {:ok, created} = CMS.create_article(community, :drink, drink_with_tags, user) + {:ok, drink} = ORM.find(Drink, created.id, preload: :article_tags) + + assert exist_in?(article_tag, drink.article_tags) + assert exist_in?(article_tag2, drink.article_tags) + end + + test "can not create drink with other community's article tags", + ~m(community user drink_attrs article_tag_attrs article_tag_attrs2)a do + {:ok, community2} = db_insert(:community) + {:ok, article_tag} = CMS.create_article_tag(community, :drink, article_tag_attrs, user) + {:ok, article_tag2} = CMS.create_article_tag(community2, :drink, article_tag_attrs2, user) + + drink_with_tags = Map.merge(drink_attrs, %{article_tags: [article_tag.id, article_tag2.id]}) + + {:error, reason} = CMS.create_article(community, :drink, drink_with_tags, user) + is_error?(reason, :invalid_domain_tag) + end + end + + describe "[drink tag set /unset]" do + test "can set a tag ", ~m(community drink article_tag_attrs article_tag_attrs2 user)a do + {:ok, article_tag} = CMS.create_article_tag(community, :drink, article_tag_attrs, user) + {:ok, article_tag2} = CMS.create_article_tag(community, :drink, article_tag_attrs2, user) + + {:ok, drink} = CMS.set_article_tag(:drink, drink.id, article_tag.id) + assert drink.article_tags |> length == 1 + assert exist_in?(article_tag, drink.article_tags) + + {:ok, drink} = CMS.set_article_tag(:drink, drink.id, article_tag2.id) + assert drink.article_tags |> length == 2 + assert exist_in?(article_tag, drink.article_tags) + assert exist_in?(article_tag2, drink.article_tags) + + {:ok, drink} = CMS.unset_article_tag(:drink, drink.id, article_tag.id) + assert drink.article_tags |> length == 1 + assert not exist_in?(article_tag, drink.article_tags) + assert exist_in?(article_tag2, drink.article_tags) + + {:ok, drink} = CMS.unset_article_tag(:drink, drink.id, article_tag2.id) + assert drink.article_tags |> length == 0 + assert not exist_in?(article_tag, drink.article_tags) + assert not exist_in?(article_tag2, drink.article_tags) + end + end +end diff --git a/test/groupher_server/cms/articles/drink_pin_test.exs b/test/groupher_server/cms/articles/drink_pin_test.exs new file mode 100644 index 000000000..fab92c637 --- /dev/null +++ b/test/groupher_server/cms/articles/drink_pin_test.exs @@ -0,0 +1,54 @@ +defmodule GroupherServer.Test.CMS.Artilces.DrinkPin 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, drink} = CMS.create_article(community, :drink, mock_attrs(:drink), user) + + {:ok, ~m(user community drink)a} + end + + describe "[cms drink pin]" do + test "can pin a drink", ~m(community drink)a do + {:ok, _} = CMS.pin_article(:drink, drink.id, community.id) + {:ok, pind_article} = ORM.find_by(PinnedArticle, %{drink_id: drink.id}) + + assert pind_article.drink_id == drink.id + end + + test "one community & thread can only pin certern count of drink", ~m(community user)a do + Enum.reduce(1..@max_pinned_article_count_per_thread, [], fn _, acc -> + {:ok, new_drink} = CMS.create_article(community, :drink, mock_attrs(:drink), user) + {:ok, _} = CMS.pin_article(:drink, new_drink.id, community.id) + acc + end) + + {:ok, new_drink} = CMS.create_article(community, :drink, mock_attrs(:drink), user) + {:error, reason} = CMS.pin_article(:drink, new_drink.id, community.id) + assert reason |> Keyword.get(:code) == ecode(:too_much_pinned_article) + end + + test "can not pin a non-exsit drink", ~m(community)a do + assert {:error, _} = CMS.pin_article(:drink, 8848, community.id) + end + + test "can undo pin to a drink", ~m(community drink)a do + {:ok, _} = CMS.pin_article(:drink, drink.id, community.id) + + assert {:ok, unpinned} = CMS.undo_pin_article(:drink, drink.id, community.id) + + assert {:error, _} = ORM.find_by(PinnedArticle, %{drink_id: drink.id}) + end + end +end diff --git a/test/groupher_server/cms/articles/drink_test.exs b/test/groupher_server/cms/articles/drink_test.exs new file mode 100644 index 000000000..b8142d063 --- /dev/null +++ b/test/groupher_server/cms/articles/drink_test.exs @@ -0,0 +1,197 @@ +defmodule GroupherServer.Test.Articles.Drink do + use GroupherServer.TestTools + + alias GroupherServer.{CMS, Repo} + alias Helper.Converter.{EditorToHTML, HtmlSanitizer} + + alias EditorToHTML.{Class, Validator} + alias CMS.Model.{Author, Drink, Community, ArticleDocument, DrinkDocument} + 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) + + drink_attrs = mock_attrs(:drink, %{community_id: community.id}) + + {:ok, ~m(user user2 community drink_attrs)a} + end + + describe "[cms drinks curd]" do + test "can create drink with valid attrs", ~m(user community drink_attrs)a do + assert {:error, _} = ORM.find_by(Author, user_id: user.id) + {:ok, drink} = CMS.create_article(community, :drink, drink_attrs, user) + drink = Repo.preload(drink, :document) + + body_map = Jason.decode!(drink.document.body) + + assert drink.meta.thread == "DRINK" + + assert drink.title == drink_attrs.title + assert body_map |> Validator.is_valid() + + assert drink.document.body_html + |> String.contains?(~s(
List.first() |> get_in(["data", "text"]) + assert drink.digest == paragraph_text |> HtmlSanitizer.strip_all_tags() + end + + test "created drink should have a acitve_at field, same with inserted_at", + ~m(user community drink_attrs)a do + {:ok, drink} = CMS.create_article(community, :drink, drink_attrs, user) + + assert drink.active_at == drink.inserted_at + end + + test "read drink should update views and meta viewed_user_list", + ~m(drink_attrs community user user2)a do + {:ok, drink} = CMS.create_article(community, :drink, drink_attrs, user) + {:ok, _} = CMS.read_article(:drink, drink.id, user) + {:ok, _created} = ORM.find(Drink, drink.id) + + # same user duplicate case + {:ok, _} = CMS.read_article(:drink, drink.id, user) + {:ok, created} = ORM.find(Drink, drink.id) + + assert created.meta.viewed_user_ids |> length == 1 + assert user.id in created.meta.viewed_user_ids + + {:ok, _} = CMS.read_article(:drink, drink.id, user2) + {:ok, created} = ORM.find(Drink, drink.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 drink should contains viewer_has_xxx state", + ~m(drink_attrs community user user2)a do + {:ok, drink} = CMS.create_article(community, :drink, drink_attrs, user) + {:ok, drink} = CMS.read_article(:drink, drink.id, user) + + assert not drink.viewer_has_collected + assert not drink.viewer_has_upvoted + assert not drink.viewer_has_reported + + {:ok, drink} = CMS.read_article(:drink, drink.id) + + assert not drink.viewer_has_collected + assert not drink.viewer_has_upvoted + assert not drink.viewer_has_reported + + {:ok, drink} = CMS.read_article(:drink, drink.id, user2) + + assert not drink.viewer_has_collected + assert not drink.viewer_has_upvoted + assert not drink.viewer_has_reported + + {:ok, _} = CMS.upvote_article(:drink, drink.id, user) + {:ok, _} = CMS.collect_article(:drink, drink.id, user) + {:ok, _} = CMS.report_article(:drink, drink.id, "reason", "attr_info", user) + + {:ok, drink} = CMS.read_article(:drink, drink.id, user) + + assert drink.viewer_has_collected + assert drink.viewer_has_upvoted + assert drink.viewer_has_reported + end + + test "create drink with an exsit community fails", ~m(user)a do + invalid_attrs = mock_attrs(:drink, %{community_id: non_exsit_id()}) + ivalid_community = %Community{id: non_exsit_id()} + + assert {:error, _} = CMS.create_article(ivalid_community, :drink, invalid_attrs, user) + end + end + + describe "[cms drink sink/undo_sink]" do + test "if a drink is too old, read drink should update can_undo_sink flag", + ~m(user community drink_attrs)a do + {:ok, drink} = CMS.create_article(community, :drink, drink_attrs, user) + + assert drink.meta.can_undo_sink + + {:ok, drink_last_year} = db_insert(:drink, %{title: "last year", inserted_at: @last_year}) + {:ok, drink_last_year} = CMS.read_article(:drink, drink_last_year.id) + assert not drink_last_year.meta.can_undo_sink + + {:ok, drink_last_year} = CMS.read_article(:drink, drink_last_year.id, user) + assert not drink_last_year.meta.can_undo_sink + end + + test "can sink a drink", ~m(user community drink_attrs)a do + {:ok, drink} = CMS.create_article(community, :drink, drink_attrs, user) + assert not drink.meta.is_sinked + + {:ok, drink} = CMS.sink_article(:drink, drink.id) + assert drink.meta.is_sinked + assert drink.active_at == drink.inserted_at + end + + test "can undo sink drink", ~m(user community drink_attrs)a do + {:ok, drink} = CMS.create_article(community, :drink, drink_attrs, user) + {:ok, drink} = CMS.sink_article(:drink, drink.id) + assert drink.meta.is_sinked + assert drink.meta.last_active_at == drink.active_at + + {:ok, drink} = CMS.undo_sink_article(:drink, drink.id) + assert not drink.meta.is_sinked + assert drink.active_at == drink.meta.last_active_at + end + + test "can not undo sink to old drink", ~m()a do + {:ok, drink_last_year} = db_insert(:drink, %{title: "last year", inserted_at: @last_year}) + + {:error, reason} = CMS.undo_sink_article(:drink, drink_last_year.id) + is_error?(reason, :undo_sink_old_article) + end + end + + describe "[cms drink document]" do + test "will create related document after create", ~m(user community drink_attrs)a do + {:ok, drink} = CMS.create_article(community, :drink, drink_attrs, user) + {:ok, drink} = CMS.read_article(:drink, drink.id) + assert not is_nil(drink.document.body_html) + {:ok, drink} = CMS.read_article(:drink, drink.id, user) + assert not is_nil(drink.document.body_html) + + {:ok, article_doc} = ORM.find_by(ArticleDocument, %{article_id: drink.id, thread: "DRINK"}) + {:ok, drink_doc} = ORM.find_by(DrinkDocument, %{drink_id: drink.id}) + + assert drink.document.body == drink_doc.body + assert article_doc.body == drink_doc.body + end + + test "delete drink should also delete related document", ~m(user community drink_attrs)a do + {:ok, drink} = CMS.create_article(community, :drink, drink_attrs, user) + {:ok, _article_doc} = ORM.find_by(ArticleDocument, %{article_id: drink.id, thread: "DRINK"}) + {:ok, _drink_doc} = ORM.find_by(DrinkDocument, %{drink_id: drink.id}) + + {:ok, _} = CMS.delete_article(drink) + + {:error, _} = ORM.find(Drink, drink.id) + {:error, _} = ORM.find_by(ArticleDocument, %{article_id: drink.id, thread: "DRINK"}) + {:error, _} = ORM.find_by(DrinkDocument, %{drink_id: drink.id}) + end + + test "update drink should also update related document", ~m(user community drink_attrs)a do + {:ok, drink} = CMS.create_article(community, :drink, drink_attrs, user) + + body = mock_rich_text(~s(new content)) + {:ok, drink} = CMS.update_article(drink, %{body: body}) + + {:ok, article_doc} = ORM.find_by(ArticleDocument, %{article_id: drink.id, thread: "DRINK"}) + {:ok, drink_doc} = ORM.find_by(DrinkDocument, %{drink_id: drink.id}) + + assert String.contains?(drink_doc.body, "new content") + assert String.contains?(article_doc.body, "new content") + end + end +end diff --git a/test/groupher_server/cms/articles/guide_test.exs b/test/groupher_server/cms/articles/guide_test.exs index af8cad980..e0c01398b 100644 --- a/test/groupher_server/cms/articles/guide_test.exs +++ b/test/groupher_server/cms/articles/guide_test.exs @@ -169,7 +169,6 @@ defmodule GroupherServer.Test.Articles.Guide do assert article_doc.body == guide_doc.body end - @tag :wip test "delete guide should also delete related document", ~m(user community guide_attrs)a do {:ok, guide} = CMS.create_article(community, :guide, guide_attrs, user) {:ok, _article_doc} = ORM.find_by(ArticleDocument, %{article_id: guide.id, thread: "GUIDE"}) diff --git a/test/groupher_server/cms/articles/meetup_test.exs b/test/groupher_server/cms/articles/meetup_test.exs index 3ecad3f2b..83dcbfde2 100644 --- a/test/groupher_server/cms/articles/meetup_test.exs +++ b/test/groupher_server/cms/articles/meetup_test.exs @@ -171,7 +171,6 @@ defmodule GroupherServer.Test.Articles.Meetup do assert article_doc.body == meetup_doc.body end - @tag :wip test "delete meetup should also delete related document", ~m(user community meetup_attrs)a do {:ok, meetup} = CMS.create_article(community, :meetup, meetup_attrs, user) diff --git a/test/groupher_server/cms/articles/radar_test.exs b/test/groupher_server/cms/articles/radar_test.exs index b2aca4107..4dfbe7567 100644 --- a/test/groupher_server/cms/articles/radar_test.exs +++ b/test/groupher_server/cms/articles/radar_test.exs @@ -169,7 +169,6 @@ defmodule GroupherServer.Test.Articles.Radar do assert article_doc.body == radar_doc.body end - @tag :wip test "delete radar should also delete related document", ~m(user community radar_attrs)a do {:ok, radar} = CMS.create_article(community, :radar, radar_attrs, user) {:ok, _article_doc} = ORM.find_by(ArticleDocument, %{article_id: radar.id, thread: "RADAR"}) diff --git a/test/groupher_server/cms/articles/works_test.exs b/test/groupher_server/cms/articles/works_test.exs index 251c7f335..873b8e0fd 100644 --- a/test/groupher_server/cms/articles/works_test.exs +++ b/test/groupher_server/cms/articles/works_test.exs @@ -169,7 +169,6 @@ 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"}) diff --git a/test/groupher_server/cms/comments/drink_comment_emotions_test.exs b/test/groupher_server/cms/comments/drink_comment_emotions_test.exs new file mode 100644 index 000000000..757dbe27b --- /dev/null +++ b/test/groupher_server/cms/comments/drink_comment_emotions_test.exs @@ -0,0 +1,192 @@ +defmodule GroupherServer.Test.CMS.Comments.DrinkCommentEmotions 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, drink} = db_insert(:drink) + + {:ok, ~m(user user2 user3 drink)a} + end + + describe "[emotion in paged article comment]" do + test "login user should got viewer has emotioned status", ~m(drink 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(:drink, drink.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( + :drink, + drink.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(drink user)a do + {:ok, parent_comment} = CMS.create_comment(:drink, drink.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(drink user user2)a do + {:ok, parent_comment} = CMS.create_comment(:drink, drink.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(drink user user2)a do + {:ok, parent_comment} = CMS.create_comment(:drink, drink.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(drink user)a do + {:ok, parent_comment} = CMS.create_comment(:drink, drink.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(drink user)a do + {:ok, parent_comment} = CMS.create_comment(:drink, drink.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(drink user user2 user3)a do + {:ok, parent_comment} = CMS.create_comment(:drink, drink.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(drink user)a do + {:ok, parent_comment} = CMS.create_comment(:drink, drink.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/drink_comment_replies_test.exs b/test/groupher_server/cms/comments/drink_comment_replies_test.exs new file mode 100644 index 000000000..1c8f0130e --- /dev/null +++ b/test/groupher_server/cms/comments/drink_comment_replies_test.exs @@ -0,0 +1,204 @@ +defmodule GroupherServer.Test.CMS.Comments.DrinkCommentReplies do + @moduledoc false + + use GroupherServer.TestTools + + alias Helper.ORM + alias GroupherServer.CMS + + alias CMS.Model.{Comment, Drink} + + @max_parent_replies_count Comment.max_parent_replies_count() + + setup do + {:ok, user} = db_insert(:user) + {:ok, user2} = db_insert(:user) + {:ok, drink} = db_insert(:drink) + + {:ok, ~m(user user2 drink)a} + end + + describe "[basic article comment replies]" do + test "exsit comment can be reply", ~m(drink user user2)a do + {:ok, parent_comment} = CMS.create_comment(:drink, drink.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(drink user user2)a do + {:ok, parent_comment} = CMS.create_comment(:drink, drink.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(drink user user2)a do + {:ok, parent_comment} = CMS.create_comment(:drink, drink.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(drink user user2)a do + {:ok, parent_comment} = CMS.create_comment(:drink, drink.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(drink user user2)a do + {:ok, parent_comment} = CMS.create_comment(:drink, drink.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(drink user)a do + total_reply_count = @max_parent_replies_count + 1 + + {:ok, parent_comment} = CMS.create_comment(:drink, drink.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(drink user user2)a do + {:ok, parent_comment} = CMS.create_comment(:drink, drink.id, mock_comment(), user) + {:ok, _} = CMS.reply_comment(parent_comment.id, mock_comment(), user2) + + {:ok, article} = ORM.find(Drink, drink.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(drink user user2)a do + {:ok, parent_comment} = CMS.create_comment(:drink, drink.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(drink user)a do + {:ok, parent_comment} = CMS.create_comment(:drink, drink.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(drink user)a do + page_number = 1 + page_size = 10 + + {:ok, parent_comment} = CMS.create_comment(:drink, drink.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( + :drink, + drink.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/drink_comment_test.exs b/test/groupher_server/cms/comments/drink_comment_test.exs new file mode 100644 index 000000000..049f9315d --- /dev/null +++ b/test/groupher_server/cms/comments/drink_comment_test.exs @@ -0,0 +1,662 @@ +defmodule GroupherServer.Test.CMS.Comments.DrinkComment 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, Drink} + + @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, drink} = db_insert(:drink) + {:ok, community} = db_insert(:community) + + {:ok, ~m(community user user2 drink)a} + end + + describe "[basic article comment]" do + test "drink are supported by article comment.", ~m(user drink)a do + {:ok, drink_comment_1} = CMS.create_comment(:drink, drink.id, mock_comment(), user) + {:ok, drink_comment_2} = CMS.create_comment(:drink, drink.id, mock_comment(), user) + + {:ok, drink} = ORM.find(Drink, drink.id, preload: :comments) + + assert exist_in?(drink_comment_1, drink.comments) + assert exist_in?(drink_comment_2, drink.comments) + end + + test "comment should have default meta after create", ~m(user drink)a do + {:ok, comment} = CMS.create_comment(:drink, drink.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 drink", ~m(user drink)a do + Process.sleep(1000) + {:ok, _comment} = CMS.create_comment(:drink, drink.id, mock_comment(), user) + {:ok, drink} = ORM.find(Drink, drink.id, preload: :comments) + + assert not is_nil(drink.active_at) + assert drink.active_at > drink.inserted_at + end + + test "drink author create comment will not update active timestamp", ~m(community user)a do + drink_attrs = mock_attrs(:drink, %{community_id: community.id}) + {:ok, drink} = CMS.create_article(community, :drink, drink_attrs, user) + {:ok, drink} = ORM.find(Drink, drink.id, preload: [author: :user]) + + Process.sleep(1000) + + {:ok, _comment} = CMS.create_comment(:drink, drink.id, mock_comment(), drink.author.user) + + {:ok, drink} = ORM.find(Drink, drink.id, preload: :comments) + + assert not is_nil(drink.active_at) + assert drink.active_at == drink.inserted_at + end + + test "old drink will not update active after comment created", ~m(user)a do + active_period_days = Map.get(@active_period, :drink) + + inserted_at = + Timex.shift(Timex.now(), days: -(active_period_days - 1)) |> Timex.to_datetime() + + {:ok, drink} = db_insert(:drink, %{inserted_at: inserted_at}) + Process.sleep(1000) + {:ok, _comment} = CMS.create_comment(:drink, drink.id, mock_comment(), user) + {:ok, drink} = ORM.find(Drink, drink.id) + + assert drink.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, drink} = db_insert(:drink, %{inserted_at: inserted_at}) + Process.sleep(3000) + {:ok, _comment} = CMS.create_comment(:drink, drink.id, mock_comment(), user) + {:ok, drink} = ORM.find(Drink, drink.id) + + assert drink.active_at |> DateTime.to_unix() !== DateTime.utc_now() |> DateTime.to_unix() + end + + test "comment can be updated", ~m(drink user)a do + {:ok, comment} = CMS.create_comment(:drink, drink.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 drink)a do + {:ok, drink_comment} = CMS.create_comment(:drink, drink.id, mock_comment(), user) + {:ok, drink_comment2} = CMS.create_comment(:drink, drink.id, mock_comment(), user) + + {:ok, drink_comment} = ORM.find(Comment, drink_comment.id) + {:ok, drink_comment2} = ORM.find(Comment, drink_comment2.id) + + assert drink_comment.floor == 1 + assert drink_comment2.floor == 2 + end + end + + describe "[article comment participator for drink]" do + test "drink will have participator after comment created", ~m(user drink)a do + {:ok, _} = CMS.create_comment(:drink, drink.id, mock_comment(), user) + + {:ok, drink} = ORM.find(Drink, drink.id) + + participator = List.first(drink.comments_participants) + assert participator.id == user.id + end + + test "psot participator will not contains same user", ~m(user drink)a do + {:ok, _} = CMS.create_comment(:drink, drink.id, mock_comment(), user) + {:ok, _} = CMS.create_comment(:drink, drink.id, mock_comment(), user) + + {:ok, drink} = ORM.find(Drink, drink.id) + + assert 1 == length(drink.comments_participants) + end + + test "recent comment user should appear at first of the psot participants", + ~m(user user2 drink)a do + {:ok, _} = CMS.create_comment(:drink, drink.id, mock_comment(), user) + {:ok, _} = CMS.create_comment(:drink, drink.id, mock_comment(), user2) + + {:ok, drink} = ORM.find(Drink, drink.id) + + participator = List.first(drink.comments_participants) + + assert participator.id == user2.id + end + end + + describe "[article comment upvotes]" do + test "user can upvote a drink comment", ~m(user drink)a do + {:ok, comment} = CMS.create_comment(:drink, drink.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 drink comment will have flag", ~m(drink user)a do + {:ok, comment} = CMS.create_comment(:drink, drink.id, mock_comment(), user) + {:ok, author_user} = ORM.find(User, drink.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 drink comment will add id to upvoted_user_ids", ~m(drink user)a do + {:ok, comment} = CMS.create_comment(:drink, drink.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 drink comment will remove id from upvoted_user_ids", + ~m(drink user user2)a do + {:ok, comment} = CMS.create_comment(:drink, drink.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 drink)a do + {:ok, comment} = CMS.create_comment(:drink, drink.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 drink)a do + {:ok, comment} = CMS.create_comment(:drink, drink.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 drink comment", ~m(user drink)a do + {:ok, comment} = CMS.create_comment(:drink, drink.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 drink comment with no upvote", ~m(user drink)a do + {:ok, comment} = CMS.create_comment(:drink, drink.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 drink)a do + {:ok, comment} = CMS.create_comment(:drink, drink.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, drink} = ORM.find(Drink, drink.id) + assert drink.meta.folded_comment_count == 1 + end + + test "user can unfold a comment", ~m(user drink)a do + {:ok, comment} = CMS.create_comment(:drink, drink.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, drink} = ORM.find(Drink, drink.id) + assert drink.meta.folded_comment_count == 0 + end + end + + describe "[article comment pin/unpin]" do + test "user can pin a comment", ~m(user drink)a do + {:ok, comment} = CMS.create_comment(:drink, drink.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(%{drink_id: drink.id}) + assert pined_record.drink_id == drink.id + end + + test "user can unpin a comment", ~m(user drink)a do + {:ok, comment} = CMS.create_comment(:drink, drink.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 drink)a do + {:ok, comment} = CMS.create_comment(:drink, drink.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 drink)a do + # {:ok, comment} = CMS.create_comment(:drink, drink.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 drink)a do + # {:ok, comment} = CMS.create_comment(:drink, drink.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 drink)a do + {:ok, comment} = CMS.create_comment(:drink, drink.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 drink)a do + {:ok, comment} = CMS.create_comment(:drink, drink.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 drink)a do + {:ok, comment} = CMS.create_comment(:drink, drink.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 drink)a do + total_count = 30 + page_size = 10 + thread = :drink + + Enum.reduce(1..total_count, [], fn _, acc -> + {:ok, new_user} = db_insert(:user) + {:ok, comment} = CMS.create_comment(:drink, drink.id, mock_comment(), new_user) + + acc ++ [comment] + end) + + {:ok, _comment} = CMS.create_comment(:drink, drink.id, mock_comment(), user) + {:ok, _comment} = CMS.create_comment(:drink, drink.id, mock_comment(), user) + + {:ok, results} = + CMS.paged_comments_participants(thread, drink.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 drink)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(:drink, drink.id, mock_comment(), user) + + acc ++ [comment] + end) + + {:ok, paged_comments} = + CMS.paged_comments( + :drink, + drink.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 drink)a do + total_count = 20 + page_number = 1 + page_size = 5 + + Enum.reduce(1..total_count, [], fn _, acc -> + {:ok, comment} = CMS.create_comment(:drink, drink.id, mock_comment(), user) + + acc ++ [comment] + end) + + {:ok, random_comment_1} = CMS.create_comment(:drink, drink.id, mock_comment(), user) + {:ok, random_comment_2} = CMS.create_comment(:drink, drink.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( + :drink, + drink.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 drink)a do + total_count = 20 + page_number = 2 + page_size = 5 + + Enum.reduce(1..total_count, [], fn _, acc -> + {:ok, comment} = CMS.create_comment(:drink, drink.id, mock_comment(), user) + + acc ++ [comment] + end) + + {:ok, random_comment_1} = CMS.create_comment(:drink, drink.id, mock_comment(), user) + {:ok, random_comment_2} = CMS.create_comment(:drink, drink.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( + :drink, + drink.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 drink)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(:drink, drink.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( + :drink, + drink.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 drink)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(:drink, drink.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(:drink, drink.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 drink)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(:drink, drink.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( + :drink, + drink.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 drink)a do + {:ok, _comment} = CMS.create_comment(:drink, drink.id, mock_comment(), user) + {:ok, _comment} = CMS.create_comment(:drink, drink.id, mock_comment(), user) + {:ok, comment} = CMS.create_comment(:drink, drink.id, mock_comment(), user) + {:ok, _comment} = CMS.create_comment(:drink, drink.id, mock_comment(), user) + {:ok, _comment} = CMS.create_comment(:drink, drink.id, mock_comment(), user) + + {:ok, drink} = ORM.find(Drink, drink.id) + + assert drink.comments_count == 5 + + {:ok, _} = CMS.delete_comment(comment) + + {:ok, drink} = ORM.find(Drink, drink.id) + assert drink.comments_count == 4 + end + + test "delete comment still delete pinned record if needed", ~m(user drink)a do + total_count = 10 + + all_comments = + Enum.reduce(1..total_count, [], fn _, acc -> + {:ok, comment} = CMS.create_comment(:drink, drink.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 drink)a do + {:ok, comment} = CMS.create_comment(:drink, drink.id, mock_comment(), user) + assert not comment.is_article_author + + author_user = drink.author.user + {:ok, comment} = CMS.create_comment(:drink, drink.id, mock_comment(), author_user) + assert comment.is_article_author + end + end + + describe "[lock/unlock drink comment]" do + test "locked drink can not be comment", ~m(user drink)a do + {:ok, _} = CMS.create_comment(:drink, drink.id, mock_comment(), user) + {:ok, _} = CMS.lock_article_comments(:drink, drink.id) + + {:error, reason} = CMS.create_comment(:drink, drink.id, mock_comment(), user) + assert reason |> is_error?(:article_comments_locked) + + {:ok, _} = CMS.undo_lock_article_comments(:drink, drink.id) + {:ok, _} = CMS.create_comment(:drink, drink.id, mock_comment(), user) + end + + test "locked drink can not by reply", ~m(user drink)a do + {:ok, parent_comment} = CMS.create_comment(:drink, drink.id, mock_comment(), user) + {:ok, _} = CMS.reply_comment(parent_comment.id, mock_comment(), user) + + {:ok, _} = CMS.lock_article_comments(:drink, drink.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(:drink, drink.id) + {:ok, _} = CMS.reply_comment(parent_comment.id, mock_comment(), user) + end + end +end diff --git a/test/groupher_server/cms/emotions/drink_emotions_test.exs b/test/groupher_server/cms/emotions/drink_emotions_test.exs new file mode 100644 index 000000000..fe661a2f3 --- /dev/null +++ b/test/groupher_server/cms/emotions/drink_emotions_test.exs @@ -0,0 +1,178 @@ +defmodule GroupherServer.Test.CMS.Emotions.DrinkEmotions do + @moduledoc false + + use GroupherServer.TestTools + + alias Helper.ORM + alias GroupherServer.CMS + + alias CMS.Model.{Drink, 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) + + drink_attrs = mock_attrs(:drink, %{community_id: community.id}) + + {:ok, ~m(user user2 user3 community drink_attrs)a} + end + + describe "[emotion in paged drinks]" do + test "login user should got viewer has emotioned status", + ~m(community drink_attrs user)a do + total_count = 10 + page_number = 10 + page_size = 20 + + all_drinks = + Enum.reduce(0..total_count, [], fn _, acc -> + {:ok, drink} = CMS.create_article(community, :drink, drink_attrs, user) + acc ++ [drink] + end) + + random_drink = all_drinks |> Enum.at(3) + + {:ok, _} = CMS.emotion_to_article(:drink, random_drink.id, :downvote, user) + {:ok, _} = CMS.emotion_to_article(:drink, random_drink.id, :beer, user) + {:ok, _} = CMS.emotion_to_article(:drink, random_drink.id, :popcorn, user) + + {:ok, paged_articles} = + CMS.paged_articles(:drink, %{page: page_number, size: page_size}, user) + + target = Enum.find(paged_articles.entries, &(&1.id == random_drink.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 "drink has default emotions after created", ~m(community drink_attrs user)a do + {:ok, drink} = CMS.create_article(community, :drink, drink_attrs, user) + + emotions = drink.emotions |> Map.from_struct() |> Map.delete(:id) + assert @default_emotions == emotions + end + + test "can make emotion to drink", ~m(community drink_attrs user user2)a do + {:ok, drink} = CMS.create_article(community, :drink, drink_attrs, user) + + {:ok, _} = CMS.emotion_to_article(:drink, drink.id, :downvote, user) + {:ok, _} = CMS.emotion_to_article(:drink, drink.id, :downvote, user2) + + {:ok, %{emotions: emotions}} = ORM.find(Drink, drink.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 drink", ~m(community drink_attrs user user2)a do + {:ok, drink} = CMS.create_article(community, :drink, drink_attrs, user) + + {:ok, _} = CMS.emotion_to_article(:drink, drink.id, :downvote, user) + {:ok, _} = CMS.emotion_to_article(:drink, drink.id, :downvote, user2) + + {:ok, _} = CMS.undo_emotion_to_article(:drink, drink.id, :downvote, user) + {:ok, _} = CMS.undo_emotion_to_article(:drink, drink.id, :downvote, user2) + + {:ok, %{emotions: emotions}} = ORM.find(Drink, drink.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 drink.", ~m(community drink_attrs user)a do + {:ok, drink} = CMS.create_article(community, :drink, drink_attrs, user) + + {:ok, _} = CMS.emotion_to_article(:drink, drink.id, :downvote, user) + {:ok, _} = CMS.emotion_to_article(:drink, drink.id, :downvote, user) + + {:ok, drink} = ORM.find(Drink, drink.id) + + assert drink.emotions.downvote_count == 1 + assert user_exist_in?(user, drink.emotions.latest_downvote_users) + end + + test "same user same emotion to same drink only have one user_emotion record", + ~m(community drink_attrs user)a do + {:ok, drink} = CMS.create_article(community, :drink, drink_attrs, user) + + {:ok, _} = CMS.emotion_to_article(:drink, drink.id, :downvote, user) + {:ok, _} = CMS.emotion_to_article(:drink, drink.id, :heart, user) + + {:ok, drink} = ORM.find(Drink, drink.id) + + {:ok, records} = ORM.find_all(ArticleUserEmotion, %{page: 1, size: 10}) + assert records.total_count == 1 + + {:ok, record} = ORM.find_by(ArticleUserEmotion, %{drink_id: drink.id, user_id: user.id}) + assert record.downvote + assert record.heart + end + + test "different user can make same emotions on same drink", + ~m(community drink_attrs user user2 user3)a do + {:ok, drink} = CMS.create_article(community, :drink, drink_attrs, user) + + {:ok, _} = CMS.emotion_to_article(:drink, drink.id, :beer, user) + {:ok, _} = CMS.emotion_to_article(:drink, drink.id, :beer, user2) + {:ok, _} = CMS.emotion_to_article(:drink, drink.id, :beer, user3) + + {:ok, %{emotions: emotions}} = ORM.find(Drink, drink.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 drink", + ~m(community drink_attrs user)a do + {:ok, drink} = CMS.create_article(community, :drink, drink_attrs, user) + + {:ok, _} = CMS.emotion_to_article(:drink, drink.id, :downvote, user) + {:ok, _} = CMS.emotion_to_article(:drink, drink.id, :downvote, user) + {:ok, _} = CMS.emotion_to_article(:drink, drink.id, :beer, user) + {:ok, _} = CMS.emotion_to_article(:drink, drink.id, :heart, user) + {:ok, _} = CMS.emotion_to_article(:drink, drink.id, :orz, user) + + {:ok, %{emotions: emotions}} = ORM.find(Drink, drink.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_drink_test.exs b/test/groupher_server/cms/hooks/cite_drink_test.exs new file mode 100644 index 000000000..99833a862 --- /dev/null +++ b/test/groupher_server/cms/hooks/cite_drink_test.exs @@ -0,0 +1,230 @@ +defmodule GroupherServer.Test.CMS.Hooks.CiteDrink do + use GroupherServer.TestTools + + import Helper.Utils, only: [get_config: 2] + + alias Helper.ORM + alias GroupherServer.CMS + + alias CMS.Model.{Drink, 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, drink} = db_insert(:drink) + {:ok, drink2} = db_insert(:drink) + {:ok, drink3} = db_insert(:drink) + {:ok, drink4} = db_insert(:drink) + {:ok, drink5} = db_insert(:drink) + + {:ok, community} = db_insert(:community) + + drink_attrs = mock_attrs(:drink, %{community_id: community.id}) + + {:ok, ~m(user user2 community drink drink2 drink3 drink4 drink5 drink_attrs)a} + end + + describe "[cite basic]" do + test "cited multi drink should work", + ~m(user community drink2 drink3 drink4 drink5 drink_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) + ) + + drink_attrs = drink_attrs |> Map.merge(%{body: body}) + {:ok, drink} = CMS.create_article(community, :drink, drink_attrs, user) + + body = mock_rich_text(~s(the )) + drink_attrs = drink_attrs |> Map.merge(%{body: body}) + {:ok, drink_n} = CMS.create_article(community, :drink, drink_attrs, user) + + Hooks.Cite.handle(drink) + Hooks.Cite.handle(drink_n) + + {:ok, drink2} = ORM.find(Drink, drink2.id) + {:ok, drink3} = ORM.find(Drink, drink3.id) + {:ok, drink4} = ORM.find(Drink, drink4.id) + {:ok, drink5} = ORM.find(Drink, drink5.id) + + assert drink2.meta.citing_count == 1 + assert drink3.meta.citing_count == 2 + assert drink4.meta.citing_count == 1 + assert drink5.meta.citing_count == 1 + end + + test "cited drink itself should not work", ~m(user community drink_attrs)a do + {:ok, drink} = CMS.create_article(community, :drink, drink_attrs, user) + + body = mock_rich_text(~s(the )) + {:ok, drink} = CMS.update_article(drink, %{body: body}) + + Hooks.Cite.handle(drink) + + {:ok, drink} = ORM.find(Drink, drink.id) + assert drink.meta.citing_count == 0 + end + + test "cited comment itself should not work", ~m(user drink)a do + {:ok, cited_comment} = CMS.create_comment(:drink, drink.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 drink's comment in drink", ~m(community user drink drink2 drink_attrs)a do + {:ok, comment} = CMS.create_comment(:drink, drink.id, mock_rich_text("hello"), user) + + body = + mock_rich_text( + ~s(the ) + ) + + drink_attrs = drink_attrs |> Map.merge(%{body: body}) + + {:ok, drink} = CMS.create_article(community, :drink, drink_attrs, user) + Hooks.Cite.handle(drink) + + {: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}) + + # 被 drink 以 comment link 的方式引用了 + assert cited_content.drink_id == drink.id + assert cited_content.cited_by_type == "COMMENT" + end + + test "can cite a comment in a comment", ~m(user drink)a do + {:ok, cited_comment} = CMS.create_comment(:drink, drink.id, mock_rich_text("hello"), user) + + comment_body = + mock_rich_text( + ~s(the ) + ) + + {:ok, comment} = CMS.create_comment(:drink, drink.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 drink inside a comment", ~m(user drink drink2 drink3 drink4 drink5)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(:drink, drink.id, comment_body, user) + Hooks.Cite.handle(comment) + + comment_body = mock_rich_text(~s(the )) + {:ok, comment} = CMS.create_comment(:drink, drink.id, comment_body, user) + + Hooks.Cite.handle(comment) + + {:ok, drink2} = ORM.find(Drink, drink2.id) + {:ok, drink3} = ORM.find(Drink, drink3.id) + {:ok, drink4} = ORM.find(Drink, drink4.id) + {:ok, drink5} = ORM.find(Drink, drink5.id) + + assert drink2.meta.citing_count == 1 + assert drink3.meta.citing_count == 2 + assert drink4.meta.citing_count == 1 + assert drink5.meta.citing_count == 1 + end + end + + describe "[cite pagi]" do + test "can get paged cited articles.", ~m(user community drink2 drink_attrs)a do + {:ok, comment} = + CMS.create_comment( + :drink, + drink2.id, + mock_comment(~s(the )), + user + ) + + Process.sleep(1000) + + body = + mock_rich_text( + ~s(the ), + ~s(the ) + ) + + drink_attrs = drink_attrs |> Map.merge(%{body: body}) + {:ok, drink_x} = CMS.create_article(community, :drink, drink_attrs, user) + + Process.sleep(1000) + body = mock_rich_text(~s(the )) + drink_attrs = drink_attrs |> Map.merge(%{body: body}) + {:ok, drink_y} = CMS.create_article(community, :drink, drink_attrs, user) + + Hooks.Cite.handle(drink_x) + Hooks.Cite.handle(comment) + Hooks.Cite.handle(drink_y) + + {:ok, result} = CMS.paged_citing_contents("DRINK", drink2.id, %{page: 1, size: 10}) + + entries = result.entries + + result_comment = entries |> List.first() + result_drink_x = entries |> Enum.at(1) + result_drink_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 == drink2.id + assert result_comment.title == drink2.title + + assert result_drink_x.id == drink_x.id + assert result_drink_x.block_linker |> length == 2 + assert result_drink_x |> Map.keys() == article_map_keys + + assert result_drink_y.id == drink_y.id + assert result_drink_y.block_linker |> length == 1 + assert result_drink_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_drink_test.exs b/test/groupher_server/cms/hooks/mention_in_drink_test.exs new file mode 100644 index 000000000..29b14ee86 --- /dev/null +++ b/test/groupher_server/cms/hooks/mention_in_drink_test.exs @@ -0,0 +1,98 @@ +defmodule GroupherServer.Test.CMS.Hooks.MentionInDrink 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, drink} = db_insert(:drink) + + {:ok, community} = db_insert(:community) + + drink_attrs = mock_attrs(:drink, %{community_id: community.id}) + + {:ok, ~m(user user2 user3 community drink drink_attrs)a} + end + + describe "[mention in drink basic]" do + test "mention multi user in drink should work", + ~m(user user2 user3 community drink_attrs)a do + body = + mock_rich_text( + ~s(hiString.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(drink user user_conn)a do + {:ok, comment} = CMS.create_comment(:drink, drink.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(drink user guest_conn user_conn owner_conn)a do + {:ok, comment} = CMS.create_comment(:drink, drink.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(drink user guest_conn user_conn owner_conn)a do + {:ok, comment} = CMS.create_comment(:drink, drink.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 drink comment", ~m(drink user guest_conn user_conn)a do + {:ok, comment} = CMS.create_comment(:drink, drink.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 drink comment", + ~m(drink user guest_conn user_conn)a do + {:ok, comment} = CMS.create_comment(:drink, drink.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(drink user user_conn)a do + {:ok, comment} = CMS.create_comment(:drink, drink.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(drink user owner_conn)a do + {:ok, comment} = CMS.create_comment(:drink, drink.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!){ + lockDrinkComment(id: $id, communityId: $communityId) { + id + } + } + """ + + test "can lock a drink's comment", ~m(community drink)a do + variables = %{id: drink.id, communityId: community.id} + passport_rules = %{community.raw => %{"drink.lock_comment" => true}} + rule_conn = simu_conn(:user, cms: passport_rules) + + result = rule_conn |> mutation_result(@query, variables, "lockDrinkComment") + assert result["id"] == to_string(drink.id) + + {:ok, drink} = ORM.find(Drink, drink.id) + assert drink.meta.is_comment_locked + end + + test "unauth user fails", ~m(guest_conn community drink)a do + variables = %{id: drink.id, communityId: community.id} + + assert guest_conn |> mutation_get_error?(@query, variables, ecode(:account_login)) + end + + @query """ + mutation($id: ID!, $communityId: ID!){ + undoLockDrinkComment(id: $id, communityId: $communityId) { + id + } + } + """ + + test "can undo lock a drink's comment", ~m(community drink)a do + {:ok, _} = CMS.lock_article_comments(:drink, drink.id) + {:ok, drink} = ORM.find(Drink, drink.id) + assert drink.meta.is_comment_locked + + variables = %{id: drink.id, communityId: community.id} + passport_rules = %{community.raw => %{"drink.undo_lock_comment" => true}} + rule_conn = simu_conn(:user, cms: passport_rules) + + result = rule_conn |> mutation_result(@query, variables, "undoLockDrinkComment") + assert result["id"] == to_string(drink.id) + + {:ok, drink} = ORM.find(Drink, drink.id) + assert not drink.meta.is_comment_locked + end + + test "unauth user undo fails", ~m(guest_conn community drink)a do + variables = %{id: drink.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 drink's comment", ~m(owner_conn drink user)a do + {:ok, comment} = CMS.create_comment(:drink, drink.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 drink user)a do + {:ok, comment} = CMS.create_comment(:drink, drink.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 drink's comment", ~m(owner_conn drink user)a do + {:ok, comment} = CMS.create_comment(:drink, drink.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 drink user)a do + {:ok, comment} = CMS.create_comment(:drink, drink.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/drink_flag_test.exs b/test/groupher_server_web/mutation/cms/flags/drink_flag_test.exs new file mode 100644 index 000000000..f7b603b2d --- /dev/null +++ b/test/groupher_server_web/mutation/cms/flags/drink_flag_test.exs @@ -0,0 +1,178 @@ +defmodule GroupherServer.Test.Mutation.Flags.DrinkFlag 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, drink} = CMS.create_article(community, :drink, mock_attrs(:drink), 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 drink)a} + end + + describe "[mutation drink flag curd]" do + @query """ + mutation($id: ID!){ + markDeleteDrink(id: $id) { + id + markDelete + } + } + """ + test "auth user can markDelete drink", ~m(drink)a do + variables = %{id: drink.id} + + passport_rules = %{"drink.mark_delete" => true} + rule_conn = simu_conn(:user, cms: passport_rules) + + updated = rule_conn |> mutation_result(@query, variables, "markDeleteDrink") + + assert updated["id"] == to_string(drink.id) + assert updated["markDelete"] == true + end + + test "mark delete drink should update drink'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, drink} = CMS.create_article(community, :drink, mock_attrs(:drink), user) + + {:ok, community} = ORM.find(Community, community.id) + assert community.meta.drinks_count == 1 + + variables = %{id: drink.id} + passport_rules = %{"drink.mark_delete" => true} + rule_conn = simu_conn(:user, cms: passport_rules) + + rule_conn |> mutation_result(@query, variables, "markDeleteDrink") + + {:ok, community} = ORM.find(Community, community.id) + assert community.meta.drinks_count == 0 + end + + test "unauth user markDelete drink fails", ~m(user_conn guest_conn drink)a do + variables = %{id: drink.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!){ + undoMarkDeleteDrink(id: $id) { + id + markDelete + } + } + """ + test "auth user can undo markDelete drink", ~m(drink)a do + variables = %{id: drink.id} + + {:ok, _} = CMS.mark_delete_article(:drink, drink.id) + + passport_rules = %{"drink.undo_mark_delete" => true} + rule_conn = simu_conn(:user, cms: passport_rules) + + updated = rule_conn |> mutation_result(@query, variables, "undoMarkDeleteDrink") + + assert updated["id"] == to_string(drink.id) + assert updated["markDelete"] == false + end + + test "undo mark delete drink should update drink'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, drink} = CMS.create_article(community, :drink, mock_attrs(:drink), user) + + {:ok, _} = CMS.mark_delete_article(:drink, drink.id) + + {:ok, community} = ORM.find(Community, community.id) + assert community.meta.drinks_count == 0 + + variables = %{id: drink.id} + passport_rules = %{"drink.undo_mark_delete" => true} + rule_conn = simu_conn(:user, cms: passport_rules) + rule_conn |> mutation_result(@query, variables, "undoMarkDeleteDrink") + + {:ok, community} = ORM.find(Community, community.id) + assert community.meta.drinks_count == 1 + end + + test "unauth user undo markDelete drink fails", ~m(user_conn guest_conn drink)a do + variables = %{id: drink.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!){ + pinDrink(id: $id, communityId: $communityId) { + id + } + } + """ + + test "auth user can pin drink", ~m(community drink)a do + variables = %{id: drink.id, communityId: community.id} + + passport_rules = %{community.raw => %{"drink.pin" => true}} + rule_conn = simu_conn(:user, cms: passport_rules) + + updated = rule_conn |> mutation_result(@query, variables, "pinDrink") + + assert updated["id"] == to_string(drink.id) + end + + test "unauth user pin drink fails", ~m(user_conn guest_conn community drink)a do + variables = %{id: drink.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!){ + undoPinDrink(id: $id, communityId: $communityId) { + id + isPinned + } + } + """ + + test "auth user can undo pin drink", ~m(community drink)a do + variables = %{id: drink.id, communityId: community.id} + + passport_rules = %{community.raw => %{"drink.undo_pin" => true}} + rule_conn = simu_conn(:user, cms: passport_rules) + + CMS.pin_article(:drink, drink.id, community.id) + updated = rule_conn |> mutation_result(@query, variables, "undoPinDrink") + + assert updated["id"] == to_string(drink.id) + end + + test "unauth user undo pin drink fails", ~m(user_conn guest_conn community drink)a do + variables = %{id: drink.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/publish_throttle_test.exs b/test/groupher_server_web/mutation/cms/publish_throttle_test.exs index 99c3e9a93..85af194a3 100644 --- a/test/groupher_server_web/mutation/cms/publish_throttle_test.exs +++ b/test/groupher_server_web/mutation/cms/publish_throttle_test.exs @@ -24,14 +24,12 @@ defmodule GroupherServer.Test.Mutation.PublishThrottle do $title: String! $body: String! $digest: String! - $length: Int! $communityId: ID! ) { createPost( title: $title body: $body digest: $digest - length: $length communityId: $communityId ) { title diff --git a/test/groupher_server_web/mutation/cms/sink/drink_sink_test.exs b/test/groupher_server_web/mutation/cms/sink/drink_sink_test.exs new file mode 100644 index 000000000..358516d09 --- /dev/null +++ b/test/groupher_server_web/mutation/cms/sink/drink_sink_test.exs @@ -0,0 +1,79 @@ +defmodule GroupherServer.Test.Mutation.Sink.DrinkSink do + @moduledoc false + use GroupherServer.TestTools + + alias GroupherServer.CMS + alias CMS.Model.Drink + + alias Helper.ORM + + setup do + {:ok, user} = db_insert(:user) + {:ok, community} = db_insert(:community) + {:ok, drink} = CMS.create_article(community, :drink, mock_attrs(:drink), user) + + guest_conn = simu_conn(:guest) + user_conn = simu_conn(:user, user) + + {:ok, ~m(user_conn guest_conn community drink user)a} + end + + describe "[drink sink]" do + @query """ + mutation($id: ID!, $communityId: ID!){ + sinkDrink(id: $id, communityId: $communityId) { + id + } + } + """ + + test "login user can sink a drink", ~m(community drink)a do + variables = %{id: drink.id, communityId: community.id} + passport_rules = %{community.raw => %{"drink.sink" => true}} + rule_conn = simu_conn(:user, cms: passport_rules) + + result = rule_conn |> mutation_result(@query, variables, "sinkDrink") + assert result["id"] == to_string(drink.id) + + {:ok, drink} = ORM.find(Drink, drink.id) + assert drink.meta.is_sinked + assert drink.active_at == drink.inserted_at + end + + test "unauth user sink a drink fails", ~m(guest_conn community drink)a do + variables = %{id: drink.id, communityId: community.id} + + assert guest_conn |> mutation_get_error?(@query, variables, ecode(:account_login)) + end + + @query """ + mutation($id: ID!, $communityId: ID!){ + undoSinkDrink(id: $id, communityId: $communityId) { + id + } + } + """ + + test "login user can undo sink to a drink", ~m(community drink)a do + variables = %{id: drink.id, communityId: community.id} + + passport_rules = %{community.raw => %{"drink.undo_sink" => true}} + rule_conn = simu_conn(:user, cms: passport_rules) + + {:ok, _} = CMS.sink_article(:drink, drink.id) + updated = rule_conn |> mutation_result(@query, variables, "undoSinkDrink") + assert updated["id"] == to_string(drink.id) + + {:ok, drink} = ORM.find(Drink, drink.id) + assert not drink.meta.is_sinked + end + + :wip2 + + test "unauth user undo sink a drink fails", ~m(guest_conn community drink)a do + variables = %{id: drink.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/drink_upvote_test.exs b/test/groupher_server_web/mutation/cms/upvotes/drink_upvote_test.exs new file mode 100644 index 000000000..31bc71433 --- /dev/null +++ b/test/groupher_server_web/mutation/cms/upvotes/drink_upvote_test.exs @@ -0,0 +1,64 @@ +defmodule GroupherServer.Test.Mutation.Upvotes.DrinkUpvote do + @moduledoc false + use GroupherServer.TestTools + + alias GroupherServer.CMS + + setup do + {:ok, drink} = db_insert(:drink) + {:ok, user} = db_insert(:user) + + guest_conn = simu_conn(:guest) + user_conn = simu_conn(:user, user) + + {:ok, ~m(user_conn guest_conn drink user)a} + end + + describe "[drink upvote]" do + @query """ + mutation($id: ID!) { + upvoteDrink(id: $id) { + id + } + } + """ + + test "login user can upvote a drink", ~m(user_conn drink)a do + variables = %{id: drink.id} + created = user_conn |> mutation_result(@query, variables, "upvoteDrink") + + assert created["id"] == to_string(drink.id) + end + + test "unauth user upvote a drink fails", ~m(guest_conn drink)a do + variables = %{id: drink.id} + + assert guest_conn + |> mutation_get_error?(@query, variables, ecode(:account_login)) + end + + @query """ + mutation($id: ID!) { + undoUpvoteDrink(id: $id) { + id + } + } + """ + + test "login user can undo upvote to a drink", ~m(user_conn drink user)a do + {:ok, _} = CMS.upvote_article(:drink, drink.id, user) + + variables = %{id: drink.id} + updated = user_conn |> mutation_result(@query, variables, "undoUpvoteDrink") + + assert updated["id"] == to_string(drink.id) + end + + test "unauth user undo upvote a drink fails", ~m(guest_conn drink)a do + variables = %{id: drink.id} + + assert guest_conn + |> mutation_get_error?(@query, variables, ecode(:account_login)) + end + end +end diff --git a/test/groupher_server_web/mutation/statistics/statistics_test.exs b/test/groupher_server_web/mutation/statistics/statistics_test.exs index d4ec757d5..5e1c696c1 100644 --- a/test/groupher_server_web/mutation/statistics/statistics_test.exs +++ b/test/groupher_server_web/mutation/statistics/statistics_test.exs @@ -22,7 +22,6 @@ defmodule GroupherServer.Test.Mutation.Statistics do $title: String! $body: String! $digest: String! - $length: Int! $communityId: ID! $articleTags: [Ids] ) { @@ -30,7 +29,6 @@ defmodule GroupherServer.Test.Mutation.Statistics do title: $title body: $body digest: $digest - length: $length communityId: $communityId articleTags: $articleTags ) { @@ -65,7 +63,6 @@ defmodule GroupherServer.Test.Mutation.Statistics do $title: String!, $body: String!, $digest: String!, - $length: Int!, $communityId: ID!, $company: String!, $articleTags: [Ids] @@ -74,7 +71,6 @@ defmodule GroupherServer.Test.Mutation.Statistics do title: $title, body: $body, digest: $digest, - length: $length, communityId: $communityId, company: $company, articleTags: $articleTags diff --git a/test/groupher_server_web/query/accounts/published/published_drinks_test.exs b/test/groupher_server_web/query/accounts/published/published_drinks_test.exs new file mode 100644 index 000000000..fcd6416ce --- /dev/null +++ b/test/groupher_server_web/query/accounts/published/published_drinks_test.exs @@ -0,0 +1,102 @@ +defmodule GroupherServer.Test.Query.Accounts.Published.Drinks do + use GroupherServer.TestTools + + alias GroupherServer.CMS + + @publish_count 10 + + setup do + {:ok, user} = db_insert(:user) + {:ok, drink} = db_insert(:drink) + {:ok, community} = db_insert(:community) + + guest_conn = simu_conn(:guest) + user_conn = simu_conn(:user, user) + + {:ok, ~m(guest_conn user_conn community drink user)a} + end + + describe "[published drinks]" do + @query """ + query($login: String!, $filter: PagedFilter!) { + pagedPublishedDrinks(login: $login, filter: $filter) { + entries { + id + title + author { + id + } + } + totalPages + totalCount + pageSize + pageNumber + } + } + """ + + test "can get published drinks", ~m(guest_conn community user)a do + drink_attrs = mock_attrs(:drink, %{community_id: community.id}) + + {:ok, drink} = CMS.create_article(community, :drink, drink_attrs, user) + {:ok, drink2} = CMS.create_article(community, :drink, drink_attrs, user) + + variables = %{login: user.login, filter: %{page: 1, size: 20}} + results = guest_conn |> query_result(@query, variables, "pagedPublishedDrinks") + + assert results["entries"] |> Enum.any?(&(&1["id"] == to_string(drink.id))) + assert results["entries"] |> Enum.any?(&(&1["id"] == to_string(drink2.id))) + end + end + + describe "[account published comments on drink]" 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 drink", ~m(guest_conn user drink)a do + pub_comments = + Enum.reduce(1..@publish_count, [], fn _, acc -> + {:ok, comment} = CMS.create_comment(:drink, drink.id, mock_comment(), user) + acc ++ [comment] + end) + + random_comment_id = pub_comments |> Enum.random() |> Map.get(:id) |> to_string + + variables = %{login: user.login, thread: "DRINK", 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(drink.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/comments/drink_comment_test.exs b/test/groupher_server_web/query/cms/comments/drink_comment_test.exs new file mode 100644 index 000000000..8abd377d1 --- /dev/null +++ b/test/groupher_server_web/query/cms/comments/drink_comment_test.exs @@ -0,0 +1,666 @@ +defmodule GroupherServer.Test.Query.Comments.DrinkComment do + @moduledoc false + + use GroupherServer.TestTools + + alias GroupherServer.CMS + + setup do + {:ok, drink} = db_insert(:drink) + {:ok, user} = db_insert(:user) + {:ok, user2} = db_insert(:user) + + guest_conn = simu_conn(:guest) + user_conn = simu_conn(:user, user) + + {:ok, ~m(user_conn guest_conn drink user user2)a} + end + + describe "[baisc article drink comment]" do + @query """ + query($id: ID!) { + drink(id: $id) { + id + title + commentsParticipants { + id + nickname + } + commentsParticipantsCount + } + } + """ + + test "guest user can get comment participants after comment created", + ~m(guest_conn drink user user2)a do + total_count = 5 + thread = :drink + + Enum.reduce(1..total_count, [], fn _, acc -> + {:ok, comment} = CMS.create_comment(thread, drink.id, mock_comment(), user) + + acc ++ [comment] + end) + + {:ok, _} = CMS.create_comment(thread, drink.id, mock_comment(), user2) + + variables = %{id: drink.id} + results = guest_conn |> query_result(@query, variables, "drink") + + comments_participants = results["commentsParticipants"] + comments_participants_count = results["commentsParticipantsCount"] + + assert is_list(comments_participants) + assert length(comments_participants) == 2 + assert comments_participants_count == 2 + end + + @query """ + query($id: ID!, $thread: Thread, $mode: CommentsMode, $filter: CommentsFilter!) { + pagedComments(id: $id, thread: $thread, mode: $mode, filter: $filter) { + entries { + id + bodyHtml + author { + id + nickname + } + isPinned + floor + upvotesCount + + emotions { + downvoteCount + latestDownvoteUsers { + login + nickname + } + viewerHasDownvoteed + beerCount + latestBeerUsers { + login + nickname + } + viewerHasBeered + + popcornCount + viewerHasPopcorned + } + isArticleAuthor + meta { + isArticleAuthorUpvoted + } + replyTo { + id + bodyHtml + floor + isArticleAuthor + author { + id + login + } + } + viewerHasUpvoted + replies { + id + bodyHtml + author { + id + login + } + } + repliesCount + } + totalPages + totalCount + pageSize + pageNumber + } + } + """ + + test "list comments with default replies-mode", ~m(guest_conn drink user user2)a do + total_count = 10 + page_size = 20 + thread = :drink + + all_comments = + Enum.reduce(1..total_count, [], fn i, acc -> + {:ok, comment} = + CMS.create_comment(thread, drink.id, mock_comment("comment #{i}"), user) + + acc ++ [comment] + end) + + random_comment = all_comments |> Enum.at(Enum.random(0..(total_count - 1))) + + {:ok, replyed_comment_1} = CMS.reply_comment(random_comment.id, mock_comment(), user2) + + {:ok, replyed_comment_2} = CMS.reply_comment(random_comment.id, mock_comment(), user2) + + variables = %{id: drink.id, thread: "DRINK", filter: %{page: 1, size: page_size}} + results = guest_conn |> query_result(@query, variables, "pagedComments") + assert results["entries"] |> length == total_count + + assert not exist_in?(replyed_comment_1, results["entries"], :string_key) + assert not exist_in?(replyed_comment_2, results["entries"], :string_key) + + random_comment = Enum.find(results["entries"], &(&1["id"] == to_string(random_comment.id))) + assert random_comment["replies"] |> length == 2 + assert random_comment["repliesCount"] == 2 + + assert random_comment["replies"] |> List.first() |> Map.get("id") == + to_string(replyed_comment_1.id) + + assert random_comment["replies"] |> List.last() |> Map.get("id") == + to_string(replyed_comment_2.id) + end + + test "timeline-mode paged comments", ~m(guest_conn drink user user2)a do + total_count = 3 + page_size = 20 + thread = :drink + + all_comments = + Enum.reduce(1..total_count, [], fn i, acc -> + {:ok, comment} = + CMS.create_comment(thread, drink.id, mock_comment("comment #{i}"), user) + + acc ++ [comment] + end) + + random_comment = all_comments |> Enum.at(Enum.random(0..(total_count - 1))) + + {:ok, replyed_comment_1} = CMS.reply_comment(random_comment.id, mock_comment(), user2) + + {:ok, replyed_comment_2} = CMS.reply_comment(random_comment.id, mock_comment(), user2) + + variables = %{ + id: drink.id, + thread: "DRINK", + mode: "TIMELINE", + filter: %{page: 1, size: page_size} + } + + results = guest_conn |> query_result(@query, variables, "pagedComments") + assert results["entries"] |> length == total_count + 2 + + assert exist_in?(replyed_comment_1, results["entries"], :string_key) + assert exist_in?(replyed_comment_2, results["entries"], :string_key) + + random_comment = Enum.find(results["entries"], &(&1["id"] == to_string(random_comment.id))) + assert random_comment["replies"] |> length == 2 + assert random_comment["repliesCount"] == 2 + end + + test "comment should have reply_to content if need", ~m(guest_conn drink user user2)a do + total_count = 2 + thread = :drink + + Enum.reduce(0..total_count, [], fn i, acc -> + {:ok, comment} = CMS.create_comment(thread, drink.id, mock_comment("comment #{i}"), user) + + acc ++ [comment] + end) + + {:ok, parent_comment} = + CMS.create_comment(:drink, drink.id, mock_comment("parent_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) + + variables = %{id: drink.id, thread: "DRINK", filter: %{page: 1, size: 10}, mode: "TIMELINE"} + results = guest_conn |> query_result(@query, variables, "pagedComments") + + replyed_comment_1 = + Enum.find(results["entries"], &(&1["id"] == to_string(replyed_comment_1.id))) + + assert replyed_comment_1 |> get_in(["replyTo", "id"]) == to_string(parent_comment.id) + + assert replyed_comment_1 |> get_in(["replyTo", "author", "id"]) == + to_string(parent_comment.author_id) + + replyed_comment_2 = + Enum.find(results["entries"], &(&1["id"] == to_string(replyed_comment_2.id))) + + assert replyed_comment_2 |> get_in(["replyTo", "id"]) == to_string(parent_comment.id) + + assert replyed_comment_2 |> get_in(["replyTo", "author", "id"]) == + to_string(parent_comment.author_id) + end + + test "guest user can get paged comment for drink", ~m(guest_conn drink user)a do + total_count = 30 + thread = :drink + + Enum.reduce(1..total_count, [], fn _, acc -> + {:ok, value} = CMS.create_comment(thread, drink.id, mock_comment(), user) + + acc ++ [value] + end) + + variables = %{id: drink.id, thread: "DRINK", filter: %{page: 1, size: 10}} + results = guest_conn |> query_result(@query, variables, "pagedComments") + + assert results |> is_valid_pagination? + assert results["totalCount"] == total_count + end + + test "guest user can get paged comment with pinned comment in it", + ~m(guest_conn drink user)a do + total_count = 20 + thread = :drink + + Enum.reduce(1..total_count, [], fn _, acc -> + {:ok, comment} = CMS.create_comment(thread, drink.id, mock_comment(), user) + + acc ++ [comment] + end) + + {:ok, comment} = CMS.create_comment(thread, drink.id, mock_comment(), user) + {:ok, pinned_comment} = CMS.pin_comment(comment.id) + + Process.sleep(1000) + + {:ok, comment} = CMS.create_comment(thread, drink.id, mock_comment(), user) + {:ok, pinned_comment2} = CMS.pin_comment(comment.id) + + variables = %{id: drink.id, thread: "DRINK", filter: %{page: 1, size: 10}} + results = guest_conn |> query_result(@query, variables, "pagedComments") + + assert results["entries"] |> List.first() |> Map.get("id") == to_string(pinned_comment2.id) + assert results["entries"] |> Enum.at(1) |> Map.get("id") == to_string(pinned_comment.id) + + assert results["totalCount"] == total_count + 2 + end + + test "guest user can get paged comment with floor it", ~m(guest_conn drink user)a do + total_count = 5 + thread = :drink + page_size = 10 + + Enum.reduce(1..total_count, [], fn _, acc -> + {:ok, comment} = CMS.create_comment(thread, drink.id, mock_comment(), user) + Process.sleep(1000) + acc ++ [comment] + end) + + variables = %{id: drink.id, thread: "DRINK", filter: %{page: 1, size: page_size}} + results = guest_conn |> query_result(@query, variables, "pagedComments") + + assert results["entries"] |> List.first() |> Map.get("floor") == 1 + assert results["entries"] |> List.last() |> Map.get("floor") == 5 + end + + test "the comments is loaded in default asc order", ~m(guest_conn drink user)a do + page_size = 10 + thread = :drink + + {:ok, comment} = CMS.create_comment(thread, drink.id, mock_comment(), user) + Process.sleep(1000) + {:ok, _comment2} = CMS.create_comment(thread, drink.id, mock_comment(), user) + Process.sleep(1000) + {:ok, comment3} = CMS.create_comment(thread, drink.id, mock_comment(), user) + + variables = %{ + id: drink.id, + thread: "DRINK", + filter: %{page: 1, size: page_size}, + mode: "TIMELINE" + } + + results = guest_conn |> query_result(@query, variables, "pagedComments") + + assert List.first(results["entries"]) |> Map.get("id") == to_string(comment.id) + assert List.last(results["entries"]) |> Map.get("id") == to_string(comment3.id) + end + + test "the comments can be loaded in desc order in timeline-mode", + ~m(guest_conn drink user)a do + page_size = 10 + thread = :drink + + {:ok, comment} = CMS.create_comment(thread, drink.id, mock_comment(), user) + Process.sleep(1000) + {:ok, _comment2} = CMS.create_comment(thread, drink.id, mock_comment(), user) + Process.sleep(1000) + {:ok, comment3} = CMS.create_comment(thread, drink.id, mock_comment(), user) + + variables = %{ + id: drink.id, + thread: "DRINK", + filter: %{page: 1, size: page_size, sort: "DESC_INSERTED"}, + mode: "TIMELINE" + } + + results = guest_conn |> query_result(@query, variables, "pagedComments") + + assert List.first(results["entries"]) |> Map.get("id") == to_string(comment3.id) + assert List.last(results["entries"]) |> Map.get("id") == to_string(comment.id) + end + + test "the comments can be loaded in desc order in replies-mode", + ~m(guest_conn drink user user2)a do + page_size = 10 + thread = :drink + + {:ok, comment} = CMS.create_comment(thread, drink.id, mock_comment(), user) + {:ok, _reply_comment} = CMS.reply_comment(comment.id, mock_comment(), user) + {:ok, _reply_comment} = CMS.reply_comment(comment.id, mock_comment(), user2) + Process.sleep(1000) + {:ok, comment2} = CMS.create_comment(thread, drink.id, mock_comment(), user) + {:ok, _reply_comment} = CMS.reply_comment(comment2.id, mock_comment(), user) + {:ok, _reply_comment} = CMS.reply_comment(comment2.id, mock_comment(), user2) + Process.sleep(1000) + {:ok, comment3} = CMS.create_comment(thread, drink.id, mock_comment(), user) + {:ok, _reply_comment} = CMS.reply_comment(comment3.id, mock_comment(), user) + {:ok, _reply_comment} = CMS.reply_comment(comment3.id, mock_comment(), user2) + + variables = %{ + id: drink.id, + thread: "DRINK", + filter: %{page: 1, size: page_size, sort: "DESC_INSERTED"} + } + + results = guest_conn |> query_result(@query, variables, "pagedComments") + + assert List.first(results["entries"]) |> Map.get("id") == to_string(comment3.id) + assert List.last(results["entries"]) |> Map.get("id") == to_string(comment.id) + end + + test "guest user can get paged comment with upvotes_count", + ~m(guest_conn drink user user2)a do + total_count = 10 + page_size = 10 + thread = :drink + + all_comment = + Enum.reduce(1..total_count, [], fn i, acc -> + {:ok, comment} = + CMS.create_comment(thread, drink.id, mock_comment("comment #{i}"), user) + + Process.sleep(1000) + acc ++ [comment] + end) + + upvote_comment = all_comment |> Enum.at(3) + upvote_comment2 = all_comment |> Enum.at(4) + {:ok, _} = CMS.upvote_comment(upvote_comment.id, user) + {:ok, _} = CMS.upvote_comment(upvote_comment2.id, user) + {:ok, _} = CMS.upvote_comment(upvote_comment2.id, user2) + + variables = %{id: drink.id, thread: "DRINK", filter: %{page: 1, size: page_size}} + results = guest_conn |> query_result(@query, variables, "pagedComments") + + assert results["entries"] |> Enum.at(3) |> Map.get("upvotesCount") == 1 + assert results["entries"] |> Enum.at(4) |> Map.get("upvotesCount") == 2 + assert results["entries"] |> List.first() |> Map.get("upvotesCount") == 0 + assert results["entries"] |> List.last() |> Map.get("upvotesCount") == 0 + end + + test "article author upvote a comment can get is_article_author and/or is_article_author_upvoted flag", + ~m(guest_conn drink user)a do + total_count = 5 + page_size = 12 + thread = :drink + + author_user = drink.author.user + + all_comments = + Enum.reduce(0..total_count, [], fn i, acc -> + {:ok, comment} = + CMS.create_comment(thread, drink.id, mock_comment("comment #{i}"), user) + + acc ++ [comment] + end) + + random_comment = all_comments |> Enum.at(Enum.random(0..(total_count - 1))) + {:ok, _} = CMS.upvote_comment(random_comment.id, author_user) + + {:ok, author_comment} = CMS.create_comment(thread, drink.id, mock_comment(), author_user) + + {:ok, _} = CMS.upvote_comment(author_comment.id, author_user) + + variables = %{id: drink.id, thread: "DRINK", filter: %{page: 1, size: page_size}} + results = guest_conn |> query_result(@query, variables, "pagedComments") + + the_author_comment = + Enum.find(results["entries"], &(&1["id"] == to_string(author_comment.id))) + + assert the_author_comment["isArticleAuthor"] + assert the_author_comment |> get_in(["meta", "isArticleAuthorUpvoted"]) + + the_random_comment = + Enum.find(results["entries"], &(&1["id"] == to_string(random_comment.id))) + + assert not the_random_comment["isArticleAuthor"] + assert the_random_comment |> get_in(["meta", "isArticleAuthorUpvoted"]) + end + + test "guest user can get paged comment with emotions info", + ~m(guest_conn drink user user2)a do + total_count = 2 + page_size = 10 + thread = :drink + + all_comment = + Enum.reduce(1..total_count, [], fn i, acc -> + {:ok, comment} = + CMS.create_comment(thread, drink.id, mock_comment("comment #{i}"), user) + + Process.sleep(1000) + acc ++ [comment] + end) + + comment = all_comment |> Enum.at(0) + comment2 = all_comment |> Enum.at(1) + + {:ok, _} = CMS.emotion_to_comment(comment.id, :downvote, user) + {:ok, _} = CMS.emotion_to_comment(comment.id, :downvote, user2) + {:ok, _} = CMS.emotion_to_comment(comment2.id, :beer, user2) + + variables = %{id: drink.id, thread: "DRINK", filter: %{page: 1, size: page_size}} + results = guest_conn |> query_result(@query, variables, "pagedComments") + + comment_emotion = + Enum.find(results["entries"], &(&1["id"] == to_string(comment.id))) |> Map.get("emotions") + + assert comment_emotion["popcornCount"] == 0 + + assert comment_emotion["downvoteCount"] == 2 + assert comment_emotion["latestDownvoteUsers"] |> length == 2 + assert not comment_emotion["viewerHasDownvoteed"] + + latest_downvote_users_logins = + Enum.map(comment_emotion["latestDownvoteUsers"], & &1["login"]) + + assert user.login in latest_downvote_users_logins + assert user2.login in latest_downvote_users_logins + + comment2_emotion = + Enum.find(results["entries"], &(&1["id"] == to_string(comment2.id))) + |> Map.get("emotions") + + assert comment2_emotion["beerCount"] == 1 + assert comment2_emotion["latestBeerUsers"] |> length == 1 + assert not comment2_emotion["viewerHasBeered"] + + latest_beer_users_logins = Enum.map(comment2_emotion["latestBeerUsers"], & &1["login"]) + assert user2.login in latest_beer_users_logins + end + + test "user make emotion can get paged comment with emotions has_motioned field", + ~m(user_conn drink user user2)a do + total_count = 10 + page_size = 12 + thread = :drink + + all_comment = + Enum.reduce(1..total_count, [], fn i, acc -> + {:ok, comment} = + CMS.create_comment(thread, drink.id, mock_comment("comment #{i}"), user) + + Process.sleep(1000) + acc ++ [comment] + end) + + comment = all_comment |> Enum.at(0) + comment2 = all_comment |> Enum.at(1) + + {:ok, _} = CMS.emotion_to_comment(comment.id, :downvote, user) + {:ok, _} = CMS.emotion_to_comment(comment2.id, :downvote, user2) + + variables = %{id: drink.id, thread: "DRINK", filter: %{page: 1, size: page_size}} + results = user_conn |> query_result(@query, variables, "pagedComments") + + assert Enum.find(results["entries"], &(&1["id"] == to_string(comment.id))) + |> get_in(["emotions", "viewerHasDownvoteed"]) + end + + test "comment should have viewer has upvoted flag", ~m(user_conn drink user)a do + total_count = 10 + page_size = 12 + thread = :drink + + all_comments = + Enum.reduce(0..total_count, [], fn i, acc -> + {:ok, comment} = + CMS.create_comment(thread, drink.id, mock_comment("comment #{i}"), user) + + acc ++ [comment] + end) + + random_comment = all_comments |> Enum.at(Enum.random(0..(total_count - 1))) + + {:ok, _} = CMS.upvote_comment(random_comment.id, user) + + variables = %{id: drink.id, thread: "DRINK", filter: %{page: 1, size: page_size}} + results = user_conn |> query_result(@query, variables, "pagedComments") + + upvoted_comment = Enum.find(results["entries"], &(&1["id"] == to_string(random_comment.id))) + + assert upvoted_comment["viewerHasUpvoted"] + end + end + + describe "paged participants" do + @query """ + query($id: ID!, $thread: Thread, $filter: PagedFilter!) { + pagedCommentsParticipants(id: $id, thread: $thread, filter: $filter) { + entries { + id + nickname + } + totalPages + totalCount + pageSize + pageNumber + } + } + """ + + test "guest user can get paged participants", ~m(guest_conn drink user)a do + total_count = 30 + page_size = 10 + thread = "DRINK" + + Enum.reduce(1..total_count, [], fn _, acc -> + {:ok, new_user} = db_insert(:user) + {:ok, comment} = CMS.create_comment(:drink, drink.id, mock_comment(), new_user) + + acc ++ [comment] + end) + + {:ok, _comment} = CMS.create_comment(:drink, drink.id, mock_comment(), user) + {:ok, _comment} = CMS.create_comment(:drink, drink.id, mock_comment(), user) + + variables = %{id: drink.id, thread: thread, filter: %{page: 1, size: page_size}} + + results = guest_conn |> query_result(@query, variables, "pagedCommentsParticipants") + + assert results |> is_valid_pagination? + assert results["totalCount"] == total_count + 1 + end + end + + describe "paged replies" do + @query """ + query($id: ID!, $filter: CommentsFilter!) { + pagedCommentReplies(id: $id, filter: $filter) { + entries { + id + bodyHtml + author { + id + nickname + } + upvotesCount + emotions { + downvoteCount + latestDownvoteUsers { + login + nickname + } + viewerHasDownvoteed + beerCount + latestBeerUsers { + login + nickname + } + viewerHasBeered + } + isArticleAuthor + meta { + isArticleAuthorUpvoted + } + replyTo { + id + bodyHtml + floor + isArticleAuthor + author { + id + login + } + } + repliesCount + viewerHasUpvoted + } + totalPages + totalCount + pageSize + pageNumber + } + } + """ + + test "guest user can get paged replies", ~m(guest_conn drink user user2)a do + total_count = 2 + page_size = 10 + thread = :drink + + author_user = drink.author.user + {:ok, parent_comment} = CMS.create_comment(thread, drink.id, mock_comment(), user) + + Enum.reduce(1..total_count, [], fn i, acc -> + {:ok, reply_comment} = + CMS.reply_comment(parent_comment.id, mock_comment("reply #{i}"), user2) + + acc ++ [reply_comment] + end) + + {:ok, author_reply_comment} = + CMS.reply_comment(parent_comment.id, mock_comment("author reply"), author_user) + + variables = %{id: parent_comment.id, filter: %{page: 1, size: page_size}} + results = guest_conn |> query_result(@query, variables, "pagedCommentReplies") + + author_reply_comment = + Enum.find(results["entries"], &(&1["id"] == to_string(author_reply_comment.id))) + + assert author_reply_comment["isArticleAuthor"] + assert results["entries"] |> length == total_count + 1 + end + end +end diff --git a/test/groupher_server_web/query/cms/paged_articles/paged_drinks_test.exs b/test/groupher_server_web/query/cms/paged_articles/paged_drinks_test.exs new file mode 100644 index 000000000..4744bed74 --- /dev/null +++ b/test/groupher_server_web/query/cms/paged_articles/paged_drinks_test.exs @@ -0,0 +1,474 @@ +defmodule GroupherServer.Test.Query.PagedArticles.PagedDrink 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.Drink + + @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, drink_last_week} = + db_insert(:drink, %{title: "last week", inserted_at: @last_week, active_at: @last_week}) + + db_insert(:drink, %{title: "last month", inserted_at: @last_month}) + + {:ok, drink_last_year} = + db_insert(:drink, %{title: "last year", inserted_at: @last_year, active_at: @last_year}) + + db_insert_multi(:drink, @today_count) + guest_conn = simu_conn(:guest) + + {:ok, ~m(guest_conn user drink_last_week drink_last_year)a} + end + + describe "[query paged_drinks filter pagination]" do + @query """ + query($filter: PagedDrinkFilter!) { + pagedDrinks(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, "pagedDrinks") + + 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) + drink_attrs = mock_attrs(:drink, %{community_id: community.id}) + Process.sleep(2000) + {:ok, _drink} = CMS.create_article(community, :drink, drink_attrs, user) + + variables = %{filter: %{page: 1, size: 30}} + results = guest_conn |> query_result(@query, variables, "pagedDrinks") + + drink = results["entries"] |> List.first() + assert not is_nil(get_in(drink, ["document", "bodyHtml"])) + end + + test "support article_tag filter", ~m(guest_conn user)a do + {:ok, community} = db_insert(:community) + drink_attrs = mock_attrs(:drink, %{community_id: community.id}) + {:ok, drink} = CMS.create_article(community, :drink, drink_attrs, user) + + article_tag_attrs = mock_attrs(:article_tag) + {:ok, article_tag} = CMS.create_article_tag(community, :drink, article_tag_attrs, user) + {:ok, _} = CMS.set_article_tag(:drink, drink.id, article_tag.id) + + variables = %{filter: %{page: 1, size: 10, article_tag: article_tag.title}} + results = guest_conn |> query_result(@query, variables, "pagedDrinks") + + drink = results["entries"] |> List.first() + assert results["totalCount"] == 1 + assert exist_in?(article_tag, drink["articleTags"], :string_key) + end + + test "support multi-tag (article_tags) filter", ~m(guest_conn user)a do + {:ok, community} = db_insert(:community) + drink_attrs = mock_attrs(:drink, %{community_id: community.id}) + {:ok, drink} = CMS.create_article(community, :drink, drink_attrs, user) + + article_tag_attrs = mock_attrs(:article_tag) + + {:ok, article_tag} = CMS.create_article_tag(community, :drink, article_tag_attrs, user) + {:ok, article_tag2} = CMS.create_article_tag(community, :drink, article_tag_attrs, user) + {:ok, article_tag3} = CMS.create_article_tag(community, :drink, article_tag_attrs, user) + + {:ok, _} = CMS.set_article_tag(:drink, drink.id, article_tag.id) + {:ok, _} = CMS.set_article_tag(:drink, drink.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, "pagedDrinks") + + drink = results["entries"] |> List.first() + assert results["totalCount"] == 1 + assert exist_in?(article_tag, drink["articleTags"], :string_key) + assert exist_in?(article_tag2, drink["articleTags"], :string_key) + assert not exist_in?(article_tag3, drink["articleTags"], :string_key) + end + + test "should not have pined drinks when filter have article_tag or article_tags", + ~m(guest_conn user)a do + {:ok, community} = db_insert(:community) + drink_attrs = mock_attrs(:drink, %{community_id: community.id}) + {:ok, pinned_drink} = CMS.create_article(community, :drink, drink_attrs, user) + {:ok, drink} = CMS.create_article(community, :drink, drink_attrs, user) + + {:ok, _} = CMS.pin_article(:drink, pinned_drink.id, community.id) + + article_tag_attrs = mock_attrs(:article_tag) + {:ok, article_tag} = CMS.create_article_tag(community, :drink, article_tag_attrs, user) + {:ok, _} = CMS.set_article_tag(:drink, drink.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, "pagedDrinks") + + assert not exist_in?(pinned_drink, results["entries"], :string_key) + assert exist_in?(drink, 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, "pagedDrinks") + + assert not exist_in?(pinned_drink, results["entries"], :string_key) + assert exist_in?(drink, results["entries"], :string_key) + end + + test "support community filter", ~m(guest_conn user)a do + {:ok, community} = db_insert(:community) + + drink_attrs = mock_attrs(:drink, %{community_id: community.id}) + {:ok, _drink} = CMS.create_article(community, :drink, drink_attrs, user) + drink_attrs2 = mock_attrs(:drink, %{community_id: community.id}) + {:ok, _drink} = CMS.create_article(community, :drink, drink_attrs2, user) + + variables = %{filter: %{page: 1, size: 10, community: community.raw}} + results = guest_conn |> query_result(@query, variables, "pagedDrinks") + + drink = results["entries"] |> List.first() + assert results["totalCount"] == 2 + assert exist_in?(%{id: to_string(community.id)}, drink["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, "pagedDrinks") + assert results |> is_valid_pagination? + assert results["pageSize"] == @page_size + assert results["totalCount"] == @total_count + end + end + + describe "[query paged_drinks filter has_xxx]" do + @query """ + query($filter: PagedDrinkFilter!) { + pagedDrinks(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, drink} = CMS.create_article(community, :drink, mock_attrs(:drink), user) + {:ok, _drink} = CMS.create_article(community, :drink, mock_attrs(:drink), user) + {:ok, _drink3} = CMS.create_article(community, :drink, mock_attrs(:drink), user) + + variables = %{filter: %{community: community.raw}} + results = user_conn |> query_result(@query, variables, "pagedDrinks") + assert results["totalCount"] == 3 + + the_drink = Enum.find(results["entries"], &(&1["id"] == to_string(drink.id))) + assert not the_drink["viewerHasViewed"] + assert not the_drink["viewerHasUpvoted"] + assert not the_drink["viewerHasCollected"] + assert not the_drink["viewerHasReported"] + + {:ok, _} = CMS.read_article(:drink, drink.id, user) + {:ok, _} = CMS.upvote_article(:drink, drink.id, user) + {:ok, _} = CMS.collect_article(:drink, drink.id, user) + {:ok, _} = CMS.report_article(:drink, drink.id, "reason", "attr_info", user) + + results = user_conn |> query_result(@query, variables, "pagedDrinks") + the_drink = Enum.find(results["entries"], &(&1["id"] == to_string(drink.id))) + assert the_drink["viewerHasViewed"] + assert the_drink["viewerHasUpvoted"] + assert the_drink["viewerHasCollected"] + assert the_drink["viewerHasReported"] + end + end + + describe "[query paged_drinks filter sort]" do + @query """ + query($filter: PagedDrinkFilter!) { + pagedDrinks(filter: $filter) { + entries { + id + inserted_at + active_at + author { + id + nickname + avatar + } + } + } + } + """ + + test "filter community should get drinks which belongs to that community", + ~m(guest_conn user)a do + {:ok, community} = db_insert(:community) + {:ok, drink} = CMS.create_article(community, :drink, mock_attrs(:drink), user) + + variables = %{filter: %{community: community.raw}} + results = guest_conn |> query_result(@query, variables, "pagedDrinks") + + assert length(results["entries"]) == 1 + assert results["entries"] |> Enum.any?(&(&1["id"] == to_string(drink.id))) + end + + test "should have a active_at same with inserted_at", ~m(guest_conn user)a do + {:ok, community} = db_insert(:community) + {:ok, _drink} = CMS.create_article(community, :drink, mock_attrs(:drink), user) + + variables = %{filter: %{community: community.raw}} + results = guest_conn |> query_result(@query, variables, "pagedDrinks") + drink = results["entries"] |> List.first() + + assert drink["inserted_at"] == drink["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, "pagedDrinks") + 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: PagedDrinkFilter!) { + pagedDrinks(filter: $filter) { + entries { + id + views + } + } + } + """ + + test "filter sort MOST_VIEWS should work", ~m(guest_conn)a do + most_views_drink = Drink |> order_by(desc: :views) |> limit(1) |> Repo.one() + variables = %{filter: %{sort: "MOST_VIEWS"}} + + results = guest_conn |> query_result(@query, variables, "pagedDrinks") + find_drink = results |> Map.get("entries") |> hd + + assert find_drink["views"] == most_views_drink |> 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_drinks filter when]" do + @query """ + query($filter: PagedDrinkFilter!) { + pagedDrinks(filter: $filter) { + entries { + id + views + inserted_at + } + totalCount + } + } + """ + test "THIS_YEAR option should work", ~m(guest_conn drink_last_year)a do + variables = %{filter: %{when: "THIS_YEAR"}} + results = guest_conn |> query_result(@query, variables, "pagedDrinks") + + assert results["entries"] |> Enum.any?(&(&1["id"] != drink_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, "pagedDrinks") + + 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, "pagedDrinks") + + 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, "pagedDrinks") + + {_, 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_drinks filter extra]" do + @query """ + query($filter: PagedDrinkFilter!) { + pagedDrinks(filter: $filter) { + entries { + id + title + } + totalCount + } + } + """ + test "basic filter should work", ~m(guest_conn)a do + {:ok, drink} = db_insert(:drink) + {:ok, drink2} = db_insert(:drink) + + variables = %{filter: %{page: 1, size: 20}} + results = guest_conn |> query_result(@query, variables, "pagedDrinks") + + assert results["totalCount"] >= 1 + assert results["entries"] |> Enum.any?(&(&1["id"] == to_string(drink.id))) + assert results["entries"] |> Enum.any?(&(&1["id"] != to_string(drink2.id))) + end + end + + describe "[paged drinks active_at]" do + @query """ + query($filter: PagedDrinkFilter!) { + pagedDrinks(filter: $filter) { + entries { + id + insertedAt + activeAt + } + } + } + """ + + test "latest commented drink should appear on top", ~m(guest_conn drink_last_week user)a do + variables = %{filter: %{page: 1, size: 20}} + results = guest_conn |> query_result(@query, variables, "pagedDrinks") + entries = results["entries"] + first_drink = entries |> List.first() + assert first_drink["id"] !== to_string(drink_last_week.id) + + Process.sleep(1500) + {:ok, _comment} = CMS.create_comment(:drink, drink_last_week.id, mock_comment(), user) + + results = guest_conn |> query_result(@query, variables, "pagedDrinks") + entries = results["entries"] + first_drink = entries |> List.first() + + assert first_drink["id"] == to_string(drink_last_week.id) + end + + test "comment on very old drink have no effect", ~m(guest_conn drink_last_year user)a do + variables = %{filter: %{page: 1, size: 20}} + + {:ok, _comment} = CMS.create_comment(:drink, drink_last_year.id, mock_comment(), user) + + results = guest_conn |> query_result(@query, variables, "pagedDrinks") + entries = results["entries"] + first_drink = entries |> List.first() + + assert first_drink["id"] !== to_string(drink_last_year.id) + end + + test "latest drink author commented drink have no effect", ~m(guest_conn drink_last_week)a do + variables = %{filter: %{page: 1, size: 20}} + + {:ok, _comment} = + CMS.create_comment( + :drink, + drink_last_week.id, + mock_comment(), + drink_last_week.author.user + ) + + results = guest_conn |> query_result(@query, variables, "pagedDrinks") + entries = results["entries"] + first_drink = entries |> List.first() + + assert first_drink["id"] !== to_string(drink_last_week.id) + end + end +end diff --git a/test/support/factory.ex b/test/support/factory.ex index c2adacbad..9097ebed6 100644 --- a/test/support/factory.ex +++ b/test/support/factory.ex @@ -299,6 +299,26 @@ defmodule GroupherServer.Support.Factory do } end + defp mock_meta(:drink) do + text = Faker.Lorem.sentence(%Range{first: 80, last: 120}) + + %{ + meta: @default_article_meta |> Map.merge(%{thread: "DRINK"}), + 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})