inferno_core 0.0.5 → 0.0.6

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9b643b7c720a9c79df5f329a9947110971076db80fe29f0b57928230cf348d1b
4
- data.tar.gz: e03d93994e7ad33e6e131969af5e31449390d36670d256a7d4bf43c1a9f80e4d
3
+ metadata.gz: 40769c54fd3854730eb080d1bb9e0755ff1551b00761968155d463dd3a6ce3e0
4
+ data.tar.gz: 8bbf5aaa2b09d7d5ff54e56a3ed33ce4e1f4df29fc53e3737f18e9972dc34f1e
5
5
  SHA512:
6
- metadata.gz: 4767e5dfd7f469a6eb70892fb9e5402f2a46708655810b91be0b3d1d9d1dfa839c3502678156f763f5e9a7999948a31013fb68e3a1d1605306d5297639f3b3c3
7
- data.tar.gz: 3adbce3ebb0786095826a114e4c6bc6a3ced2f9e560e2eaa6f6e6f6784bbf44c10ef25734f67bb097ee384b4cd6ab3296dc156e2c14cd5085bc64cb4fb27f575
6
+ metadata.gz: 35fe2518d3856f01b3358da68d862318784f05ab067b579d3ac62053dc1deed2be2895e3cfb5ff8efbc97e71d2495a11ebf66eadf23c5b8b4edb502707334838
7
+ data.tar.gz: 04e37af9daf40c01f0d4182f055e4ddf187d72f0e9d8a43e8858af5c549a4a60900d71f034a8e027e20f53a56f6b5e835bce1fca3a0b98870b07e0681793a2d9
@@ -5,7 +5,8 @@ module Inferno
5
5
  class Create < Controller
6
6
  include Import[
7
7
  test_sessions_repo: 'repositories.test_sessions',
8
- session_data_repo: 'repositories.session_data'
8
+ session_data_repo: 'repositories.session_data',
9
+ test_runs_repo: 'repositories.test_runs'
9
10
  ]
10
11
 
11
12
  PARAMS = [:test_session_id, :test_suite_id, :test_group_id, :test_id].freeze
@@ -14,8 +15,18 @@ module Inferno
14
15
  test_session = test_sessions_repo.find(params[:test_session_id])
15
16
 
16
17
  # if testsession.nil?
18
+ if test_runs_repo.active_test_run_for_session?(test_session.id)
19
+ self.status = 409
20
+ self.body = { error: 'Cannot run new test while another test run is in progress' }.to_json
21
+ return
22
+ end
17
23
 
18
24
  test_run = repo.create(create_params(params).merge(status: 'queued'))
25
+ missing_inputs = test_run.runnable.missing_inputs(params[:inputs])
26
+
27
+ raise Inferno::Exceptions::RequiredInputsNotFound, missing_inputs if missing_inputs.any?
28
+ raise Inferno::Exceptions::NotUserRunnableException unless test_run.runnable.user_runnable?
29
+
19
30
  self.body = serialize(test_run)
20
31
 
21
32
  params[:inputs]&.each do |input|
@@ -27,7 +38,9 @@ module Inferno
27
38
  end
28
39
 
29
40
  Jobs.perform(Jobs::ExecuteTestRun, test_run.id)
30
- rescue Sequel::ValidationFailed, Sequel::ForeignKeyConstraintViolation => e
41
+ rescue Sequel::ValidationFailed, Sequel::ForeignKeyConstraintViolation,
42
+ Inferno::Exceptions::RequiredInputsNotFound,
43
+ Inferno::Exceptions::NotUserRunnableException => e
31
44
  self.body = { errors: e.message }.to_json
32
45
  self.status = 422
33
46
  rescue StandardError => e
@@ -0,0 +1,11 @@
1
+ module Inferno
2
+ module Web
3
+ module Serializers
4
+ class HashValueExtractor < Blueprinter::Extractor
5
+ def extract(field_name, object, _local_options, _options)
6
+ object.send(field_name).values
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -4,11 +4,10 @@ module Inferno
4
4
  class Test < Serializer
5
5
  identifier :id
6
6
  field :title
7
- field :inputs
8
- field :outputs do |test, _options|
9
- test.outputs.map { |output| { name: output } }
10
- end
7
+ field :input_definitions, name: :inputs, extractor: HashValueExtractor, blueprint: Input
8
+ field :output_definitions, name: :outputs, extractor: HashValueExtractor
11
9
  field :description
10
+ field :user_runnable?, name: :user_runnable
12
11
  end
13
12
  end
14
13
  end
@@ -4,18 +4,16 @@ module Inferno
4
4
  class TestGroup < Serializer
5
5
  identifier :id
6
6
 
7
- # TODO: fill out test group
8
7
  field :title
9
8
  field :description
10
9
  field :test_count
11
- # field :run_as_group
10
+ field :run_as_group?, name: :run_as_group
11
+ field :user_runnable?, name: :user_runnable
12
12
 
13
13
  association :groups, name: :test_groups, blueprint: TestGroup
14
14
  association :tests, blueprint: Test
15
- field :inputs
16
- field :outputs do |group, _options|
17
- group.outputs.map { |input| { name: input } }
18
- end
15
+ field :input_definitions, name: :inputs, extractor: HashValueExtractor, blueprint: Input
16
+ field :output_definitions, name: :outputs, extractor: HashValueExtractor
19
17
  end
20
18
  end
21
19
  end
@@ -9,6 +9,7 @@ module Inferno
9
9
 
10
10
  Application.register('js_host', ENV.fetch('JS_HOST', ''))
11
11
  Application.register('async_jobs', ENV['ASYNC_JOBS'] != 'false')
12
+ Application.register('inferno_host', ENV.fetch('INFERNO_HOST', 'http://localhost:4567'))
12
13
 
13
14
  configure do |config|
14
15
  config.root = File.expand_path('../../..', __dir__)
@@ -4,12 +4,10 @@ Inferno::Application.boot(:suites) do
4
4
 
5
5
  files_to_load = Dir.glob(File.join(Dir.pwd, 'lib', '*.rb'))
6
6
 
7
- if ENV['LOAD_DEV_SUITES'] == 'true'
8
- files_to_load.concat Dir.glob(File.join(Inferno::Application.root, 'dev_suites', '**', '*.rb'))
9
- end
10
-
11
- if ENV['LOAD_UI_SUITES'] == 'true'
12
- files_to_load.concat Dir.glob(File.join(Inferno::Application.root, 'ui_suites', '**', '*.rb'))
7
+ if ENV['LOAD_DEV_SUITES'].present?
8
+ ENV['LOAD_DEV_SUITES'].split(',').map(&:strip).reject(&:empty?).each do |suite|
9
+ files_to_load.concat Dir.glob(File.join(Inferno::Application.root, 'dev_suites', suite, '**', '*.rb'))
10
+ end
13
11
  end
14
12
 
15
13
  if ENV['APP_ENV'] == 'test'
@@ -88,6 +88,26 @@ module Inferno
88
88
  rescue JSON::ParserError
89
89
  assert false, "Invalid JSON. #{message}"
90
90
  end
91
+
92
+ def assert_valid_http_uri(uri, message = '')
93
+ error_message = message || "\"#{uri}\" is not a valid URI"
94
+ assert uri =~ /\A#{URI::DEFAULT_PARSER.make_regexp(['http', 'https'])}\z/, error_message
95
+ end
96
+
97
+ def assert_response_content_type(type, request: self.request)
98
+ header = request.response_header('Content-Type')
99
+ assert header.present?, no_content_type_message
100
+
101
+ assert header.value.start_with?(type), bad_content_type_message(type, header.value)
102
+ end
103
+
104
+ def no_content_type_message
105
+ 'Response did not contain a `Content-Type` header.'
106
+ end
107
+
108
+ def bad_content_type_message(expected, received)
109
+ "Expected `Content-Type` to be `#{expected}`, but found `#{received}`"
110
+ end
91
111
  end
92
112
  end
93
113
  end
@@ -0,0 +1,126 @@
1
+ module Inferno
2
+ module DSL
3
+ # This module contains the DSL for managing runnable configuration.
4
+ module Configurable
5
+ def self.extended(klass)
6
+ klass.extend Forwardable
7
+ klass.def_delegator 'self.class', :config
8
+ end
9
+
10
+ def config(new_configuration = {})
11
+ @config ||= Configuration.new
12
+
13
+ return @config if new_configuration.blank?
14
+
15
+ @config.apply(new_configuration)
16
+ end
17
+
18
+ # @api private
19
+ class Configuration
20
+ attr_accessor :configuration
21
+
22
+ def initialize(configuration = {})
23
+ self.configuration = configuration
24
+ end
25
+
26
+ def apply(new_configuration)
27
+ config_to_apply =
28
+ if new_configuration.is_a? Configuration
29
+ new_configuration.configuration
30
+ else
31
+ new_configuration
32
+ end
33
+
34
+ self.configuration = configuration.deep_merge(config_to_apply)
35
+ end
36
+
37
+ def options
38
+ configuration[:options] ||= {}
39
+ end
40
+
41
+ ### Input Configuration ###
42
+
43
+ def inputs
44
+ configuration[:inputs] ||= {}
45
+ end
46
+
47
+ def add_input(identifier, new_config = {})
48
+ existing_config = input_config(identifier) || {}
49
+ inputs[identifier] = default_input_config(identifier).merge(existing_config, new_config)
50
+ end
51
+
52
+ def default_input_config(identifier)
53
+ { name: identifier, type: 'text' }
54
+ end
55
+
56
+ def input_config_exists?(identifier)
57
+ inputs.key? identifier
58
+ end
59
+
60
+ def input_config(identifier)
61
+ inputs[identifier]
62
+ end
63
+
64
+ def input_name(identifier)
65
+ inputs.dig(identifier, :name) || identifier
66
+ end
67
+
68
+ ### Output Configuration ###
69
+
70
+ def outputs
71
+ configuration[:outputs] ||= {}
72
+ end
73
+
74
+ def add_output(identifier)
75
+ return if output_config_exists?(identifier)
76
+
77
+ outputs[identifier] = default_output_config(identifier)
78
+ end
79
+
80
+ def default_output_config(identifier)
81
+ { name: identifier }
82
+ end
83
+
84
+ def output_config_exists?(identifier)
85
+ outputs.key? identifier
86
+ end
87
+
88
+ def output_config(identifier)
89
+ outputs[identifier]
90
+ end
91
+
92
+ def output_name(identifier)
93
+ outputs.dig(identifier, :name) || identifier
94
+ end
95
+
96
+ ### Request Configuration ###
97
+
98
+ def requests
99
+ configuration[:requests] ||= {}
100
+ end
101
+
102
+ def add_request(identifier)
103
+ return if request_config_exists?(identifier)
104
+
105
+ requests[identifier] = default_request_config(identifier)
106
+ end
107
+
108
+ def default_request_config(identifier)
109
+ { name: identifier }
110
+ end
111
+
112
+ def request_config_exists?(identifier)
113
+ requests.key? identifier
114
+ end
115
+
116
+ def request_config(identifier)
117
+ requests[identifier]
118
+ end
119
+
120
+ def request_name(identifier)
121
+ requests.dig(identifier, :name) || identifier
122
+ end
123
+ end
124
+ end
125
+ end
126
+ end
@@ -69,12 +69,14 @@ module Inferno
69
69
  # @param client [Symbol]
70
70
  # @param name [Symbol] Name for this request to allow it to be used by
71
71
  # other tests
72
- # @param _options [Hash] TODO
72
+ # @option options [Hash] Input headers here - headers are optional and
73
+ # must be entered as the last piece of input to this method
73
74
  # @return [Inferno::Entities::Request]
74
- def fhir_operation(path, body: nil, client: :default, name: nil, **_options)
75
+ def fhir_operation(path, body: nil, client: :default, name: nil, **options)
75
76
  store_request('outgoing', name) do
76
77
  headers = fhir_client(client).fhir_headers
77
78
  headers.merge!('Content-Type' => 'application/fhir+json') if body.present?
79
+ headers.merge!(options[:headers]) if options[:headers].present?
78
80
  fhir_client(client).send(:post, path, body, headers)
79
81
  end
80
82
  end
@@ -60,16 +60,17 @@ module Inferno
60
60
  # @param client [Symbol]
61
61
  # @param name [Symbol] Name for this request to allow it to be used by
62
62
  # other tests
63
- # @param _options [Hash] TODO
63
+ # @option options [Hash] Input headers here - headers are optional and
64
+ # must be entered as the last piece of input to this method
64
65
  # @return [Inferno::Entities::Request]
65
- def get(url = '', client: :default, name: nil, **_options)
66
+ def get(url = '', client: :default, name: nil, **options)
66
67
  store_request('outgoing', name) do
67
68
  client = http_client(client)
68
69
 
69
70
  if client
70
- client.get(url)
71
+ client.get(url, nil, options[:headers])
71
72
  elsif url.match?(%r{\Ahttps?://})
72
- Faraday.get(url)
73
+ Faraday.get(url, nil, options[:headers])
73
74
  else
74
75
  raise StandardError, 'Must use an absolute url or define an HTTP client with a base url'
75
76
  end
@@ -85,16 +86,17 @@ module Inferno
85
86
  # @param client [Symbol]
86
87
  # @param name [Symbol] Name for this request to allow it to be used by
87
88
  # other tests
88
- # @param _options [Hash] TODO
89
+ # @option options [Hash] Input headers here - headers are optional and
90
+ # must be entered as the last piece of input to this method
89
91
  # @return [Inferno::Entities::Request]
90
- def post(url = '', body: nil, client: :default, name: nil, **_options)
92
+ def post(url = '', body: nil, client: :default, name: nil, **options)
91
93
  store_request('outgoing', name) do
92
94
  client = http_client(client)
93
95
 
94
96
  if client
95
- client.post(url, body)
97
+ client.post(url, body, options[:headers])
96
98
  elsif url.match?(%r{\Ahttps?://})
97
- Faraday.post(url, body)
99
+ Faraday.post(url, body, options[:headers])
98
100
  else
99
101
  raise StandardError, 'Must use an absolute url or define an HTTP client with a base url'
100
102
  end
@@ -38,13 +38,14 @@ module Inferno
38
38
 
39
39
  # TODO: do a check in the test runner
40
40
  def named_request(name)
41
- requests.find { |request| request.name == name.to_sym }
41
+ requests.find { |request| request.name == self.class.config.request_name(name.to_sym) }
42
42
  end
43
43
 
44
44
  # @api private
45
45
  def store_request(direction, name = nil, &block)
46
46
  response = block.call
47
47
 
48
+ name = self.class.config.request_name(name)
48
49
  request =
49
50
  if response.is_a? FHIR::ClientReply
50
51
  Entities::Request.from_fhir_client_reply(
@@ -64,8 +65,9 @@ module Inferno
64
65
  def load_named_requests
65
66
  requests_repo = Inferno::Repositories::Requests.new
66
67
  self.class.named_requests_used.map do |request_name|
67
- request = requests_repo.find_named_request(test_session_id, request_name)
68
- raise StandardError, "Unable to find '#{request_name}' request" if request.nil?
68
+ request_alias = self.class.config.request_name(request_name)
69
+ request = requests_repo.find_named_request(test_session_id, request_alias)
70
+ raise StandardError, "Unable to find '#{request_alias}' request" if request.nil?
69
71
 
70
72
  requests << request
71
73
  end
@@ -84,16 +86,20 @@ module Inferno
84
86
 
85
87
  # Specify the named requests made by a test
86
88
  #
87
- # @param *names [Symbol] one or more Symbols
88
- def makes_request(*names)
89
- named_requests_made.concat(names)
89
+ # @param *identifiers [Symbol] one or more Symbols
90
+ def makes_request(*identifiers)
91
+ named_requests_made.concat(identifiers).uniq!
92
+ identifiers.each do |identifier|
93
+ config.add_request(identifier)
94
+ end
90
95
  end
91
96
 
92
97
  # Specify the name for a request received by a test
93
98
  #
94
- # @param *names [Symbol] one or more Symbols
95
- def receives_request(name)
96
- @incoming_request_name = name
99
+ # @param *identifiers [Symbol] one or more Symbols
100
+ def receives_request(identifier)
101
+ config.add_request(identifier)
102
+ @incoming_request_name = identifier
97
103
  end
98
104
 
99
105
  # @api private
@@ -103,9 +109,12 @@ module Inferno
103
109
 
104
110
  # Specify the named requests used by a test
105
111
  #
106
- # @param *names [Symbol] one or more Symbols
107
- def uses_request(*names)
108
- named_requests_used.concat(names)
112
+ # @param *identifiers [Symbol] one or more Symbols
113
+ def uses_request(*identifiers)
114
+ named_requests_used.concat(identifiers).uniq!
115
+ identifiers.each do |identifier|
116
+ config.add_request(identifier)
117
+ end
109
118
  end
110
119
  end
111
120
  end
@@ -48,7 +48,7 @@ module Inferno
48
48
  request.to_hash.merge(
49
49
  test_session_id: test_run.test_session_id,
50
50
  result_id: waiting_result.id,
51
- name: test.incoming_request_name
51
+ name: test.config.request_name(test.incoming_request_name)
52
52
  )
53
53
  )
54
54
  end
@@ -1,4 +1,6 @@
1
+ require_relative 'configurable'
1
2
  require_relative 'resume_test_route'
3
+ require_relative '../utils/markdown_formatter'
2
4
 
3
5
  module Inferno
4
6
  module DSL
@@ -7,6 +9,8 @@ module Inferno
7
9
  module Runnable
8
10
  attr_accessor :parent
9
11
 
12
+ include Inferno::Utils::MarkdownFormatter
13
+
10
14
  # When a class (e.g. TestSuite/TestGroup) uses this module, set it up
11
15
  # so that subclassing it works correctly.
12
16
  # - add the subclass to the relevant repository when it is created
@@ -15,6 +19,7 @@ module Inferno
15
19
  # @api private
16
20
  def self.extended(extending_class)
17
21
  super
22
+ extending_class.extend Configurable
18
23
 
19
24
  extending_class.define_singleton_method(:inherited) do |subclass|
20
25
  copy_instance_variables(subclass)
@@ -39,11 +44,13 @@ module Inferno
39
44
  # @api private
40
45
  def copy_instance_variables(subclass)
41
46
  instance_variables.each do |variable|
42
- next if [:@id, :@groups, :@tests, :@parent, :@children, :@test_count].include?(variable)
47
+ next if [:@id, :@groups, :@tests, :@parent, :@children, :@test_count, :@config].include?(variable)
43
48
 
44
49
  subclass.instance_variable_set(variable, instance_variable_get(variable).dup)
45
50
  end
46
51
 
52
+ subclass.config(config)
53
+
47
54
  child_types.each do |child_type|
48
55
  new_children = send(child_type).map do |child|
49
56
  Class.new(child).tap do |subclass_child|
@@ -126,16 +133,16 @@ module Inferno
126
133
 
127
134
  # @api private
128
135
  def configure_child_class(klass, hash_args) # rubocop:disable Metrics/CyclomaticComplexity
129
- inputs.each do |input_definition|
130
- next if klass.inputs.any? { |input| input[:name] == input_definition[:name] }
136
+ inputs.each do |name|
137
+ next if klass.inputs.any? { |klass_input_name| klass_input_name == name }
131
138
 
132
- klass.input input_definition[:name], input_definition
139
+ klass.input name
133
140
  end
134
141
 
135
- outputs.each do |output_definition|
136
- next if klass.outputs.include? output_definition
142
+ outputs.each do |output_name|
143
+ next if klass.outputs.include? output_name
137
144
 
138
- klass.output output_definition
145
+ klass.output output_name
139
146
  end
140
147
 
141
148
  new_fhir_client_definitions = klass.instance_variable_get(:@fhir_client_definitions) || {}
@@ -154,8 +161,14 @@ module Inferno
154
161
  end
155
162
  klass.instance_variable_set(:@http_client_definitions, new_http_client_definitions)
156
163
 
164
+ klass.config(config)
165
+
157
166
  hash_args.each do |key, value|
158
- klass.send(key, *value)
167
+ if value.is_a? Array
168
+ klass.send(key, *value)
169
+ else
170
+ klass.send(key, value)
171
+ end
159
172
  end
160
173
 
161
174
  klass.children.each do |child_class|
@@ -193,43 +206,48 @@ module Inferno
193
206
  def description(new_description = nil)
194
207
  return @description if new_description.nil?
195
208
 
196
- @description = new_description
209
+ @description = format_markdown(new_description)
197
210
  end
198
211
 
199
212
  # Define inputs
200
213
  #
201
- # @param name [Symbol] name of the input
202
- # @param other_names [Symbol] array of symbols if specifying multiple inputs
214
+ # @param identifier [Symbol] identifier for the input
215
+ # @param other_identifiers [Symbol] array of symbols if specifying multiple inputs
203
216
  # @param input_definition [Hash] options for input such as type, description, or title
204
217
  # @option input_definition [String] :title Human readable title for input
205
218
  # @option input_definition [String] :description Description for the input
206
219
  # @option input_definition [String] :type text | textarea
207
220
  # @option input_definition [String] :default The default value for the input
221
+ # @option input_definition [Boolean] :optional Set to true to not require input for test execution
208
222
  # @return [void]
209
223
  # @example
210
224
  # input :patientid, title: 'Patient ID', description: 'The ID of the patient being searched for',
211
225
  # default: 'default_patient_id'
212
226
  # @example
213
- # input :textarea, title: 'Textarea Input Example', type: 'textarea'
214
- def input(name, *other_names, **input_definition)
215
- if other_names.present?
216
- [name, *other_names].each do |input_name|
217
- inputs.push({ name: input_name, title: nil, description: nil, type: 'text' })
227
+ # input :textarea, title: 'Textarea Input Example', type: 'textarea', optional: true
228
+ def input(identifier, *other_identifiers, **input_definition)
229
+ if other_identifiers.present?
230
+ [identifier, *other_identifiers].compact.each do |input_identifier|
231
+ inputs << input_identifier
232
+ config.add_input(input_identifier)
218
233
  end
219
234
  else
220
- input_definition[:type] = 'text' unless input_definition.key? :type
221
- inputs.push({ name: name }.merge(input_definition))
235
+ inputs << identifier
236
+ config.add_input(identifier, input_definition)
222
237
  end
223
238
  end
224
239
 
225
240
  # Define outputs
226
241
  #
227
- # @param output_definitions [Symbol]
242
+ # @param output_lists [Symbol]
228
243
  # @return [void]
229
244
  # @example
230
245
  # output :patient_id, :bearer_token
231
- def output(*output_definitions)
232
- outputs.concat(output_definitions)
246
+ def output(*output_list)
247
+ output_list.each do |output_identifier|
248
+ outputs << output_identifier
249
+ config.add_output(output_identifier)
250
+ end
233
251
  end
234
252
 
235
253
  # @api private
@@ -242,6 +260,14 @@ module Inferno
242
260
  @inputs ||= []
243
261
  end
244
262
 
263
+ def input_definitions
264
+ config.inputs.slice(*inputs)
265
+ end
266
+
267
+ def output_definitions
268
+ config.outputs.slice(*outputs)
269
+ end
270
+
245
271
  # @api private
246
272
  def outputs
247
273
  @outputs ||= []
@@ -334,6 +360,28 @@ module Inferno
334
360
  def test_count
335
361
  @test_count ||= children&.reduce(0) { |sum, child| sum + child.test_count } || 0
336
362
  end
363
+
364
+ def required_inputs(prior_outputs = [])
365
+ required_inputs = inputs.select do |input|
366
+ !input_definitions[input][:optional] && !prior_outputs.include?(input)
367
+ end
368
+ required_inputs.map! { |input_identifier| input_definitions[input_identifier][:name] }
369
+ children_required_inputs = children.flat_map { |child| child.required_inputs(prior_outputs) }
370
+ prior_outputs.concat(outputs)
371
+ (required_inputs + children_required_inputs).flatten.uniq
372
+ end
373
+
374
+ def missing_inputs(submitted_inputs)
375
+ submitted_inputs = [] if submitted_inputs.nil?
376
+
377
+ required_inputs.map(&:to_s) - submitted_inputs.map { |input| input[:name] }
378
+ end
379
+
380
+ def user_runnable?
381
+ @user_runnable ||= parent.nil? ||
382
+ !parent.respond_to?(:run_as_group?) ||
383
+ (parent.user_runnable? && !parent.run_as_group?)
384
+ end
337
385
  end
338
386
  end
339
387
  end
@@ -46,7 +46,7 @@ module Inferno
46
46
  # @param name [String] the header name
47
47
  # @return [Inferno::Entities::RequestHeader, nil]
48
48
  def response_header(name)
49
- response_headers.find { |header| header.name == name.downcase }
49
+ response_headers.find { |header| header.name.casecmp(name).zero? }
50
50
  end
51
51
 
52
52
  # Find a request header
@@ -54,7 +54,7 @@ module Inferno
54
54
  # @param name [String] the header name
55
55
  # @return [Inferno::Entities::RequestHeader, nil]
56
56
  def request_header(name)
57
- request_headers.find { |header| header.name == name.downcase }
57
+ request_headers.find { |header| header.name.casecmp(name).zero? }
58
58
  end
59
59
 
60
60
  # All of the request headers