This repository has been archived by the owner on Jan 23, 2024. It is now read-only.
/
us_core_medicationrequest_sequence.rb
848 lines (650 loc) · 39.4 KB
/
us_core_medicationrequest_sequence.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
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
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
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
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
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
307
308
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
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
# frozen_string_literal: true
require_relative './data_absent_reason_checker'
require_relative './profile_definitions/us_core_medicationrequest_definitions'
module Inferno
module Sequence
class USCore311MedicationrequestSequence < SequenceBase
include Inferno::DataAbsentReasonChecker
include Inferno::USCore311ProfileDefinitions
title 'MedicationRequest Tests'
description 'Verify support for the server capabilities required by the US Core MedicationRequest Profile.'
details %(
# Background
The US Core #{title} sequence verifies that the system under test is able to provide correct responses
for MedicationRequest queries. These queries must contain resources conforming to US Core MedicationRequest Profile as specified
in the US Core v3.1.1 Implementation Guide.
# Testing Methodology
## Searching
This test sequence will first perform each required search associated with this resource. This sequence will perform searches
with the following parameters:
* patient + intent
* patient + intent + status
### Search Parameters
The first search uses the selected patient(s) from the prior launch sequence. Any subsequent searches will look for its
parameter values from the results of the first search. For example, the `identifier` search in the patient sequence is
performed by looking for an existing `Patient.identifier` from any of the resources returned in the `_id` search. If a
value cannot be found this way, the search is skipped.
### Search Validation
Inferno will retrieve up to the first 20 bundle pages of the reply for MedicationRequest resources and save them
for subsequent tests.
Each of these resources 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 patient search
for gender=male returns a female patient.
## Must Support
Each profile has a list of elements marked as "must support". This test sequence expects to see each of these elements
at least once. If at least one cannot be found, the test will fail. The test will look through the MedicationRequest
resources found for these elements.
## Profile Validation
Each resource returned from the first search is expected to conform to the [US Core MedicationRequest Profile](http://hl7.org/fhir/us/core/STU3.1.1/StructureDefinition/us-core-medicationrequest).
Each element is checked against teminology binding and cardinality requirements.
Elements with a required binding is validated against its bound valueset. If the code/system in the element is not part
of the valueset, then the test will fail.
## Reference Validation
Each reference within the resources found from the first search must resolve. The test will attempt to read each reference found
and will fail if any attempted read fails.
)
test_id_prefix 'USCMR'
requires :token, :patient_ids
conformance_supports :MedicationRequest
def validate_resource_item(resource, property, value)
case property
when 'status'
values_found = resolve_path(resource, 'status')
values = value.split(/(?<!\\),/).each { |str| str.gsub!('\,', ',') }
match_found = values_found.any? { |value_in_resource| values.include? value_in_resource }
assert match_found, "status in MedicationRequest/#{resource.id} (#{values_found}) does not match status requested (#{value})"
when 'intent'
values_found = resolve_path(resource, 'intent')
values = value.split(/(?<!\\),/).each { |str| str.gsub!('\,', ',') }
match_found = values_found.any? { |value_in_resource| values.include? value_in_resource }
assert match_found, "intent in MedicationRequest/#{resource.id} (#{values_found}) does not match intent requested (#{value})"
when 'patient'
values_found = resolve_path(resource, 'subject.reference')
value = value.split('Patient/').last
match_found = values_found.any? { |reference| [value, 'Patient/' + value, "#{@instance.url}/Patient/#{value}"].include? reference }
assert match_found, "patient in MedicationRequest/#{resource.id} (#{values_found}) does not match patient requested (#{value})"
when 'encounter'
values_found = resolve_path(resource, 'encounter.reference')
values = value.split(/(?<!\\),/).each { |str| str.gsub!('\,', ',') }
match_found = values_found.any? { |value_in_resource| values.include? value_in_resource }
assert match_found, "encounter in MedicationRequest/#{resource.id} (#{values_found}) does not match encounter requested (#{value})"
when 'authoredon'
values_found = resolve_path(resource, 'authoredOn')
values = value.split(/(?<!\\),/).each { |str| str.gsub!('\,', ',') }
match_found = values_found.any? { |value_in_resource| values.include? value_in_resource }
assert match_found, "authoredon in MedicationRequest/#{resource.id} (#{values_found}) does not match authoredon requested (#{value})"
end
end
def test_medication_inclusion(medication_requests, search_params)
@medications ||= []
@contained_medications ||= []
requests_with_external_references =
medication_requests
.select { |request| request&.medicationReference&.present? }
.reject { |request| request&.medicationReference&.reference&.start_with? '#' }
@contained_medications +=
medication_requests
.select { |request| request&.medicationReference&.reference&.start_with? '#' }
.flat_map(&:contained)
.select { |resource| resource.resourceType == 'Medication' }
return if requests_with_external_references.blank?
search_params.merge!(_include: 'MedicationRequest:medication')
response = get_resource_by_params(FHIR::MedicationRequest, search_params)
assert_response_ok(response)
assert_bundle_response(response)
requests_with_medications = fetch_all_bundled_resources(response, check_for_data_absent_reasons)
medications = requests_with_medications.select { |resource| resource.resourceType == 'Medication' }
assert medications.present?, 'No Medications were included in the search results'
@medications += medications
@medications.uniq!(&:id)
end
def perform_search_with_status(reply, search_param, search_method: :get)
begin
parsed_reply = JSON.parse(reply.body)
assert parsed_reply['resourceType'] == 'OperationOutcome', 'Server returned a status of 400 without an OperationOutcome.'
rescue JSON::ParserError
assert false, 'Server returned a status of 400 without an OperationOutcome.'
end
warning do
assert @instance.server_capabilities&.search_documented?('MedicationRequest'),
%(Server returned a status of 400 with an OperationOutcome, but the
search interaction for this resource is not documented in the
CapabilityStatement. If this response was due to the server
requiring a status parameter, the server must document this
requirement in its CapabilityStatement.)
end
['active,on-hold,cancelled,completed,entered-in-error,stopped,draft,unknown'].each do |status_value|
params_with_status = search_param.merge('status': status_value)
reply = get_resource_by_params(versioned_resource_class('MedicationRequest'), params_with_status, search_method: search_method)
assert_response_ok(reply)
assert_bundle_response(reply)
entries = reply.resource.entry.select { |entry| entry.resource.resourceType == 'MedicationRequest' }
next if entries.blank?
search_param.merge!('status': status_value)
break
end
reply
end
def patient_ids
@instance.patient_ids.split(',').map(&:strip)
end
@resources_found = false
test :search_by_patient_intent do
metadata do
id '01'
name 'Server returns valid results for MedicationRequest search by patient+intent.'
link 'http://hl7.org/fhir/us/core/STU3.1.1/CapabilityStatement-us-core-server.html'
description %(
A server SHALL support searching by patient+intent on the MedicationRequest resource.
This test will pass if resources are returned and match the search criteria. If none are returned, the test is skipped.
If any MedicationRequest resources use external references to
Medications, the search will be repeated with
_include=MedicationRequest:medication.
This test verifies that the server supports searching by
reference using the form `patient=[id]` as well as
`patient=Patient/[id]`. The two different forms are expected
to return the same number of results. US Core requires that
both forms are supported by US Core responders.
Additionally, this test will check that GET and POST search
methods return the same number of results. Search by POST
is required by the FHIR R4 specification, and these tests
interpret search by GET as a requirement of US Core v3.1.1.
Because this is the first search of the sequence, resources in
the response will be used for subsequent tests.
)
versions :r4
end
skip_if_known_search_not_supported('MedicationRequest', ['patient', 'intent'])
@medication_request_ary = {}
@resources_found = false
search_query_variants_tested_once = false
intent_val = ['proposal', 'plan', 'order', 'original-order', 'reflex-order', 'filler-order', 'instance-order', 'option']
patient_ids.each do |patient|
@medication_request_ary[patient] = []
intent_val.each do |val|
search_params = { 'patient': patient, 'intent': val }
reply = get_resource_by_params(versioned_resource_class('MedicationRequest'), search_params)
reply = perform_search_with_status(reply, search_params, search_method: :get) if reply.code == 400
assert_response_ok(reply)
assert_bundle_response(reply)
next unless reply&.resource&.entry&.any? { |entry| entry&.resource&.resourceType == 'MedicationRequest' }
@resources_found = true
resources_returned = fetch_all_bundled_resources(reply, check_for_data_absent_reasons)
types_in_response = Set.new(resources_returned.map { |resource| resource&.resourceType })
resources_returned.select! { |resource| resource.resourceType == 'MedicationRequest' }
@medication_request = resources_returned.first
@medication_request_ary[patient] += resources_returned
save_resource_references(versioned_resource_class('MedicationRequest'), @medication_request_ary[patient])
save_delayed_sequence_references(resources_returned, USCore311MedicationrequestSequenceDefinitions::DELAYED_REFERENCES)
invalid_types_in_response = types_in_response - Set.new(['MedicationRequest', 'OperationOutcome'])
assert(invalid_types_in_response.empty?,
'All resources returned must be of the type MedicationRequest or OperationOutcome, but includes ' + invalid_types_in_response.to_a.join(', '))
validate_reply_entries(resources_returned, search_params)
next if search_query_variants_tested_once
# Search with type of reference variant (patient=Patient/[id])
search_params_with_type = search_params.merge('patient': "Patient/#{patient}")
reply = get_resource_by_params(versioned_resource_class('MedicationRequest'), search_params_with_type)
reply = perform_search_with_status(reply, search_params, search_method: :get) if reply.code == 400
assert_response_ok(reply)
assert_bundle_response(reply)
search_with_type = fetch_all_bundled_resources(reply, check_for_data_absent_reasons)
search_with_type.select! { |resource| resource.resourceType == 'MedicationRequest' }
assert search_with_type.length == resources_returned.length, 'Expected search by Patient/ID to have the same results as search by ID'
search_with_type = fetch_all_bundled_resources(reply, check_for_data_absent_reasons)
search_with_type.select! { |resource| resource.resourceType == 'MedicationRequest' }
assert search_with_type.length == resources_returned.length, 'Expected search by Patient/ID to have the same results as search by ID'
# Search by POST variant
reply = get_resource_by_params(versioned_resource_class('MedicationRequest'), search_params, search_method: :post)
reply = perform_search_with_status(reply, search_params, search_method: :post) if reply.code == 400
assert_response_ok(reply)
assert_bundle_response(reply)
search_by_post_resources = fetch_all_bundled_resources(reply, check_for_data_absent_reasons)
search_by_post_resources.select! { |resource| resource.resourceType == 'MedicationRequest' }
assert search_by_post_resources.length == resources_returned.length, 'Expected search by POST to have the same results as search by GET'
test_medication_inclusion(@medication_request_ary[patient], search_params)
search_query_variants_tested_once = true
end
end
skip_if_not_found(resource_type: 'MedicationRequest', delayed: false)
end
test :search_by_patient_intent_status do
metadata do
id '02'
name 'Server returns valid results for MedicationRequest search by patient+intent+status.'
link 'http://hl7.org/fhir/us/core/STU3.1.1/CapabilityStatement-us-core-server.html'
description %(
A server SHALL support searching by patient+intent+status on the MedicationRequest resource.
This test will pass if resources are returned and match the search criteria. If none are returned, the test is skipped.
If any MedicationRequest resources use external references to
Medications, the search will be repeated with
_include=MedicationRequest:medication.
)
versions :r4
end
skip_if_known_search_not_supported('MedicationRequest', ['patient', 'intent', 'status'])
skip_if_not_found(resource_type: 'MedicationRequest', delayed: false)
resolved_one = false
patient_ids.each do |patient|
next unless @medication_request_ary[patient].present?
Array.wrap(@medication_request_ary[patient]).each do |medication_request|
search_params = {
'patient': patient,
'intent': get_value_for_search_param(resolve_element_from_path(medication_request, 'intent') { |el| get_value_for_search_param(el).present? }),
'status': get_value_for_search_param(resolve_element_from_path(medication_request, 'status') { |el| get_value_for_search_param(el).present? })
}
next if search_params.any? { |_param, value| value.nil? }
resolved_one = true
reply = get_resource_by_params(versioned_resource_class('MedicationRequest'), search_params)
validate_search_reply(versioned_resource_class('MedicationRequest'), reply, search_params)
resources_returned = fetch_all_bundled_resources(reply, check_for_data_absent_reasons)
assert(resources_returned.all? { |resource| ['MedicationRequest', 'OperationOutcome'].include? resource.resourceType },
'All resources returned must be of the type MedicationRequest or OperationOutcome')
resources_returned.reject! { |resource| resource.resourceType == 'OperationOutcome' }
test_medication_inclusion(resources_returned, search_params)
break if resolved_one
end
end
skip 'Could not resolve all parameters (patient, intent, status) in any resource.' unless resolved_one
end
test :search_by_patient_intent_encounter do
metadata do
id '03'
name 'Server returns valid results for MedicationRequest search by patient+intent+encounter.'
link 'http://hl7.org/fhir/us/core/STU3.1.1/CapabilityStatement-us-core-server.html'
optional
description %(
A server SHOULD support searching by patient+intent+encounter on the MedicationRequest resource.
This test will pass if resources are returned and match the search criteria. If none are returned, the test is skipped.
If any MedicationRequest resources use external references to
Medications, the search will be repeated with
_include=MedicationRequest:medication.
)
versions :r4
end
skip_if_known_search_not_supported('MedicationRequest', ['patient', 'intent', 'encounter'])
skip_if_not_found(resource_type: 'MedicationRequest', delayed: false)
resolved_one = false
patient_ids.each do |patient|
next unless @medication_request_ary[patient].present?
Array.wrap(@medication_request_ary[patient]).each do |medication_request|
search_params = {
'patient': patient,
'intent': get_value_for_search_param(resolve_element_from_path(medication_request, 'intent') { |el| get_value_for_search_param(el).present? }),
'encounter': get_value_for_search_param(resolve_element_from_path(medication_request, 'encounter') { |el| get_value_for_search_param(el).present? })
}
next if search_params.any? { |_param, value| value.nil? }
resolved_one = true
reply = get_resource_by_params(versioned_resource_class('MedicationRequest'), search_params)
reply = perform_search_with_status(reply, search_params, search_method: :get) if reply.code == 400
validate_search_reply(versioned_resource_class('MedicationRequest'), reply, search_params)
resources_returned = fetch_all_bundled_resources(reply, check_for_data_absent_reasons)
assert(resources_returned.all? { |resource| ['MedicationRequest', 'OperationOutcome'].include? resource.resourceType },
'All resources returned must be of the type MedicationRequest or OperationOutcome')
resources_returned.reject! { |resource| resource.resourceType == 'OperationOutcome' }
test_medication_inclusion(resources_returned, search_params)
break if resolved_one
end
end
skip 'Could not resolve all parameters (patient, intent, encounter) in any resource.' unless resolved_one
end
test :search_by_patient_intent_authoredon do
metadata do
id '04'
name 'Server returns valid results for MedicationRequest search by patient+intent+authoredon.'
link 'http://hl7.org/fhir/us/core/STU3.1.1/CapabilityStatement-us-core-server.html'
optional
description %(
A server SHOULD support searching by patient+intent+authoredon on the MedicationRequest resource.
This test will pass if resources are returned and match the search criteria. If none are returned, the test is skipped.
This will also test support for these authoredon comparators: gt, ge, lt, le. Comparator values are created by taking
a authoredon value from a resource returned in the first search of this sequence and adding/subtracting a day. For example, a date
of 05/05/2020 will create comparator values of lt2020-05-06 and gt2020-05-04
If any MedicationRequest resources use external references to
Medications, the search will be repeated with
_include=MedicationRequest:medication.
)
versions :r4
end
skip_if_known_search_not_supported('MedicationRequest', ['patient', 'intent', 'authoredon'])
skip_if_not_found(resource_type: 'MedicationRequest', delayed: false)
resolved_one = false
patient_ids.each do |patient|
next unless @medication_request_ary[patient].present?
Array.wrap(@medication_request_ary[patient]).each do |medication_request|
search_params = {
'patient': patient,
'intent': get_value_for_search_param(resolve_element_from_path(medication_request, 'intent') { |el| get_value_for_search_param(el).present? }),
'authoredon': get_value_for_search_param(resolve_element_from_path(medication_request, 'authoredOn') { |el| get_value_for_search_param(el).present? })
}
next if search_params.any? { |_param, value| value.nil? }
resolved_one = true
reply = get_resource_by_params(versioned_resource_class('MedicationRequest'), search_params)
reply = perform_search_with_status(reply, search_params, search_method: :get) if reply.code == 400
validate_search_reply(versioned_resource_class('MedicationRequest'), reply, search_params)
resources_returned = fetch_all_bundled_resources(reply, check_for_data_absent_reasons)
assert(resources_returned.all? { |resource| ['MedicationRequest', 'OperationOutcome'].include? resource.resourceType },
'All resources returned must be of the type MedicationRequest or OperationOutcome')
resources_returned.reject! { |resource| resource.resourceType == 'OperationOutcome' }
test_medication_inclusion(resources_returned, search_params)
break if resolved_one
end
end
skip 'Could not resolve all parameters (patient, intent, authoredon) in any resource.' unless resolved_one
end
test :read_interaction do
metadata do
id '05'
name 'Server returns correct MedicationRequest resource from MedicationRequest read interaction'
link 'http://hl7.org/fhir/us/core/STU3.1.1/CapabilityStatement-us-core-server.html'
description %(
A server SHALL support the MedicationRequest read interaction.
)
versions :r4
end
skip_if_known_not_supported(:MedicationRequest, [:read])
skip_if_not_found(resource_type: 'MedicationRequest', delayed: false)
validate_read_reply(@medication_request, versioned_resource_class('MedicationRequest'), check_for_data_absent_reasons)
end
test :vread_interaction do
metadata do
id '06'
name 'Server returns correct MedicationRequest resource from MedicationRequest vread interaction'
link 'http://hl7.org/fhir/us/core/STU3.1.1/CapabilityStatement-us-core-server.html'
optional
description %(
A server SHOULD support the MedicationRequest vread interaction.
)
versions :r4
end
skip_if_known_not_supported(:MedicationRequest, [:vread])
skip_if_not_found(resource_type: 'MedicationRequest', delayed: false)
validate_vread_reply(@medication_request, versioned_resource_class('MedicationRequest'))
end
test :history_interaction do
metadata do
id '07'
name 'Server returns correct MedicationRequest resource from MedicationRequest history interaction'
link 'http://hl7.org/fhir/us/core/STU3.1.1/CapabilityStatement-us-core-server.html'
optional
description %(
A server SHOULD support the MedicationRequest history interaction.
)
versions :r4
end
skip_if_known_not_supported(:MedicationRequest, [:history])
skip_if_not_found(resource_type: 'MedicationRequest', delayed: false)
validate_history_reply(@medication_request, versioned_resource_class('MedicationRequest'))
end
test 'Server returns the appropriate resource from the following patient + intent + _includes: MedicationRequest:medication' do
metadata do
id '08'
link 'https://www.hl7.org/fhir/search.html#include'
optional
description %(
A Server SHOULD be capable of supporting the following _includes: MedicationRequest:medication
This test will perform a search for patient + intent + each of the following _includes: MedicationRequest:medication
The test will fail unless resources for MedicationRequest:medication are returned in their search.
)
versions :r4
end
resolved_one = false
medication_results = false
patient_ids.each do |patient|
search_params = {
'patient': patient,
'intent': get_value_for_search_param(resolve_element_from_path(@medication_request_ary[patient], 'intent') { |el| get_value_for_search_param(el).present? })
}
next if search_params.any? { |_param, value| value.nil? }
resolved_one = true
skip_if_known_include_not_supported('MedicationRequest', 'MedicationRequest:medication')
search_params['_include'] = 'MedicationRequest:medication'
reply = get_resource_by_params(versioned_resource_class('MedicationRequest'), search_params)
assert_response_ok(reply)
assert_bundle_response(reply)
medication_results ||= reply&.resource&.entry&.map(&:resource)&.any? { |resource| resource.resourceType == 'Medication' }
end
skip 'Could not resolve all parameters (patient, intent) in any resource.' unless resolved_one
assert medication_results, 'No Medication resources were returned from this search'
end
test 'Server returns Provenance resources from MedicationRequest search by patient + intent + _revIncludes: Provenance:target' do
metadata do
id '09'
link 'https://www.hl7.org/fhir/search.html#revinclude'
description %(
A Server SHALL be capable of supporting the following _revincludes: Provenance:target.
This test will perform a search for patient + intent + _revIncludes: Provenance:target and will pass
if a Provenance resource is found in the reponse.
)
versions :r4
end
skip_if_known_revinclude_not_supported('MedicationRequest', 'Provenance:target')
skip_if_not_found(resource_type: 'MedicationRequest', delayed: false)
resolved_one = false
provenance_results = []
patient_ids.each do |patient|
search_params = {
'patient': patient,
'intent': get_value_for_search_param(resolve_element_from_path(@medication_request_ary[patient], 'intent') { |el| get_value_for_search_param(el).present? })
}
next if search_params.any? { |_param, value| value.nil? }
resolved_one = true
search_params['_revinclude'] = 'Provenance:target'
reply = get_resource_by_params(versioned_resource_class('MedicationRequest'), search_params)
reply = perform_search_with_status(reply, search_params, search_method: :get) if reply.code == 400
assert_response_ok(reply)
assert_bundle_response(reply)
provenance_results += fetch_all_bundled_resources(reply, check_for_data_absent_reasons)
.select { |resource| resource.resourceType == 'Provenance' }
end
save_resource_references(versioned_resource_class('Provenance'), provenance_results)
save_delayed_sequence_references(provenance_results, USCore311ProvenanceSequenceDefinitions::DELAYED_REFERENCES)
skip 'Could not resolve all parameters (patient, intent) in any resource.' unless resolved_one
skip 'No Provenance resources were returned from this search' unless provenance_results.present?
end
test :validate_resources do
metadata do
id '10'
name 'MedicationRequest resources returned during previous tests conform to the US Core MedicationRequest Profile.'
link 'http://hl7.org/fhir/us/core/STU3.1.1/StructureDefinition/us-core-medicationrequest'
description %(
This test verifies resources returned from the first search conform to the [US Core MedicationRequest Profile](http://hl7.org/fhir/us/core/STU3.1.1/StructureDefinition/us-core-medicationrequest).
It verifies the presence of manditory elements and that elements with required bindgings contain appropriate values.
CodeableConcept element bindings will fail if none of its codings have a code/system that is part of the bound ValueSet.
Quantity, Coding, and code element bindings will fail if its code/system is not found in the valueset.
)
versions :r4
end
skip_if_not_found(resource_type: 'MedicationRequest', delayed: false)
test_resources_against_profile('MedicationRequest')
bindings = USCore311MedicationrequestSequenceDefinitions::BINDINGS
invalid_binding_messages = []
invalid_binding_resources = Set.new
bindings.select { |binding_def| binding_def[:strength] == 'required' }.each do |binding_def|
begin
invalid_bindings = resources_with_invalid_binding(binding_def, @medication_request_ary&.values&.flatten)
rescue Inferno::Terminology::UnknownValueSetException => e
warning do
assert false, e.message
end
invalid_bindings = []
end
invalid_bindings.each { |invalid| invalid_binding_resources << "#{invalid[:resource]&.resourceType}/#{invalid[:resource].id}" }
invalid_binding_messages.concat(invalid_bindings.map { |invalid| invalid_binding_message(invalid, binding_def) })
end
assert invalid_binding_messages.blank?, "#{invalid_binding_messages.count} invalid required #{'binding'.pluralize(invalid_binding_messages.count)}" \
" found in #{invalid_binding_resources.count} #{'resource'.pluralize(invalid_binding_resources.count)}: " \
"#{invalid_binding_messages.join('. ')}"
bindings.select { |binding_def| binding_def[:strength] == 'extensible' }.each do |binding_def|
begin
invalid_bindings = resources_with_invalid_binding(binding_def, @medication_request_ary&.values&.flatten)
binding_def_new = binding_def
# If the valueset binding wasn't valid, check if the codes are in the stated codesystem
if invalid_bindings.present?
invalid_bindings = resources_with_invalid_binding(binding_def.except(:system), @medication_request_ary&.values&.flatten)
binding_def_new = binding_def.except(:system)
end
rescue Inferno::Terminology::UnknownValueSetException, Inferno::Terminology::ValueSet::UnknownCodeSystemException => e
warning do
assert false, e.message
end
invalid_bindings = []
end
invalid_binding_messages.concat(invalid_bindings.map { |invalid| invalid_binding_message(invalid, binding_def_new) })
end
warning do
invalid_binding_messages.each do |error_message|
assert false, error_message
end
end
end
test :validate_medication_resources do
metadata do
id '11'
name 'Medication resources returned conform to US Core v3.1.1 profiles'
link 'http://hl7.org/fhir/us/core/STU3.1.1/StructureDefinition/us-core-medicationrequest'
description %(
This test checks if the resources returned from prior searches conform to the US Core profiles.
This includes checking for missing data elements and valueset verification.
)
versions :r4
end
medications_found = (@medications || []) + (@contained_medications || [])
omit 'MedicationRequests did not reference any Medication resources.' if medications_found.blank?
test_resource_collection('Medication', medications_found)
end
test 'All must support elements are provided in the MedicationRequest resources returned.' do
metadata do
id '12'
link 'http://hl7.org/fhir/us/core/STU3.1.1/general-guidance.html#must-support'
description %(
US Core Responders SHALL be capable of populating all data elements as part of the query results as specified by the US Core Server Capability Statement.
This will look through the MedicationRequest resources found previously for the following must support elements:
* authoredOn
* dosageInstruction
* dosageInstruction.text
* encounter
* intent
* medication[x]
* reported[x]
* requester
* status
* subject
)
versions :r4
end
skip_if_not_found(resource_type: 'MedicationRequest', delayed: false)
must_supports = USCore311MedicationrequestSequenceDefinitions::MUST_SUPPORTS
missing_must_support_elements = must_supports[:elements].reject do |element|
@medication_request_ary&.values&.flatten&.any? do |resource|
value_found = resolve_element_from_path(resource, element[:path]) do |value|
value_without_extensions = value.respond_to?(:to_hash) ? value.to_hash.reject { |key, _| key == 'extension' } : value
(value_without_extensions.present? || value_without_extensions == false) && (element[:fixed_value].blank? || value == element[:fixed_value])
end
# Note that false.present? => false, which is why we need to add this extra check
value_found.present? || value_found == false
end
end
missing_must_support_elements.map! { |must_support| "#{must_support[:path]}#{': ' + must_support[:fixed_value] if must_support[:fixed_value].present?}" }
skip_if missing_must_support_elements.present?,
"Could not find #{missing_must_support_elements.join(', ')} in the #{@medication_request_ary&.values&.flatten&.length} provided MedicationRequest resource(s)"
@instance.save!
end
test 'The server returns results when parameters use composite-or' do
metadata do
id '13'
link 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-medicationrequest'
description %(
This test will check if the server is capable of returning results for composite search parameters.
The test will look through the resources returned from the first search to identify two different values
to use for the parameter being tested. If no two different values can be found, then the test is skipped.
[FHIR Composite Search Guideline](https://www.hl7.org/fhir/search.html#combining)
Parameters being tested: status, intent
)
versions :r4
end
skip_if_known_search_not_supported('MedicationRequest', ['patient', 'intent'])
resolved_one = false
composite_or_parameters = ['intent']
patient_ids.each do |patient|
next unless @medication_request_ary[patient].present?
search_params = {
'patient': patient,
'intent': 'proposal,plan,order,original-order,reflex-order,filler-order,instance-order,option'
}
existing_values = {
intent: []
}
missing_values = {
intent: []
}
composite_or_parameters.each do |param|
existing_values[param.to_sym] = @medication_request_ary[patient].map(¶m.to_sym).compact.uniq
end
next if existing_values.values.any?(&:empty?)
resolved_one = true
reply = get_resource_by_params(versioned_resource_class('MedicationRequest'), search_params)
validate_search_reply(versioned_resource_class('MedicationRequest'), reply, search_params)
resources_returned = fetch_all_bundled_resources(reply, check_for_data_absent_reasons)
applicable_resources_returned = resources_returned.reject { |entry| entry.class == FHIR::OperationOutcome }
composite_or_parameters.each do |param|
missing_values[param.to_sym] = existing_values[param.to_sym] - applicable_resources_returned.map(¶m.to_sym)
end
missing_value_message = missing_values.reject { |_k, v| v.empty? }.map { |k, v| "#{v.join(',')} values from #{k}" }.join(' and ')
assert missing_value_message.blank?, "Could not find #{missing_value_message} in any of the resources returned"
break if resolved_one
end
skip_if_known_search_not_supported('MedicationRequest', ['patient', 'intent', 'status'])
resolved_one = false
composite_or_parameters = ['intent', 'status']
patient_ids.each do |patient|
next unless @medication_request_ary[patient].present?
search_params = {
'patient': patient,
'intent': 'proposal,plan,order,original-order,reflex-order,filler-order,instance-order,option',
'status': 'active,on-hold,cancelled,completed,entered-in-error,stopped,draft,unknown'
}
existing_values = {
intent: [],
status: []
}
missing_values = {
intent: [],
status: []
}
composite_or_parameters.each do |param|
existing_values[param.to_sym] = @medication_request_ary[patient].map(¶m.to_sym).compact.uniq
end
next if existing_values.values.any?(&:empty?)
resolved_one = true
reply = get_resource_by_params(versioned_resource_class('MedicationRequest'), search_params)
validate_search_reply(versioned_resource_class('MedicationRequest'), reply, search_params)
resources_returned = fetch_all_bundled_resources(reply, check_for_data_absent_reasons)
applicable_resources_returned = resources_returned.reject { |entry| entry.class == FHIR::OperationOutcome }
composite_or_parameters.each do |param|
missing_values[param.to_sym] = existing_values[param.to_sym] - applicable_resources_returned.map(¶m.to_sym)
end
missing_value_message = missing_values.reject { |_k, v| v.empty? }.map { |k, v| "#{v.join(',')} values from #{k}" }.join(' and ')
assert missing_value_message.blank?, "Could not find #{missing_value_message} in any of the resources returned"
break if resolved_one
end
end
test 'Every reference within MedicationRequest resources can be read.' do
metadata do
id '14'
link 'http://hl7.org/fhir/references.html'
description %(
This test will attempt to read the first 50 reference found in the resources from the first search.
The test will fail if Inferno fails to read any of those references.
)
versions :r4
end
skip_if_not_found(resource_type: 'MedicationRequest', delayed: false)
validated_resources = Set.new
max_resolutions = 50
@medication_request_ary&.values&.flatten&.each do |resource|
validate_reference_resolutions(resource, validated_resources, max_resolutions) if validated_resources.length < max_resolutions
end
end
end
end
end