Class: DaVinciPlanNetTestKit::Generator::GroupGenerator

Inherits:
Object
  • Object
show all
Defined in:
lib/davinci_plan_net_test_kit/generator/group_generator.rb

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(group_metadata, base_output_dir) ⇒ GroupGenerator

Returns a new instance of GroupGenerator.



17
18
19
20
# File 'lib/davinci_plan_net_test_kit/generator/group_generator.rb', line 17

def initialize(, base_output_dir)
  self. = 
  self.base_output_dir = base_output_dir
end

Instance Attribute Details

#base_output_dirObject

Returns the value of attribute base_output_dir.



15
16
17
# File 'lib/davinci_plan_net_test_kit/generator/group_generator.rb', line 15

def base_output_dir
  @base_output_dir
end

#group_metadataObject

Returns the value of attribute group_metadata.



15
16
17
# File 'lib/davinci_plan_net_test_kit/generator/group_generator.rb', line 15

def 
  @group_metadata
end

Class Method Details

.generate(ig_metadata, base_output_dir) ⇒ Object



8
9
10
11
12
# File 'lib/davinci_plan_net_test_kit/generator/group_generator.rb', line 8

def generate(, base_output_dir)
  .ordered_groups
    .reject { |group| SpecialCases.exclude_group? group }
    .each { |group| new(group, base_output_dir).generate }
end

Instance Method Details

#add_special_testsObject



116
117
118
# File 'lib/davinci_plan_net_test_kit/generator/group_generator.rb', line 116

def add_special_tests
  return if .reformatted_version == 'v311'
end

#any_chain_requirements?Boolean

Returns:

  • (Boolean)


96
97
98
# File 'lib/davinci_plan_net_test_kit/generator/group_generator.rb', line 96

def any_chain_requirements?
  !chainable_parameters.empty?
end

#base_metadata_file_nameObject



34
35
36
# File 'lib/davinci_plan_net_test_kit/generator/group_generator.rb', line 34

def 
  "metadata.yml"
end

#base_output_file_nameObject



30
31
32
# File 'lib/davinci_plan_net_test_kit/generator/group_generator.rb', line 30

def base_output_file_name
  "#{class_name.underscore}.rb"
end

#chain_requirement_list_for_param(search_parameter) ⇒ Object



90
91
92
93
94
# File 'lib/davinci_plan_net_test_kit/generator/group_generator.rb', line 90

def chain_requirement_list_for_param(search_parameter)
  sym = search_parameter.class != Symbol ? search_parameter.to_sym : search_parameter
  req_list = .search_definitions[sym][:chain].nil? ? [] : .search_definitions[sym][:chain]
  req_list
end

#chainable_parametersObject



100
101
102
103
104
105
106
# File 'lib/davinci_plan_net_test_kit/generator/group_generator.rb', line 100

def chainable_parameters
  chainable_params = .search_definitions.keys.reject do |search_parameter| 
    req_list = chain_requirement_list_for_param(search_parameter)  
    req_list.nil? || req_list.empty?
  end
  chainable_params
end

#class_nameObject



38
39
40
# File 'lib/davinci_plan_net_test_kit/generator/group_generator.rb', line 38

def class_name
  "#{Naming.upper_camel_case_for_profile()}Group"
end

#descriptionObject



309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
# File 'lib/davinci_plan_net_test_kit/generator/group_generator.rb', line 309

def description
  <<~DESCRIPTION
  # Background

  The #{title} sequence verifies that the system under test is
  able to provide correct responses for #{resource_type} queries. These queries
  must return resources conforming to the #{profile_name} profile as
  specified in the Plan Net #{.version} Implementation Guide.

  # Testing Methodology
  
  ## Instance Gathering

  Inferno will first identify and obtain a set of instances to use for the rest
  of the tests, requiring at least one instance to be identified for the test to pass. 
  Instances to gather are indentified in two ways. One or both will be used,
  depending on user input.

  ### Parameterless searches 
  Instances can be gathered using a query requesting all instances of #{resource_type} 
  (e.g., `GET [FHIR Endpoint]/#{resource_type}`). #{SpecialCases.has_parameterless_filter?(profile_name) ? SpecialCases.parameterless_filter_description(profile_name) : "" }
  Gathering through this method is controlled by the following inputs (used for all profiles):
  - "Use parameterless searches to identify instances?": 
    parameterless searches can be disabled using this input if, for example, 
    the server under test does not support them, or not all instances on the server 
    should be expected to conform to Plan Net profiles. In this case the user **MUST**
    provide specific instance ids to gather.
  - "Maximum number of instances to gather using parameterless searches": sets an upper 
    bound on the number of instances Inferno will gather from parameterless searches.
  - "Maximum pages of results to consider when using parameterless searches": sets an upper bound 
    on the number of pages of search results Inferno will load when gathering instances 
    using parameterless searches.
  
  ### User-provided instance ids
  
  If ids are listed in the "ids of #{profile_name} instances" optional input, 
  they will be read and included at the start of the set of gathered instances.

  #{search_description}
  #{include_description}
  #{revinclude_description}
  #{forward_chain_description}
  #{reverse_chain_description}

  ## Profile Validation
  Each resource identified during instance gathering and other queries run during this test sequence
  is expected to conform to the [#{profile_name}](#{.versioned_profile_url}). Each element is checked 
  by the HL7 Validator against terminology binding and cardinality requirements. Elements with a 
  required binding are validated against their bound ValueSet. If the code/system in the element 
  is not part of the ValueSet, then the test will fail.

  ## Must Support
  Each profile contains elements marked as "must support". This test
  sequence expects to see each of these elements populated at least once. 
  The test will look through the #{profile_name} instances identified 
  during instance gathering and other queries run during this test sequence.
  If no populated instance can be found for any must support element, the test 
  will fail. 

  ## Reference Validation
  At least one instance of each external reference in elements marked as
  "must support" within the resources provided by the system must resolve.
  The test will attempt to read each reference found and will fail if no
  read succeeds.
  DESCRIPTION
end

#forward_chain_descriptionObject



253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
# File 'lib/davinci_plan_net_test_kit/generator/group_generator.rb', line 253

def forward_chain_description
  return '' if !any_chain_requirements?

  <<~FORWARD_CHAINING_DESCRIPTION
  ## Forward Chaining Requirement Testing
  This test sequence will perform a search with each required combination of forward chaining 
  search parameters. This sequence will perform searches with the following chaining parameters:

  #{forward_chain_table}

  All forward chain searches will look for candidate instances of the resource being chained through
  from the results of previously run _include tests.  Candidates are chosen from previously returned
  instances that have the chain parameter element filled.  Each search test will use one of these values
  to build the requests for the test.  The test will be skipped if no candidates can be found.

  The test will first create and execute the forward chaining request.
  The test will then perform a basic search test on the resource being chained through,
  using the same value in the previous request.  Each resource returned in the first
  request will then be checked, validating that the element being chained through is populated by
  the id of _any_ of the resources returned by the second request.
  
  FORWARD_CHAINING_DESCRIPTION
end

#forward_chain_tableObject



156
157
158
159
160
161
162
163
164
165
# File 'lib/davinci_plan_net_test_kit/generator/group_generator.rb', line 156

def forward_chain_table
  chain_table = "| Search Parameters | Chain Requirements |\n| :---: | :---: |\n"
  # Iterate through the chain requirements and add to table
  chainable_parameters.each do |chain_param|
    chain_requirement_list = chain_requirement_list_for_param(chain_param).map { |chain| chain[:chain]}
    chain_table += "| #{chain_param} | #{chain_requirement_list.join(', ')} |\n"
  end
  
  chain_table
end

#generateObject



108
109
110
111
112
113
114
# File 'lib/davinci_plan_net_test_kit/generator/group_generator.rb', line 108

def generate
#       add_special_tests
  File.open(output_file_name, 'w') { |f| f.write(output) }
  .id = group_id
  .file_name = base_output_file_name
  File.open(, 'w') { |f| f.write(YAML.dump(.to_hash)) }
end

#group_idObject



66
67
68
# File 'lib/davinci_plan_net_test_kit/generator/group_generator.rb', line 66

def group_id
  "davinci_plan_net_#{.reformatted_version}_#{profile_identifier}"
end

#include_descriptionObject



207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
# File 'lib/davinci_plan_net_test_kit/generator/group_generator.rb', line 207

def include_description
  return '' if .include_params.blank?

  <<~INCLUDE_DESCRIPTION
  ## _include Requirement Testing
  This test sequence will perform a search with each required _include search 
  parameter associated with this profile. This sequence will perform searches with the
  following includes:

  #{include_param_name_string}

  Each _include search will look for a candidate id that has the target reference element
  populated from the results of instance gathering.  Each search will use the identified 
  #{profile_name} id and the include parameter.
  The returned instances are checked to ensure that any instances of the included
  type are referenced by returned instances of the searched resource type.

  INCLUDE_DESCRIPTION
end

#include_param_name_stringObject



144
145
146
147
148
# File 'lib/davinci_plan_net_test_kit/generator/group_generator.rb', line 144

def include_param_name_string
  .include_params
    .map { |names| "* #{names}" }
    .join("\n")
end

#metadata_file_nameObject



58
59
60
# File 'lib/davinci_plan_net_test_kit/generator/group_generator.rb', line 58

def 
  File.join(base_output_dir, profile_identifier, )
end

#module_nameObject



42
43
44
# File 'lib/davinci_plan_net_test_kit/generator/group_generator.rb', line 42

def module_name
  "DaVinciPlanNet#{.reformatted_version.upcase}"
end

#optional?Boolean

Returns:

  • (Boolean)


86
87
88
# File 'lib/davinci_plan_net_test_kit/generator/group_generator.rb', line 86

def optional?
  false #No Optional groups in Plan Net
end

#outputObject



26
27
28
# File 'lib/davinci_plan_net_test_kit/generator/group_generator.rb', line 26

def output
  @output ||= ERB.new(template).result(binding)
end

#output_file_nameObject



54
55
56
# File 'lib/davinci_plan_net_test_kit/generator/group_generator.rb', line 54

def output_file_name
  File.join(base_output_dir, base_output_file_name)
end

#profile_identifierObject



62
63
64
# File 'lib/davinci_plan_net_test_kit/generator/group_generator.rb', line 62

def profile_identifier
  Naming.snake_case_for_profile()
end

#profile_nameObject



78
79
80
# File 'lib/davinci_plan_net_test_kit/generator/group_generator.rb', line 78

def profile_name
  .profile_name
end

#profile_urlObject



82
83
84
# File 'lib/davinci_plan_net_test_kit/generator/group_generator.rb', line 82

def profile_url
  .profile_url
end

#required_searchesObject



133
134
135
# File 'lib/davinci_plan_net_test_kit/generator/group_generator.rb', line 133

def required_searches
  .searches.select { |search| search[:expectation] == 'SHALL' }
end

#resource_typeObject



70
71
72
# File 'lib/davinci_plan_net_test_kit/generator/group_generator.rb', line 70

def resource_type
  .resource
end

#reverse_chain_descriptionObject



277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
# File 'lib/davinci_plan_net_test_kit/generator/group_generator.rb', line 277

def reverse_chain_description
  return '' if !test_id_list.any? {|test_id| test_id.include?('reverse_chain')}
  <<~REVERSE_CHAINING_DESCRIPTION
  ## Reverse Chaining Requirement Testing
  This test sequence will perform a search with each required combination of reverse chaining 
  search parameters, including the following combinations:

  #{reverse_chain_string}

  All reverse chain searches will look for candidate instances from the results of 
  previous tests _only_ if tests are ran from the suite level.  Candidates are 
  selected by checking they have both the second (reference element) and third (constraining element) elements
  populated. The search value will be taken from the constraining element on the
  identified candidate.

  If running from the profile level, inputs of the form 
  "\'\[constraining element\]\' value from a \[source resource type\] instance with \'\[reference element\]\' populated"
  are provided for these tests upon test start. Enter a value from the \[constraining element\] element
  of an instance of a \[source resource type\] resource that also contains a reference to
  the tested #{resource_type} in its \[reference element\] element. The input will
  be used as the search value.

  The test will first create and execute a request with the chain parameter.
  The test will then perform a search against the \[source resource type\] with
  the \[constraining\] SeachParameter using the same search value.  Each resource returned in the first
  request will then be checked, validating that the ids of those resources are also referenced
  by _any_ of the resources returned by the second request in its \[reference element\] element.
  
  REVERSE_CHAINING_DESCRIPTION
end

#reverse_chain_stringObject



167
168
169
170
171
172
173
# File 'lib/davinci_plan_net_test_kit/generator/group_generator.rb', line 167

def reverse_chain_string
  # Placeholder until we have a more clear way of inferring reverse requirements
  examples = File.read('lib/davinci_plan_net_test_kit/custom_groups/reverse_chain_tests/examples.json')
  examples_hash = JSON.parse(examples)[Naming.upper_camel_case_for_profile()]
    .map { |test_example| "* #{test_example['source_resource']}:#{test_example['target_param']}:#{test_example['constraining_param']}" }
    .join("\n")
end

#revinclude_descriptionObject



227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
# File 'lib/davinci_plan_net_test_kit/generator/group_generator.rb', line 227

def revinclude_description
  return '' if .revincludes.blank?

  <<~REVINCLUDE_DESCRIPTION
  ## _revinclude Requirement Testing
  This test sequence will perform a search with each required _revinclude search 
  parameter associated with this resource. This sequence will perform searches with the
  following revincludes:

  #{revinclude_param_name_string}

  All _revinclude searches will look for candidate ids from the results of 
  instance gathering _only_ if tests are ran from the suite level.  Each search 
  will use a #{profile_name} id that is referenced by an instance of the revincluded resource
  in the element that is the target of the revinclude search parameter. The returned instances 
  are checked to ensure that any 
  instances of the revincluded type reference returned instances of the searched resource type.

  If running from the group level, inputs of the form 
  "#{resource_type} instance ids referenced in \[referencing profile\].\[referencing element\]"
  are provided for these tests. Enter ids of the #{resource_type} profile that
  are referenced by the \[referencing element\] of an instance of the \[referencing profile\].

  REVINCLUDE_DESCRIPTION
end

#revinclude_param_name_stringObject



150
151
152
153
154
# File 'lib/davinci_plan_net_test_kit/generator/group_generator.rb', line 150

def revinclude_param_name_string
  .revincludes
    .map { |names| "* #{names}" }
    .join("\n")
end

#search_descriptionObject



175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
# File 'lib/davinci_plan_net_test_kit/generator/group_generator.rb', line 175

def search_description
  return '' if required_searches.blank?

  <<~SEARCH_DESCRIPTION
  
  ## Searching
  This test sequence will perform a search with each required search parameter
  associated with this resource individually. Searches with the
  following parameters will be performed:

  #{search_param_name_string}

  ### Search Parameters
  Each search will look for its parameter values
  from the results of the instance gathering step. For example, for a search using
  the `identifier` search parameter, the test searches the gathered instances
  for one with the `identifier` element populated and then uses that value
  as the queried `identifier` value. If a value cannot be found this way, 
  the search test is skipped for that search parameter.

  ### Search Validation
  Inferno will retrieve all bundle pages of the reply for
  #{search_validation_resource_type}. Each of the returned instances
  is then checked to see if it matches the searched
  parameters in accordance with [FHIR search
  guidelines](https://www.hl7.org/fhir/search.html). The test will fail,
  for example, if a #{profile_name} search for `#{required_searches.first[:names].first}=X`
  returns a #{profile_name} instance where `#{required_searches.first[:names].first}!=X`

  SEARCH_DESCRIPTION
end

#search_param_name_stringObject



137
138
139
140
141
142
# File 'lib/davinci_plan_net_test_kit/generator/group_generator.rb', line 137

def search_param_name_string
  required_searches
    .map { |search| search[:names].join(' + ') }
    .map { |names| "* #{names}" }
    .join("\n")
end

#search_validation_resource_typeObject



74
75
76
# File 'lib/davinci_plan_net_test_kit/generator/group_generator.rb', line 74

def search_validation_resource_type
  "#{resource_type} resources"
end

#short_descriptionObject



50
51
52
# File 'lib/davinci_plan_net_test_kit/generator/group_generator.rb', line 50

def short_description
  .short_description
end

#templateObject



22
23
24
# File 'lib/davinci_plan_net_test_kit/generator/group_generator.rb', line 22

def template
  @template ||= File.read(File.join(__dir__, 'templates', 'group.rb.erb'))
end

#test_file_listObject



125
126
127
128
129
130
131
# File 'lib/davinci_plan_net_test_kit/generator/group_generator.rb', line 125

def test_file_list
  @test_file_list ||=
    .tests.map do |test|
      name_without_suffix = test[:file_name].delete_suffix('.rb')
      name_without_suffix.start_with?('..') ? name_without_suffix : "#{profile_identifier}/#{name_without_suffix}"
    end
end

#test_id_listObject



120
121
122
123
# File 'lib/davinci_plan_net_test_kit/generator/group_generator.rb', line 120

def test_id_list
  @test_id_list ||=
    .tests.map { |test| test[:id] }
end

#titleObject



46
47
48
# File 'lib/davinci_plan_net_test_kit/generator/group_generator.rb', line 46

def title
  .title
end