inferno_core 1.4.3 → 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/main.rb +46 -5
- 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_validation.rb +90 -4
- 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 +6 -4
- data/lib/inferno/apps/cli/templates/lib/%library_name%/suite.rb.tt +0 -59
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c8c3149fd821b0530f7481d6a01bdef6da32263f1cca74f7b852567edb67af43
|
|
4
|
+
data.tar.gz: df8cd12ccb8054d182655c7537c730cd0a1f6fa538552e98171b93c43300d17b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f6d6c9b29f44b0f3169bee590aac204520aa3fc24d651443b5136958cecfffc889d71172881b4c97323b6b17b65d78afd8fd8027a0b8d933037023345fed975d
|
|
7
|
+
data.tar.gz: 196c88889027e64aa05e3e7a9997749c9dfc5fab9fecdfa1a40437005d2cfd9a5390945f3efc3382ae4c2a3ea3f208ebc5720b5837ca1ad46fd5f08dfdab8678
|
|
@@ -9,6 +9,7 @@ require_relative 'suites'
|
|
|
9
9
|
require_relative 'new'
|
|
10
10
|
require_relative 'execute'
|
|
11
11
|
require_relative 'execute_script'
|
|
12
|
+
require_relative '../../utils/execution_script_runner'
|
|
12
13
|
require_relative '../../version'
|
|
13
14
|
|
|
14
15
|
module Inferno
|
|
@@ -78,8 +79,31 @@ module Inferno
|
|
|
78
79
|
desc 'requirements SUBCOMMAND ...ARGS', 'Perform requirements operations'
|
|
79
80
|
subcommand 'requirements', Requirements
|
|
80
81
|
|
|
81
|
-
desc 'execute_script
|
|
82
|
-
'Run
|
|
82
|
+
desc 'execute_script [PATTERN]',
|
|
83
|
+
'Run one or more session orchestration scripts defined by YAML config file(s).'
|
|
84
|
+
long_desc <<-LONGDESC
|
|
85
|
+
Run a session orchestration script defined by a YAML config file.
|
|
86
|
+
|
|
87
|
+
PATTERN may be a single YAML file, or it may contain wildcards (e.g. glob
|
|
88
|
+
patterns like `*` or `**`) to match and run multiple scripts in sequence.
|
|
89
|
+
When PATTERN matches more than one file, each matching script is run in a
|
|
90
|
+
separate process; a summary of passed and failed scripts is printed at the
|
|
91
|
+
end, and the command exits non-zero if any script failed.
|
|
92
|
+
|
|
93
|
+
If PATTERN is omitted, it defaults to `execution_scripts/**/*.yaml`, running
|
|
94
|
+
every script under the execution_scripts directory.
|
|
95
|
+
|
|
96
|
+
Examples:
|
|
97
|
+
|
|
98
|
+
# Run every script under execution_scripts
|
|
99
|
+
`bundle exec inferno execute_script`
|
|
100
|
+
|
|
101
|
+
# Run a single script
|
|
102
|
+
`bundle exec inferno execute_script execution_scripts/demo/demo_individual_tests.yaml`
|
|
103
|
+
|
|
104
|
+
# Run every script in a directory
|
|
105
|
+
`bundle exec inferno execute_script "execution_scripts/demo/*.yaml"`
|
|
106
|
+
LONGDESC
|
|
83
107
|
option :inferno_base_url,
|
|
84
108
|
aliases: ['-I'],
|
|
85
109
|
type: :string,
|
|
@@ -113,9 +137,26 @@ module Inferno
|
|
|
113
137
|
type: :boolean,
|
|
114
138
|
default: false,
|
|
115
139
|
desc: 'Allow execution script steps that run arbitrary shell commands. ' \
|
|
116
|
-
'Scripts with command: steps will fail unless this flag is set.'
|
|
117
|
-
|
|
118
|
-
|
|
140
|
+
'Scripts with command: steps will fail unless this flag is set. ' \
|
|
141
|
+
'When PATTERN matches multiple scripts, this also applies to all of them ' \
|
|
142
|
+
'(in addition to any individual script whose filename contains "_with_commands").'
|
|
143
|
+
def execute_script(pattern = 'execution_scripts/**/*.yaml')
|
|
144
|
+
matches = Dir.glob(pattern).select { |file| file.end_with?('.yaml', '.yml') }.sort
|
|
145
|
+
|
|
146
|
+
if matches.length > 1 || (matches.empty? && pattern.match?(/[*?\[\]{}]/))
|
|
147
|
+
Utils::ExecutionScriptRunner.run_all(
|
|
148
|
+
pattern:,
|
|
149
|
+
inferno_base_url: options[:inferno_base_url],
|
|
150
|
+
allow_commands: options[:allow_commands],
|
|
151
|
+
compare_messages: options[:compare_messages],
|
|
152
|
+
compare_result_message: options[:compare_result_message],
|
|
153
|
+
poll_interval: options[:poll_interval],
|
|
154
|
+
default_poll_timeout: options[:default_poll_timeout],
|
|
155
|
+
only_different_messages: options[:only_different_messages]
|
|
156
|
+
)
|
|
157
|
+
else
|
|
158
|
+
ExecuteScript.new(matches.first || pattern, options).run
|
|
159
|
+
end
|
|
119
160
|
end
|
|
120
161
|
|
|
121
162
|
desc 'session SUBCOMMAND ...ARGS', 'Perform session operations'
|
|
@@ -5,19 +5,6 @@ begin
|
|
|
5
5
|
rescue LoadError # rubocop:disable Lint/SuppressedException
|
|
6
6
|
end
|
|
7
7
|
|
|
8
|
-
namespace :execute_scripts do
|
|
9
|
-
desc 'Run all execution script YAML files against a local Inferno instance (already running). ' \
|
|
10
|
-
'Optional FILTER env var restricts by File.fnmatch pattern, e.g. FILTER="execution_scripts/demo/*". ' \
|
|
11
|
-
'Optional INFERNO_BASE_URL env var sets the target Inferno URL, e.g. INFERNO_BASE_URL="http://localhost:4567/"'
|
|
12
|
-
task :run_all do
|
|
13
|
-
require 'inferno/utils/execution_script_runner'
|
|
14
|
-
Inferno::Utils::ExecutionScriptRunner.run_all(
|
|
15
|
-
pattern: ENV.fetch('FILTER', 'execution_scripts/**/*.yaml'),
|
|
16
|
-
inferno_base_url: ENV.fetch('INFERNO_BASE_URL', nil)
|
|
17
|
-
)
|
|
18
|
-
end
|
|
19
|
-
end
|
|
20
|
-
|
|
21
8
|
namespace :db do
|
|
22
9
|
desc 'Apply changes to the database'
|
|
23
10
|
task :migrate do
|
|
@@ -53,36 +53,6 @@ http {
|
|
|
53
53
|
# the server will close connections after this time
|
|
54
54
|
keepalive_timeout 600;
|
|
55
55
|
|
|
56
|
-
location /validator {
|
|
57
|
-
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
58
|
-
proxy_set_header Host $http_host;
|
|
59
|
-
proxy_set_header X-Forwarded-Proto $scheme;
|
|
60
|
-
proxy_set_header X-Forwarded-Port $server_port;
|
|
61
|
-
proxy_redirect off;
|
|
62
|
-
proxy_set_header Connection '';
|
|
63
|
-
proxy_http_version 1.1;
|
|
64
|
-
chunked_transfer_encoding off;
|
|
65
|
-
proxy_buffering off;
|
|
66
|
-
proxy_cache off;
|
|
67
|
-
|
|
68
|
-
proxy_pass http://fhir_validator_app;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
location /validatorapi/ {
|
|
72
|
-
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
73
|
-
proxy_set_header Host $http_host;
|
|
74
|
-
proxy_set_header X-Forwarded-Proto $scheme;
|
|
75
|
-
proxy_set_header X-Forwarded-Port $server_port;
|
|
76
|
-
proxy_redirect off;
|
|
77
|
-
proxy_set_header Connection '';
|
|
78
|
-
proxy_http_version 1.1;
|
|
79
|
-
chunked_transfer_encoding off;
|
|
80
|
-
proxy_buffering off;
|
|
81
|
-
proxy_cache off;
|
|
82
|
-
|
|
83
|
-
proxy_pass http://validator_service:4567/;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
56
|
location /hl7validatorapi/ {
|
|
87
57
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
88
58
|
proxy_set_header Host $http_host;
|
|
@@ -68,36 +68,6 @@ http {
|
|
|
68
68
|
proxy_pass http://inferno:4567;
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
-
location /validator {
|
|
72
|
-
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
73
|
-
proxy_set_header Host $http_host;
|
|
74
|
-
proxy_set_header X-Forwarded-Proto $scheme;
|
|
75
|
-
proxy_set_header X-Forwarded-Port $server_port;
|
|
76
|
-
proxy_redirect off;
|
|
77
|
-
proxy_set_header Connection '';
|
|
78
|
-
proxy_http_version 1.1;
|
|
79
|
-
chunked_transfer_encoding off;
|
|
80
|
-
proxy_buffering off;
|
|
81
|
-
proxy_cache off;
|
|
82
|
-
|
|
83
|
-
proxy_pass http://fhir_validator_app;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
location /validatorapi/ {
|
|
87
|
-
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
88
|
-
proxy_set_header Host $http_host;
|
|
89
|
-
proxy_set_header X-Forwarded-Proto $scheme;
|
|
90
|
-
proxy_set_header X-Forwarded-Port $server_port;
|
|
91
|
-
proxy_redirect off;
|
|
92
|
-
proxy_set_header Connection '';
|
|
93
|
-
proxy_http_version 1.1;
|
|
94
|
-
chunked_transfer_encoding off;
|
|
95
|
-
proxy_buffering off;
|
|
96
|
-
proxy_cache off;
|
|
97
|
-
|
|
98
|
-
proxy_pass http://validator_service:4567/;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
71
|
location /hl7validatorapi/ {
|
|
102
72
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
103
73
|
proxy_set_header Host $http_host;
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
version: '3'
|
|
2
1
|
services:
|
|
3
2
|
hl7_validator_service:
|
|
4
3
|
image: infernocommunity/inferno-resource-validator
|
|
@@ -11,18 +10,6 @@ services:
|
|
|
11
10
|
# To let the service share your local FHIR package cache,
|
|
12
11
|
# uncomment the below line
|
|
13
12
|
# - ~/.fhir:/home/ktor/.fhir
|
|
14
|
-
validator_service:
|
|
15
|
-
image: infernocommunity/fhir-validator-service
|
|
16
|
-
# Update this path to match your directory structure
|
|
17
|
-
volumes:
|
|
18
|
-
- ./data/igs:/home/igs
|
|
19
|
-
fhir_validator_app:
|
|
20
|
-
image: infernocommunity/fhir-validator-app
|
|
21
|
-
depends_on:
|
|
22
|
-
- validator_service
|
|
23
|
-
environment:
|
|
24
|
-
EXTERNAL_VALIDATOR_URL: http://localhost/validatorapi
|
|
25
|
-
VALIDATOR_BASE_PATH: /validator
|
|
26
13
|
fhirpath:
|
|
27
14
|
image: infernocommunity/fhirpath-service
|
|
28
15
|
ports:
|
|
@@ -35,7 +22,7 @@ services:
|
|
|
35
22
|
- "80:80"
|
|
36
23
|
command: [nginx, '-g', 'daemon off;']
|
|
37
24
|
depends_on:
|
|
38
|
-
-
|
|
25
|
+
- hl7_validator_service
|
|
39
26
|
- fhirpath
|
|
40
27
|
redis:
|
|
41
28
|
image: redis
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
version: '3'
|
|
2
1
|
services:
|
|
3
2
|
inferno:
|
|
4
3
|
build:
|
|
@@ -6,7 +5,7 @@ services:
|
|
|
6
5
|
volumes:
|
|
7
6
|
- ./data:/opt/inferno/data
|
|
8
7
|
depends_on:
|
|
9
|
-
-
|
|
8
|
+
- hl7_validator_service
|
|
10
9
|
worker:
|
|
11
10
|
build:
|
|
12
11
|
context: ./
|
|
@@ -19,14 +18,6 @@ services:
|
|
|
19
18
|
extends:
|
|
20
19
|
file: docker-compose.background.yml
|
|
21
20
|
service: hl7_validator_service
|
|
22
|
-
validator_service:
|
|
23
|
-
extends:
|
|
24
|
-
file: docker-compose.background.yml
|
|
25
|
-
service: validator_service
|
|
26
|
-
fhir_validator_app:
|
|
27
|
-
extends:
|
|
28
|
-
file: docker-compose.background.yml
|
|
29
|
-
service: fhir_validator_app
|
|
30
21
|
fhirpath:
|
|
31
22
|
extends:
|
|
32
23
|
file: docker-compose.background.yml
|
|
@@ -10,7 +10,7 @@ as `tx.fhir.org`.
|
|
|
10
10
|
|
|
11
11
|
Execution scripts defined here will
|
|
12
12
|
be [executed](https://inferno-framework.github.io/docs/advanced-test-features/scripting-execution.html#execution)
|
|
13
|
-
automatically on pull requests as a part of the
|
|
13
|
+
automatically on pull requests as a part of the GitHub
|
|
14
14
|
workflows and can also be executed locally using the
|
|
15
|
-
[`execute_script` CLI](https://inferno-framework.github.io/docs/getting-started/inferno-cli.html#complex-scripted-execution)
|
|
16
|
-
|
|
15
|
+
[`execute_script` CLI](https://inferno-framework.github.io/docs/getting-started/inferno-cli.html#complex-scripted-execution).
|
|
16
|
+
If no `.yaml` files are found under this directory, the GitHub workflow will be skipped.
|
data/lib/inferno/apps/cli/templates/lib/%library_name%/requirements/%library_name%_requirements.csv
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
Req Set,ID,URL,Requirement,Conformance,Actors,Sub-Requirement(s),Conditionality,Not Tested Reason,Not Tested Details
|
|
2
|
+
sample_v1,12,www.example.com/page.html#section,Requirement text SHALL go here,SHALL,Server,sample_v1@13,false,Not Tested,We've decided not to test this requirement
|
|
3
|
+
sample_v1,13,www.example.com/page.html#section,"This is a subsrequirement, and it SHOULD be implemented.",SHOULD,Server,,false,,
|
|
Binary file
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# Using Core Requirements Tools
|
|
2
|
+
|
|
3
|
+
This directory contains [requirement artifacts](https://inferno-framework.github.io/docs/advanced-test-features/requirements.html)
|
|
4
|
+
that contain example requirements that could be linked to tests
|
|
5
|
+
within this test kit and displayed within the UI as justification
|
|
6
|
+
for the tests.
|
|
7
|
+
|
|
8
|
+
Each `.xlsx` file defined in this directory will contribute towards the
|
|
9
|
+
test kit's requirements and a GitHub workflow executed automatically on pull
|
|
10
|
+
requests checks that these files are in sync with the generated requirement
|
|
11
|
+
list and coverage analysis `.csv` files. If no `.xlsx` files are found in
|
|
12
|
+
this directory, the GitHub workflow will be skipped.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Req Set,ID,URL,Requirement,Conformance,Actors,Conditionality,Not Tested Reason,Not Tested Details,<%= title_name %> Short ID(s),<%= title_name %> Full ID(s)
|
|
@@ -55,6 +55,19 @@ module Inferno
|
|
|
55
55
|
@validator_session_repo ||= Inferno::Repositories::ValidatorSessions.new
|
|
56
56
|
end
|
|
57
57
|
|
|
58
|
+
# Environment variable that, when set to a truthy-looking value,
|
|
59
|
+
# enables verbose validation logging (the validationContext sent
|
|
60
|
+
# with each request, and the resulting issues including which were
|
|
61
|
+
# filtered out) through Inferno's normal application logger, tagged
|
|
62
|
+
# with the validator definition and, when available, the test
|
|
63
|
+
# session and test that triggered the request.
|
|
64
|
+
VALIDATOR_DEBUG_LOGGING_ENV_VAR = 'FHIR_RESOURCE_VALIDATOR_DEBUG_LOGGING'.freeze
|
|
65
|
+
|
|
66
|
+
# @private
|
|
67
|
+
def debug_logging_enabled?
|
|
68
|
+
ENV.fetch(VALIDATOR_DEBUG_LOGGING_ENV_VAR, nil).present?
|
|
69
|
+
end
|
|
70
|
+
|
|
58
71
|
# Set the url of the validator service
|
|
59
72
|
#
|
|
60
73
|
# @param validator_url [String]
|
|
@@ -212,6 +225,36 @@ module Inferno
|
|
|
212
225
|
}
|
|
213
226
|
end
|
|
214
227
|
|
|
228
|
+
# Environment variable that, when set, enables terminology server
|
|
229
|
+
# request logging by the validator itself. Its value is sent as
|
|
230
|
+
# `txLog` in the validationContext of every validation request,
|
|
231
|
+
# telling the validator where to log the terminology server
|
|
232
|
+
# requests it makes. Note that the log will appear within
|
|
233
|
+
# the container running the validator.
|
|
234
|
+
TX_LOG_ENV_VAR = 'FHIR_RESOURCE_VALIDATOR_TX_LOG'.freeze
|
|
235
|
+
|
|
236
|
+
# @private
|
|
237
|
+
def tx_log
|
|
238
|
+
ENV.fetch(TX_LOG_ENV_VAR, nil).presence
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
# @private
|
|
242
|
+
# Builds the validationContext sent with a request for the given
|
|
243
|
+
# profile. Pulled out on its own (rather than inlined in
|
|
244
|
+
# `wrap_target_for_hl7_wrapper`) so the same, resource-content-free
|
|
245
|
+
# context can also be used for logging in `log_validation_result`.
|
|
246
|
+
#
|
|
247
|
+
# @param profile_url [String]
|
|
248
|
+
# @return [Hash]
|
|
249
|
+
def build_validation_context(profile_url)
|
|
250
|
+
context = {
|
|
251
|
+
**validation_context.definition,
|
|
252
|
+
profiles: [profile_url]
|
|
253
|
+
}
|
|
254
|
+
context[:txLog] = tx_log if tx_log
|
|
255
|
+
context
|
|
256
|
+
end
|
|
257
|
+
|
|
215
258
|
# @private
|
|
216
259
|
# Used internally by perform_additional_validation
|
|
217
260
|
def additional_validations
|
|
@@ -334,6 +377,7 @@ module Inferno
|
|
|
334
377
|
|
|
335
378
|
# 4. Mark resources as filtered
|
|
336
379
|
mark_issues_for_filtering(issues)
|
|
380
|
+
log_validation_result(profile_url, issues, runnable) if debug_logging_enabled?
|
|
337
381
|
|
|
338
382
|
# 5. Add error messages to runnable
|
|
339
383
|
filtered_issues = issues.reject(&:filtered)
|
|
@@ -447,12 +491,57 @@ module Inferno
|
|
|
447
491
|
# @private
|
|
448
492
|
def call_validator(target, profile_url)
|
|
449
493
|
request_body = wrap_target_for_hl7_wrapper(target, profile_url)
|
|
494
|
+
|
|
450
495
|
Faraday.new(
|
|
451
496
|
url,
|
|
452
497
|
request: { timeout: 600 }
|
|
453
498
|
).post('validate', request_body, content_type: 'application/json')
|
|
454
499
|
end
|
|
455
500
|
|
|
501
|
+
# @private
|
|
502
|
+
# Logs the validationContext and expansionParameters sent with a
|
|
503
|
+
# request together with the resulting issues (including which were
|
|
504
|
+
# filtered out) in a single entry, tagged with enough context to
|
|
505
|
+
# trace it back to the validator definition and triggering test run.
|
|
506
|
+
#
|
|
507
|
+
# Deliberately omits the resource content itself (sent separately as
|
|
508
|
+
# `filesToValidate`): only the small, non-PHI-bearing
|
|
509
|
+
# validationContext and expansionParameters are logged, not the full
|
|
510
|
+
# request body.
|
|
511
|
+
#
|
|
512
|
+
# @param profile_url [String] the profile URL validated against
|
|
513
|
+
# @param issues [Array<ValidatorIssue>] the resulting issues, already marked for filtering
|
|
514
|
+
# @param runnable [Object] the runnable (typically a Test) that triggered the request
|
|
515
|
+
def log_validation_result(profile_url, issues, runnable)
|
|
516
|
+
payload = {
|
|
517
|
+
validator_name: name,
|
|
518
|
+
test_suite_id: test_suite_id,
|
|
519
|
+
test_session_id: runnable.respond_to?(:test_session_id) ? runnable.test_session_id : nil,
|
|
520
|
+
test_id: runnable.id,
|
|
521
|
+
validation_context: build_validation_context(profile_url),
|
|
522
|
+
expansion_parameters:,
|
|
523
|
+
issues: issues.map { |issue| issue_summary(issue) }
|
|
524
|
+
}.compact
|
|
525
|
+
|
|
526
|
+
Application[:logger].info("FHIR validation result: #{payload.to_json}")
|
|
527
|
+
end
|
|
528
|
+
|
|
529
|
+
# @private
|
|
530
|
+
# Recursively builds a loggable summary of a validation issue,
|
|
531
|
+
# including nested slice_info, without any resource content.
|
|
532
|
+
#
|
|
533
|
+
# @param issue [ValidatorIssue]
|
|
534
|
+
# @return [Hash]
|
|
535
|
+
def issue_summary(issue)
|
|
536
|
+
{
|
|
537
|
+
severity: issue.severity,
|
|
538
|
+
location: issue.location,
|
|
539
|
+
message: issue.message,
|
|
540
|
+
filtered: issue.filtered,
|
|
541
|
+
slice_info: issue.slice_info.any? ? issue.slice_info.map { |nested| issue_summary(nested) } : nil
|
|
542
|
+
}.compact
|
|
543
|
+
end
|
|
544
|
+
|
|
456
545
|
# @private
|
|
457
546
|
# Post an object to the validation service for validating.
|
|
458
547
|
# Returns the raw validator response body.
|
|
@@ -703,10 +792,7 @@ module Inferno
|
|
|
703
792
|
end
|
|
704
793
|
|
|
705
794
|
wrapped_resource = {
|
|
706
|
-
context_key =>
|
|
707
|
-
**validation_context.definition,
|
|
708
|
-
profiles: [profile_url]
|
|
709
|
-
},
|
|
795
|
+
context_key => build_validation_context(profile_url),
|
|
710
796
|
filesToValidate: [
|
|
711
797
|
{
|
|
712
798
|
fileName: "#{profile_url.split('/').last}.json",
|
|
@@ -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
|
|
@@ -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
|