Module: OpenapiFirst::Test

Defined in:
lib/openapi_first/test.rb,
lib/openapi_first/test/methods.rb,
lib/openapi_first/test/observe.rb,
lib/openapi_first/test/callable.rb,
lib/openapi_first/test/coverage.rb,
lib/openapi_first/test/registry.rb,
lib/openapi_first/test/configuration.rb,
lib/openapi_first/test/coverage/plan.rb,
lib/openapi_first/test/plain_helpers.rb,
lib/openapi_first/test/minitest_helpers.rb,
lib/openapi_first/test/coverage/route_task.rb,
lib/openapi_first/test/observer_middleware.rb,
lib/openapi_first/test/coverage/request_task.rb,
lib/openapi_first/test/coverage/response_task.rb,
lib/openapi_first/test/coverage/terminal_formatter.rb

Overview

Test integration

Defined Under Namespace

Modules: Callable, Coverage, Methods, MinitestHelpers, Observe, Observed, PlainHelpers Classes: AlreadyRegisteredError, Configuration, CoverageError, NotRegisteredError, ObserveError, ObserverMiddleware

Class Method Summary collapse

Class Method Details

.app(app, spec: nil, api: :default) ⇒ Object

Returns the Rack app wrapped with silent request, response validation You can use this if you want to track coverage via Test::Coverage, but don’t want to use the middlewares or manual request, response validation.



89
90
91
92
93
94
95
96
# File 'lib/openapi_first/test.rb', line 89

def self.app(app, spec: nil, api: :default)
  spec ||= self[api]
  Rack::Builder.app do
    use OpenapiFirst::Middlewares::ResponseValidation, spec:, raise_error: false
    use OpenapiFirst::Middlewares::RequestValidation, spec:, raise_error: false, error_response: false
    run app
  end
end

.configurationObject



28
29
30
# File 'lib/openapi_first/test.rb', line 28

def self.configuration
  @configuration ||= Configuration.new
end

.handle_exitObject



62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# File 'lib/openapi_first/test.rb', line 62

def self.handle_exit
  return unless configuration.report_coverage

  report_coverage(
    formatter: configuration.coverage_formatter,
    **configuration.coverage_formatter_options
  )
  return unless configuration.report_coverage == true

  coverage = Coverage.result.coverage
  return if coverage >= configuration.minimum_coverage

  puts "API Coverage fails with exit 2, because not all described requests and responses have been tested (#{coverage.round(4)}% covered)." # rubocop:disable Layout/LineLength

  exit 2
end

.installObject



98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
# File 'lib/openapi_first/test.rb', line 98

def self.install
  return if @installed

  OpenapiFirst.configure do |config|
    @after_request_validation = config.after_request_validation do |validated_request, oad|
      raise validated_request.error.exception if raise_request_error?(validated_request)

      Coverage.track_request(validated_request, oad)
    end

    @after_response_validation = config.after_response_validation do |validated_response, rack_request, oad|
      if validated_response.invalid? && raise_response_error?(validated_response)
        raise validated_response.error.exception
      end

      Coverage.track_response(validated_response, rack_request, oad)
    end
  end
  @installed = true
end

.minitest?(base) ⇒ Boolean

Returns:

  • (Boolean)


22
23
24
25
26
# File 'lib/openapi_first/test.rb', line 22

def self.minitest?(base)
  base.include?(::Minitest::Assertions)
rescue NameError
  false
end

.observe(app, api: :default) ⇒ Object

Inject request/response validation in a rack app class



18
19
20
# File 'lib/openapi_first/test.rb', line 18

def self.observe(app, api: :default)
  Observe.observe(app, api:)
end

.raise_request_error?(validated_request) ⇒ Boolean

Returns:

  • (Boolean)


119
120
121
122
123
124
# File 'lib/openapi_first/test.rb', line 119

def self.raise_request_error?(validated_request)
  return false if validated_request.valid?
  return false if validated_request.known?

  !configuration.ignore_unknown_requests
end

.raise_response_error?(validated_response) ⇒ Boolean

Returns:

  • (Boolean)


126
127
128
# File 'lib/openapi_first/test.rb', line 126

def self.raise_response_error?(validated_response)
  configuration.response_raise_error && !configuration.ignored_unknown_status.include?(validated_response.status)
end

.report_coverage(formatter: Coverage::TerminalFormatter) ⇒ Object

Print the coverage report

Parameters:

  • formatter (defaults to: Coverage::TerminalFormatter)

    A formatter to define the report.



82
83
84
# File 'lib/openapi_first/test.rb', line 82

def self.report_coverage(formatter: Coverage::TerminalFormatter, **)
  puts formatter.new(**).format(Coverage.result)
end

.setup {|configuration| ... } ⇒ Object

Sets up OpenAPI test coverage and OAD registration.

Yield Parameters:



34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# File 'lib/openapi_first/test.rb', line 34

def self.setup
  unless block_given?
    raise ArgumentError, "Please provide a block to #{self.class}.confgure to register you API descriptions"
  end

  install
  yield configuration

  configuration.registry.each { |name, oad| register(oad, as: name) }
  configuration.apps.each { |name, apps| apps.each { |app| observe(app, api: name) } }
  Coverage.start(skip_response: configuration.skip_response_coverage, skip_route: configuration.skip_coverage)

  if definitions.empty?
    raise NotRegisteredError,
          'No API descriptions have been registered. ' \
          'Please register your API description via ' \
          "OpenapiFirst::Test.setup { |test| test.register('myopenapi.yaml') }"
  end

  @exit_handler = method(:handle_exit)

  @setup ||= at_exit do
    # :nocov:
    @exit_handler&.call
    # :nocov:
  end
end

.uninstallObject



130
131
132
133
134
135
136
137
138
# File 'lib/openapi_first/test.rb', line 130

def self.uninstall
  configuration = OpenapiFirst.configuration
  configuration.hooks[:after_request_validation].delete(@after_request_validation)
  configuration.hooks[:after_response_validation].delete(@after_response_validation)
  definitions.clear
  @configuration = nil
  @installed = nil
  @exit_handler = nil
end