davinci_pas_test_kit 0.11.0 → 0.11.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: adbcd07bb5f1a40e3ca790bab8009a1d905f444a22fee717fe06a2630e9360f4
4
- data.tar.gz: 1b3ec155956da7225994cb43a1d4cbaaf73ebc988e25b9ef1acdf375cc917026
3
+ metadata.gz: c4814f5a03b2688fac37d470bfa033640ba717a566897bf1d037c618cd404a66
4
+ data.tar.gz: 7d346d15fcbac4c5032263ed40da6ae018792cbbc9c5dd2589cbb0eee193897f
5
5
  SHA512:
6
- metadata.gz: 9f0e290e3d5c5af6f5263d23f78b69d4d9f017ea5452ebf56602361b57e7c22e1ff5cfa63f5bbbbc06541ddb66a031373a65699c6b6ff5ac0026038ce1633678
7
- data.tar.gz: 10d76ce494d9cfd1aee463b25a04624b8556f1f80f99b175a2b4c1e00a7e6f8c31bb1a5844b83688df0ac3f86e80e02465f9791ea657c82651e5b6761e97e221
6
+ metadata.gz: 99ad90a6c99758c1bcc61d0d67529676c4a63f328e73ec527bd5b429e5509d0133848a884220dc7e82a064c229879eff7a42233966a4ff254dd2f41a9eb3bf4b
7
+ data.tar.gz: 9fe9a036b193b5a201515881a40f657325a0535730c89a70c32acdc936eb6c9210d93b698393aff135eb25a309f27e583073b0f5f90789469f6b152a0e2f0cda
@@ -1,10 +1,8 @@
1
- require_relative 'ext/inferno_core/record_response_route'
2
- require_relative 'ext/inferno_core/runnable'
3
- require_relative 'ext/inferno_core/request'
4
1
  require_relative 'validator_suppressions'
5
2
  require_relative 'tags'
6
3
  require_relative 'urls'
7
- require_relative 'mock_server'
4
+ require_relative 'endpoints/claim_endpoint'
5
+ require_relative 'endpoints/token_endpoint'
8
6
  require_relative 'custom_groups/v2.0.1/pas_client_authentication_group'
9
7
  require_relative 'custom_groups/v2.0.1/pas_client_approval_group'
10
8
  require_relative 'custom_groups/v2.0.1/pas_client_denial_group'
@@ -15,8 +13,6 @@ require_relative 'version'
15
13
 
16
14
  module DaVinciPASTestKit
17
15
  class ClientSuite < Inferno::TestSuite
18
- extend MockServer
19
-
20
16
  id :davinci_pas_client_suite_v201
21
17
  title 'Da Vinci PAS Client Suite v2.0.1'
22
18
  version VERSION
@@ -37,10 +33,6 @@ module DaVinciPASTestKit
37
33
  }
38
34
  ]
39
35
 
40
- def self.test_resumes?(test)
41
- !test.config.options[:accepts_multiple_requests]
42
- end
43
-
44
36
  fhir_resource_validator do
45
37
  igs 'hl7.fhir.us.davinci-pas#2.0.1'
46
38
 
@@ -51,26 +43,16 @@ module DaVinciPASTestKit
51
43
  end
52
44
  end
53
45
 
54
- record_response_route :post, TOKEN_PATH, AUTH_TAG, method(:token_response) do |request|
55
- ClientSuite.extract_client_id(request)
56
- end
57
-
58
- record_response_route :post, SUBMIT_PATH, SUBMIT_TAG, method(:claim_response),
59
- resumes: method(:test_resumes?) do |request|
60
- ClientSuite.extract_bearer_token(request)
61
- end
62
-
63
- record_response_route :post, INQUIRE_PATH, INQUIRE_TAG, method(:claim_response),
64
- resumes: method(:test_resumes?) do |request|
65
- ClientSuite.extract_bearer_token(request)
66
- end
46
+ suite_endpoint :post, TOKEN_PATH, TokenEndpoint
47
+ suite_endpoint :post, SUBMIT_PATH, ClaimEndpoint
48
+ suite_endpoint :post, INQUIRE_PATH, ClaimEndpoint
67
49
 
68
50
  resume_test_route :get, RESUME_PASS_PATH do |request|
69
- ClientSuite.extract_token_from_query_params(request)
51
+ request.query_parameters['token']
70
52
  end
71
53
 
72
54
  resume_test_route :get, RESUME_FAIL_PATH, result: 'fail' do |request|
73
- ClientSuite.extract_token_from_query_params(request)
55
+ request.query_parameters['token']
74
56
  end
75
57
 
76
58
  group from: :pas_client_v201_authentication_group
@@ -1,38 +1,35 @@
1
- require_relative 'user_input_response'
1
+ require_relative '../user_input_response'
2
2
 
3
3
  module DaVinciPASTestKit
4
- # Serve responses to PAS requests
5
- #
6
- # Note that there are numerous expected validation issues that can safely be ignored.
7
- # See here for full list: https://hl7.org/fhir/us/davinci-pas/STU2/qa.html#suppressed
8
- module MockServer
9
- def token_response(request, _test = nil, _test_result = nil)
10
- # Placeholder for a more complete mock token endpoint
11
- request.response_body = { access_token: SecureRandom.hex, token_type: 'bearer', expires_in: 300 }.to_json
12
- request.status = 200
4
+ class ClaimEndpoint < Inferno::DSL::SuiteEndpoint
5
+ def test_run_identifier
6
+ request.headers['authorization']&.delete_prefix('Bearer ')
13
7
  end
14
8
 
15
- def claim_response(request, test = nil, test_result = nil)
16
- request.status = 200
17
- request.response_headers = { 'Content-Type': 'application/json' }
9
+ def tags
10
+ [operation == 'submit' ? SUBMIT_TAG : INQUIRE_TAG]
11
+ end
12
+
13
+ def make_response
14
+ response.status = 200
15
+ response.format = :json
18
16
 
19
- user_inputted_response = UserInputResponse.user_inputted_response(test, test_result)
20
- if test.present? && test_result.present? && user_inputted_response.present?
21
- request.response_body = user_inputted_response
17
+ user_inputted_response = UserInputResponse.user_inputted_response(test, result)
18
+ if user_inputted_response.present?
19
+ response.body = user_inputted_response
22
20
  return
23
21
  end
24
22
 
25
- operation = request&.url&.split('$')&.last
26
- req_bundle = FHIR.from_contents(request&.request_body)
23
+ req_bundle = FHIR.from_contents(request.body.string)
27
24
  claim_entry = req_bundle&.entry&.find { |e| e&.resource&.resourceType == 'Claim' }
28
25
  claim_full_url = claim_entry&.fullUrl
29
26
  if claim_entry.blank? || claim_full_url.blank?
30
- handle_missing_required_elements(claim_entry, request)
27
+ handle_missing_required_elements(claim_entry, response)
31
28
  return
32
29
  end
33
30
 
34
31
  root_url = base_url(claim_full_url)
35
- claim_response = mock_claim_response(claim_entry.resource, req_bundle, operation, root_url)
32
+ claim_response = mock_claim_response(claim_entry.resource, req_bundle, root_url)
36
33
 
37
34
  res_bundle = FHIR::Bundle.new(
38
35
  id: SecureRandom.uuid,
@@ -51,16 +48,20 @@ module DaVinciPASTestKit
51
48
 
52
49
  res_bundle.entry.concat(referenced_entities(claim_response, req_bundle.entry, root_url))
53
50
 
54
- request.response_body = res_bundle.to_json
55
- request.status = 200
56
- request.response_headers = { 'Content-Type': 'application/json' }
51
+ response.body = res_bundle.to_json
57
52
  end
58
53
 
54
+ def update_result
55
+ results_repo.update_result(result.id, 'pass') unless test.config.options[:accepts_multiple_requests]
56
+ end
57
+
58
+ private
59
+
59
60
  # Note that references from the claim to other resources in the bundle need to be changed to absolute URLs
60
61
  # if they are relative, because the ClaimResponse's fullUrl is a urn:uuid
61
62
  #
62
63
  # @private
63
- def mock_claim_response(claim, bundle, operation, root_url)
64
+ def mock_claim_response(claim, bundle, root_url)
64
65
  return FHIR::ClaimResponse.new(id: SecureRandom.uuid) if claim.blank?
65
66
 
66
67
  now = Time.now.utc
@@ -128,33 +129,23 @@ module DaVinciPASTestKit
128
129
  )
129
130
  end
130
131
 
131
- def extract_client_id(request)
132
- URI.decode_www_form(request.request_body).to_h['client_id']
133
- end
134
-
135
- # Header expected to be a bearer token of the form "Bearer: <token>"
136
- def extract_bearer_token(request)
137
- request.request_header('Authorization')&.value&.split&.last
138
- end
139
-
140
- def extract_token_from_query_params(request)
141
- request.query_parameters['token']
142
- end
143
-
144
- def handle_missing_required_elements(claim_entry, request)
145
- request.status = 400
146
- request.response_headers = { 'Content-Type': 'application/json' }
132
+ def handle_missing_required_elements(claim_entry, response)
133
+ response.status = 400
147
134
  details = if claim_entry.blank?
148
135
  'Required Claim entry missing from bundle'
149
136
  else
150
137
  'Required element fullUrl missing from Claim entry'
151
138
  end
152
- request.response_body = FHIR::OperationOutcome.new(
139
+ response.body = FHIR::OperationOutcome.new(
153
140
  issue: FHIR::OperationOutcome::Issue.new(severity: 'fatal', code: 'required',
154
141
  details: FHIR::CodeableConcept.new(text: details))
155
142
  ).to_json
156
143
  end
157
144
 
145
+ def operation
146
+ request.url&.split('$')&.last
147
+ end
148
+
158
149
  # Drop the last two segments of a URL, i.e. the resource type and ID of a FHIR resource
159
150
  # e.g. http://example.org/fhir/Patient/123 -> http://example.org/fhir
160
151
  # @private
@@ -165,7 +156,6 @@ module DaVinciPASTestKit
165
156
  url.sub(%r{/[^/]*/[^/]*(/)?\z}, '')
166
157
  end
167
158
 
168
- # @private
169
159
  def referenced_entities(resource, entries, root_url)
170
160
  matches = []
171
161
  attributes = resource&.source_hash&.keys
@@ -185,21 +175,18 @@ module DaVinciPASTestKit
185
175
  matches
186
176
  end
187
177
 
188
- # @private
189
178
  def absolute_reference(ref, entries, root_url)
190
179
  url = find_matching_entry(ref&.reference, entries, root_url)&.fullUrl
191
180
  ref.reference = url if url
192
181
  ref
193
182
  end
194
183
 
195
- # @private
196
184
  def find_matching_entry(ref, entries, root_url = '')
197
185
  ref = "#{root_url}/#{ref}" if relative_reference?(ref) && root_url&.present?
198
186
 
199
187
  entries&.find { |entry| entry&.fullUrl == ref }
200
188
  end
201
189
 
202
- # @private
203
190
  def relative_reference?(ref)
204
191
  ref&.count('/') == 1
205
192
  end
@@ -0,0 +1,22 @@
1
+ module DaVinciPASTestKit
2
+ class TokenEndpoint < Inferno::DSL::SuiteEndpoint
3
+ def test_run_identifier
4
+ URI.decode_www_form(request.body.string).to_h['client_id']
5
+ end
6
+
7
+ def tags
8
+ [AUTH_TAG]
9
+ end
10
+
11
+ def make_response
12
+ # Placeholder for a more complete mock token endpoint
13
+ response.status = 200
14
+ response.format = :json
15
+ response.body = { access_token: SecureRandom.hex, token_type: 'bearer', expires_in: 300 }.to_json
16
+ end
17
+
18
+ def update_result
19
+ results_repo.update_result(result.id, 'pass')
20
+ end
21
+ end
22
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DaVinciPASTestKit
4
- VERSION = '0.11.0'
4
+ VERSION = '0.11.1'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: davinci_pas_test_kit
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.11.0
4
+ version: 0.11.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Inferno Team
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-11-06 00:00:00.000000000 Z
11
+ date: 2024-12-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: inferno_core
@@ -120,9 +120,8 @@ files:
120
120
  - lib/davinci_pas_test_kit/custom_groups/v2.0.1/pas_error_group.rb
121
121
  - lib/davinci_pas_test_kit/docs/client_suite_description_v201.md
122
122
  - lib/davinci_pas_test_kit/docs/server_suite_description_v201.md
123
- - lib/davinci_pas_test_kit/ext/inferno_core/record_response_route.rb
124
- - lib/davinci_pas_test_kit/ext/inferno_core/request.rb
125
- - lib/davinci_pas_test_kit/ext/inferno_core/runnable.rb
123
+ - lib/davinci_pas_test_kit/endpoints/claim_endpoint.rb
124
+ - lib/davinci_pas_test_kit/endpoints/token_endpoint.rb
126
125
  - lib/davinci_pas_test_kit/fhir_resource_navigation.rb
127
126
  - lib/davinci_pas_test_kit/generated/v2.0.1/beneficiary/client_inquire_request_beneficiary_must_support_test.rb
128
127
  - lib/davinci_pas_test_kit/generated/v2.0.1/beneficiary/client_submit_request_beneficiary_must_support_test.rb
@@ -248,7 +247,6 @@ files:
248
247
  - lib/davinci_pas_test_kit/generator/validation_test_generator.rb
249
248
  - lib/davinci_pas_test_kit/generator/value_extractor.rb
250
249
  - lib/davinci_pas_test_kit/igs/README.md
251
- - lib/davinci_pas_test_kit/mock_server.rb
252
250
  - lib/davinci_pas_test_kit/must_support_test.rb
253
251
  - lib/davinci_pas_test_kit/pas_bundle_validation.rb
254
252
  - lib/davinci_pas_test_kit/tags.rb
@@ -1,98 +0,0 @@
1
- require 'hanami/controller'
2
-
3
- module Inferno
4
- module DSL
5
- # A base class for creating routes with custom response logic. Requests and responses are tagged and saved.
6
- # @private
7
- # @see Inferno::DSL::Runnable#resume_test_route
8
- class RecordResponseRoute < Hanami::Action
9
- include Import[
10
- requests_repo: 'inferno.repositories.requests',
11
- results_repo: 'inferno.repositories.results',
12
- test_runs_repo: 'inferno.repositories.test_runs',
13
- tests_repo: 'inferno.repositories.tests'
14
- ]
15
-
16
- def self.call(...)
17
- new.call(...)
18
- end
19
-
20
- # @private
21
- def test_run_identifier_block
22
- self.class.singleton_class.instance_variable_get(:@test_run_identifier_block)
23
- end
24
-
25
- # @private
26
- def build_response_block
27
- self.class.singleton_class.instance_variable_get(:@build_response_block)
28
- end
29
-
30
- # @private
31
- def tags
32
- self.class.singleton_class.instance_variable_get(:@tags)
33
- end
34
-
35
- # @private
36
- def resumes?(test)
37
- instance_exec(test, &self.class.singleton_class.instance_variable_get(:@resumes))
38
- end
39
-
40
- # @private
41
- def find_test_run(test_run_identifier)
42
- test_runs_repo.find_latest_waiting_by_identifier(test_run_identifier)
43
- end
44
-
45
- # @private
46
- def find_waiting_result(test_run)
47
- results_repo.find_waiting_result(test_run_id: test_run.id)
48
- end
49
-
50
- # @private
51
- def update_result(waiting_result)
52
- results_repo.update_result(waiting_result.id, 'pass')
53
- end
54
-
55
- # @private
56
- def persist_request(request, test_run, waiting_result, test)
57
- requests_repo.create(
58
- request.to_hash.merge(
59
- test_session_id: test_run.test_session_id,
60
- result_id: waiting_result.id,
61
- name: test.config.request_name(test.incoming_request_name),
62
- tags:
63
- )
64
- )
65
- end
66
-
67
- # @private
68
- def find_test(waiting_result)
69
- tests_repo.find(waiting_result.test_id)
70
- end
71
-
72
- # @private
73
- def handle(req, res)
74
- request = Inferno::Entities::Request.from_hanami_request(req)
75
-
76
- test_run_identifier = instance_exec(request, &test_run_identifier_block)
77
-
78
- test_run = find_test_run(test_run_identifier)
79
-
80
- halt 500, "Unable to find test run with identifier '#{test_run_identifier}'." if test_run.nil?
81
-
82
- waiting_result = find_waiting_result(test_run)
83
- test = find_test(waiting_result)
84
-
85
- test_runs_repo.mark_as_no_longer_waiting(test_run.id) if resumes? test
86
-
87
- update_result(waiting_result) if resumes? test
88
-
89
- instance_exec(request, test, waiting_result, &build_response_block)
90
-
91
- Inferno::Entities::Request.to_hanami_response(request, res)
92
- persist_request(request, test_run, waiting_result, test)
93
-
94
- Jobs.perform(Jobs::ResumeTestRun, test_run.id) if resumes? test
95
- end
96
- end
97
- end
98
- end
@@ -1,19 +0,0 @@
1
- module Inferno
2
- module Entities
3
- class Request
4
- def self.to_hanami_response(request, response)
5
- response.status = request.status
6
- response.body = request.response_body
7
- request.response_headers.each do |header|
8
- response.headers[header.name] = header.value
9
- end
10
-
11
- response
12
- end
13
-
14
- def response_headers=(headers_hash)
15
- headers.concat(headers_hash.map { |key, value| Header.new(name: key.to_s, value:, type: 'response') })
16
- end
17
- end
18
- end
19
- end
@@ -1,18 +0,0 @@
1
- require_relative 'record_response_route'
2
-
3
- module Inferno
4
- module DSL
5
- module Runnable
6
- def record_response_route(method, path, tags, build_response, resumes: ->(_) { true }, &block)
7
- route_class = Class.new(Inferno::DSL::RecordResponseRoute) do |klass|
8
- klass.singleton_class.instance_variable_set(:@build_response_block, build_response)
9
- klass.singleton_class.instance_variable_set(:@test_run_identifier_block, block)
10
- klass.singleton_class.instance_variable_set(:@tags, Array.wrap(tags))
11
- klass.singleton_class.instance_variable_set(:@resumes, resumes)
12
- end
13
-
14
- route(method, path, route_class)
15
- end
16
- end
17
- end
18
- end