Merge pull request #4553 from BrokenEagle/attribute-searching

Add attribute includes chaining on searches
This commit is contained in:
evazion
2020-08-17 14:45:35 -05:00
committed by GitHub
84 changed files with 1271 additions and 523 deletions

View File

@@ -1,6 +1,15 @@
module Searchable module Searchable
extend ActiveSupport::Concern extend ActiveSupport::Concern
def parameter_hash?(params)
params.present? && params.respond_to?(:each)
end
def parameter_depth(params)
return 0 if params.values.empty?
1 + params.values.map { |v| parameter_hash?(v) ? parameter_depth(v) : 1 }.max
end
def negate(kind = :nand) def negate(kind = :nand)
unscoped.where(all.where_clause.invert(kind).ast) unscoped.where(all.where_clause.invert(kind).ast)
end end
@@ -139,8 +148,13 @@ module Searchable
end end
def search_attributes(params, *attributes) def search_attributes(params, *attributes)
raise ArgumentError, "max parameter depth of 10 exceeded" if parameter_depth(params) > 10
# This allows the hash keys to be either strings or symbols
indifferent_params = params.try(:with_indifferent_access) || params.try(:to_unsafe_h)
raise ArgumentError, "unable to process params" if indifferent_params.nil?
attributes.reduce(all) do |relation, attribute| attributes.reduce(all) do |relation, attribute|
relation.search_attribute(attribute, params) relation.search_attribute(attribute, indifferent_params)
end end
end end
@@ -159,7 +173,9 @@ module Searchable
when "User" when "User"
search_user_attribute(name, params) search_user_attribute(name, params)
when "Post" when "Post"
search_post_id_attribute(params) search_post_attribute(name, params)
when "Model"
search_polymorphic_attribute(name, params)
when :string, :text when :string, :text
search_text_attribute(name, params) search_text_attribute(name, params)
when :boolean when :boolean
@@ -173,7 +189,8 @@ module Searchable
when :array when :array
search_array_attribute(name, subtype, params) search_array_attribute(name, subtype, params)
else else
raise NotImplementedError, "unhandled attribute type: #{name}" raise NotImplementedError, "unhandled attribute type: #{name}" if type.blank?
search_includes(name, params, type)
end end
end end
@@ -214,26 +231,56 @@ module Searchable
end end
def search_user_attribute(attr, params) def search_user_attribute(attr, params)
if params["#{attr}_id"] if params["#{attr}_name"].present?
search_attribute("#{attr}_id", params)
elsif params["#{attr}_name"]
where(attr => User.search(name_matches: params["#{attr}_name"]).reorder(nil)) where(attr => User.search(name_matches: params["#{attr}_name"]).reorder(nil))
elsif params[attr] else
where(attr => User.search(params[attr]).reorder(nil)) search_includes(attr, params, "User")
end
end
def search_post_attribute(attr, params)
if params["#{attr}_tags_match"]
where(attr => Post.user_tag_match(params["#{attr}_tags_match"]).reorder(nil))
else
search_includes(attr, params, "Post")
end
end
def search_includes(attr, params, type)
model = Kernel.const_get(type)
if params["#{attr}_id"].present?
search_attribute("#{attr}_id", params)
elsif params["has_#{attr}"].to_s.truthy? || params["has_#{attr}"].to_s.falsy?
search_has_include(attr, params["has_#{attr}"].to_s.truthy?, model)
elsif parameter_hash?(params[attr])
where(attr => model.search(params[attr]).reorder(nil))
else else
all all
end end
end end
def search_post_id_attribute(params) def search_polymorphic_attribute(attr, params)
relation = all model_keys = ((model_types || []) & params.keys)
# The user can only logically specify one model at a time, so more than that should return no results
return none if model_keys.length > 1
if params[:post_id].present? relation = all
relation = relation.search_attribute(:post_id, params) model_specified = false
model_key = model_keys[0]
if model_keys.length == 1 && parameter_hash?(params[model_key])
# Returning none here for the same reason specified above
return none if params["#{attr}_type"].present? && params["#{attr}_type"] != model_key
model_specified = true
model = Kernel.const_get(model_key)
relation = relation.where(attr => model.search(params[model_key]))
end end
if params[:post_tags_match].present? if params["#{attr}_id"].present?
relation = relation.where(post_id: Post.user_tag_match(params[:post_tags_match]).reorder(nil)) relation = relation.search_attribute("#{attr}_id", params)
end
if params["#{attr}_type"].present? && !model_specified
relation = relation.search_attribute("#{attr}_type", params)
end end
relation relation
@@ -292,6 +339,28 @@ module Searchable
relation relation
end end
def search_has_include(name, exists, model)
if column_names.include?("#{name}_id")
return exists ? where.not("#{name}_id" => nil) : where("#{name}_id" => nil)
end
association = reflect_on_association(name)
primary_key = association.active_record_primary_key
foreign_key = association.foreign_key
# The belongs_to macro has its primary and foreign keys reversed
primary_key, foreign_key = foreign_key, primary_key if association.macro == :belongs_to
return all if primary_key.nil? || foreign_key.nil?
self_table = arel_table
model_table = model.arel_table
model_exists = model.model_restriction(model_table).where(model_table[foreign_key].eq(self_table[primary_key])).exists
if exists
attribute_restriction(name).where(model_exists)
else
attribute_restriction(name).where.not(model_exists)
end
end
def apply_default_order(params) def apply_default_order(params)
if params[:order] == "custom" if params[:order] == "custom"
parse_ids = PostQueryBuilder.new(nil).parse_range(params[:id], :integer) parse_ids = PostQueryBuilder.new(nil).parse_range(params[:id], :integer)
@@ -319,7 +388,8 @@ module Searchable
params ||= {} params ||= {}
default_attributes = (attribute_names.map(&:to_sym) & %i[id created_at updated_at]) default_attributes = (attribute_names.map(&:to_sym) & %i[id created_at updated_at])
search_attributes(params, *default_attributes) all_attributes = default_attributes + searchable_includes
search_attributes(params, *all_attributes)
end end
private private

View File

@@ -93,6 +93,22 @@ class ApplicationRecord < ActiveRecord::Base
end end
end end
concerning :SearchMethods do
class_methods do
def searchable_includes
[]
end
def model_restriction(table)
table.project(1)
end
def attribute_restriction(*)
all
end
end
end
concerning :ActiveRecordExtensions do concerning :ActiveRecordExtensions do
class_methods do class_methods do
def without_timeout def without_timeout

View File

@@ -266,12 +266,6 @@ class Artist < ApplicationRecord
q = q.url_matches(params[:url_matches]) q = q.url_matches(params[:url_matches])
end end
if params[:has_tag].to_s.truthy?
q = q.where(name: Tag.nonempty.select(:name))
elsif params[:has_tag].to_s.falsy?
q = q.where.not(name: Tag.nonempty.select(:name))
end
case params[:order] case params[:order]
when "name" when "name"
q = q.order("artists.name") q = q.order("artists.name")
@@ -295,6 +289,14 @@ class Artist < ApplicationRecord
include BanMethods include BanMethods
extend SearchMethods extend SearchMethods
def self.model_restriction(table)
super.where(table[:is_deleted].eq(false))
end
def self.searchable_includes
[:urls, :wiki_page, :tag_alias, :tag]
end
def self.available_includes def self.available_includes
[:members, :urls, :wiki_page, :tag_alias, :tag] [:members, :urls, :wiki_page, :tag_alias, :tag]
end end

View File

@@ -33,7 +33,7 @@ class ArtistCommentary < ApplicationRecord
def search(params) def search(params)
q = super q = super
q = q.search_attributes(params, :post, :original_title, :original_description, :translated_title, :translated_description) q = q.search_attributes(params, :original_title, :original_description, :translated_title, :translated_description)
if params[:text_matches].present? if params[:text_matches].present?
q = q.text_matches(params[:text_matches]) q = q.text_matches(params[:text_matches])
@@ -146,6 +146,10 @@ class ArtistCommentary < ApplicationRecord
extend SearchMethods extend SearchMethods
include VersionMethods include VersionMethods
def self.searchable_includes
[:post]
end
def self.available_includes def self.available_includes
[:post] [:post]
end end

View File

@@ -2,9 +2,23 @@ class ArtistCommentaryVersion < ApplicationRecord
belongs_to :post belongs_to :post
belongs_to_updater belongs_to_updater
def self.text_matches(query)
query = "*#{query}*" unless query =~ /\*/
where_ilike(:original_title, query)
.or(where_ilike(:original_description, query))
.or(where_ilike(:translated_title, query))
.or(where_ilike(:translated_description, query))
end
def self.search(params) def self.search(params)
q = super q = super
q = q.search_attributes(params, :post, :updater, :original_title, :original_description, :translated_title, :translated_description) q = q.search_attributes(params, :original_title, :original_description, :translated_title, :translated_description)
if params[:text_matches].present?
q = q.text_matches(params[:text_matches])
end
q.apply_default_order(params) q.apply_default_order(params)
end end
@@ -42,6 +56,10 @@ class ArtistCommentaryVersion < ApplicationRecord
self[field].strip.empty? && (previous.nil? || previous[field].strip.empty?) self[field].strip.empty? && (previous.nil? || previous[field].strip.empty?)
end end
def self.searchable_includes
[:post, :updater]
end
def self.available_includes def self.available_includes
[:post, :updater] [:post, :updater]
end end

View File

@@ -42,9 +42,8 @@ class ArtistUrl < ApplicationRecord
def self.search(params = {}) def self.search(params = {})
q = super q = super
q = q.search_attributes(params, :url, :normalized_url, :artist_id, :is_active) q = q.search_attributes(params, :url, :normalized_url, :is_active)
q = q.artist_matches(params[:artist])
q = q.url_matches(params[:url_matches]) q = q.url_matches(params[:url_matches])
q = q.normalized_url_matches(params[:normalized_url_matches]) q = q.normalized_url_matches(params[:normalized_url_matches])
@@ -59,11 +58,6 @@ class ArtistUrl < ApplicationRecord
q q
end end
def self.artist_matches(params = {})
return all if params.blank?
where(artist_id: Artist.search(params).reorder(nil))
end
def self.url_attribute_matches(attr, url) def self.url_attribute_matches(attr, url)
if url.blank? if url.blank?
all all
@@ -134,6 +128,10 @@ class ArtistUrl < ApplicationRecord
errors[:url] << "'#{uri}' is malformed: #{error}" errors[:url] << "'#{uri}' is malformed: #{error}"
end end
def self.searchable_includes
[:artist]
end
def self.available_includes def self.available_includes
[:artist] [:artist]
end end

View File

@@ -9,7 +9,9 @@ class ArtistVersion < ApplicationRecord
def search(params) def search(params)
q = super q = super
q = q.search_attributes(params, :updater, :is_deleted, :is_banned, :artist_id, :name, :group_name) q = q.search_attributes(params, :is_deleted, :is_banned, :name, :group_name, :urls, :other_names)
q = q.text_attribute_matches(:name, params[:name_matches])
q = q.text_attribute_matches(:group_name, params[:group_name_matches])
if params[:order] == "name" if params[:order] == "name"
q = q.order("artist_versions.name").default_order q = q.order("artist_versions.name").default_order
@@ -103,6 +105,10 @@ class ArtistVersion < ApplicationRecord
end end
end end
def self.searchable_includes
[:updater, :artist]
end
def self.available_includes def self.available_includes
[:updater, :artist] [:updater, :artist]
end end

View File

@@ -22,7 +22,7 @@ class Ban < ApplicationRecord
def self.search(params) def self.search(params)
q = super q = super
q = q.search_attributes(params, :banner, :user, :expires_at, :reason) q = q.search_attributes(params, :expires_at, :reason)
q = q.text_attribute_matches(:reason, params[:reason_matches]) q = q.text_attribute_matches(:reason, params[:reason_matches])
q = q.expired if params[:expired].to_s.truthy? q = q.expired if params[:expired].to_s.truthy?
@@ -97,6 +97,10 @@ class Ban < ApplicationRecord
ModAction.log(%{Unbanned <@#{user_name}>}, :user_unban) ModAction.log(%{Unbanned <@#{user_name}>}, :user_unban)
end end
def self.searchable_includes
[:user, :banner]
end
def self.available_includes def self.available_includes
[:user, :banner] [:user, :banner]
end end

View File

@@ -35,7 +35,7 @@ class BulkUpdateRequest < ApplicationRecord
def search(params = {}) def search(params = {})
q = super q = super
q = q.search_attributes(params, :user, :approver, :forum_topic_id, :forum_post_id, :script, :tags) q = q.search_attributes(params, :script, :tags)
q = q.text_attribute_matches(:script, params[:script_matches]) q = q.text_attribute_matches(:script, params[:script_matches])
if params[:status].present? if params[:status].present?
@@ -152,6 +152,10 @@ class BulkUpdateRequest < ApplicationRecord
status == "rejected" status == "rejected"
end end
def self.searchable_includes
[:user, :forum_topic, :forum_post, :approver]
end
def self.available_includes def self.available_includes
[:user, :forum_topic, :forum_post, :approver] [:user, :forum_topic, :forum_post, :approver]
end end

View File

@@ -28,7 +28,7 @@ class Comment < ApplicationRecord
def search(params) def search(params)
q = super q = super
q = q.search_attributes(params, :post, :creator, :updater, :is_deleted, :is_sticky, :do_not_bump_post, :body, :score) q = q.search_attributes(params, :is_deleted, :is_sticky, :do_not_bump_post, :body, :score)
q = q.text_attribute_matches(:body, params[:body_matches], index_column: :body_index) q = q.text_attribute_matches(:body, params[:body_matches], index_column: :body_index)
case params[:order] case params[:order]
@@ -140,6 +140,10 @@ class Comment < ApplicationRecord
DText.quote(body, creator.name) DText.quote(body, creator.name)
end end
def self.searchable_includes
[:post, :creator, :updater]
end
def self.available_includes def self.available_includes
[:post, :creator, :updater] [:post, :creator, :updater]
end end

View File

@@ -18,15 +18,9 @@ class CommentVote < ApplicationRecord
end end
end end
def self.comment_matches(params)
return all if params.blank?
where(comment_id: Comment.search(params).reorder(nil).select(:id))
end
def self.search(params) def self.search(params)
q = super q = super
q = q.search_attributes(params, :comment_id, :user, :score) q = q.search_attributes(params, :score)
q = q.comment_matches(params[:comment])
q.apply_default_order(params) q.apply_default_order(params)
end end
@@ -44,6 +38,10 @@ class CommentVote < ApplicationRecord
score == -1 score == -1
end end
def self.searchable_includes
[:comment, :user]
end
def self.available_includes def self.available_includes
[:comment, :user] [:comment, :user]
end end

View File

@@ -98,7 +98,7 @@ class Dmail < ApplicationRecord
def search(params) def search(params)
q = super q = super
q = q.search_attributes(params, :to, :from, :is_read, :is_deleted, :title, :body) q = q.search_attributes(params, :is_read, :is_deleted, :title, :body)
q = q.text_attribute_matches(:title, params[:title_matches]) q = q.text_attribute_matches(:title, params[:title_matches])
q = q.text_attribute_matches(:body, params[:message_matches], index_column: :message_index) q = q.text_attribute_matches(:body, params[:message_matches], index_column: :message_index)
@@ -172,6 +172,10 @@ class Dmail < ApplicationRecord
key ? "dmail ##{id}/#{self.key}" : "dmail ##{id}" key ? "dmail ##{id}/#{self.key}" : "dmail ##{id}"
end end
def self.searchable_includes
[:to, :from]
end
def self.available_includes def self.available_includes
[:owner, :to, :from] [:owner, :to, :from]
end end

View File

@@ -1,6 +1,7 @@
class DtextLink < ApplicationRecord class DtextLink < ApplicationRecord
belongs_to :model, polymorphic: true belongs_to :model, polymorphic: true
belongs_to :linked_wiki, primary_key: :title, foreign_key: :link_target, class_name: "WikiPage", optional: true belongs_to :linked_wiki, primary_key: :title, foreign_key: :link_target, class_name: "WikiPage", optional: true
belongs_to :linked_tag, primary_key: :name, foreign_key: :link_target, class_name: "Tag", optional: true
enum link_type: [:wiki_link, :external_link] enum link_type: [:wiki_link, :external_link]
@@ -28,43 +29,9 @@ class DtextLink < ApplicationRecord
links links
end end
def self.model_matches(params)
return all if params.blank?
where(model_type: "WikiPage", model_id: WikiPage.search(params).reorder(nil))
end
def self.linked_wiki_exists(exists = true)
dtext_links = DtextLink.arel_table
wiki_pages = WikiPage.arel_table
wiki_exists = wiki_pages.project(1).where(wiki_pages[:is_deleted].eq(false)).where(wiki_pages[:title].eq(dtext_links[:link_target])).exists
if exists
where(link_type: :wiki_link).where(wiki_exists)
else
where(link_type: :wiki_link).where.not(wiki_exists)
end
end
def self.linked_tag_exists(exists = true)
dtext_links = DtextLink.arel_table
tags = Tag.arel_table
tag_exists = tags.project(1).where(tags[:post_count].gt(0)).where(tags[:name].eq(dtext_links[:link_target])).exists
if exists
where(link_type: :wiki_link).where(tag_exists)
else
where(link_type: :wiki_link).where.not(tag_exists)
end
end
def self.search(params) def self.search(params)
q = super q = super
q = q.search_attributes(params, :model_type, :model_id, :link_type, :link_target) q = q.search_attributes(params, :link_type, :link_target)
q = q.model_matches(params[:model])
q = q.linked_wiki_exists(params[:linked_wiki_exists].truthy?) if params[:linked_wiki_exists].present?
q = q.linked_tag_exists(params[:linked_tag_exists].truthy?) if params[:linked_tag_exists].present?
q.apply_default_order(params) q.apply_default_order(params)
end end
@@ -78,7 +45,15 @@ class DtextLink < ApplicationRecord
self.link_target = self.link_target.truncate(2048, omission: "") self.link_target = self.link_target.truncate(2048, omission: "")
end end
def self.attribute_restriction(*)
where(link_type: :wiki_link)
end
def self.searchable_includes
[:model, :linked_wiki, :linked_tag]
end
def self.available_includes def self.available_includes
[:model] [:model, :linked_wiki, :linked_tag]
end end
end end

View File

@@ -27,7 +27,7 @@ class FavoriteGroup < ApplicationRecord
def search(params) def search(params)
q = super q = super
q = q.search_attributes(params, :name, :is_public, :post_ids, :creator) q = q.search_attributes(params, :name, :is_public, :post_ids)
if params[:name_matches].present? if params[:name_matches].present?
q = q.name_matches(params[:name_matches]) q = q.name_matches(params[:name_matches])
@@ -164,6 +164,10 @@ class FavoriteGroup < ApplicationRecord
post_ids.include?(post_id) post_ids.include?(post_id)
end end
def self.searchable_includes
[:creator]
end
def self.available_includes def self.available_includes
[:creator] [:creator]
end end

View File

@@ -32,31 +32,19 @@ class ForumPost < ApplicationRecord
) )
module SearchMethods module SearchMethods
def topic_title_matches(title)
where(topic_id: ForumTopic.search(title_matches: title).select(:id))
end
def visible(user) def visible(user)
where(topic_id: ForumTopic.visible(user)) where(topic_id: ForumTopic.visible(user))
end end
def search(params) def search(params)
q = super q = super
q = q.search_attributes(params, :creator, :updater, :topic_id, :is_deleted, :body) q = q.search_attributes(params, :is_deleted, :body)
q = q.text_attribute_matches(:body, params[:body_matches], index_column: :text_index) q = q.text_attribute_matches(:body, params[:body_matches], index_column: :text_index)
if params[:linked_to].present? if params[:linked_to].present?
q = q.where(id: DtextLink.forum_post.wiki_link.where(link_target: params[:linked_to]).select(:model_id)) q = q.where(id: DtextLink.forum_post.wiki_link.where(link_target: params[:linked_to]).select(:model_id))
end end
if params[:topic_title_matches].present?
q = q.topic_title_matches(params[:topic_title_matches])
end
if params[:topic_category_id].present?
q = q.where(topic_id: ForumTopic.where(category_id: params[:topic_category_id]))
end
q.apply_default_order(params) q.apply_default_order(params)
end end
end end
@@ -179,6 +167,10 @@ class ForumPost < ApplicationRecord
"forum ##{id}" "forum ##{id}"
end end
def self.searchable_includes
[:creator, :updater, :topic, :dtext_links, :votes, :tag_alias, :tag_implication, :bulk_update_request]
end
def self.available_includes def self.available_includes
[:creator, :updater, :topic, :dtext_links, :votes, :tag_alias, :tag_implication, :bulk_update_request] [:creator, :updater, :topic, :dtext_links, :votes, :tag_alias, :tag_implication, :bulk_update_request]
end end

View File

@@ -20,7 +20,7 @@ class ForumPostVote < ApplicationRecord
def self.search(params) def self.search(params)
q = super q = super
q = q.search_attributes(params, :creator, :forum_post_id, :score) q = q.search_attributes(params, :score)
q = q.forum_post_matches(params[:forum_post]) q = q.forum_post_matches(params[:forum_post])
q.apply_default_order(params) q.apply_default_order(params)
end end
@@ -59,7 +59,11 @@ class ForumPostVote < ApplicationRecord
end end
end end
def self.searchable_includes
[:creator, :forum_post]
end
def self.available_includes def self.available_includes
[:creator] [:creator, :forum_post]
end end
end end

View File

@@ -19,6 +19,8 @@ class ForumTopic < ApplicationRecord
has_many :moderation_reports, through: :forum_posts has_many :moderation_reports, through: :forum_posts
has_one :original_post, -> { order(id: :asc) }, class_name: "ForumPost", foreign_key: "topic_id", inverse_of: :topic has_one :original_post, -> { order(id: :asc) }, class_name: "ForumPost", foreign_key: "topic_id", inverse_of: :topic
has_many :bulk_update_requests, :foreign_key => "forum_topic_id" has_many :bulk_update_requests, :foreign_key => "forum_topic_id"
has_many :tag_aliases, :foreign_key => "forum_topic_id"
has_many :tag_implications, :foreign_key => "forum_topic_id"
validates_presence_of :title validates_presence_of :title
validates_associated :original_post validates_associated :original_post
@@ -85,7 +87,7 @@ class ForumTopic < ApplicationRecord
def search(params) def search(params)
q = super q = super
q = q.search_attributes(params, :creator, :updater, :is_sticky, :is_locked, :is_deleted, :category_id, :title, :response_count) q = q.search_attributes(params, :is_sticky, :is_locked, :is_deleted, :category_id, :title, :response_count)
q = q.text_attribute_matches(:title, params[:title_matches], index_column: :text_index) q = q.text_attribute_matches(:title, params[:title_matches], index_column: :text_index)
if params[:is_private].to_s.truthy? if params[:is_private].to_s.truthy?
@@ -111,6 +113,8 @@ class ForumTopic < ApplicationRecord
case params[:order] case params[:order]
when "sticky" when "sticky"
q = q.sticky_first q = q.sticky_first
when "id"
q = q.order(id: :desc)
else else
q = q.apply_default_order(params) q = q.apply_default_order(params)
end end
@@ -190,6 +194,10 @@ class ForumTopic < ApplicationRecord
super + [:is_read?] super + [:is_read?]
end end
def self.searchable_includes
[:creator, :updater, :forum_posts, :bulk_update_requests, :tag_aliases, :tag_implications]
end
def self.available_includes def self.available_includes
[:creator, :updater, :original_post] [:creator, :updater, :original_post]
end end

View File

@@ -13,7 +13,7 @@ class IpAddress < ApplicationRecord
def self.search(params) def self.search(params)
q = super q = super
q = q.search_attributes(params, :user, :model_type, :model_id, :ip_addr) q = q.search_attributes(params, :ip_addr)
q.order(created_at: :desc) q.order(created_at: :desc)
end end
@@ -54,6 +54,10 @@ class IpAddress < ApplicationRecord
super & attributes.keys.map(&:to_sym) super & attributes.keys.map(&:to_sym)
end end
def self.searchable_includes
[:user, :model]
end
def self.available_includes def self.available_includes
[:user, :model] [:user, :model]
end end

View File

@@ -26,7 +26,8 @@ class IpBan < ApplicationRecord
def self.search(params) def self.search(params)
q = super q = super
q = q.search_attributes(params, :creator, :reason) q = q.search_attributes(params, :reason)
q = q.text_attribute_matches(:reason, params[:reason_matches])
if params[:ip_addr].present? if params[:ip_addr].present?
q = q.where("ip_addr = ?", params[:ip_addr]) q = q.where("ip_addr = ?", params[:ip_addr])
@@ -77,6 +78,10 @@ class IpBan < ApplicationRecord
super(ip_addr.strip) super(ip_addr.strip)
end end
def self.searchable_includes
[:creator]
end
def self.available_includes def self.available_includes
[:creator] [:creator]
end end

View File

@@ -63,7 +63,7 @@ class ModAction < ApplicationRecord
def self.search(params) def self.search(params)
q = super q = super
q = q.search_attributes(params, :creator, :category, :description) q = q.search_attributes(params, :category, :description)
q = q.text_attribute_matches(:description, params[:description_matches]) q = q.text_attribute_matches(:description, params[:description_matches])
q.apply_default_order(params) q.apply_default_order(params)
@@ -77,6 +77,10 @@ class ModAction < ApplicationRecord
create(creator: user, description: desc, category: categories[cat]) create(creator: user, description: desc, category: categories[cat])
end end
def self.searchable_includes
[:creator]
end
def self.available_includes def self.available_includes
[:creator] [:creator]
end end

View File

@@ -83,11 +83,16 @@ class ModerationReport < ApplicationRecord
def self.search(params) def self.search(params)
q = super q = super
q = q.search_attributes(params, :model_type, :model_id, :creator_id) q = q.search_attributes(params, :reason)
q = q.text_attribute_matches(:reason, params[:reason_matches])
q.apply_default_order(params) q.apply_default_order(params)
end end
def self.searchable_includes
[:creator, :model]
end
def self.available_includes def self.available_includes
[:creator, :model] [:creator, :model]
end end

View File

@@ -16,7 +16,7 @@ class Note < ApplicationRecord
def search(params) def search(params)
q = super q = super
q = q.search_attributes(params, :post, :is_active, :x, :y, :width, :height, :body, :version) q = q.search_attributes(params, :is_active, :x, :y, :width, :height, :body, :version)
q = q.text_attribute_matches(:body, params[:body_matches], index_column: :body_index) q = q.text_attribute_matches(:body, params[:body_matches], index_column: :body_index)
q.apply_default_order(params) q.apply_default_order(params)
@@ -129,6 +129,10 @@ class Note < ApplicationRecord
new_note.save new_note.save
end end
def self.searchable_includes
[:post]
end
def self.available_includes def self.available_includes
[:post] [:post]
end end

View File

@@ -6,7 +6,7 @@ class NoteVersion < ApplicationRecord
def self.search(params) def self.search(params)
q = super q = super
q = q.search_attributes(params, :updater, :is_active, :post, :note_id, :x, :y, :width, :height, :body, :version) q = q.search_attributes(params, :is_active, :x, :y, :width, :height, :body, :version)
q = q.text_attribute_matches(:body, params[:body_matches]) q = q.text_attribute_matches(:body, params[:body_matches])
q.apply_default_order(params) q.apply_default_order(params)
@@ -71,6 +71,10 @@ class NoteVersion < ApplicationRecord
end end
end end
def self.searchable_includes
[:updater, :note, :post]
end
def self.available_includes def self.available_includes
[:updater, :note, :post] [:updater, :note, :post]
end end

View File

@@ -4,13 +4,17 @@ class PixivUgoiraFrameData < ApplicationRecord
serialize :data serialize :data
before_validation :normalize_data, on: :create before_validation :normalize_data, on: :create
def self.searchable_includes
[:post]
end
def self.available_includes def self.available_includes
[:post] [:post]
end end
def self.search(params) def self.search(params)
q = super q = super
q = q.search_attributes(params, :post, :data, :content_type) q = q.search_attributes(params, :data, :content_type)
q.apply_default_order(params) q.apply_default_order(params)
end end

View File

@@ -1291,12 +1291,12 @@ class Post < ApplicationRecord
def search(params) def search(params)
q = super q = super
q = q.search_attributes(params, q = q.search_attributes(
:approver, :uploader, :rating, :source, :pixiv_id, :fav_count, :score, :up_score, params,
:down_score, :md5, :file_ext, :file_size, :image_width, :image_height, :tag_count, :rating, :source, :pixiv_id, :fav_count, :score, :up_score, :down_score, :md5, :file_ext,
:parent, :has_children, :has_active_children, :is_note_locked, :is_rating_locked, :file_size, :image_width, :image_height, :tag_count, :has_children, :has_active_children,
:is_status_locked, :is_pending, :is_flagged, :is_deleted, :is_banned, :is_note_locked, :is_rating_locked, :is_status_locked, :is_pending, :is_flagged, :is_deleted,
:last_comment_bumped_at, :last_commented_at, :last_noted_at :is_banned, :last_comment_bumped_at, :last_commented_at, :last_noted_at
) )
if params[:tags].present? if params[:tags].present?
@@ -1508,6 +1508,14 @@ class Post < ApplicationRecord
super + [:has_large?, :current_image_size] super + [:has_large?, :current_image_size]
end end
def self.model_restriction(table)
super.where(table[:is_pending].eq(false)).where(table[:is_flagged].eq(false)).where(table[:is_deleted].eq(false))
end
def self.searchable_includes
[:uploader, :updater, :approver, :parent, :upload, :artist_commentary, :flags, :appeals, :notes, :comments, :children, :approvals, :replacements, :pixiv_ugoira_frame_data]
end
def self.available_includes def self.available_includes
[:uploader, :updater, :approver, :parent, :upload, :artist_commentary, :flags, :appeals, :notes, :comments, :children, :approvals, :replacements, :pixiv_ugoira_frame_data] [:uploader, :updater, :approver, :parent, :upload, :artist_commentary, :flags, :appeals, :notes, :comments, :children, :approvals, :replacements, :pixiv_ugoira_frame_data]
end end

View File

@@ -18,7 +18,7 @@ class PostAppeal < ApplicationRecord
module SearchMethods module SearchMethods
def search(params) def search(params)
q = super q = super
q = q.search_attributes(params, :creator, :post, :reason, :status) q = q.search_attributes(params, :reason, :status)
q = q.text_attribute_matches(:reason, params[:reason_matches]) q = q.text_attribute_matches(:reason, params[:reason_matches])
q.apply_default_order(params) q.apply_default_order(params)
@@ -35,6 +35,10 @@ class PostAppeal < ApplicationRecord
errors[:post] << "cannot be appealed" if post.is_status_locked? || !post.is_appealable? errors[:post] << "cannot be appealed" if post.is_status_locked? || !post.is_appealable?
end end
def self.searchable_includes
[:creator, :post]
end
def self.available_includes def self.available_includes
[:creator, :post] [:creator, :post]
end end

View File

@@ -39,10 +39,13 @@ class PostApproval < ApplicationRecord
def self.search(params) def self.search(params)
q = super q = super
q = q.search_attributes(params, :user, :post)
q.apply_default_order(params) q.apply_default_order(params)
end end
def self.searchable_includes
[:user, :post]
end
def self.available_includes def self.available_includes
[:user, :post] [:user, :post]
end end

View File

@@ -23,7 +23,7 @@ class PostDisapproval < ApplicationRecord
def search(params) def search(params)
q = super q = super
q = q.search_attributes(params, :post, :user, :message, :reason) q = q.search_attributes(params, :message, :reason)
q = q.text_attribute_matches(:message, params[:message_matches]) q = q.text_attribute_matches(:message, params[:message_matches])
q = q.with_message if params[:has_message].to_s.truthy? q = q.with_message if params[:has_message].to_s.truthy?
@@ -41,6 +41,10 @@ class PostDisapproval < ApplicationRecord
end end
end end
def self.searchable_includes
[:user, :post]
end
def self.available_includes def self.available_includes
[:user, :post] [:user, :post]
end end

View File

@@ -58,7 +58,7 @@ class PostFlag < ApplicationRecord
def search(params) def search(params)
q = super q = super
q = q.search_attributes(params, :post, :reason, :status) q = q.search_attributes(params, :reason, :status)
q = q.text_attribute_matches(:reason, params[:reason_matches]) q = q.text_attribute_matches(:reason, params[:reason_matches])
if params[:creator_id].present? if params[:creator_id].present?
@@ -113,6 +113,10 @@ class PostFlag < ApplicationRecord
post.uploader_id post.uploader_id
end end
def self.searchable_includes
[:post]
end
def self.available_includes def self.available_includes
[:post] [:post]
end end

View File

@@ -22,7 +22,7 @@ class PostReplacement < ApplicationRecord
class_methods do class_methods do
def search(params = {}) def search(params = {})
q = super q = super
q = q.search_attributes(params, :post, :creator, :md5, :md5_was, :file_ext, :file_ext_was, :original_url, :replacement_url) q = q.search_attributes(params, :md5, :md5_was, :file_ext, :file_ext_was, :original_url, :replacement_url)
q.apply_default_order(params) q.apply_default_order(params)
end end
end end
@@ -39,6 +39,10 @@ class PostReplacement < ApplicationRecord
tags.join(" ") tags.join(" ")
end end
def self.searchable_includes
[:creator, :post]
end
def self.available_includes def self.available_includes
[:creator, :post] [:creator, :post]
end end

View File

@@ -20,12 +20,12 @@ class PostVote < ApplicationRecord
def self.search(params) def self.search(params)
q = super q = super
q = q.search_attributes(params, :post, :user, :score) q = q.search_attributes(params, :score)
q.apply_default_order(params) q.apply_default_order(params)
end end
def initialize_attributes def initialize_attributes
self.user_id ||= CurrentUser.user.id self.user_id ||= CurrentUser.id
if vote == "up" if vote == "up"
self.score = 1 self.score = 1
@@ -50,6 +50,10 @@ class PostVote < ApplicationRecord
end end
end end
def self.searchable_includes
[:user, :post]
end
def self.available_includes def self.available_includes
[:user, :post] [:user, :post]
end end

View File

@@ -5,6 +5,7 @@ class Tag < ApplicationRecord
has_many :consequent_aliases, -> {active}, :class_name => "TagAlias", :foreign_key => "consequent_name", :primary_key => "name" has_many :consequent_aliases, -> {active}, :class_name => "TagAlias", :foreign_key => "consequent_name", :primary_key => "name"
has_many :antecedent_implications, -> {active}, :class_name => "TagImplication", :foreign_key => "antecedent_name", :primary_key => "name" has_many :antecedent_implications, -> {active}, :class_name => "TagImplication", :foreign_key => "antecedent_name", :primary_key => "name"
has_many :consequent_implications, -> {active}, :class_name => "TagImplication", :foreign_key => "consequent_name", :primary_key => "name" has_many :consequent_implications, -> {active}, :class_name => "TagImplication", :foreign_key => "consequent_name", :primary_key => "name"
has_many :dtext_links, foreign_key: :link_target, primary_key: :name
validates :name, tag_name: true, uniqueness: true, on: :create validates :name, tag_name: true, uniqueness: true, on: :create
validates :name, tag_name: true, on: :name validates :name, tag_name: true, on: :name
@@ -284,18 +285,6 @@ class Tag < ApplicationRecord
q = q.nonempty q = q.nonempty
end end
if params[:has_wiki].to_s.truthy?
q = q.joins(:wiki_page).merge(WikiPage.undeleted)
elsif params[:has_wiki].to_s.falsy?
q = q.left_outer_joins(:wiki_page).where("wiki_pages.title IS NULL OR wiki_pages.is_deleted = TRUE")
end
if params[:has_artist].to_s.truthy?
q = q.joins(:artist).merge(Artist.undeleted)
elsif params[:has_artist].to_s.falsy?
q = q.left_outer_joins(:artist).where("artists.name IS NULL OR artists.is_deleted = TRUE")
end
case params[:order] case params[:order]
when "name" when "name"
q = q.order("name") q = q.order("name")
@@ -355,8 +344,16 @@ class Tag < ApplicationRecord
Post.system_tag_match(name) Post.system_tag_match(name)
end end
def self.model_restriction(table)
super.where(table[:post_count].gt(0))
end
def self.searchable_includes
[:wiki_page, :artist, :antecedent_alias, :consequent_aliases, :antecedent_implications, :consequent_implications, :dtext_links]
end
def self.available_includes def self.available_includes
[:wiki_page, :artist, :antecedent_alias, :consequent_aliases, :antecedent_implications, :consequent_implications] [:wiki_page, :artist, :antecedent_alias, :consequent_aliases, :antecedent_implications, :consequent_implications, :dtext_links]
end end
include ApiMethods include ApiMethods

View File

@@ -12,8 +12,8 @@ class TagRelationship < ApplicationRecord
belongs_to :forum_topic, optional: true belongs_to :forum_topic, optional: true
belongs_to :antecedent_tag, class_name: "Tag", foreign_key: "antecedent_name", primary_key: "name", default: -> { Tag.find_or_create_by_name(antecedent_name) } belongs_to :antecedent_tag, class_name: "Tag", foreign_key: "antecedent_name", primary_key: "name", default: -> { Tag.find_or_create_by_name(antecedent_name) }
belongs_to :consequent_tag, class_name: "Tag", foreign_key: "consequent_name", primary_key: "name", default: -> { Tag.find_or_create_by_name(consequent_name) } belongs_to :consequent_tag, class_name: "Tag", foreign_key: "consequent_name", primary_key: "name", default: -> { Tag.find_or_create_by_name(consequent_name) }
has_one :antecedent_wiki, through: :antecedent_tag, source: :wiki_page belongs_to :antecedent_wiki, class_name: "WikiPage", foreign_key: "antecedent_name", primary_key: "title", optional: true
has_one :consequent_wiki, through: :consequent_tag, source: :wiki_page belongs_to :consequent_wiki, class_name: "WikiPage", foreign_key: "antecedent_name", primary_key: "title", optional: true
scope :active, -> {approved} scope :active, -> {approved}
scope :approved, -> {where(status: %w[active processing queued])} scope :approved, -> {where(status: %w[active processing queued])}
@@ -94,7 +94,7 @@ class TagRelationship < ApplicationRecord
def search(params) def search(params)
q = super q = super
q = q.search_attributes(params, :creator, :approver, :forum_topic_id, :forum_post_id, :antecedent_name, :consequent_name) q = q.search_attributes(params, :antecedent_name, :consequent_name)
if params[:name_matches].present? if params[:name_matches].present?
q = q.name_matches(params[:name_matches]) q = q.name_matches(params[:name_matches])
@@ -157,6 +157,14 @@ class TagRelationship < ApplicationRecord
end end
end end
def self.model_restriction(table)
super.where(table[:status].eq("active"))
end
def self.searchable_includes
[:creator, :approver, :forum_post, :forum_topic, :antecedent_tag, :consequent_tag, :antecedent_wiki, :consequent_wiki]
end
def self.available_includes def self.available_includes
[:creator, :approver, :forum_post, :forum_topic, :antecedent_tag, :consequent_tag, :antecedent_wiki, :consequent_wiki] [:creator, :approver, :forum_post, :forum_topic, :antecedent_tag, :consequent_tag, :antecedent_wiki, :consequent_wiki]
end end

View File

@@ -184,7 +184,7 @@ class Upload < ApplicationRecord
def self.search(params) def self.search(params)
q = super q = super
q = q.search_attributes(params, :uploader, :post, :source, :rating, :parent_id, :server, :md5, :server, :file_ext, :file_size, :image_width, :image_height, :referer_url) q = q.search_attributes(params, :source, :rating, :parent_id, :server, :md5, :server, :file_ext, :file_size, :image_width, :image_height, :referer_url)
if params[:source_matches].present? if params[:source_matches].present?
q = q.where_like(:source, params[:source_matches]) q = q.where_like(:source, params[:source_matches])
@@ -225,6 +225,10 @@ class Upload < ApplicationRecord
artist_commentary_title.present? || artist_commentary_desc.present? || translated_commentary_title.present? || translated_commentary_desc.present? artist_commentary_title.present? || artist_commentary_desc.present? || translated_commentary_title.present? || translated_commentary_desc.present?
end end
def self.searchable_includes
[:uploader, :post]
end
def self.available_includes def self.available_includes
[:uploader, :post] [:uploader, :post]
end end

View File

@@ -80,7 +80,6 @@ class User < ApplicationRecord
validates_presence_of :comment_threshold validates_presence_of :comment_threshold
before_validation :normalize_blacklisted_tags before_validation :normalize_blacklisted_tags
before_create :promote_to_admin_if_first_user before_create :promote_to_admin_if_first_user
has_many :artists, foreign_key: :creator_id
has_many :artist_versions, foreign_key: :updater_id has_many :artist_versions, foreign_key: :updater_id
has_many :artist_commentary_versions, foreign_key: :updater_id has_many :artist_commentary_versions, foreign_key: :updater_id
has_many :comments, foreign_key: :creator_id has_many :comments, foreign_key: :creator_id
@@ -91,7 +90,6 @@ class User < ApplicationRecord
has_many :forum_topic_visits, dependent: :destroy has_many :forum_topic_visits, dependent: :destroy
has_many :visited_forum_topics, through: :forum_topic_visits, source: :forum_topic has_many :visited_forum_topics, through: :forum_topic_visits, source: :forum_topic
has_many :moderation_reports, as: :model has_many :moderation_reports, as: :model
has_many :pools, foreign_key: :creator_id
has_many :posts, :foreign_key => "uploader_id" has_many :posts, :foreign_key => "uploader_id"
has_many :post_appeals, foreign_key: :creator_id has_many :post_appeals, foreign_key: :creator_id
has_many :post_approvals, :dependent => :destroy has_many :post_approvals, :dependent => :destroy
@@ -105,10 +103,10 @@ class User < ApplicationRecord
has_one :api_key has_one :api_key
has_one :token_bucket has_one :token_bucket
has_one :email_address, dependent: :destroy has_one :email_address, dependent: :destroy
has_many :notes, foreign_key: :creator_id
has_many :note_versions, :foreign_key => "updater_id" has_many :note_versions, :foreign_key => "updater_id"
has_many :dmails, -> {order("dmails.id desc")}, :foreign_key => "owner_id" has_many :dmails, -> {order("dmails.id desc")}, :foreign_key => "owner_id"
has_many :saved_searches has_many :saved_searches
has_many :forum_topics, :foreign_key => "creator_id"
has_many :forum_posts, -> {order("forum_posts.created_at, forum_posts.id")}, :foreign_key => "creator_id" has_many :forum_posts, -> {order("forum_posts.created_at, forum_posts.id")}, :foreign_key => "creator_id"
has_many :user_name_change_requests, -> {order("user_name_change_requests.created_at desc")} has_many :user_name_change_requests, -> {order("user_name_change_requests.created_at desc")}
has_many :favorite_groups, -> {order(name: :asc)}, foreign_key: :creator_id has_many :favorite_groups, -> {order(name: :asc)}, foreign_key: :creator_id
@@ -537,7 +535,7 @@ class User < ApplicationRecord
params = params.dup params = params.dup
params[:name_matches] = params.delete(:name) if params[:name].present? params[:name_matches] = params.delete(:name) if params[:name].present?
q = q.search_attributes(params, :name, :level, :inviter, :post_upload_count, :post_update_count, :note_update_count, :favorite_count) q = q.search_attributes(params, :name, :level, :post_upload_count, :post_update_count, :note_update_count, :favorite_count)
if params[:name_matches].present? if params[:name_matches].present?
q = q.where_ilike(:name, normalize_name(params[:name_matches])) q = q.where_ilike(:name, normalize_name(params[:name_matches]))
@@ -606,6 +604,10 @@ class User < ApplicationRecord
"<@#{name}>" "<@#{name}>"
end end
def self.searchable_includes
[:posts, :note_versions, :artist_commentary_versions, :post_appeals, :post_approvals, :artist_versions, :comments, :wiki_page_versions, :feedback, :forum_topics, :forum_posts, :forum_post_votes, :tag_aliases, :tag_implications, :bans, :inviter]
end
def self.available_includes def self.available_includes
[:inviter] [:inviter]
end end

View File

@@ -32,7 +32,7 @@ class UserFeedback < ApplicationRecord
def search(params) def search(params)
q = super q = super
q = q.search_attributes(params, :user, :creator, :category, :body, :is_deleted) q = q.search_attributes(params, :category, :body, :is_deleted)
q = q.text_attribute_matches(:body, params[:body_matches]) q = q.text_attribute_matches(:body, params[:body_matches])
q.apply_default_order(params) q.apply_default_order(params)
@@ -65,6 +65,10 @@ class UserFeedback < ApplicationRecord
Dmail.create_automated(:to_id => user_id, :title => "Your user record has been updated", :body => body) Dmail.create_automated(:to_id => user_id, :title => "Your user record has been updated", :body => body)
end end
def self.searchable_includes
[:creator, :user]
end
def self.available_includes def self.available_includes
[:creator, :user] [:creator, :user]
end end

View File

@@ -48,10 +48,6 @@ class WikiPage < ApplicationRecord
end end
end end
def tag_matches(params)
where(title: Tag.search(params).select(:name).reorder(nil))
end
def linked_to(title) def linked_to(title)
where(dtext_links: DtextLink.wiki_page.wiki_link.where(link_target: normalize_title(title))) where(dtext_links: DtextLink.wiki_page.wiki_link.where(link_target: normalize_title(title)))
end end
@@ -78,10 +74,6 @@ class WikiPage < ApplicationRecord
q = q.other_names_match(params[:other_names_match]) q = q.other_names_match(params[:other_names_match])
end end
if params[:tag].present?
q = q.tag_matches(params[:tag])
end
if params[:linked_to].present? if params[:linked_to].present?
q = q.linked_to(params[:linked_to]) q = q.linked_to(params[:linked_to])
end end
@@ -197,12 +189,12 @@ class WikiPage < ApplicationRecord
def merge_version? def merge_version?
prev = versions.last prev = versions.last
prev && prev.updater_id == CurrentUser.user.id && prev.updated_at > 1.hour.ago prev && prev.updater_id == CurrentUser.id && prev.updated_at > 1.hour.ago
end end
def create_new_version def create_new_version
versions.create( versions.create(
:updater_id => CurrentUser.user.id, :updater_id => CurrentUser.id,
:updater_ip_addr => CurrentUser.ip_addr, :updater_ip_addr => CurrentUser.ip_addr,
:title => title, :title => title,
:body => body, :body => body,
@@ -245,6 +237,14 @@ class WikiPage < ApplicationRecord
end end
end end
def self.model_restriction(table)
super.where(table[:is_deleted].eq(false))
end
def self.searchable_includes
[:tag, :artist, :dtext_links]
end
def self.available_includes def self.available_includes
[:tag, :artist, :dtext_links] [:tag, :artist, :dtext_links]
end end

View File

@@ -9,9 +9,9 @@ class WikiPageVersion < ApplicationRecord
def search(params) def search(params)
q = super q = super
q = q.search_attributes(params, :updater, :is_locked, :is_deleted, :wiki_page_id) q = q.search_attributes(params, :title, :body, :other_names, :is_locked, :is_deleted)
q = q.text_attribute_matches(:title, params[:title]) q = q.text_attribute_matches(:title, params[:title_matches])
q = q.text_attribute_matches(:body, params[:body]) q = q.text_attribute_matches(:body, params[:body_matches])
q.apply_default_order(params) q.apply_default_order(params)
end end
@@ -77,7 +77,11 @@ class WikiPageVersion < ApplicationRecord
end end
end end
def self.searchable_includes
[:updater, :wiki_page, :artist, :tag]
end
def self.available_includes def self.available_includes
[:updater, :wiki_page, :artist] [:updater, :wiki_page, :artist, :tag]
end end
end end

View File

@@ -4,8 +4,8 @@
<%= f.input :link_target_ilike, label: "Link", hint: "Use * for wildcard", input_html: { value: params[:search][:link_target_ilike], data: { autocomplete: "wiki-page" } } %> <%= f.input :link_target_ilike, label: "Link", hint: "Use * for wildcard", input_html: { value: params[:search][:link_target_ilike], data: { autocomplete: "wiki-page" } } %>
<%= f.input :model_type, label: "Page Type", collection: [["Wiki Page", "WikiPage"], ["Forum Post", "ForumPost"]], include_blank: true, selected: params[:search][:model_type] %> <%= f.input :model_type, label: "Page Type", collection: [["Wiki Page", "WikiPage"], ["Forum Post", "ForumPost"]], include_blank: true, selected: params[:search][:model_type] %>
<%= f.input :link_type, label: "Link Type", collection: [["Wiki", "0"], ["External", "1"]], include_blank: true, selected: params[:search][:link_type] %> <%= f.input :link_type, label: "Link Type", collection: [["Wiki", "0"], ["External", "1"]], include_blank: true, selected: params[:search][:link_type] %>
<%= f.input :linked_wiki_exists, label: "Wiki Exists?", collection: ["Yes", "No"], include_blank: true, selected: params[:search][:linked_wiki_exists] %> <%= f.input :has_linked_wiki, label: "Wiki Exists?", collection: ["Yes", "No"], include_blank: true, selected: params[:search][:has_linked_wiki] %>
<%= f.input :linked_tag_exists, label: "Tag Exists?", collection: ["Yes", "No"], include_blank: true, selected: params[:search][:linked_tag_exists] %> <%= f.input :has_linked_tag, label: "Tag Exists?", collection: ["Yes", "No"], include_blank: true, selected: params[:search][:has_linked_tag] %>
<%= f.submit "Search" %> <%= f.submit "Search" %>
<% end %> <% end %>

View File

@@ -3,11 +3,15 @@
<h1>Search Forum</h1> <h1>Search Forum</h1>
<%= search_form_for(forum_posts_path) do |f| %> <%= search_form_for(forum_posts_path) do |f| %>
<%= f.input :topic_title_matches, label: "Title" %> <%= f.simple_fields_for :topic do |pf| %>
<%= pf.input :title_matches, label: "Title" %>
<% end %>
<%= f.input :body_matches, label: "Body" %> <%= f.input :body_matches, label: "Body" %>
<%= f.input :creator_name, label: "Creator", input_html: { "data-autocomplete": "user" } %> <%= f.input :creator_name, label: "Creator", input_html: { "data-autocomplete": "user" } %>
<%= f.input :linked_to, label: "Tag", hint: "Find posts mentioning a tag", input_html: { "data-autocomplete": "tag" } %> <%= f.input :linked_to, label: "Tag", hint: "Find posts mentioning a tag", input_html: { "data-autocomplete": "tag" } %>
<%= f.input :topic_category_id, label: "Category", collection: ForumTopic::CATEGORIES.invert.to_a, include_blank: true %> <%= f.simple_fields_for :topic do |pf| %>
<%= pf.input :category_id, label: "Category", collection: ForumTopic::CATEGORIES.invert.to_a, include_blank: true %>
<% end %>
<%= f.submit "Search" %> <%= f.submit "Search" %>
<% end %> <% end %>
</div> </div>

View File

@@ -3,7 +3,7 @@
<%= f.input :category, label: "Category", collection: TagCategory.canonical_mapping.to_a, include_blank: true,selected: params[:search][:category] %> <%= f.input :category, label: "Category", collection: TagCategory.canonical_mapping.to_a, include_blank: true,selected: params[:search][:category] %>
<%= f.input :order, collection: [%w[Newest date], %w[Count count], %w[Name name]], include_blank: false, selected: params[:search][:order] %> <%= f.input :order, collection: [%w[Newest date], %w[Count count], %w[Name name]], include_blank: false, selected: params[:search][:order] %>
<%= f.input :hide_empty, label: "Hide empty?", collection: %w[yes no], selected: params[:search][:hide_empty] %> <%= f.input :hide_empty, label: "Hide empty?", collection: %w[yes no], selected: params[:search][:hide_empty] %>
<%= f.input :has_wiki, label: "Has wiki?", collection: %w[yes no], include_blank: true, selected: params[:search][:has_wiki] %> <%= f.input :has_wiki_page, label: "Has wiki?", collection: %w[yes no], include_blank: true, selected: params[:search][:has_wiki_page] %>
<%= f.input :has_artist, label: "Has artist?", collection: %w[yes no], include_blank: true, selected: params[:search][:has_artist] %> <%= f.input :has_artist, label: "Has artist?", collection: %w[yes no], include_blank: true, selected: params[:search][:has_artist] %>
<%= f.submit "Search" %> <%= f.submit "Search" %>
<% end %> <% end %>

View File

@@ -1,6 +1,7 @@
FactoryBot.define do FactoryBot.define do
factory(:forum_post) do factory(:forum_post) do
creator creator
topic factory: :forum_topic
body {FFaker::Lorem.sentences.join(" ")} body {FFaker::Lorem.sentences.join(" ")}
end end
end end

View File

@@ -2,5 +2,6 @@ FactoryBot.define do
factory(:mod_action) do factory(:mod_action) do
creator :factory => :user creator :factory => :user
description {"1234"} description {"1234"}
category {"other"}
end end
end end

View File

@@ -1,7 +1,7 @@
FactoryBot.define do FactoryBot.define do
factory(:post_disapproval) do factory(:post_disapproval) do
user user factory: :moderator_user
post post factory: :post, is_pending: true
reason { PostDisapproval::REASONS.sample } reason { PostDisapproval::REASONS.sample }
message { FFaker::Lorem.sentence } message { FFaker::Lorem.sentence }
end end

View File

@@ -1,5 +1,6 @@
FactoryBot.define do FactoryBot.define do
factory(:post_replacement) do factory(:post_replacement) do
post factory: :post, source: FFaker::Internet.http_url
original_url { FFaker::Internet.http_url } original_url { FFaker::Internet.http_url }
replacement_url { FFaker::Internet.http_url } replacement_url { FFaker::Internet.http_url }
end end

View File

@@ -1,6 +1,6 @@
FactoryBot.define do FactoryBot.define do
factory(:post_vote) do factory(:post_vote) do
user user factory: :gold_user
post post
score { [-1, 1].sample } score { [-1, 1].sample }
end end

View File

@@ -1,8 +1,8 @@
FactoryBot.define do FactoryBot.define do
factory :tag_alias do factory :tag_alias do
creator creator
antecedent_name {"aaa"} antecedent_name {"#{FFaker::Name.first_name.downcase}#{rand(1000)}"}
consequent_name {"bbb"} consequent_name {"#{FFaker::Name.first_name.downcase}#{rand(1000)}"}
status {"active"} status {"active"}
skip_secondary_validations {true} skip_secondary_validations {true}
end end

View File

@@ -1,8 +1,8 @@
FactoryBot.define do FactoryBot.define do
factory :tag_implication do factory :tag_implication do
creator creator
antecedent_name {"aaa"} antecedent_name {"#{FFaker::Name.first_name.downcase}#{rand(1000)}"}
consequent_name {"bbb"} consequent_name {"#{FFaker::Name.first_name.downcase}#{rand(1000)}"}
status {"active"} status {"active"}
skip_secondary_validations {true} skip_secondary_validations {true}
end end

View File

@@ -3,43 +3,43 @@ require 'test_helper'
class ArtistCommentariesControllerTest < ActionDispatch::IntegrationTest class ArtistCommentariesControllerTest < ActionDispatch::IntegrationTest
context "The artist commentaries controller" do context "The artist commentaries controller" do
setup do setup do
@user = create(:user) @user = create(:user, id: 1000, name: "danbo", created_at: 2.weeks.ago)
as(@user) do as(@user) do
@commentary1 = create(:artist_commentary) @commentary = create(:artist_commentary, post: build(:post, id: 999, tag_string: "hakurei_reimu", uploader: @user), original_title: "ot1", translated_title: "tt1")
@commentary2 = create(:artist_commentary) @other_commentary = create(:artist_commentary, translated_title: "", translated_description: "")
end end
end end
context "index action" do context "index action" do
setup do
@deleted_commentary = create(:artist_commentary, original_title: "", original_description: "", translated_title: "", translated_description: "")
end
should "render" do should "render" do
get artist_commentaries_path get artist_commentaries_path
assert_response :success assert_response :success
end end
should "render with search params" do should respond_to_search({}).with { [@deleted_commentary, @other_commentary, @commentary] }
params = { should respond_to_search(text_matches: "ot1").with { @commentary }
search: { should respond_to_search(original_present: "true").with { [@other_commentary, @commentary] }
text_matches: @commentary1.original_title, should respond_to_search(translated_present: "true").with { @commentary }
post_id: @commentary1.post_id, should respond_to_search(is_deleted: "yes").with { @deleted_commentary }
original_present: "yes",
translated_present: "yes",
post_tags_match: @commentary1.post.tag_array.first
}
}
get artist_commentaries_path(params) context "using includes" do
assert_response :success should respond_to_search(post_id: 999).with { @commentary }
should respond_to_search(post_tags_match: "hakurei_reimu").with { @commentary }
should respond_to_search(post: {uploader_name: "danbo"}).with { @commentary }
end end
end end
context "show action" do context "show action" do
should "render" do should "render" do
get artist_commentary_path(@commentary1.id) get artist_commentary_path(@commentary.id)
assert_redirected_to(@commentary1.post) assert_redirected_to(@commentary.post)
get artist_commentary_path(post_id: @commentary1.post_id) get artist_commentary_path(post_id: @commentary.post_id)
assert_redirected_to(@commentary1.post) assert_redirected_to(@commentary.post)
end end
end end
@@ -62,27 +62,27 @@ class ArtistCommentariesControllerTest < ActionDispatch::IntegrationTest
should "render for update" do should "render for update" do
params = { params = {
artist_commentary: { artist_commentary: {
post_id: @commentary1.post_id, post_id: @commentary.post_id,
original_title: "foo" original_title: "foo"
}, },
format: "js" format: "js"
} }
put_auth create_or_update_artist_commentaries_path(params), @user put_auth create_or_update_artist_commentaries_path(params), @user
@commentary1.reload @commentary.reload
assert_response :success assert_response :success
assert_equal("foo", @commentary1.reload.original_title) assert_equal("foo", @commentary.reload.original_title)
end end
end end
context "revert action" do context "revert action" do
should "work" do should "work" do
original_title = @commentary1.original_title original_title = @commentary.original_title
@commentary1.update(original_title: "foo") @commentary.update(original_title: "foo")
@commentary1.reload @commentary.reload
put_auth revert_artist_commentary_path(@commentary1.post_id, version_id: @commentary1.versions.first.id, format: "js"), @user put_auth revert_artist_commentary_path(@commentary.post_id, version_id: @commentary.versions.first.id, format: "js"), @user
assert_response :success assert_response :success
assert_equal(original_title, @commentary1.reload.original_title) assert_equal(original_title, @commentary.reload.original_title)
end end
should "return 404 when trying to revert a nonexistent commentary" do should "return 404 when trying to revert a nonexistent commentary" do
@@ -91,9 +91,9 @@ class ArtistCommentariesControllerTest < ActionDispatch::IntegrationTest
end end
should "not allow reverting to a previous version of another artist commentary" do should "not allow reverting to a previous version of another artist commentary" do
put_auth revert_artist_commentary_path(@commentary1.post_id, version_id: @commentary2.versions.first.id, format: "js"), @user put_auth revert_artist_commentary_path(@commentary.post_id, version_id: @other_commentary.versions.first.id, format: "js"), @user
@commentary1.reload @commentary.reload
assert_not_equal(@commentary1.original_title, @commentary2.original_title) assert_not_equal(@commentary.original_title, @other_commentary.original_title)
assert_response :missing assert_response :missing
end end
end end

View File

@@ -3,24 +3,48 @@ require 'test_helper'
class ArtistCommentaryVersionsControllerTest < ActionDispatch::IntegrationTest class ArtistCommentaryVersionsControllerTest < ActionDispatch::IntegrationTest
context "The artist commentary versions controller" do context "The artist commentary versions controller" do
setup do setup do
@user = create(:user) @user = create(:member_user, id: 1000, created_at: 2.weeks.ago)
@commentary1 = as(@user) { create(:artist_commentary) } @builder = create(:builder_user, created_at: 2.weeks.ago)
@commentary2 = as(@user) { create(:artist_commentary) } as(@user) do
@commentary = create(:artist_commentary, post: build(:post, id: 999, tag_string: "hakurei_reimu", uploader: @user))
end
as (@builder) { @commentary.update(original_title: "traslated") }
as (@user) { @commentary.update(original_title: "translated") }
end end
context "index action" do context "index action" do
setup do
@versions = @commentary.versions
as(@builder) do
@other_commentary = create(:artist_commentary, post: build(:post, uploader: @builder))
end
@other_versions = @other_commentary.versions
end
should "render" do should "render" do
get artist_commentary_versions_path get artist_commentary_versions_path
assert_response :success assert_response :success
end end
should respond_to_search({}).with { @other_versions + @versions.reverse }
should respond_to_search(original_title: "translated").with { @versions[2] }
should respond_to_search(text_matches: "traslated").with { @versions[1] }
context "using includes" do
should respond_to_search(post_id: 999).with { @versions.reverse }
should respond_to_search(post_tags_match: "hakurei_reimu").with { @versions.reverse }
should respond_to_search(post: {uploader: {level: User::Levels::BUILDER}}).with { @other_commentary.versions }
should respond_to_search(updater_id: 1000).with { [@versions[2], @versions[0]] }
should respond_to_search(updater: {level: User::Levels::BUILDER}).with { [@other_versions[0], @versions[1]] }
end
end end
context "show action" do context "show action" do
should "work" do should "work" do
get artist_commentary_version_path(@commentary1.versions.first) get artist_commentary_version_path(@commentary.versions.first)
assert_redirected_to artist_commentary_versions_path(search: { post_id: @commentary1.post_id }) assert_redirected_to artist_commentary_versions_path(search: { post_id: @commentary.post_id })
get artist_commentary_version_path(@commentary1.versions.first), as: :json get artist_commentary_version_path(@commentary.versions.first), as: :json
assert_response :success assert_response :success
end end
end end

View File

@@ -2,28 +2,24 @@ require 'test_helper'
class ArtistUrlsControllerTest < ActionDispatch::IntegrationTest class ArtistUrlsControllerTest < ActionDispatch::IntegrationTest
context "The artist urls controller" do context "The artist urls controller" do
setup do
@artist = create(:artist, name: "bkub", url_string: "-http://bkub.com")
@banned = create(:artist, name: "danbo", is_banned: true, url_string: "https://danbooru.donmai.us")
end
context "index page" do context "index page" do
should "render" do should "render" do
get artist_urls_path get artist_urls_path
assert_response :success assert_response :success
end end
should "render for a json request" do should respond_to_search({}).with { @banned.urls + @artist.urls }
get artist_urls_path, as: :json should respond_to_search(url_matches: "*bkub*").with { @artist.urls }
assert_response :success should respond_to_search(is_active: "false").with { @artist.urls }
end
should "render for a complex search" do context "using includes" do
@artist = FactoryBot.create(:artist, name: "bkub", url_string: "-http://bkub.com") should respond_to_search(artist: {name: "bkub"}).with { @artist.urls }
should respond_to_search(artist: {is_banned: "true"}).with { @banned.urls }
get artist_urls_path(search: {
artist: { name: "bkub" },
url_matches: "*bkub*",
is_active: "false",
order: "created_at"
})
assert_response :success
end end
end end
end end

View File

@@ -3,18 +3,37 @@ require 'test_helper'
class ArtistVersionsControllerTest < ActionDispatch::IntegrationTest class ArtistVersionsControllerTest < ActionDispatch::IntegrationTest
context "An artist versions controller" do context "An artist versions controller" do
setup do setup do
@user = create(:gold_user) @user = create(:gold_user, id: 100)
@artist = as(@user) { create(:artist) } @builder = create(:builder_user, name: "danbo")
as(@builder) { @artist = create(:artist, name: "masao", url_string: "https://masao.deviantart.com") }
as(@user) { @artist.update(name: "masao_(deleted)", is_deleted: true) }
as(@builder) { @artist.update(name: "masao", is_deleted: false, group_name: "the_best", url_string: "https://www.deviantart.com/masao") }
end end
should "get the index page" do context "index action" do
get_auth artist_versions_path, @user setup do
assert_response :success @versions = @artist.versions
end end
should "get the index page when searching for something" do should "render" do
get_auth artist_versions_path(search: {name: @artist.name}), @user get artist_versions_path
assert_response :success assert_response :success
end
should respond_to_search({}).with { @versions.reverse }
should respond_to_search(name: "masao").with { [@versions[2], @versions[0]] }
should respond_to_search(name_matches: "(deleted)").with { @versions[1] }
should respond_to_search(group_name_matches: "the_best").with { @versions[2] }
should respond_to_search(urls_include_any: "https://www.deviantart.com/masao").with { @versions[2] }
should respond_to_search(is_deleted: "true").with { @versions[1] }
context "using includes" do
should respond_to_search(updater_id: 100).with { @versions[1] }
should respond_to_search(updater_name: "danbo").with { [@versions[2], @versions[0]] }
should respond_to_search(updater: {level: User::Levels::BUILDER}).with { [@versions[2], @versions[0]] }
should respond_to_search(artist: {name: "masao"}).with { @versions.reverse }
should respond_to_search(artist: {name: "doesntexist"}).with { [] }
end
end end
context "show action" do context "show action" do

View File

@@ -28,6 +28,8 @@ class ArtistsControllerTest < ActionDispatch::IntegrationTest
@artist = create(:artist) @artist = create(:artist)
@masao = create(:artist, name: "masao", url_string: "http://www.pixiv.net/member.php?id=32777") @masao = create(:artist, name: "masao", url_string: "http://www.pixiv.net/member.php?id=32777")
@artgerm = create(:artist, name: "artgerm", url_string: "http://artgerm.deviantart.com/") @artgerm = create(:artist, name: "artgerm", url_string: "http://artgerm.deviantart.com/")
@wiki = create(:wiki_page, title: "artgerm")
@post = create(:post, tag_string: "masao")
end end
end end
@@ -130,7 +132,7 @@ class ArtistsControllerTest < ActionDispatch::IntegrationTest
end end
context "index action" do context "index action" do
should "get the index page" do should "render" do
get artists_path get artists_path
assert_response :success assert_response :success
end end
@@ -142,26 +144,36 @@ class ArtistsControllerTest < ActionDispatch::IntegrationTest
end end
context "when searching the index page" do context "when searching the index page" do
setup do
@deleted = create(:artist, is_deleted: true)
@banned = create(:artist, is_banned: true)
end
should "find artists by name" do should "find artists by name" do
get artists_path(name: "masao", format: "json") get artists_path(name: "masao", format: "json")
assert_artist_found("masao") assert_artist_found("masao")
end end
should "find artists by image URL" do should respond_to_search({}).with { [@banned, @deleted, @artgerm, @masao, @artist] }
get artists_path(search: { url_matches: "http://i2.pixiv.net/img04/img/syounen_no_uta/46170939_m.jpg" }, format: "json") should respond_to_search(name: "masao").with { @masao }
assert_artist_found("masao") should respond_to_search(is_banned: "true").with { @banned }
should respond_to_search(is_deleted: "true").with { @deleted }
should respond_to_search(url_matches: "http://i2.pixiv.net/img04/img/syounen_no_uta/46170939_m.jpg").with { @masao }
should respond_to_search(url_matches: "http://www.pixiv.net/member_illust.php?mode=medium&illust_id=46170939").with { @masao }
context "ignoring whitespace" do
should respond_to_search(url_matches: " http://www.pixiv.net/member_illust.php?mode=medium&illust_id=46170939 ").with { @masao }
end end
should "find artists by page URL" do context "using includes" do
url = "http://www.pixiv.net/member_illust.php?mode=medium&illust_id=46170939" should respond_to_search(has_wiki_page: "true").with { @artgerm }
get artists_path(search: { url_matches: url }, format: "json") should respond_to_search(has_wiki_page: "false").with { [@banned, @deleted, @masao, @artist] }
assert_artist_found("masao") should respond_to_search(has_tag: "true").with { @masao }
end should respond_to_search(has_tag: "false").with { [@banned, @deleted, @artgerm, @artist] }
should respond_to_search(has_urls: "true").with { [@artgerm, @masao] }
should "ignore whitespace when searching by URL" do should respond_to_search(has_urls: "false").with { [@banned, @deleted, @artist] }
url = " http://www.pixiv.net/member_illust.php?mode=medium&illust_id=46170939 " should respond_to_search(urls: {url: "http://www.pixiv.net/member.php?id=32777"}).with { @masao }
get artists_path(search: { url_matches: url }, format: "json") should respond_to_search(urls: {normalized_url: "http://www.deviantart.com/artgerm/"}).with { @artgerm }
assert_artist_found("masao")
end end
end end
end end

View File

@@ -3,8 +3,11 @@ require 'test_helper'
class BansControllerTest < ActionDispatch::IntegrationTest class BansControllerTest < ActionDispatch::IntegrationTest
context "A bans controller" do context "A bans controller" do
setup do setup do
@mod = create(:moderator_user) @mod = create(:moderator_user, name: "danbo")
@ban = create(:ban) @admin = create(:admin_user)
@user = create(:member_user, id: 999, name: "cirno")
as(@mod) { @ban = create(:ban, reason: "blah", user: @user, banner: @mod) }
end end
context "new action" do context "new action" do
@@ -29,16 +32,25 @@ class BansControllerTest < ActionDispatch::IntegrationTest
end end
context "index action" do context "index action" do
setup do
as(@admin) { @admin_ban = create(:ban, user: build(:builder_user), banner: @admin, expires_at: 1.day.ago ) }
end
should "render" do should "render" do
get_auth bans_path, @mod get bans_path
assert_response :success assert_response :success
end end
end
context "search action" do should respond_to_search({}).with { [@admin_ban, @ban] }
should "render" do should respond_to_search(reason_matches: "blah").with { @ban }
get_auth bans_path(search: { user_name: @ban.user.name }), @mod should respond_to_search(expired: "true").with { @admin_ban }
assert_response :success
context "using includes" do
should respond_to_search(banner_name: "danbo").with { @ban }
should respond_to_search(banner: {level: User::Levels::ADMIN}).with { @admin_ban }
should respond_to_search(user_id: 999).with { @ban }
should respond_to_search(user: {name: "cirno"}).with { @ban }
should respond_to_search(user: {level: User::Levels::BUILDER}).with { @admin_ban }
end end
end end
@@ -55,7 +67,6 @@ class BansControllerTest < ActionDispatch::IntegrationTest
should "not allow mods to ban admins" do should "not allow mods to ban admins" do
assert_difference("Ban.count", 0) do assert_difference("Ban.count", 0) do
@admin = create(:admin_user)
post_auth bans_path, @mod, params: { ban: { duration: 60, reason: "xxx", user_id: @admin.id }} post_auth bans_path, @mod, params: { ban: { duration: 60, reason: "xxx", user_id: @admin.id }}
assert_response 403 assert_response 403

View File

@@ -3,10 +3,11 @@ require 'test_helper'
class BulkUpdateRequestsControllerTest < ActionDispatch::IntegrationTest class BulkUpdateRequestsControllerTest < ActionDispatch::IntegrationTest
context "BulkUpdateRequestsController" do context "BulkUpdateRequestsController" do
setup do setup do
@user = create(:user) @user = create(:user, id: 999)
@builder = create(:builder_user) @builder = create(:builder_user)
@admin = create(:admin_user) @admin = create(:admin_user)
@bulk_update_request = create(:bulk_update_request, user: @user) as(@admin) { @forum_topic = create(:forum_topic, id: 100, category_id: 0) }
as(@user) { @bulk_update_request = create(:bulk_update_request, user: @user, forum_topic: @forum_topic) }
end end
context "#new" do context "#new" do
@@ -73,10 +74,31 @@ class BulkUpdateRequestsControllerTest < ActionDispatch::IntegrationTest
end end
context "#index" do context "#index" do
setup do
@other_BUR = create(:bulk_update_request, user: @builder, script: "create alias cirno -> 9")
@rejected_BUR = create(:bulk_update_request, status: "rejected")
@approved_BUR = create(:bulk_update_request, status: "approved", approver: @admin)
end
should "render" do should "render" do
get bulk_update_requests_path get bulk_update_requests_path
assert_response :success assert_response :success
end end
should respond_to_search({}).with { [@other_BUR, @bulk_update_request, @approved_BUR, @rejected_BUR] }
should respond_to_search(order: "id_desc").with { [@approved_BUR, @rejected_BUR, @other_BUR, @bulk_update_request] }
should respond_to_search(status: "pending").with { [@other_BUR, @bulk_update_request] }
should respond_to_search(script_matches: "cirno -> 9").with { @other_BUR }
should respond_to_search(tags_include_any: "cirno").with { @other_BUR }
context "using includes" do
should respond_to_search(forum_topic_id: 100).with { @bulk_update_request }
should respond_to_search(forum_topic: {category_id: 0}).with { @bulk_update_request }
should respond_to_search(user_id: 999).with { @bulk_update_request }
should respond_to_search(user: {level: User::Levels::BUILDER}).with { @other_BUR }
should respond_to_search(has_approver: "true").with { @approved_BUR }
should respond_to_search(has_approver: "false").with { [@other_BUR, @bulk_update_request, @rejected_BUR] }
end
end end
context "#show" do context "#show" do

View File

@@ -3,9 +3,9 @@ require 'test_helper'
class CommentVotesControllerTest < ActionDispatch::IntegrationTest class CommentVotesControllerTest < ActionDispatch::IntegrationTest
context "A comment votes controller" do context "A comment votes controller" do
setup do setup do
CurrentUser.user = @user = create(:user) CurrentUser.user = @user = create(:user, name: "cirno")
CurrentUser.ip_addr = "127.0.0.1" CurrentUser.ip_addr = "127.0.0.1"
@comment = create(:comment) @comment = create(:comment, creator: @user)
end end
teardown do teardown do
@@ -13,12 +13,36 @@ class CommentVotesControllerTest < ActionDispatch::IntegrationTest
CurrentUser.ip_addr = nil CurrentUser.ip_addr = nil
end end
context "#index" do context "index action" do
should "work" do setup do
create(:comment_vote, user: @user) @voter = create(:gold_user, name: "rumia")
get_auth comment_votes_path, @user @vote = as (@voter) { create(:comment_vote, comment: @comment, user: @voter) }
@negative_vote = create(:comment_vote, comment: @comment, score: -1)
@unrelated_vote = create(:comment_vote)
end
assert_response :success context "as a user" do
should "render" do
get_auth comment_votes_path, @user
assert_response :success
end
should respond_to_search({}).with { [] }
end
context "as a moderator" do
setup do
CurrentUser.user = create(:mod_user)
end
should respond_to_search({}).with { [@unrelated_vote, @negative_vote, @vote] }
should respond_to_search(score: -1).with { @negative_vote }
context "using includes" do
should respond_to_search(comment: {creator_name: "cirno"}).with { [@negative_vote, @vote] }
should respond_to_search(user_name: "rumia").with { @vote }
should respond_to_search(user: {level: User::Levels::GOLD}).with { @vote }
end
end end
end end

View File

@@ -4,8 +4,8 @@ class CommentsControllerTest < ActionDispatch::IntegrationTest
context "A comments controller" do context "A comments controller" do
setup do setup do
@mod = FactoryBot.create(:moderator_user) @mod = FactoryBot.create(:moderator_user)
@user = FactoryBot.create(:member_user) @user = FactoryBot.create(:member_user, name: "cirno")
@post = create(:post) @post = create(:post, id: 100)
CurrentUser.user = @user CurrentUser.user = @user
CurrentUser.ip_addr = "127.0.0.1" CurrentUser.ip_addr = "127.0.0.1"
@@ -87,10 +87,31 @@ class CommentsControllerTest < ActionDispatch::IntegrationTest
end end
end end
should "render by comment" do context "grouped by comment" do
@comment = as(@user) { create(:comment, post: @post) } setup do
get comments_path(group_by: "comment") @user_comment = create(:comment, post: @post, score: 10, do_not_bump_post: true, creator: @user)
assert_response :success @mod_comment = create(:comment, post: build(:post, tag_string: "touhou"), body: "blah", is_sticky: true, creator: @mod)
@deleted_comment = create(:comment, is_deleted: true)
end
should "render" do
get comments_path(group_by: "comment")
assert_response :success
end
should respond_to_search({}, other_params: {group_by: "comment"}).with { [@deleted_comment, @mod_comment, @user_comment] }
should respond_to_search(body_matches: "blah").with { @mod_comment }
should respond_to_search(score: 10).with { @user_comment }
should respond_to_search(is_sticky: "true").with { @mod_comment }
should respond_to_search(do_not_bump_post: "true").with { @user_comment }
should respond_to_search(is_deleted: "true").with { @deleted_comment }
context "using includes" do
should respond_to_search(post_id: 100).with { @user_comment }
should respond_to_search(post_tags_match: "touhou").with { @mod_comment }
should respond_to_search(creator_name: "cirno").with { @user_comment }
should respond_to_search(creator: {level: User::Levels::MODERATOR}).with { @mod_comment }
end
end end
context "for atom feeds" do context "for atom feeds" do

View File

@@ -3,9 +3,9 @@ require 'test_helper'
class DmailsControllerTest < ActionDispatch::IntegrationTest class DmailsControllerTest < ActionDispatch::IntegrationTest
context "The dmails controller" do context "The dmails controller" do
setup do setup do
@user = create(:user, unread_dmail_count: 1) @user = create(:user, id: 999, unread_dmail_count: 1)
@unrelated_user = create(:user) @unrelated_user = create(:moderator_user, id: 1000, name: "reimu")
@dmail = create(:dmail, owner: @user) @dmail = create(:dmail, owner: @user, from: @user)
end end
teardown do teardown do
@@ -40,28 +40,41 @@ class DmailsControllerTest < ActionDispatch::IntegrationTest
end end
context "index action" do context "index action" do
should "show dmails owned by the current user by sent" do setup do
get_auth dmails_path, @user, params: {:search => {:owner_id => @dmail.owner_id, :folder => "sent"}} CurrentUser.user = @user
@received_dmail = create(:dmail, owner: @user, body: "blah", to: @user, from: @unrelated_user, is_read: true)
@deleted_dmail = create(:dmail, owner: @user, title: "UMAD", to: @unrelated_user, from: @user, is_deleted: true)
@unrelated_dmail = create(:dmail, owner: @unrelated_user, from: @unrelated_user)
end
should "render" do
get_auth dmails_path, @user
assert_response :success assert_response :success
end end
should "show dmails owned by the current user by received" do should respond_to_search({}).with { [@deleted_dmail, @received_dmail, @dmail] }
get_auth dmails_path, @user, params: {:search => {:owner_id => @dmail.owner_id, :folder => "received"}} should respond_to_search(folder: "sent").with { @dmail }
assert_response :success should respond_to_search(folder: "received").with { @received_dmail }
should respond_to_search(title_matches: "UMAD").with { @deleted_dmail }
should respond_to_search(message_matches: "blah").with { @received_dmail }
should respond_to_search(is_read: "true").with { @received_dmail }
should respond_to_search(is_deleted: "true").with { @deleted_dmail }
context "using includes" do
should respond_to_search(to_id: 1000).with { @deleted_dmail }
should respond_to_search(from_id: 999).with { [@deleted_dmail, @dmail] }
should respond_to_search(from_name: "reimu").with { @received_dmail }
should respond_to_search(from: {level: User::Levels::MODERATOR}).with { @received_dmail }
end end
should "not show dmails not owned by the current user" do context "as a banned user" do
get_auth dmails_path, @user, params: {:search => {:owner_id => @dmail.owner_id}} setup do
assert_response :success as(create(:admin_user)) do
end create(:ban, user: @user)
end
should "work for banned users" do should respond_to_search({}).with { [@received_dmail, @dmail] }
as(create(:admin_user)) do
create(:ban, :user => @user)
end end
get_auth dmails_path, @dmail.owner, params: {:search => {:owner_id => @dmail.owner_id, :folder => "sent"}}
assert_response :success
end end
end end

View File

@@ -1,12 +1,30 @@
require "test_helper" require "test_helper"
class DtextLinksControllerTest < ActionDispatch::IntegrationTest class DtextLinksControllerTest < ActionDispatch::IntegrationTest
setup do
@user = create(:user)
as(@user) do
@wiki = create(:wiki_page, title: "case", body: "[[test]]")
@forum = create(:forum_post, topic: build(:forum_topic, title: "blah"), body: "[[case]]")
create(:tag, name: "test")
end
end
context "index action" do context "index action" do
should "work" do should "render" do
@user = create(:user)
@wiki = as(@user) { create(:wiki_page, body: "[[test]]") }
get dtext_links_path get dtext_links_path
assert_response :success assert_response :success
end end
should respond_to_search({}).with { @forum.dtext_links + @wiki.dtext_links }
context "using includes" do
should respond_to_search(model_type: "WikiPage").with { @wiki.dtext_links }
should respond_to_search(model_type: "ForumPost").with { @forum.dtext_links }
should respond_to_search(has_linked_tag: "true").with { @wiki.dtext_links }
should respond_to_search(has_linked_wiki: "true").with { @forum.dtext_links }
should respond_to_search(ForumPost: {topic: {title_matches: "blah"}}).with { @forum.dtext_links }
should respond_to_search(ForumPost: {topic: {title_matches: "nah"}}).with { [] }
end
end end
end end

View File

@@ -8,10 +8,31 @@ class FavoriteGroupsControllerTest < ActionDispatch::IntegrationTest
end end
context "index action" do context "index action" do
setup do
@mod_favgroup = create(:favorite_group, name: "monochrome", creator: build(:moderator_user, name: "fumimi"))
@private_favgroup = create(:favorite_group, creator: @user, is_public: false)
end
should "render" do should "render" do
get favorite_groups_path get favorite_groups_path
assert_response :success assert_response :success
end end
should respond_to_search({}).with { [@mod_favgroup, @favgroup] }
should respond_to_search(name: "monochrome").with { @mod_favgroup }
context "using includes" do
should respond_to_search(creator_name: "fumimi").with { @mod_favgroup }
should respond_to_search(creator: {level: User::Levels::MEMBER}).with { @favgroup }
end
context "for private favorite groups as the creator" do
setup do
CurrentUser.user = @user
end
should respond_to_search(is_public: "false").with { @private_favgroup }
end
end end
context "show action" do context "show action" do

View File

@@ -3,22 +3,35 @@ require 'test_helper'
class ForumPostVotesControllerTest < ActionDispatch::IntegrationTest class ForumPostVotesControllerTest < ActionDispatch::IntegrationTest
context "The forum post votes controller" do context "The forum post votes controller" do
setup do setup do
@user = create(:user) @user = create(:user, name: "cirno")
@other_user = create(:user) @other_user = create(:user)
as(@user) do as(@user) do
@forum_topic = create(:forum_topic) @forum_post = create(:forum_post, body: "blah", creator: @user)
@forum_post = create(:forum_post, topic: @forum_topic)
@bulk_update_request = create(:bulk_update_request, forum_post: @forum_post) @bulk_update_request = create(:bulk_update_request, forum_post: @forum_post)
end end
end end
context "index action" do context "index action" do
setup do
@vote = create(:forum_post_vote, forum_post: @forum_post, creator: build(:user, name: "rumia"), score: 1)
@negative_vote = create(:forum_post_vote, forum_post: @forum_post, score: -1)
@unrelated_vote = as (@user) { create(:forum_post_vote, score: 0) }
end
should "render" do should "render" do
@forum_post_vote = create(:forum_post_vote, creator: @user, forum_post: @forum_post)
get forum_post_votes_path get forum_post_votes_path
assert_response :success assert_response :success
end end
should respond_to_search({}).with { [@unrelated_vote, @negative_vote, @vote] }
should respond_to_search(score: -1).with { @negative_vote }
context "using includes" do
should respond_to_search(creator_name: "rumia").with { @vote }
should respond_to_search(forum_post: {creator_name: "cirno"}).with { [@negative_vote, @vote] }
should respond_to_search(forum_post: {body_matches: "blah"}).with { [@negative_vote, @vote] }
end
end end
context "create action" do context "create action" do

View File

@@ -3,9 +3,9 @@ require 'test_helper'
class ForumPostsControllerTest < ActionDispatch::IntegrationTest class ForumPostsControllerTest < ActionDispatch::IntegrationTest
context "The forum posts controller" do context "The forum posts controller" do
setup do setup do
@user = create(:user) @user = create(:user, id: 999)
@other_user = create(:user) @other_user = create(:user)
@mod = create(:moderator_user) @mod = create(:moderator_user, name: "okuu")
@forum_topic = as(@user) { create(:forum_topic, title: "my forum topic", creator: @user) } @forum_topic = as(@user) { create(:forum_topic, title: "my forum topic", creator: @user) }
@forum_post = as(@user) { create(:forum_post, creator: @user, topic: @forum_topic, body: "alias xxx -> yyy") } @forum_post = as(@user) { create(:forum_post, creator: @user, topic: @forum_topic, body: "alias xxx -> yyy") }
end end
@@ -52,65 +52,61 @@ class ForumPostsControllerTest < ActionDispatch::IntegrationTest
end end
context "index action" do context "index action" do
should "list all forum posts" do setup do
get forum_posts_path @admin = create(:admin_user)
assert_response :success @other_forum = as(@user) { create(:forum_post, body: "[[test]]", topic: build(:forum_topic, title: "my topic", category_id: 1)) }
@mod_forum = as(@mod) { create(:forum_post, creator: @mod, topic: build(:forum_topic, min_level: User::Levels::MODERATOR)) }
@admin_forum = as(@admin) { create(:forum_post, creator: @admin, topic: build(:forum_topic, min_level: User::Levels::ADMIN)) }
@unrelated_forum = as (@user) { create(:forum_post, is_deleted: true) }
as (@user) { create(:forum_post_vote, forum_post: @forum_post) }
create(:bulk_update_request, forum_post: @other_forum)
end end
context "with search conditions" do context "as a user" do
should "list all matching forum posts" do
get forum_posts_path, params: { search: { body_matches: "xxx", topic_title_matches: "my forum topic" }}
assert_response :success
assert_select "#forum-post-#{@forum_post.id}"
end
should "list nothing for when the search matches nothing" do
get forum_posts_path, params: {:search => {:body_matches => "bababa"}}
assert_response :success
assert_select "#forum-post-#{@forum_post.id}", false
end
should "list by creator id" do
get forum_posts_path, params: {:search => {:creator_id => @user.id}}
assert_response :success
assert_select "#forum-post-#{@forum_post.id}"
end
end
context "for posts in private topics" do
setup do setup do
@admin = create(:admin_user) CurrentUser.user = @user
@mod_post = as(@mod) { create(:forum_post, creator: @mod, topic: build(:forum_topic, min_level: User::Levels::MODERATOR)) }
@admin_post = as(@admin) { create(:forum_post, creator: @admin, topic: build(:forum_topic, min_level: User::Levels::ADMIN)) }
end end
should "list only permitted posts for anons" do should "render" do
get forum_posts_path get_auth comment_votes_path, @user
assert_response :success assert_response :success
assert_select "#forum-post-#{@forum_post.id}"
assert_select "#forum-post-#{@mod_post.id}", false
assert_select "#forum-post-#{@admin_post.id}", false
end end
should "list only permitted posts for mods" do should respond_to_search({}).with { [@unrelated_forum, @other_forum, @forum_post] }
get_auth forum_posts_path, @mod should respond_to_search(body_matches: "xxx").with { @forum_post }
should respond_to_search(body_matches: "bababa").with { [] }
should respond_to_search(is_deleted: "true").with { @unrelated_forum }
assert_response :success context "using includes" do
assert_select "#forum-post-#{@forum_post.id}" should respond_to_search(topic: {title_matches: "my forum topic"}).with { @forum_post }
assert_select "#forum-post-#{@mod_post.id}" should respond_to_search(topic: {category_id: 1}).with { @other_forum }
assert_select "#forum-post-#{@admin_post.id}", false should respond_to_search(has_bulk_update_request: "true").with { @other_forum }
should respond_to_search(has_votes: "true").with { @forum_post }
should respond_to_search(has_dtext_links: "true").with { @other_forum }
should respond_to_search(creator_id: 999).with { @forum_post }
should respond_to_search(creator: {name: "okuu"}).with { [] }
end
end
context "as a moderator" do
setup do
CurrentUser.user = @mod
end end
should "list only permitted posts for admins" do should respond_to_search({}).with { [@unrelated_forum, @mod_forum, @other_forum, @forum_post] }
get_auth forum_posts_path, @admin
assert_response :success context "using includes" do
assert_select "#forum-post-#{@forum_post.id}" should respond_to_search(creator: {name: "okuu"}).with { @mod_forum }
assert_select "#forum-post-#{@mod_post.id}"
assert_select "#forum-post-#{@admin_post.id}"
end end
end end
context "as an admin" do
setup do
CurrentUser.user = @admin
end
should respond_to_search({}).with { [@unrelated_forum, @admin_forum, @mod_forum, @other_forum, @forum_post] }
end
end end
context "show action" do context "show action" do

View File

@@ -1,15 +1,18 @@
require 'test_helper' require 'test_helper'
class ForumTopicsControllerTest < ActionDispatch::IntegrationTest class ForumTopicsControllerTest < ActionDispatch::IntegrationTest
def default_search_order(items)
->{ items.each { |val| val.reload }.sort_by(&:updated_at).reverse }
end
context "The forum topics controller" do context "The forum topics controller" do
setup do setup do
@user = create(:user) @user = create(:user)
@other_user = create(:user) @other_user = create(:user)
@mod = create(:moderator_user) @mod = create(:moderator_user, name: "okuu")
as(@user) do as(@user) do
@forum_topic = create(:forum_topic, creator: @user, title: "my forum topic") @forum_topic = create(:forum_topic, creator: @user, title: "my forum topic", original_post: build(:forum_post, creator: @user, topic: @forum_topic, body: "xxx"))
@forum_post = create(:forum_post, creator: @user, topic: @forum_topic, body: "xxx")
end end
end end
@@ -88,25 +91,26 @@ class ForumTopicsControllerTest < ActionDispatch::IntegrationTest
context "index action" do context "index action" do
setup do setup do
as(@user) do as(@user) do
@topic1 = create(:forum_topic, is_sticky: true, creator: @user) @sticky_topic = create(:forum_topic, is_sticky: true, creator: @user, original_post: build(:forum_post))
@topic2 = create(:forum_topic, creator: @user) @other_topic = create(:forum_topic, creator: @user, original_post: build(:forum_post))
@post1 = create(:forum_post, topic: @topic1, creator: @user, body: "xxx")
@post2 = create(:forum_post, topic: @topic2, creator: @user, body: "xxx")
end end
@mod_topic = as(@mod) { create(:forum_topic, creator: @mod, min_level: User::Levels::MODERATOR, original_post: build(:forum_post)) }
create(:bulk_update_request, forum_topic: @forum_topic)
create(:tag_alias, forum_topic: @other_topic)
end end
should "list public forum topics for members" do should "list public forum topics for members" do
get forum_topics_path get forum_topics_path
assert_response :success assert_response :success
assert_select "a.forum-post-link", count: 1, text: @topic1.title assert_select "a.forum-post-link", count: 1, text: @sticky_topic.title
assert_select "a.forum-post-link", count: 1, text: @topic2.title assert_select "a.forum-post-link", count: 1, text: @other_topic.title
end end
should "not list stickied topics first for JSON responses" do should "not list stickied topics first for JSON responses" do
get forum_topics_path, params: {format: :json} get forum_topics_path, params: {format: :json}
forum_topics = JSON.parse(response.body) forum_topics = JSON.parse(response.body)
assert_equal([@topic2.id, @topic1.id, @forum_topic.id], forum_topics.map {|t| t["id"]}) assert_equal(default_search_order([@other_topic, @sticky_topic, @forum_topic]).call.map(&:id), forum_topics.map {|t| t["id"]})
end end
should "render for atom feed" do should "render for atom feed" do
@@ -117,44 +121,59 @@ class ForumTopicsControllerTest < ActionDispatch::IntegrationTest
should "render for a sitemap" do should "render for a sitemap" do
get forum_topics_path(format: :sitemap) get forum_topics_path(format: :sitemap)
assert_response :success assert_response :success
assert_equal(ForumTopic.count, response.parsed_body.css("urlset url loc").size) assert_equal(ForumTopic.visible(User.anonymous).count, response.parsed_body.css("urlset url loc").size)
end end
context "with private topics" do context "with private topics" do
should "not show private topics to unprivileged users" do should "not show private topics to unprivileged users" do
as(@user) { @topic2.update!(min_level: User::Levels::MODERATOR) } as(@user) { @other_topic.update!(min_level: User::Levels::MODERATOR) }
get forum_topics_path get forum_topics_path
assert_response :success assert_response :success
assert_select "a.forum-post-link", count: 1, text: @topic1.title assert_select "a.forum-post-link", count: 1, text: @sticky_topic.title
assert_select "a.forum-post-link", count: 0, text: @topic2.title assert_select "a.forum-post-link", count: 0, text: @other_topic.title
end end
should "show private topics to privileged users" do should "show private topics to privileged users" do
as(@user) { @topic2.update!(min_level: User::Levels::MODERATOR) } as(@user) { @other_topic.update!(min_level: User::Levels::MODERATOR) }
get_auth forum_topics_path, @mod get_auth forum_topics_path, @mod
assert_response :success assert_response :success
assert_select "a.forum-post-link", count: 1, text: @topic1.title assert_select "a.forum-post-link", count: 1, text: @sticky_topic.title
assert_select "a.forum-post-link", count: 1, text: @topic2.title assert_select "a.forum-post-link", count: 1, text: @other_topic.title
end end
end end
context "with search conditions" do context "with search conditions" do
should "list all matching forum topics" do context "as a user" do
get forum_topics_path, params: {:search => {:title_matches => "forum"}} setup do
assert_response :success CurrentUser.user = @user
assert_select "a.forum-post-link", @forum_topic.title end
assert_select "a.forum-post-link", count: 0, text: @topic1.title
assert_select "a.forum-post-link", count: 0, text: @topic2.title should respond_to_search({}).with { default_search_order([@sticky_topic, @other_topic, @forum_topic]) }
should respond_to_search(order: "id").with { [@other_topic, @sticky_topic, @forum_topic] }
should respond_to_search(title_matches: "forum").with { @forum_topic }
should respond_to_search(title_matches: "bababa").with { [] }
should respond_to_search(is_sticky: "true").with { @sticky_topic }
context "using includes" do
should respond_to_search(forum_posts: {body_matches: "xxx"}).with { @forum_topic }
should respond_to_search(has_bulk_update_requests: "true").with { @forum_topic }
should respond_to_search(has_tag_aliases: "true").with { @other_topic }
should respond_to_search(creator_name: "okuu").with { [] }
end
end end
should "list nothing for when the search matches nothing" do context "as a moderator" do
get forum_topics_path, params: {:search => {:title_matches => "bababa"}} setup do
assert_response :success CurrentUser.user = @mod
assert_select "a.forum-post-link", count: 0, text: @forum_topic.title end
assert_select "a.forum-post-link", count: 0, text: @topic1.title
assert_select "a.forum-post-link", count: 0, text: @topic2.title should respond_to_search({}).with { default_search_order([@sticky_topic, @other_topic, @mod_topic, @forum_topic]) }
context "using includes" do
should respond_to_search(creator_name: "okuu").with { @mod_topic }
end
end end
end end

View File

@@ -3,8 +3,8 @@ require 'test_helper'
class IpBansControllerTest < ActionDispatch::IntegrationTest class IpBansControllerTest < ActionDispatch::IntegrationTest
context "The ip bans controller" do context "The ip bans controller" do
setup do setup do
@admin = create(:admin_user) @admin = create(:admin_user, name: "yukari")
@ip_ban = create(:ip_ban) @ip_ban = create(:ip_ban, ip_addr: "6.7.8.9")
end end
context "new action" do context "new action" do
@@ -24,21 +24,34 @@ class IpBansControllerTest < ActionDispatch::IntegrationTest
should "log a mod action" do should "log a mod action" do
post_auth ip_bans_path, @admin, params: { ip_ban: { ip_addr: "1.2.3.4", reason: "xyz" }} post_auth ip_bans_path, @admin, params: { ip_ban: { ip_addr: "1.2.3.4", reason: "xyz" }}
assert_equal("ip_ban_create", ModAction.last.category) assert_equal("ip_ban_create", ModAction.last&.category)
end end
end end
context "index action" do context "index action" do
setup do
CurrentUser.user = @admin
@subnet_ban = create(:ip_ban, ip_addr: "2.0.0.0/24", creator: @admin)
@other_ban = create(:ip_ban, reason: "malware")
end
should "render access denied for anonymous users" do
get ip_bans_path
assert_response 403
end
should "render" do should "render" do
get_auth ip_bans_path, @admin get_auth ip_bans_path, @admin
assert_response :success assert_response :success
end end
context "with search parameters" do should respond_to_search({}).with { [@other_ban, @subnet_ban, @ip_ban] }
should "render" do should respond_to_search(ip_addr: "6.7.8.9").with { @ip_ban }
get_auth ip_bans_path, @admin, params: {:search => {:ip_addr => "1.2.3.4"}} should respond_to_search(reason_matches: "malware").with { @other_ban }
assert_response :success
end context "using includes" do
should respond_to_search(creator_name: "yukari").with { @subnet_ban }
should respond_to_search(creator: {level: User::Levels::ADMIN}).with { @subnet_ban }
end end
end end

View File

@@ -2,6 +2,10 @@ require 'test_helper'
class ModActionsControllerTest < ActionDispatch::IntegrationTest class ModActionsControllerTest < ActionDispatch::IntegrationTest
context "The mod actions controller" do context "The mod actions controller" do
setup do
@mod_action = create(:mod_action, description: "blah", category: "post_delete")
end
context "index action" do context "index action" do
setup do setup do
@ban = create(:mod_action, category: :post_ban) @ban = create(:mod_action, category: :post_ban)
@@ -13,6 +17,15 @@ class ModActionsControllerTest < ActionDispatch::IntegrationTest
assert_response :success assert_response :success
end end
should respond_to_search({}).with { [@unrelated_action, @promote_action, @mod_action] }
should respond_to_search(category: ModAction.categories["user_level_change"]).with { @promote_action }
should respond_to_search(description_matches: "blah").with { @mod_action }
context "using includes" do
should respond_to_search(creator_name: "rumia").with { @promote_action }
should respond_to_search(creator: {level: User::Levels::BUILDER}).with { @promote_action }
end
context "category enum searches" do context "category enum searches" do
should respond_to_search(category: "post_ban").with { [@ban] } should respond_to_search(category: "post_ban").with { [@ban] }
should respond_to_search(category: "post_unban").with { [@unban] } should respond_to_search(category: "post_unban").with { [@unban] }
@@ -23,6 +36,7 @@ class ModActionsControllerTest < ActionDispatch::IntegrationTest
should respond_to_search(category_id: "44").with { [@ban] } should respond_to_search(category_id: "44").with { [@ban] }
should respond_to_search(category_id: "44,45").with { [@unban, @ban] } should respond_to_search(category_id: "44,45").with { [@unban, @ban] }
should respond_to_search(category_id: ">=44").with { [@unban, @ban] } should respond_to_search(category_id: ">=44").with { [@unban, @ban] }
end end
end end

View File

@@ -9,9 +9,8 @@ class ModerationReportsControllerTest < ActionDispatch::IntegrationTest
as(@spammer) do as(@spammer) do
@dmail = create(:dmail, from: @spammer, owner: @user, to: @user) @dmail = create(:dmail, from: @spammer, owner: @user, to: @user)
@comment = create(:comment, creator: @spammer) @comment = create(:comment, id: 1234, creator: @spammer)
@forum_topic = create(:forum_topic, creator: @spammer) @forum_post = create(:forum_post, topic: build(:forum_topic), body: "xxx", creator: @spammer)
@forum_post = create(:forum_post, topic: @forum_topic, creator: @spammer)
end end
end end
@@ -29,24 +28,37 @@ class ModerationReportsControllerTest < ActionDispatch::IntegrationTest
context "index action" do context "index action" do
setup do setup do
create(:moderation_report, model: @comment, creator: @user) @comment_report = create(:moderation_report, model: @comment, creator: @user)
@forum_report = create(:moderation_report, model: @forum_post, creator: @user)
@dmail_report = create(:moderation_report, reason: "spam", model: @dmail, creator: build(:builder_user, name: "daiyousei", created_at: 2.weeks.ago))
end end
should "render the access denied page for members" do context "as a user" do
get_auth moderation_reports_path, @user should "render the access denied page" do
assert_response 403 get_auth moderation_reports_path, @user
assert_response 403
end
end end
should "render for mods" do context "as a moderator" do
get_auth moderation_reports_path, @mod setup do
assert_response :success CurrentUser.user = @mod
end end
context "with search parameters" do
should "render" do should "render" do
get_auth moderation_reports_path, @mod, params: {:search => {:model_id => @comment.id}} get_auth moderation_reports_path, @mod
assert_response :success assert_response :success
end end
should respond_to_search({}).with { [@dmail_report, @forum_report, @comment_report] }
should respond_to_search(reason_matches: "spam").with { @dmail_report }
context "using includes" do
should respond_to_search(model_id: 1234).with { @comment_report }
should respond_to_search(model_type: "ForumPost").with { @forum_report }
should respond_to_search(ForumPost: {body_matches: "xxx"}).with { @forum_report }
should respond_to_search(creator_name: "daiyousei").with { @dmail_report }
end
end end
end end

View File

@@ -3,23 +3,34 @@ require 'test_helper'
class NoteVersionsControllerTest < ActionDispatch::IntegrationTest class NoteVersionsControllerTest < ActionDispatch::IntegrationTest
context "The note versions controller" do context "The note versions controller" do
setup do setup do
@user = create(:user) @user = create(:user, id: 100)
@user_2 = create(:user) @user_2 = create(:user, name: "cirno")
as(@user) { @note = create(:note) } as(@user) { @note = create(:note, id: 101) }
as(@user_2) { @note.update(body: "1 2") } as(@user_2) { @note.update(body: "blah", is_active: false) }
as(@user) { @note.update(body: "1 2 3") } as(@user) { @note.update(body: "1 2 3", is_active: true) }
end end
context "index action" do context "index action" do
should "list all versions" do setup do
@versions = @note.versions
end
should "render" do
get note_versions_path get note_versions_path
assert_response :success assert_response :success
end end
should "list all versions that match the search criteria" do should respond_to_search({}).with { @versions.reverse }
get note_versions_path, params: {:search => {:updater_id => @user_2.id}} should respond_to_search(body_matches: "blah").with { @versions[1] }
assert_response :success should respond_to_search(version: 1).with { @versions[0] }
should respond_to_search(is_active: "false").with { @versions[1] }
context "using includes" do
should respond_to_search(note_id: 101).with { @versions.reverse }
should respond_to_search(note_id: 102).with { [] }
should respond_to_search(updater_id: 100).with { [@versions[2], @versions[0]] }
should respond_to_search(updater: {name: "cirno"}).with { @versions[1] }
end end
end end

View File

@@ -8,24 +8,25 @@ class NotesControllerTest < ActionDispatch::IntegrationTest
end end
context "index action" do context "index action" do
should "list all notes" do setup do
as(@user) do
@post_note = create(:note, post: build(:post, id: 2001, tag_string: "touhou"))
@deleted_note = create(:note, is_active: false)
end
end
should "render" do
get notes_path get notes_path
assert_response :success assert_response :success
end end
should "list all notes (with search)" do should respond_to_search({}).with { [@deleted_note, @post_note, @note] }
params = { should respond_to_search(body_matches: "000").with { @note }
group_by: "note", should respond_to_search(is_active: "true").with { [@post_note, @note] }
search: {
body_matches: "000",
is_active: true,
post_id: @note.post_id,
post_tags_match: @note.post.tag_array.first
}
}
get notes_path, params: params context "using includes" do
assert_response :success should respond_to_search(post_id: 2001).with { @post_note }
should respond_to_search(post_tags_match: "touhou").with { @post_note }
end end
end end

View File

@@ -3,7 +3,8 @@ require 'test_helper'
class PostAppealsControllerTest < ActionDispatch::IntegrationTest class PostAppealsControllerTest < ActionDispatch::IntegrationTest
context "The post appeals controller" do context "The post appeals controller" do
setup do setup do
@user = create(:user) @user = create(:user, name: "orin")
@post = create(:post, id: 101, is_deleted: true)
end end
context "new action" do context "new action" do
@@ -24,8 +25,10 @@ class PostAppealsControllerTest < ActionDispatch::IntegrationTest
context "index action" do context "index action" do
setup do setup do
as(@user) do as(@user) do
@post = create(:post, :is_deleted => true) @post_appeal = create(:post_appeal, post: @post, creator: @user)
@post_appeal = create(:post_appeal, :post => @post) @unrelated_appeal = create(:post_appeal, reason: "Good.")
@resolved_appeal = create(:post_appeal)
@resolved_appeal.post.update(is_deleted: false)
end end
end end
@@ -34,16 +37,14 @@ class PostAppealsControllerTest < ActionDispatch::IntegrationTest
assert_response :success assert_response :success
end end
should "render for json" do should respond_to_search({}).with { [@resolved_appeal, @unrelated_appeal, @post_appeal] }
get post_appeals_path, as: :json should respond_to_search(reason_matches: "Good.").with { @unrelated_appeal }
assert_response :success should respond_to_search(is_resolved: "true").with { @resolved_appeal }
end
context "with search parameters" do context "using includes" do
should "render" do should respond_to_search(post_id: 101).with { @post_appeal }
get_auth post_appeals_path, @user, params: {:search => {:post_id => @post_appeal.post_id}} should respond_to_search(post: {is_deleted: "true"}).with { [@unrelated_appeal, @post_appeal] }
assert_response :success should respond_to_search(creator_name: "orin").with { @post_appeal }
end
end end
end end

View File

@@ -3,7 +3,7 @@ require 'test_helper'
class PostApprovalsControllerTest < ActionDispatch::IntegrationTest class PostApprovalsControllerTest < ActionDispatch::IntegrationTest
context "The post approvals controller" do context "The post approvals controller" do
setup do setup do
@approver = create(:approver) @approver = create(:approver, name: "eiki")
end end
context "create action" do context "create action" do
@@ -60,11 +60,25 @@ class PostApprovalsControllerTest < ActionDispatch::IntegrationTest
end end
context "index action" do context "index action" do
setup do
@post = create(:post, tag_string: "touhou", is_pending: true, uploader: build(:user, name: "komachi", created_at: 2.weeks.ago))
@post_approval = create(:post_approval, post: @post)
@user_approval = create(:post_approval, user: @approver)
@unrelated_approval = create(:post_approval)
end
should "render" do should "render" do
@approval = create(:post_approval)
get post_approvals_path get post_approvals_path
assert_response :success assert_response :success
end end
should respond_to_search({}).with { [@unrelated_approval, @user_approval, @post_approval] }
context "using includes" do
should respond_to_search(user_name: "eiki").with { @user_approval }
should respond_to_search(post_tags_match: "touhou").with { @post_approval }
should respond_to_search(post: {uploader_name: "komachi"}).with { @post_approval }
end
end end
end end
end end

View File

@@ -3,9 +3,8 @@ require 'test_helper'
class PostDisapprovalsControllerTest < ActionDispatch::IntegrationTest class PostDisapprovalsControllerTest < ActionDispatch::IntegrationTest
context "The post disapprovals controller" do context "The post disapprovals controller" do
setup do setup do
@approver = create(:approver) @approver = create(:approver, name: "eiki")
@post = create(:post, is_pending: true) @post = create(:post, tag_string: "touhou", is_pending: true, uploader: build(:user, name: "marisa", created_at: 2.weeks.ago))
@post_disapproval = create(:post_disapproval, post: @post)
end end
context "create action" do context "create action" do
@@ -40,16 +39,36 @@ class PostDisapprovalsControllerTest < ActionDispatch::IntegrationTest
end end
context "index action" do context "index action" do
setup do
@post_disapproval = create(:post_disapproval, post: @post)
@user_disapproval = create(:post_disapproval, user: @approver)
@unrelated_disapproval = create(:post_disapproval, message: "bad")
end
should "render" do
get post_disapprovals_path
assert_response :success
end
should respond_to_search({}).with { [@unrelated_disapproval, @user_disapproval, @post_disapproval] }
should respond_to_search(message: "bad").with { @unrelated_disapproval }
context "using includes" do
should respond_to_search(post_tags_match: "touhou").with { @post_disapproval }
should respond_to_search(post: {uploader_name: "marisa"}).with { @post_disapproval }
should respond_to_search(user_name: "eiki").with { @user_disapproval }
end
should "allow mods to see disapprover names" do should "allow mods to see disapprover names" do
get_auth post_disapprovals_path, create(:mod_user) get_auth post_disapprovals_path, create(:mod_user)
assert_response :success assert_response :success
assert_select "tr#post-disapproval-#{@post_disapproval.id} .created-column a.user-member", true assert_select "tr#post-disapproval-#{@post_disapproval.id} .created-column a.user-post-approver", true
end end
should "not allow non-mods to see disapprover names" do should "not allow non-mods to see disapprover names" do
get post_disapprovals_path get post_disapprovals_path
assert_response :success assert_response :success
assert_select "tr#post-disapproval-#{@post_disapproval.id} .created-column a.user-member", false assert_select "tr#post-disapproval-#{@post_disapproval.id} .created-column a.user-post-approver", false
end end
end end
end end

View File

@@ -4,10 +4,10 @@ class PostFlagsControllerTest < ActionDispatch::IntegrationTest
context "The post flags controller" do context "The post flags controller" do
setup do setup do
@user = create(:user) @user = create(:user)
@flagger = create(:gold_user, created_at: 2.weeks.ago) @flagger = create(:gold_user, id: 999, created_at: 2.weeks.ago)
@uploader = create(:mod_user, created_at: 2.weeks.ago) @uploader = create(:mod_user, name: "chen", created_at: 2.weeks.ago)
@mod = create(:mod_user) @mod = create(:mod_user)
@post = create(:post, is_flagged: true, uploader: @uploader) @post = create(:post, id: 101, is_flagged: true, uploader: @uploader)
@post_flag = create(:post_flag, post: @post, creator: @flagger) @post_flag = create(:post_flag, post: @post, creator: @flagger)
end end
@@ -26,6 +26,11 @@ class PostFlagsControllerTest < ActionDispatch::IntegrationTest
end end
context "index action" do context "index action" do
setup do
@other_flag = create(:post_flag, post: build(:post, is_flagged: true, tag_string: "touhou"))
@unrelated_flag = create(:post_flag, reason: "poor quality")
end
should "render" do should "render" do
get post_flags_path get post_flags_path
assert_response :success assert_response :success
@@ -60,23 +65,46 @@ class PostFlagsControllerTest < ActionDispatch::IntegrationTest
assert_select "tr#post-flag-#{@post_flag.id} .flagged-column a.user-gold", true assert_select "tr#post-flag-#{@post_flag.id} .flagged-column a.user-gold", true
end end
context "with search parameters" do context "as a normal user" do
should "render" do setup do
get_auth post_flags_path(search: { post_id: @post_flag.post_id }), @user CurrentUser.user = @user
assert_response :success
end end
should "hide flagged posts when the searcher is the uploader" do should respond_to_search({}).with { [@unrelated_flag, @other_flag, @post_flag] }
get_auth post_flags_path(search: { creator_id: @flagger.id }), @uploader should respond_to_search(reason_matches: "poor quality").with { @unrelated_flag }
assert_response :success should respond_to_search(category: "normal").with { [@unrelated_flag, @other_flag, @post_flag] }
assert_select "tr#post-flag-#{@post_flag.id}", false should respond_to_search(category: "deleted").with { [] }
context "using includes" do
should respond_to_search(post_id: 101).with { @post_flag }
should respond_to_search(post_tags_match: "touhou").with { @other_flag }
should respond_to_search(post: {uploader_name: "chen"}).with { @post_flag }
should respond_to_search(creator_id: 999).with { [] }
end
end
context "when the user is the uploader" do
setup do
CurrentUser.user = @uploader
end end
should "show flagged posts when the searcher is not the uploader" do should respond_to_search(creator_id: 999).with { [] }
get_auth post_flags_path(search: { creator_id: @flagger.id }), @mod end
assert_response :success
assert_select "tr#post-flag-#{@post_flag.id}", true context "when the user is a mod and not the uploader" do
setup do
CurrentUser.user = @mod
end end
should respond_to_search(creator_id: 999).with { @post_flag }
end
context "when the user is the flagger" do
setup do
CurrentUser.user = @flagger
end
should respond_to_search(creator_id: 999).with { @post_flag }
end end
end end

View File

@@ -3,9 +3,9 @@ require 'test_helper'
class PostReplacementsControllerTest < ActionDispatch::IntegrationTest class PostReplacementsControllerTest < ActionDispatch::IntegrationTest
context "The post replacements controller" do context "The post replacements controller" do
setup do setup do
@mod = create(:moderator_user, can_approve_posts: true, created_at: 1.month.ago) @mod = create(:moderator_user, name: "yukari", can_approve_posts: true, created_at: 1.month.ago)
as(@mod) do as(@mod) do
@post = create(:post, source: "https://google.com") @post = create(:post, source: "https://google.com", tag_string: "touhou")
@post_replacement = create(:post_replacement, post: @post) @post_replacement = create(:post_replacement, post: @post)
end end
end end
@@ -60,10 +60,23 @@ class PostReplacementsControllerTest < ActionDispatch::IntegrationTest
end end
context "index action" do context "index action" do
setup do
as(create(:admin_user)) { @admin_replacement = create(:post_replacement, replacement_url: "https://danbooru.donmai.us") }
end
should "render" do should "render" do
get post_replacements_path, params: {format: "json"} get post_replacements_path
assert_response :success assert_response :success
end end
should respond_to_search({}).with { [@admin_replacement, @post_replacement] }
should respond_to_search(replacement_url_like: "*danbooru*").with { @admin_replacement }
context "using includes" do
should respond_to_search(post_tags_match: "touhou").with { @post_replacement }
should respond_to_search(creator: {level: User::Levels::ADMIN}).with { @admin_replacement }
should respond_to_search(creator_name: "yukari").with { @post_replacement }
end
end end
end end
end end

View File

@@ -3,17 +3,45 @@ require 'test_helper'
class PostVotesControllerTest < ActionDispatch::IntegrationTest class PostVotesControllerTest < ActionDispatch::IntegrationTest
context "The post vote controller" do context "The post vote controller" do
setup do setup do
@user = create(:gold_user) @user = create(:gold_user, name: "meiling")
@post = create(:post) @post = create(:post, tag_string: "dragon")
end end
context "index action" do context "index action" do
should "work" do setup do
as(@user) { create(:post_vote, post_id: @post.id, user_id: @user.id) } @admin = create(:admin_user)
get_auth post_votes_path, @user as(@user) { @post_vote = create(:post_vote, post: @post, user: @user) }
as(@admin) { @admin_vote = create(:post_vote, post: @post, user: @admin) }
@unrelated_vote = create(:post_vote)
end
should "render" do
get_auth post_votes_path, @user
assert_response :success assert_response :success
end end
context "as a user" do
setup do
CurrentUser.user = @user
end
should respond_to_search({}).with { @post_vote }
end
context "as a moderator" do
setup do
CurrentUser.user = @admin
end
should respond_to_search({}).with { [@unrelated_vote, @admin_vote, @post_vote] }
should respond_to_search(score: 1).with { [@unrelated_vote, @admin_vote, @post_vote].select{ |v| v.score == 1 } }
context "using includes" do
should respond_to_search(post_tags_match: "dragon").with { [@admin_vote, @post_vote] }
should respond_to_search(user_name: "meiling").with { @post_vote }
should respond_to_search(user: {level: User::Levels::ADMIN}).with { @admin_vote }
end
end
end end
context "create action" do context "create action" do

View File

@@ -7,14 +7,42 @@ class TagAliasesControllerTest < ActionDispatch::IntegrationTest
end end
context "index action" do context "index action" do
should "list all tag alias" do setup do
@user = create(:builder_user, name: "sakuya")
as (@user) do
@forum_topic = create(:forum_topic, title: "Touhou BUR")
@forum_post = create(:forum_post, topic: @forum_topic, body: "because")
end
@antecedent_tag = create(:copyright_tag, name: "touhou", post_count: 1000)
@consequent_tag = create(:copyright_tag, name: "touhou_project", post_count: 10)
@antecedent_wiki = create(:wiki_page, title: "touhou", body: "zun project")
@consequent_wiki = create(:wiki_page, title: "touhou_project")
@other_alias = create(:tag_alias, antecedent_name: "touhou", consequent_name: "touhou_project", creator: @user, status: "pending", forum_topic: @forum_topic, forum_post: @forum_post)
@unrelated_alias = create(:tag_alias)
end
should "render" do
get tag_aliases_path get tag_aliases_path
assert_response :success assert_response :success
end end
should "list all tag_alias (with search)" do should respond_to_search({}).with { [@unrelated_alias, @other_alias, @tag_alias] }
get tag_aliases_path, params: {:search => {:antecedent_name => "aaa"}} should respond_to_search(antecedent_name: "aaa").with { @tag_alias }
assert_response :success should respond_to_search(consequent_name: "bbb").with { @tag_alias }
should respond_to_search(status: "pending").with { @other_alias }
context "using includes" do
should respond_to_search(antecedent_tag: {post_count: 1000}).with { @other_alias }
should respond_to_search(consequent_tag: {category: Tag.categories.copyright}).with { @other_alias }
should respond_to_search(has_antecedent_tag: "true").with { @other_alias }
should respond_to_search(has_consequent_tag: "false").with { [@unrelated_alias, @tag_alias] }
should respond_to_search(antecedent_wiki: {body_matches: "zun project"}).with { @other_alias }
should respond_to_search(has_consequent_wiki: "true").with { @other_alias }
should respond_to_search(forum_topic: {title_matches: "Touhou BUR"}).with { @other_alias }
should respond_to_search(forum_post: {body: "because"}).with { @other_alias }
should respond_to_search(creator_name: "sakuya").with { @other_alias }
should respond_to_search(creator: {level: User::Levels::BUILDER}).with { @other_alias }
end end
end end

View File

@@ -7,6 +7,44 @@ class TagImplicationsControllerTest < ActionDispatch::IntegrationTest
end end
context "index action" do context "index action" do
setup do
@user = create(:builder_user, name: "sakuya")
as (@user) do
@forum_topic = create(:forum_topic, title: "Weapon BUR")
@forum_post = create(:forum_post, topic: @forum_topic, body: "because")
end
@antecedent_tag = create(:copyright_tag, name: "cannon", post_count: 10)
@consequent_tag = create(:copyright_tag, name: "weapon", post_count: 1000)
@antecedent_wiki = create(:wiki_page, title: "cannon", body: "made of fun")
@consequent_wiki = create(:wiki_page, title: "weapon")
@other_implication = create(:tag_implication, antecedent_name: "cannon", consequent_name: "weapon", creator: @user, status: "pending", forum_topic: @forum_topic, forum_post: @forum_post)
@unrelated_implication = create(:tag_implication)
end
should "render" do
get tag_implications_path
assert_response :success
end
should respond_to_search({}).with { [@unrelated_implication, @other_implication, @tag_implication] }
should respond_to_search(antecedent_name: "aaa").with { @tag_implication }
should respond_to_search(consequent_name: "bbb").with { @tag_implication }
should respond_to_search(status: "pending").with { @other_implication }
context "using includes" do
should respond_to_search(antecedent_tag: {post_count: 10}).with { @other_implication }
should respond_to_search(consequent_tag: {category: Tag.categories.copyright}).with { @other_implication }
should respond_to_search(has_antecedent_tag: "true").with { @other_implication }
should respond_to_search(has_consequent_tag: "false").with { [@unrelated_implication, @tag_implication] }
should respond_to_search(antecedent_wiki: {body_matches: "made of fun"}).with { @other_implication }
should respond_to_search(has_consequent_wiki: "true").with { @other_implication }
should respond_to_search(forum_topic: {title_matches: "Weapon BUR"}).with { @other_implication }
should respond_to_search(forum_post: {body: "because"}).with { @other_implication }
should respond_to_search(creator_name: "sakuya").with { @other_implication }
should respond_to_search(creator: {level: User::Levels::BUILDER}).with { @other_implication }
end
should "list all tag implications" do should "list all tag implications" do
get tag_implications_path get tag_implications_path
assert_response :success assert_response :success

View File

@@ -36,27 +36,43 @@ class TagsControllerTest < ActionDispatch::IntegrationTest
context "searching" do context "searching" do
setup do setup do
as(@user) do as(@user) do
@miku = create(:tag, name: "hatsune_miku", category: Tag.categories.character) @miku = create(:character_tag, name: "hatsune_miku")
@wokada = create(:tag, name: "wokada", category: Tag.categories.artist) @wokada = create(:artist_tag, name: "wokada")
@vocaloid = create(:tag, name: "vocaloid", category: Tag.categories.copyright) @vocaloid = create(:copyright_tag, name: "vocaloid")
@weapon = create(:tag, name: "weapon")
@empty = create(:tag, name: "empty", post_count: 0) @empty = create(:tag, name: "empty", post_count: 0)
create(:tag_alias, antecedent_name: "miku", consequent_name: "hatsune_miku") create(:tag_alias, antecedent_name: "miku", consequent_name: "hatsune_miku")
create(:wiki_page, title: "hatsune_miku") create(:tag_implication, antecedent_name: "axe", consequent_name: "weapon")
create(:wiki_page, title: "hatsune_miku", body: "[[vocaloid]]")
create(:artist, name: "wokada") create(:artist, name: "wokada")
end end
end end
should "render" do
get tags_path
assert_response :success
end
should respond_to_search({}).with { [@weapon, @vocaloid, @wokada, @miku, @tag] }
should respond_to_search(name_matches: "hatsune_miku").with { @miku } should respond_to_search(name_matches: "hatsune_miku").with { @miku }
should respond_to_search(name_normalize: "HATSUNE_MIKU ").with { @miku } should respond_to_search(name_normalize: "HATSUNE_MIKU ").with { @miku }
should respond_to_search(name_or_alias_matches: "miku").with { @miku } should respond_to_search(name_or_alias_matches: "miku").with { @miku }
should respond_to_search(fuzzy_name_matches: "miku_hatsune", order: "similarity").with { @miku } should respond_to_search(fuzzy_name_matches: "miku_hatsune", order: "similarity").with { @miku }
should respond_to_search(name: "empty", hide_empty: "true").with { [] } should respond_to_search(name: "empty", hide_empty: "true").with { [] }
should respond_to_search(name: "empty", hide_empty: "false").with { [@empty] } should respond_to_search(name: "empty", hide_empty: "false").with { [@empty] }
should respond_to_search(name: "wokada", has_artist: "true").with { @wokada }
should respond_to_search(name: "hatsune_miku", has_artist: "false").with { @miku } context "using includes" do
should respond_to_search(name: "hatsune_miku", has_wiki: "true").with { @miku } should respond_to_search(name: "wokada", has_artist: "true").with { @wokada }
should respond_to_search(name: "vocaloid", has_wiki: "false").with { @vocaloid } should respond_to_search(name: "hatsune_miku", has_artist: "false").with { @miku }
should respond_to_search(name: "hatsune_miku", has_wiki_page: "true").with { @miku }
should respond_to_search(name: "vocaloid", has_wiki_page: "false").with { @vocaloid }
should respond_to_search(consequent_aliases: {antecedent_name: "miku"}).with { @miku }
should respond_to_search(consequent_implications: {antecedent_name: "axe"}).with { @weapon }
should respond_to_search(wiki_page: {body_matches: "*vocaloid*"}).with { @miku }
should respond_to_search(artist: {is_banned: "false"}).with { @wokada }
should respond_to_search(has_dtext_links: "true").with { @vocaloid }
end
end end
end end

View File

@@ -30,7 +30,7 @@ class UploadsControllerTest < ActionDispatch::IntegrationTest
context "The uploads controller" do context "The uploads controller" do
setup do setup do
@user = create(:contributor_user) @user = create(:contributor_user, name: "marisa")
mock_iqdb_service! mock_iqdb_service!
end end
@@ -173,8 +173,11 @@ class UploadsControllerTest < ActionDispatch::IntegrationTest
context "index action" do context "index action" do
setup do setup do
as(@user) do as(@user) do
@upload = create(:source_upload, tag_string: "foo bar") @upload = create(:upload, tag_string: "foo bar", source: "http://example.com/foobar")
@upload2 = create(:source_upload, tag_string: "tagme", rating: "e") @post_upload = create(:source_upload, status: "completed", post: build(:post, tag_string: "touhou"), rating: "e")
end
as(create(:user)) do
@upload3 = create(:upload)
end end
end end
@@ -183,23 +186,28 @@ class UploadsControllerTest < ActionDispatch::IntegrationTest
assert_response :success assert_response :success
end end
context "with search parameters" do context "as an uploader" do
should "render" do setup do
search_params = { CurrentUser.user = @user
uploader_name: @upload.uploader.name,
source_matches: @upload.source,
rating: @upload.rating,
status: @upload.status,
server: @upload.server
}
get_auth uploads_path, @user, params: { search: search_params }
assert_response :success
get_auth uploads_path(format: :json), @user, params: { search: search_params }
assert_response :success
assert_equal(@upload.id, response.parsed_body.first["id"])
end end
should respond_to_search({}).with { [@post_upload, @upload] }
should respond_to_search(source: "http://example.com/foobar").with { @upload }
should respond_to_search(rating: "e").with { @post_upload }
should respond_to_search(tag_string: "*foo*").with { @upload }
context "using includes" do
should respond_to_search(post_tags_match: "touhou").with { @post_upload }
should respond_to_search(uploader: {name: "marisa"}).with { [@post_upload, @upload] }
end
end
context "as an admin" do
setup do
CurrentUser.user = create(:admin_user)
end
should respond_to_search({}).with { [@upload3, @post_upload, @upload] }
end end
end end

View File

@@ -3,9 +3,9 @@ require 'test_helper'
class UserFeedbacksControllerTest < ActionDispatch::IntegrationTest class UserFeedbacksControllerTest < ActionDispatch::IntegrationTest
context "The user feedbacks controller" do context "The user feedbacks controller" do
setup do setup do
@user = create(:user) @user = create(:user, name: "cirno")
@critic = create(:gold_user) @critic = create(:gold_user, name: "eiki")
@mod = create(:moderator_user) @mod = create(:moderator_user, id: 1000)
@user_feedback = create(:user_feedback, user: @user, creator: @critic) @user_feedback = create(:user_feedback, user: @user, creator: @critic)
end end
@@ -37,23 +37,43 @@ class UserFeedbacksControllerTest < ActionDispatch::IntegrationTest
end end
context "index action" do context "index action" do
setup do
@other_feedback = create(:user_feedback, user: @user, creator: @mod, body: "blah", category: "neutral")
@unrelated_feedback = create(:user_feedback, is_deleted: true)
end
should "render" do should "render" do
get_auth user_feedbacks_path, @user get_auth user_feedbacks_path, @user
assert_response :success assert_response :success
end end
should "not allow members to see deleted feedbacks" do context "as a user" do
as(@user) { @user_feedback.update!(is_deleted: true) } setup do
get_auth user_feedbacks_path, @user CurrentUser.user = @user
end
assert_response :success should respond_to_search({}).with { [@other_feedback, @user_feedback] }
assert_select "tr#user-feedback-#{@user_feedback.id}", false should respond_to_search(body_matches: "blah").with { @other_feedback }
should respond_to_search(category: "positive").with { @user_feedback }
should respond_to_search(is_deleted: "true").with { [] }
context "using includes" do
should respond_to_search(creator_name: "eiki").with { @user_feedback }
should respond_to_search(creator_id: 1000).with { @other_feedback }
should respond_to_search(creator: {level: User::Levels::GOLD}).with { @user_feedback }
end
end end
context "with search parameters" do context "as a moderator" do
should "render" do setup do
get_auth user_feedbacks_path, @critic, params: {:search => {:user_id => @user.id}} CurrentUser.user = @mod
assert_response :success end
should respond_to_search({}).with { [@unrelated_feedback, @other_feedback, @user_feedback] }
should respond_to_search(is_deleted: "true").with { @unrelated_feedback }
context "using includes" do
should respond_to_search(user_name: "cirno").with { [@other_feedback, @user_feedback] }
end end
end end
end end

View File

@@ -7,7 +7,14 @@ class UsersControllerTest < ActionDispatch::IntegrationTest
end end
context "index action" do context "index action" do
should "list all users" do setup do
@first_user = User.find(1)
@mod_user = create(:moderator_user, name: "yukari")
@other_user = create(:builder_user, can_upload_free: true, inviter: @mod_user, created_at: 2.weeks.ago)
@uploader = create(:user, created_at: 2.weeks.ago)
end
should "render" do
get users_path get users_path
assert_response :success assert_response :success
end end
@@ -28,14 +35,54 @@ class UsersControllerTest < ActionDispatch::IntegrationTest
assert_response 404 assert_response 404
end end
should "list all users (with search)" do should respond_to_search({}).with { [@uploader, @other_user, @mod_user, @user, @first_user] }
get users_path, params: {:search => {:name_matches => @user.name}} should respond_to_search(min_level: User::Levels::BUILDER).with { [@other_user, @mod_user, @first_user] }
assert_response :success should respond_to_search(can_upload_free: "true").with { @other_user }
end should respond_to_search(name_matches: "yukari").with { @mod_user }
should "list all users (with blank search parameters)" do context "using includes" do
get users_path, params: { search: { inviter: { name_matches: "" }, level: "", name: "test" } } setup do
assert_redirected_to users_path(search: { name: "test" }) as (@uploader) { @post = create(:post, tag_string: "touhou", uploader: @uploader, is_flagged: true) }
as (@user) do
create(:note, post: @post)
create(:artist_commentary, post: @post)
create(:artist)
create(:wiki_page)
@forum = create(:forum_post, creator: @user, topic: build(:forum_topic, creator: @user))
end
as (@other_user) do
@other_post = create(:post, rating: "e", uploader: @other_user)
create(:post_appeal, creator: @other_user, post: @post)
create(:comment, creator: @other_user, post: @other_post)
create(:forum_post_vote, creator: @other_user, forum_post: @forum)
create(:tag_alias, creator: @other_user)
create(:tag_implication, creator: @other_user)
end
as (@mod_user) do
create(:post_approval, user: @mod_user, post: @post)
create(:user_feedback, user: @other_user, creator: @mod_user)
create(:ban, user: @other_user, banner: @mod_user)
end
end
should respond_to_search(has_artist_versions: "true").with { @user }
should respond_to_search(has_wiki_page_versions: "true").with { @user }
should respond_to_search(has_forum_topics: "true").with { @user }
should respond_to_search(has_forum_posts: "true").with { @user }
should respond_to_search(has_forum_post_votes: "true").with { @other_user }
should respond_to_search(has_feedback: "true").with { @other_user }
should respond_to_search(has_tag_aliases: "true").with { @other_user }
should respond_to_search(has_tag_implications: "true").with { @other_user }
should respond_to_search(has_bans: "true").with { @other_user }
should respond_to_search(has_artist_commentary_versions: "true").with { @user }
should respond_to_search(has_comments: "true").with { @other_user }
should respond_to_search(has_note_versions: "true").with { @user }
should respond_to_search(has_post_appeals: "true").with { @other_user }
should respond_to_search(has_post_approvals: "true").with { @mod_user }
should respond_to_search(has_posts: "true").with { [@uploader, @other_user] }
should respond_to_search(posts_tags_match: "touhou").with { @uploader }
should respond_to_search(posts: {rating: "e"}).with { @other_user }
should respond_to_search(inviter: {name: "yukari"}).with { @other_user }
end end
end end

View File

@@ -3,23 +3,34 @@ require 'test_helper'
class WikiPageVersionsControllerTest < ActionDispatch::IntegrationTest class WikiPageVersionsControllerTest < ActionDispatch::IntegrationTest
context "The wiki page versions controller" do context "The wiki page versions controller" do
setup do setup do
@user = create(:user) @user = create(:user, id: 100)
as(@user) do @builder = create(:builder_user, name: "nitori")
@wiki_page = create(:wiki_page) as(@user) { @wiki_page = create(:wiki_page, id: 101) }
@wiki_page.update(:body => "1 2") as(@builder) { @wiki_page.update(title: "supreme", body: "blah", other_names: ["not_this"]) }
@wiki_page.update(:body => "2 3") as(@user) { @wiki_page.update(body: "blah blah") }
end
end end
context "index action" do context "index action" do
should "list all versions" do setup do
@versions = @wiki_page.versions
end
should "render" do
get wiki_page_versions_path get wiki_page_versions_path
assert_response :success assert_response :success
end end
should "list all versions that match the search criteria" do should respond_to_search({}).with { @versions.reverse }
get wiki_page_versions_path, params: {:search => {:wiki_page_id => @wiki_page.id}} should respond_to_search(title_matches: "supreme").with { [@versions[2], @versions[1]] }
assert_response :success should respond_to_search(body_matches: "blah").with { [@versions[2], @versions[1]] }
should respond_to_search(other_names_include_any: "not_this").with { [@versions[2], @versions[1]] }
context "using includes" do
should respond_to_search(wiki_page_id: 101).with { @versions.reverse }
should respond_to_search(wiki_page_id: 102).with { [] }
should respond_to_search(updater_id: 100).with { [@versions[2], @versions[0]] }
should respond_to_search(updater_name: "nitori").with { @versions[1] }
should respond_to_search(updater: {level: User::Levels::BUILDER}).with { @versions[1] }
end end
end end

View File

@@ -14,11 +14,13 @@ class WikiPagesControllerTest < ActionDispatch::IntegrationTest
@deleted = create(:wiki_page, title: "deleted", is_deleted: true) @deleted = create(:wiki_page, title: "deleted", is_deleted: true)
@vocaloid = create(:wiki_page, title: "vocaloid") @vocaloid = create(:wiki_page, title: "vocaloid")
@miku = create(:wiki_page, title: "hatsune_miku", other_names: ["初音ミク"], body: "miku is a [[vocaloid]]") @miku = create(:wiki_page, title: "hatsune_miku", other_names: ["初音ミク"], body: "miku is a [[vocaloid]]")
create(:tag, name: "hatsune_miku", category: Tag.categories.character) @picasso = create(:wiki_page, title: "picasso")
create(:artist, name: "picasso", is_banned: true)
create(:character_tag, name: "hatsune_miku")
end end
end end
should "list all wiki_pages" do should "render" do
get wiki_pages_path get wiki_pages_path
assert_response :success assert_response :success
end end
@@ -34,19 +36,27 @@ class WikiPagesControllerTest < ActionDispatch::IntegrationTest
assert_redirected_to wiki_pages_path(search: { title_normalize: "tagme" }, redirect: true) assert_redirected_to wiki_pages_path(search: { title_normalize: "tagme" }, redirect: true)
end end
should respond_to_search({}).with { [@picasso, @miku, @vocaloid, @deleted, @tagme] }
should respond_to_search(title: "tagme").with { @tagme } should respond_to_search(title: "tagme").with { @tagme }
should respond_to_search(title: "tagme", order: "post_count").with { @tagme } should respond_to_search(title: "tagme", order: "post_count").with { @tagme }
should respond_to_search(title_normalize: "TAGME ").with { @tagme } should respond_to_search(title_normalize: "TAGME ").with { @tagme }
should respond_to_search(tag: { category: Tag.categories.character }).with { @miku } should respond_to_search(hide_deleted: "true").with { [@picasso, @miku, @vocaloid, @tagme] }
should respond_to_search(hide_deleted: "true").with { [@miku, @vocaloid, @tagme] }
should respond_to_search(linked_to: "vocaloid").with { @miku } should respond_to_search(linked_to: "vocaloid").with { @miku }
should respond_to_search(not_linked_to: "vocaloid").with { [@vocaloid, @deleted, @tagme] } should respond_to_search(not_linked_to: "vocaloid").with { [@picasso, @vocaloid, @deleted, @tagme] }
should respond_to_search(other_names_match: "初音ミク").with { @miku } should respond_to_search(other_names_match: "初音ミク").with { @miku }
should respond_to_search(other_names_match: "初*").with { @miku } should respond_to_search(other_names_match: "初*").with { @miku }
should respond_to_search(other_names_present: "true").with { @miku } should respond_to_search(other_names_present: "true").with { @miku }
should respond_to_search(other_names_present: "false").with { [@vocaloid, @deleted, @tagme] } should respond_to_search(other_names_present: "false").with { [@picasso, @vocaloid, @deleted, @tagme] }
context "using includes" do
should respond_to_search(has_tag: "true").with { @miku }
should respond_to_search(tag: { category: Tag.categories.character }).with { @miku }
should respond_to_search(has_dtext_links: "true").with { @miku }
should respond_to_search(has_artist: "true").with { @picasso }
should respond_to_search(artist: {is_banned: "true"}).with { @picasso }
end
end end
context "search action" do context "search action" do

View File

@@ -9,28 +9,37 @@ module ControllerHelper
# setup { @touhou = create(:tag, name: "touhou") } # setup { @touhou = create(:tag, name: "touhou") }
# should respond_to_search(name: "touhou").with { @touhou } # should respond_to_search(name: "touhou").with { @touhou }
# #
def respond_to_search(search_params) def respond_to_search(search_params, other_params: {})
RespondToSearchMatcher.new(search_params) RespondToSearchMatcher.new(search_params, other_params)
end end
class RespondToSearchMatcher < Struct.new(:params) class RespondToSearchMatcher < Struct.new(:params, :other_params)
def description def description
"should respond to a search for #{params}" "should respond to a search for #{params}"
end end
def matches?(subject, &block) def matches?(subject, &block)
search_params = { search: params } search_params = other_params.merge({ search: params })
expected_items = @test_case.instance_eval(&@expected) expected_items = @test_case.instance_eval(&@expected)
@test_case.instance_eval do @test_case.instance_eval do
# calls e.g. "wiki_pages_path" if we're in WikiPagesControllerTest. # calls e.g. "wiki_pages_path" if we're in WikiPagesControllerTest.
index_url = send("#{subject.controller_path}_path") index_url = send("#{subject.controller_path}_path")
get index_url, as: :json, params: search_params # Allows for different authorization levels to be used, instead of just anonymous
if CurrentUser.user.present?
get_auth index_url, CurrentUser.user, as: :json, params: search_params
else
get index_url, as: :json, params: search_params
end
# Don't continue processing if there was an error
assert_response :success
# Some fields like :updated_at do not get finalized until later, so allow lambda functions
# to evaluate expressions like a sort after the network call has completed
expected_items = expected_items.call if expected_items.respond_to?(:call)
expected_ids = Array(expected_items).map(&:id) expected_ids = Array(expected_items).map(&:id)
responded_ids = response.parsed_body.map { |item| item["id"] } responded_ids = response.parsed_body.map { |item| item["id"] }
assert_response :success
assert_equal(expected_ids, responded_ids) assert_equal(expected_ids, responded_ids)
end end
end end

View File

@@ -83,7 +83,7 @@ class BulkUpdateRequestTest < ActiveSupport::TestCase
context "that has an invalid alias" do context "that has an invalid alias" do
setup do setup do
@alias1 = create(:tag_alias, creator: @admin) @alias1 = create(:tag_alias, antecedent_name: "aaa", consequent_name: "bbb", creator: @admin)
@req = FactoryBot.build(:bulk_update_request, :script => "create alias bbb -> aaa") @req = FactoryBot.build(:bulk_update_request, :script => "create alias bbb -> aaa")
end end