From d66bbc5414e0340c06ff8edeef5b0d94ed7d90c1 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Fri, 12 Apr 2019 15:04:32 +0200 Subject: [PATCH] Add ability to search on Group, Person or Event Signed-off-by: Thomas Citharel --- lib/mobilizon/actors/actors.ex | 22 +- lib/mobilizon/events/events.ex | 10 +- lib/mobilizon_web/api/search.ex | 118 ++++----- lib/mobilizon_web/resolvers/search.ex | 20 +- lib/mobilizon_web/schema.ex | 28 +-- lib/mobilizon_web/schema/search.ex | 55 +++++ test/mobilizon/actors/actors_test.exs | 19 +- test/mobilizon/events/events_test.exs | 18 +- test/mobilizon_web/api/search_test.exs | 43 ++-- .../resolvers/search_resolver_test.exs | 225 ++++++++++++++---- 10 files changed, 385 insertions(+), 173 deletions(-) create mode 100644 lib/mobilizon_web/schema/search.ex diff --git a/lib/mobilizon/actors/actors.ex b/lib/mobilizon/actors/actors.ex index 539d8587a..cc4221052 100644 --- a/lib/mobilizon/actors/actors.ex +++ b/lib/mobilizon/actors/actors.ex @@ -498,12 +498,17 @@ defmodule Mobilizon.Actors do @doc """ Find actors by their name or displayed name """ - @spec find_actors_by_username_or_name(String.t(), integer(), integer()) :: list(Actor.t()) - def find_actors_by_username_or_name(username, page \\ nil, limit \\ nil) - def find_actors_by_username_or_name("", _page, _limit), do: [] + @spec find_and_count_actors_by_username_or_name( + String.t(), + [ActorTypeEnum.t()], + integer() | nil, + integer() | nil + ) :: + %{total: integer(), elements: list(Actor.t())} + def find_and_count_actors_by_username_or_name(username, _types, page \\ nil, limit \\ nil) - def find_actors_by_username_or_name(username, page, limit) do - Repo.all( + def find_and_count_actors_by_username_or_name(username, types, page, limit) do + query = from( a in Actor, where: @@ -515,6 +520,7 @@ defmodule Mobilizon.Actors do a.name, ^username ), + where: a.type in ^types, order_by: fragment( "word_similarity(?, ?) + word_similarity(coalesce(?, ''), ?) desc", @@ -525,7 +531,11 @@ defmodule Mobilizon.Actors do ) ) |> paginate(page, limit) - ) + + total = Task.async(fn -> Repo.aggregate(query, :count, :id) end) + elements = Task.async(fn -> Repo.all(query) end) + + %{total: Task.await(total), elements: Task.await(elements)} end @doc """ diff --git a/lib/mobilizon/events/events.ex b/lib/mobilizon/events/events.ex index 42ae4b484..cca978604 100644 --- a/lib/mobilizon/events/events.ex +++ b/lib/mobilizon/events/events.ex @@ -246,10 +246,9 @@ defmodule Mobilizon.Events do @doc """ Find events by name """ - def find_events_by_name(name, page \\ nil, limit \\ nil) - def find_events_by_name("", page, limit), do: list_events(page, limit) + def find_and_count_events_by_name(name, page \\ nil, limit \\ nil) - def find_events_by_name(name, page, limit) do + def find_and_count_events_by_name(name, page, limit) do name = String.trim(name) query = @@ -271,7 +270,10 @@ defmodule Mobilizon.Events do ) |> paginate(page, limit) - Repo.all(query) + total = Task.async(fn -> Repo.aggregate(query, :count, :id) end) + elements = Task.async(fn -> Repo.all(query) end) + + %{total: Task.await(total), elements: Task.await(elements)} end @doc """ diff --git a/lib/mobilizon_web/api/search.ex b/lib/mobilizon_web/api/search.ex index 0b7c5c7e4..626936af5 100644 --- a/lib/mobilizon_web/api/search.ex +++ b/lib/mobilizon_web/api/search.ex @@ -11,92 +11,98 @@ defmodule MobilizonWeb.API.Search do require Logger @doc """ - Search + Search actors """ - @spec search(String.t(), integer(), integer()) :: - {:ok, list(Actor.t())} | {:ok, []} | {:error, any()} - def search(search, page \\ 1, limit \\ 10) do - do_search(search, page, limit, %{events: true, actors: true}) - end - - @doc """ - Not used at the moment - """ - # TODO: Use me - @spec search_actors(String.t(), integer(), integer()) :: - {:ok, list(Actor.t())} | {:ok, []} | {:error, any()} - def search_actors(search, page \\ 1, limit \\ 10) do - do_search(search, page, limit, %{actors: true}) - end - - @doc """ - Not used at the moment - """ - # TODO: Use me - @spec search_events(String.t(), integer(), integer()) :: - {:ok, list(Event.t())} | {:ok, []} | {:error, any()} - def search_events(search, page \\ 1, limit \\ 10) do - do_search(search, page, limit, %{events: true}) - end - - # Do the actual search - @spec do_search(String.t(), integer(), integer(), map()) :: {:ok, list(any())} - defp do_search(search, page, limit, opts) do + @spec search_actors(String.t(), integer(), integer(), String.t()) :: + {:ok, %{total: integer(), elements: list(Actor.t())}} | {:error, any()} + def search_actors(search, page \\ 1, limit \\ 10, result_type) do search = String.trim(search) cond do search == "" -> {:error, "Search can't be empty"} - String.match?(search, ~r/@/) -> + # Some URLs could be domain.tld/@username, so keep this condition above handle_search? function + url_search?(search) -> + # If this is not an actor, skip + with %{:total => total, :elements => [%Actor{}] = elements} <- process_from_url(search) do + {:ok, %{total: total, elements: elements}} + else + _ -> + {:ok, %{total: 0, elements: []}} + end + + handle_search?(search) -> {:ok, process_from_username(search)} - String.starts_with?(search, "https://") -> - {:ok, process_from_url(search)} + true -> + {:ok, + Actors.find_and_count_actors_by_username_or_name(search, [result_type], page, limit)} + end + end - String.starts_with?(search, "http://") -> - {:ok, process_from_url(search)} + @doc """ + Search events + """ + @spec search_events(String.t(), integer(), integer()) :: + {:ok, %{total: integer(), elements: list(Event.t())}} | {:error, any()} + def search_events(search, page \\ 1, limit \\ 10) do + search = String.trim(search) + + cond do + search == "" -> + {:error, "Search can't be empty"} + + url_search?(search) -> + # If this is not an event, skip + with {total = total, [%Event{} = elements]} <- process_from_url(search) do + {:ok, %{total: total, elements: elements}} + else + _ -> + {:ok, %{total: 0, elements: []}} + end true -> - events = - Task.async(fn -> - if Map.get(opts, :events, false), - do: Events.find_events_by_name(search, page, limit), - else: [] - end) - - actors = - Task.async(fn -> - if Map.get(opts, :actors, false), - do: Actors.find_actors_by_username_or_name(search, page, limit), - else: [] - end) - - {:ok, Task.await(events) ++ Task.await(actors)} + {:ok, Events.find_and_count_events_by_name(search, page, limit)} end end # If the search string is an username - @spec process_from_username(String.t()) :: Actor.t() | nil + @spec process_from_username(String.t()) :: %{total: integer(), elements: [Actor.t()]} defp process_from_username(search) do with {:ok, actor} <- ActivityPub.find_or_make_actor_from_nickname(search) do - actor + %{total: 1, elements: [actor]} else {:error, _err} -> Logger.debug(fn -> "Unable to find or make actor '#{search}'" end) - nil + %{total: 0, elements: []} end end # If the search string is an URL - @spec process_from_url(String.t()) :: Actor.t() | Event.t() | Comment.t() | nil + @spec process_from_url(String.t()) :: %{ + total: integer(), + elements: [Actor.t() | Event.t() | Comment.t()] + } defp process_from_url(search) do with {:ok, object} <- ActivityPub.fetch_object_from_url(search) do - object + %{total: 1, elements: [object]} else {:error, _err} -> Logger.debug(fn -> "Unable to find or make object from URL '#{search}'" end) - nil + %{total: 0, elements: []} end end + + # Is the search an URL search? + @spec url_search?(String.t()) :: boolean + defp url_search?(search) do + String.starts_with?(search, "https://") or String.starts_with?(search, "http://") + end + + # Is the search an handle search? + @spec handle_search?(String.t()) :: boolean + defp handle_search?(search) do + String.match?(search, ~r/@/) + end end diff --git a/lib/mobilizon_web/resolvers/search.ex b/lib/mobilizon_web/resolvers/search.ex index adc4b8125..f6b7995ad 100644 --- a/lib/mobilizon_web/resolvers/search.ex +++ b/lib/mobilizon_web/resolvers/search.ex @@ -5,9 +5,23 @@ defmodule MobilizonWeb.Resolvers.Search do alias MobilizonWeb.API.Search @doc """ - Search events and actors by title + Search persons """ - def search_events_and_actors(_parent, %{search: search, page: page, limit: limit}, _resolution) do - Search.search(search, page, limit) + def search_persons(_parent, %{search: search, page: page, limit: limit}, _resolution) do + Search.search_actors(search, page, limit, :Person) + end + + @doc """ + Search groups + """ + def search_groups(_parent, %{search: search, page: page, limit: limit}, _resolution) do + Search.search_actors(search, page, limit, :Group) + end + + @doc """ + Search events + """ + def search_events(_parent, %{search: search, page: page, limit: limit}, _resolution) do + Search.search_events(search, page, limit) end end diff --git a/lib/mobilizon_web/schema.ex b/lib/mobilizon_web/schema.ex index 9d4cf0be1..b234d33de 100644 --- a/lib/mobilizon_web/schema.ex +++ b/lib/mobilizon_web/schema.ex @@ -18,10 +18,9 @@ defmodule MobilizonWeb.Schema do import_types(MobilizonWeb.Schema.Actors.PersonType) import_types(MobilizonWeb.Schema.Actors.GroupType) import_types(MobilizonWeb.Schema.CommentType) + import_types(MobilizonWeb.Schema.SearchType) import_types(MobilizonWeb.Schema.ConfigType) - alias MobilizonWeb.Resolvers - @desc "A struct containing the id of the deleted object" object :deleted_object do field(:id, :integer) @@ -85,22 +84,6 @@ defmodule MobilizonWeb.Schema do end) end - @desc "A search result" - union :search_result do - types([:event, :person, :group]) - - resolve_type(fn - %Actor{type: :Person}, _ -> - :person - - %Actor{type: :Group}, _ -> - :group - - %Event{}, _ -> - :event - end) - end - def context(ctx) do loader = Dataloader.new() @@ -120,14 +103,7 @@ defmodule MobilizonWeb.Schema do Root Query """ query do - @desc "Search through events, persons and groups" - field :search, list_of(:search_result) do - arg(:search, non_null(:string)) - arg(:page, :integer, default_value: 1) - arg(:limit, :integer, default_value: 10) - resolve(&Resolvers.Search.search_events_and_actors/3) - end - + import_fields(:search_queries) import_fields(:user_queries) import_fields(:person_queries) import_fields(:group_queries) diff --git a/lib/mobilizon_web/schema/search.ex b/lib/mobilizon_web/schema/search.ex new file mode 100644 index 000000000..a752c0ffb --- /dev/null +++ b/lib/mobilizon_web/schema/search.ex @@ -0,0 +1,55 @@ +defmodule MobilizonWeb.Schema.SearchType do + @moduledoc """ + Schema representation for Search + """ + use Absinthe.Schema.Notation + + alias MobilizonWeb.Resolvers + + @desc "Search persons result" + object :persons do + field(:total, non_null(:integer), description: "Total elements") + field(:elements, non_null(list_of(:person)), description: "Person elements") + end + + @desc "Search groups result" + object :groups do + field(:total, non_null(:integer), description: "Total elements") + field(:elements, non_null(list_of(:group)), description: "Group elements") + end + + @desc "Search events result" + object :events do + field(:total, non_null(:integer), description: "Total elements") + field(:elements, non_null(list_of(:event)), description: "Event elements") + end + + object :search_queries do + @desc "Search persons" + field :search_persons, :persons do + arg(:search, non_null(:string)) + arg(:page, :integer, default_value: 1) + arg(:limit, :integer, default_value: 10) + + resolve(&Resolvers.Search.search_persons/3) + end + + @desc "Search groups" + field :search_groups, :groups do + arg(:search, non_null(:string)) + arg(:page, :integer, default_value: 1) + arg(:limit, :integer, default_value: 10) + + resolve(&Resolvers.Search.search_groups/3) + end + + @desc "Search events" + field :search_events, :events do + arg(:search, non_null(:string)) + arg(:page, :integer, default_value: 1) + arg(:limit, :integer, default_value: 10) + + resolve(&Resolvers.Search.search_events/3) + end + end +end diff --git a/test/mobilizon/actors/actors_test.exs b/test/mobilizon/actors/actors_test.exs index 4b79cf76c..80289ec75 100644 --- a/test/mobilizon/actors/actors_test.exs +++ b/test/mobilizon/actors/actors_test.exs @@ -184,19 +184,26 @@ defmodule Mobilizon.ActorsTest do assert MapSet.new([actor_found_id, actor2_found_id]) == MapSet.new([actor.id, actor2.id]) end - test "test find_actors_by_username_or_name/1 returns actors with similar usernames", %{ - actor: %Actor{id: actor_id} - } do + test "test find_and_count_actors_by_username_or_name/4 returns actors with similar usernames", + %{ + actor: %Actor{id: actor_id} + } do use_cassette "actors/remote_actor_mastodon_tcit" do with {:ok, %Actor{id: actor2_id}} <- Actors.get_or_fetch_by_url(@remote_account_url) do - actors_ids = Actors.find_actors_by_username_or_name("tcit") |> Enum.map(& &1.id) + %{total: 2, elements: actors} = + Actors.find_and_count_actors_by_username_or_name("tcit", [:Person]) + + actors_ids = actors |> Enum.map(& &1.id) + assert MapSet.new(actors_ids) == MapSet.new([actor2_id, actor_id]) end end end - test "test find_actors_by_username_or_name/1 returns actors with similar names" do - actors = Actors.find_actors_by_username_or_name("ohno") + test "test find_and_count_actors_by_username_or_name/4 returns actors with similar names" do + %{total: 0, elements: actors} = + Actors.find_and_count_actors_by_username_or_name("ohno", [:Person]) + assert actors == [] end diff --git a/test/mobilizon/events/events_test.exs b/test/mobilizon/events/events_test.exs index b07283994..8a8402728 100644 --- a/test/mobilizon/events/events_test.exs +++ b/test/mobilizon/events/events_test.exs @@ -54,18 +54,22 @@ defmodule Mobilizon.EventsTest do assert Events.get_event_full!(event.id).participants == [] end - test "find_events_by_name/1 returns events for a given name", %{ + test "find_and_count_events_by_name/1 returns events for a given name", %{ event: %Event{title: title} = event } do - assert title == hd(Events.find_events_by_name(event.title)).title + assert title == hd(Events.find_and_count_events_by_name(event.title).elements).title - %Event{title: title2} = event2 = insert(:event, title: "Special event") - assert event2.title == Events.find_events_by_name("Special") |> hd() |> Map.get(:title) + %Event{} = event2 = insert(:event, title: "Special event") - assert event2.title == Events.find_events_by_name(" Special ") |> hd() |> Map.get(:title) + assert event2.title == + Events.find_and_count_events_by_name("Special").elements |> hd() |> Map.get(:title) - assert title == Events.find_events_by_name("") |> hd() |> Map.get(:title) - assert title2 == Events.find_events_by_name("") |> tl |> hd() |> Map.get(:title) + assert event2.title == + Events.find_and_count_events_by_name(" Special ").elements + |> hd() + |> Map.get(:title) + + assert %{elements: [], total: 0} == Events.find_and_count_events_by_name("") end test "find_close_events/3 returns events in the area" do diff --git a/test/mobilizon_web/api/search_test.exs b/test/mobilizon_web/api/search_test.exs index d26933ddb..6f7395f68 100644 --- a/test/mobilizon_web/api/search_test.exs +++ b/test/mobilizon_web/api/search_test.exs @@ -12,44 +12,45 @@ defmodule MobilizonWeb.API.SearchTest do test "search an user by username" do with_mock ActivityPub, - find_or_make_actor_from_nickname: fn "toto@domain.tld" -> {:ok, %Actor{id: 1}} end do - assert {:ok, %Actor{id: 1}} == Search.search("toto@domain.tld") + find_or_make_actor_from_nickname: fn "toto@domain.tld" -> {:ok, %Actor{id: 42}} end do + assert {:ok, %{total: 1, elements: [%Actor{id: 42}]}} == + Search.search_actors("toto@domain.tld", 1, 10, :Person) + assert_called(ActivityPub.find_or_make_actor_from_nickname("toto@domain.tld")) end end test "search something by URL" do with_mock ActivityPub, - fetch_object_from_url: fn "https://social.tcit.fr/users/tcit" -> {:ok, %Actor{id: 1}} end do - assert {:ok, %Actor{id: 1}} == Search.search("https://social.tcit.fr/users/tcit") - assert_called(ActivityPub.fetch_object_from_url("https://social.tcit.fr/users/tcit")) - end - end + fetch_object_from_url: fn "https://social.tcit.fr/users/tcit" -> {:ok, %Actor{id: 42}} end do + assert {:ok, %{total: 1, elements: [%Actor{id: 42}]}} == + Search.search_actors("https://social.tcit.fr/users/tcit", 1, 10, :Person) - test "search everything" do - with_mocks([ - {Actors, [], [find_actors_by_username_or_name: fn "toto", 1, 10 -> [%Actor{}] end]}, - {Events, [], [find_events_by_name: fn "toto", 1, 10 -> [%Event{}] end]} - ]) do - assert {:ok, [%Event{}, %Actor{}]} = Search.search("toto") - assert_called(Actors.find_actors_by_username_or_name("toto", 1, 10)) - assert_called(Events.find_events_by_name("toto", 1, 10)) + assert_called(ActivityPub.fetch_object_from_url("https://social.tcit.fr/users/tcit")) end end test "search actors" do with_mock Actors, - find_actors_by_username_or_name: fn "toto", 1, 10 -> [%Actor{}] end do - assert {:ok, [%Actor{}]} = Search.search_actors("toto") - assert_called(Actors.find_actors_by_username_or_name("toto", 1, 10)) + find_and_count_actors_by_username_or_name: fn "toto", _type, 1, 10 -> + %{total: 1, elements: [%Actor{id: 42}]} + end do + assert {:ok, %{total: 1, elements: [%Actor{id: 42}]}} = + Search.search_actors("toto", 1, 10, :Person) + + assert_called(Actors.find_and_count_actors_by_username_or_name("toto", [:Person], 1, 10)) end end test "search events" do with_mock Events, - find_events_by_name: fn "toto", 1, 10 -> [%Event{}] end do - assert {:ok, [%Event{}]} = Search.search_events("toto") - assert_called(Events.find_events_by_name("toto", 1, 10)) + find_and_count_events_by_name: fn "toto", 1, 10 -> + %{total: 1, elements: [%Event{title: "super toto event"}]} + end do + assert {:ok, %{total: 1, elements: [%Event{title: "super toto event"}]}} = + Search.search_events("toto", 1, 10) + + assert_called(Events.find_and_count_events_by_name("toto", 1, 10)) end end end diff --git a/test/mobilizon_web/resolvers/search_resolver_test.exs b/test/mobilizon_web/resolvers/search_resolver_test.exs index 7774ec4ed..d7374474f 100644 --- a/test/mobilizon_web/resolvers/search_resolver_test.exs +++ b/test/mobilizon_web/resolvers/search_resolver_test.exs @@ -9,26 +9,24 @@ defmodule MobilizonWeb.Resolvers.SearchResolverTest do {:ok, conn: conn, user: user} end - test "search_events_and_actors/3 finds events and actors with basic search", %{ + test "search_events/3 finds events with basic search", %{ conn: conn, user: user } do - actor = insert(:actor, user: user, preferred_username: "test") - event = insert(:event, title: "test") + insert(:actor, user: user, preferred_username: "test_person") + insert(:actor, type: :Group, preferred_username: "test_group") + event = insert(:event, title: "test_event") query = """ { - search(search: "test") { - ...on Event { + search_events(search: "test") { + total, + elements { title, uuid, __typename - }, - ...on Actor { - preferredUsername, - __typename } - } + }, } """ @@ -36,33 +34,172 @@ defmodule MobilizonWeb.Resolvers.SearchResolverTest do conn |> get("/api", AbsintheHelpers.query_skeleton(query, "search")) - assert hd(json_response(res, 200)["data"]["search"])["uuid"] == to_string(event.uuid) + assert json_response(res, 200)["errors"] == nil + assert json_response(res, 200)["data"]["search_events"]["total"] == 1 + assert json_response(res, 200)["data"]["search_events"]["elements"] |> length == 1 - assert hd(tl(json_response(res, 200)["data"]["search"]))["preferredUsername"] == - actor.preferred_username + assert hd(json_response(res, 200)["data"]["search_events"]["elements"])["uuid"] == + to_string(event.uuid) end - test "search_events_and_actors/3 finds events and actors with word search", %{ + test "search_persons/3 finds persons with basic search", %{ conn: conn, user: user } do - actor = insert(:actor, user: user, preferred_username: "toto", name: "I like pineapples") - event = insert(:event, title: "Pineapple fashion week") + actor = insert(:actor, user: user, preferred_username: "test_person") + insert(:actor, type: :Group, preferred_username: "test_group") + insert(:event, title: "test_event") + + query = """ + { + search_persons(search: "test") { + total, + elements { + preferredUsername, + __typename + } + }, + } + """ + + res = + conn + |> get("/api", AbsintheHelpers.query_skeleton(query, "search")) + + assert json_response(res, 200)["errors"] == nil + assert json_response(res, 200)["data"]["search_persons"]["total"] == 1 + assert json_response(res, 200)["data"]["search_persons"]["elements"] |> length == 1 + + assert hd(json_response(res, 200)["data"]["search_persons"]["elements"])["preferredUsername"] == + actor.preferred_username + end + + test "search_groups/3 finds persons with basic search", %{ + conn: conn, + user: user + } do + insert(:actor, user: user, preferred_username: "test_person") + group = insert(:actor, type: :Group, preferred_username: "test_group") + insert(:event, title: "test_event") + + query = """ + { + search_groups(search: "test") { + total, + elements { + preferredUsername, + __typename + } + }, + } + """ + + res = + conn + |> get("/api", AbsintheHelpers.query_skeleton(query, "search")) + + assert json_response(res, 200)["errors"] == nil + assert json_response(res, 200)["data"]["search_groups"]["total"] == 1 + assert json_response(res, 200)["data"]["search_groups"]["elements"] |> length == 1 + + assert hd(json_response(res, 200)["data"]["search_groups"]["elements"])["preferredUsername"] == + group.preferred_username + end + + test "search_events/3 finds events and actors with word search", %{ + conn: conn, + user: user + } do + insert(:actor, user: user, preferred_username: "person", name: "I like pineapples") + insert(:event, title: "Pineapple fashion week") + insert(:event, title: "I love pineAPPLE") + insert(:event, title: "Hello") + + query = """ + { + search_events(search: "pineapple") { + total, + elements { + title, + uuid, + __typename + } + } + } + """ + + res = + conn + |> get("/api", AbsintheHelpers.query_skeleton(query, "search")) + + assert json_response(res, 200)["errors"] == nil + assert json_response(res, 200)["data"]["search_events"]["total"] == 2 + + assert json_response(res, 200)["data"]["search_events"]["elements"] + |> length == 2 + + assert json_response(res, 200)["data"]["search_events"]["elements"] + |> Enum.map(& &1["title"]) == [ + "I love pineAPPLE", + "Pineapple fashion week" + ] + end + + test "search_persons/3 finds persons with word search", %{ + conn: conn, + user: user + } do + actor = insert(:actor, user: user, preferred_username: "person", name: "I like pineapples") + insert(:actor, preferred_username: "group", type: :Group, name: "pineapple group") + insert(:event, title: "Pineapple fashion week") + insert(:event, title: "I love pineAPPLE") + insert(:event, title: "Hello") + + query = """ + { + search_persons(search: "pineapple") { + total, + elements { + preferredUsername, + __typename + } + } + } + """ + + res = + conn + |> get("/api", AbsintheHelpers.query_skeleton(query, "search")) + + assert json_response(res, 200)["errors"] == nil + assert json_response(res, 200)["data"]["search_persons"]["total"] == 1 + + assert json_response(res, 200)["data"]["search_persons"]["elements"] + |> length == 1 + + assert hd(json_response(res, 200)["data"]["search_persons"]["elements"])["preferredUsername"] == + actor.preferred_username + end + + test "search_events/3 finds events with accented search", %{ + conn: conn, + user: user + } do + insert(:actor, user: user, preferred_username: "person", name: "Torréfaction du Kafé") + insert(:actor, type: :Group, preferred_username: "group", name: "Kafé group") + event = insert(:event, title: "Tour du monde des Kafés") # Elaborate query query = """ { - search(search: "pineapple") { - ...on Event { - title, - uuid, - __typename - }, - ...on Actor { - preferredUsername, - __typename - } + search_events(search: "Kafé") { + total, + elements { + title, + uuid, + __typename } + } } """ @@ -70,33 +207,29 @@ defmodule MobilizonWeb.Resolvers.SearchResolverTest do conn |> get("/api", AbsintheHelpers.query_skeleton(query, "search")) - assert hd(json_response(res, 200)["data"]["search"])["uuid"] == to_string(event.uuid) - - assert hd(tl(json_response(res, 200)["data"]["search"]))["preferredUsername"] == - actor.preferred_username + assert json_response(res, 200)["errors"] == nil + assert json_response(res, 200)["data"]["search_events"]["total"] == 1 + assert hd(json_response(res, 200)["data"]["search_events"]["elements"])["uuid"] == event.uuid end - test "search_events_and_actors/3 finds events and actors with accented search", %{ + test "search_groups/3 finds groups with accented search", %{ conn: conn, user: user } do - insert(:actor, user: user, preferred_username: "toto", name: "Torréfaction") - event = insert(:event, title: "Tour du monde des cafés") + insert(:actor, user: user, preferred_username: "person", name: "Torréfaction du Kafé") + group = insert(:actor, type: :Group, preferred_username: "group", name: "Kafé group") + insert(:event, title: "Tour du monde des Kafés") # Elaborate query query = """ { - search(search: "café") { - ...on Event { - title, - uuid, - __typename - }, - ...on Actor { - preferredUsername, - __typename - } + search_groups(search: "Kafé") { + total, + elements { + preferredUsername, + __typename } + } } """ @@ -104,6 +237,10 @@ defmodule MobilizonWeb.Resolvers.SearchResolverTest do conn |> get("/api", AbsintheHelpers.query_skeleton(query, "search")) - assert hd(json_response(res, 200)["data"]["search"])["uuid"] == to_string(event.uuid) + assert json_response(res, 200)["errors"] == nil + assert json_response(res, 200)["data"]["search_groups"]["total"] == 1 + + assert hd(json_response(res, 200)["data"]["search_groups"]["elements"])["preferredUsername"] == + group.preferred_username end end