require "test_helper" module Inspect def inspect "<#{self.class.to_s.split("::").last} @model=#{@model}>" end alias_method :to_s, :inspect end class OperationSetupParamsTest < MiniTest::Spec class OperationSetupParam < Trailblazer::Operation def process(params) @model = params end def setup_params!(params) params.merge!(garrett: "Rocks!") end include Inspect end # allows you changing params in #setup_params!. it { OperationSetupParam.run({valid: true}).to_s.must_equal "[true, true, :garrett=>\"Rocks!\"}>]" } end # Operation#model. class OperationModelTest < MiniTest::Spec class Operation < Trailblazer::Operation def process(params) end def model!(params) params end end # #model. it { Operation.(Object).model.must_equal Object } end class OperationRunTest < MiniTest::Spec class Operation < Trailblazer::Operation # allow providing your own contract. self.contract_class = class Contract def initialize(*) end def validate(params) return true if params == "yes, true" false end def errors Struct.new(:to_s).new("Op just calls #to_s on Errors!") end self end def process(params) model = Object validate(params, model) end include Inspect end # contract is inferred from self::contract_class. # ::run returns result set when run without block. it { Operation.run("not true").to_s.must_equal %{[false, ]} } it { Operation.run("yes, true").to_s.must_equal %{[true, ]} } # ::call raises exception when invalid. it do exception = assert_raises(Trailblazer::Operation::InvalidContract) { Operation.("not true") } exception.message.must_equal "Op just calls #to_s on Errors!" end # return operation when ::call it { Operation.("yes, true").to_s.must_equal %{} } # #[] is alias for .() it { Operation["yes, true"].to_s.must_equal %{} } # ::run with block returns operation. # valid executes block. it "block" do outcome = nil res = Operation.run("yes, true") do outcome = "true" end outcome.must_equal "true" # block was executed. res.to_s.must_equal %{} end # invalid doesn't execute block. it "block, invalid" do outcome = nil res = Operation.run("no, not true, false") do outcome = "true" end outcome.must_equal nil # block was _not_ executed. res.to_s.must_equal %{} end # block yields operation it do outcome = nil res = Operation.run("yes, true") do |op| outcome = op end outcome.to_s.must_equal %{} # block was executed. res.to_s.must_equal %{} end # # Operation#contract returns @contract it { Operation.("yes, true").contract.class.to_s.must_equal "OperationRunTest::Operation::Contract" } end class OperationTest < MiniTest::Spec class Operation < Trailblazer::Operation def process(params) validate(Object, params) end end # contract is retrieved from ::contract_class. it { assert_raises(NoMethodError) { Operation.run({}) } } # TODO: if you call #validate without defining a contract, the error is quite cryptic. # test #invalid! class OperationWithoutValidateCall < Trailblazer::Operation def process(params) params || invalid!(params) end include Inspect end # ::run it { OperationWithoutValidateCall.run(true).to_s.must_equal %{[true, ]} } # invalid. it { OperationWithoutValidateCall.run(false).to_s.must_equal %{[false, ]} } # #validate yields contract when valid class OperationWithValidateBlock < Trailblazer::Operation self.contract_class = class Contract def initialize(*) end def validate(params) params end self end def process(params) validate(params, Object.new) do |c| @secret_contract = c.class end end attr_reader :secret_contract end it { OperationWithValidateBlock.run(false).last.secret_contract.must_equal nil } it { OperationWithValidateBlock.(true).secret_contract.must_equal OperationWithValidateBlock::Contract } # test validate wit if/else class OperationWithValidateAndIf < Trailblazer::Operation self.contract_class = class Contract def initialize(*) end def validate(params) params end self end def process(params) if validate(params, Object.new) @secret_contract = contract.class else @secret_contract = "so wrong!" end end attr_reader :secret_contract end it { OperationWithValidateAndIf.run(false).last.secret_contract.must_equal "so wrong!" } it { OperationWithValidateAndIf.(true).secret_contract.must_equal OperationWithValidateAndIf::Contract } # unlimited arguments for ::run and friends. # class OperationReceivingLottaArguments < Trailblazer::Operation # def process(model, params) # @model = [model, params] # end # include Inspect # end # it { OperationReceivingLottaArguments.run(Object, {}).to_s.must_equal %{[true, ]} } # ::present only runs #setup! which runs #model!. class ContractOnlyOperation < Trailblazer::Operation self.contract_class = class Contract def initialize(model) @_model = model end attr_reader :_model self end def model!(params) Object end def process(params) raise "This is not run!" end end it { ContractOnlyOperation.present({}).contract._model.must_equal Object } end class OperationErrorsTest < MiniTest::Spec class Operation < Trailblazer::Operation contract do property :title, validates: {presence: true} end def process(params) validate(params, OpenStruct.new) {} end end it do res, op = Operation.run({}) op.errors.to_s.must_equal "{:title=>[\"can't be blank\"]}" end end