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
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)
unscoped.where(all.where_clause.invert(kind).ast)
end
@@ -139,8 +148,13 @@ module Searchable
end
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|
relation.search_attribute(attribute, params)
relation.search_attribute(attribute, indifferent_params)
end
end
@@ -159,7 +173,9 @@ module Searchable
when "User"
search_user_attribute(name, params)
when "Post"
search_post_id_attribute(params)
search_post_attribute(name, params)
when "Model"
search_polymorphic_attribute(name, params)
when :string, :text
search_text_attribute(name, params)
when :boolean
@@ -173,7 +189,8 @@ module Searchable
when :array
search_array_attribute(name, subtype, params)
else
raise NotImplementedError, "unhandled attribute type: #{name}"
raise NotImplementedError, "unhandled attribute type: #{name}" if type.blank?
search_includes(name, params, type)
end
end
@@ -214,26 +231,56 @@ module Searchable
end
def search_user_attribute(attr, params)
if params["#{attr}_id"]
search_attribute("#{attr}_id", params)
elsif params["#{attr}_name"]
if params["#{attr}_name"].present?
where(attr => User.search(name_matches: params["#{attr}_name"]).reorder(nil))
elsif params[attr]
where(attr => User.search(params[attr]).reorder(nil))
else
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
all
end
end
def search_post_id_attribute(params)
relation = all
def search_polymorphic_attribute(attr, params)
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 = relation.search_attribute(:post_id, params)
relation = all
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
if params[:post_tags_match].present?
relation = relation.where(post_id: Post.user_tag_match(params[:post_tags_match]).reorder(nil))
if params["#{attr}_id"].present?
relation = relation.search_attribute("#{attr}_id", params)
end
if params["#{attr}_type"].present? && !model_specified
relation = relation.search_attribute("#{attr}_type", params)
end
relation
@@ -292,6 +339,28 @@ module Searchable
relation
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)
if params[:order] == "custom"
parse_ids = PostQueryBuilder.new(nil).parse_range(params[:id], :integer)
@@ -319,7 +388,8 @@ module Searchable
params ||= {}
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
private

View File

@@ -93,6 +93,22 @@ class ApplicationRecord < ActiveRecord::Base
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
class_methods do
def without_timeout

View File

@@ -266,12 +266,6 @@ class Artist < ApplicationRecord
q = q.url_matches(params[:url_matches])
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]
when "name"
q = q.order("artists.name")
@@ -295,6 +289,14 @@ class Artist < ApplicationRecord
include BanMethods
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
[:members, :urls, :wiki_page, :tag_alias, :tag]
end

View File

@@ -33,7 +33,7 @@ class ArtistCommentary < ApplicationRecord
def search(params)
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?
q = q.text_matches(params[:text_matches])
@@ -146,6 +146,10 @@ class ArtistCommentary < ApplicationRecord
extend SearchMethods
include VersionMethods
def self.searchable_includes
[:post]
end
def self.available_includes
[:post]
end

View File

@@ -2,9 +2,23 @@ class ArtistCommentaryVersion < ApplicationRecord
belongs_to :post
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)
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)
end
@@ -42,6 +56,10 @@ class ArtistCommentaryVersion < ApplicationRecord
self[field].strip.empty? && (previous.nil? || previous[field].strip.empty?)
end
def self.searchable_includes
[:post, :updater]
end
def self.available_includes
[:post, :updater]
end

View File

@@ -42,9 +42,8 @@ class ArtistUrl < ApplicationRecord
def self.search(params = {})
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.normalized_url_matches(params[:normalized_url_matches])
@@ -59,11 +58,6 @@ class ArtistUrl < ApplicationRecord
q
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)
if url.blank?
all
@@ -134,6 +128,10 @@ class ArtistUrl < ApplicationRecord
errors[:url] << "'#{uri}' is malformed: #{error}"
end
def self.searchable_includes
[:artist]
end
def self.available_includes
[:artist]
end

View File

@@ -9,7 +9,9 @@ class ArtistVersion < ApplicationRecord
def search(params)
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"
q = q.order("artist_versions.name").default_order
@@ -103,6 +105,10 @@ class ArtistVersion < ApplicationRecord
end
end
def self.searchable_includes
[:updater, :artist]
end
def self.available_includes
[:updater, :artist]
end

View File

@@ -22,7 +22,7 @@ class Ban < ApplicationRecord
def self.search(params)
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.expired if params[:expired].to_s.truthy?
@@ -97,6 +97,10 @@ class Ban < ApplicationRecord
ModAction.log(%{Unbanned <@#{user_name}>}, :user_unban)
end
def self.searchable_includes
[:user, :banner]
end
def self.available_includes
[:user, :banner]
end

View File

@@ -35,7 +35,7 @@ class BulkUpdateRequest < ApplicationRecord
def search(params = {})
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])
if params[:status].present?
@@ -152,6 +152,10 @@ class BulkUpdateRequest < ApplicationRecord
status == "rejected"
end
def self.searchable_includes
[:user, :forum_topic, :forum_post, :approver]
end
def self.available_includes
[:user, :forum_topic, :forum_post, :approver]
end

View File

@@ -28,7 +28,7 @@ class Comment < ApplicationRecord
def search(params)
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)
case params[:order]
@@ -140,6 +140,10 @@ class Comment < ApplicationRecord
DText.quote(body, creator.name)
end
def self.searchable_includes
[:post, :creator, :updater]
end
def self.available_includes
[:post, :creator, :updater]
end

View File

@@ -18,15 +18,9 @@ class CommentVote < ApplicationRecord
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)
q = super
q = q.search_attributes(params, :comment_id, :user, :score)
q = q.comment_matches(params[:comment])
q = q.search_attributes(params, :score)
q.apply_default_order(params)
end
@@ -44,6 +38,10 @@ class CommentVote < ApplicationRecord
score == -1
end
def self.searchable_includes
[:comment, :user]
end
def self.available_includes
[:comment, :user]
end

View File

@@ -98,7 +98,7 @@ class Dmail < ApplicationRecord
def search(params)
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(:body, params[:message_matches], index_column: :message_index)
@@ -172,6 +172,10 @@ class Dmail < ApplicationRecord
key ? "dmail ##{id}/#{self.key}" : "dmail ##{id}"
end
def self.searchable_includes
[:to, :from]
end
def self.available_includes
[:owner, :to, :from]
end

View File

@@ -1,6 +1,7 @@
class DtextLink < ApplicationRecord
belongs_to :model, polymorphic: 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]
@@ -28,43 +29,9 @@ class DtextLink < ApplicationRecord
links
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)
q = super
q = q.search_attributes(params, :model_type, :model_id, :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 = q.search_attributes(params, :link_type, :link_target)
q.apply_default_order(params)
end
@@ -78,7 +45,15 @@ class DtextLink < ApplicationRecord
self.link_target = self.link_target.truncate(2048, omission: "")
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
[:model]
[:model, :linked_wiki, :linked_tag]
end
end

View File

@@ -27,7 +27,7 @@ class FavoriteGroup < ApplicationRecord
def search(params)
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?
q = q.name_matches(params[:name_matches])
@@ -164,6 +164,10 @@ class FavoriteGroup < ApplicationRecord
post_ids.include?(post_id)
end
def self.searchable_includes
[:creator]
end
def self.available_includes
[:creator]
end

View File

@@ -32,31 +32,19 @@ class ForumPost < ApplicationRecord
)
module SearchMethods
def topic_title_matches(title)
where(topic_id: ForumTopic.search(title_matches: title).select(:id))
end
def visible(user)
where(topic_id: ForumTopic.visible(user))
end
def search(params)
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)
if params[:linked_to].present?
q = q.where(id: DtextLink.forum_post.wiki_link.where(link_target: params[:linked_to]).select(:model_id))
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)
end
end
@@ -179,6 +167,10 @@ class ForumPost < ApplicationRecord
"forum ##{id}"
end
def self.searchable_includes
[:creator, :updater, :topic, :dtext_links, :votes, :tag_alias, :tag_implication, :bulk_update_request]
end
def self.available_includes
[:creator, :updater, :topic, :dtext_links, :votes, :tag_alias, :tag_implication, :bulk_update_request]
end

View File

@@ -20,7 +20,7 @@ class ForumPostVote < ApplicationRecord
def self.search(params)
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.apply_default_order(params)
end
@@ -59,7 +59,11 @@ class ForumPostVote < ApplicationRecord
end
end
def self.searchable_includes
[:creator, :forum_post]
end
def self.available_includes
[:creator]
[:creator, :forum_post]
end
end

View File

@@ -19,6 +19,8 @@ class ForumTopic < ApplicationRecord
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_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_associated :original_post
@@ -85,7 +87,7 @@ class ForumTopic < ApplicationRecord
def search(params)
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)
if params[:is_private].to_s.truthy?
@@ -111,6 +113,8 @@ class ForumTopic < ApplicationRecord
case params[:order]
when "sticky"
q = q.sticky_first
when "id"
q = q.order(id: :desc)
else
q = q.apply_default_order(params)
end
@@ -190,6 +194,10 @@ class ForumTopic < ApplicationRecord
super + [:is_read?]
end
def self.searchable_includes
[:creator, :updater, :forum_posts, :bulk_update_requests, :tag_aliases, :tag_implications]
end
def self.available_includes
[:creator, :updater, :original_post]
end

View File

@@ -13,7 +13,7 @@ class IpAddress < ApplicationRecord
def self.search(params)
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)
end
@@ -54,6 +54,10 @@ class IpAddress < ApplicationRecord
super & attributes.keys.map(&:to_sym)
end
def self.searchable_includes
[:user, :model]
end
def self.available_includes
[:user, :model]
end

View File

@@ -26,7 +26,8 @@ class IpBan < ApplicationRecord
def self.search(params)
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?
q = q.where("ip_addr = ?", params[:ip_addr])
@@ -77,6 +78,10 @@ class IpBan < ApplicationRecord
super(ip_addr.strip)
end
def self.searchable_includes
[:creator]
end
def self.available_includes
[:creator]
end

View File

@@ -63,7 +63,7 @@ class ModAction < ApplicationRecord
def self.search(params)
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.apply_default_order(params)
@@ -77,6 +77,10 @@ class ModAction < ApplicationRecord
create(creator: user, description: desc, category: categories[cat])
end
def self.searchable_includes
[:creator]
end
def self.available_includes
[:creator]
end

View File

@@ -83,11 +83,16 @@ class ModerationReport < ApplicationRecord
def self.search(params)
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)
end
def self.searchable_includes
[:creator, :model]
end
def self.available_includes
[:creator, :model]
end

View File

@@ -16,7 +16,7 @@ class Note < ApplicationRecord
def search(params)
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.apply_default_order(params)
@@ -129,6 +129,10 @@ class Note < ApplicationRecord
new_note.save
end
def self.searchable_includes
[:post]
end
def self.available_includes
[:post]
end

View File

@@ -6,7 +6,7 @@ class NoteVersion < ApplicationRecord
def self.search(params)
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.apply_default_order(params)
@@ -71,6 +71,10 @@ class NoteVersion < ApplicationRecord
end
end
def self.searchable_includes
[:updater, :note, :post]
end
def self.available_includes
[:updater, :note, :post]
end

View File

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

View File

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

View File

@@ -18,7 +18,7 @@ class PostAppeal < ApplicationRecord
module SearchMethods
def search(params)
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.apply_default_order(params)
@@ -35,6 +35,10 @@ class PostAppeal < ApplicationRecord
errors[:post] << "cannot be appealed" if post.is_status_locked? || !post.is_appealable?
end
def self.searchable_includes
[:creator, :post]
end
def self.available_includes
[:creator, :post]
end

View File

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

View File

@@ -23,7 +23,7 @@ class PostDisapproval < ApplicationRecord
def search(params)
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.with_message if params[:has_message].to_s.truthy?
@@ -41,6 +41,10 @@ class PostDisapproval < ApplicationRecord
end
end
def self.searchable_includes
[:user, :post]
end
def self.available_includes
[:user, :post]
end

View File

@@ -58,7 +58,7 @@ class PostFlag < ApplicationRecord
def search(params)
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])
if params[:creator_id].present?
@@ -113,6 +113,10 @@ class PostFlag < ApplicationRecord
post.uploader_id
end
def self.searchable_includes
[:post]
end
def self.available_includes
[:post]
end

View File

@@ -22,7 +22,7 @@ class PostReplacement < ApplicationRecord
class_methods do
def search(params = {})
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)
end
end
@@ -39,6 +39,10 @@ class PostReplacement < ApplicationRecord
tags.join(" ")
end
def self.searchable_includes
[:creator, :post]
end
def self.available_includes
[:creator, :post]
end

View File

@@ -20,12 +20,12 @@ class PostVote < ApplicationRecord
def self.search(params)
q = super
q = q.search_attributes(params, :post, :user, :score)
q = q.search_attributes(params, :score)
q.apply_default_order(params)
end
def initialize_attributes
self.user_id ||= CurrentUser.user.id
self.user_id ||= CurrentUser.id
if vote == "up"
self.score = 1
@@ -50,6 +50,10 @@ class PostVote < ApplicationRecord
end
end
def self.searchable_includes
[:user, :post]
end
def self.available_includes
[:user, :post]
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 :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 :dtext_links, foreign_key: :link_target, primary_key: :name
validates :name, tag_name: true, uniqueness: true, on: :create
validates :name, tag_name: true, on: :name
@@ -284,18 +285,6 @@ class Tag < ApplicationRecord
q = q.nonempty
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]
when "name"
q = q.order("name")
@@ -355,8 +344,16 @@ class Tag < ApplicationRecord
Post.system_tag_match(name)
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
[: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
include ApiMethods

View File

@@ -12,8 +12,8 @@ class TagRelationship < ApplicationRecord
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 :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
has_one :consequent_wiki, through: :consequent_tag, source: :wiki_page
belongs_to :antecedent_wiki, class_name: "WikiPage", foreign_key: "antecedent_name", primary_key: "title", optional: true
belongs_to :consequent_wiki, class_name: "WikiPage", foreign_key: "antecedent_name", primary_key: "title", optional: true
scope :active, -> {approved}
scope :approved, -> {where(status: %w[active processing queued])}
@@ -94,7 +94,7 @@ class TagRelationship < ApplicationRecord
def search(params)
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?
q = q.name_matches(params[:name_matches])
@@ -157,6 +157,14 @@ class TagRelationship < ApplicationRecord
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
[:creator, :approver, :forum_post, :forum_topic, :antecedent_tag, :consequent_tag, :antecedent_wiki, :consequent_wiki]
end

View File

@@ -184,7 +184,7 @@ class Upload < ApplicationRecord
def self.search(params)
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?
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?
end
def self.searchable_includes
[:uploader, :post]
end
def self.available_includes
[:uploader, :post]
end

View File

@@ -80,7 +80,6 @@ class User < ApplicationRecord
validates_presence_of :comment_threshold
before_validation :normalize_blacklisted_tags
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_commentary_versions, foreign_key: :updater_id
has_many :comments, foreign_key: :creator_id
@@ -91,7 +90,6 @@ class User < ApplicationRecord
has_many :forum_topic_visits, dependent: :destroy
has_many :visited_forum_topics, through: :forum_topic_visits, source: :forum_topic
has_many :moderation_reports, as: :model
has_many :pools, foreign_key: :creator_id
has_many :posts, :foreign_key => "uploader_id"
has_many :post_appeals, foreign_key: :creator_id
has_many :post_approvals, :dependent => :destroy
@@ -105,10 +103,10 @@ class User < ApplicationRecord
has_one :api_key
has_one :token_bucket
has_one :email_address, dependent: :destroy
has_many :notes, foreign_key: :creator_id
has_many :note_versions, :foreign_key => "updater_id"
has_many :dmails, -> {order("dmails.id desc")}, :foreign_key => "owner_id"
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 :user_name_change_requests, -> {order("user_name_change_requests.created_at desc")}
has_many :favorite_groups, -> {order(name: :asc)}, foreign_key: :creator_id
@@ -537,7 +535,7 @@ class User < ApplicationRecord
params = params.dup
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?
q = q.where_ilike(:name, normalize_name(params[:name_matches]))
@@ -606,6 +604,10 @@ class User < ApplicationRecord
"<@#{name}>"
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
[:inviter]
end

View File

@@ -32,7 +32,7 @@ class UserFeedback < ApplicationRecord
def search(params)
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.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)
end
def self.searchable_includes
[:creator, :user]
end
def self.available_includes
[:creator, :user]
end

View File

@@ -48,10 +48,6 @@ class WikiPage < ApplicationRecord
end
end
def tag_matches(params)
where(title: Tag.search(params).select(:name).reorder(nil))
end
def linked_to(title)
where(dtext_links: DtextLink.wiki_page.wiki_link.where(link_target: normalize_title(title)))
end
@@ -78,10 +74,6 @@ class WikiPage < ApplicationRecord
q = q.other_names_match(params[:other_names_match])
end
if params[:tag].present?
q = q.tag_matches(params[:tag])
end
if params[:linked_to].present?
q = q.linked_to(params[:linked_to])
end
@@ -197,12 +189,12 @@ class WikiPage < ApplicationRecord
def merge_version?
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
def create_new_version
versions.create(
:updater_id => CurrentUser.user.id,
:updater_id => CurrentUser.id,
:updater_ip_addr => CurrentUser.ip_addr,
:title => title,
:body => body,
@@ -245,6 +237,14 @@ class WikiPage < ApplicationRecord
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
[:tag, :artist, :dtext_links]
end

View File

@@ -9,9 +9,9 @@ class WikiPageVersion < ApplicationRecord
def search(params)
q = super
q = q.search_attributes(params, :updater, :is_locked, :is_deleted, :wiki_page_id)
q = q.text_attribute_matches(:title, params[:title])
q = q.text_attribute_matches(:body, params[:body])
q = q.search_attributes(params, :title, :body, :other_names, :is_locked, :is_deleted)
q = q.text_attribute_matches(:title, params[:title_matches])
q = q.text_attribute_matches(:body, params[:body_matches])
q.apply_default_order(params)
end
@@ -77,7 +77,11 @@ class WikiPageVersion < ApplicationRecord
end
end
def self.searchable_includes
[:updater, :wiki_page, :artist, :tag]
end
def self.available_includes
[:updater, :wiki_page, :artist]
[:updater, :wiki_page, :artist, :tag]
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 :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 :linked_wiki_exists, label: "Wiki Exists?", collection: ["Yes", "No"], include_blank: true, selected: params[:search][:linked_wiki_exists] %>
<%= f.input :linked_tag_exists, label: "Tag Exists?", collection: ["Yes", "No"], include_blank: true, selected: params[:search][:linked_tag_exists] %>
<%= f.input :has_linked_wiki, label: "Wiki Exists?", collection: ["Yes", "No"], include_blank: true, selected: params[:search][:has_linked_wiki] %>
<%= f.input :has_linked_tag, label: "Tag Exists?", collection: ["Yes", "No"], include_blank: true, selected: params[:search][:has_linked_tag] %>
<%= f.submit "Search" %>
<% end %>

View File

@@ -3,11 +3,15 @@
<h1>Search Forum</h1>
<%= 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 :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 :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" %>
<% end %>
</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 :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 :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.submit "Search" %>
<% end %>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -3,24 +3,48 @@ require 'test_helper'
class ArtistCommentaryVersionsControllerTest < ActionDispatch::IntegrationTest
context "The artist commentary versions controller" do
setup do
@user = create(:user)
@commentary1 = as(@user) { create(:artist_commentary) }
@commentary2 = as(@user) { create(:artist_commentary) }
@user = create(:member_user, id: 1000, created_at: 2.weeks.ago)
@builder = create(:builder_user, created_at: 2.weeks.ago)
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
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
get artist_commentary_versions_path
assert_response :success
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
context "show action" do
should "work" do
get artist_commentary_version_path(@commentary1.versions.first)
assert_redirected_to artist_commentary_versions_path(search: { post_id: @commentary1.post_id })
get artist_commentary_version_path(@commentary.versions.first)
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
end
end

View File

@@ -2,28 +2,24 @@ require 'test_helper'
class ArtistUrlsControllerTest < ActionDispatch::IntegrationTest
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
should "render" do
get artist_urls_path
assert_response :success
end
should "render for a json request" do
get artist_urls_path, as: :json
assert_response :success
end
should respond_to_search({}).with { @banned.urls + @artist.urls }
should respond_to_search(url_matches: "*bkub*").with { @artist.urls }
should respond_to_search(is_active: "false").with { @artist.urls }
should "render for a complex search" do
@artist = FactoryBot.create(:artist, name: "bkub", url_string: "-http://bkub.com")
get artist_urls_path(search: {
artist: { name: "bkub" },
url_matches: "*bkub*",
is_active: "false",
order: "created_at"
})
assert_response :success
context "using includes" do
should respond_to_search(artist: {name: "bkub"}).with { @artist.urls }
should respond_to_search(artist: {is_banned: "true"}).with { @banned.urls }
end
end
end

View File

@@ -3,18 +3,37 @@ require 'test_helper'
class ArtistVersionsControllerTest < ActionDispatch::IntegrationTest
context "An artist versions controller" do
setup do
@user = create(:gold_user)
@artist = as(@user) { create(:artist) }
@user = create(:gold_user, id: 100)
@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
should "get the index page" do
get_auth artist_versions_path, @user
assert_response :success
end
context "index action" do
setup do
@versions = @artist.versions
end
should "get the index page when searching for something" do
get_auth artist_versions_path(search: {name: @artist.name}), @user
assert_response :success
should "render" do
get artist_versions_path
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
context "show action" do

View File

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

View File

@@ -3,8 +3,11 @@ require 'test_helper'
class BansControllerTest < ActionDispatch::IntegrationTest
context "A bans controller" do
setup do
@mod = create(:moderator_user)
@ban = create(:ban)
@mod = create(:moderator_user, name: "danbo")
@admin = create(:admin_user)
@user = create(:member_user, id: 999, name: "cirno")
as(@mod) { @ban = create(:ban, reason: "blah", user: @user, banner: @mod) }
end
context "new action" do
@@ -29,16 +32,25 @@ class BansControllerTest < ActionDispatch::IntegrationTest
end
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
get_auth bans_path, @mod
get bans_path
assert_response :success
end
end
context "search action" do
should "render" do
get_auth bans_path(search: { user_name: @ban.user.name }), @mod
assert_response :success
should respond_to_search({}).with { [@admin_ban, @ban] }
should respond_to_search(reason_matches: "blah").with { @ban }
should respond_to_search(expired: "true").with { @admin_ban }
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
@@ -55,7 +67,6 @@ class BansControllerTest < ActionDispatch::IntegrationTest
should "not allow mods to ban admins" 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 }}
assert_response 403

View File

@@ -3,10 +3,11 @@ require 'test_helper'
class BulkUpdateRequestsControllerTest < ActionDispatch::IntegrationTest
context "BulkUpdateRequestsController" do
setup do
@user = create(:user)
@user = create(:user, id: 999)
@builder = create(:builder_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
context "#new" do
@@ -73,10 +74,31 @@ class BulkUpdateRequestsControllerTest < ActionDispatch::IntegrationTest
end
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
get bulk_update_requests_path
assert_response :success
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
context "#show" do

View File

@@ -3,9 +3,9 @@ require 'test_helper'
class CommentVotesControllerTest < ActionDispatch::IntegrationTest
context "A comment votes controller" do
setup do
CurrentUser.user = @user = create(:user)
CurrentUser.user = @user = create(:user, name: "cirno")
CurrentUser.ip_addr = "127.0.0.1"
@comment = create(:comment)
@comment = create(:comment, creator: @user)
end
teardown do
@@ -13,12 +13,36 @@ class CommentVotesControllerTest < ActionDispatch::IntegrationTest
CurrentUser.ip_addr = nil
end
context "#index" do
should "work" do
create(:comment_vote, user: @user)
get_auth comment_votes_path, @user
context "index action" do
setup do
@voter = create(:gold_user, name: "rumia")
@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

View File

@@ -4,8 +4,8 @@ class CommentsControllerTest < ActionDispatch::IntegrationTest
context "A comments controller" do
setup do
@mod = FactoryBot.create(:moderator_user)
@user = FactoryBot.create(:member_user)
@post = create(:post)
@user = FactoryBot.create(:member_user, name: "cirno")
@post = create(:post, id: 100)
CurrentUser.user = @user
CurrentUser.ip_addr = "127.0.0.1"
@@ -87,10 +87,31 @@ class CommentsControllerTest < ActionDispatch::IntegrationTest
end
end
should "render by comment" do
@comment = as(@user) { create(:comment, post: @post) }
get comments_path(group_by: "comment")
assert_response :success
context "grouped by comment" do
setup do
@user_comment = create(:comment, post: @post, score: 10, do_not_bump_post: true, creator: @user)
@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
context "for atom feeds" do

View File

@@ -3,9 +3,9 @@ require 'test_helper'
class DmailsControllerTest < ActionDispatch::IntegrationTest
context "The dmails controller" do
setup do
@user = create(:user, unread_dmail_count: 1)
@unrelated_user = create(:user)
@dmail = create(:dmail, owner: @user)
@user = create(:user, id: 999, unread_dmail_count: 1)
@unrelated_user = create(:moderator_user, id: 1000, name: "reimu")
@dmail = create(:dmail, owner: @user, from: @user)
end
teardown do
@@ -40,28 +40,41 @@ class DmailsControllerTest < ActionDispatch::IntegrationTest
end
context "index action" do
should "show dmails owned by the current user by sent" do
get_auth dmails_path, @user, params: {:search => {:owner_id => @dmail.owner_id, :folder => "sent"}}
setup do
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
end
should "show dmails owned by the current user by received" do
get_auth dmails_path, @user, params: {:search => {:owner_id => @dmail.owner_id, :folder => "received"}}
assert_response :success
should respond_to_search({}).with { [@deleted_dmail, @received_dmail, @dmail] }
should respond_to_search(folder: "sent").with { @dmail }
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
should "not show dmails not owned by the current user" do
get_auth dmails_path, @user, params: {:search => {:owner_id => @dmail.owner_id}}
assert_response :success
end
context "as a banned user" do
setup do
as(create(:admin_user)) do
create(:ban, user: @user)
end
should "work for banned users" do
as(create(:admin_user)) do
create(:ban, :user => @user)
should respond_to_search({}).with { [@received_dmail, @dmail] }
end
get_auth dmails_path, @dmail.owner, params: {:search => {:owner_id => @dmail.owner_id, :folder => "sent"}}
assert_response :success
end
end

View File

@@ -1,12 +1,30 @@
require "test_helper"
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
should "work" do
@user = create(:user)
@wiki = as(@user) { create(:wiki_page, body: "[[test]]") }
should "render" do
get dtext_links_path
assert_response :success
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

View File

@@ -8,10 +8,31 @@ class FavoriteGroupsControllerTest < ActionDispatch::IntegrationTest
end
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
get favorite_groups_path
assert_response :success
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
context "show action" do

View File

@@ -3,22 +3,35 @@ require 'test_helper'
class ForumPostVotesControllerTest < ActionDispatch::IntegrationTest
context "The forum post votes controller" do
setup do
@user = create(:user)
@user = create(:user, name: "cirno")
@other_user = create(:user)
as(@user) do
@forum_topic = create(:forum_topic)
@forum_post = create(:forum_post, topic: @forum_topic)
@forum_post = create(:forum_post, body: "blah", creator: @user)
@bulk_update_request = create(:bulk_update_request, forum_post: @forum_post)
end
end
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
@forum_post_vote = create(:forum_post_vote, creator: @user, forum_post: @forum_post)
get forum_post_votes_path
assert_response :success
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
context "create action" do

View File

@@ -3,9 +3,9 @@ require 'test_helper'
class ForumPostsControllerTest < ActionDispatch::IntegrationTest
context "The forum posts controller" do
setup do
@user = create(:user)
@user = create(:user, id: 999)
@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_post = as(@user) { create(:forum_post, creator: @user, topic: @forum_topic, body: "alias xxx -> yyy") }
end
@@ -52,65 +52,61 @@ class ForumPostsControllerTest < ActionDispatch::IntegrationTest
end
context "index action" do
should "list all forum posts" do
get forum_posts_path
assert_response :success
setup do
@admin = create(:admin_user)
@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
context "with search conditions" 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
context "as a user" do
setup do
@admin = create(:admin_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)) }
CurrentUser.user = @user
end
should "list only permitted posts for anons" do
get forum_posts_path
should "render" do
get_auth comment_votes_path, @user
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
should "list only permitted posts for mods" do
get_auth forum_posts_path, @mod
should respond_to_search({}).with { [@unrelated_forum, @other_forum, @forum_post] }
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
assert_select "#forum-post-#{@forum_post.id}"
assert_select "#forum-post-#{@mod_post.id}"
assert_select "#forum-post-#{@admin_post.id}", false
context "using includes" do
should respond_to_search(topic: {title_matches: "my forum topic"}).with { @forum_post }
should respond_to_search(topic: {category_id: 1}).with { @other_forum }
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
should "list only permitted posts for admins" do
get_auth forum_posts_path, @admin
should respond_to_search({}).with { [@unrelated_forum, @mod_forum, @other_forum, @forum_post] }
assert_response :success
assert_select "#forum-post-#{@forum_post.id}"
assert_select "#forum-post-#{@mod_post.id}"
assert_select "#forum-post-#{@admin_post.id}"
context "using includes" do
should respond_to_search(creator: {name: "okuu"}).with { @mod_forum }
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
context "show action" do

View File

@@ -1,15 +1,18 @@
require 'test_helper'
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
setup do
@user = create(:user)
@other_user = create(:user)
@mod = create(:moderator_user)
@mod = create(:moderator_user, name: "okuu")
as(@user) do
@forum_topic = create(:forum_topic, creator: @user, title: "my forum topic")
@forum_post = create(:forum_post, creator: @user, topic: @forum_topic, body: "xxx")
@forum_topic = create(:forum_topic, creator: @user, title: "my forum topic", original_post: build(:forum_post, creator: @user, topic: @forum_topic, body: "xxx"))
end
end
@@ -88,25 +91,26 @@ class ForumTopicsControllerTest < ActionDispatch::IntegrationTest
context "index action" do
setup do
as(@user) do
@topic1 = create(:forum_topic, is_sticky: true, creator: @user)
@topic2 = create(:forum_topic, creator: @user)
@post1 = create(:forum_post, topic: @topic1, creator: @user, body: "xxx")
@post2 = create(:forum_post, topic: @topic2, creator: @user, body: "xxx")
@sticky_topic = create(:forum_topic, is_sticky: true, creator: @user, original_post: build(:forum_post))
@other_topic = create(:forum_topic, creator: @user, original_post: build(:forum_post))
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
should "list public forum topics for members" do
get forum_topics_path
assert_response :success
assert_select "a.forum-post-link", count: 1, text: @topic1.title
assert_select "a.forum-post-link", count: 1, text: @topic2.title
assert_select "a.forum-post-link", count: 1, text: @sticky_topic.title
assert_select "a.forum-post-link", count: 1, text: @other_topic.title
end
should "not list stickied topics first for JSON responses" do
get forum_topics_path, params: {format: :json}
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
should "render for atom feed" do
@@ -117,44 +121,59 @@ class ForumTopicsControllerTest < ActionDispatch::IntegrationTest
should "render for a sitemap" do
get forum_topics_path(format: :sitemap)
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
context "with private topics" 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
assert_response :success
assert_select "a.forum-post-link", count: 1, text: @topic1.title
assert_select "a.forum-post-link", count: 0, text: @topic2.title
assert_select "a.forum-post-link", count: 1, text: @sticky_topic.title
assert_select "a.forum-post-link", count: 0, text: @other_topic.title
end
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
assert_response :success
assert_select "a.forum-post-link", count: 1, text: @topic1.title
assert_select "a.forum-post-link", count: 1, text: @topic2.title
assert_select "a.forum-post-link", count: 1, text: @sticky_topic.title
assert_select "a.forum-post-link", count: 1, text: @other_topic.title
end
end
context "with search conditions" do
should "list all matching forum topics" do
get forum_topics_path, params: {:search => {:title_matches => "forum"}}
assert_response :success
assert_select "a.forum-post-link", @forum_topic.title
assert_select "a.forum-post-link", count: 0, text: @topic1.title
assert_select "a.forum-post-link", count: 0, text: @topic2.title
context "as a user" do
setup do
CurrentUser.user = @user
end
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
should "list nothing for when the search matches nothing" do
get forum_topics_path, params: {:search => {:title_matches => "bababa"}}
assert_response :success
assert_select "a.forum-post-link", count: 0, text: @forum_topic.title
assert_select "a.forum-post-link", count: 0, text: @topic1.title
assert_select "a.forum-post-link", count: 0, text: @topic2.title
context "as a moderator" do
setup do
CurrentUser.user = @mod
end
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

View File

@@ -3,8 +3,8 @@ require 'test_helper'
class IpBansControllerTest < ActionDispatch::IntegrationTest
context "The ip bans controller" do
setup do
@admin = create(:admin_user)
@ip_ban = create(:ip_ban)
@admin = create(:admin_user, name: "yukari")
@ip_ban = create(:ip_ban, ip_addr: "6.7.8.9")
end
context "new action" do
@@ -24,21 +24,34 @@ class IpBansControllerTest < ActionDispatch::IntegrationTest
should "log a mod action" do
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
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
get_auth ip_bans_path, @admin
assert_response :success
end
context "with search parameters" do
should "render" do
get_auth ip_bans_path, @admin, params: {:search => {:ip_addr => "1.2.3.4"}}
assert_response :success
end
should respond_to_search({}).with { [@other_ban, @subnet_ban, @ip_ban] }
should respond_to_search(ip_addr: "6.7.8.9").with { @ip_ban }
should respond_to_search(reason_matches: "malware").with { @other_ban }
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

View File

@@ -2,6 +2,10 @@ require 'test_helper'
class ModActionsControllerTest < ActionDispatch::IntegrationTest
context "The mod actions controller" do
setup do
@mod_action = create(:mod_action, description: "blah", category: "post_delete")
end
context "index action" do
setup do
@ban = create(:mod_action, category: :post_ban)
@@ -13,6 +17,15 @@ class ModActionsControllerTest < ActionDispatch::IntegrationTest
assert_response :success
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
should respond_to_search(category: "post_ban").with { [@ban] }
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,45").with { [@unban, @ban] }
should respond_to_search(category_id: ">=44").with { [@unban, @ban] }
end
end

View File

@@ -9,9 +9,8 @@ class ModerationReportsControllerTest < ActionDispatch::IntegrationTest
as(@spammer) do
@dmail = create(:dmail, from: @spammer, owner: @user, to: @user)
@comment = create(:comment, creator: @spammer)
@forum_topic = create(:forum_topic, creator: @spammer)
@forum_post = create(:forum_post, topic: @forum_topic, creator: @spammer)
@comment = create(:comment, id: 1234, creator: @spammer)
@forum_post = create(:forum_post, topic: build(:forum_topic), body: "xxx", creator: @spammer)
end
end
@@ -29,24 +28,37 @@ class ModerationReportsControllerTest < ActionDispatch::IntegrationTest
context "index action" 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
should "render the access denied page for members" do
get_auth moderation_reports_path, @user
assert_response 403
context "as a user" do
should "render the access denied page" do
get_auth moderation_reports_path, @user
assert_response 403
end
end
should "render for mods" do
get_auth moderation_reports_path, @mod
assert_response :success
end
context "as a moderator" do
setup do
CurrentUser.user = @mod
end
context "with search parameters" 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
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

View File

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

View File

@@ -8,24 +8,25 @@ class NotesControllerTest < ActionDispatch::IntegrationTest
end
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
assert_response :success
end
should "list all notes (with search)" do
params = {
group_by: "note",
search: {
body_matches: "000",
is_active: true,
post_id: @note.post_id,
post_tags_match: @note.post.tag_array.first
}
}
should respond_to_search({}).with { [@deleted_note, @post_note, @note] }
should respond_to_search(body_matches: "000").with { @note }
should respond_to_search(is_active: "true").with { [@post_note, @note] }
get notes_path, params: params
assert_response :success
context "using includes" do
should respond_to_search(post_id: 2001).with { @post_note }
should respond_to_search(post_tags_match: "touhou").with { @post_note }
end
end

View File

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

View File

@@ -3,7 +3,7 @@ require 'test_helper'
class PostApprovalsControllerTest < ActionDispatch::IntegrationTest
context "The post approvals controller" do
setup do
@approver = create(:approver)
@approver = create(:approver, name: "eiki")
end
context "create action" do
@@ -60,11 +60,25 @@ class PostApprovalsControllerTest < ActionDispatch::IntegrationTest
end
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
@approval = create(:post_approval)
get post_approvals_path
assert_response :success
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

View File

@@ -3,9 +3,8 @@ require 'test_helper'
class PostDisapprovalsControllerTest < ActionDispatch::IntegrationTest
context "The post disapprovals controller" do
setup do
@approver = create(:approver)
@post = create(:post, is_pending: true)
@post_disapproval = create(:post_disapproval, post: @post)
@approver = create(:approver, name: "eiki")
@post = create(:post, tag_string: "touhou", is_pending: true, uploader: build(:user, name: "marisa", created_at: 2.weeks.ago))
end
context "create action" do
@@ -40,16 +39,36 @@ class PostDisapprovalsControllerTest < ActionDispatch::IntegrationTest
end
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
get_auth post_disapprovals_path, create(:mod_user)
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
should "not allow non-mods to see disapprover names" do
get post_disapprovals_path
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

View File

@@ -4,10 +4,10 @@ class PostFlagsControllerTest < ActionDispatch::IntegrationTest
context "The post flags controller" do
setup do
@user = create(:user)
@flagger = create(:gold_user, created_at: 2.weeks.ago)
@uploader = create(:mod_user, created_at: 2.weeks.ago)
@flagger = create(:gold_user, id: 999, created_at: 2.weeks.ago)
@uploader = create(:mod_user, name: "chen", created_at: 2.weeks.ago)
@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)
end
@@ -26,6 +26,11 @@ class PostFlagsControllerTest < ActionDispatch::IntegrationTest
end
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
get post_flags_path
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
end
context "with search parameters" do
should "render" do
get_auth post_flags_path(search: { post_id: @post_flag.post_id }), @user
assert_response :success
context "as a normal user" do
setup do
CurrentUser.user = @user
end
should "hide flagged posts when the searcher is the uploader" do
get_auth post_flags_path(search: { creator_id: @flagger.id }), @uploader
assert_response :success
assert_select "tr#post-flag-#{@post_flag.id}", false
should respond_to_search({}).with { [@unrelated_flag, @other_flag, @post_flag] }
should respond_to_search(reason_matches: "poor quality").with { @unrelated_flag }
should respond_to_search(category: "normal").with { [@unrelated_flag, @other_flag, @post_flag] }
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
should "show flagged posts when the searcher is not the uploader" do
get_auth post_flags_path(search: { creator_id: @flagger.id }), @mod
assert_response :success
assert_select "tr#post-flag-#{@post_flag.id}", true
should respond_to_search(creator_id: 999).with { [] }
end
context "when the user is a mod and not the uploader" do
setup do
CurrentUser.user = @mod
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

View File

@@ -3,9 +3,9 @@ require 'test_helper'
class PostReplacementsControllerTest < ActionDispatch::IntegrationTest
context "The post replacements controller" 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
@post = create(:post, source: "https://google.com")
@post = create(:post, source: "https://google.com", tag_string: "touhou")
@post_replacement = create(:post_replacement, post: @post)
end
end
@@ -60,10 +60,23 @@ class PostReplacementsControllerTest < ActionDispatch::IntegrationTest
end
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
get post_replacements_path, params: {format: "json"}
get post_replacements_path
assert_response :success
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

View File

@@ -3,17 +3,45 @@ require 'test_helper'
class PostVotesControllerTest < ActionDispatch::IntegrationTest
context "The post vote controller" do
setup do
@user = create(:gold_user)
@post = create(:post)
@user = create(:gold_user, name: "meiling")
@post = create(:post, tag_string: "dragon")
end
context "index action" do
should "work" do
as(@user) { create(:post_vote, post_id: @post.id, user_id: @user.id) }
get_auth post_votes_path, @user
setup do
@admin = create(:admin_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
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
context "create action" do

View File

@@ -7,14 +7,42 @@ class TagAliasesControllerTest < ActionDispatch::IntegrationTest
end
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
assert_response :success
end
should "list all tag_alias (with search)" do
get tag_aliases_path, params: {:search => {:antecedent_name => "aaa"}}
assert_response :success
should respond_to_search({}).with { [@unrelated_alias, @other_alias, @tag_alias] }
should respond_to_search(antecedent_name: "aaa").with { @tag_alias }
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

View File

@@ -7,6 +7,44 @@ class TagImplicationsControllerTest < ActionDispatch::IntegrationTest
end
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
get tag_implications_path
assert_response :success

View File

@@ -36,27 +36,43 @@ class TagsControllerTest < ActionDispatch::IntegrationTest
context "searching" do
setup do
as(@user) do
@miku = create(:tag, name: "hatsune_miku", category: Tag.categories.character)
@wokada = create(:tag, name: "wokada", category: Tag.categories.artist)
@vocaloid = create(:tag, name: "vocaloid", category: Tag.categories.copyright)
@miku = create(:character_tag, name: "hatsune_miku")
@wokada = create(:artist_tag, name: "wokada")
@vocaloid = create(:copyright_tag, name: "vocaloid")
@weapon = create(:tag, name: "weapon")
@empty = create(:tag, name: "empty", post_count: 0)
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")
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_normalize: "HATSUNE_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(name: "empty", hide_empty: "true").with { [] }
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 }
should respond_to_search(name: "hatsune_miku", has_wiki: "true").with { @miku }
should respond_to_search(name: "vocaloid", has_wiki: "false").with { @vocaloid }
context "using includes" do
should respond_to_search(name: "wokada", has_artist: "true").with { @wokada }
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

View File

@@ -30,7 +30,7 @@ class UploadsControllerTest < ActionDispatch::IntegrationTest
context "The uploads controller" do
setup do
@user = create(:contributor_user)
@user = create(:contributor_user, name: "marisa")
mock_iqdb_service!
end
@@ -173,8 +173,11 @@ class UploadsControllerTest < ActionDispatch::IntegrationTest
context "index action" do
setup do
as(@user) do
@upload = create(:source_upload, tag_string: "foo bar")
@upload2 = create(:source_upload, tag_string: "tagme", rating: "e")
@upload = create(:upload, tag_string: "foo bar", source: "http://example.com/foobar")
@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
@@ -183,23 +186,28 @@ class UploadsControllerTest < ActionDispatch::IntegrationTest
assert_response :success
end
context "with search parameters" do
should "render" do
search_params = {
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"])
context "as an uploader" do
setup do
CurrentUser.user = @user
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

View File

@@ -3,9 +3,9 @@ require 'test_helper'
class UserFeedbacksControllerTest < ActionDispatch::IntegrationTest
context "The user feedbacks controller" do
setup do
@user = create(:user)
@critic = create(:gold_user)
@mod = create(:moderator_user)
@user = create(:user, name: "cirno")
@critic = create(:gold_user, name: "eiki")
@mod = create(:moderator_user, id: 1000)
@user_feedback = create(:user_feedback, user: @user, creator: @critic)
end
@@ -37,23 +37,43 @@ class UserFeedbacksControllerTest < ActionDispatch::IntegrationTest
end
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
get_auth user_feedbacks_path, @user
assert_response :success
end
should "not allow members to see deleted feedbacks" do
as(@user) { @user_feedback.update!(is_deleted: true) }
get_auth user_feedbacks_path, @user
context "as a user" do
setup do
CurrentUser.user = @user
end
assert_response :success
assert_select "tr#user-feedback-#{@user_feedback.id}", false
should respond_to_search({}).with { [@other_feedback, @user_feedback] }
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
context "with search parameters" do
should "render" do
get_auth user_feedbacks_path, @critic, params: {:search => {:user_id => @user.id}}
assert_response :success
context "as a moderator" do
setup do
CurrentUser.user = @mod
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

View File

@@ -7,7 +7,14 @@ class UsersControllerTest < ActionDispatch::IntegrationTest
end
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
assert_response :success
end
@@ -28,14 +35,54 @@ class UsersControllerTest < ActionDispatch::IntegrationTest
assert_response 404
end
should "list all users (with search)" do
get users_path, params: {:search => {:name_matches => @user.name}}
assert_response :success
end
should respond_to_search({}).with { [@uploader, @other_user, @mod_user, @user, @first_user] }
should respond_to_search(min_level: User::Levels::BUILDER).with { [@other_user, @mod_user, @first_user] }
should respond_to_search(can_upload_free: "true").with { @other_user }
should respond_to_search(name_matches: "yukari").with { @mod_user }
should "list all users (with blank search parameters)" do
get users_path, params: { search: { inviter: { name_matches: "" }, level: "", name: "test" } }
assert_redirected_to users_path(search: { name: "test" })
context "using includes" do
setup do
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

View File

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

View File

@@ -14,11 +14,13 @@ class WikiPagesControllerTest < ActionDispatch::IntegrationTest
@deleted = create(:wiki_page, title: "deleted", is_deleted: true)
@vocaloid = create(:wiki_page, title: "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
should "list all wiki_pages" do
should "render" do
get wiki_pages_path
assert_response :success
end
@@ -34,19 +36,27 @@ class WikiPagesControllerTest < ActionDispatch::IntegrationTest
assert_redirected_to wiki_pages_path(search: { title_normalize: "tagme" }, redirect: true)
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", order: "post_count").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 { [@miku, @vocaloid, @tagme] }
should respond_to_search(hide_deleted: "true").with { [@picasso, @miku, @vocaloid, @tagme] }
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_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
context "search action" do

View File

@@ -9,28 +9,37 @@ module ControllerHelper
# setup { @touhou = create(:tag, name: "touhou") }
# should respond_to_search(name: "touhou").with { @touhou }
#
def respond_to_search(search_params)
RespondToSearchMatcher.new(search_params)
def respond_to_search(search_params, other_params: {})
RespondToSearchMatcher.new(search_params, other_params)
end
class RespondToSearchMatcher < Struct.new(:params)
class RespondToSearchMatcher < Struct.new(:params, :other_params)
def description
"should respond to a search for #{params}"
end
def matches?(subject, &block)
search_params = { search: params }
search_params = other_params.merge({ search: params })
expected_items = @test_case.instance_eval(&@expected)
@test_case.instance_eval do
# calls e.g. "wiki_pages_path" if we're in WikiPagesControllerTest.
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)
responded_ids = response.parsed_body.map { |item| item["id"] }
assert_response :success
assert_equal(expected_ids, responded_ids)
end
end

View File

@@ -83,7 +83,7 @@ class BulkUpdateRequestTest < ActiveSupport::TestCase
context "that has an invalid alias" 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")
end