diff --git a/Gemfile b/Gemfile index d45208d8..70b00499 100644 --- a/Gemfile +++ b/Gemfile @@ -36,6 +36,9 @@ gem "hcaptcha", "~> 6.0", git: "https://github.com/Retrospring/hcaptcha.git", re gem "rolify", "~> 5.2" +gem "dry-initializer", "~> 3.0" +gem "dry-types", "~> 1.4" + gem 'ruby-progressbar' gem 'rails_admin' diff --git a/Gemfile.lock b/Gemfile.lock index 623bad26..3224a5db 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -142,6 +142,25 @@ GEM docile (1.4.0) domain_name (0.5.20190701) unf (>= 0.0.5, < 1.0.0) + dry-configurable (0.12.1) + concurrent-ruby (~> 1.0) + dry-core (~> 0.5, >= 0.5.0) + dry-container (0.8.0) + concurrent-ruby (~> 1.0) + dry-configurable (~> 0.1, >= 0.1.3) + dry-core (0.7.1) + concurrent-ruby (~> 1.0) + dry-inflector (0.2.1) + dry-initializer (3.0.4) + dry-logic (1.2.0) + concurrent-ruby (~> 1.0) + dry-core (~> 0.5, >= 0.5) + dry-types (1.5.1) + concurrent-ruby (~> 1.0) + dry-container (~> 0.3) + dry-core (~> 0.5, >= 0.5) + dry-inflector (~> 0.1, >= 0.1.2) + dry-logic (~> 1.0, >= 1.0.2) equalizer (0.0.11) erubi (1.10.0) excon (0.89.0) @@ -589,6 +608,8 @@ DEPENDENCIES devise (~> 4.0) devise-async devise-i18n + dry-initializer (~> 3.0) + dry-types (~> 1.4) factory_bot_rails fake_email_validator faker diff --git a/Rakefile b/Rakefile index 9c689bd9..b386660c 100644 --- a/Rakefile +++ b/Rakefile @@ -154,9 +154,11 @@ namespace :justask do fail "screen name required" if args[:screen_name].nil? user = User.find_by_screen_name(args[:screen_name]) fail "user #{args[:screen_name]} not found" if user.nil? - user.permanently_banned = true - user.ban_reason = args[:reason] - user.save! + UseCase::User::Ban.call( + target_user_id: user.id, + expiry: nil, + reason: args[:reason], + ) puts "#{user.screen_name} got hit by\033[5m YE OLDE BANHAMMER\033[0m!!1!" end @@ -164,10 +166,11 @@ namespace :justask do task :ban, [:screen_name, :reason] => :environment do |t, args| fail "screen name required" if args[:screen_name].nil? user = User.find_by_screen_name(args[:screen_name]) - user.permanently_banned = false - user.banned_until = DateTime.current + 1 - user.ban_reason = args[:reason] - user.save! + UseCase::User::Ban.call( + target_user_id: user.id, + expiry: DateTime.current + 1, + reason: args[:reason], + ) puts "#{user.screen_name} got hit by\033[5m YE OLDE BANHAMMER\033[0m!!1!" end @@ -175,10 +178,11 @@ namespace :justask do task :week_ban, [:screen_name, :reason] => :environment do |t, args| fail "screen name required" if args[:screen_name].nil? user = User.find_by_screen_name(args[:screen_name]) - user.permanently_banned = false - user.banned_until = DateTime.current + 7 - user.ban_reason = args[:reason] - user.save! + UseCase::User::Ban.call( + target_user_id: user.id, + expiry: DateTime.current + 7, + reason: args[:reason], + ) puts "#{user.screen_name} got hit by\033[5m YE OLDE BANHAMMER\033[0m!!1!" end @@ -186,10 +190,11 @@ namespace :justask do task :month_ban, [:screen_name, :reason] => :environment do |t, args| fail "screen name required" if args[:screen_name].nil? user = User.find_by_screen_name(args[:screen_name]) - user.permanently_banned = false - user.banned_until = DateTime.current + 30 - user.ban_reason = args[:reason] - user.save! + UseCase::User::Ban.call( + target_user_id: user.id, + expiry: DateTime.current + 30, + reason: args[:reason], + ) puts "#{user.screen_name} got hit by\033[5m YE OLDE BANHAMMER\033[0m!!1!" end @@ -197,10 +202,11 @@ namespace :justask do task :year_ban, [:screen_name, :reason] => :environment do |t, args| fail "screen name required" if args[:screen_name].nil? user = User.find_by_screen_name(args[:screen_name]) - user.permanently_banned = false - user.banned_until = DateTime.current + 365 - user.ban_reason = args[:reason] - user.save! + UseCase::User::Ban.call( + target_user_id: user.id, + expiry: DateTime.current + 365, + reason: args[:reason], + ) puts "#{user.screen_name} got hit by\033[5m YE OLDE BANHAMMER\033[0m!!1!" end @@ -208,10 +214,11 @@ namespace :justask do task :aeon_ban, [:screen_name, :reason] => :environment do |t, args| fail "screen name required" if args[:screen_name].nil? user = User.find_by_screen_name(args[:screen_name]) - user.permanently_banned = false - user.banned_until = DateTime.current + 365_000_000_000 - user.ban_reason = args[:reason] - user.save! + UseCase::User::Ban.call( + target_user_id: user.id, + expiry: DateTime.current + 365_000_000_000, + reason: args[:reason], + ) puts "#{user.screen_name} got hit by\033[5m YE OLDE BANHAMMER\033[0m!!1!" end @@ -220,10 +227,7 @@ namespace :justask do fail "screen name required" if args[:screen_name].nil? user = User.find_by_screen_name(args[:screen_name]) fail "user #{args[:screen_name]} not found" if user.nil? - user.permanently_banned = false - user.banned_until = nil - user.ban_reason = nil - user.save! + UseCase::User::Unban.call(user.id) puts "#{user.screen_name} is no longer banned." end diff --git a/app/controllers/ajax/moderation_controller.rb b/app/controllers/ajax/moderation_controller.rb index 8bd822e6..85f23296 100644 --- a/app/controllers/ajax/moderation_controller.rb +++ b/app/controllers/ajax/moderation_controller.rb @@ -1,3 +1,7 @@ +require 'use_case/user/ban' +require 'use_case/user/unban' +require 'errors' + class Ajax::ModerationController < AjaxController def vote params.require :id @@ -108,36 +112,49 @@ class Ajax::ModerationController < AjaxController params.require :user params.require :ban - params.require :permaban - reason = params[:reason] - target = User.find_by_screen_name!(params[:user]) - unban = params[:ban] == "0" - perma = params[:permaban] == "1" + duration = params[:duration].to_i + duration_unit = params[:duration_unit].to_s + reason = params[:reason].to_s + target_user = User.find_by_screen_name!(params[:user]) + unban = params[:ban] == '0' + perma = params[:duration].blank? - buntil = DateTime.strptime params[:until], "%m/%d/%Y %I:%M %p" unless unban || perma - - if !unban && target.has_role?(:administrator) + if !unban && target_user.has_role?(:administrator) @response[:status] = :nopriv @response[:message] = I18n.t('messages.moderation.ban.nopriv') return end if unban - target.unban + UseCase::User::Unban.call(target_user.id) @response[:message] = I18n.t('messages.moderation.ban.unban') @response[:success] = true + @response[:status] = :okay + return elsif perma - target.ban nil, reason @response[:message] = I18n.t('messages.moderation.ban.perma') + expiry = nil else - target.ban buntil, reason - @response[:message] = I18n.t('messages.moderation.ban.temp', date: buntil.to_s) + params.require :duration + params.require :duration_unit + + raise Errors::InvalidBanDuration unless %w[hours days weeks months].include? duration_unit + + expiry = DateTime.now + duration.public_send(duration_unit) + @response[:message] = I18n.t('messages.moderation.ban.temp', date: expiry.to_s) end - target.save! + + UseCase::User::Ban.call( + target_user_id: target_user.id, + expiry: expiry, + reason: reason, + source_user_id: current_user.id) + + target_user.save! @response[:status] = :okay - @response[:success] = target.banned? == !unban + @response[:success] = true end def privilege @@ -162,7 +179,7 @@ class Ajax::ModerationController < AjaxController @response[:checked] = status type = params[:type].downcase - target_role = {"admin" => "administrator"}.fetch(type, type).to_sym + target_role = {'admin' => 'administrator'}.fetch(type, type).to_sym if status target_user.add_role target_role diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 94d2ef02..78d9ee01 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -40,10 +40,11 @@ class ApplicationController < ActionController::Base name = current_user.screen_name # obligatory '2001: A Space Odyssey' reference flash[:notice] = t('flash.ban.error', name: name) - unless current_user.ban_reason.nil? - flash[:notice] += "\n#{t('flash.ban.reason', reason: current_user.ban_reason)}" + current_ban = current_user.bans.current.first + unless current_ban&.reason.nil? + flash[:notice] += "\n#{t('flash.ban.reason', reason: current_user.bans.current.first.reason)}" end - if not current_user.permanently_banned? + unless current_ban&.permanently_banned? # TODO format banned_until flash[:notice] += "\n#{t('flash.ban.until', time: current_user.banned_until)}" end diff --git a/app/javascript/legacy/moderation/ban.coffee b/app/javascript/legacy/moderation/ban.coffee index 8530ff23..f62b8b3b 100644 --- a/app/javascript/legacy/moderation/ban.coffee +++ b/app/javascript/legacy/moderation/ban.coffee @@ -6,36 +6,35 @@ load = -> banCheckbox = modalForm.querySelector('[name="ban"][type="checkbox"]') permabanCheckbox = modalForm.querySelector('[name="permaban"][type="checkbox"]') - banCheckbox.addEventListener "change", (event) -> - $t = $ this - if $t.is(":checked") - $("#ban-controls").show() - else - $("#ban-controls").hide() - permabanCheckbox.addEventListener "change", (event) -> - $t = $ this - if $t.is(":checked") - $("#ban-controls-time").hide() - else - $("#ban-controls-time").show() + if banCheckbox + banCheckbox.addEventListener "change", (event) -> + $t = $ this + if $t.is(":checked") + $("#ban-controls").show() + else + $("#ban-controls").hide() + permabanCheckbox.addEventListener "change", (event) -> + $t = $ this + if $t.is(":checked") + $("#ban-controls-time").hide() + else + $("#ban-controls-time").show() modalForm.addEventListener "submit", (event) -> event.preventDefault(); - checktostr = (el) -> - if el.checked - "1" - else - "0" - data = { - ban: checktostr banCheckbox - permaban: checktostr permabanCheckbox - until: modalForm.elements["until"].value.trim() - reason: modalForm.elements["reason"].value.trim() + ban: "0" user: modalForm.elements["user"].value } + if banCheckbox && banCheckbox.checked + data.ban = "1" + data.reason = modalForm.elements["reason"].value.trim() + unless permabanCheckbox.checked + data.duration = modalForm.elements["duration"].value.trim() + data.duration_unit = modalForm.elements["duration_unit"].value.trim() + $.ajax url: '/ajax/mod/ban' type: 'POST' @@ -43,6 +42,7 @@ load = -> success: (data, status, jqxhr) -> showNotification data.message, data.success error: (jqxhr, status, error) -> + console.error 'request failed', data console.log jqxhr, status, error showNotification translate('frontend.error.message'), false complete: (jqxhr, status) -> diff --git a/app/models/user.rb b/app/models/user.rb index f3407967..612c8a8d 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -49,6 +49,11 @@ class User < ApplicationRecord has_one :profile, dependent: :destroy has_one :theme, dependent: :destroy + has_many :bans, class_name: 'UserBan', dependent: :destroy + has_many :banned_users, class_name: 'UserBan', + foreign_key: 'banned_by_id', + dependent: :nullify + SCREEN_NAME_REGEX = /\A[a-zA-Z0-9_]{1,16}\z/ WEBSITE_REGEX = /https?:\/\/([A-Za-z.\-]+)\/?(?:.*)/i @@ -218,21 +223,31 @@ class User < ApplicationRecord end # endregion - # forwards fill def banned? - self.permanently_banned? or ((not self.banned_until.nil?) and self.banned_until >= DateTime.current) + self.bans.current.count > 0 end def unban - self.update(permanently_banned: false, ban_reason: nil, banned_until: nil) + UseCase::User::Unban.call(id) end - def ban(buntil=nil, reason=nil) - if buntil == nil - self.update(permanently_banned: true, ban_reason: reason) + # Bans a user. + # @param duration [Integer?] Ban duration + # @param duration_unit [String, nil] Unit for the duration parameter. Accepted units: hours, days, weeks, months + # @param reason [String] Reason for the ban. This is displayed to the user. + # @param banned_by [User] User who instated the ban + def ban(duration, duration_unit = 'hours', reason = nil, banned_by = nil) + if duration + expiry = duration.public_send(duration_unit) else - self.update(permanently_banned: false, banned_until: buntil, ban_reason: reason) + expiry = nil end + UseCase::User::Ban.call( + target_user_id: id, + expiry: expiry, + reason: reason, + source_user_id: banned_by&.id + ) end def can_export? diff --git a/app/models/user_ban.rb b/app/models/user_ban.rb new file mode 100644 index 00000000..bce39f5c --- /dev/null +++ b/app/models/user_ban.rb @@ -0,0 +1,6 @@ +class UserBan < ApplicationRecord + belongs_to :user + belongs_to :banned_by, class_name: 'User' + + scope :current, -> { where('expires_at IS NULL or expires_at > NOW()') } +end diff --git a/app/views/modal/_ban.haml b/app/views/modal/_ban.haml index 9854cae1..64fa4bb6 100644 --- a/app/views/modal/_ban.haml +++ b/app/views/modal/_ban.haml @@ -1,6 +1,7 @@ +- current_ban = user.bans.current.first .modal.fade#modal-ban{ aria: { hidden: true, labelledby: 'modal-ban-label' }, role: :dialog, tabindex: -1 } .modal-dialog - .modal-content + .modal-content#ban-control-super .modal-header %h5.modal-title#modal-ban-label = t 'views.modal.bancontrol.title' @@ -9,13 +10,38 @@ %span.sr-only Close = bootstrap_form_tag(url: '/mod/ban', html: { method: :post, novalidate: :novalidate }) do |f| = f.hidden_field :user, value: user.screen_name - .modal-body#ban-control-super - = f.check_box :ban, label: t('views.modal.bancontrol.ban'), checked: user.banned? - #ban-controls{ style: user.banned? ? '' : 'display: none' } - = f.check_box :permaban, label: t('views.modal.bancontrol.permanent'), checked: user.permanently_banned? - #ban-controls-time{ style: user.permanently_banned? ? 'display: none' : '' } - = f.text_field :until, label: '', required: true, value: (user.banned_until || DateTime.current).strftime('%m/%d/%Y %I:%M %p') - = f.text_field :reason, placeholder: t('views.modal.bancontrol.reason'), value: user.ban_reason - .modal-footer - %button.btn.btn-default{ name: 'stop-time', type: :button, data: { dismiss: :modal } }= t 'views.actions.close' - = f.submit t('views.modal.bancontrol.hammertime'), class: 'btn btn-primary', name: 'hammer-time' + - if current_ban.nil? + .modal-body + = f.check_box :ban, label: t('views.modal.bancontrol.ban'), checked: user.banned? + #ban-controls{ style: user.banned? ? '' : 'display: none' } + = f.check_box :permaban, label: t('views.modal.bancontrol.permanent'), checked: user.permanently_banned? + #ban-controls-time{ style: user.permanently_banned? ? 'display: none' : '' } + = f.text_field :duration, label: '', required: true + .form-check.form-check-inline + = f.radio_button :duration_unit, 'hours', label: 'Hours', checked: true + = f.radio_button :duration_unit, 'days', label: 'Days' + = f.radio_button :duration_unit, 'weeks', label: 'Weeks' + = f.radio_button :duration_unit, 'months', label: 'Months' + = f.text_field :reason, placeholder: t('views.modal.bancontrol.reason'), value: user.bans.current.first&.reason + .modal-footer + %button.btn.btn-default{ name: 'stop-time', type: :button, data: { dismiss: :modal } }= t 'views.actions.close' + = f.submit t('views.modal.bancontrol.hammertime'), class: 'btn btn-primary', name: 'hammer-time' + - else + = f.hidden_field :ban, value: '0' + .modal-body + - if current_ban.expires_at.nil? + This user is currently permanently banned for + %strong= current_ban.reason + - else + This user is currently banned until + %strong= current_ban.expires_at + for + %strong= current_ban.reason + - if current_ban.banned_by.present? + %br + This ban was instated by + %strong= current_ban.banned_by.profile.safe_name + on + %strong= current_ban.created_at + .modal-footer + = f.submit 'Unban', class: 'btn btn-primary', name: 'hammer-time' diff --git a/config/initializers/rails_admin.rb b/config/initializers/rails_admin.rb index f2a17fee..ce7d783c 100644 --- a/config/initializers/rails_admin.rb +++ b/config/initializers/rails_admin.rb @@ -44,5 +44,6 @@ RailsAdmin.config do |config| Smile Theme User + UserBan ] end diff --git a/db/migrate/20210814134115_create_user_bans.rb b/db/migrate/20210814134115_create_user_bans.rb new file mode 100644 index 00000000..a5146c90 --- /dev/null +++ b/db/migrate/20210814134115_create_user_bans.rb @@ -0,0 +1,28 @@ +class CreateUserBans < ActiveRecord::Migration[5.2] + def up + create_table :user_bans do |t| + t.bigint :user_id + t.string :reason + t.datetime :expires_at + t.bigint :banned_by_id, nullable: true + + t.timestamps + end + + # foxy's functional fqueries + execute "INSERT INTO user_bans + (user_id, reason, expires_at, created_at, updated_at) + SELECT users.id, users.ban_reason, users.banned_until, users.updated_at, NOW() FROM users + WHERE banned_until IS NOT NULL AND NOT permanently_banned;" + + + execute "INSERT INTO user_bans + (user_id, reason, expires_at, created_at, updated_at) + SELECT users.id, users.ban_reason, NULL, users.updated_at, NOW() FROM users + WHERE permanently_banned;" + end + + def down + drop_table :user_bans + end +end diff --git a/db/schema.rb b/db/schema.rb index 9046d8f4..0e3d2915 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -248,6 +248,15 @@ ActiveRecord::Schema.define(version: 2022_01_05_171216) do t.index ["user_id", "code"], name: "index_totp_recovery_codes_on_user_id_and_code" end + create_table "user_bans", force: :cascade do |t| + t.bigint "user_id" + t.string "reason" + t.datetime "expires_at" + t.bigint "banned_by_id" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + end + create_table "users", id: :bigint, default: -> { "gen_timestamp_id('users'::text)" }, force: :cascade do |t| t.string "email", default: "", null: false t.string "encrypted_password", default: "", null: false diff --git a/lib/errors.rb b/lib/errors.rb new file mode 100644 index 00000000..1ddb9198 --- /dev/null +++ b/lib/errors.rb @@ -0,0 +1,26 @@ +module Errors + class Base < StandardError + def status + 500 + end + + def code + @code ||= self.class.name.sub('Errors::', '').underscore + end + end + + class BadRequest < Base + def status + 400 + end + end + + class InvalidBanDuration < BadRequest + end + + class Forbidden < Base + def status + 403 + end + end +end diff --git a/lib/types.rb b/lib/types.rb new file mode 100644 index 00000000..51e96551 --- /dev/null +++ b/lib/types.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +require 'dry-types' + +module Types + include Dry.Types() +end diff --git a/lib/use_case/base.rb b/lib/use_case/base.rb new file mode 100644 index 00000000..5468e0ff --- /dev/null +++ b/lib/use_case/base.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +require 'dry-initializer' +require 'types' +require 'errors' + +module UseCase + class Base + extend Dry::Initializer + + def self.call(*args, **kwargs) + new(*args, **kwargs).call + end + + def call + raise NotImplementedError + end + end +end \ No newline at end of file diff --git a/lib/use_case/user/ban.rb b/lib/use_case/user/ban.rb new file mode 100644 index 00000000..5ba61f40 --- /dev/null +++ b/lib/use_case/user/ban.rb @@ -0,0 +1,56 @@ +# frozen_string_literal: true + +require 'use_case/base' + +module UseCase + module User + class Ban < UseCase::Base + REASON_SPAM = 'Spam' + REASON_HARASSMENT = 'Harassment' + REASON_BAN_EVASION = 'Ban evasion' + + option :target_user_id, type: Types::Coercible::Integer + option :expiry, types: Types::Nominal::DateTime.optional + option :source_user_id, type: Types::Coercible::Integer.optional + option :reason, type: Types::Coercible::String.optional + + def call + ban = ::UserBan.create!( + user: target_user, + expires_at: expiry, + banned_by: source_user, + reason: reason + ) + + if reason == REASON_SPAM + target_user.update!( + profile_picture: nil, + profile_header: nil + ) + target_user.profile.update!( + display_name: nil, + description: '', + location: '', + website: '', + ) + end + + { + ban: ban + } + end + + def target_user + @target_user ||= ::User.find(target_user_id) + end + + def source_user + if source_user_id + @source_user ||= ::User.find(source_user_id) + else + nil + end + end + end + end +end diff --git a/lib/use_case/user/unban.rb b/lib/use_case/user/unban.rb new file mode 100644 index 00000000..81c5ca1c --- /dev/null +++ b/lib/use_case/user/unban.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +require 'use_case/base' + +module UseCase + module User + class Unban < UseCase::Base + param :target_user_id, type: Types::Coercible::Integer + + def call + UserBan.current.where(user_id: target_user_id).update_all( + # -1s to account for flakyness with timings in tests + expires_at: DateTime.now - 1.second + ) + end + end + end +end \ No newline at end of file diff --git a/spec/controllers/ajax/moderation_controller_spec.rb b/spec/controllers/ajax/moderation_controller_spec.rb index 013bdd25..265a1308 100644 --- a/spec/controllers/ajax/moderation_controller_spec.rb +++ b/spec/controllers/ajax/moderation_controller_spec.rb @@ -373,9 +373,9 @@ describe Ajax::ModerationController, :ajax_controller, type: :controller do { user: user_param, ban: ban, - permaban: permaban, reason: "just a prank, bro", - until: wrongly_formatted_date_ugh + duration: duration, + duration_unit: duration_unit, } end @@ -414,17 +414,17 @@ describe Ajax::ModerationController, :ajax_controller, type: :controller do context "when ban = 0" do let(:ban) { "0" } - let(:wrongly_formatted_date_ugh) { nil } "01".each_char do |pb| context "when permaban = #{pb}" do - let(:permaban) { pb } + let(:duration) { pb == '0' ? 3 : nil } + let(:duration_unit) { pb == '0' ? 'hours' : nil } context "when user is already banned" do - before { target_user.ban } + before { target_user.ban(nil) } it "unbans the user" do - expect { subject }.to(change { target_user.reload.banned? }.from(true).to(false)) + expect { subject }.to change { target_user.reload.banned? }.from(true).to(false) end include_examples "returns the expected response" @@ -443,16 +443,17 @@ describe Ajax::ModerationController, :ajax_controller, type: :controller do context "when ban = 1" do let(:ban) { "1" } - let(:wrongly_formatted_date_ugh) { "4/20/2420 12:00 AM" } context "when permaban = 0" do - let(:permaban) { "0" } + let(:duration) { 3 } + let(:duration_unit) { 'hours' } - it "bans the user until 2420-04-20" do - expect { subject }.to(change { target_user.reload.banned? }.from(false).to(true)) - expect(target_user).not_to be_permanently_banned - expect(target_user.ban_reason).to eq("just a prank, bro") - expect(target_user.banned_until).to eq(DateTime.strptime(wrongly_formatted_date_ugh, "%m/%d/%Y %I:%M %p")) + it "bans the user for 3 hours" do + Timecop.freeze do + expect { subject }.to change { target_user.reload.banned? }.from(false).to(true) + expect(target_user.bans.current.first.reason).to eq("just a prank, bro") + expect(target_user.bans.current.first.expires_at.to_i).to eq((Time.now.utc + 3.hours).to_i) + end end include_examples "returns the expected response" @@ -461,13 +462,13 @@ describe Ajax::ModerationController, :ajax_controller, type: :controller do end context "when permaban = 1" do - let(:permaban) { "1" } + let(:duration) { nil } + let(:duration_unit) { nil } it "bans the user for all eternity" do - expect { subject }.to(change { target_user.reload.banned? }.from(false).to(true)) - expect(target_user).to be_permanently_banned - expect(target_user.ban_reason).to eq("just a prank, bro") - expect(target_user.banned_until).to be_nil + expect { subject }.to change { target_user.reload.banned? }.from(false).to(true) + expect(target_user.bans.current.first.reason).to eq("just a prank, bro") + expect(target_user.bans.current.first.expires_at).to be_nil end include_examples "returns the expected response" @@ -477,11 +478,38 @@ describe Ajax::ModerationController, :ajax_controller, type: :controller do end end + context "when reason = Spam" do + let(:params) do + { + user: target_user.screen_name, + ban: "1", + reason: "Spam", + duration: nil, + duration_unit: nil, + } + end + + it "empties the user's profile" do + user.profile.display_name = "Veggietales Facts" + user.profile.description = "Are you a fan of Veggietales? Want to expand your veggie knowledge? Here at Veggietales Facts, we tweet trivia for fans like you." + user.profile.location = "Hell" + user.profile.website = "https://twitter.com/veggiefact" + + expect { subject }.to change { target_user.reload.banned? }.from(false).to(true) + expect(target_user.bans.current.first.reason).to eq("Spam") + + expect(target_user.profile.display_name).to be_nil + expect(target_user.profile.description).to be_empty + expect(target_user.profile.location).to be_empty + expect(target_user.profile.website).to be_empty + end + end + context "when user does not exist" do let(:user_param) { "fritz-fantom" } let(:ban) { "1" } - let(:permaban) { "1" } - let(:wrongly_formatted_date_ugh) { "4/20/2420 12:00 AM" } + let(:duration) { nil } + let(:duration_unit) { nil } let(:expected_response) do { "success" => false,