pacio_inferno_core 0.1.0
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 +7 -0
- data/LICENSE +201 -0
- data/lib/pacio_inferno_core/custom_groups/capability_statement/conformance_support_test.rb +41 -0
- data/lib/pacio_inferno_core/custom_groups/capability_statement/fhir_version_test.rb +15 -0
- data/lib/pacio_inferno_core/custom_groups/capability_statement/instantiate_test.rb +23 -0
- data/lib/pacio_inferno_core/custom_groups/capability_statement/json_support_test.rb +40 -0
- data/lib/pacio_inferno_core/date_search_validation.rb +112 -0
- data/lib/pacio_inferno_core/fhir_resource_navigation.rb +7 -0
- data/lib/pacio_inferno_core/generator/group_generator.rb +161 -0
- data/lib/pacio_inferno_core/generator/group_metadata.rb +114 -0
- data/lib/pacio_inferno_core/generator/group_metadata_extractor.rb +301 -0
- data/lib/pacio_inferno_core/generator/ig_loader.rb +118 -0
- data/lib/pacio_inferno_core/generator/ig_metadata.rb +60 -0
- data/lib/pacio_inferno_core/generator/ig_metadata_extractor.rb +88 -0
- data/lib/pacio_inferno_core/generator/ig_resources.rb +88 -0
- data/lib/pacio_inferno_core/generator/must_support_metadata_extractor.rb +8 -0
- data/lib/pacio_inferno_core/generator/must_support_test_generator.rb +138 -0
- data/lib/pacio_inferno_core/generator/naming.rb +50 -0
- data/lib/pacio_inferno_core/generator/read_test_generator.rb +102 -0
- data/lib/pacio_inferno_core/generator/reference_resolution_test_generator.rb +96 -0
- data/lib/pacio_inferno_core/generator/search_definition_metadata_extractor.rb +228 -0
- data/lib/pacio_inferno_core/generator/search_metadata_extractor.rb +78 -0
- data/lib/pacio_inferno_core/generator/search_test_generator.rb +298 -0
- data/lib/pacio_inferno_core/generator/special_cases.rb +61 -0
- data/lib/pacio_inferno_core/generator/suite_generator.rb +105 -0
- data/lib/pacio_inferno_core/generator/templates/group.rb.erb +27 -0
- data/lib/pacio_inferno_core/generator/templates/must_support.rb.erb +36 -0
- data/lib/pacio_inferno_core/generator/templates/read.rb.erb +34 -0
- data/lib/pacio_inferno_core/generator/templates/reference_resolution.rb.erb +40 -0
- data/lib/pacio_inferno_core/generator/templates/search.rb.erb +40 -0
- data/lib/pacio_inferno_core/generator/templates/validation.rb.erb +36 -0
- data/lib/pacio_inferno_core/generator/terminology_binding_metadata_extractor.rb +116 -0
- data/lib/pacio_inferno_core/generator/validation_test_generator.rb +146 -0
- data/lib/pacio_inferno_core/generator/value_extractor.rb +152 -0
- data/lib/pacio_inferno_core/generator.rb +130 -0
- data/lib/pacio_inferno_core/must_support_test.rb +20 -0
- data/lib/pacio_inferno_core/primitive_type.rb +5 -0
- data/lib/pacio_inferno_core/read_test.rb +103 -0
- data/lib/pacio_inferno_core/reference_resolution_test.rb +181 -0
- data/lib/pacio_inferno_core/request_logger.rb +46 -0
- data/lib/pacio_inferno_core/resource_search_param_checker.rb +136 -0
- data/lib/pacio_inferno_core/search_test.rb +859 -0
- data/lib/pacio_inferno_core/search_test_properties.rb +56 -0
- data/lib/pacio_inferno_core/validation_test.rb +55 -0
- data/lib/pacio_inferno_core/version.rb +4 -0
- data/lib/pacio_inferno_core/well_known_code_systems.rb +21 -0
- metadata +165 -0
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
require_relative 'naming'
|
|
2
|
+
require_relative 'special_cases'
|
|
3
|
+
|
|
4
|
+
module PacioInfernoCore
|
|
5
|
+
class Generator
|
|
6
|
+
class SuiteGenerator
|
|
7
|
+
class << self
|
|
8
|
+
def generate(ig_metadata, base_output_dir)
|
|
9
|
+
new(ig_metadata, base_output_dir).generate
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
attr_accessor :ig_metadata, :base_output_dir
|
|
14
|
+
|
|
15
|
+
def initialize(ig_metadata, base_output_dir)
|
|
16
|
+
self.ig_metadata = ig_metadata
|
|
17
|
+
self.base_output_dir = base_output_dir
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def version_specific_message_filters
|
|
21
|
+
[]
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def template
|
|
25
|
+
@template ||= File.read(File.join(__dir__, 'templates', 'suite.rb.erb'))
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def output
|
|
29
|
+
@output ||= ERB.new(template, trim_mode: '-').result(binding)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def naming
|
|
33
|
+
self.class.module_parent::Naming
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def base_output_file_name
|
|
37
|
+
"#{naming.prefix}_test_suite.rb"
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def class_name
|
|
41
|
+
"#{naming.module_name}TestSuite"
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def module_name
|
|
45
|
+
naming.module_name.to_s
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def module_name_with_version
|
|
49
|
+
"#{naming.module_name}#{ig_metadata.reformatted_version.upcase}"
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def output_file_name
|
|
53
|
+
File.join(base_output_dir, base_output_file_name)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def suite_id
|
|
57
|
+
"#{naming.prefix}_#{ig_metadata.reformatted_version}"
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def fhir_api_group_id
|
|
61
|
+
"#{naming.prefix}_#{ig_metadata.reformatted_version}_fhir_api"
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def title
|
|
65
|
+
"#{naming.long_name} #{ig_metadata.ig_version}"
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def ig_identifier
|
|
69
|
+
version = ig_metadata.ig_version[1..] # Remove leading 'v'
|
|
70
|
+
"#{ig_metadata.ig_package_id}##{version}"
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def ig_link
|
|
74
|
+
naming.ig_link(ig_metadata.ig_version)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def generate
|
|
78
|
+
File.write(output_file_name, output)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def groups
|
|
82
|
+
ig_metadata.ordered_groups
|
|
83
|
+
.reject { |group| SpecialCases.exclude_group? group }
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def group_id_list
|
|
87
|
+
@group_id_list ||=
|
|
88
|
+
groups.map(&:id)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def group_file_list
|
|
92
|
+
@group_file_list ||=
|
|
93
|
+
groups.map { |group| group.file_name.delete_suffix('.rb') }
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def capability_statement_file_name
|
|
97
|
+
"../../custom_groups/#{ig_metadata.ig_version}/capability_statement_group"
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def capability_statement_group_id
|
|
101
|
+
"#{naming.prefix}_#{ig_metadata.reformatted_version}_capability_statement"
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
<% test_file_list.each do |file_name| %>require_relative '<%= file_name %>'
|
|
2
|
+
<% end %>
|
|
3
|
+
module <%= naming.module_name %>TestKit
|
|
4
|
+
module <%= module_name_with_version %>
|
|
5
|
+
class <%= class_name %> < Inferno::TestGroup
|
|
6
|
+
title '<%= title %> Tests'
|
|
7
|
+
short_description <<~DESC
|
|
8
|
+
'<%= short_description %>'
|
|
9
|
+
DESC
|
|
10
|
+
description %(
|
|
11
|
+
<%= description %>
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
id :<%= group_id %>
|
|
15
|
+
run_as_group<% if optional? %>
|
|
16
|
+
optional<% end %>
|
|
17
|
+
|
|
18
|
+
def self.metadata
|
|
19
|
+
@metadata ||= Generator::GroupMetadata.new(
|
|
20
|
+
YAML.load_file(File.join(__dir__, '<%= profile_identifier %>', 'metadata.yml'), aliases: true)
|
|
21
|
+
)
|
|
22
|
+
end
|
|
23
|
+
<% test_id_list.each do |id| %>
|
|
24
|
+
test from: :<%= id %><% end %>
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
require_relative '../../../must_support_test'
|
|
2
|
+
|
|
3
|
+
module <%= naming.module_name %>TestKit
|
|
4
|
+
module <%= module_name_with_version %>
|
|
5
|
+
class <%= class_name %> < Inferno::Test
|
|
6
|
+
include PacioInfernoCore::MustSupportTest
|
|
7
|
+
|
|
8
|
+
title 'All must support elements are provided in the <%= resource_type %> resources returned'
|
|
9
|
+
|
|
10
|
+
description %(
|
|
11
|
+
This test will look through the <%= resource_type %> resources
|
|
12
|
+
found previously for the following must support elements:
|
|
13
|
+
|
|
14
|
+
<%= must_support_list_string %>
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
id :<%= test_id %>
|
|
18
|
+
|
|
19
|
+
def resource_type
|
|
20
|
+
'<%= resource_type %>'
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def self.metadata
|
|
24
|
+
@metadata ||= Generator::GroupMetadata.new(YAML.load_file(File.join(__dir__, 'metadata.yml'), aliases: true))
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def scratch_resources
|
|
28
|
+
scratch[:<%= profile_identifier %>_resources] ||= {}
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
run do
|
|
32
|
+
perform_must_support_test(<%= resource_collection_string %>)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
require_relative '../../../read_test'
|
|
2
|
+
|
|
3
|
+
module <%= naming.module_name %>TestKit
|
|
4
|
+
module <%= module_name_with_version %>
|
|
5
|
+
class <%= class_name %> < Inferno::Test
|
|
6
|
+
include PacioInfernoCore::ReadTest
|
|
7
|
+
|
|
8
|
+
title 'Server returns correct <%= resource_type %> resource from <%= resource_type %> read interaction'
|
|
9
|
+
description 'A server <%= conformance_expectation %> support the <%= resource_type %> read interaction.'
|
|
10
|
+
|
|
11
|
+
id :<%= test_id %>
|
|
12
|
+
<% if input_resource_id? %>
|
|
13
|
+
input :<%= resource_id_input_string %>,
|
|
14
|
+
title: 'ID(s) for <%= group_title %> resources present on the server.',
|
|
15
|
+
description: %(
|
|
16
|
+
Comma separated list of <%= group_title %> ids that in sum contain
|
|
17
|
+
all MUST SUPPORT elements
|
|
18
|
+
)<% if optional_profile? %>,
|
|
19
|
+
optional: true<% end %>
|
|
20
|
+
<% end %>
|
|
21
|
+
def resource_type
|
|
22
|
+
'<%= resource_type %>'
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def scratch_resources
|
|
26
|
+
scratch[:<%= profile_identifier %>_resources] ||= {}
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
run do
|
|
30
|
+
perform_read_test(<%= resource_collection_string %>)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
require_relative '../../../reference_resolution_test'
|
|
2
|
+
|
|
3
|
+
module <%= naming.module_name %>TestKit
|
|
4
|
+
module <%= module_name_with_version %>
|
|
5
|
+
class <%= class_name %> < Inferno::Test
|
|
6
|
+
include PacioInfernoCore::ReferenceResolutionTest
|
|
7
|
+
|
|
8
|
+
title 'MustSupport references within <%= resource_type %> resources are valid'
|
|
9
|
+
description %(
|
|
10
|
+
This test will attempt to read external references provided within elements
|
|
11
|
+
marked as 'MustSupport', if any are available.
|
|
12
|
+
|
|
13
|
+
It verifies that at least one external reference for each MustSupport Reference element
|
|
14
|
+
can be accessed by the test client, and conforms to corresponding US Core profile.
|
|
15
|
+
|
|
16
|
+
Elements which may provide external references include:
|
|
17
|
+
|
|
18
|
+
<%= must_support_reference_list_string %>
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
id :<%= test_id %>
|
|
22
|
+
|
|
23
|
+
def resource_type
|
|
24
|
+
'<%= resource_type %>'
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def self.metadata
|
|
28
|
+
@metadata ||= Generator::GroupMetadata.new(YAML.load_file(File.join(__dir__, 'metadata.yml'), aliases: true))
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def scratch_resources
|
|
32
|
+
scratch[:<%= profile_identifier %>_resources] ||= {}
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
run do
|
|
36
|
+
perform_reference_resolution_test(<%= resource_collection_string %>)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
require_relative '../../../search_test'
|
|
2
|
+
require_relative '../../../generator/group_metadata'
|
|
3
|
+
|
|
4
|
+
module <%= naming.module_name %>TestKit
|
|
5
|
+
module <%= module_name_with_version %>
|
|
6
|
+
class <%= class_name %> < Inferno::Test
|
|
7
|
+
include PacioInfernoCore::SearchTest
|
|
8
|
+
|
|
9
|
+
title 'Server returns valid results for <%= resource_type %> search by <%= search_param_name_string %>'
|
|
10
|
+
description %(
|
|
11
|
+
<%= description %>
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
id :<%= test_id %><% if optional? %>
|
|
15
|
+
optional
|
|
16
|
+
<% end %><% if needs_patient_id? %>
|
|
17
|
+
input :patient_ids,
|
|
18
|
+
title: 'Patient IDs',
|
|
19
|
+
description: 'Comma separated list of patient IDs that in sum contain all MUST SUPPORT elements'
|
|
20
|
+
<% end %>
|
|
21
|
+
def self.properties
|
|
22
|
+
@properties ||= SearchTestProperties.new(
|
|
23
|
+
<%= search_test_properties_string %>
|
|
24
|
+
)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def self.metadata
|
|
28
|
+
@metadata ||= Generator::GroupMetadata.new(YAML.load_file(File.join(__dir__, 'metadata.yml'), aliases: true))
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def scratch_resources
|
|
32
|
+
scratch[:<%= profile_identifier %>_resources] ||= {}
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
run do
|
|
36
|
+
run_search_test
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
require_relative '../../../validation_test'
|
|
2
|
+
|
|
3
|
+
module <%= naming.module_name %>TestKit
|
|
4
|
+
module <%= module_name_with_version %>
|
|
5
|
+
class <%= class_name %> < Inferno::Test
|
|
6
|
+
include PacioInfernoCore::ValidationTest
|
|
7
|
+
|
|
8
|
+
id :<%= test_id %>
|
|
9
|
+
|
|
10
|
+
title <<~DESC
|
|
11
|
+
<%= resource_type %> resources returned during previous tests conform to the <%= profile_name %>
|
|
12
|
+
DESC
|
|
13
|
+
|
|
14
|
+
description %(
|
|
15
|
+
<%= description %>
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
output :dar_code_found, :dar_extension_found
|
|
19
|
+
|
|
20
|
+
def resource_type
|
|
21
|
+
'<%= resource_type %>'
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def scratch_resources
|
|
25
|
+
scratch[:<%= profile_identifier %>_resources] ||= {}
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
run do
|
|
29
|
+
perform_validation_test(scratch_resources[:all] || [],
|
|
30
|
+
'<%= profile_url %>',
|
|
31
|
+
'<%= profile_version %>',
|
|
32
|
+
skip_if_empty: <%= skip_if_empty %>)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
module PacioInfernoCore
|
|
2
|
+
class Generator
|
|
3
|
+
class TerminologyBindingMetadataExtractor
|
|
4
|
+
attr_accessor :profile_elements, :ig_resources, :resource
|
|
5
|
+
|
|
6
|
+
def initialize(profile_elements, ig_resources, resource)
|
|
7
|
+
self.profile_elements = profile_elements
|
|
8
|
+
self.ig_resources = ig_resources
|
|
9
|
+
self.resource = resource
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def terminology_bindings
|
|
13
|
+
(element_terminology_bindings + extension_terminology_bindings).compact
|
|
14
|
+
# add_terminology_bindings_from_extensions
|
|
15
|
+
# profile_elements.select { |element| element.type&.first&.code == 'Extension' }
|
|
16
|
+
# .each { |extension| add_terminology_bindings_from_extension(extension) }
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def element_has_fixed_value?(element)
|
|
20
|
+
case element.type.first.code
|
|
21
|
+
when 'Quantity'
|
|
22
|
+
code = profile_elements.find { |e| e.path == "#{element.path}.code" }
|
|
23
|
+
system = profile_elements.find { |e| e.path == "#{element.path}.system" }
|
|
24
|
+
code&.fixedCode || system&.fixedUri
|
|
25
|
+
when 'code'
|
|
26
|
+
element.fixedCode.present?
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def element_has_optional_binding_slice?(element)
|
|
31
|
+
element.sliceName.present? && element.min.zero?
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def profile_elements_with_bindings
|
|
35
|
+
profile_elements
|
|
36
|
+
.select { |element| element.binding.present? && element.binding.strength == 'required' }
|
|
37
|
+
.reject { |element| element_has_fixed_value?(element) || element_has_optional_binding_slice?(element) }
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def element_terminology_bindings
|
|
41
|
+
profile_elements_with_bindings.map do |element|
|
|
42
|
+
binding = {
|
|
43
|
+
type: element.type.first.code,
|
|
44
|
+
strength: element.binding.strength,
|
|
45
|
+
# Goal.target.detail has an unbound binding
|
|
46
|
+
system: element.binding.valueSet&.split('|')&.first,
|
|
47
|
+
path: element.path.gsub('[x]', '').gsub("#{resource}.", '')
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
binding[:required_binding_slice] = true if element.sliceName.present? && element.min.positive?
|
|
51
|
+
|
|
52
|
+
binding
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def extension_profile_elements
|
|
57
|
+
profile_elements
|
|
58
|
+
.select { |element| element.type&.first&.code == 'Extension' }
|
|
59
|
+
.select { |element| extension_profile_url(element).present? }
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def extension_profile_url(extension)
|
|
63
|
+
extension.type.first.profile&.first
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def extension_terminology_bindings
|
|
67
|
+
extension_profile_elements
|
|
68
|
+
.flat_map do |extension_profile_element|
|
|
69
|
+
url = extension_profile_url(extension_profile_element)
|
|
70
|
+
extension = ig_resources.profile_by_url(url)
|
|
71
|
+
|
|
72
|
+
# TODO: Temporaray fix for extension defined out of US Core. FI-1623
|
|
73
|
+
next if extension.nil?
|
|
74
|
+
|
|
75
|
+
elements = extension.snapshot.element
|
|
76
|
+
elements_with_bindings = elements.select do |element|
|
|
77
|
+
element.binding.present? && !element.id.include?('Extension.extension')
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
elements_with_bindings.map do |element|
|
|
81
|
+
{
|
|
82
|
+
type: element.type.first.code,
|
|
83
|
+
strength: element.binding.strength,
|
|
84
|
+
system: element.binding.valueSet&.split('|')&.first,
|
|
85
|
+
path: element.path.gsub('[x]', '').gsub('Extension.', ''),
|
|
86
|
+
extensions: [url]
|
|
87
|
+
}
|
|
88
|
+
end + nested_extension_terminology_bindings(elements, url)
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def nested_extension_terminology_bindings(elements, extension_url)
|
|
93
|
+
nested_extensions = elements.select { |element| element.path == 'Extension.extension' }
|
|
94
|
+
nested_extensions.flat_map do |nested_extension|
|
|
95
|
+
nested_extension_element = elements.find { |element| element.id == "#{nested_extension.id}.url" }
|
|
96
|
+
next unless nested_extension_element.present?
|
|
97
|
+
|
|
98
|
+
nested_extension_url = nested_extension_element.fixedUri
|
|
99
|
+
nested_elements_with_bindings = elements.select do |element|
|
|
100
|
+
element.id.include?(nested_extension.id) && element.binding.present?
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
nested_elements_with_bindings.map do |element|
|
|
104
|
+
{
|
|
105
|
+
type: element.type.first.code,
|
|
106
|
+
strength: element.binding.strength,
|
|
107
|
+
system: element.binding.valueSet&.split('|')&.first,
|
|
108
|
+
path: element.path.gsub('[x]', '').gsub('Extension.extension.', ''),
|
|
109
|
+
extensions: [extension_url, nested_extension_url]
|
|
110
|
+
}
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
end
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
require_relative 'naming'
|
|
2
|
+
require_relative 'special_cases'
|
|
3
|
+
|
|
4
|
+
module PacioInfernoCore
|
|
5
|
+
class Generator
|
|
6
|
+
class ValidationTestGenerator
|
|
7
|
+
class << self
|
|
8
|
+
def generate(ig_metadata, base_output_dir)
|
|
9
|
+
ig_metadata.groups
|
|
10
|
+
.reject { |group| SpecialCases.exclude_group? group }
|
|
11
|
+
.each do |group|
|
|
12
|
+
new(group, base_output_dir: base_output_dir).generate
|
|
13
|
+
next unless group.resource == 'MedicationRequest'
|
|
14
|
+
|
|
15
|
+
# The Medication validation test lives in the MedicationRequest
|
|
16
|
+
# group, so we need to pass in that group's metadata
|
|
17
|
+
medication_group_metadata = ig_metadata.groups.find do |group|
|
|
18
|
+
group.resource == 'Medication'
|
|
19
|
+
end
|
|
20
|
+
new(medication_group_metadata, group, base_output_dir: base_output_dir).generate
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
attr_accessor :group_metadata, :medication_request_metadata, :base_output_dir
|
|
26
|
+
|
|
27
|
+
def initialize(group_metadata, medication_request_metadata = nil, base_output_dir:)
|
|
28
|
+
self.group_metadata = group_metadata
|
|
29
|
+
self.medication_request_metadata = medication_request_metadata
|
|
30
|
+
self.base_output_dir = base_output_dir
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def template
|
|
34
|
+
@template ||= File.read(File.join(__dir__, 'templates', 'validation.rb.erb'))
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def output
|
|
38
|
+
@output ||= ERB.new(template, trim_mode: '-').result(binding)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def naming
|
|
42
|
+
self.class.module_parent::Naming
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def base_output_file_name
|
|
46
|
+
"#{class_name.underscore}.rb"
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def output_file_directory
|
|
50
|
+
File.join(base_output_dir, directory_name)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def output_file_name
|
|
54
|
+
File.join(output_file_directory, base_output_file_name)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def directory_name
|
|
58
|
+
naming.snake_case_for_profile(medication_request_metadata || group_metadata)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def profile_identifier
|
|
62
|
+
naming.snake_case_for_profile(group_metadata)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def profile_url
|
|
66
|
+
group_metadata.profile_url
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def profile_name
|
|
70
|
+
group_metadata.profile_name
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def profile_version
|
|
74
|
+
group_metadata.profile_version
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def test_id
|
|
78
|
+
"#{naming.prefix}_#{group_metadata.reformatted_version}_#{profile_identifier}_validation_test"
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def class_name
|
|
82
|
+
"#{naming.upper_camel_case_for_profile(group_metadata)}ValidationTest"
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def module_name_with_version
|
|
86
|
+
"#{naming.module_name}#{group_metadata.reformatted_version.upcase}"
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def resource_type
|
|
90
|
+
group_metadata.resource
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def conformance_expectation
|
|
94
|
+
read_interaction[:expectation]
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def skip_if_empty
|
|
98
|
+
# Return true if a system must demonstrate at least one example of the resource type.
|
|
99
|
+
# This drives omit vs. skip result statuses in this test.
|
|
100
|
+
resource_type != 'Medication'
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def generate
|
|
104
|
+
FileUtils.mkdir_p(output_file_directory)
|
|
105
|
+
File.write(output_file_name, output)
|
|
106
|
+
|
|
107
|
+
test_metadata = {
|
|
108
|
+
id: test_id,
|
|
109
|
+
file_name: base_output_file_name
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if resource_type == 'Medication'
|
|
113
|
+
medication_request_metadata.add_test(**test_metadata)
|
|
114
|
+
else
|
|
115
|
+
group_metadata.add_test(**test_metadata)
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def description
|
|
120
|
+
<<~DESCRIPTION
|
|
121
|
+
#{description_intro}
|
|
122
|
+
It verifies the presence of mandatory elements and that elements with
|
|
123
|
+
required bindings contain appropriate values. CodeableConcept element
|
|
124
|
+
bindings will fail if none of their codings have a code/system belonging
|
|
125
|
+
to the bound ValueSet. Quantity, Coding, and code element bindings will
|
|
126
|
+
fail if their code/system are not found in the valueset.
|
|
127
|
+
DESCRIPTION
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def description_intro
|
|
131
|
+
if resource_type == 'Medication'
|
|
132
|
+
<<~MEDICATION_INTRO
|
|
133
|
+
This test verifies resources returned from previous tests conform to
|
|
134
|
+
the [#{profile_name}](#{profile_url}).
|
|
135
|
+
MEDICATION_INTRO
|
|
136
|
+
else
|
|
137
|
+
<<~GENERIC_INTRO
|
|
138
|
+
This test verifies resources returned from the first search conform to
|
|
139
|
+
the [#{profile_name}](#{profile_url}).
|
|
140
|
+
Systems must demonstrate at least one valid example in order to pass this test.
|
|
141
|
+
GENERIC_INTRO
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
end
|