bigcommerce/gruf-rspec

View on GitHub
lib/gruf/rspec.rb

Summary

Maintainability
A
0 mins
Test Coverage
F
47%
# frozen_string_literal: true

# Copyright (c) 2018-present, BigCommerce Pty. Ltd. All rights reserved
#
# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
# documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit
# persons to whom the Software is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
# Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#
require 'rspec/core'
require 'rspec/expectations'

# use Zeitwerk to lazily autoload all the files in the lib directory
require 'zeitwerk'
lib_path = File.dirname(__dir__)
loader = ::Zeitwerk::Loader.new
loader.tag = 'gruf-rspec'
loader.inflector = ::Zeitwerk::GemInflector.new(__FILE__)
loader.ignore("#{lib_path}/gruf/rspec/railtie.rb")
loader.push_dir(lib_path)
loader.setup

##
# Base gruf module
#
module Gruf
  ##
  # Base gruf-rspec module
  #
  module Rspec
    extend Gruf::Rspec::Configuration
  end
end

Gruf::Rspec.reset # initial reset

# Attempt to load railtie if we're in a rails environment. This assists with autoloading in a rails rspec context
begin
  require 'rails'
rescue LoadError
  nil
end
require_relative 'rspec/railtie' if defined?(::Rails::Railtie)

RSpec.configure do |config|
  config.include Gruf::Rspec::Helpers

  config.define_derived_metadata(file_path: Regexp.new(Gruf::Rspec.rpc_spec_path)) do |metadata|
    metadata[:type] = :gruf_controller
  end

  config.before(:each, type: :gruf_controller) do
    define_singleton_method :run_rpc do |method_name, request, active_call_options: {}, &block|
      @gruf_controller = described_class.new(
        method_key: method_name.to_s.underscore.to_sym,
        service: grpc_bound_service,
        rpc_desc: grpc_bound_service.rpc_descs[method_name.to_sym],
        active_call: grpc_active_call(active_call_options),
        message: request
      )
      resp = @gruf_controller.call(@gruf_controller.request.method_key)
      block.call(resp) if block.is_a?(Proc)
      resp
    end

    define_singleton_method :grpc_bound_service do
      described_class.bound_service
    end

    define_singleton_method :gruf_controller do
      @gruf_controller
    end
  end
end

RSpec::Matchers.define :raise_rpc_error do |expected_error_class|
  supports_block_expectations

  def with_serialized(&block)
    @serialized_block = block
    self
  end

  match do |rpc_call_proc|
    @error_matcher = Gruf::Rspec::ErrorMatcher.new(
      rpc_call_proc: rpc_call_proc,
      expected_error_class: expected_error_class,
      serialized_block: @serialized_block
    )
    @error_matcher.valid?
  end

  failure_message do |_|
    @error_matcher.error_message
  end
end

RSpec::Matchers.define :be_a_successful_rpc do |_|
  match do |actual|
    if !gruf_controller || actual.is_a?(GRPC::BadStatus) || actual.is_a?(GRPC::Core::CallError)
      false
    else
      method_key = gruf_controller.request.method_key.to_s.camelcase.to_sym
      expected_class = gruf_controller.class.bound_service.rpc_descs[method_key].output
      expected_class == actual.class
    end
  end
end