davinci_pas_test_kit 0.11.1 → 0.12.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/config/presets/pas_client_example.json +30 -0
- data/config/presets/pas_ri.json +69 -0
- data/lib/davinci_pas_test_kit/client_suite.rb +28 -2
- data/lib/davinci_pas_test_kit/custom_groups/v2.0.1/client_tests/pas_client_approval_submit_test.rb +29 -1
- data/lib/davinci_pas_test_kit/custom_groups/v2.0.1/client_tests/pas_client_denial_submit_test.rb +27 -4
- data/lib/davinci_pas_test_kit/{generated/v2.0.1/client_tests/client_pended_pas_inquiry_request_bundle_validation_test.rb → custom_groups/v2.0.1/client_tests/pas_client_inquire_request_bundle_validation_test.rb} +22 -20
- data/lib/davinci_pas_test_kit/{generated/v2.0.1/client_tests/client_denial_pas_response_bundle_validation_test.rb → custom_groups/v2.0.1/client_tests/pas_client_inquire_response_bundle_validation_test.rb} +34 -21
- data/lib/davinci_pas_test_kit/custom_groups/v2.0.1/client_tests/pas_client_pended_submit_test.rb +124 -5
- data/lib/davinci_pas_test_kit/{generated/v2.0.1/client_tests/client_pas_request_bundle_validation_test.rb → custom_groups/v2.0.1/client_tests/pas_client_request_bundle_validation_test.rb} +22 -20
- data/lib/davinci_pas_test_kit/custom_groups/v2.0.1/client_tests/{pas_client_approval_submit_response_attest.rb → pas_client_response_attest.rb} +26 -9
- data/lib/davinci_pas_test_kit/{generated/v2.0.1/client_tests/client_pended_pas_response_bundle_validation_test.rb → custom_groups/v2.0.1/client_tests/pas_client_response_bundle_validation_test.rb} +44 -20
- data/lib/davinci_pas_test_kit/custom_groups/v2.0.1/client_tests/pas_client_subscription_create_test.rb +49 -0
- data/lib/davinci_pas_test_kit/custom_groups/v2.0.1/client_tests/pas_client_subscription_pas_conformance_test.rb +48 -0
- data/lib/davinci_pas_test_kit/custom_groups/v2.0.1/pas_client_approval_group.rb +21 -9
- data/lib/davinci_pas_test_kit/custom_groups/v2.0.1/pas_client_authentication_group.rb +2 -2
- data/lib/davinci_pas_test_kit/custom_groups/v2.0.1/pas_client_denial_group.rb +21 -22
- data/lib/davinci_pas_test_kit/custom_groups/v2.0.1/pas_client_pended_group.rb +97 -31
- data/lib/davinci_pas_test_kit/docs/PAS Requirements Interpretation.xlsx +0 -0
- data/lib/davinci_pas_test_kit/docs/client_suite_description_v201.md +213 -72
- data/lib/davinci_pas_test_kit/endpoints/claim_endpoint.rb +85 -134
- data/lib/davinci_pas_test_kit/endpoints/subscription_create_endpoint.rb +96 -0
- data/lib/davinci_pas_test_kit/endpoints/subscription_status_endpoint.rb +90 -0
- data/lib/davinci_pas_test_kit/fhir_resource_navigation.rb +3 -3
- data/lib/davinci_pas_test_kit/generated/v2.0.1/pas_inquiry_request_bundle/metadata.yml +0 -2
- data/lib/davinci_pas_test_kit/generated/v2.0.1/pas_inquiry_request_bundle/server_pas_inquiry_request_bundle_validation_test.rb +3 -1
- data/lib/davinci_pas_test_kit/generated/v2.0.1/pas_inquiry_response_bundle/server_pas_inquiry_response_bundle_validation_test.rb +2 -1
- data/lib/davinci_pas_test_kit/generated/v2.0.1/pas_request_bundle/metadata.yml +0 -2
- data/lib/davinci_pas_test_kit/generated/v2.0.1/pas_request_bundle/server_pas_request_bundle_validation_test.rb +3 -1
- data/lib/davinci_pas_test_kit/generated/v2.0.1/pas_response_bundle/metadata.yml +0 -4
- data/lib/davinci_pas_test_kit/generated/v2.0.1/pas_response_bundle/server_pas_response_bundle_validation_test.rb +2 -1
- data/lib/davinci_pas_test_kit/generated/v2.0.1/pas_server_denial_use_case_group.rb +1 -1
- data/lib/davinci_pas_test_kit/generated/v2.0.1/pas_server_pended_use_case_group.rb +6 -5
- data/lib/davinci_pas_test_kit/generated/v2.0.1/server_suite.rb +5 -2
- data/lib/davinci_pas_test_kit/generator/group_generator.rb +9 -8
- data/lib/davinci_pas_test_kit/generator/group_metadata_extractor.rb +7 -3
- data/lib/davinci_pas_test_kit/generator/templates/group.rb.erb +129 -0
- data/lib/davinci_pas_test_kit/generator/templates/must_support.rb.erb +73 -0
- data/lib/davinci_pas_test_kit/generator/templates/operation.rb.erb +62 -0
- data/lib/davinci_pas_test_kit/generator/templates/resource_list.rb.erb +13 -0
- data/lib/davinci_pas_test_kit/generator/templates/suite.rb.erb +92 -0
- data/lib/davinci_pas_test_kit/generator/templates/validation.rb.erb +98 -0
- data/lib/davinci_pas_test_kit/generator/validation_test_generator.rb +19 -56
- data/lib/davinci_pas_test_kit/generator/value_extractor.rb +4 -1
- data/lib/davinci_pas_test_kit/generator.rb +1 -1
- data/lib/davinci_pas_test_kit/igs/davinci_pas_2.0.1.tgz +0 -0
- data/lib/davinci_pas_test_kit/jobs/send_pas_subscription_notification.rb +136 -0
- data/lib/davinci_pas_test_kit/jobs/send_subscription_handshake.rb +139 -0
- data/lib/davinci_pas_test_kit/metadata.rb +87 -0
- data/lib/davinci_pas_test_kit/pas_bundle_validation.rb +8 -7
- data/lib/davinci_pas_test_kit/response_generator.rb +397 -0
- data/lib/davinci_pas_test_kit/tags.rb +9 -0
- data/lib/davinci_pas_test_kit/urls.rb +8 -0
- data/lib/davinci_pas_test_kit/user_input_response.rb +11 -8
- data/lib/davinci_pas_test_kit/validation_test.rb +0 -1
- data/lib/davinci_pas_test_kit/version.rb +2 -1
- data/lib/davinci_pas_test_kit.rb +1 -0
- metadata +44 -15
- data/lib/davinci_pas_test_kit/custom_groups/v2.0.1/client_tests/pas_client_denial_submit_response_attest.rb +0 -38
- data/lib/davinci_pas_test_kit/custom_groups/v2.0.1/client_tests/pas_client_pended_inquire_response_attest.rb +0 -39
- data/lib/davinci_pas_test_kit/custom_groups/v2.0.1/client_tests/pas_client_pended_inquire_test.rb +0 -35
- data/lib/davinci_pas_test_kit/custom_groups/v2.0.1/client_tests/pas_client_pended_submit_response_attest.rb +0 -39
@@ -0,0 +1,62 @@
|
|
1
|
+
module DaVinciPASTestKit
|
2
|
+
module <%= module_name %>
|
3
|
+
class <%= class_name %> < Inferno::Test
|
4
|
+
|
5
|
+
id :<%= test_id %>
|
6
|
+
title '<%= title %>'
|
7
|
+
description %(
|
8
|
+
<%=description.gsub("\n", "\n" + " "*8) %>
|
9
|
+
)
|
10
|
+
|
11
|
+
input :pa_<%= request_type %>_payload,
|
12
|
+
title: 'PAS <%= request_type.humanize.titleize%> Payload',
|
13
|
+
description: 'Insert Bundle to be sent for PAS <%= request_type.humanize.titleize%>',
|
14
|
+
type: 'textarea',
|
15
|
+
optional: true
|
16
|
+
input_order :server_endpoint, :smart_credentials
|
17
|
+
<% if operation == 'submit'%>output :response_time<% end %>
|
18
|
+
makes_request :pa_<%= operation %>
|
19
|
+
|
20
|
+
def scratch_resources
|
21
|
+
scratch[:<%= operation %>_request_response_pair] ||= {}
|
22
|
+
end
|
23
|
+
|
24
|
+
def request_bundles
|
25
|
+
parsed_payload = JSON.parse(pa_<%= request_type %>_payload)
|
26
|
+
[parsed_payload].flatten.compact.uniq
|
27
|
+
end
|
28
|
+
|
29
|
+
def perform_operation(request_payload)
|
30
|
+
<% if operation == 'submit'%>start_time = Time.now<% end %>
|
31
|
+
fhir_operation('/Claim/$<%= operation %>', body: request_payload, name: :pa_<%= operation %>)
|
32
|
+
<% if operation == 'submit'%>response_time = Time.now - start_time
|
33
|
+
|
34
|
+
if response_time > 15
|
35
|
+
warning %(
|
36
|
+
The server took more that 15 seconds to respond to the Prior Authorization
|
37
|
+
request.
|
38
|
+
|
39
|
+
Response Time: #{response_time}
|
40
|
+
)
|
41
|
+
end
|
42
|
+
<% end %>
|
43
|
+
assert_response_status([200, 201])
|
44
|
+
assert_valid_json(request.response_body)
|
45
|
+
|
46
|
+
# Save request/response pair
|
47
|
+
scratch_resources[:all] ||= []
|
48
|
+
scratch_resources[:all] << {request_bundle: request.request_body, response_bundle: resource}
|
49
|
+
<% if operation == 'submit'%>output response_time:<% end %>
|
50
|
+
end
|
51
|
+
|
52
|
+
run do
|
53
|
+
skip_if pa_<%= request_type %>_payload.blank?, 'No bundle request provided to perform the <%= operation %> operation'
|
54
|
+
assert_valid_json(pa_<%= request_type %>_payload)
|
55
|
+
|
56
|
+
request_bundles.each do |bundle|
|
57
|
+
perform_operation(bundle)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module DaVinciPASTestKit
|
2
|
+
module <%= module_name %>
|
3
|
+
module ResourceList
|
4
|
+
RESOURCE_LIST = [
|
5
|
+
<%= resource_list_string.gsub("\n", "\n" + " "*4) %>
|
6
|
+
].freeze
|
7
|
+
|
8
|
+
RESOURCE_SUPPORTED_PROFILES = {<% resource_supported_profiles.each do |resource, profile_list| %>
|
9
|
+
<%= resource %>: <%= profile_list %>,<% end %>
|
10
|
+
}.freeze
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
require 'subscriptions_test_kit'
|
2
|
+
require_relative '../../validator_suppressions'
|
3
|
+
require_relative '<%= error_group_file_name %>'
|
4
|
+
<% group_file_list.each do |file_name| %>require_relative '<%= file_name %>'
|
5
|
+
<% end %>
|
6
|
+
module DaVinciPASTestKit
|
7
|
+
module <%= module_name %>
|
8
|
+
class <%= class_name %> < Inferno::TestSuite
|
9
|
+
id :<%= suite_id %>
|
10
|
+
title '<%= title %>'
|
11
|
+
description File.read(File.join(__dir__, '..', '..', 'docs', 'server_suite_description_<%= ig_metadata.reformatted_version %>.md'))
|
12
|
+
|
13
|
+
links [
|
14
|
+
{
|
15
|
+
label: 'Report Issue',
|
16
|
+
url: 'https://github.com/inferno-framework/davinci-pas-test-kit/issues/'
|
17
|
+
},
|
18
|
+
{
|
19
|
+
label: 'Open Source',
|
20
|
+
url: 'https://github.com/inferno-framework/davinci-pas-test-kit/'
|
21
|
+
},
|
22
|
+
{
|
23
|
+
label: 'Download',
|
24
|
+
url: 'https://github.com/inferno-framework/davinci-pas-test-kit/releases'
|
25
|
+
},
|
26
|
+
{
|
27
|
+
label: 'Implementation Guide',
|
28
|
+
url: '<%= ig_link %>'
|
29
|
+
}
|
30
|
+
]
|
31
|
+
|
32
|
+
resume_test_route :get, '/resume_after_notification' do |request|
|
33
|
+
request.query_parameters['token']
|
34
|
+
end
|
35
|
+
|
36
|
+
fhir_resource_validator do
|
37
|
+
igs '<%= ig_identifier %>'
|
38
|
+
|
39
|
+
exclude_message do |message|
|
40
|
+
# Messages expected of the form `<ResourceType>: <FHIRPath>: <message>`
|
41
|
+
# We strip `<ResourceType>: <FHIRPath>: ` for the sake of matching
|
42
|
+
SUPPRESSED_MESSAGES.match?(message.message.sub(/\A\S+: \S+: /, '')) ||
|
43
|
+
message.message.downcase.include?('x12')
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
input :server_endpoint,
|
48
|
+
title: 'FHIR Server Endpoint URL',
|
49
|
+
description: 'Insert the FHIR server endpoint URL for PAS'
|
50
|
+
|
51
|
+
input :smart_credentials,
|
52
|
+
title: 'OAuth Credentials',
|
53
|
+
type: :auth_info,
|
54
|
+
options: {
|
55
|
+
mode: 'access',
|
56
|
+
components: [
|
57
|
+
{
|
58
|
+
name: :auth_type,
|
59
|
+
default: 'backend_services'
|
60
|
+
}
|
61
|
+
]
|
62
|
+
},
|
63
|
+
optional: true
|
64
|
+
|
65
|
+
fhir_client do
|
66
|
+
url :server_endpoint
|
67
|
+
auth_info :smart_credentials
|
68
|
+
end
|
69
|
+
|
70
|
+
# Used for attestation experiment - see pas_claim_response_decision_test.rb
|
71
|
+
# resume_test_route :get, RESUME_PASS_PATH do |request|
|
72
|
+
# request.query_parameters['token']
|
73
|
+
# end
|
74
|
+
#
|
75
|
+
# resume_test_route :get, RESUME_FAIL_PATH, result: 'fail' do |request|
|
76
|
+
# request.query_parameters['token']
|
77
|
+
# end
|
78
|
+
|
79
|
+
group 'Demonstrate Workflow Support' do
|
80
|
+
description %(
|
81
|
+
The workflow tests validate that the server can participate in complete
|
82
|
+
end-to-end prior authorization interactions, returning responses that are
|
83
|
+
conformant and also contain the correct codes.
|
84
|
+
)
|
85
|
+
<% group_id_list.each do |id| %>
|
86
|
+
group from: :<%= id %><% end %>
|
87
|
+
end
|
88
|
+
group from: :<%= must_support_group_id %>
|
89
|
+
group from: :<%= error_group_id %>
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
require_relative '../../../pas_bundle_validation'
|
2
|
+
|
3
|
+
module DaVinciPASTestKit
|
4
|
+
module <%= module_name %>
|
5
|
+
class <%= class_name %> < Inferno::Test
|
6
|
+
include DaVinciPASTestKit::PasBundleValidation
|
7
|
+
|
8
|
+
id :<%= test_id %>
|
9
|
+
title '<%= title %>'
|
10
|
+
description %(
|
11
|
+
<%=description.strip.gsub("\n", "\n" + " "*8) %>
|
12
|
+
)
|
13
|
+
<% if request_type.include?('request') %>
|
14
|
+
input :pa_<%= request_type %>_payload,
|
15
|
+
title: 'PAS <%= request_type.humanize.titleize%> Payload',
|
16
|
+
description: 'Insert Bundle to be sent for PAS <%= request_type.humanize.titleize%>',
|
17
|
+
type: 'textarea',
|
18
|
+
optional: true
|
19
|
+
|
20
|
+
input_order :server_endpoint, :smart_credentials<% end %>
|
21
|
+
output :dar_code_found, :dar_extension_found
|
22
|
+
|
23
|
+
def resource_type
|
24
|
+
'<%= resource_type %>'
|
25
|
+
end
|
26
|
+
|
27
|
+
def scratch_resources
|
28
|
+
scratch[:<%= request_type %>_resources] ||= {}
|
29
|
+
end
|
30
|
+
|
31
|
+
def request_type
|
32
|
+
'<%= request_type.split('_').first %>'
|
33
|
+
end
|
34
|
+
<%if request_type.include?('response')%>
|
35
|
+
def target_request_response_pairs<% operation = request_type.split('_').first %>
|
36
|
+
scratch[:<%= operation %>_request_response_pair] ||= {}
|
37
|
+
scratch[:<%= operation %>_request_response_pair][:all] ||= []
|
38
|
+
end<% end %>
|
39
|
+
|
40
|
+
<%if request_type.include?('request')%>
|
41
|
+
def request_bundles
|
42
|
+
parsed_payload = JSON.parse(pa_<%= request_type %>_payload)
|
43
|
+
fhir_resources = [parsed_payload].flatten.compact.uniq.map { |payload| FHIR.from_contents(payload.to_json)}.compact
|
44
|
+
fhir_resources.select { |res| res.resourceType == 'Bundle'}
|
45
|
+
end<% end %>
|
46
|
+
|
47
|
+
run do<% if request_type.include?('request') %>
|
48
|
+
skip_if pa_<%= request_type %>_payload.blank?, 'No bundle request input provided.'
|
49
|
+
assert_valid_json(pa_<%= request_type %>_payload)
|
50
|
+
assert request_bundles.present?, 'Provided input is not a bundle or list of bundles'
|
51
|
+
|
52
|
+
save_bundles_and_entries_to_scratch(request_bundles)
|
53
|
+
|
54
|
+
request_bundles.each do |bundle|
|
55
|
+
perform_request_validation(
|
56
|
+
bundle,
|
57
|
+
'<%= profile_url %>',
|
58
|
+
'<%= profile_version %>',
|
59
|
+
request_type
|
60
|
+
)
|
61
|
+
end
|
62
|
+
|
63
|
+
validation_error_messages.each do |msg|
|
64
|
+
messages << { type: 'error', message: msg }
|
65
|
+
end
|
66
|
+
msg = 'Bundle(s) provided and/or entry resources are not conformant. Check messages for issues found.'
|
67
|
+
skip_if validation_error_messages.present?, msg
|
68
|
+
<% else %>
|
69
|
+
skip_if target_request_response_pairs.blank?, 'No <%= request_type.split('_').first%> response to validate. Either no <%= request_type.split('_').first%> request was made in a previous test or it resulted in a server error.'
|
70
|
+
target_pairs = target_request_response_pairs
|
71
|
+
# Clean request/response pair after validatation
|
72
|
+
scratch[:<%= operation %>_request_response_pair][:all] = []
|
73
|
+
|
74
|
+
response_bundles = target_pairs.map { |pair| pair[:response_bundle] }
|
75
|
+
save_bundles_and_entries_to_scratch(response_bundles)
|
76
|
+
|
77
|
+
target_pairs.each do |pair|
|
78
|
+
pair => {request_bundle:, response_bundle:}
|
79
|
+
assert_resource_type(:bundle, resource: response_bundle)
|
80
|
+
|
81
|
+
perform_response_validation(
|
82
|
+
response_bundle,
|
83
|
+
'<%= profile_url %>',
|
84
|
+
'<%= profile_version %>',
|
85
|
+
request_type,
|
86
|
+
request_bundle
|
87
|
+
)
|
88
|
+
end
|
89
|
+
|
90
|
+
validation_error_messages.each do |msg|
|
91
|
+
messages << { type: 'error', message: msg }
|
92
|
+
end
|
93
|
+
msg = 'Bundle response returned and/or entry resources are not conformant. Check messages for issues found.'
|
94
|
+
assert validation_error_messages.blank?, msg<% end %>
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -6,42 +6,23 @@ module DaVinciPASTestKit
|
|
6
6
|
class ValidationTestGenerator
|
7
7
|
class << self
|
8
8
|
def generate(ig_metadata, base_output_dir)
|
9
|
-
|
10
|
-
|
11
|
-
ig_metadata.bundle_groups.each do |group|
|
12
|
-
new(group, system, base_output_dir:).generate
|
13
|
-
end
|
14
|
-
else
|
15
|
-
ig_metadata.bundle_groups.each do |group|
|
16
|
-
case group.profile_name
|
17
|
-
when 'PAS Request Bundle'
|
18
|
-
new(group, system, base_output_dir:).generate
|
19
|
-
when 'PAS Inquiry Request Bundle'
|
20
|
-
new(group, system, 'pended_inquiry', base_output_dir:).generate
|
21
|
-
when 'PAS Response Bundle'
|
22
|
-
['denial', 'pended'].each do |workflow|
|
23
|
-
new(group, system, workflow, base_output_dir:).generate
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
9
|
+
ig_metadata.bundle_groups.each do |group|
|
10
|
+
new(group, base_output_dir:).generate
|
28
11
|
end
|
29
12
|
end
|
30
13
|
end
|
31
14
|
|
32
|
-
attr_accessor :group_metadata, :medication_request_metadata, :base_output_dir, :
|
15
|
+
attr_accessor :group_metadata, :medication_request_metadata, :base_output_dir, :workflow
|
33
16
|
|
34
|
-
def initialize(group_metadata,
|
17
|
+
def initialize(group_metadata, workflow = nil, medication_request_metadata = nil, base_output_dir:)
|
35
18
|
self.group_metadata = group_metadata
|
36
|
-
self.system = system
|
37
19
|
self.workflow = workflow
|
38
20
|
self.medication_request_metadata = medication_request_metadata
|
39
21
|
self.base_output_dir = base_output_dir
|
40
22
|
end
|
41
23
|
|
42
24
|
def template
|
43
|
-
|
44
|
-
@template ||= File.read(File.join(__dir__, 'templates', temp))
|
25
|
+
@template ||= File.read(File.join(__dir__, 'templates', 'validation.rb.erb'))
|
45
26
|
end
|
46
27
|
|
47
28
|
def output
|
@@ -61,8 +42,6 @@ module DaVinciPASTestKit
|
|
61
42
|
end
|
62
43
|
|
63
44
|
def directory_name
|
64
|
-
return 'client_tests' if system == 'client'
|
65
|
-
|
66
45
|
Naming.snake_case_for_profile(medication_request_metadata || group_metadata)
|
67
46
|
end
|
68
47
|
|
@@ -87,12 +66,12 @@ module DaVinciPASTestKit
|
|
87
66
|
end
|
88
67
|
|
89
68
|
def test_id
|
90
|
-
pref = "
|
69
|
+
pref = "pas_server_#{group_metadata.reformatted_version}_#{formatted_workflow}".delete_suffix('_')
|
91
70
|
"#{pref}_#{profile_identifier}_validation_test"
|
92
71
|
end
|
93
72
|
|
94
73
|
def class_name
|
95
|
-
pref = "#{
|
74
|
+
pref = "Server#{formatted_workflow.camelize}"
|
96
75
|
"#{pref}#{Naming.upper_camel_case_for_profile(group_metadata)}ValidationTest"
|
97
76
|
end
|
98
77
|
|
@@ -135,14 +114,23 @@ module DaVinciPASTestKit
|
|
135
114
|
end
|
136
115
|
|
137
116
|
def user_input?
|
138
|
-
|
139
|
-
(system == 'client' && request_type.include?('response'))
|
117
|
+
request_type.include?('request')
|
140
118
|
end
|
141
119
|
|
142
120
|
def description
|
143
121
|
<<~DESCRIPTION
|
144
122
|
#{description_user_input_validation if user_input?}
|
145
|
-
|
123
|
+
|
124
|
+
This test validates the conformity of the
|
125
|
+
#{request_type.include?('request') ? 'user input' : "server's response"} to the
|
126
|
+
[#{profile_name}](#{profile_url})#{' '}
|
127
|
+
structure#{request_type.include?('request') ? ', ensuring subsequent tests can accurately simulate content.' : '.'}
|
128
|
+
|
129
|
+
It also checks that other conformance requirements defined in the [PAS Formal
|
130
|
+
Specification](https://hl7.org/fhir/us/davinci-pas/STU2/specification.html),
|
131
|
+
such as the presence of all referenced instances within the bundle and the
|
132
|
+
conformance of those instances to the appropriate profiles, are met.
|
133
|
+
|
146
134
|
It verifies the presence of mandatory elements and that elements with
|
147
135
|
required bindings contain appropriate values. CodeableConcept element
|
148
136
|
bindings will fail if none of their codings have a code/system belonging
|
@@ -162,31 +150,6 @@ module DaVinciPASTestKit
|
|
162
150
|
DESCRIPTION
|
163
151
|
end
|
164
152
|
|
165
|
-
def description_intro_server
|
166
|
-
<<~GENERIC_INTRO
|
167
|
-
This test validates the conformity of the
|
168
|
-
#{request_type.include?('request') ? 'user input' : "server's response"} to the
|
169
|
-
[#{profile_name}](#{profile_url}) structure#{request_type.include?('request') ? ', ensuring subsequent tests can accurately simulate content.' : '.'}
|
170
|
-
|
171
|
-
It also checks that other conformance requirements defined in the [PAS Formal
|
172
|
-
Specification](https://hl7.org/fhir/us/davinci-pas/STU2/specification.html),
|
173
|
-
such as the presence of all referenced instances within the bundle and the
|
174
|
-
conformance of those instances to the appropriate profiles, are met.
|
175
|
-
GENERIC_INTRO
|
176
|
-
end
|
177
|
-
|
178
|
-
def description_intro_client
|
179
|
-
<<~GENERIC_INTRO
|
180
|
-
This test validates the conformity of the
|
181
|
-
#{request_type.include?('response') ? 'user input' : "client's request"} to the
|
182
|
-
[#{profile_name}](#{profile_url}) structure.
|
183
|
-
It also checks that other conformance requirements defined in the [PAS Formal
|
184
|
-
Specification](https://hl7.org/fhir/us/davinci-pas/STU2/specification.html),
|
185
|
-
such as the presence of all referenced instances within the bundle and the
|
186
|
-
conformance of those instances to the appropriate profiles, are met.
|
187
|
-
GENERIC_INTRO
|
188
|
-
end
|
189
|
-
|
190
153
|
def description_user_input_validation
|
191
154
|
<<~USER_INPUT_INTRO
|
192
155
|
**USER INPUT VALIDATION**: This test validates input provided by the user instead of the system under test.
|
@@ -17,7 +17,10 @@ module DaVinciPASTestKit
|
|
17
17
|
end
|
18
18
|
|
19
19
|
def bound_systems(the_element)
|
20
|
-
value_set(the_element)
|
20
|
+
vs = value_set(the_element)
|
21
|
+
return unless vs.present?
|
22
|
+
|
23
|
+
vs.compose&.include&.reject { |code| code.concept.nil? }
|
21
24
|
end
|
22
25
|
|
23
26
|
def values_from_value_set_binding(the_element)
|
Binary file
|
@@ -0,0 +1,136 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../tags'
|
4
|
+
require_relative '../urls'
|
5
|
+
require 'subscriptions_test_kit'
|
6
|
+
|
7
|
+
module DaVinciPASTestKit
|
8
|
+
module Jobs
|
9
|
+
class SendPASSubscriptionNotification
|
10
|
+
include Sidekiq::Job
|
11
|
+
include SubscriptionsTestKit::SubscriptionsR5BackportR4Client::SubscriptionSimulationUtils
|
12
|
+
include URLs
|
13
|
+
|
14
|
+
# override the one from URLs
|
15
|
+
def suite_id
|
16
|
+
@notification_suite_id
|
17
|
+
end
|
18
|
+
|
19
|
+
sidekiq_options retry: false
|
20
|
+
|
21
|
+
def perform(test_run_id, test_session_id, result_id, notification_bearer_token, notification_json, resume_token,
|
22
|
+
notification_suite_id)
|
23
|
+
@test_run_id = test_run_id
|
24
|
+
@test_session_id = test_session_id
|
25
|
+
@result_id = result_id
|
26
|
+
@notification_bearer_token = notification_bearer_token
|
27
|
+
@notification_json = notification_json
|
28
|
+
@resume_token = resume_token
|
29
|
+
@notification_suite_id = notification_suite_id
|
30
|
+
|
31
|
+
await_subscription_creation # NOTE: currently must exist - see PASClientPendedSubmitTest
|
32
|
+
sleep 1
|
33
|
+
return unless test_still_waiting?
|
34
|
+
|
35
|
+
sleep rand(5..10)
|
36
|
+
return unless test_still_waiting?
|
37
|
+
|
38
|
+
send_event_notification
|
39
|
+
end
|
40
|
+
|
41
|
+
def requests_repo
|
42
|
+
@requests_repo ||= Inferno::Repositories::Requests.new
|
43
|
+
end
|
44
|
+
|
45
|
+
def results_repo
|
46
|
+
@results_repo ||= Inferno::Repositories::Results.new
|
47
|
+
end
|
48
|
+
|
49
|
+
def subscription
|
50
|
+
@subscription ||= find_subscription(@test_session_id)
|
51
|
+
end
|
52
|
+
|
53
|
+
def subscription_notification_endpoint
|
54
|
+
subscription&.channel&.endpoint
|
55
|
+
end
|
56
|
+
|
57
|
+
def headers
|
58
|
+
@headers ||= subscription_headers.merge(content_type_header).merge(authorization_header)
|
59
|
+
end
|
60
|
+
|
61
|
+
def rest_hook_connection
|
62
|
+
@rest_hook_connection ||= Faraday.new(url: subscription_notification_endpoint, request: { open_timeout: 30 },
|
63
|
+
headers:)
|
64
|
+
end
|
65
|
+
|
66
|
+
def test_suite_connection
|
67
|
+
@test_suite_connection ||= Faraday.new(base_url)
|
68
|
+
end
|
69
|
+
|
70
|
+
def content_type_header
|
71
|
+
@content_type_header ||= { 'Content-Type' => actual_mime_type(subscription) }
|
72
|
+
end
|
73
|
+
|
74
|
+
def subscription_headers
|
75
|
+
@subscription_headers ||= subscription.channel&.header&.each_with_object({}) do |header, hash|
|
76
|
+
header_name, header_value = header.split(': ', 2)
|
77
|
+
hash[header_name] = header_value
|
78
|
+
end || {}
|
79
|
+
end
|
80
|
+
|
81
|
+
def authorization_header
|
82
|
+
@authorization_header ||=
|
83
|
+
@notification_bearer_token.present? ? { 'Authorization' => "Bearer #{@notification_bearer_token}" } : {}
|
84
|
+
end
|
85
|
+
|
86
|
+
def subscription_topic
|
87
|
+
@subscription_topic ||= subscription&.criteria
|
88
|
+
end
|
89
|
+
|
90
|
+
def subscription_full_url
|
91
|
+
@subscription_full_url ||= "#{fhir_subscription_url}/#{subscription.id}"
|
92
|
+
end
|
93
|
+
|
94
|
+
def test_still_waiting?
|
95
|
+
results_repo.find_waiting_result(test_run_id: @test_run_id)
|
96
|
+
end
|
97
|
+
|
98
|
+
def await_subscription_creation
|
99
|
+
sleep 0.5 until subscription.present? || !test_still_waiting?
|
100
|
+
end
|
101
|
+
|
102
|
+
def send_event_notification
|
103
|
+
event_json = derive_event_notification(@notification_json, subscription_full_url, subscription_topic, 1).to_json
|
104
|
+
response = send_notification(event_json)
|
105
|
+
persist_notification_request(response, [REST_HOOK_EVENT_NOTIFICATION_TAG])
|
106
|
+
end
|
107
|
+
|
108
|
+
def send_notification(request_body)
|
109
|
+
rest_hook_connection.post('', request_body)
|
110
|
+
rescue Faraday::Error => e
|
111
|
+
# Warning: This is a hack. If there is an error with the request such that we never get a response, we have
|
112
|
+
# no clean way to persist that information for the Inferno test to check later. The solution here
|
113
|
+
# is to persist the request anyway with a status of nil, using the error message as response body
|
114
|
+
Faraday::Response.new(response_body: e.message, url: rest_hook_connection.url_prefix.to_s)
|
115
|
+
end
|
116
|
+
|
117
|
+
def persist_notification_request(response, tags)
|
118
|
+
inferno_request_headers = headers.map { |name, value| { name:, value: } }
|
119
|
+
inferno_response_headers = response.headers&.map { |name, value| { name:, value: } }
|
120
|
+
requests_repo.create(
|
121
|
+
verb: 'POST',
|
122
|
+
url: response.env.url.to_s,
|
123
|
+
direction: 'outgoing',
|
124
|
+
status: response.status,
|
125
|
+
request_body: response.env.request_body,
|
126
|
+
response_body: response.env.response_body,
|
127
|
+
test_session_id: @test_session_id,
|
128
|
+
result_id: @result_id,
|
129
|
+
request_headers: inferno_request_headers,
|
130
|
+
response_headers: inferno_response_headers,
|
131
|
+
tags:
|
132
|
+
)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|