Update `Ajax::RelationshipController` to use usernames; Test Use Case

This commit is contained in:
Karina Kwiatek 2022-01-16 18:38:06 +01:00 committed by Karina Kwiatek
parent 4a1ed5394a
commit d609435f7c
11 changed files with 191 additions and 48 deletions

View File

@ -8,8 +8,10 @@ class Ajax::RelationshipController < AjaxController
before_action :authenticate_user! before_action :authenticate_user!
def create def create
params.require :target_user
UseCase::Relationship::Create.call( UseCase::Relationship::Create.call(
current_user_id: current_user.id, current_user: current_user.screen_name,
target_user: params[:target_user], target_user: params[:target_user],
type: params[:type] type: params[:type]
) )
@ -23,7 +25,7 @@ class Ajax::RelationshipController < AjaxController
def destroy def destroy
UseCase::Relationship::Destroy.call( UseCase::Relationship::Destroy.call(
current_user_id: current_user.id, current_user: current_user.screen_name,
target_user: params[:target_user], target_user: params[:target_user],
type: params[:type] type: params[:type]
) )

View File

@ -18,6 +18,30 @@ class AjaxController < ApplicationController
return_response return_response
end end
rescue_from(KeyError) do |e|
Sentry.capture_exception(e)
@response = {
success: false,
message: "Missing parameter: #{e.key}",
status: :err
}
return_response
end
rescue_from(Dry::Types::CoercionError, Dry::Types::ConstraintError) do |e|
Sentry.capture_exception(e)
@response = {
success: false,
message: "Invalid parameter",
status: :err
}
return_response
end
rescue_from(ActiveRecord::RecordNotFound) do |e| rescue_from(ActiveRecord::RecordNotFound) do |e|
Sentry.capture_exception(e) Sentry.capture_exception(e)

View File

@ -19,9 +19,6 @@ module Errors
end end
end end
class ParamIsMissing < BadRequest
end
class InvalidBanDuration < BadRequest class InvalidBanDuration < BadRequest
end end

View File

@ -4,4 +4,6 @@ require 'dry-types'
module Types module Types
include Dry.Types() include Dry.Types()
RelationshipTypes = Types::String.enum('follow')
end end

View File

@ -15,13 +15,5 @@ module UseCase
def call def call
raise NotImplementedError raise NotImplementedError
end end
private
def not_blank!(*args)
args.each do |arg|
raise Errors::ParamIsMissing if instance_variable_get("@#{arg}").blank?
end
end
end end
end end

View File

@ -6,15 +6,11 @@ require "errors"
module UseCase module UseCase
module Relationship module Relationship
class Create < UseCase::Base class Create < UseCase::Base
option :current_user_id, type: Types::Coercible::Integer option :current_user, type: Types::Coercible::String
option :target_user, type: Types::Coercible::String option :target_user, type: Types::Coercible::String
option :type, type: Types::Coercible::String option :type, type: Types::RelationshipTypes
def call def call
not_blank! :current_user_id, :target_user, :type
type = @type.downcase
ensure_type(type)
source_user = find_source_user source_user = find_source_user
target_user = find_target_user target_user = find_target_user
@ -31,19 +27,14 @@ module UseCase
private private
def ensure_type(type)
raise Errors::BadRequest unless type == 'follow'
end
def find_source_user def find_source_user
user_id = @current_user_id return current_user if current_user.is_a?(::User)
::User.find(user_id) ::User.find_by!(screen_name: current_user)
rescue ActiveRecord::RecordNotFound rescue ActiveRecord::RecordNotFound
raise Errors::UserNotFound raise Errors::UserNotFound
end end
def find_target_user def find_target_user
target_user = @target_user
return target_user if target_user.is_a?(::User) return target_user if target_user.is_a?(::User)
::User.find_by!(screen_name: target_user) ::User.find_by!(screen_name: target_user)
rescue ActiveRecord::RecordNotFound rescue ActiveRecord::RecordNotFound

View File

@ -6,15 +6,11 @@ require "errors"
module UseCase module UseCase
module Relationship module Relationship
class Destroy < UseCase::Base class Destroy < UseCase::Base
option :current_user_id, type: Types::Coercible::Integer option :current_user, type: Types::Coercible::String | Types.Instance(User)
option :target_user, type: Types::Coercible::String option :target_user, type: Types::Coercible::String | Types.Instance(User)
option :type, type: Types::Coercible::String option :type, type: Types::RelationshipTypes
def call def call
not_blank! :current_user_id, :target_user, :type
type = @type.downcase
ensure_type(type)
source_user = find_source_user source_user = find_source_user
target_user = find_target_user target_user = find_target_user
@ -31,20 +27,13 @@ module UseCase
private private
def ensure_type(type)
raise Errors::BadRequest unless type == 'follow'
end
def find_source_user def find_source_user
user_id = @current_user_id ::User.find_by!(screen_name: current_user)
::User.find(user_id)
rescue ActiveRecord::RecordNotFound rescue ActiveRecord::RecordNotFound
raise Errors::UserNotFound raise Errors::UserNotFound
end end
def find_target_user def find_target_user
target_user = @target_user
return target_user if target_user.is_a?(::User)
::User.find_by!(screen_name: target_user) ::User.find_by!(screen_name: target_user)
rescue ActiveRecord::RecordNotFound rescue ActiveRecord::RecordNotFound
raise Errors::UserNotFound raise Errors::UserNotFound

View File

@ -5,9 +5,9 @@ require "rails_helper"
describe Ajax::RelationshipController, type: :controller do describe Ajax::RelationshipController, type: :controller do
shared_examples_for "params is empty" do shared_examples_for "params is empty" do
let(:params) { { type: type } } # type is still required as it's part of the route let(:params) { }
include_examples "ajax does not succeed", "param is missing" include_examples "ajax does not succeed", "is required"
end end
let!(:user) { FactoryBot.create(:user) } let!(:user) { FactoryBot.create(:user) }
@ -60,7 +60,7 @@ describe Ajax::RelationshipController, type: :controller do
let(:type) { "dick" } let(:type) { "dick" }
it_behaves_like "params is empty" it_behaves_like "params is empty"
include_examples "ajax does not succeed", "Bad Request" include_examples "ajax does not succeed", "Invalid parameter"
end end
end end
end end
@ -85,8 +85,6 @@ describe Ajax::RelationshipController, type: :controller do
end end
end end
it_behaves_like "params is empty"
context "target_user does not exist" do context "target_user does not exist" do
let(:target_user) { "peter-witzig" } let(:target_user) { "peter-witzig" }
@ -114,8 +112,7 @@ describe Ajax::RelationshipController, type: :controller do
context "type = 'dick'" do context "type = 'dick'" do
let(:type) { "dick" } let(:type) { "dick" }
it_behaves_like "params is empty" include_examples "ajax does not succeed", "Invalid parameter"
include_examples "ajax does not succeed", "Bad Request"
end end
end end
end end

View File

@ -0,0 +1,70 @@
# frozen_string_literal: true
require "rails_helper"
require "use_case/relationship/create"
require "errors"
describe UseCase::Relationship::Create do
shared_examples_for "valid relationship type" do
context "current_user does not exist" do
let(:current_user) { "Schweinsbraten" }
include_examples "raises an error", Errors::UserNotFound
end
context "target_user does not exist" do
let(:target_user) { "peterwitzig" }
include_examples "raises an error", Errors::UserNotFound
end
context "target_user is current_user" do
let(:target_user) { user1.screen_name }
include_examples "raises an error", Errors::SelfAction
end
context "target_user is different from current_user" do
its([:status]) { is_expected.to eq(201) }
its([:extra]) { is_expected.to eq(target_user: user2) }
it "creates a relationship" do
expect { subject }.to change { Relationship.count }.by(1)
last_relationship = Relationship.last
expect(last_relationship.source_id).to eq(user1.id)
expect(last_relationship.target_id).to eq(user2.id)
end
end
end
let(:base_params) do
{
current_user: current_user,
target_user: target_user,
type: type
}
end
let(:params) { base_params }
let(:current_user) { user1.screen_name }
let(:target_user) { user2.screen_name }
let(:type) { nil }
# test data:
let!(:user1) { FactoryBot.create(:user, screen_name: "timallen") }
let!(:user2) { FactoryBot.create(:user, screen_name: "joehilyar") }
subject { described_class.call(params) }
context "type = 'follow'" do
let(:type) { "follow" }
include_examples "valid relationship type"
end
context "type = 'dick'" do
let(:type) { "dick" }
include_examples "raises an error", Dry::Types::ConstraintError
end
end

View File

@ -0,0 +1,73 @@
# frozen_string_literal: true
require "rails_helper"
require "use_case/relationship/destroy"
require "errors"
describe UseCase::Relationship::Destroy do
shared_examples_for "valid relationship type" do
its([:status]) { is_expected.to eq(204) }
its([:extra]) { is_expected.to eq(target_user: user2) }
context "relationship exists" do
before do
user1.public_send(type, user2)
end
it "destroys a relationship" do
expect { subject }.to change { Relationship.count }.by(-1)
end
end
context "relationship does not exist" do
it "does not change anything at all" do
expect { subject }.to change { Relationship.count }.by(0)
end
end
context "current_user does not exist" do
let(:current_user) { "Schweinsbraten" }
let(:target_user) { user2.screen_name }
include_examples "raises an error", Errors::UserNotFound
end
context "target_user does not exist" do
let(:current_user) { user1.screen_name }
let(:target_user) { "peterwitzig" }
include_examples "raises an error", Errors::UserNotFound
end
end
let(:base_params) do
{
current_user: current_user,
target_user: target_user,
type: type
}
end
let(:params) { base_params }
let(:current_user) { user1.screen_name }
let(:target_user) { user2.screen_name }
let(:type) { nil }
# test data:
let!(:user1) { FactoryBot.create(:user, screen_name: "timallen") }
let!(:user2) { FactoryBot.create(:user, screen_name: "joehilyar") }
subject { described_class.call(params) }
context "type = 'follow'" do
let(:type) { "follow" }
include_examples "valid relationship type"
end
context "type = 'dick'" do
let(:type) { "dick" }
include_examples "raises an error", Dry::Types::ConstraintError
end
end

View File

@ -1,5 +1,11 @@
# frozen_string_literal: true # frozen_string_literal: true
RSpec.shared_examples_for "raises an error" do |error_klass|
it "raises an error" do
expect { subject }.to raise_error(kind_of(error_klass))
end
end
RSpec.shared_examples_for "ajax does not succeed" do |part_of_error_message| RSpec.shared_examples_for "ajax does not succeed" do |part_of_error_message|
it "ajax does not succeed" do it "ajax does not succeed" do
subject subject