Refactor custom fields into services

This commit is contained in:
Oliver Günther
2020-09-03 08:38:57 +02:00
parent a169138f40
commit 41f8dbaff2
13 changed files with 863 additions and 22 deletions
@@ -0,0 +1,43 @@
#-- encoding: UTF-8
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2020 the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2017 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See docs/COPYRIGHT.rdoc for more details.
#++
module RequiresAdminGuard
extend ActiveSupport::Concern
included do
validate :validate_admin_only
def validate_admin_only
unless user.admin?
errors.add :base, :error_unauthorized
end
end
end
end
@@ -0,0 +1,53 @@
#-- encoding: UTF-8
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2020 the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2017 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See docs/COPYRIGHT.rdoc for more details.
#++
module CustomFields
class BaseContract < ::ModelContract
include RequiresAdminGuard
attribute :editable
attribute :type
attribute :field_format
attribute :is_filter
attribute :is_for_all
attribute :is_required
attribute :max_length
attribute :min_length
attribute :name
attribute :possible_values
attribute :regexp
attribute :searchable
attribute :visible
attribute :default_value
attribute :possible_values
attribute :multi_value
attribute :content_right_to_left
end
end
@@ -0,0 +1,34 @@
#-- encoding: UTF-8
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2020 the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2017 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See docs/COPYRIGHT.rdoc for more details.
#++
module CustomFields
class CreateContract < BaseContract
end
end
@@ -0,0 +1,34 @@
#-- encoding: UTF-8
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2020 the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2017 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See docs/COPYRIGHT.rdoc for more details.
#++
module CustomFields
class UpdateContract < BaseContract
end
end
+24 -22
View File
@@ -47,17 +47,26 @@ class CustomFieldsController < ApplicationController
end
def new
@custom_field = careful_new_custom_field permitted_params.custom_field_type
@custom_field = new_custom_field
if @custom_field.nil?
flash[:error] = 'Invalid CF type'
redirect_to action: :index
end
end
def create
@custom_field = careful_new_custom_field permitted_params.custom_field_type, get_custom_field_params
call = ::CustomFields::CreateService
.new(user: current_user)
.call(get_custom_field_params.merge(type: permitted_params.custom_field_type))
if @custom_field.save
flash[:notice] = l(:notice_successful_create)
call_hook(:controller_custom_fields_new_after_save, custom_field: @custom_field)
redirect_to custom_fields_path(tab: @custom_field.class.name)
if call.success?
flash[:notice] = t(:notice_successful_create)
call_hook(:controller_custom_fields_new_after_save, custom_field: call.result)
redirect_to custom_fields_path(tab: call.result.class.name)
else
flash[:error] = call.message || I18n.t('notice_internal_server_error')
@custom_field = call.result || new_custom_field
render action: 'new'
end
end
@@ -65,13 +74,16 @@ class CustomFieldsController < ApplicationController
def edit; end
def update
@custom_field.attributes = get_custom_field_params
call = ::CustomFields::UpdateService
.new(user: current_user, model: @custom_field)
.call(get_custom_field_params)
if @custom_field.save
if call.success?
flash[:notice] = t(:notice_successful_update)
call_hook(:controller_custom_fields_edit_after_save, custom_field: @custom_field)
redirect_back_or_default edit_custom_field_path(id: @custom_field.id)
else
flash[:error] = call.message || I18n.t('notice_internal_server_error')
render action: 'edit'
end
end
@@ -101,6 +113,10 @@ class CustomFieldsController < ApplicationController
private
def new_custom_field
::CustomFields::CreateService.careful_new_custom_field(permitted_params.custom_field_type)
end
def get_custom_field_params
custom_field_params = permitted_params.custom_field
@@ -133,20 +149,6 @@ class CustomFieldsController < ApplicationController
end
end
def careful_new_custom_field(type, params = {})
cf = begin
if type.to_s =~ /.+CustomField\z/
klass = type.to_s.constantize
klass.new(params) if klass.ancestors.include? CustomField
end
rescue NameError => e
Rails.logger.error "#{e.message}:\n#{e.backtrace.join("\n")}"
nil
end
redirect_to custom_fields_path unless cf
cf
end
def find_custom_field
@custom_field = CustomField.find(params[:id])
rescue ActiveRecord::RecordNotFound
@@ -0,0 +1,62 @@
#-- encoding: UTF-8
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2020 the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2017 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See docs/COPYRIGHT.rdoc for more details.
#++
module CustomFields
class CreateService < ::BaseServices::Create
def self.careful_new_custom_field(type)
if type.to_s =~ /.+CustomField\z/
klass = type.to_s.constantize
klass.new if klass.ancestors.include? CustomField
end
rescue NameError => e
Rails.logger.error "#{e.message}:\n#{e.backtrace.join("\n")}"
nil
end
def perform(params)
super
rescue StandardError => e
ServiceResult.new(success: false, message: e.message)
end
def instance(params)
cf = self.class.careful_new_custom_field(params[:type])
raise ArgumentError.new("Invalid CF type") unless cf
cf
end
private
def add_cf_to_visible_columns(id)
Setting.enabled_projects_columns = (Setting.enabled_projects_columns + ["cf_#{id}"]).uniq
end
end
end
@@ -0,0 +1,34 @@
#-- encoding: UTF-8
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2020 the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2017 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See docs/COPYRIGHT.rdoc for more details.
#++
module CustomFields
class SetAttributesService < ::BaseServices::SetAttributes
end
end
@@ -0,0 +1,34 @@
#-- encoding: UTF-8
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2020 the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2017 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See docs/COPYRIGHT.rdoc for more details.
#++
module CustomFields
class UpdateService < ::BaseServices::Update
end
end
@@ -0,0 +1,56 @@
#-- encoding: UTF-8
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2020 the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2017 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See docs/COPYRIGHT.rdoc for more details.
#++
require 'spec_helper'
describe CustomFields::CreateContract do
let(:cf) { FactoryBot.build :project_custom_field }
let(:contract) do
described_class.new(cf, current_user, options: { changed_by_system: [] })
end
describe 'as admin' do
let(:current_user) { FactoryBot.build_stubbed :admin }
it 'validates the contract' do
expect(contract.validate).to eq(true)
end
end
describe 'as regular user' do
let(:current_user) { FactoryBot.build_stubbed :user }
it 'invalidates the contract' do
expect(contract.validate).to eq(false)
expect(contract.errors.symbols_for(:base))
.to match_array [:error_unauthorized]
end
end
end
@@ -0,0 +1,56 @@
#-- encoding: UTF-8
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2020 the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2017 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See docs/COPYRIGHT.rdoc for more details.
#++
require 'spec_helper'
describe CustomFields::UpdateContract do
let(:cf) { FactoryBot.build :project_custom_field }
let(:contract) do
described_class.new(cf, current_user, options: { changed_by_system: [] })
end
describe 'as admin' do
let(:current_user) { FactoryBot.build_stubbed :admin }
it 'validates the contract' do
expect(contract.validate).to eq(true)
end
end
describe 'as regular user' do
let(:current_user) { FactoryBot.build_stubbed :user }
it 'invalidates the contract' do
expect(contract.validate).to eq(false)
expect(contract.errors.symbols_for(:base))
.to match_array [:error_unauthorized]
end
end
end
@@ -0,0 +1,155 @@
#-- encoding: UTF-8
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2020 the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2017 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See docs/COPYRIGHT.rdoc for more details.
#++
require 'spec_helper'
describe CustomFields::CreateService, type: :model do
let(:user) { FactoryBot.build_stubbed(:user) }
let(:contract_class) do
double('contract_class', '<=': true)
end
let(:cf_valid) { true }
let(:instance) do
described_class.new(user: user, contract_class: contract_class)
end
let(:call_attributes) { { type: 'ProjectCustomField', name: 'Some name', field_format: 'string' } }
let(:set_attributes_success) do
true
end
let(:set_attributes_errors) do
double('set_attributes_errors')
end
let(:set_attributes_result) do
ServiceResult.new result: created_cf,
success: set_attributes_success,
errors: set_attributes_errors
end
let!(:created_cf) do
cf = FactoryBot.build_stubbed(:project_custom_field)
allow(ProjectCustomField)
.to receive(:new)
.and_return(cf)
allow(cf)
.to receive(:save)
.and_return(cf_valid)
cf
end
let!(:set_attributes_service) do
service = double('set_attributes_service_instance')
allow(CustomFields::SetAttributesService)
.to receive(:new)
.with(user: user,
model: created_cf,
contract_class: contract_class,
contract_options: {})
.and_return(service)
allow(service)
.to receive(:call)
.and_return(set_attributes_result)
end
let(:new_project_role) { FactoryBot.build_stubbed(:role) }
describe 'call' do
subject { instance.call(call_attributes) }
it 'is successful' do
expect(subject.success?).to be_truthy
end
it 'returns the result of the SetAttributesService' do
expect(subject)
.to eql set_attributes_result
end
it 'persists the custom_field' do
expect(created_cf)
.to receive(:save)
.and_return(cf_valid)
subject
end
it 'modifies the settings' do
subject
expect(Setting.enabled_projects_columns).to include "cf_#{created_cf.id}"
end
it 'creates the custom field' do
expect(subject.result).to eql created_cf
expect(subject.result).to be_kind_of(ProjectCustomField)
end
context 'if the SetAttributeService is unsuccessful' do
let(:set_attributes_success) { false }
it 'is unsuccessful' do
expect(subject.success?).to be_falsey
end
it 'returns the result of the SetAttributesService' do
expect(subject)
.to eql set_attributes_result
end
it 'does not persist the changes' do
expect(created_cf)
.to_not receive(:save)
subject
end
it "exposes the contract's errors" do
subject
expect(subject.errors).to eql set_attributes_errors
end
end
context 'when the cf is invalid' do
let(:cf_valid) { false }
it 'is unsuccessful' do
expect(subject.success?).to be_falsey
end
it "exposes the message's errors" do
subject
expect(subject.errors).to eql created_cf.errors
end
end
end
end
@@ -0,0 +1,126 @@
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2020 the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2017 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See docs/COPYRIGHT.rdoc for more details.
#++
require 'spec_helper'
describe CustomFields::SetAttributesService, type: :model do
let(:user) { FactoryBot.build_stubbed(:user) }
let(:contract_instance) do
contract = double('contract_instance')
allow(contract)
.to receive(:validate)
.and_return(contract_valid)
allow(contract)
.to receive(:errors)
.and_return(contract_errors)
contract
end
let(:contract_errors) { double('contract_errors') }
let(:contract_valid) { true }
let(:cf_valid) { true }
let(:instance) do
described_class.new(user: user,
model: cf_instance,
contract_class: contract_class,
contract_options: {})
end
let(:cf_instance) { ProjectCustomField.new }
let(:contract_class) do
allow(CustomFields::CreateContract)
.to receive(:new)
.with(cf_instance, user, options: { changed_by_system: [] })
.and_return(contract_instance)
CustomFields::CreateContract
end
let(:params) { {} }
before do
allow(cf_instance)
.to receive(:valid?)
.and_return(cf_valid)
end
subject { instance.call(params) }
it 'returns the cf instance as the result' do
expect(subject.result)
.to eql cf_instance
end
it 'is a success' do
is_expected
.to be_success
end
context 'with params' do
let(:params) do
{
name: 'Foobar'
}
end
let(:expected) do
{
name: 'Foobar'
}.with_indifferent_access
end
it 'assigns the params' do
subject
attributes_of_interest = cf_instance
.attributes
.slice(*expected.keys)
expect(attributes_of_interest)
.to eql(expected)
end
end
context 'with an invalid contract' do
let(:contract_valid) { false }
let(:expect_time_instance_save) do
expect(cf_instance)
.not_to receive(:save)
end
it 'returns failure' do
is_expected
.not_to be_success
end
it "returns the contract's errors" do
expect(subject.errors)
.to eql(contract_errors)
end
end
end
@@ -0,0 +1,152 @@
#-- encoding: UTF-8
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2020 the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2017 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See docs/COPYRIGHT.rdoc for more details.
#++
require 'spec_helper'
describe CustomFields::UpdateService, type: :model do
let(:user) { FactoryBot.build_stubbed(:user) }
let(:contract_class) do
double('contract_class', "<=": true)
end
let(:cf_valid) { true }
let(:instance) do
described_class.new(user: user,
model: cf,
contract_class: contract_class,
contract_options: {})
end
let(:call_attributes) { {} }
let(:set_attributes_success) do
true
end
let(:set_attributes_errors) do
double('set_attributes_errors')
end
let(:set_attributes_result) do
ServiceResult.new result: cf,
success: set_attributes_success,
errors: set_attributes_errors
end
let!(:cf) do
cf = FactoryBot.build_stubbed(:project_custom_field)
allow(cf)
.to receive(:save)
.and_return(cf_valid)
cf
end
let!(:set_attributes_service) do
service = double('set_attributes_service_instance')
allow(CustomFields::SetAttributesService)
.to receive(:new)
.with(user: user,
model: cf,
contract_class: contract_class,
contract_options: {})
.and_return(service)
allow(service)
.to receive(:call)
.and_return(set_attributes_result)
service
end
describe 'call' do
shared_examples_for 'service call' do
subject { instance.call(call_attributes) }
it 'is successful' do
expect(subject.success?).to be_truthy
end
it 'returns the result of the SetAttributesService' do
expect(subject)
.to eql set_attributes_result
end
it 'persists the custom field' do
expect(cf)
.to receive(:save)
.and_return(cf_valid)
subject
end
context 'when the SetAttributeService is unsuccessful' do
let(:set_attributes_success) { false }
it 'is unsuccessful' do
expect(subject.success?).to be_falsey
end
it 'returns the result of the SetAttributesService' do
expect(subject)
.to eql set_attributes_result
end
it 'does not persist the changes' do
expect(cf)
.to_not receive(:save)
subject
end
it "exposes the contract's errors" do
subject
expect(subject.errors).to eql set_attributes_errors
end
end
context 'when the custom field is invalid' do
let(:cf_valid) { false }
it 'is unsuccessful' do
expect(subject.success?).to be_falsey
end
it "exposes the custom field's errors" do
subject
expect(subject.errors).to eql cf.errors
end
end
end
context 'with parameters' do
let(:call_attributes) { { sticky: true } }
it_behaves_like 'service call'
end
end
end