inferno_core 1.4.2 → 1.4.4
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 +4 -4
- data/lib/inferno/apps/cli/execute_script.rb +8 -4
- data/lib/inferno/apps/cli/main.rb +46 -5
- data/lib/inferno/apps/cli/requirements_coverage_checker.rb +1 -1
- data/lib/inferno/apps/cli/templates/Rakefile.tt +0 -13
- data/lib/inferno/apps/cli/templates/config/nginx.background.conf.tt +0 -30
- data/lib/inferno/apps/cli/templates/config/nginx.conf.tt +0 -30
- data/lib/inferno/apps/cli/templates/docker-compose.background.yml.tt +1 -14
- data/lib/inferno/apps/cli/templates/docker-compose.yml.tt +1 -10
- data/lib/inferno/apps/cli/templates/execution_scripts/{README.md.tt → README.md} +3 -3
- data/lib/inferno/apps/cli/templates/lib/%library_name%/requirements/%library_name%_requirements.csv +3 -0
- data/lib/inferno/apps/cli/templates/lib/%library_name%/requirements/Inferno Requirements Template.xlsx +0 -0
- data/lib/inferno/apps/cli/templates/lib/%library_name%/requirements/README.md +12 -0
- data/lib/inferno/apps/cli/templates/lib/%library_name%/requirements/generated/%test_suite_id%_requirements_coverage.csv.tt +1 -0
- data/lib/inferno/dsl/fhir_resource_navigation.rb +12 -2
- data/lib/inferno/dsl/fhir_resource_validation.rb +186 -4
- data/lib/inferno/dsl/must_support_assessment.rb +19 -8
- data/lib/inferno/dsl/must_support_metadata_extractor.rb +44 -1
- data/lib/inferno/dsl/profile_metadata.rb +113 -0
- data/lib/inferno/dsl/resume_test_route.rb +4 -1
- data/lib/inferno/dsl/suite_endpoint.rb +164 -6
- data/lib/inferno/dsl.rb +1 -0
- data/lib/inferno/entities/ig.rb +51 -8
- data/lib/inferno/public/bundle.js +34 -34
- data/lib/inferno/public/bundle.js.LICENSE.txt +0 -2
- data/lib/inferno/test_runner.rb +55 -40
- data/lib/inferno/utils/execution_script_runner.rb +56 -7
- data/lib/inferno/version.rb +1 -1
- data/spec/spec_helper.rb +18 -0
- metadata +7 -4
- data/lib/inferno/apps/cli/templates/lib/%library_name%/suite.rb.tt +0 -59
data/lib/inferno/test_runner.rb
CHANGED
|
@@ -59,51 +59,66 @@ module Inferno
|
|
|
59
59
|
end
|
|
60
60
|
|
|
61
61
|
def run_test(test, scratch)
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
62
|
+
around_test(test) do
|
|
63
|
+
inputs = load_inputs(test)
|
|
64
|
+
input_json_string = inputs_as_json(test, inputs)
|
|
65
|
+
|
|
66
|
+
test_instance =
|
|
67
|
+
test.new(
|
|
68
|
+
inputs:,
|
|
69
|
+
test_session_id: test_session.id,
|
|
70
|
+
scratch:,
|
|
71
|
+
suite_options: test_session.suite_options_hash
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
result = evaluate_runnable_result(test, test_instance, inputs)
|
|
75
|
+
|
|
76
|
+
outputs = save_outputs(test_instance)
|
|
77
|
+
output_json_string = JSON.generate(outputs)
|
|
78
|
+
|
|
79
|
+
result_params = {
|
|
80
|
+
messages: test_instance.messages,
|
|
81
|
+
requests: test_instance.requests,
|
|
82
|
+
result:,
|
|
83
|
+
result_message: test_instance.result_message,
|
|
84
|
+
input_json: input_json_string,
|
|
85
|
+
output_json: output_json_string
|
|
86
|
+
}.merge(test.reference_hash)
|
|
87
|
+
|
|
88
|
+
test_result =
|
|
89
|
+
if result == 'wait'
|
|
90
|
+
# The waiting status and the wait result must become visible to
|
|
91
|
+
# readers atomically, so that pollers never see the run waiting
|
|
92
|
+
# without the waiting test's result being present.
|
|
93
|
+
Inferno::Application['db.connection'].transaction do
|
|
94
|
+
test_runs_repo.mark_as_waiting(test_run.id, test_instance.identifier, test_instance.wait_timeout)
|
|
95
|
+
persist_result(result_params)
|
|
96
|
+
end
|
|
97
|
+
else
|
|
94
98
|
persist_result(result_params)
|
|
95
99
|
end
|
|
96
|
-
else
|
|
97
|
-
persist_result(result_params)
|
|
98
|
-
end
|
|
99
100
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
101
|
+
# If running a single test, update its parents' results. If running a
|
|
102
|
+
# group or suite, #run_group handles updating the parents.
|
|
103
|
+
update_parent_result(test.parent) if test_run.test_id.present?
|
|
103
104
|
|
|
104
|
-
|
|
105
|
+
test_result
|
|
106
|
+
end
|
|
107
|
+
end
|
|
105
108
|
|
|
106
|
-
|
|
109
|
+
# Wraps the execution of a single test. Prepend a module overriding this method
|
|
110
|
+
# to run instrumentation around each test, for example to emit a distinct trace,
|
|
111
|
+
# span, or timing metric per test instead of one that spans the whole run.
|
|
112
|
+
#
|
|
113
|
+
# An override must call `super` exactly once and return its value unchanged:
|
|
114
|
+
# it is the test's result, and parent results are rolled up from it. An
|
|
115
|
+
# override that does not call `super` silently skips the test.
|
|
116
|
+
#
|
|
117
|
+
# @param test [Class] the test being run (an Inferno::Entities::Test subclass)
|
|
118
|
+
# @yield executes the test and returns its result
|
|
119
|
+
# @return the value returned by the block
|
|
120
|
+
def around_test(_test)
|
|
121
|
+
yield
|
|
107
122
|
end
|
|
108
123
|
|
|
109
124
|
def check_inputs(test, _test_instance, inputs)
|
|
@@ -3,7 +3,11 @@ require 'open3'
|
|
|
3
3
|
module Inferno
|
|
4
4
|
module Utils
|
|
5
5
|
module ExecutionScriptRunner
|
|
6
|
-
def self.run_all(pattern: 'execution_scripts/**/*.yaml', inferno_base_url: nil,
|
|
6
|
+
def self.run_all(pattern: 'execution_scripts/**/*.yaml', inferno_base_url: nil,
|
|
7
|
+
allow_known_errors: false, allow_commands: false,
|
|
8
|
+
compare_messages: true, compare_result_message: true,
|
|
9
|
+
poll_interval: 3, default_poll_timeout: 120,
|
|
10
|
+
only_different_messages: true)
|
|
7
11
|
scripts = Dir.glob(pattern)
|
|
8
12
|
|
|
9
13
|
if scripts.empty?
|
|
@@ -22,7 +26,10 @@ module Inferno
|
|
|
22
26
|
next
|
|
23
27
|
end
|
|
24
28
|
|
|
25
|
-
result = run_script(config, inferno_base_url:, allow_known_errors
|
|
29
|
+
result = run_script(config, inferno_base_url:, allow_known_errors:, allow_commands:,
|
|
30
|
+
compare_messages:, compare_result_message:,
|
|
31
|
+
poll_interval:, default_poll_timeout:,
|
|
32
|
+
only_different_messages:)
|
|
26
33
|
(result == :pass ? passed : failed) << config
|
|
27
34
|
|
|
28
35
|
puts
|
|
@@ -31,19 +38,61 @@ module Inferno
|
|
|
31
38
|
print_summary(passed, failed)
|
|
32
39
|
end
|
|
33
40
|
|
|
34
|
-
def self.run_script(config, inferno_base_url:, allow_known_errors:
|
|
41
|
+
def self.run_script(config, inferno_base_url:, allow_known_errors:, allow_commands: false,
|
|
42
|
+
compare_messages: true, compare_result_message: true,
|
|
43
|
+
poll_interval: 3, default_poll_timeout: 120,
|
|
44
|
+
only_different_messages: true)
|
|
35
45
|
puts '=' * 60
|
|
36
46
|
puts "Running: #{config}"
|
|
37
47
|
puts '=' * 60
|
|
38
48
|
|
|
39
|
-
allow_commands
|
|
49
|
+
allow_commands ||= File.basename(config, '.yaml').include?('_with_commands')
|
|
50
|
+
cmd = build_command(config, inferno_base_url:, allow_commands:, compare_messages:,
|
|
51
|
+
compare_result_message:, poll_interval:, default_poll_timeout:,
|
|
52
|
+
only_different_messages:)
|
|
53
|
+
output, exitstatus = stream_command(*cmd)
|
|
54
|
+
|
|
55
|
+
determine_result(config, exitstatus, output, allow_known_errors)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# @private
|
|
59
|
+
def self.build_command(config, inferno_base_url:, allow_commands:, compare_messages:,
|
|
60
|
+
compare_result_message:, poll_interval:, default_poll_timeout:,
|
|
61
|
+
only_different_messages:)
|
|
40
62
|
cmd = ['bundle', 'exec', 'inferno', 'execute_script', config]
|
|
41
63
|
cmd += ['--inferno-base-url', inferno_base_url] if inferno_base_url
|
|
42
64
|
cmd += ['--allow-commands'] if allow_commands
|
|
43
|
-
|
|
44
|
-
|
|
65
|
+
cmd += ['--poll-interval', poll_interval.to_s] if poll_interval != 3
|
|
66
|
+
cmd += ['--default-poll-timeout', default_poll_timeout.to_s] if default_poll_timeout != 120
|
|
67
|
+
cmd + comparison_flags(compare_messages:, compare_result_message:, only_different_messages:)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# @private
|
|
71
|
+
def self.comparison_flags(compare_messages:, compare_result_message:, only_different_messages:)
|
|
72
|
+
flags = []
|
|
73
|
+
flags << '--no-compare-messages' unless compare_messages
|
|
74
|
+
flags << '--no-compare-result-message' unless compare_result_message
|
|
75
|
+
flags << '--no-only-different-messages' unless only_different_messages
|
|
76
|
+
flags
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# Runs +cmd+ as a subprocess, echoing its combined stdout/stderr to the console
|
|
80
|
+
# line-by-line as it's produced (rather than buffering until the process exits),
|
|
81
|
+
# while still returning the full captured output for result determination.
|
|
82
|
+
def self.stream_command(*cmd)
|
|
83
|
+
output = +''
|
|
84
|
+
exitstatus = nil
|
|
85
|
+
|
|
86
|
+
Open3.popen2e(*cmd) do |stdin, stdout_and_stderr, wait_thread|
|
|
87
|
+
stdin.close
|
|
88
|
+
stdout_and_stderr.each_line do |line|
|
|
89
|
+
puts line
|
|
90
|
+
output << line
|
|
91
|
+
end
|
|
92
|
+
exitstatus = wait_thread.value.exitstatus
|
|
93
|
+
end
|
|
45
94
|
|
|
46
|
-
|
|
95
|
+
[output, exitstatus]
|
|
47
96
|
end
|
|
48
97
|
|
|
49
98
|
def self.determine_result(config, return_code, output, allow_known_errors)
|
data/lib/inferno/version.rb
CHANGED
data/spec/spec_helper.rb
CHANGED
|
@@ -128,6 +128,24 @@ RSpec.configure do |config|
|
|
|
128
128
|
DatabaseCleaner.cleaning { example.run }
|
|
129
129
|
end
|
|
130
130
|
|
|
131
|
+
# Inferno::Entities::Test/TestGroup/TestSuite are meant to be used only as
|
|
132
|
+
# abstract bases -- real tests/groups/suites are always DSL-defined subclasses.
|
|
133
|
+
# Some specs instantiate the bare classes directly (e.g. `Inferno::Test.new`) as
|
|
134
|
+
# lightweight doubles. If anything then reads `.id` on one of those, `Runnable#id`
|
|
135
|
+
# permanently memoizes @id/@base_id/@database_id onto that *shared* class object
|
|
136
|
+
# (since it's a genuinely named Ruby class, `default_id` returns its `Module#name`).
|
|
137
|
+
# Left in place, that id leaks into every subsequently-defined anonymous subclass
|
|
138
|
+
# via `copy_instance_variables`, causing DuplicateEntityIdException collisions in
|
|
139
|
+
# unrelated, later-running examples. Reset it after every example so it can never
|
|
140
|
+
# survive to pollute a different example.
|
|
141
|
+
config.after do
|
|
142
|
+
[Inferno::Entities::Test, Inferno::Entities::TestGroup, Inferno::Entities::TestSuite].each do |klass|
|
|
143
|
+
klass.instance_variable_set(:@id, nil)
|
|
144
|
+
klass.instance_variable_set(:@base_id, nil)
|
|
145
|
+
klass.instance_variable_set(:@database_id, nil)
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
|
|
131
149
|
config.include FactoryBot::Syntax::Methods
|
|
132
150
|
|
|
133
151
|
config.before(:suite) do
|
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: 1.4.
|
|
4
|
+
version: 1.4.4
|
|
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: 2026-
|
|
13
|
+
date: 2026-09-04 00:00:00.000000000 Z
|
|
14
14
|
dependencies:
|
|
15
15
|
- !ruby/object:Gem::Dependency
|
|
16
16
|
name: activesupport
|
|
@@ -556,7 +556,7 @@ files:
|
|
|
556
556
|
- lib/inferno/apps/cli/templates/docs/_Sidebar.md
|
|
557
557
|
- lib/inferno/apps/cli/templates/execution_scripts/%library_name%_script.yaml.tt
|
|
558
558
|
- lib/inferno/apps/cli/templates/execution_scripts/%library_name%_script_expected.json.tt
|
|
559
|
-
- lib/inferno/apps/cli/templates/execution_scripts/README.md
|
|
559
|
+
- lib/inferno/apps/cli/templates/execution_scripts/README.md
|
|
560
560
|
- lib/inferno/apps/cli/templates/lib/%library_name%.rb.tt
|
|
561
561
|
- lib/inferno/apps/cli/templates/lib/%library_name%/example_suite.rb.tt
|
|
562
562
|
- lib/inferno/apps/cli/templates/lib/%library_name%/example_suite/patient_group.rb.tt
|
|
@@ -564,9 +564,11 @@ files:
|
|
|
564
564
|
- lib/inferno/apps/cli/templates/lib/%library_name%/igs/README.md
|
|
565
565
|
- lib/inferno/apps/cli/templates/lib/%library_name%/igs/put_ig_package_dot_tgz_here
|
|
566
566
|
- lib/inferno/apps/cli/templates/lib/%library_name%/metadata.rb.tt
|
|
567
|
+
- lib/inferno/apps/cli/templates/lib/%library_name%/requirements/%library_name%_requirements.csv
|
|
567
568
|
- lib/inferno/apps/cli/templates/lib/%library_name%/requirements/Inferno Requirements
|
|
568
569
|
Template.xlsx
|
|
569
|
-
- lib/inferno/apps/cli/templates/lib/%library_name%/
|
|
570
|
+
- lib/inferno/apps/cli/templates/lib/%library_name%/requirements/README.md
|
|
571
|
+
- lib/inferno/apps/cli/templates/lib/%library_name%/requirements/generated/%test_suite_id%_requirements_coverage.csv.tt
|
|
570
572
|
- lib/inferno/apps/cli/templates/lib/%library_name%/version.rb.tt
|
|
571
573
|
- lib/inferno/apps/cli/templates/run.sh
|
|
572
574
|
- lib/inferno/apps/cli/templates/setup.sh
|
|
@@ -678,6 +680,7 @@ files:
|
|
|
678
680
|
- lib/inferno/dsl/must_support_metadata_extractor.rb
|
|
679
681
|
- lib/inferno/dsl/oauth_credentials.rb
|
|
680
682
|
- lib/inferno/dsl/primitive_type.rb
|
|
683
|
+
- lib/inferno/dsl/profile_metadata.rb
|
|
681
684
|
- lib/inferno/dsl/request_storage.rb
|
|
682
685
|
- lib/inferno/dsl/requirement_set.rb
|
|
683
686
|
- lib/inferno/dsl/result_collection.rb
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
require_relative 'metadata'
|
|
2
|
-
require_relative 'patient_group'
|
|
3
|
-
|
|
4
|
-
module <%= module_name %>
|
|
5
|
-
class Suite < Inferno::TestSuite
|
|
6
|
-
id :<%= test_suite_id %>
|
|
7
|
-
title '<%= title_name %> Test Suite'
|
|
8
|
-
description '<%= human_name %> test suite.'
|
|
9
|
-
|
|
10
|
-
# These inputs will be available to all tests in this suite
|
|
11
|
-
input :url,
|
|
12
|
-
title: 'FHIR Server Base Url'
|
|
13
|
-
|
|
14
|
-
input :credentials,
|
|
15
|
-
title: 'OAuth Credentials',
|
|
16
|
-
type: :auth_info,
|
|
17
|
-
optional: true
|
|
18
|
-
|
|
19
|
-
# All FHIR requests in this suite will use this FHIR client
|
|
20
|
-
fhir_client do
|
|
21
|
-
url :url
|
|
22
|
-
auth_info :credentials
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
# All FHIR validation requests will use this FHIR validator
|
|
26
|
-
fhir_resource_validator do
|
|
27
|
-
# igs 'identifier#version' # Use this method for published IGs/versions
|
|
28
|
-
# igs 'igs/filename.tgz' # Use this otherwise
|
|
29
|
-
|
|
30
|
-
exclude_message do |message|
|
|
31
|
-
message.message.match?(/\A\S+: \S+: URL value '.*' does not resolve/)
|
|
32
|
-
end
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
# Tests and TestGroups can be defined inline
|
|
36
|
-
group do
|
|
37
|
-
id :capability_statement
|
|
38
|
-
title 'Capability Statement'
|
|
39
|
-
description 'Verify that the server has a CapabilityStatement'
|
|
40
|
-
|
|
41
|
-
test do
|
|
42
|
-
id :capability_statement_read
|
|
43
|
-
title 'Read CapabilityStatement'
|
|
44
|
-
description 'Read CapabilityStatement from /metadata endpoint'
|
|
45
|
-
|
|
46
|
-
run do
|
|
47
|
-
fhir_get_capability_statement
|
|
48
|
-
|
|
49
|
-
assert_response_status(200)
|
|
50
|
-
assert_resource_type(:capability_statement)
|
|
51
|
-
end
|
|
52
|
-
end
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
# Tests and TestGroups can be written in separate files and then included
|
|
56
|
-
# using their id
|
|
57
|
-
group from: :patient_group
|
|
58
|
-
end
|
|
59
|
-
end
|