Files
danbooru/test/unit/concerns/searchable.rb
evazion da3e8e4726 searchable: fix bug with searching multiple association attributes.
Fix a bug with searches like the following not working correctly:

* https://danbooru.donmai.us/comments.json?search[creator][level]=20&search[creator_id]=1234
* https://danbooru.donmai.us/comments.json?search[creator][level]=20&search[creator_name]=abcd
* https://danbooru.donmai.us/comments.json?search[post][rating]=s&search[post_tags_match]=touhou

It wasn't possible to search for both `creator` and `creator_id` at the
same time (or `post` and `post_tags_match`, etc). Only the `creator_id`
param would be recognized.

Also refactor some internals:

* `search_includes` was renamed to `search_associated_attribute`.
* `search_attribute` was split up into `search_basic_attribute` and
  `search_associated_attribute`.
2021-01-07 17:10:29 -06:00

206 lines
7.8 KiB
Ruby

require 'test_helper'
class SearchableTest < ActiveSupport::TestCase
def assert_search_equals(results, current_user: User.anonymous, **params)
as(current_user) do
assert_equal(Array(results).map(&:id), subject.search(**params).ids)
end
end
context "#search method" do
subject { Post }
setup do
@p1 = create(:post, source: "a1", score: 1, is_deleted: true, uploader_ip_addr: "10.0.0.1")
@p2 = create(:post, source: "b2", score: 2, is_deleted: false)
@p3 = create(:post, source: "c3", score: 3, is_deleted: false)
end
context "for a nonexistent attribute" do
should "raise an error" do
assert_raises(ArgumentError) do
Post.search_attribute(:answer, 42, User.anonymous)
end
end
end
context "for a numeric attribute" do
should "support basic operators" do
assert_search_equals(@p1, score_eq: 1)
assert_search_equals(@p3, score_gt: 2)
assert_search_equals(@p1, score_lt: 2)
assert_search_equals([@p3, @p1], score_not_eq: 2)
assert_search_equals([@p3, @p2], score_gteq: 2)
assert_search_equals([@p2, @p1], score_lteq: 2)
end
should "support embedded expressions" do
assert_search_equals(@p1, score: "1")
assert_search_equals(@p3, score: ">2")
assert_search_equals(@p1, score: "<2")
assert_search_equals([@p3, @p2], score: ">=2")
assert_search_equals([@p2, @p1], score: "<=2")
assert_search_equals([@p3, @p2], score: "3,2")
assert_search_equals([@p2, @p1], score: "1...3")
assert_search_equals([@p2, @p1], score: "3...1")
assert_search_equals([@p3, @p2, @p1], score: "1..3")
assert_search_equals([@p3, @p2, @p1], score: "3..1")
end
end
context "for a string attribute" do
should "support various operators" do
assert_search_equals(@p1, source: "a1")
assert_search_equals(@p1, source_eq: "a1")
assert_search_equals(@p1, source_like: "a*")
assert_search_equals(@p1, source_ilike: "A*")
assert_search_equals(@p1, source_regex: "^a.*")
assert_search_equals(@p1, source_array: ["a1", "blah"])
assert_search_equals(@p1, source_comma: "a1,blah")
assert_search_equals(@p1, source_space: "a1 blah")
assert_search_equals(@p1, source_lower_array: ["a1", "BLAH"])
assert_search_equals(@p1, source_lower_comma: "a1,BLAH")
assert_search_equals(@p1, source_lower_space: "a1 BLAH")
assert_search_equals([@p3, @p2], source_not_eq: "a1")
assert_search_equals([@p3, @p2], source_not_like: "a*")
assert_search_equals([@p3, @p2], source_not_ilike: "A*")
assert_search_equals([@p3, @p2], source_not_regex: "^a.*")
end
end
context "for a boolean attribute" do
should "work" do
assert_search_equals(@p1, is_deleted: "true")
assert_search_equals(@p1, is_deleted: "yes")
assert_search_equals(@p1, is_deleted: "on")
assert_search_equals(@p1, is_deleted: "1")
assert_search_equals([@p3, @p2], is_deleted: "false")
assert_search_equals([@p3, @p2], is_deleted: "no")
assert_search_equals([@p3, @p2], is_deleted: "off")
assert_search_equals([@p3, @p2], is_deleted: "0")
end
end
context "for an inet attribute" do
should "work" do
assert_search_equals(@p1, uploader_ip_addr: "10.0.0.1")
assert_search_equals(@p1, uploader_ip_addr: "10.0.0.1/24")
assert_search_equals(@p1, uploader_ip_addr: "10.0.0.1,1.1.1.1")
assert_search_equals(@p1, uploader_ip_addr: "10.0.0.1 1.1.1.1")
end
end
context "for an enum attribute" do
subject { PostFlag }
should "work" do
@pf = create(:post_flag, status: :pending)
assert_search_equals(@pf, status: "pending")
assert_search_equals(@pf, status: "pending,blah")
assert_search_equals(@pf, status: "pending blah")
assert_search_equals(@pf, status_id: 0)
end
end
context "for an array attribute" do
subject { WikiPage }
should "work" do
@wp = create(:wiki_page, other_names: ["a1", "b2"])
assert_search_equals(@wp, other_names_include_any: "a1")
assert_search_equals(@wp, other_names_include_any: "a1 blah")
assert_search_equals(@wp, other_names_include_all: "a1")
assert_search_equals(@wp, other_names_include_all: "a1 b2")
assert_search_equals(@wp, other_names_include_any_array: ["a1", "blah"])
assert_search_equals(@wp, other_names_include_all_array: ["a1", "b2"])
assert_search_equals(@wp, other_names_include_any_lower: "A1 BLAH")
assert_search_equals(@wp, other_names_include_all_lower: "A1 B2")
assert_search_equals(@wp, other_names_include_any_lower_array: ["A1", "BLAH"])
assert_search_equals(@wp, other_names_include_all_lower_array: ["A1", "B2"])
assert_search_equals(@wp, other_name_count: 2)
end
end
context "for a belongs_to association" do
context "for a user association" do
should "work" do
assert_search_equals(@p1, uploader_id: @p1.uploader_id)
assert_search_equals(@p1, uploader_name: @p1.uploader.name)
assert_search_equals(@p1, uploader: { id: @p1.uploader_id })
assert_search_equals(@p1, uploader: { name: @p1.uploader.name })
assert_search_equals(@p1, uploader: { name: @p1.uploader.name }, uploader_id: @p1.uploader.id)
assert_search_equals([], uploader: { name: @p1.uploader.name }, uploader_id: @p2.uploader.id)
end
end
context "for a post association" do
should "work" do
@p1.update!(parent: @p2)
assert_search_equals(@p1, parent_id: @p2.id)
assert_search_equals(@p1, parent: { id: @p2.id })
assert_search_equals(@p1, parent_tags_match: "id:#{@p2.id}")
assert_search_equals([], parent_tags_match: "id:0")
assert_search_equals(@p2, children_tags_match: "id:#{@p1.id}")
assert_search_equals([], children_tags_match: "id:0")
assert_search_equals(@p1, has_parent: true)
assert_search_equals([@p3, @p2], has_parent: false)
end
end
context "for a polymorphic association" do
subject { ModerationReport }
should "work" do
as(create(:user)) do
@mr1 = create(:moderation_report, model: create(:comment))
@mr2 = create(:moderation_report, model: create(:forum_post))
@mr3 = create(:moderation_report, model: create(:dmail))
end
assert_search_equals(@mr1, model_type: "Comment")
assert_search_equals(@mr2, model_type: "ForumPost")
assert_search_equals(@mr3, model_type: "Dmail")
assert_search_equals(@mr1, model_type: "Comment", model_id: @mr1.model.id)
assert_search_equals(@mr2, model_type: "ForumPost", model_id: @mr2.model.id)
assert_search_equals(@mr3, model_type: "Dmail", model_id: @mr3.model.id)
assert_search_equals(@mr1, Comment: { body: @mr1.model.body })
assert_search_equals(@mr2, ForumPost: { body: @mr2.model.body })
assert_search_equals([], Dmail: { body: @mr3.model.body }, current_user: User.anonymous)
assert_search_equals(@mr3, Dmail: { body: @mr3.model.body }, current_user: @mr3.model.owner)
end
end
end
context "for a has_many association" do
should "work" do
as(@p1.uploader) { create(:comment, post: @p1) }
assert_search_equals(@p1, has_comments: true)
assert_search_equals([@p3, @p2], has_comments: false)
assert_search_equals(@p1, comments: { id: @p1.comments.first.id })
assert_search_equals(@p1, has_comments: true, comments: { id: @p1.comments.first.id })
end
end
end
end