davinci_us_drug_formulary_test_kit 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (116) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +201 -0
  3. data/lib/davinci_us_drug_formulary_test_kit/custom_groups/capability_statement/conformance_support_test.rb +41 -0
  4. data/lib/davinci_us_drug_formulary_test_kit/custom_groups/capability_statement/fhir_version_test.rb +15 -0
  5. data/lib/davinci_us_drug_formulary_test_kit/custom_groups/capability_statement/instantiate_test.rb +19 -0
  6. data/lib/davinci_us_drug_formulary_test_kit/custom_groups/capability_statement/json_support_test.rb +40 -0
  7. data/lib/davinci_us_drug_formulary_test_kit/custom_groups/capability_statement/profile_support_test.rb +39 -0
  8. data/lib/davinci_us_drug_formulary_test_kit/custom_groups/v2.0.1/capability_statement_group.rb +78 -0
  9. data/lib/davinci_us_drug_formulary_test_kit/date_search_validation.rb +121 -0
  10. data/lib/davinci_us_drug_formulary_test_kit/fhir_resource_navigation.rb +155 -0
  11. data/lib/davinci_us_drug_formulary_test_kit/generated/v2.0.1/basic/basic_code_search_test.rb +54 -0
  12. data/lib/davinci_us_drug_formulary_test_kit/generated/v2.0.1/basic/basic_drug_tier_search_test.rb +43 -0
  13. data/lib/davinci_us_drug_formulary_test_kit/generated/v2.0.1/basic/basic_formulary_include_search_test.rb +40 -0
  14. data/lib/davinci_us_drug_formulary_test_kit/generated/v2.0.1/basic/basic_formulary_search_test.rb +43 -0
  15. data/lib/davinci_us_drug_formulary_test_kit/generated/v2.0.1/basic/basic_id_search_test.rb +42 -0
  16. data/lib/davinci_us_drug_formulary_test_kit/generated/v2.0.1/basic/basic_lastupdated_search_test.rb +42 -0
  17. data/lib/davinci_us_drug_formulary_test_kit/generated/v2.0.1/basic/basic_must_support_test.rb +46 -0
  18. data/lib/davinci_us_drug_formulary_test_kit/generated/v2.0.1/basic/basic_period_search_test.rb +42 -0
  19. data/lib/davinci_us_drug_formulary_test_kit/generated/v2.0.1/basic/basic_pharmacy_benefit_type_search_test.rb +43 -0
  20. data/lib/davinci_us_drug_formulary_test_kit/generated/v2.0.1/basic/basic_read_test.rb +26 -0
  21. data/lib/davinci_us_drug_formulary_test_kit/generated/v2.0.1/basic/basic_reference_resolution_test.rb +40 -0
  22. data/lib/davinci_us_drug_formulary_test_kit/generated/v2.0.1/basic/basic_status_search_test.rb +42 -0
  23. data/lib/davinci_us_drug_formulary_test_kit/generated/v2.0.1/basic/basic_subject_include_search_test.rb +40 -0
  24. data/lib/davinci_us_drug_formulary_test_kit/generated/v2.0.1/basic/basic_subject_search_test.rb +43 -0
  25. data/lib/davinci_us_drug_formulary_test_kit/generated/v2.0.1/basic/basic_validation_test.rb +39 -0
  26. data/lib/davinci_us_drug_formulary_test_kit/generated/v2.0.1/basic/metadata.yml +292 -0
  27. data/lib/davinci_us_drug_formulary_test_kit/generated/v2.0.1/basic_group.rb +107 -0
  28. data/lib/davinci_us_drug_formulary_test_kit/generated/v2.0.1/formulary/formulary_coverage_area_search_test.rb +45 -0
  29. data/lib/davinci_us_drug_formulary_test_kit/generated/v2.0.1/formulary/formulary_coverage_type_search_test.rb +45 -0
  30. data/lib/davinci_us_drug_formulary_test_kit/generated/v2.0.1/formulary/formulary_formulary_coverage_search_test.rb +45 -0
  31. data/lib/davinci_us_drug_formulary_test_kit/generated/v2.0.1/formulary/formulary_id_search_test.rb +42 -0
  32. data/lib/davinci_us_drug_formulary_test_kit/generated/v2.0.1/formulary/formulary_identifier_search_test.rb +43 -0
  33. data/lib/davinci_us_drug_formulary_test_kit/generated/v2.0.1/formulary/formulary_lastupdated_search_test.rb +42 -0
  34. data/lib/davinci_us_drug_formulary_test_kit/generated/v2.0.1/formulary/formulary_must_support_test.rb +42 -0
  35. data/lib/davinci_us_drug_formulary_test_kit/generated/v2.0.1/formulary/formulary_name_search_test.rb +42 -0
  36. data/lib/davinci_us_drug_formulary_test_kit/generated/v2.0.1/formulary/formulary_period_search_test.rb +42 -0
  37. data/lib/davinci_us_drug_formulary_test_kit/generated/v2.0.1/formulary/formulary_read_test.rb +26 -0
  38. data/lib/davinci_us_drug_formulary_test_kit/generated/v2.0.1/formulary/formulary_status_search_test.rb +53 -0
  39. data/lib/davinci_us_drug_formulary_test_kit/generated/v2.0.1/formulary/formulary_type_search_test.rb +43 -0
  40. data/lib/davinci_us_drug_formulary_test_kit/generated/v2.0.1/formulary/formulary_validation_test.rb +39 -0
  41. data/lib/davinci_us_drug_formulary_test_kit/generated/v2.0.1/formulary/metadata.yml +278 -0
  42. data/lib/davinci_us_drug_formulary_test_kit/generated/v2.0.1/formulary_group.rb +104 -0
  43. data/lib/davinci_us_drug_formulary_test_kit/generated/v2.0.1/location/location_address_city_search_test.rb +44 -0
  44. data/lib/davinci_us_drug_formulary_test_kit/generated/v2.0.1/location/location_address_postalcode_search_test.rb +44 -0
  45. data/lib/davinci_us_drug_formulary_test_kit/generated/v2.0.1/location/location_address_search_test.rb +42 -0
  46. data/lib/davinci_us_drug_formulary_test_kit/generated/v2.0.1/location/location_address_state_search_test.rb +44 -0
  47. data/lib/davinci_us_drug_formulary_test_kit/generated/v2.0.1/location/location_id_search_test.rb +56 -0
  48. data/lib/davinci_us_drug_formulary_test_kit/generated/v2.0.1/location/location_lastupdated_search_test.rb +44 -0
  49. data/lib/davinci_us_drug_formulary_test_kit/generated/v2.0.1/location/location_must_support_test.rb +37 -0
  50. data/lib/davinci_us_drug_formulary_test_kit/generated/v2.0.1/location/location_read_test.rb +26 -0
  51. data/lib/davinci_us_drug_formulary_test_kit/generated/v2.0.1/location/location_validation_test.rb +39 -0
  52. data/lib/davinci_us_drug_formulary_test_kit/generated/v2.0.1/location/metadata.yml +177 -0
  53. data/lib/davinci_us_drug_formulary_test_kit/generated/v2.0.1/location_group.rb +92 -0
  54. data/lib/davinci_us_drug_formulary_test_kit/generated/v2.0.1/medication_knowledge/medication_knowledge_code_search_test.rb +43 -0
  55. data/lib/davinci_us_drug_formulary_test_kit/generated/v2.0.1/medication_knowledge/medication_knowledge_doseform_search_test.rb +45 -0
  56. data/lib/davinci_us_drug_formulary_test_kit/generated/v2.0.1/medication_knowledge/medication_knowledge_drug_name_search_test.rb +44 -0
  57. data/lib/davinci_us_drug_formulary_test_kit/generated/v2.0.1/medication_knowledge/medication_knowledge_id_search_test.rb +42 -0
  58. data/lib/davinci_us_drug_formulary_test_kit/generated/v2.0.1/medication_knowledge/medication_knowledge_lastupdated_search_test.rb +42 -0
  59. data/lib/davinci_us_drug_formulary_test_kit/generated/v2.0.1/medication_knowledge/medication_knowledge_must_support_test.rb +47 -0
  60. data/lib/davinci_us_drug_formulary_test_kit/generated/v2.0.1/medication_knowledge/medication_knowledge_read_test.rb +26 -0
  61. data/lib/davinci_us_drug_formulary_test_kit/generated/v2.0.1/medication_knowledge/medication_knowledge_status_search_test.rb +53 -0
  62. data/lib/davinci_us_drug_formulary_test_kit/generated/v2.0.1/medication_knowledge/medication_knowledge_validation_test.rb +39 -0
  63. data/lib/davinci_us_drug_formulary_test_kit/generated/v2.0.1/medication_knowledge/metadata.yml +214 -0
  64. data/lib/davinci_us_drug_formulary_test_kit/generated/v2.0.1/medication_knowledge_group.rb +91 -0
  65. data/lib/davinci_us_drug_formulary_test_kit/generated/v2.0.1/metadata.yml +1192 -0
  66. data/lib/davinci_us_drug_formulary_test_kit/generated/v2.0.1/payer_insurance_plan/metadata.yml +371 -0
  67. data/lib/davinci_us_drug_formulary_test_kit/generated/v2.0.1/payer_insurance_plan/payer_insurance_plan_coverage_area_search_test.rb +43 -0
  68. data/lib/davinci_us_drug_formulary_test_kit/generated/v2.0.1/payer_insurance_plan/payer_insurance_plan_coverage_type_search_test.rb +43 -0
  69. data/lib/davinci_us_drug_formulary_test_kit/generated/v2.0.1/payer_insurance_plan/payer_insurance_plan_formulary_coverage_include_search_test.rb +40 -0
  70. data/lib/davinci_us_drug_formulary_test_kit/generated/v2.0.1/payer_insurance_plan/payer_insurance_plan_formulary_coverage_search_test.rb +43 -0
  71. data/lib/davinci_us_drug_formulary_test_kit/generated/v2.0.1/payer_insurance_plan/payer_insurance_plan_id_search_test.rb +42 -0
  72. data/lib/davinci_us_drug_formulary_test_kit/generated/v2.0.1/payer_insurance_plan/payer_insurance_plan_identifier_search_test.rb +43 -0
  73. data/lib/davinci_us_drug_formulary_test_kit/generated/v2.0.1/payer_insurance_plan/payer_insurance_plan_lastupdated_search_test.rb +42 -0
  74. data/lib/davinci_us_drug_formulary_test_kit/generated/v2.0.1/payer_insurance_plan/payer_insurance_plan_must_support_test.rb +66 -0
  75. data/lib/davinci_us_drug_formulary_test_kit/generated/v2.0.1/payer_insurance_plan/payer_insurance_plan_name_search_test.rb +42 -0
  76. data/lib/davinci_us_drug_formulary_test_kit/generated/v2.0.1/payer_insurance_plan/payer_insurance_plan_period_search_test.rb +42 -0
  77. data/lib/davinci_us_drug_formulary_test_kit/generated/v2.0.1/payer_insurance_plan/payer_insurance_plan_read_test.rb +26 -0
  78. data/lib/davinci_us_drug_formulary_test_kit/generated/v2.0.1/payer_insurance_plan/payer_insurance_plan_reference_resolution_test.rb +40 -0
  79. data/lib/davinci_us_drug_formulary_test_kit/generated/v2.0.1/payer_insurance_plan/payer_insurance_plan_status_search_test.rb +53 -0
  80. data/lib/davinci_us_drug_formulary_test_kit/generated/v2.0.1/payer_insurance_plan/payer_insurance_plan_type_search_test.rb +43 -0
  81. data/lib/davinci_us_drug_formulary_test_kit/generated/v2.0.1/payer_insurance_plan/payer_insurance_plan_validation_test.rb +39 -0
  82. data/lib/davinci_us_drug_formulary_test_kit/generated/v2.0.1/payer_insurance_plan_group.rb +108 -0
  83. data/lib/davinci_us_drug_formulary_test_kit/generated/v2.0.1/usdf_test_suite.rb +114 -0
  84. data/lib/davinci_us_drug_formulary_test_kit/generator/group_generator.rb +181 -0
  85. data/lib/davinci_us_drug_formulary_test_kit/generator/group_metadata.rb +79 -0
  86. data/lib/davinci_us_drug_formulary_test_kit/generator/group_metadata_extractor.rb +329 -0
  87. data/lib/davinci_us_drug_formulary_test_kit/generator/ig_loader.rb +77 -0
  88. data/lib/davinci_us_drug_formulary_test_kit/generator/ig_metadata.rb +33 -0
  89. data/lib/davinci_us_drug_formulary_test_kit/generator/ig_metadata_extractor.rb +40 -0
  90. data/lib/davinci_us_drug_formulary_test_kit/generator/ig_resources.rb +60 -0
  91. data/lib/davinci_us_drug_formulary_test_kit/generator/include_search_test_generator.rb +68 -0
  92. data/lib/davinci_us_drug_formulary_test_kit/generator/must_support_metadata_extractor.rb +384 -0
  93. data/lib/davinci_us_drug_formulary_test_kit/generator/must_support_test_generator.rb +117 -0
  94. data/lib/davinci_us_drug_formulary_test_kit/generator/naming.rb +28 -0
  95. data/lib/davinci_us_drug_formulary_test_kit/generator/read_test_generator.rb +92 -0
  96. data/lib/davinci_us_drug_formulary_test_kit/generator/reference_resolution_test_generator.rb +91 -0
  97. data/lib/davinci_us_drug_formulary_test_kit/generator/search_definition_metadata_extractor.rb +187 -0
  98. data/lib/davinci_us_drug_formulary_test_kit/generator/search_metadata_extractor.rb +59 -0
  99. data/lib/davinci_us_drug_formulary_test_kit/generator/search_test_generator.rb +270 -0
  100. data/lib/davinci_us_drug_formulary_test_kit/generator/suite_generator.rb +94 -0
  101. data/lib/davinci_us_drug_formulary_test_kit/generator/terminology_binding_metadata_extractor.rb +116 -0
  102. data/lib/davinci_us_drug_formulary_test_kit/generator/validation_test_generator.rb +102 -0
  103. data/lib/davinci_us_drug_formulary_test_kit/generator/value_extractor.rb +113 -0
  104. data/lib/davinci_us_drug_formulary_test_kit/generator.rb +94 -0
  105. data/lib/davinci_us_drug_formulary_test_kit/igs/package/r4_search-parameters.json +65408 -0
  106. data/lib/davinci_us_drug_formulary_test_kit/igs/package.tgz +0 -0
  107. data/lib/davinci_us_drug_formulary_test_kit/must_support_test.rb +224 -0
  108. data/lib/davinci_us_drug_formulary_test_kit/read_test.rb +62 -0
  109. data/lib/davinci_us_drug_formulary_test_kit/reference_resolution_test.rb +174 -0
  110. data/lib/davinci_us_drug_formulary_test_kit/request_logger.rb +46 -0
  111. data/lib/davinci_us_drug_formulary_test_kit/search_test.rb +767 -0
  112. data/lib/davinci_us_drug_formulary_test_kit/search_test_properties.rb +58 -0
  113. data/lib/davinci_us_drug_formulary_test_kit/validation_test.rb +56 -0
  114. data/lib/davinci_us_drug_formulary_test_kit/version.rb +5 -0
  115. data/lib/davinci_us_drug_formulary_test_kit.rb +1 -0
  116. metadata +245 -0
@@ -0,0 +1,92 @@
1
+ require_relative 'naming'
2
+
3
+ module DaVinciUSDrugFormularyTestKit
4
+ class Generator
5
+ class ReadTestGenerator
6
+ class << self
7
+ def generate(ig_metadata, base_output_dir)
8
+ ig_metadata.groups
9
+ .select { |group| read_interaction(group).present? }
10
+ .each { |group| new(group, base_output_dir).generate }
11
+ end
12
+
13
+ def read_interaction(group_metadata)
14
+ group_metadata.interactions.find { |interaction| interaction[:code] == 'read' }
15
+ end
16
+ end
17
+
18
+ attr_accessor :group_metadata, :base_output_dir
19
+
20
+ def initialize(group_metadata, base_output_dir)
21
+ self.group_metadata = group_metadata
22
+ self.base_output_dir = base_output_dir
23
+ end
24
+
25
+ def template
26
+ @template ||= File.read(File.join(__dir__, 'templates', 'read.rb.erb'))
27
+ end
28
+
29
+ def output
30
+ @output ||= ERB.new(template).result(binding)
31
+ end
32
+
33
+ def base_output_file_name
34
+ "#{class_name.underscore}.rb"
35
+ end
36
+
37
+ def output_file_directory
38
+ File.join(base_output_dir, profile_identifier)
39
+ end
40
+
41
+ def output_file_name
42
+ File.join(output_file_directory, base_output_file_name)
43
+ end
44
+
45
+ def read_interaction
46
+ self.class.read_interaction(group_metadata)
47
+ end
48
+
49
+ def profile_identifier
50
+ Naming.snake_case_for_profile(group_metadata)
51
+ end
52
+
53
+ def test_id
54
+ "usdf_#{group_metadata.reformatted_version}_#{profile_identifier}_read_test"
55
+ end
56
+
57
+ def class_name
58
+ "#{Naming.upper_camel_case_for_profile(group_metadata)}ReadTest"
59
+ end
60
+
61
+ def module_name
62
+ "USDF#{group_metadata.reformatted_version.upcase}"
63
+ end
64
+
65
+ def resource_type
66
+ group_metadata.resource
67
+ end
68
+
69
+ def resource_collection_string
70
+ if group_metadata.delayed? && resource_type != 'Provenance'
71
+ "scratch.dig(:references, '#{resource_type}')"
72
+ else
73
+ 'all_scratch_resources'
74
+ end
75
+ end
76
+
77
+ def conformance_expectation
78
+ read_interaction[:expectation]
79
+ end
80
+
81
+ def generate
82
+ FileUtils.mkdir_p(output_file_directory)
83
+ File.write(output_file_name, output)
84
+
85
+ group_metadata.add_test(
86
+ id: test_id,
87
+ file_name: base_output_file_name
88
+ )
89
+ end
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,91 @@
1
+ require_relative 'naming'
2
+
3
+ module DaVinciUSDrugFormularyTestKit
4
+ class Generator
5
+ class ReferenceResolutionTestGenerator
6
+ class << self
7
+ def generate(ig_metadata, base_output_dir)
8
+ ig_metadata.groups
9
+ .each { |group| new(group, base_output_dir).generate }
10
+ end
11
+ end
12
+
13
+ attr_accessor :group_metadata, :base_output_dir
14
+
15
+ def initialize(group_metadata, base_output_dir)
16
+ self.group_metadata = group_metadata
17
+ self.base_output_dir = base_output_dir
18
+ end
19
+
20
+ def template
21
+ @template ||= File.read(File.join(__dir__, 'templates', 'reference_resolution.rb.erb'))
22
+ end
23
+
24
+ def output
25
+ @output ||= ERB.new(template).result(binding)
26
+ end
27
+
28
+ def base_output_file_name
29
+ "#{class_name.underscore}.rb"
30
+ end
31
+
32
+ def output_file_directory
33
+ File.join(base_output_dir, profile_identifier)
34
+ end
35
+
36
+ def output_file_name
37
+ File.join(output_file_directory, base_output_file_name)
38
+ end
39
+
40
+ def profile_identifier
41
+ Naming.snake_case_for_profile(group_metadata)
42
+ end
43
+
44
+ def test_id
45
+ "usdf_#{group_metadata.reformatted_version}_#{profile_identifier}_reference_resolution_test"
46
+ end
47
+
48
+ def class_name
49
+ "#{Naming.upper_camel_case_for_profile(group_metadata)}ReferenceResolutionTest"
50
+ end
51
+
52
+ def module_name
53
+ "DaVinciUSDrugFormulary#{group_metadata.reformatted_version.upcase}"
54
+ end
55
+
56
+ def resource_type
57
+ group_metadata.resource
58
+ end
59
+
60
+ def resource_collection_string
61
+ 'scratch_resources[:all]'
62
+ end
63
+
64
+ def must_support_references
65
+ group_metadata.must_supports[:references]
66
+ end
67
+
68
+ def must_support_reference_list_string
69
+ must_support_references
70
+ .map { |element| "#{' ' * 8}* #{resource_type}.#{element[:path]}" }
71
+ .map do |string|
72
+ string
73
+ .gsub(".where(url='http://hl7.org/fhir/us/davinci-drug-formulary/StructureDefinition/", ':')
74
+ .gsub("')", '')
75
+ end.uniq.sort.join("\n")
76
+ end
77
+
78
+ def generate
79
+ return if must_support_references.empty?
80
+
81
+ FileUtils.mkdir_p(output_file_directory)
82
+ File.write(output_file_name, output)
83
+
84
+ group_metadata.add_test(
85
+ id: test_id,
86
+ file_name: base_output_file_name
87
+ )
88
+ end
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,187 @@
1
+ require_relative 'value_extractor'
2
+
3
+ module DaVinciUSDrugFormularyTestKit
4
+ class Generator
5
+ class SearchDefinitionMetadataExtractor
6
+ attr_accessor :ig_resources, :name, :resource, :profile_elements
7
+
8
+ def initialize(name, ig_resources, resource, profile_elements)
9
+ self.name = name
10
+ self.ig_resources = ig_resources
11
+ self.resource = resource
12
+ self.profile_elements = profile_elements
13
+ end
14
+
15
+ def search_definition
16
+ @search_definition ||=
17
+ {
18
+ paths:,
19
+ full_paths:,
20
+ comparators:,
21
+ values:,
22
+ type:,
23
+ target:,
24
+ contains_multiple: contains_multiple?,
25
+ multiple_or: multiple_or_expectation,
26
+ chain:
27
+ }.compact
28
+ end
29
+
30
+ def param
31
+ @param ||= ig_resources.search_param_by_resource_and_name(resource, name)
32
+ end
33
+
34
+ def param_hash
35
+ param.source_hash
36
+ end
37
+
38
+ def full_paths
39
+ @full_paths ||=
40
+ begin
41
+ path = param.expression.gsub(/.where\(resolve\((.*)/, '').gsub("url = '", 'url=\'')
42
+ path = path[1..-2] if path.start_with?('(') && path.end_with?(')')
43
+ path.scan(/[. ]as[( ]([^)]*)[)]?/).flatten.map do |as_type|
44
+ path.gsub!(/[. ]as[( ](#{as_type}[^)]*)[)]?/, as_type.upcase_first) if as_type.present?
45
+ end
46
+
47
+ path.split('|')
48
+ end
49
+ end
50
+
51
+ def paths
52
+ @paths ||= full_paths.map { |a_path| a_path.gsub("#{resource}.", '').strip }
53
+ end
54
+
55
+ def extensions
56
+ @extensions ||= full_paths.select { |a_path| a_path.include?('extension.where') }
57
+ .map { |a_path| { url: a_path[/(?<=extension.where\(url=').*(?='\))/] } }
58
+ .presence
59
+ end
60
+
61
+ def profile_element
62
+ @profile_element ||=
63
+ profile_elements.find { |element| full_paths.include?(element.id) } ||
64
+ extension_definition&.differential&.element&.find { |element| element.id == 'Extension.value[x]' }
65
+ end
66
+
67
+ def extension_definition
68
+ @extension_definition ||=
69
+ begin
70
+ ext_definition = nil
71
+ extensions&.each do |ext_metadata|
72
+ ext_definition = ig_resources.profile_by_url(ext_metadata[:url])
73
+ break if ext_definition.present?
74
+ end
75
+ ext_definition
76
+ end
77
+ end
78
+
79
+ def comparator_expectation_extensions
80
+ @comparator_expectation_extensions ||= param_hash['_comparator'] || []
81
+ end
82
+
83
+ def support_expectation(extension)
84
+ extension['extension'].first['valueCode']
85
+ end
86
+
87
+ def comparator_expectation(extension)
88
+ if extension.nil?
89
+ 'MAY'
90
+ else
91
+ support_expectation(extension)
92
+ end
93
+ end
94
+
95
+ def comparators
96
+ {}.tap do |comparators|
97
+ param.comparator&.each_with_index do |comparator, index|
98
+ comparators[comparator.to_sym] = comparator_expectation(comparator_expectation_extensions[index])
99
+ end
100
+ end
101
+ end
102
+
103
+ def type
104
+ if profile_element.present?
105
+ profile_element.type.first.code
106
+ else
107
+ # search is a variable type, eg. Condition.onsetDateTime - element
108
+ # in profile def is Condition.onset[x]
109
+ param.type
110
+ end
111
+ end
112
+
113
+ def target
114
+ # The search param definition for FormularyItem.subject doesn't have a target for some reason
115
+ return 'MedicationKnowledge' if param.url == 'http://hl7.org/fhir/us/davinci-drug-formulary/SearchParameter/Basic-subject'
116
+
117
+ param.target.first
118
+ end
119
+
120
+ def contains_multiple?
121
+ if profile_element.present?
122
+ if profile_element.id.start_with?('Extension') && extension_definition.present?
123
+ # Find the extension instance in a US Core profile
124
+ target_element = profile_elements.find do |element|
125
+ element.type.any? { |type| type.code == 'Extension' && type.profile.include?(extension_definition.url) }
126
+ end
127
+ target_element&.max == '*'
128
+ else
129
+ profile_element.max == '*'
130
+ end
131
+ else
132
+ false
133
+ end
134
+ end
135
+
136
+ def chain_extensions
137
+ param_hash['_chain']
138
+ end
139
+
140
+ def chain_expectations
141
+ chain_extensions.map { |extension| support_expectation(extension) }
142
+ end
143
+
144
+ def chain
145
+ return nil if param.chain.blank?
146
+
147
+ param.chain
148
+ .zip(chain_expectations)
149
+ .map { |chain, expectation| { chain:, expectation: } }
150
+ end
151
+
152
+ def multiple_or_expectation
153
+ param_hash['multipleOr']
154
+ end
155
+
156
+ def basic_status_search?
157
+ resource == 'Basic' && name == 'status'
158
+ end
159
+
160
+ def basic_status_values
161
+ ['draft', 'active', 'retired', 'unknown']
162
+ end
163
+
164
+ def values
165
+ return basic_status_values if basic_status_search?
166
+
167
+ value_extractor.values_from_slicing(profile_element, type).presence ||
168
+ value_extractor.values_from_required_binding(profile_element).presence ||
169
+ value_extractor.values_from_value_set_binding(profile_element).presence ||
170
+ values_from_resource_metadata(paths).presence ||
171
+ []
172
+ end
173
+
174
+ def values_from_resource_metadata(paths)
175
+ if multiple_or_expectation || paths.any? { |path| path.downcase.include?('status') }
176
+ value_extractor.values_from_resource_metadata(paths)
177
+ else
178
+ []
179
+ end
180
+ end
181
+
182
+ def value_extractor
183
+ @value_extractor ||= ValueExactor.new(ig_resources, resource, profile_elements)
184
+ end
185
+ end
186
+ end
187
+ end
@@ -0,0 +1,59 @@
1
+ require_relative 'search_definition_metadata_extractor'
2
+
3
+ module DaVinciUSDrugFormularyTestKit
4
+ class Generator
5
+ class SearchMetadataExtractor
6
+ COMBO_EXTENSION_URL =
7
+ 'http://hl7.org/fhir/StructureDefinition/capabilitystatement-search-parameter-combination'.freeze
8
+
9
+ attr_accessor :resource_capabilities, :ig_resources, :resource, :profile_elements
10
+
11
+ def initialize(resource_capabilities, ig_resources, resource, profile_elements)
12
+ self.resource_capabilities = resource_capabilities
13
+ self.ig_resources = ig_resources
14
+ self.resource = resource
15
+ self.profile_elements = profile_elements
16
+ end
17
+
18
+ def searches
19
+ @searches ||= basic_searches + combo_searches
20
+ end
21
+
22
+ def conformance_expectation(search_param)
23
+ search_param.extension.first.valueCode # TODO: fix expectation extension finding
24
+ end
25
+
26
+ def no_search_params?
27
+ resource_capabilities.searchParam.blank?
28
+ end
29
+
30
+ def basic_searches
31
+ return [] if no_search_params?
32
+
33
+ resource_capabilities.searchParam
34
+ .select { |search_param| ['SHALL', 'SHOULD'].include? conformance_expectation(search_param) }
35
+ .map do |search_param|
36
+ {
37
+ names: [search_param.name],
38
+ expectation: conformance_expectation(search_param)
39
+ }
40
+ end
41
+ end
42
+
43
+ def combo_searches
44
+ []
45
+ end
46
+
47
+ def search_param_names
48
+ searches.flat_map { |search| search[:names] }.uniq
49
+ end
50
+
51
+ def search_definitions
52
+ search_param_names.each_with_object({}) do |name, definitions|
53
+ definitions[name.to_sym] =
54
+ SearchDefinitionMetadataExtractor.new(name, ig_resources, resource, profile_elements).search_definition
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,270 @@
1
+ require_relative 'naming'
2
+ module DaVinciUSDrugFormularyTestKit
3
+ class Generator
4
+ class SearchTestGenerator
5
+ class << self
6
+ def generate(ig_metadata, base_output_dir)
7
+ ig_metadata.groups
8
+ .select { |group| group.searches.present? }
9
+ .each do |group|
10
+ group.searches.each { |search| new(group, search, base_output_dir).generate }
11
+ end
12
+ end
13
+ end
14
+
15
+ attr_accessor :group_metadata, :search_metadata, :base_output_dir
16
+
17
+ def initialize(group_metadata, search_metadata, base_output_dir)
18
+ self.group_metadata = group_metadata
19
+ self.search_metadata = search_metadata
20
+ self.base_output_dir = base_output_dir
21
+ end
22
+
23
+ def template
24
+ @template ||= File.read(File.join(__dir__, 'templates', 'search.rb.erb'))
25
+ end
26
+
27
+ def output
28
+ @output ||= ERB.new(template).result(binding)
29
+ end
30
+
31
+ def base_output_file_name
32
+ "#{class_name.underscore}.rb"
33
+ end
34
+
35
+ def output_file_directory
36
+ File.join(base_output_dir, profile_identifier)
37
+ end
38
+
39
+ def output_file_name
40
+ File.join(output_file_directory, base_output_file_name)
41
+ end
42
+
43
+ def profile_identifier
44
+ Naming.snake_case_for_profile(group_metadata)
45
+ end
46
+
47
+ def test_id
48
+ "usdf_#{group_metadata.reformatted_version}_#{profile_identifier}_#{search_identifier}_search_test"
49
+ end
50
+
51
+ def search_identifier
52
+ search_metadata[:names].join('_').tr('-', '_')
53
+ end
54
+
55
+ def search_title
56
+ search_identifier.camelize
57
+ end
58
+
59
+ def class_name
60
+ "#{Naming.upper_camel_case_for_profile(group_metadata)}#{search_title}SearchTest"
61
+ end
62
+
63
+ def module_name
64
+ "DaVinciUSDrugFormulary#{group_metadata.reformatted_version.upcase}"
65
+ end
66
+
67
+ def resource_type
68
+ group_metadata.resource
69
+ end
70
+
71
+ def conformance_expectation
72
+ search_metadata[:expectation]
73
+ end
74
+
75
+ def search_params
76
+ @search_params ||=
77
+ search_metadata[:names].map do |name|
78
+ {
79
+ name:,
80
+ path: search_definition(name)[:path]
81
+ }
82
+ end
83
+ end
84
+
85
+ def first_search?
86
+ group_metadata.searches.first == search_metadata
87
+ end
88
+
89
+ def fixed_value_search?
90
+ first_search? && (search_metadata[:names] == ['status'] || search_metadata[:names] == ['code'])
91
+ end
92
+
93
+ def fixed_value_search_param_name
94
+ (search_metadata[:names] - [:patient]).first
95
+ end
96
+
97
+ def search_param_name_string
98
+ search_metadata[:names].join(' + ')
99
+ end
100
+
101
+ def needs_id?
102
+ # resource_type == 'Location'
103
+ search_metadata[:names].include?('_id') && resource_type == 'Location'
104
+
105
+ # search_metadata[:names].include?('patient') ||
106
+ # (resource_type == 'Patient' && search_metadata[:names].include?('_id'))
107
+ end
108
+
109
+ def search_param_names
110
+ search_params.map { |param| param[:name] }
111
+ end
112
+
113
+ def search_param_names_array
114
+ array_of_strings(search_param_names)
115
+ end
116
+
117
+ def path_for_value(path)
118
+ path == 'class' ? 'local_class' : path
119
+ end
120
+
121
+ def required_comparators_for_param(name)
122
+ search_definition(name)[:comparators].select { |_comparator, expectation| expectation == 'SHALL' }
123
+ end
124
+
125
+ def required_comparators
126
+ @required_comparators ||=
127
+ search_param_names.each_with_object({}) do |name, comparators|
128
+ required_comparators = required_comparators_for_param(name)
129
+ comparators[name] = required_comparators if required_comparators.present?
130
+ end
131
+ end
132
+
133
+ def optional?
134
+ conformance_expectation != 'SHALL' || !search_metadata[:must_support_or_mandatory]
135
+ end
136
+
137
+ def search_definition(name)
138
+ group_metadata.search_definitions[name.to_sym]
139
+ end
140
+
141
+ def saves_delayed_references?
142
+ first_search? && group_metadata.delayed_references.present?
143
+ end
144
+
145
+ def token_search_params
146
+ @token_search_params ||=
147
+ search_param_names.select do |name|
148
+ ['Identifier', 'CodeableConcept', 'Coding'].include? group_metadata.search_definitions[name.to_sym][:type]
149
+ end
150
+ end
151
+
152
+ def token_search_params_string
153
+ array_of_strings(token_search_params)
154
+ end
155
+
156
+ def required_multiple_or_search_params
157
+ @required_multiple_or_search_params ||=
158
+ search_param_names.select do |name|
159
+ search_definition(name)[:multiple_or] == 'SHALL'
160
+ end
161
+ end
162
+
163
+ def required_multiple_or_search_params_string
164
+ array_of_strings(required_multiple_or_search_params)
165
+ end
166
+
167
+ def required_comparators_string
168
+ array_of_strings(required_comparators.keys)
169
+ end
170
+
171
+ def array_of_strings(array)
172
+ quoted_strings = array.map { |element| "'#{element}'" }
173
+ "[#{quoted_strings.join(', ')}]"
174
+ end
175
+
176
+ def test_reference_variants?
177
+ search_param_names.any? do |name|
178
+ group_metadata.search_definitions[name.to_sym][:type] == 'Reference'
179
+ end
180
+ end
181
+
182
+ def test_post_search?
183
+ first_search?
184
+ end
185
+
186
+ def search_properties
187
+ {}.tap do |properties|
188
+ properties[:first_search] = 'true' if first_search?
189
+ properties[:fixed_value_search] = 'true' if fixed_value_search?
190
+ properties[:resource_type] = "'#{resource_type}'"
191
+ properties[:search_param_names] = search_param_names_array
192
+ properties[:saves_delayed_references] = 'true' if saves_delayed_references?
193
+ properties[:token_search_params] = token_search_params_string if token_search_params.present?
194
+ properties[:test_reference_variants] = 'true' if test_reference_variants?
195
+ properties[:params_with_comparators] = required_comparators_string if required_comparators.present?
196
+ if required_multiple_or_search_params.present?
197
+ properties[:multiple_or_search_params] =
198
+ required_multiple_or_search_params_string
199
+ end
200
+ properties[:test_post_search] = 'true' if first_search?
201
+ end
202
+ end
203
+
204
+ def url_version
205
+ 'STU2'
206
+ end
207
+
208
+ def search_test_properties_string
209
+ search_properties
210
+ .map { |key, value| "#{' ' * 10}#{key}: #{value}" }
211
+ .join(",\n")
212
+ end
213
+
214
+ def generate
215
+ FileUtils.mkdir_p(output_file_directory)
216
+ File.write(output_file_name, output)
217
+
218
+ group_metadata.add_test(
219
+ id: test_id,
220
+ file_name: base_output_file_name
221
+ )
222
+ end
223
+
224
+ def reference_search_description
225
+ return '' unless test_reference_variants?
226
+
227
+ <<~REFERENCE_SEARCH_DESCRIPTION
228
+ This test verifies that the server supports searching by reference using
229
+ the form `formulary=[id]` as well as `formulary=Formulary/[id]`. The two
230
+ different forms are expected to return the same number of results. USDF#{' '}
231
+ requires that both forms are supported by responders.
232
+ REFERENCE_SEARCH_DESCRIPTION
233
+ end
234
+
235
+ def first_search_description
236
+ return '' unless first_search?
237
+
238
+ <<~FIRST_SEARCH_DESCRIPTION
239
+ Because this is the first search of the sequence, resources in the
240
+ response will be used for subsequent tests.
241
+ FIRST_SEARCH_DESCRIPTION
242
+ end
243
+
244
+ def post_search_description
245
+ return '' unless test_post_search?
246
+
247
+ <<~POST_SEARCH_DESCRIPTION
248
+ Additionally, this test will check that GET and POST search methods
249
+ return the same number of results. Search by POST is required by the
250
+ FHIR R4 specification, and these tests interpret search by GET as a
251
+ requirement of US Core #{group_metadata.version}.
252
+ POST_SEARCH_DESCRIPTION
253
+ end
254
+
255
+ def description
256
+ <<~DESCRIPTION.gsub(/\n{3,}/, "\n\n")
257
+ A server #{conformance_expectation} support searching by
258
+ #{search_param_name_string} on the #{resource_type} resource. This test
259
+ will pass if resources are returned and match the search criteria. If
260
+ none are returned, the test is skipped.
261
+
262
+ #{first_search_description}
263
+ #{post_search_description}
264
+
265
+ [US Drug Formulary](http://hl7.org/fhir/us/davinci-drug-formulary/#{url_version}/CapabilityStatement-usdf-server.html)
266
+ DESCRIPTION
267
+ end
268
+ end
269
+ end
270
+ end