discourse/spec/lib/service_spec.rb
Loïc Guitaut 133a648d9b DEV: Fix policy classes delegating their #call method in services
There’s currently a bug when using a dedicated class as a policy in
services: if that class delegates its `#call` method (to an underlying
strategy object for example), then an error will be raised saying steps
aren’t allowed to provide default parameters.

This should not happen, and this patch fixes that issue.
2024-12-18 09:59:40 +01:00

126 lines
3.2 KiB
Ruby

# frozen_string_literal: true
RSpec.describe Service do
let(:service_class) { Class.new { include Service::Base } }
describe "Steps" do
describe "Model step" do
context "when providing default values to step implementation" do
before do
service_class.class_eval do
model :my_model
def fetch_my_model(default_arg: 2)
true
end
end
end
it "raises an error" do
expect { service_class.call }.to raise_error(/In model 'my_model': default values/)
end
end
end
describe "Policy step" do
context "when providing default values to step implementation" do
before do
service_class.class_eval do
policy :my_policy
def my_policy(default_arg: 2)
true
end
end
end
it "raises an error" do
expect { service_class.call }.to raise_error(/In policy 'my_policy': default values/)
end
end
context "when providing a class which delegates its `#call` method" do
before do
service_class.class_eval do
class MyPolicy < Service::PolicyBase
class MyStrategy
def call
end
def reason
end
end
attr_reader :strategy
delegate :call, :reason, to: :strategy
def initialize(*)
@strategy = MyStrategy.new
end
end
policy :my_policy, class_name: MyPolicy
end
end
it "does not raise an error" do
expect { service_class.call }.not_to raise_error
end
end
end
describe "Generic step" do
context "when providing default values to step implementation" do
before do
service_class.class_eval do
step :generic_step
def generic_step(default_arg: 2)
true
end
end
end
it "raises an error" do
expect { service_class.call }.to raise_error(/In step 'generic_step': default values/)
end
end
end
end
describe "Parameters handling" do
subject(:result) { service_class.call(**args) }
context "when calling the service without any params" do
let(:args) { {} }
it "instantiate a default params object" do
expect(result[:params]).not_to be_nil
end
end
context "when calling the service with params" do
let(:args) { { params: { param1: "one" } } }
context "when there is no `params` step defined" do
it "allows accessing `params` through methods" do
expect(result[:params].param1).to eq("one")
end
it "returns nothing for a non-existent key" do
expect(result[:params].non_existent_key).to be_nil
end
end
context "when there is a `params` step defined" do
before { service_class.class_eval { params { attribute :param1 } } }
it "returns the contract as the params object" do
expect(result[:params]).to be_a(Service::ContractBase)
end
end
end
end
end