inferno_core 0.6.2 → 0.6.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7789d88730cda670cc051b8d0d56d1a9d3f8fba3133628eda872eddebd57abf9
4
- data.tar.gz: 7f617722c5dd12f6ed349dd474954b7af2fc499420815e4087a18af68dbe5024
3
+ metadata.gz: afdf870396c4f72a853bcbc56b7f134c827391a0b0b2e956b47e79aa36fccdf8
4
+ data.tar.gz: d77d91fc728001379862adb5d740039704d3dd81fe184b8e11d3b99a650e5218
5
5
  SHA512:
6
- metadata.gz: 47b1907c5b14d12b59466d131addf56ba5579e99c7ad96ed8d7e5a7de8e8b83825f4599e39f01aa9abecd263b2fb4945c8e53efc11d247fb7eb969771ede9a06
7
- data.tar.gz: 04d10803787c55903e58131832b8586a5592e27608f46b17831abf30ff4ce560fed2061e9db203965f967109e393641a3d5c17a048845df8ab1b3fba664adacc
6
+ metadata.gz: 03ff313b070ce6d09ae5ec35c0f677ed30c04010915189fdf9361ddb0e69b5df9ae0bdb7350a9e8db041a3352cc183d60173b0598fc729273ccfdf1386b03351
7
+ data.tar.gz: 37ea35ed4bad96e583f935763f22865244d01ae8daed58b43b0a82d9e2b74d5a39efcc6995bdc087c19fa620eddbc131f81a6365aa8ece6b12dcf8db319c9245
@@ -6,7 +6,6 @@ RUN mkdir -p $INSTALL_PATH
6
6
 
7
7
  WORKDIR $INSTALL_PATH
8
8
 
9
- ADD lib/<%= library_name %>/metadata.rb $INSTALL_PATH/lib/<%= library_name %>/metadata.rb
10
9
  ADD lib/<%= library_name %>/version.rb $INSTALL_PATH/lib/<%= library_name %>/version.rb
11
10
  ADD *.gemspec $INSTALL_PATH
12
11
  ADD Gemfile* $INSTALL_PATH
@@ -3,6 +3,16 @@
3
3
  <%= human_name %> [Inferno](https://github.com/inferno-community/inferno-core) Test Kit
4
4
  for FHIR testing.
5
5
 
6
+ ## Getting Started
7
+
8
+ The quickest way to run this test kit locally is with [Docker](https://www.docker.com/).
9
+
10
+ - Install Docker
11
+ - Clone this repository, or download an [official release](/releases) if available.
12
+ - Run `./setup.sh` within the test kit directory to download necessary dependencies
13
+ - Run `./run.sh` within the test kit directory to start the application
14
+ - Navigate to `http://localhost`
15
+
6
16
  ## Instructions for Developing Your Test Kit
7
17
 
8
18
  Refer to the Inferno documentation for information about [setting up
@@ -0,0 +1,7 @@
1
+ This file was generated from the Inferno template to demonstrate providing
2
+ documentation across multiple files, and has not yet customized for this test
3
+ kit.
4
+
5
+ For examples of test kit documentation, refer to the [US Core Test
6
+ Kit](/inferno-framework/us-core-test-kit/wiki) and the [ONC Certification
7
+ (g)(10) Test Kit](/inferno-framework/onc-certification-g10-test-kit/wiki).
@@ -0,0 +1,10 @@
1
+ <%= human_name %> documentation. This file has been generated from the Inferno
2
+ test kit template and has not yet been customized for this test kit.
3
+
4
+ *Note to developer: After customizing this documentation, initialize a GitHub
5
+ wiki within this repository to enable automatic publishing to the wiki via the
6
+ included Publish Docs Wiki action.*
7
+
8
+ ## Using this Test Kit (example)
9
+ * [Getting Started](../?tab=readme-ov-file#getting-started): Installation instructions for this test kit.
10
+ * [Test Kit Overview](Overview.md): An overview of the test kit and its tests.
@@ -0,0 +1,5 @@
1
+ _Test kit documentation is stored within the [./docs](../tree/main/docs) folder of
2
+ this repository and is automatically synchronized to this wiki with each update
3
+ to the `main` branch using the [Publish Docs Wiki
4
+ workflow](../actions/workflows/publish-docs-wiki.yml). Do not change content
5
+ within this wiki directly as changes will be overwritten._
@@ -0,0 +1,5 @@
1
+ **[Documentation Home](Home)**
2
+
3
+ **Using this Test Kit**
4
+ - [Getting Started](../?tab=readme-ov-file#getting-started)
5
+ - [Test Kit Overview](Overview.md)
@@ -307,9 +307,95 @@ module Inferno
307
307
  self.expires_in = expires_in
308
308
  self.issue_time = DateTime.now
309
309
 
310
- add_to_client(client)
310
+ add_to_client(client) if client
311
311
  self
312
312
  end
313
+
314
+ # Returns the default configuration for the "auth_type" component
315
+ # @return [Hash]
316
+ def self.default_auth_type_component
317
+ {
318
+ name: :auth_type,
319
+ options: {
320
+ list_options: [
321
+ { label: 'Public', value: 'public' },
322
+ { label: 'Confidential Symmetric', value: 'symmetric' },
323
+ { label: 'Confidential Asymmetric', value: 'asymmetric' },
324
+ { label: 'Backend Services', value: 'backend_services' }
325
+ ]
326
+ }
327
+ }
328
+ end
329
+
330
+ # Returns the default configuration for the "auth_type" component without
331
+ # the option for backend services auth
332
+ # @return [Hash]
333
+ def self.default_auth_type_component_without_backend_services
334
+ {
335
+ name: :auth_type,
336
+ options: {
337
+ list_options: [
338
+ { label: 'Public', value: 'public' },
339
+ { label: 'Confidential Symmetric', value: 'symmetric' },
340
+ { label: 'Confidential Asymmetric', value: 'asymmetric' }
341
+ ]
342
+ }
343
+ }
344
+ end
345
+
346
+ # Returns true when using public auth
347
+ # @return [Boolean]
348
+ def public_auth?
349
+ auth_type&.casecmp? 'public'
350
+ end
351
+
352
+ # Returns true when using confidential symmetric auth
353
+ # @return [Boolean]
354
+ def symmetric_auth?
355
+ auth_type&.casecmp? 'symmetric'
356
+ end
357
+
358
+ # Returns true when using confidential asymmetric auth
359
+ # @return [Boolean]
360
+ def asymmetric_auth?
361
+ auth_type&.casecmp? 'asymmetric'
362
+ end
363
+
364
+ # Returns true when using backend services auth
365
+ # @return [Boolean]
366
+ def backend_services_auth?
367
+ auth_type&.casecmp? 'backend_services'
368
+ end
369
+
370
+ # Returns true when using GET as the authorization request method
371
+ # @return [Boolean]
372
+ def get_auth_request?
373
+ auth_request_method&.casecmp? 'get'
374
+ end
375
+
376
+ # Returns true when using POST as the authorization request method
377
+ # @return [Boolean]
378
+ def post_auth_request?
379
+ auth_request_method&.casecmp? 'post'
380
+ end
381
+
382
+ # Returns true when pkce is enabled
383
+ # @return [Boolean]
384
+ def pkce_enabled?
385
+ pkce_support&.casecmp? 'enabled'
386
+ end
387
+
388
+ # Returns true when using the S256 pkce code challenge method
389
+ # @return [Boolean]
390
+ def s256_code_challenge_method?
391
+ pkce_code_challenge_method&.casecmp? 'S256'
392
+ end
393
+
394
+ # Returns true when using the palin pkce code challenge method
395
+ # @return [Boolean]
396
+ def plain_code_challenge_method?
397
+ pkce_code_challenge_method&.casecmp? 'plain'
398
+ end
313
399
  end
314
400
  end
315
401
  end
@@ -138,6 +138,19 @@ module Inferno
138
138
  configuration[:inputs] ||= {}
139
139
  end
140
140
 
141
+ # @private
142
+ # Recursively duplicate arrays/hashes to prevent them from being shared
143
+ # across different runnables
144
+ def deep_dup(value)
145
+ if value.is_a? Array
146
+ value.map { |element| deep_dup(element) }
147
+ elsif value.is_a? Hash
148
+ value.transform_values { |element| deep_dup(element) }
149
+ else
150
+ value.dup
151
+ end
152
+ end
153
+
141
154
  # @private
142
155
  def add_input(identifier, new_config = {})
143
156
  existing_config = input(identifier)
@@ -148,7 +161,7 @@ module Inferno
148
161
 
149
162
  inputs[identifier] =
150
163
  Entities::Input
151
- .new(**existing_config.to_hash)
164
+ .new(**deep_dup(existing_config.to_hash))
152
165
  .merge(Entities::Input.new(**new_config))
153
166
  end
154
167
 
@@ -194,6 +194,7 @@ module Inferno
194
194
  end
195
195
 
196
196
  available_inputs = children_available_inputs(selected_suite_options).merge(available_inputs)
197
+
197
198
  order_available_inputs(available_inputs)
198
199
  end
199
200
  end
@@ -47,11 +47,23 @@ module Inferno
47
47
  :@children_available_inputs # Needs to be recalculated
48
48
  ].freeze
49
49
 
50
+ # Recursively duplicate arrays/hashes to prevent them from being shared
51
+ # across different runnables
52
+ def deep_dup(value)
53
+ if value.is_a? Array
54
+ value.map { |element| deep_dup(element) }
55
+ elsif value.is_a? Hash
56
+ value.transform_values { |element| deep_dup(element) }
57
+ else
58
+ value.dup
59
+ end
60
+ end
61
+
50
62
  # @private
51
63
  def copy_instance_variables(subclass)
52
64
  instance_variables
53
65
  .reject { |variable| VARIABLES_NOT_TO_COPY.include? variable }
54
- .each { |variable| subclass.instance_variable_set(variable, instance_variable_get(variable).dup) }
66
+ .each { |variable| subclass.instance_variable_set(variable, deep_dup(instance_variable_get(variable))) }
55
67
 
56
68
  subclass.config(config)
57
69
 
data/lib/inferno/dsl.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require_relative 'dsl/assertions'
2
+ require_relative 'dsl/auth_info'
2
3
  require_relative 'dsl/fhir_client'
3
4
  require_relative 'dsl/fhir_validation'
4
5
  require_relative 'dsl/fhir_evaluation/evaluator'
@@ -43,7 +43,7 @@ module Inferno
43
43
 
44
44
  # These are the attributes that can be directly copied when merging a
45
45
  # runnable's input with an input configuration.
46
- MERGEABLE_ATTRIBUTES = (ATTRIBUTES - [:type]).freeze
46
+ MERGEABLE_ATTRIBUTES = (ATTRIBUTES - [:type, :options]).freeze
47
47
 
48
48
  def initialize(**params)
49
49
  bad_params = params.keys - ATTRIBUTES
@@ -69,21 +69,27 @@ module Inferno
69
69
 
70
70
  self.type = child_input.type if child_input.present? && child_input.type != 'text'
71
71
 
72
+ merge_options(primary_source: self, secondary_source: child_input)
73
+
72
74
  self
73
75
  end
74
76
 
75
77
  # @private
76
78
  # Merge this input with an input from a configuration. Fields defined in
77
79
  # the configuration take precedence over those defined on this input.
78
- def merge(other_input)
80
+ def merge(other_input, merge_all: false)
79
81
  return self if other_input.nil?
80
82
 
81
- MERGEABLE_ATTRIBUTES.each do |attribute|
83
+ attributes_to_merge = merge_all ? ATTRIBUTES : MERGEABLE_ATTRIBUTES
84
+
85
+ attributes_to_merge.each do |attribute|
82
86
  merge_attribute(attribute, primary_source: other_input, secondary_source: self)
83
87
  end
84
88
 
85
89
  self.type = other_input.type if other_input.type.present? && other_input.type != 'text'
86
90
 
91
+ merge_options(primary_source: other_input, secondary_source: self)
92
+
87
93
  self
88
94
  end
89
95
 
@@ -103,6 +109,60 @@ module Inferno
103
109
  send("#{attribute}=", value)
104
110
  end
105
111
 
112
+ # @private
113
+ # Merge input options. This performs a normal merge for all options except
114
+ # for the "components" field, the members of which are individually merged
115
+ # by `merge_components`
116
+ # @param primary_source [Input]
117
+ # @param secondary_source [Input]
118
+ def merge_options(primary_source:, secondary_source:)
119
+ primary_options = primary_source.options.dup || {}
120
+ secondary_options = secondary_source.options.dup || {}
121
+
122
+ return if primary_options.blank? && secondary_options.blank?
123
+
124
+ primary_components = primary_options.delete(:components) || []
125
+ secondary_components = secondary_options.delete(:components) || []
126
+
127
+ send('options=', secondary_options.merge(primary_options))
128
+
129
+ merge_components(primary_components:, secondary_components:)
130
+ end
131
+
132
+ # @private
133
+ # Merge component hashes.
134
+ # @param primary_source [Input]
135
+ # @param secondary_source [Input]
136
+ def merge_components(primary_components:, secondary_components:) # rubocop:disable Metrics/CyclomaticComplexity
137
+ primary_components
138
+ .each { |component| component[:name] = component[:name].to_sym }
139
+ secondary_components
140
+ .each { |component| component[:name] = component[:name].to_sym }
141
+
142
+ return if primary_components.blank? && secondary_components.blank?
143
+
144
+ component_keys =
145
+ (primary_components + secondary_components)
146
+ .map { |component| component[:name] }
147
+ .uniq
148
+
149
+ merged_components = component_keys.map do |key|
150
+ primary_component = primary_components.find { |component| component[:name] == key }
151
+ secondary_component = secondary_components.find { |component| component[:name] == key }
152
+
153
+ next secondary_component if primary_component.blank?
154
+
155
+ next primary_component if secondary_component.blank?
156
+
157
+ Input.new(**secondary_component).merge(Input.new(**primary_component), merge_all: true).to_hash
158
+ end
159
+
160
+ merged_components.each { |component| component[:name] = component[:name].to_sym }
161
+
162
+ self.options ||= {}
163
+ self.options[:components] = merged_components
164
+ end
165
+
106
166
  def to_hash
107
167
  ATTRIBUTES.each_with_object({}) do |attribute, hash|
108
168
  value = send(attribute)
@@ -112,6 +112,8 @@ module Inferno
112
112
  auth =
113
113
  if params[:value].is_a? String
114
114
  DSL::AuthInfo.new(JSON.parse(params[:value]))
115
+ elsif params[:value].is_a? Hash
116
+ DSL::AuthInfo.new(params[:value])
115
117
  elsif !params[:value].is_a? DSL::AuthInfo
116
118
  raise Exceptions::BadSessionDataType.new(
117
119
  params[:name],
@@ -1,4 +1,4 @@
1
1
  module Inferno
2
2
  # Standard patterns for gem versions: https://guides.rubygems.org/patterns/
3
- VERSION = '0.6.2'.freeze
3
+ VERSION = '0.6.3'.freeze
4
4
  end
@@ -5,24 +5,27 @@ RSpec.shared_context('when testing a runnable') do
5
5
  let(:test_session) { repo_create(:test_session, test_suite_id: suite_id) }
6
6
 
7
7
  before do
8
- allow(described_class).to receive(:suite).and_return(suite) if described_class.parent.nil?
8
+ if described_class.respond_to?(:parent) && described_class.parent.nil?
9
+ allow(described_class).to receive(:suite).and_return(suite)
10
+ end
9
11
  rescue NameError
10
12
  raise StandardError, "No suite id defined. Add `let(:suite_id) { 'your_suite_id' }` to the spec"
11
13
  end
12
14
 
13
- def run(runnable, inputs = {})
15
+ def run(runnable, inputs = {}, scratch = {})
14
16
  test_run_params = { test_session_id: test_session.id }.merge(runnable.reference_hash)
15
17
  test_run = Inferno::Repositories::TestRuns.new.create(test_run_params)
16
- inputs.each do |name, value|
18
+ inputs.each do |original_name, value|
19
+ name = runnable.config.input_name(original_name).presence || original_name
17
20
  session_data_repo.save(
18
21
  test_session_id: test_session.id,
19
22
  name:,
20
23
  value:,
21
- type: runnable.config.input_type(name)
24
+ type: runnable.available_inputs[name.to_sym]&.type
22
25
  )
23
26
  end
24
27
 
25
- Inferno::TestRunner.new(test_session:, test_run:).run(runnable)
28
+ Inferno::TestRunner.new(test_session:, test_run:).run(runnable, scratch)
26
29
  end
27
30
 
28
31
  # depth-first search looking for a runnable with a runtime id
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.6.2
4
+ version: 0.6.3
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: 2025-02-06 00:00:00.000000000 Z
13
+ date: 2025-02-13 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: activesupport
@@ -444,6 +444,10 @@ files:
444
444
  - lib/inferno/apps/cli/templates/data/redis/.keep
445
445
  - lib/inferno/apps/cli/templates/docker-compose.background.yml.tt
446
446
  - lib/inferno/apps/cli/templates/docker-compose.yml.tt
447
+ - lib/inferno/apps/cli/templates/docs/Overview.md
448
+ - lib/inferno/apps/cli/templates/docs/README.md.tt
449
+ - lib/inferno/apps/cli/templates/docs/_Footer.md
450
+ - lib/inferno/apps/cli/templates/docs/_Sidebar.md
447
451
  - lib/inferno/apps/cli/templates/lib/%library_name%.rb.tt
448
452
  - lib/inferno/apps/cli/templates/lib/%library_name%/igs/.keep
449
453
  - lib/inferno/apps/cli/templates/lib/%library_name%/igs/README.md