Merge branch 'master' into feature/bootstrap

This commit is contained in:
Andreas Nedbal 2020-05-04 23:19:54 +02:00
commit 53979580d8
27 changed files with 2739 additions and 26 deletions

View File

@ -67,3 +67,7 @@ jobs:
env:
POSTGRES_PORT: ${{ job.services.postgres.ports[5432] }}
REDIS_URL: "redis://localhost:${{ job.services.redis.ports[6379] }}"
- uses: codecov/codecov-action@v1
with:
token: ${{ secrets.CODECOV_TOKEN }}
file: ./coverage/coverage.xml

View File

@ -90,14 +90,14 @@ group :development, :test do
gem 'puma'
gem 'rspec-rails', '~> 3.9'
gem 'rspec-its', '~> 1.3'
gem "rspec-sidekiq", "~> 3.0"
gem "rspec-sidekiq", "~> 3.0", require: false
gem 'factory_bot_rails', require: false
gem 'faker'
gem 'capybara'
gem 'poltergeist'
gem 'simplecov', require: false
gem 'simplecov-json', require: false
gem 'simplecov-rcov', require: false
gem 'simplecov-cobertura', require: false
gem 'database_cleaner'
gem 'better_errors'
gem 'letter_opener' # Use this just in local test environments

View File

@ -444,12 +444,12 @@ GEM
simplecov (0.18.5)
docile (~> 1.1)
simplecov-html (~> 0.11)
simplecov-cobertura (1.3.1)
simplecov (~> 0.8)
simplecov-html (0.12.2)
simplecov-json (0.2.1)
json
simplecov
simplecov-rcov (0.2.3)
simplecov (>= 0.4.1)
spring (2.1.0)
sprockets (3.7.2)
concurrent-ruby (~> 1.0)
@ -572,8 +572,8 @@ DEPENDENCIES
sass-rails (~> 5.0)
sidekiq (< 6)
simplecov
simplecov-cobertura
simplecov-json
simplecov-rcov
spring (~> 2.0)
sweetalert-rails
timecop

View File

@ -19,7 +19,7 @@ class Ajax::GroupController < AjaxController
params.require :user
begin
target_user = User.find_by_screen_name(params[:user])
target_user = User.find_by_screen_name!(params[:user])
group = Group.create! user: current_user, display_name: params[:name]
rescue ActiveRecord::RecordInvalid => e
NewRelic::Agent.notice_error(e)
@ -85,7 +85,7 @@ class Ajax::GroupController < AjaxController
add = params[:add] == 'true'
begin
group = current_user.groups.find_by_name(params[:group])
group = current_user.groups.find_by_name!(params[:group])
rescue ActiveRecord::RecordNotFound => e
NewRelic::Agent.notice_error(e)
@response[:status] = :notfound
@ -93,7 +93,7 @@ class Ajax::GroupController < AjaxController
return
end
target_user = User.find_by_screen_name(params[:user])
target_user = User.find_by_screen_name!(params[:user])
if add
group.add_member target_user if group.members.find_by_user_id(target_user.id).nil?

View File

@ -46,6 +46,8 @@ class Ajax::InboxController < AjaxController
end
def remove_all
raise unless user_signed_in?
begin
Inbox.where(user: current_user).each { |i| i.remove }
rescue => e

View File

@ -111,7 +111,7 @@ class Ajax::ModerationController < AjaxController
params.require :permaban
reason = params[:reason]
target = User.find_by_screen_name(params[:user])
target = User.find_by_screen_name!(params[:user])
unban = params[:ban] == "0"
perma = params[:permaban] == "1"
@ -149,7 +149,7 @@ class Ajax::ModerationController < AjaxController
status = params[:status] == 'true'
target_user = User.find_by_screen_name(params[:user])
target_user = User.find_by_screen_name!(params[:user])
@response[:message] = I18n.t('messages.moderation.privilege.nope')
return unless %w(moderator admin).include? params[:type].downcase

View File

@ -27,9 +27,11 @@ class Ajax::QuestionController < AjaxController
params.require :anonymousQuestion
params.require :rcpt
is_never_anonymous = user_signed_in? && (params[:rcpt].start_with?('grp:') || params[:rcpt] == 'followers')
begin
question = Question.create!(content: params[:question],
author_is_anonymous: params[:anonymousQuestion],
author_is_anonymous: is_never_anonymous ? false : params[:anonymousQuestion],
user: current_user)
rescue ActiveRecord::RecordInvalid => e
NewRelic::Agent.notice_error(e)
@ -38,6 +40,11 @@ class Ajax::QuestionController < AjaxController
return
end
if !user_signed_in? && !question.author_is_anonymous
question.delete
return
end
unless current_user.nil?
current_user.increment! :asked_count unless params[:anonymousQuestion] == 'true'
end
@ -53,19 +60,27 @@ class Ajax::QuestionController < AjaxController
QuestionWorker.perform_async params[:rcpt], current_user.id, question.id
rescue ActiveRecord::RecordNotFound => e
NewRelic::Agent.notice_error(e)
question.delete
@response[:status] = :not_found
@response[:message] = I18n.t('messages.question.create.not_found')
return
end
end
else
if User.find(params[:rcpt]).nil?
u = User.find_by_id(params[:rcpt])
if u.nil?
@response[:status] = :not_found
@response[:message] = I18n.t('messages.question.create.not_found')
question.delete
return
end
Inbox.create!(user_id: params[:rcpt], question_id: question.id, new: true)
if !u.privacy_allow_anonymous_questions && question.author_is_anonymous
question.delete
return
end
Inbox.create!(user_id: u.id, question_id: question.id, new: true)
end
@response[:status] = :okay

View File

@ -19,7 +19,7 @@ class Ajax::ReportController < AjaxController
object = case obj
when 'User'
User.find_by_screen_name params[:id]
User.find_by_screen_name! params[:id]
when 'Question'
Question.find params[:id]
when 'Answer'

View File

@ -6,6 +6,18 @@ class AjaxController < ApplicationController
respond_to :json
rescue_from(StandardError) do |e|
NewRelic::Agent.notice_error(e)
@response = {
success: false,
message: "Something went wrong",
status: :err
}
return_response
end
rescue_from(ActiveRecord::RecordNotFound) do |e|
NewRelic::Agent.notice_error(e)

View File

@ -1,7 +1,7 @@
class Question < ApplicationRecord
include Question::AnswerMethods
belongs_to :user
belongs_to :user, optional: true
has_many :answers, dependent: :destroy
has_many :inboxes, dependent: :destroy

View File

@ -3,14 +3,7 @@
require "rails_helper"
describe Ajax::AnswerController, type: :controller do
shared_examples "returns the expected response" do
it "returns the expected response" do
expect(JSON.parse(subject.body)).to match(expected_response)
end
end
let(:user) { FactoryBot.create(:user) }
describe Ajax::AnswerController, :ajax_controller, type: :controller do
let(:question) { FactoryBot.create(:question, user: FactoryBot.build(:user, privacy_allow_stranger_answers: asker_allows_strangers)) }
let(:asker_allows_strangers) { true }
@ -186,7 +179,7 @@ describe Ajax::AnswerController, type: :controller do
describe "#destroy" do
let(:answer_user) { user }
let(:question) { FactoryBot.create(:question, user: FactoryBot.create(:user)) }
let(:question) { FactoryBot.create(:question) }
let(:answer) { FactoryBot.create(:answer, user: answer_user, question: question) }
let(:answer_id) { answer.id }

View File

@ -0,0 +1,226 @@
# coding: utf-8
# frozen_string_literal: true
require "rails_helper"
describe Ajax::CommentController, :ajax_controller, type: :controller do
let(:answer) { FactoryBot.create(:answer, user: FactoryBot.create(:user)) }
describe "#create" do
let(:params) do
{
answer: answer_id,
comment: comment
}.compact
end
subject { post(:create, params: params) }
context "when user is signed in" do
shared_examples "creates the comment" do
it "creates a comment to the answer" do
expect { subject }.to(change { Comment.count }.by(1))
expect(answer.reload.comments.ids).to include(Comment.last.id)
end
include_examples "returns the expected response"
end
shared_examples "does not create the comment" do
it "does not create a comment" do
expect { subject }.not_to(change { Comment.count })
end
include_examples "returns the expected response"
end
before(:each) { sign_in(user) }
context "when all parameters are given" do
let(:comment) { "// Here be dragons." }
context "when answer exists" do
let(:answer_id) { answer.id }
let(:expected_response) do
{
"success" => true,
"status" => "okay",
"message" => anything,
"render" => anything,
"count" => 1
}
end
include_examples "creates the comment"
context "when comment is too long" do
let(:comment) { "E" * 621 }
let(:expected_response) do
{
"success" => false,
"status" => "rec_inv",
"message" => anything
}
end
include_examples "does not create the comment"
end
end
context "when answer does not exist" do
let(:answer_id) { "nein!" }
let(:expected_response) do
{
"success" => false,
"status" => anything,
"message" => anything
}
end
include_examples "does not create the comment"
end
end
context "when some parameters are missing" do
let(:answer_id) { nil }
let(:comment) { "" }
let(:expected_response) do
{
"success" => false,
"status" => "parameter_error",
"message" => anything
}
end
include_examples "returns the expected response"
end
end
context "when user is not signed in" do
let(:answer_id) { answer.id }
let(:comment) { "HACKED" }
let(:expected_response) do
{
"success" => false,
"status" => "err",
"message" => anything
}
end
include_examples "returns the expected response"
end
end
describe "#destroy" do
let(:answer_user) { FactoryBot.create(:user) }
let(:answer) { FactoryBot.create(:answer, user: answer_user) }
let(:comment_user) { user }
let(:comment) { FactoryBot.create(:comment, user: comment_user, answer: answer) }
let(:comment_id) { comment.id }
let(:params) do
{
comment: comment_id
}
end
subject { delete(:destroy, params: params) }
context "when user is signed in" do
shared_examples "deletes the comment" do
let(:expected_response) do
{
"success" => true,
"status" => "okay",
"message" => anything,
"count" => 0
}
end
it "deletes the comment" do
comment # ensure we already have it in the db
expect { subject }.to(change { Comment.count }.by(-1))
end
include_examples "returns the expected response"
end
shared_examples "does not delete the comment" do
let(:expected_response) do
{
"success" => false,
"status" => "nopriv",
"message" => anything
}
end
it "does not delete the comment" do
comment # ensure we already have it in the db
expect { subject }.not_to(change { Comment.count })
end
include_examples "returns the expected response"
end
before(:each) { sign_in(user) }
context "when the comment exists and was made by the current user" do
include_examples "deletes the comment"
end
context "when the comment exists and was not made by the current user" do
let(:comment_user) { FactoryBot.create(:user) }
include_examples "does not delete the comment"
context "when the current user created the answer" do
let(:answer_user) { user }
include_examples "deletes the comment"
end
%i[moderator administrator].each do |privileged_role|
context "when the current user is a #{privileged_role}" do
around do |example|
user.add_role privileged_role
example.run
user.remove_role privileged_role
end
include_examples "deletes the comment"
end
end
end
context "when the comment does not exist" do
let(:comment_id) { "sonic_the_hedgehog" }
let(:expected_response) do
{
"success" => false,
"status" => anything,
"message" => anything
}
end
include_examples "returns the expected response"
end
end
context "when user is not signed in" do
let(:expected_response) do
{
"success" => false,
"status" => "nopriv",
"message" => anything
}
end
include_examples "returns the expected response"
end
end
end

View File

@ -0,0 +1,159 @@
# frozen_string_literal: true
require "rails_helper"
describe Ajax::FriendController, :ajax_controller, type: :controller do
describe "#create" do
let(:params) do
{
screen_name: screen_name
}
end
subject { post(:create, params: params) }
context "when user is signed in" do
before(:each) { sign_in(user) }
context "when target user exists" do
let(:target_user) { FactoryBot.create(:user) }
let(:screen_name) { target_user.screen_name }
let(:expected_response) do
{
"success" => true,
"status" => "okay",
"message" => anything
}
end
it "creates a follow relationship" do
expect(user.friends.ids).not_to include(target_user.id)
expect { subject }.to(change { user.friends.count }.by(1))
expect(user.friends.ids).to include(target_user.id)
end
include_examples "returns the expected response"
end
context "when target user does not exist" do
let(:screen_name) { "tripmeister_eder" }
let(:expected_response) do
{
"success" => true,
"status" => "okay",
"message" => anything
}
end
it "does not create a follow relationship" do
expect { subject }.not_to(change { user.friends.count })
end
include_examples "returns the expected response"
end
end
context "when user is not signed in" do
let(:screen_name) { "tutenchamun" }
let(:expected_response) do
{
"success" => false,
"status" => "fail",
"message" => anything
}
end
include_examples "returns the expected response"
end
end
describe "#destroy" do
let(:params) do
{
screen_name: screen_name
}
end
subject { delete(:destroy, params: params) }
context "when user is signed in" do
before(:each) { sign_in(user) }
context "when target user exists" do
let(:target_user) { FactoryBot.create(:user) }
let(:screen_name) { target_user.screen_name }
before(:each) { target_user }
context "when user follows target" do
let(:expected_response) do
{
"success" => true,
"status" => "okay",
"message" => anything
}
end
before(:each) { user.follow target_user }
it "destroys a follow relationship" do
expect(user.friends.ids).to include(target_user.id)
expect { subject }.to(change { user.friends.count }.by(-1))
expect(user.friends.ids).not_to include(target_user.id)
end
include_examples "returns the expected response"
end
context "when user does not already follow target" do
let(:expected_response) do
{
"success" => false,
"status" => "fail",
"message" => anything
}
end
it "does not destroy a follow relationship" do
expect { subject }.not_to(change { user.friends.count })
end
include_examples "returns the expected response"
end
end
context "when target user does not exist" do
let(:screen_name) { "tripmeister_eder" }
let(:expected_response) do
{
"success" => false,
"status" => "fail",
"message" => anything
}
end
it "does not destroy a follow relationship" do
expect { subject }.not_to(change { user.friends.count })
end
include_examples "returns the expected response"
end
end
context "when user is not signed in" do
let(:screen_name) { "tutenchamun" }
let(:expected_response) do
{
"success" => false,
"status" => "fail",
"message" => anything
}
end
include_examples "returns the expected response"
end
end
end

View File

@ -0,0 +1,341 @@
# frozen_string_literal: true
require "rails_helper"
describe Ajax::GroupController, :ajax_controller, type: :controller do
let(:target_user) { FactoryBot.create(:user) }
describe "#create" do
let(:name) { "I signori della gallassia" }
let(:target_user_param) { target_user.screen_name }
let(:params) do
{
"name" => name,
"user" => target_user_param
}
end
subject { post(:create, params: params) }
context "when user is signed in" do
let(:expected_response) do
{
"success" => true,
"status" => "okay",
"message" => anything,
"render" => anything
}
end
before(:each) { sign_in(user) }
it "creates the group" do
expect { subject }.to(change { user.groups.count }.by(1))
end
include_examples "returns the expected response"
context "when name param is missing" do
let(:name) { "" }
let(:expected_response) do
{
"success" => false,
"status" => "toolong",
"message" => anything
}
end
it "does not create the group" do
expect { subject }.not_to(change { user.groups.count })
end
include_examples "returns the expected response"
end
context "when target user does not exist" do
let(:target_user_param) { "giuseppe-drogo" }
let(:expected_response) do
{
"success" => false,
"status" => "notfound",
"message" => anything
}
end
it "does not create the group" do
expect { subject }.not_to(change { user.groups.count })
end
include_examples "returns the expected response"
end
context "when group name is invalid for reasons" do
let(:name) { "\u{1f43e}" }
let(:expected_response) do
{
"success" => false,
"status" => "toolong",
"message" => anything
}
end
it "does not create the group" do
expect { subject }.not_to(change { user.groups.count })
end
include_examples "returns the expected response"
end
context "when group already exists" do
before(:each) { post(:create, params: params) }
let(:expected_response) do
{
"success" => false,
"status" => "exists",
"message" => anything
}
end
it "does not create the group" do
expect { subject }.not_to(change { user.groups.count })
end
include_examples "returns the expected response"
end
context "when someone else created a group with the same name" do
before(:each) do
FactoryBot.create(:group, user: target_user, display_name: name)
end
it "creates the group" do
expect { subject }.to(change { user.groups.count }.by(1))
end
include_examples "returns the expected response"
end
end
context "when user is not signed in" do
let(:expected_response) do
{
"success" => false,
"status" => "noauth",
"message" => anything
}
end
include_examples "returns the expected response"
end
end
describe "#destroy" do
let(:name) { "I signori della gallassia" }
let(:group) { FactoryBot.create(:group, user: user, display_name: name) }
let(:group_param) { group.name }
let(:params) do
{
"group" => group_param
}
end
subject { delete(:destroy, params: params) }
context "when user is signed in" do
let(:expected_response) do
{
"success" => true,
"status" => "okay",
"message" => anything
}
end
before(:each) { sign_in(user) }
it "deletes the group" do
group
expect { subject }.to(change { user.groups.count }.by(-1))
end
include_examples "returns the expected response"
context "when group param is missing" do
let(:group_param) { "" }
let(:expected_response) do
{
"success" => false,
"status" => "parameter_error",
"message" => anything
}
end
it "does not delete the group" do
expect { subject }.not_to(change { user.groups.count })
end
include_examples "returns the expected response"
end
context "when group does not exist" do
let(:group_param) { "the-foobars-and-the-dingdongs" }
let(:expected_response) do
{
"success" => false,
"status" => "err",
"message" => anything
}
end
it "does not delete the group" do
expect { subject }.not_to(change { user.groups.count })
end
include_examples "returns the expected response"
end
context "when someone else created a group with the same name" do
before(:each) do
group
FactoryBot.create(:group, user: target_user, display_name: name)
end
it "deletes the group" do
expect { subject }.to(change { user.groups.count }.by(-1))
end
it "does not delete the other users' group" do
expect { subject }.not_to(change { target_user.groups.count })
end
include_examples "returns the expected response"
end
end
context "when user is not signed in" do
let(:expected_response) do
{
"success" => false,
"status" => "noauth",
"message" => anything
}
end
include_examples "returns the expected response"
end
end
describe "#membership" do
let(:name) { "The Agency" }
let(:members) { [] }
let(:group) { FactoryBot.create(:group, user: user, display_name: name, members: members) }
let(:group_param) { group.name }
let(:target_user_param) { target_user.screen_name }
let(:params) do
{
"group" => group_param,
"user" => target_user_param,
"add" => add_param
}
end
subject { post(:membership, params: params) }
context "when user is signed in" do
let(:expected_response) do
{
"success" => true,
"status" => "okay",
"message" => anything,
"checked" => expected_checked
}
end
before(:each) { sign_in(user) }
context "when add is false" do
let(:add_param) { "false" }
let(:expected_checked) { false }
it "does not do anything" do
expect { subject }.not_to(change { group.members })
expect(group.members.map { |gm| gm.user.id }.sort ).to eq([])
end
include_examples "returns the expected response"
context "when the user was already added to the group" do
let(:members) { [target_user] }
it "removes the user from the group" do
expect { subject }.to(change { group.reload.members.map { |gm| gm.user.id }.sort }.from([target_user.id]).to([]))
end
include_examples "returns the expected response"
end
end
context "when add is true" do
let(:add_param) { "true" }
let(:expected_checked) { true }
it "adds the user to the group" do
expect { subject }.to(change { group.reload.members.map { |gm| gm.user.id }.sort }.from([]).to([target_user.id]))
end
include_examples "returns the expected response"
context "when the user was already added to the group" do
let(:members) { [target_user] }
it "does not add the user to the group again" do
expect { subject }.not_to(change { group.members })
expect(group.members.map { |gm| gm.user.id }.sort ).to eq([target_user.id])
end
include_examples "returns the expected response"
end
end
context "when group does not exist" do
let(:group_param) { "the-good-agency" }
let(:add_param) { "add" }
let(:expected_response) do
{
"success" => false,
"status" => "notfound",
"message" => anything
}
end
include_examples "returns the expected response"
end
context "when target user does not exist" do
let(:target_user_param) { "erwin-proell" }
let(:add_param) { "add" }
let(:expected_response) do
{
"success" => false,
"status" => "not_found",
"message" => anything
}
end
include_examples "returns the expected response"
end
end
context "when user is not signed in" do
let(:add_param) { "whatever" }
let(:expected_response) do
{
"success" => false,
"status" => "noauth",
"message" => anything
}
end
include_examples "returns the expected response"
end
end
end

View File

@ -0,0 +1,244 @@
# coding: utf-8
# frozen_string_literal: true
require "rails_helper"
describe Ajax::InboxController, :ajax_controller, type: :controller do
describe "#create" do
subject { post(:create) }
context "when user is signed in" do
before(:each) { sign_in(user) }
let(:expected_response) do
{
"success" => true,
"status" => "okay",
"message" => anything,
"render" => anything
}
end
it "creates a generated question to the user's inbox" do
allow(QuestionGenerator).to receive(:generate).and_return("Is Mayonnaise an instrument?")
expect { subject }.to(change { user.inboxes.count }.by(1))
expect(user.inboxes.last.question.author_is_anonymous).to eq(true)
expect(user.inboxes.last.question.author_name).to eq("justask")
expect(user.inboxes.last.question.user).to eq(user)
expect(user.inboxes.last.question.content).to eq("Is Mayonnaise an instrument?")
end
include_examples "returns the expected response"
end
context "when user is not signed in" do
let(:expected_response) do
{
"success" => false,
"status" => "noauth",
"message" => anything
}
end
include_examples "returns the expected response"
end
end
describe "#remove" do
let(:params) do
{
id: inbox_entry_id
}
end
subject { delete(:remove, params: params) }
context "when user is signed in" do
before(:each) { sign_in(user) }
context "when inbox entry exists" do
let(:inbox_entry) { FactoryBot.create(:inbox, user: inbox_user) }
let(:inbox_entry_id) { inbox_entry.id }
# ensure the inbox entry exists
before(:each) { inbox_entry }
context "when inbox entry belongs to the current user" do
let(:inbox_user) { user }
let(:expected_response) do
{
"success" => true,
"status" => "okay",
"message" => anything
}
end
it "removes the inbox entry" do
expect { subject }.to(change { user.inboxes.count }.by(-1))
expect { Inbox.find(inbox_entry.id) }.to raise_error(ActiveRecord::RecordNotFound)
end
include_examples "returns the expected response"
end
context "when inbox entry does not belong to the current user" do
let(:inbox_user) { FactoryBot.create(:user) }
let(:expected_response) do
{
"success" => false,
"status" => "fail",
"message" => anything
}
end
it "does not remove the inbox entry" do
expect { subject }.not_to(change { Inbox.count })
expect { Inbox.find(inbox_entry.id) }.not_to raise_error
end
include_examples "returns the expected response"
end
end
context "when inbox entry does not exist" do
let(:inbox_entry_id) { "Nein!" }
let(:expected_response) do
{
"success" => false,
"status" => "not_found",
"message" => anything
}
end
include_examples "returns the expected response"
end
end
context "when user is not signed in" do
let(:inbox_entry_id) { "HACKED" }
let(:expected_response) do
{
"success" => false,
"status" => "not_found",
"message" => anything
}
end
include_examples "returns the expected response"
end
end
describe "#remove_all" do
subject { delete(:remove_all) }
context "when user is signed in" do
before(:each) { sign_in(user) }
let(:expected_response) do
{
"success" => true,
"status" => "okay",
"message" => anything
}
end
include_examples "returns the expected response"
context "when user has some inbox entries" do
let(:some_other_user) { FactoryBot.create(:user) }
before do
10.times { FactoryBot.create(:inbox, user: user) }
10.times { FactoryBot.create(:inbox, user: some_other_user) }
end
it "deletes all the entries from the user's inbox" do
expect { subject }.to(change { [Inbox.count, user.inboxes.count] }.from([20, 10]).to([10, 0]))
end
include_examples "returns the expected response"
end
end
context "when user is not signed in" do
let(:expected_response) do
{
"success" => false,
"status" => "err",
"message" => anything
}
end
include_examples "returns the expected response"
end
end
describe "#remove_all_author" do
let(:params) do
{
author: author
}
end
subject { delete(:remove_all_author, params: params) }
context "when user is signed in" do
before(:each) { sign_in(user) }
let(:author) { user.screen_name }
let(:expected_response) do
{
"success" => true,
"status" => "okay",
"message" => anything
}
end
include_examples "returns the expected response"
context "when user has some inbox entries" do
let(:some_other_user) { FactoryBot.create(:user) }
let(:author) { some_other_user.screen_name }
before do
normal_question = FactoryBot.create(:question, user: some_other_user, author_is_anonymous: false)
anon_question = FactoryBot.create(:question, user: some_other_user, author_is_anonymous: true)
10.times { FactoryBot.create(:inbox, user: user) }
3.times { FactoryBot.create(:inbox, user: user, question: normal_question) }
2.times { FactoryBot.create(:inbox, user: user, question: anon_question) }
end
it "deletes all the entries asked by some other user which are not anonymous from the user's inbox" do
expect { subject }.to(change { user.inboxes.count }.from(15).to(12))
end
include_examples "returns the expected response"
end
context "when author is unknown" do
let(:author) { "schmarrn" }
let(:expected_response) do
{
"success" => false,
"status" => "err",
"message" => anything
}
end
include_examples "returns the expected response"
end
end
context "when user is not signed in" do
let(:author) { "hackerman1337" }
let(:expected_response) do
{
"success" => false,
"status" => "err",
"message" => anything
}
end
include_examples "returns the expected response"
end
end
end

View File

@ -0,0 +1,650 @@
# coding: utf-8
# frozen_string_literal: true
require "rails_helper"
describe Ajax::ModerationController, :ajax_controller, type: :controller do
shared_examples "fails when report does not exist" do
let(:report_id) { "Burgenland" }
let(:expected_response) do
{
"success" => false,
"status" => "not_found",
"message" => anything
}
end
include_examples "returns the expected response"
end
let(:target_user) { FactoryBot.create(:user) }
let(:report) do
Reports::User.create!(
user: user,
target_id: target_user.id
)
end
let(:user_role) { :moderator }
before do
user.add_role user_role if user_role
sign_in(user)
end
describe "#vote" do
let(:params) do
{
id: report_id,
upvote: upvote
}
end
subject { post(:vote, params: params) }
context "when report exists" do
let(:report_id) { report.id }
let(:expected_response) do
{
"success" => true,
"status" => "okay",
"message" => anything,
"count" => expected_count
}
end
context "when upvote is true" do
let(:upvote) { "true" }
let(:expected_count) { 1 }
it "creates a moderation vote" do
expect { subject }.to(change { ModerationVote.count }.by(1))
expect(report.moderation_votes.last.user).to eq(user)
expect(report.moderation_votes.last.upvote).to eq(true)
end
include_examples "returns the expected response"
context "when moderation vote already exists" do
let(:expected_response) do
{
"success" => false,
"status" => "fail",
"message" => anything
}
end
before { post(:vote, params: params) }
it "does not create a new moderation vote" do
expect { subject }.to_not(change { ModerationVote.count })
end
include_examples "returns the expected response"
end
end
context "when upvote is false" do
let(:upvote) { "false" }
let(:expected_count) { 0 }
it "creates a moderation vote" do
expect { subject }.to(change { ModerationVote.count }.by(1))
expect(report.moderation_votes.last.user).to eq(user)
expect(report.moderation_votes.last.upvote).to eq(false)
end
context "when moderation vote already exists" do
let(:expected_response) do
{
"success" => false,
"status" => "fail",
"message" => anything
}
end
before { post(:vote, params: params) }
it "does not create a new moderation vote" do
expect { subject }.to_not(change { ModerationVote.count })
end
include_examples "returns the expected response"
end
end
end
it_behaves_like "fails when report does not exist" do
let(:upvote) { "true" }
it "does not create a moderation vote" do
expect { subject }.to_not(change { ModerationVote.count })
end
end
end
describe "#destroy_vote" do
let(:params) do
{
id: report_id
}
end
subject { post(:destroy_vote, params: params) }
context "when report exists" do
let(:report_id) { report.id }
let(:expected_response) do
{
"success" => true,
"status" => "okay",
"message" => anything,
"count" => expected_count
}
end
context "when the user already voted" do
let(:expected_count) { 0 }
before { post(:vote, params: params.merge("upvote" => true)) }
it "removes a moderation vote" do
expect { subject }.to(change { ModerationVote.count }.by(-1))
end
include_examples "returns the expected response"
end
context "when the user has not voted yet" do
let(:expected_count) { 0 }
let(:expected_response) do
{
"success" => false,
"status" => "fail",
"message" => anything
}
end
it "does not create a new moderation vote" do
expect { subject }.to_not(change { ModerationVote.count })
end
include_examples "returns the expected response"
end
end
it_behaves_like "fails when report does not exist" do
it "does not create a moderation vote" do
expect { subject }.to_not(change { ModerationVote.count })
end
end
end
describe "#destroy_report" do
let(:params) do
{
id: report_id
}
end
subject { post(:destroy_report, params: params) }
context "when report exists" do
let(:report_id) { report.id }
let(:expected_response) do
{
"success" => true,
"status" => "okay",
"message" => anything
}
end
before { report }
it "does not actually destroy the report" do
expect { subject }.to_not(change { Report.count })
end
it "only marks the report as deleted" do
expect { subject }.to(change { report.reload.deleted }.from(false).to(true))
end
include_examples "returns the expected response"
end
it_behaves_like "fails when report does not exist"
end
describe "#create_comment" do
let(:params) do
{
id: report_id,
comment: comment
}
end
let(:comment) { "ZEFIX NUAMOI!" }
subject { post(:create_comment, params: params) }
context "when report exists" do
let(:report_id) { report.id }
let(:expected_response) do
{
"success" => true,
"status" => "okay",
"message" => anything,
"render" => anything,
"count" => 1
}
end
it "creates a moderation comment" do
expect { subject }.to(change { ModerationComment.count }.by(1))
expect(report.moderation_comments.last.user).to eq(user)
expect(report.moderation_comments.last.content).to eq(comment)
end
include_examples "returns the expected response"
context "when comment is blank" do
let(:comment) { "" }
let(:expected_response) do
{
"success" => false,
"status" => "parameter_error",
"message" => anything
}
end
it "does not create a moderation comment" do
expect { subject }.to_not(change { ModerationComment.count })
end
include_examples "returns the expected response"
end
context "when comment is the letter E 621 times" do
let(:comment) { "E" * 621 }
let(:expected_response) do
{
"success" => false,
"status" => "rec_inv",
"message" => anything
}
end
it "does not create a moderation comment" do
expect { subject }.to_not(change { ModerationComment.count })
end
include_examples "returns the expected response"
end
end
it_behaves_like "fails when report does not exist" do
it "does not create a moderation comment" do
expect { subject }.to_not(change { ModerationComment.count })
end
end
end
describe "#destroy_comment" do
let(:comment) { ModerationComment.create!(user: comment_user, report: report, content: "sigh") }
let(:params) do
{
comment: comment_id
}
end
subject { post(:destroy_comment, params: params) }
context "when comment exists" do
let(:comment_id) { comment.id }
before { comment }
context "when comment was made by the current user" do
let(:comment_user) { user }
let(:expected_response) do
{
"success" => true,
"status" => "okay",
"message" => anything
}
end
it "destroys the comment" do
expect { subject }.to(change { ModerationComment.count }.by(-1))
expect { comment.reload }.to raise_error(ActiveRecord::RecordNotFound)
end
include_examples "returns the expected response"
end
context "when comment was made by someone else" do
let(:comment_user) { FactoryBot.create(:user) }
let(:expected_response) do
{
"success" => false,
"status" => "nopriv",
"message" => anything
}
end
it "does not destroy the comment" do
expect { subject }.not_to(change { ModerationComment.count })
expect { comment.reload }.not_to raise_error
end
include_examples "returns the expected response"
context "when current user is an administrator" do
let(:user_role) { :administrator }
it "does not destroy the comment" do
expect { subject }.not_to(change { ModerationComment.count })
expect { comment.reload }.not_to raise_error
end
include_examples "returns the expected response"
end
end
end
context "when comment does not exist" do
let(:comment_id) { "Rügenwalder" }
let(:expected_response) do
{
"success" => false,
"status" => "not_found",
"message" => anything
}
end
it "does not destroy any comment" do
expect { subject }.not_to(change { ModerationComment.count })
end
include_examples "returns the expected response"
end
end
describe "#ban" do
let(:params) do
{
user: user_param,
ban: ban,
permaban: permaban,
reason: "just a prank, bro",
until: wrongly_formatted_date_ugh
}
end
subject { post(:ban, params: params) }
context "when user exists" do
shared_examples "does not ban administrators" do
let(:expected_response) do
{
"success" => false,
"status" => "nopriv",
"message" => anything
}
end
before { target_user.add_role :administrator }
it "does not ban the target user" do
subject
expect(target_user).not_to be_banned
end
include_examples "returns the expected response"
end
let(:user_param) { target_user.screen_name }
let(:expected_response) do
{
"success" => true,
"status" => "okay",
"message" => anything
}
end
before { target_user }
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 }
context "when user is already banned" do
before { target_user.ban }
it "unbans the user" do
expect { subject }.to(change { target_user.reload.banned? }.from(true).to(false))
end
include_examples "returns the expected response"
end
context "when user is not yet banned" do
it "does not change the status of the ban" do
expect { subject }.not_to(change { target_user.reload.banned? })
end
include_examples "returns the expected response"
end
end
end
end
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" }
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"))
end
include_examples "returns the expected response"
it_behaves_like "does not ban administrators"
end
context "when permaban = 1" do
let(:permaban) { "1" }
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
end
include_examples "returns the expected response"
it_behaves_like "does not ban administrators"
end
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(:expected_response) do
{
"success" => false,
"status" => "not_found",
"message" => anything
}
end
include_examples "returns the expected response"
end
end
describe "#privilege" do
valid_role_pairs = {
moderator: :moderator,
admin: :administrator
}.freeze
let(:params) do
{
user: user_param,
type: type,
status: status
}
end
subject { post(:privilege, params: params) }
context "when user exists" do
let(:user_param) { target_user.screen_name }
before { target_user }
{
nil => "has no extra roles",
:moderator => "is a moderator"
}.each do |u_role, context_desc|
context "when the current user #{context_desc}" do
let(:user_role) { u_role }
let(:expected_response) do
{
"success" => false,
"status" => "nopriv",
"message" => anything
}
end
valid_role_pairs.each do |type, role_name|
context "when type is #{type}" do
let(:type) { type }
context "when status is true" do
let(:status) { "true" }
it "does not modify the roles on the target user" do
expect { subject }.not_to(change { target_user.reload.roles.to_a })
end
include_examples "returns the expected response"
end
context "when status is false" do
let(:status) { "true" }
before { target_user.add_role role_name }
it "does not modify the roles on the target user" do
expect { subject }.not_to(change { target_user.reload.roles.to_a })
end
include_examples "returns the expected response"
end
end
end
end
end
context "when the current user is an administrator" do
let(:user_role) { :administrator }
valid_role_pairs.each do |type, role_name|
context "when type is #{type}" do
let(:type) { type }
context "when status is true" do
let(:status) { "true" }
let(:expected_response) do
{
"success" => true,
"status" => "okay",
"message" => anything,
"checked" => true
}
end
it "adds the #{role_name} role to the target user" do
expect { subject }.to(change { target_user.roles.reload.to_a })
expect(target_user).to have_role(role_name)
end
include_examples "returns the expected response"
end
context "when status is false" do
let(:status) { "false" }
let(:expected_response) do
{
"success" => true,
"status" => "okay",
"message" => anything,
"checked" => false
}
end
before { target_user.add_role role_name }
it "removes the #{role_name} role from the target user" do
expect { subject }.to(change { target_user.reload.roles.to_a })
expect(target_user).not_to have_role(role_name)
end
include_examples "returns the expected response"
end
end
end
context "when type is some bogus value" do
let(:type) { "some bogus value" }
let(:expected_response) do
{
"success" => false,
"status" => "err",
"message" => anything
}
end
%w[true false].each do |s|
context "when status is #{s}" do
let(:status) { s }
it "does not modify the roles on the target user" do
expect { subject }.not_to(change { target_user.reload.roles.to_a })
end
include_examples "returns the expected response"
end
end
end
end
end
context "when user does not exist" do
let(:user_param) { "fritz-fantom" }
let(:type) { "admin" }
let(:status) { "true" }
let(:expected_response) do
{
"success" => false,
"status" => "not_found",
"message" => anything
}
end
include_examples "returns the expected response"
end
end
end

View File

@ -0,0 +1,399 @@
# frozen_string_literal: true
require "rails_helper"
describe Ajax::QuestionController, :ajax_controller, type: :controller do
describe "#create" do
shared_examples "creates the question" do |check_for_inbox = true|
it "creates the question" do
expect { subject }.to(change { Question.count }.by(1))
expect(Question.last.content).to eq(question_content)
expect(Question.last.author_is_anonymous).to be(expected_question_anonymous)
expect(Question.last.user).to eq(expected_question_user)
end
if check_for_inbox
it "adds the question to the target users' inbox" do
expect { subject }.to(change { target_user.inboxes.count }.by(1))
expect(target_user.inboxes.last.question.content).to eq(question_content)
end
end
include_examples "returns the expected response"
end
shared_examples "does not create the question" do |check_for_inbox = true|
it "does not create the question" do
expect { subject }.not_to(change { Question.count })
end
if check_for_inbox
it "does not add the question to the target users' inbox" do
expect { subject }.not_to(change { target_user.inboxes.count })
end
end
include_examples "returns the expected response"
end
shared_examples "enqueues a QuestionWorker job" do |expected_rcpt|
it "enqueues a QuestionWorker job" do
allow(QuestionWorker).to receive(:perform_async)
subject
expect(QuestionWorker).to have_received(:perform_async).with(expected_rcpt, user.id, Question.last.id)
end
include_examples "returns the expected response"
end
shared_examples "does not enqueue a QuestionWorker job" do
it "does not enqueue a QuestionWorker job" do
allow(QuestionWorker).to receive(:perform_async)
subject
expect(QuestionWorker).not_to have_received(:perform_async)
end
include_examples "returns the expected response"
end
let(:target_user) { FactoryBot.create(:user, privacy_allow_anonymous_questions: user_allows_anonymous_questions) }
let(:params) do
{
question: question_content,
anonymousQuestion: anonymous_question,
rcpt: rcpt
}
end
subject { post(:create, params: params) }
context "when user is signed in" do
let(:question_content) { "Was letzte Preis?" }
let(:expected_response) do
{
"success" => true,
"status" => "okay",
"message" => anything
}
end
let(:expected_question_user) { user }
before(:each) { sign_in(user) }
context "when rcpt is a valid user" do
let(:rcpt) { target_user.id }
context "when user allows anonymous questions" do
let(:user_allows_anonymous_questions) { true }
context "when anonymousQuestion is true" do
let(:anonymous_question) { "true" }
let(:expected_question_anonymous) { true }
include_examples "creates the question"
end
context "when anonymousQuestion is false" do
let(:anonymous_question) { "false" }
let(:expected_question_anonymous) { false }
include_examples "creates the question"
end
end
context "when user does not allow anonymous questions" do
let(:user_allows_anonymous_questions) { false }
context "when anonymousQuestion is true" do
let(:anonymous_question) { "true" }
let(:expected_response) do
{
"success" => false,
"status" => "unknown",
"message" => anything
}
end
include_examples "does not create the question"
end
context "when anonymousQuestion is false" do
let(:anonymous_question) { "false" }
let(:expected_question_anonymous) { false }
include_examples "creates the question"
end
end
end
context "when rcpt is followers" do
let(:rcpt) { "followers" }
context "when anonymousQuestion is true" do
let(:anonymous_question) { "true" }
let(:expected_question_anonymous) { false }
include_examples "creates the question", false
include_examples "enqueues a QuestionWorker job", "followers"
end
context "when anonymousQuestion is false" do
let(:anonymous_question) { "false" }
let(:expected_question_anonymous) { false }
include_examples "creates the question", false
include_examples "enqueues a QuestionWorker job", "followers"
end
end
context "when rcpt is a group" do
let(:rcpt) { "grp:foobar" }
context "when group exists" do
let(:group) { FactoryBot.create(:group, display_name: "FooBar", user: user) }
before { group }
context "when anonymousQuestion is true" do
let(:anonymous_question) { "true" }
let(:expected_question_anonymous) { false }
include_examples "creates the question", false
include_examples "enqueues a QuestionWorker job", "grp:foobar"
end
context "when anonymousQuestion is false" do
let(:anonymous_question) { "false" }
let(:expected_question_anonymous) { false }
include_examples "creates the question", false
include_examples "enqueues a QuestionWorker job", "grp:foobar"
end
end
context "when group does not exist" do
let(:expected_response) do
{
"success" => false,
"status" => "not_found",
"message" => anything
}
end
context "when anonymousQuestion is true" do
let(:anonymous_question) { "true" }
include_examples "does not create the question", false
include_examples "does not enqueue a QuestionWorker job"
end
context "when anonymousQuestion is false" do
let(:anonymous_question) { "false" }
include_examples "does not create the question", false
include_examples "does not enqueue a QuestionWorker job"
end
end
end
context "when rcpt is a non-existent user" do
let(:rcpt) { "tripmeister_eder" }
let(:anonymous_question) { "false" }
let(:expected_response) do
{
"success" => false,
"status" => "not_found",
"message" => anything
}
end
include_examples "does not create the question", false
include_examples "returns the expected response"
end
end
context "when user is not signed in" do
let(:target_user) { FactoryBot.create(:user, privacy_allow_anonymous_questions: user_allows_anonymous_questions) }
let(:question_content) { "Was letzte Preis?" }
let(:anonymous_question) { "true" }
let(:expected_response) do
{
"success" => true,
"status" => "okay",
"message" => anything
}
end
let(:expected_question_anonymous) { true }
let(:expected_question_user) { nil }
context "when rcpt is a valid user" do
let(:rcpt) { target_user.id }
context "when user allows anonymous questions" do
let(:user_allows_anonymous_questions) { true }
include_examples "creates the question"
context "when anonymousQuestion is false" do
let(:anonymous_question) { "false" }
let(:expected_response) do
{
"success" => false,
"status" => "unknown",
"message" => anything
}
end
include_examples "does not create the question"
end
end
context "when user does not allow anonymous questions" do
let(:user_allows_anonymous_questions) { false }
let(:expected_response) do
{
"success" => false,
"status" => "unknown",
"message" => anything
}
end
include_examples "does not create the question"
context "when anonymousQuestion is false" do
let(:anonymous_question) { "false" }
include_examples "does not create the question"
end
end
end
context "when rcpt is followers" do
let(:rcpt) { "followers" }
include_examples "does not enqueue a QuestionWorker job"
end
context "when rcpt is a group" do
let(:rcpt) { "grp:foobar" }
include_examples "does not enqueue a QuestionWorker job"
end
context "when rcpt is a non-existent user" do
let(:rcpt) { "tripmeister_eder" }
let(:expected_response) do
{
"success" => false,
"status" => "not_found",
"message" => anything
}
end
include_examples "does not create the question", false
end
end
end
describe "#destroy" do
shared_examples "does not delete the question" do |expected_status|
let(:expected_response) do
{
"success" => false,
"status" => expected_status,
"message" => anything
}
end
it "does not delete the question" do
question # ensure we already have it in the db
expect { subject }.not_to(change { Question.count })
end
include_examples "returns the expected response"
end
let(:question_user) { user }
let(:question) { FactoryBot.create(:question, user: question_user) }
let(:question_id) { question.id }
let(:params) do
{
question: question_id
}
end
subject { delete(:destroy, params: params) }
context "when user is signed in" do
shared_examples "deletes the question" do
let(:expected_response) do
{
"success" => true,
"status" => "okay",
"message" => anything
}
end
it "deletes the question" do
question # ensure we already have it in the db
expect { subject }.to(change { Question.count }.by(-1))
end
include_examples "returns the expected response"
end
before(:each) { sign_in(user) }
context "when the question exists and was made by the current user" do
include_examples "deletes the question"
end
context "when the question exists and was not made by the current user" do
let(:question_user) { FactoryBot.create(:user) }
include_examples "does not delete the question", "not_authorized"
%i[moderator administrator].each do |privileged_role|
context "when the current user is a #{privileged_role}" do
around do |example|
user.add_role privileged_role
example.run
user.remove_role privileged_role
end
include_examples "deletes the question"
end
end
end
context "when the question exists and was not made by any registered user" do
let(:question_user) { nil }
include_examples "does not delete the question", "not_authorized"
%i[moderator administrator].each do |privileged_role|
context "when the current user is a #{privileged_role}" do
around do |example|
user.add_role privileged_role
example.run
user.remove_role privileged_role
end
include_examples "deletes the question"
end
end
end
context "when the question does not exist" do
let(:question_id) { "sonic_the_hedgehog" }
include_examples "does not delete the question", "not_found"
end
end
context "when user is not signed in" do
include_examples "does not delete the question", "err"
end
end
end

View File

@ -0,0 +1,151 @@
# frozen_string_literal: true
require "rails_helper"
describe Ajax::ReportController, :ajax_controller, type: :controller do
describe "#create" do
let(:params) do
{
id: id,
type: type,
reason: reason
}
end
subject { post(:create, params: params) }
context "when user is signed in" do
shared_examples "reporting an item" do |type|
let(:type) { type }
let(:id) { object.id }
context "when #{type} exists" do
before { object }
context "when reason is empty" do
let(:reason) { "" }
it "creates a report of type Reports::#{type.capitalize}" do
report_klass = "Reports::#{type.capitalize}".constantize
expect { subject }.to(change { report_klass.count }.by(1))
expect(report_klass.last.target).to eq(object)
expect(report_klass.last.reason).to be_blank
end
include_examples "returns the expected response"
end
context "when reason is not empty" do
let(:reason) { "I don't like this" }
it "creates a report of type Reports::#{type.capitalize}" do
report_klass = "Reports::#{type.capitalize}".constantize
expect { subject }.to(change { report_klass.count }.by(1))
expect(report_klass.last.target).to eq(object)
expect(report_klass.last.reason).to eq(reason)
end
include_examples "returns the expected response"
end
end
context "when #{type} does not exist" do
let(:id) { "nonexistent" }
let(:expected_response) do
{
"success" => false,
"status" => "not_found",
"message" => anything,
}
end
context "when reason is empty" do
let(:reason) { "" }
it "does not create a report" do
expect { subject }.not_to(change { Report.count })
end
include_examples "returns the expected response"
end
context "when reason is not empty" do
let(:reason) { "I don't like this" }
it "does not create a report" do
expect { subject }.not_to(change { Report.count })
end
include_examples "returns the expected response"
end
end
end
let(:target_user) { FactoryBot.create(:user) }
let(:expected_response) do
{
"success" => true,
"status" => "okay",
"message" => anything,
}
end
before(:each) { sign_in(user) }
it_behaves_like "reporting an item", "user" do
let(:object) { target_user }
let(:id) { object.screen_name }
end
it_behaves_like "reporting an item", "question" do
let(:object) { FactoryBot.create(:question, user: target_user) }
end
it_behaves_like "reporting an item", "answer" do
let(:object) { FactoryBot.create(:answer, user: target_user) }
end
it_behaves_like "reporting an item", "comment" do
let(:answer) { FactoryBot.create(:answer, user: target_user) }
let(:object) { FactoryBot.create(:comment, user: target_user, answer: answer) }
end
context "when type is anything else" do
let(:id) { "whatever" }
let(:type) { "whatever" }
let(:reason) { "whatever" }
let(:expected_response) do
{
"success" => false,
"status" => "err",
"message" => anything,
}
end
it "does not create a report" do
expect { subject }.not_to(change { Report.count })
end
include_examples "returns the expected response"
end
end
context "when user is not signed in" do
let(:id) { "peter_zwegat" }
let(:type) { "user" }
let(:reason) { "I'm broke now thanks to this bloke" }
let(:expected_response) do
{
"success" => false,
"status" => "err",
"message" => anything
}
end
it "does not create a report" do
expect { subject }.not_to(change { Report.count })
end
include_examples "returns the expected response"
end
end
end

View File

@ -0,0 +1,292 @@
# coding: utf-8
# frozen_string_literal: true
require "rails_helper"
describe Ajax::SmileController, :ajax_controller, type: :controller do
describe "#create" do
let(:params) do
{
id: answer_id
}.compact
end
let(:answer) { FactoryBot.create(:answer, user: user) }
subject { post(:create, params: params) }
context "when user is signed in" do
before(:each) { sign_in(user) }
context "when answer exists" do
let(:answer_id) { answer.id }
let(:expected_response) do
{
"success" => true,
"status" => "okay",
"message" => anything
}
end
it "creates a smile to the answer" do
expect { subject }.to(change { Smile.count }.by(1))
expect(answer.reload.smiles.ids).to include(Smile.last.id)
end
include_examples "returns the expected response"
end
context "when answer does not exist" do
let(:answer_id) { "nein!" }
let(:expected_response) do
{
"success" => false,
"status" => anything,
"message" => anything
}
end
it "does not create a smile" do
expect { subject }.not_to(change { Smile.count })
end
include_examples "returns the expected response"
end
context "when some parameters are missing" do
let(:answer_id) { nil }
let(:expected_response) do
{
"success" => false,
"status" => "parameter_error",
"message" => anything
}
end
include_examples "returns the expected response"
end
end
context "when user is not signed in" do
let(:answer_id) { answer.id }
let(:expected_response) do
{
"success" => false,
"status" => "fail",
"message" => anything
}
end
include_examples "returns the expected response"
end
end
describe "#destroy" do
let(:answer) { FactoryBot.create(:answer, user: user) }
let(:smile) { FactoryBot.create(:smile, user: user, answer: answer) }
let(:answer_id) { answer.id }
let(:params) do
{
id: answer_id
}
end
subject { delete(:destroy, params: params) }
context "when user is signed in" do
before(:each) { sign_in(user) }
context "when the smile exists" do
# ensure we already have it in the db
before(:each) { smile }
let(:expected_response) do
{
"success" => true,
"status" => "okay",
"message" => anything
}
end
it "deletes the smile" do
expect { subject }.to(change { Smile.count }.by(-1))
end
include_examples "returns the expected response"
end
context "when the smile does not exist" do
let(:answer_id) { "sonic_the_hedgehog" }
let(:expected_response) do
{
"success" => false,
"status" => anything,
"message" => anything
}
end
include_examples "returns the expected response"
end
end
context "when user is not signed in" do
let(:expected_response) do
{
"success" => false,
"status" => "fail",
"message" => anything
}
end
include_examples "returns the expected response"
end
end
describe "#create_comment" do
let(:params) do
{
id: comment_id
}.compact
end
let(:answer) { FactoryBot.create(:answer, user: user) }
let(:comment) { FactoryBot.create(:comment, user: user, answer: answer) }
subject { post(:create_comment, params: params) }
context "when user is signed in" do
before(:each) { sign_in(user) }
context "when comment exists" do
let(:comment_id) { comment.id }
let(:expected_response) do
{
"success" => true,
"status" => "okay",
"message" => anything
}
end
it "creates a smile to the comment" do
expect { subject }.to(change { CommentSmile.count }.by(1))
expect(comment.reload.smiles.ids).to include(CommentSmile.last.id)
end
include_examples "returns the expected response"
end
context "when comment does not exist" do
let(:comment_id) { "nein!" }
let(:expected_response) do
{
"success" => false,
"status" => anything,
"message" => anything
}
end
it "does not create a smile" do
expect { subject }.not_to(change { CommentSmile.count })
end
include_examples "returns the expected response"
end
context "when some parameters are missing" do
let(:comment_id) { nil }
let(:expected_response) do
{
"success" => false,
"status" => "parameter_error",
"message" => anything
}
end
include_examples "returns the expected response"
end
end
context "when user is not signed in" do
let(:comment_id) { comment.id }
let(:expected_response) do
{
"success" => false,
"status" => "fail",
"message" => anything
}
end
include_examples "returns the expected response"
end
end
describe "#destroy_comment" do
let(:answer) { FactoryBot.create(:answer, user: user) }
let(:comment) { FactoryBot.create(:comment, user: user, answer: answer) }
let(:comment_smile) { FactoryBot.create(:comment_smile, user: user, comment: comment) }
let(:comment_id) { comment.id }
let(:params) do
{
id: comment_id
}
end
subject { delete(:destroy_comment, params: params) }
context "when user is signed in" do
before(:each) { sign_in(user) }
context "when the smile exists" do
# ensure we already have it in the db
before(:each) { comment_smile }
let(:expected_response) do
{
"success" => true,
"status" => "okay",
"message" => anything
}
end
it "deletes the smile" do
expect { subject }.to(change { CommentSmile.count }.by(-1))
end
include_examples "returns the expected response"
end
context "when the smile does not exist" do
let(:answer_id) { "sonic_the_hedgehog" }
let(:expected_response) do
{
"success" => false,
"status" => anything,
"message" => anything
}
end
include_examples "returns the expected response"
end
end
context "when user is not signed in" do
let(:expected_response) do
{
"success" => false,
"status" => "fail",
"message" => anything
}
end
include_examples "returns the expected response"
end
end
end

View File

@ -0,0 +1,160 @@
# frozen_string_literal: true
require "rails_helper"
describe Ajax::SubscriptionController, :ajax_controller, type: :controller do
# need to use a different user here, as after a create the user owning the
# answer is automatically subscribed to it
let(:answer_user) { FactoryBot.create(:user) }
let(:answer) { FactoryBot.create(:answer, user: answer_user) }
describe "#subscribe" do
let(:params) do
{
answer: answer_id
}
end
subject { post(:subscribe, params: params) }
context "when user is signed in" do
before(:each) { sign_in(user) }
context "when answer exists" do
let(:answer_id) { answer.id }
let(:expected_response) do
{
"success" => true,
"status" => 418,
"message" => anything
}
end
context "when subscription does not exist" do
it "creates a subscription on the answer" do
expect { subject }.to(change { answer.subscriptions.count }.by(1))
expect(answer.subscriptions.where(is_active: true).map { |s| s.user.id }.sort).to eq([answer_user.id, user.id].sort)
end
include_examples "returns the expected response"
end
context "when subscription already exists" do
before(:each) { Subscription.subscribe(user, answer) }
it "does not modify the answer's subscriptions" do
expect { subject }.to(change { answer.subscriptions.count }.by(0))
expect(answer.subscriptions.where(is_active: true).map { |s| s.user.id }.sort).to eq([answer_user.id, user.id].sort)
end
include_examples "returns the expected response"
end
end
context "when answer does not exist" do
let(:answer_id) { "Bielefeld" }
let(:expected_response) do
{
"success" => false,
"status" => "not_found",
"message" => anything
}
end
it "does not create a new subscription" do
expect { subject }.not_to(change { Subscription.count })
end
include_examples "returns the expected response"
end
end
context "when user is not signed in" do
let(:answer_id) { answer.id }
it "redirects to somewhere else, apparently" do
subject
expect(response).to be_a_redirect
end
end
end
describe "#unsubscribe" do
let(:params) do
{
answer: answer_id
}
end
subject { post(:unsubscribe, params: params) }
context "when user is signed in" do
before(:each) { sign_in(user) }
context "when answer exists" do
let(:answer_id) { answer.id }
let(:expected_response) do
{
"success" => true,
"status" => 418,
"message" => anything
}
end
context "when subscription exists" do
before(:each) { Subscription.subscribe(user, answer) }
it "removes an active subscription from the answer" do
expect { subject }.to(change { answer.subscriptions.where(is_active: true).count }.by(-1))
expect(answer.subscriptions.where(is_active: true).map { |s| s.user.id }.sort).to eq([answer_user.id].sort)
end
include_examples "returns the expected response"
end
context "when subscription does not exist" do
let(:expected_response) do
{
"success" => false,
"status" => 418,
"message" => anything
}
end
it "does not modify the answer's subscriptions" do
expect { subject }.to(change { answer.subscriptions.count }.by(0))
expect(answer.subscriptions.where(is_active: true).map { |s| s.user.id }.sort).to eq([answer_user.id].sort)
end
include_examples "returns the expected response"
end
end
context "when answer does not exist" do
let(:answer_id) { "Bielefeld" }
let(:expected_response) do
{
"success" => false,
"status" => "not_found",
"message" => anything
}
end
it "does not create a new subscription" do
expect { subject }.not_to(change { Subscription.count })
end
include_examples "returns the expected response"
end
end
context "when user is not signed in" do
let(:answer_id) { answer.id }
it "redirects to somewhere else, apparently" do
subject
expect(response).to be_a_redirect
end
end
end
end

12
spec/factories/comment.rb Normal file
View File

@ -0,0 +1,12 @@
# frozen_string_literal: true
FactoryBot.define do
factory :comment do
transient do
answer_content { Faker::Lorem.sentence }
end
content { Faker::Lorem.sentence }
answer { FactoryBot.build(:answer, content: answer_content) }
end
end

View File

@ -0,0 +1,8 @@
# frozen_string_literal: true
FactoryBot.define do
factory :comment_smile do
user { FactoryBot.build(:user) }
comment { FactoryBot.build(:comment) }
end
end

18
spec/factories/group.rb Normal file
View File

@ -0,0 +1,18 @@
# frozen_string_literal: true
FactoryBot.define do
factory :group do
sequence(:display_name) { |i| "#{Faker::Internet.username(specifier: 0..12, separators: %w[_])}#{i}" }
user { FactoryBot.build(:user) }
transient do
members { [] }
end
after(:create) do |group, evaluator|
evaluator.members.each do |member|
GroupMember.create(group_id: group.id, user_id: member.id)
end
end
end
end

8
spec/factories/smile.rb Normal file
View File

@ -0,0 +1,8 @@
# frozen_string_literal: true
FactoryBot.define do
factory :smile do
user { FactoryBot.build(:user) }
answer { FactoryBot.build(:answer) }
end
end

View File

@ -12,6 +12,7 @@ require "rspec/its"
require "devise"
require "capybara/rails"
require "capybara/rspec"
require "rspec-sidekiq"
# Requires supporting ruby files with custom matchers and macros, etc, in
# spec/support/ and its subdirectories. Files matching `spec/**/*_spec.rb` are

View File

@ -0,0 +1,15 @@
# frozen_string_literal: true
RSpec.shared_context "AjaxController context" do
let(:user) { FactoryBot.create(:user) }
shared_examples "returns the expected response" do
it "returns the expected response" do
expect(JSON.parse(subject.body)).to match(expected_response)
end
end
end
RSpec.configure do |c|
c.include_context "AjaxController context", ajax_controller: true
end

View File

@ -1,6 +1,9 @@
# frozen_string_literal: true
require "simplecov"
# require "simplecov-rcov"
# SimpleCov.formatter = SimpleCov::Formatter::RcovFormatter
SimpleCov.start "rails"
if ENV.key?("GITHUB_ACTIONS")
require "simplecov-cobertura"
SimpleCov.formatter = SimpleCov::Formatter::CoberturaFormatter
end