inferno_core 0.4.39 → 0.4.40

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,72 @@
1
+ module Inferno
2
+ # The ResultCollection class is used to manage a collection of Inferno::Entities::Result objects.
3
+ # It provides methods to filter required and optional results, access results
4
+ # by index or by their runnable IDs, and iterate over the collection.
5
+ #
6
+ # @example
7
+ #
8
+ # results = [
9
+ # Result.new(test_group_id: 'group_id1', result: 'pass'),
10
+ # Result.new(test_group_id: 'group_id2', result: 'fail'),
11
+ # Result.new(test_group_id: 'group_id3', result: 'pass')
12
+ # ]
13
+ #
14
+ # result_collection = Inferno::ResultCollection.new(results)
15
+ #
16
+ # # Access by index
17
+ # result = result_collection[0]
18
+ #
19
+ # # Access by runnable ID (partial)
20
+ # result = result_collection['group_id2']
21
+ #
22
+ # # Iterate over results
23
+ # result_collection.each do |result|
24
+ # puts result.result
25
+ # end
26
+ #
27
+ # # Get required results
28
+ # required_results = result_collection.required_results
29
+ #
30
+ # # Get optional results
31
+ # optional_results = result_collection.optional_results
32
+ # @private
33
+ class ResultCollection
34
+ include Enumerable
35
+
36
+ attr_reader :results
37
+
38
+ def initialize(results = [])
39
+ @results = results
40
+ end
41
+
42
+ def [](key)
43
+ key.is_a?(Integer) ? results[key] : lookup_by_runnable_id(key)
44
+ end
45
+
46
+ def <<(result)
47
+ (results << result).flatten!
48
+ self
49
+ end
50
+
51
+ def each(&)
52
+ return to_enum(:each) unless block_given?
53
+
54
+ results.each(&)
55
+ self
56
+ end
57
+
58
+ def required_results
59
+ results.select(&:required?)
60
+ end
61
+
62
+ def optional_results
63
+ results.select(&:optional?)
64
+ end
65
+
66
+ private
67
+
68
+ def lookup_by_runnable_id(key)
69
+ results.find { |result| result.runnable&.id == key.to_s || result.runnable&.id&.end_with?("-#{key}") }
70
+ end
71
+ end
72
+ end
@@ -1,3 +1,4 @@
1
+ require_relative './result_collection'
1
2
  module Inferno
2
3
  # @private
3
4
  # This class takes an array of results and determines the overall result. This
@@ -7,13 +8,11 @@ module Inferno
7
8
  attr_reader :results
8
9
 
9
10
  def initialize(results)
10
- @results = results
11
+ @results = results.is_a?(ResultCollection) ? results : ResultCollection.new(results)
11
12
  end
12
13
 
13
14
  def summarize
14
- return 'pass' if all_optional_results? &&
15
- unique_result_strings.any?('pass') &&
16
- unique_result_strings.none? { |result| %w[wait running].include? result }
15
+ return 'pass' if optional_results_passing_criteria_met?
17
16
 
18
17
  prioritized_result_strings.find { |result_string| unique_result_strings.include? result_string }
19
18
  end
@@ -24,21 +23,21 @@ module Inferno
24
23
  Entities::Result::RESULT_OPTIONS
25
24
  end
26
25
 
27
- def required_results
28
- @required_results ||= results.select(&:required?)
26
+ def optional_results_passing_criteria_met?
27
+ all_optional_results? && unique_result_strings.any?('pass') &&
28
+ unique_result_strings.none? { |result| %w[wait running].include? result }
29
29
  end
30
30
 
31
31
  def all_optional_results?
32
- required_results.blank?
32
+ results.required_results.blank?
33
33
  end
34
34
 
35
35
  def results_for_summary
36
- all_optional_results? ? results : required_results
36
+ all_optional_results? ? results : results.required_results
37
37
  end
38
38
 
39
39
  def unique_result_strings
40
- @unique_result_strings ||=
41
- results_for_summary.map(&:result).uniq
40
+ @unique_result_strings ||= results_for_summary.map(&:result).uniq
42
41
  end
43
42
  end
44
43
  end
@@ -70,22 +70,7 @@ module Inferno
70
70
  suite_options: test_session.suite_options_hash
71
71
  )
72
72
 
73
- result = begin
74
- raise Exceptions::CancelException, 'Test cancelled by user' if test_run_is_cancelling
75
-
76
- check_inputs(test, test_instance, inputs)
77
-
78
- test_instance.load_named_requests
79
- test_instance.instance_eval(&test.block)
80
- 'pass'
81
- rescue Exceptions::TestResultException => e
82
- test_instance.result_message = format_markdown(e.message)
83
- e.result
84
- rescue StandardError => e
85
- Application['logger'].error(e.full_message)
86
- test_instance.result_message = format_markdown("Error: #{e.message}\n\n#{e.backtrace.first}")
87
- 'error'
88
- end
73
+ result = evaluate_runnable_result(test, test_instance, inputs)
89
74
 
90
75
  outputs = save_outputs(test_instance)
91
76
  output_json_string = JSON.generate(outputs)
@@ -144,17 +129,22 @@ module Inferno
144
129
  return group_result
145
130
  end
146
131
 
147
- results = []
132
+ group_instance = group.new
133
+
148
134
  group.children(test_session.suite_options).each do |child|
149
135
  result = run(child, scratch)
150
- results << result
151
- break if results.last.waiting?
136
+ group_instance.results << result
137
+ break if result.waiting?
152
138
  end
153
139
 
154
- results.flatten!
140
+ result = evaluate_runnable_result(group, group_instance) || roll_up_result(group_instance.results)
155
141
 
156
- group_result = persist_result(group.reference_hash.merge(result: roll_up_result(results),
157
- input_json: JSON.generate(group_inputs_with_values)))
142
+ group_result = persist_result(group.reference_hash.merge(
143
+ messages: group_instance.messages,
144
+ result:,
145
+ result_message: group_instance.result_message,
146
+ input_json: JSON.generate(group_inputs_with_values)
147
+ ))
158
148
 
159
149
  update_parent_result(group.parent)
160
150
 
@@ -166,15 +156,19 @@ module Inferno
166
156
 
167
157
  children = parent.children(test_session.suite_options)
168
158
  child_results = results_repo.current_results_for_test_session_and_runnables(test_session.id, children)
169
- required_children = children.select(&:required?)
170
- required_results = child_results.select(&:required?)
171
- return if required_children.length != required_results.length
159
+ return unless need_to_update_parent_result?(children, child_results, &parent.block)
172
160
 
161
+ parent_instance = parent.new
162
+ parent_instance.results << child_results
173
163
  old_result = results_repo.current_result_for_test_session(test_session.id, parent.reference_hash)&.result
174
- new_result = roll_up_result(child_results)
164
+ new_result = evaluate_runnable_result(parent, parent_instance) || roll_up_result(child_results)
175
165
 
176
166
  if new_result != old_result
177
- persist_result(parent.reference_hash.merge(result: new_result))
167
+ persist_result(parent.reference_hash.merge(
168
+ result: new_result,
169
+ result_message: parent_instance.result_message,
170
+ messages: parent_instance.messages
171
+ ))
178
172
 
179
173
  update_parent_result(parent.parent)
180
174
  end
@@ -182,6 +176,40 @@ module Inferno
182
176
  new_result
183
177
  end
184
178
 
179
+ def evaluate_runnable_result(runnable, runnable_instance, inputs = nil)
180
+ return if !(runnable < Entities::Test) && !runnable.block
181
+
182
+ if runnable < Entities::Test
183
+ raise Exceptions::CancelException, 'Test cancelled by user' if test_run_is_cancelling
184
+
185
+ check_inputs(runnable, runnable_instance, inputs)
186
+
187
+ runnable_instance.load_named_requests
188
+ end
189
+ runnable_instance.instance_eval(&runnable.block)
190
+ 'pass'
191
+ rescue Exceptions::TestResultException => e
192
+ runnable_instance.result_message = format_markdown(e.message)
193
+ e.result
194
+ rescue StandardError => e
195
+ Application['logger'].error(e.full_message)
196
+ runnable_instance.result_message = format_markdown("Error: #{e.message}\n\n#{e.backtrace.first}")
197
+ 'error'
198
+ end
199
+
200
+ # Determines if the parent result needs to be updated based on the results of its children.
201
+ #
202
+ # The parent result needs to be updated if:
203
+ # - No custom result block is provided and all required children have corresponding required results.
204
+ # - A custom result block is provided and all children have corresponding results.
205
+ def need_to_update_parent_result?(children, child_results, &)
206
+ required_children = children.select(&:required?)
207
+ required_results = child_results.select(&:required?)
208
+
209
+ (!block_given? && required_children.length == required_results.length) ||
210
+ (block_given? && children.length == child_results.length)
211
+ end
212
+
185
213
  def load_inputs(runnable)
186
214
  runnable.inputs.each_with_object({}) do |input_identifier, input_hash|
187
215
  input_alias = runnable.config.input_name(input_identifier)
@@ -1,4 +1,4 @@
1
1
  module Inferno
2
2
  # Standard patterns for gem versions: https://guides.rubygems.org/patterns/
3
- VERSION = '0.4.39'.freeze
3
+ VERSION = '0.4.40'.freeze
4
4
  end
@@ -0,0 +1,71 @@
1
+ module AuthInfoConstants
2
+ AUTH_URL = 'http://example.com/authorization'.freeze
3
+ TOKEN_URL = 'http://example.com/token'.freeze
4
+ REQUESTED_SCOPES = 'launch/patient openid fhirUser patient/*.*'.freeze
5
+ ENCRYPTION_ALGORITHM = 'ES384'.freeze
6
+ KID = '4b49a739d1eb115b3225f4cf9beb6d1b'.freeze
7
+ JWKS = File.read(File.join('lib', 'inferno', 'dsl', 'jwks.json')).freeze
8
+ class << self
9
+ def token_info
10
+ {
11
+ access_token: 'SAMPLE_TOKEN',
12
+ refresh_token: 'SAMPLE_REFRESH_TOKEN',
13
+ expires_in: '3600',
14
+ issue_time: Time.now.iso8601
15
+ }
16
+ end
17
+
18
+ def public_access_default
19
+ {
20
+ auth_type: 'public',
21
+ token_url: TOKEN_URL,
22
+ client_id: 'SAMPLE_PUBLIC_CLIENT_ID',
23
+ requested_scopes: REQUESTED_SCOPES,
24
+ pkce_support: 'enabled',
25
+ pkce_code_challenge_method: 'S256',
26
+ auth_request_method: 'GET'
27
+ }.merge(token_info)
28
+ end
29
+
30
+ def symmetric_confidential_access_default
31
+ {
32
+ auth_type: 'symmetric',
33
+ token_url: TOKEN_URL,
34
+ client_id: 'SAMPLE_CONFIDENTIAL_CLIENT_ID',
35
+ client_secret: 'SAMPLE_CONFIDENTIAL_CLIENT_SECRET',
36
+ auth_url: AUTH_URL,
37
+ requested_scopes: REQUESTED_SCOPES,
38
+ pkce_support: 'enabled',
39
+ pkce_code_challenge_method: 'S256',
40
+ auth_request_method: 'post',
41
+ use_discovery: 'false'
42
+ }.merge(token_info)
43
+ end
44
+
45
+ def asymmetric_confidential_access_default
46
+ {
47
+ auth_type: 'asymmetric',
48
+ token_url: TOKEN_URL,
49
+ client_id: 'SAMPLE_CONFIDENTIAL_CLIENT_ID',
50
+ requested_scopes: REQUESTED_SCOPES,
51
+ pkce_support: 'disabled',
52
+ auth_request_method: 'post',
53
+ encryption_algorithm: ENCRYPTION_ALGORITHM,
54
+ jwks: JWKS,
55
+ kid: KID
56
+ }.merge(token_info)
57
+ end
58
+
59
+ def backend_services_access_default
60
+ {
61
+ auth_type: 'backend_services',
62
+ token_url: TOKEN_URL,
63
+ client_id: 'SAMPLE_CONFIDENTIAL_CLIENT_ID',
64
+ requested_scopes: REQUESTED_SCOPES,
65
+ encryption_algorithm: ENCRYPTION_ALGORITHM,
66
+ jwks: JWKS,
67
+ kid: KID
68
+ }.merge(token_info)
69
+ end
70
+ end
71
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: inferno_core
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.39
4
+ version: 0.4.40
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stephen MacVicar
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2024-06-25 00:00:00.000000000 Z
13
+ date: 2024-09-12 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: activesupport
@@ -649,9 +649,12 @@ files:
649
649
  - lib/inferno/dsl/fhir_client_builder.rb
650
650
  - lib/inferno/dsl/fhir_resource_validation.rb
651
651
  - lib/inferno/dsl/fhir_validation.rb
652
+ - lib/inferno/dsl/fhirpath_evaluation.rb
652
653
  - lib/inferno/dsl/http_client.rb
653
654
  - lib/inferno/dsl/http_client_builder.rb
654
655
  - lib/inferno/dsl/input_output_handling.rb
656
+ - lib/inferno/dsl/jwks.rb
657
+ - lib/inferno/dsl/messages.rb
655
658
  - lib/inferno/dsl/oauth_credentials.rb
656
659
  - lib/inferno/dsl/request_storage.rb
657
660
  - lib/inferno/dsl/results.rb
@@ -713,6 +716,7 @@ files:
713
716
  - lib/inferno/repositories/tests.rb
714
717
  - lib/inferno/repositories/validate_runnable_reference.rb
715
718
  - lib/inferno/repositories/validator_sessions.rb
719
+ - lib/inferno/result_collection.rb
716
720
  - lib/inferno/result_summarizer.rb
717
721
  - lib/inferno/spec_support.rb
718
722
  - lib/inferno/test_runner.rb
@@ -731,6 +735,7 @@ files:
731
735
  - spec/factories/result.rb
732
736
  - spec/factories/test_run.rb
733
737
  - spec/factories/test_session.rb
738
+ - spec/fixtures/auth_info_constants.rb
734
739
  - spec/fixtures/basic_test_group.rb
735
740
  - spec/fixtures/basic_test_suite.rb
736
741
  - spec/support/factory_bot.rb