bulk_data_test_kit 0.13.2 → 0.14.0

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.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/config/presets/bulk_data_v400_submit_consumer_demo.json.erb +65 -0
  3. data/config/presets/bulk_data_v400_submit_provider_demo.json +22 -0
  4. data/lib/bulk_data_test_kit/metadata.rb +20 -4
  5. data/lib/bulk_data_test_kit/requirements/generated/bulk_data_v101_requirements_coverage.csv +1 -1
  6. data/lib/bulk_data_test_kit/requirements/generated/bulk_data_v200_client_requirements_coverage.csv +1 -1
  7. data/lib/bulk_data_test_kit/requirements/generated/bulk_data_v200_requirements_coverage.csv +1 -1
  8. data/lib/bulk_data_test_kit/requirements/generated/bulk_data_v400_submit_consumer_requirements_coverage.csv +18 -0
  9. data/lib/bulk_data_test_kit/requirements/generated/bulk_data_v400_submit_provider_requirements_coverage.csv +4 -0
  10. data/lib/bulk_data_test_kit/v1.0.1/bulk_data_test_suite.rb +3 -3
  11. data/lib/bulk_data_test_kit/v2.0.0/bulk_data_test_suite.rb +3 -3
  12. data/lib/bulk_data_test_kit/v2.0.0_client/bulk_data_client_test_suite.rb +1 -1
  13. data/lib/bulk_data_test_kit/v2.0.0_client/docs/suite_description.md +4 -4
  14. data/lib/bulk_data_test_kit/v4.0.0/submit/consumer/backend_services_group.rb +50 -0
  15. data/lib/bulk_data_test_kit/v4.0.0/submit/consumer/complete_poll_group.rb +105 -0
  16. data/lib/bulk_data_test_kit/v4.0.0/submit/consumer/docs/suite_description.md +49 -0
  17. data/lib/bulk_data_test_kit/v4.0.0/submit/consumer/endpoints/file_download.rb +39 -0
  18. data/lib/bulk_data_test_kit/v4.0.0/submit/consumer/endpoints/manifest.rb +56 -0
  19. data/lib/bulk_data_test_kit/v4.0.0/submit/consumer/helpers.rb +78 -0
  20. data/lib/bulk_data_test_kit/v4.0.0/submit/consumer/manifest_and_file_retrieval.rb +59 -0
  21. data/lib/bulk_data_test_kit/v4.0.0/submit/consumer/poll_group.rb +66 -0
  22. data/lib/bulk_data_test_kit/v4.0.0/submit/consumer/processing_wait_group.rb +53 -0
  23. data/lib/bulk_data_test_kit/v4.0.0/submit/consumer/status_submit_group.rb +78 -0
  24. data/lib/bulk_data_test_kit/v4.0.0/submit/consumer/submit_completed_group.rb +65 -0
  25. data/lib/bulk_data_test_kit/v4.0.0/submit/consumer/submit_group.rb +66 -0
  26. data/lib/bulk_data_test_kit/v4.0.0/submit/consumer/tags.rb +12 -0
  27. data/lib/bulk_data_test_kit/v4.0.0/submit/consumer/test_suite.rb +116 -0
  28. data/lib/bulk_data_test_kit/v4.0.0/submit/consumer/urls.rb +43 -0
  29. data/lib/bulk_data_test_kit/v4.0.0/submit/provider/client_registration_group.rb +42 -0
  30. data/lib/bulk_data_test_kit/v4.0.0/submit/provider/docs/suite_description.md +50 -0
  31. data/lib/bulk_data_test_kit/v4.0.0/submit/provider/endpoints/poll.rb +90 -0
  32. data/lib/bulk_data_test_kit/v4.0.0/submit/provider/endpoints/status.rb +63 -0
  33. data/lib/bulk_data_test_kit/v4.0.0/submit/provider/endpoints/submit.rb +35 -0
  34. data/lib/bulk_data_test_kit/v4.0.0/submit/provider/helpers.rb +222 -0
  35. data/lib/bulk_data_test_kit/v4.0.0/submit/provider/manifest_and_file_retrieval.rb +165 -0
  36. data/lib/bulk_data_test_kit/v4.0.0/submit/provider/poll_group.rb +36 -0
  37. data/lib/bulk_data_test_kit/v4.0.0/submit/provider/processing_wait_group.rb +83 -0
  38. data/lib/bulk_data_test_kit/v4.0.0/submit/provider/retrieval.rb +139 -0
  39. data/lib/bulk_data_test_kit/v4.0.0/submit/provider/status_submit_group.rb +96 -0
  40. data/lib/bulk_data_test_kit/v4.0.0/submit/provider/submit_group.rb +314 -0
  41. data/lib/bulk_data_test_kit/v4.0.0/submit/provider/submit_stopped_group.rb +43 -0
  42. data/lib/bulk_data_test_kit/v4.0.0/submit/provider/tags.rb +18 -0
  43. data/lib/bulk_data_test_kit/v4.0.0/submit/provider/test_suite.rb +122 -0
  44. data/lib/bulk_data_test_kit/v4.0.0/submit/provider/urls.rb +53 -0
  45. data/lib/bulk_data_test_kit/v4.0.0/submit/provider/wait_group.rb +73 -0
  46. data/lib/bulk_data_test_kit/v4.0.0/submit/shared/jwks.rb +27 -0
  47. data/lib/bulk_data_test_kit/v4.0.0/submit/shared/submission_status.rb +14 -0
  48. data/lib/bulk_data_test_kit/v4.0.0/submit/shared/submitter_identifier.rb +14 -0
  49. data/lib/bulk_data_test_kit/version.rb +2 -2
  50. data/lib/bulk_data_test_kit.rb +2 -0
  51. metadata +41 -2
@@ -0,0 +1,50 @@
1
+ The Bulk Data Access v4.0.0-ballot Submit - Provider test suite validates a Data Provider
2
+ implementation of the [Bulk Submit Operation](https://hl7.org/fhir/uv/bulkdata/4.0.0-202609-ballot/en/)
3
+ within the Bulk Data Access v4.0.0-ballot IG.
4
+
5
+ ## Scope
6
+
7
+ These tests target the
8
+ [Bulk Data Access v4.0.0-ballot IG](https://hl7.org/fhir/uv/bulkdata/4.0.0-202609-ballot/en/).
9
+ The ballot specification is subject to change before final publication.
10
+ These tests are intended to allow implementers to perform
11
+ preliminary checks of their systems against the requirements stated for Bulk Submit actors
12
+ and [provide feedback](https://github.com/inferno-framework/bulk-data-test-kit/issues)
13
+ on the tests. Future versions of these tests may verify other
14
+ requirements and may change the test verification logic.
15
+
16
+ ## Running the Tests
17
+
18
+ ### Quick Start
19
+
20
+ Bulk data access requires the use of SMART Backend Services for authentication.
21
+ In order to interact with Inferno's simulated bulk server, the tester must provide
22
+ the JSON Web Key Set (JWKS) containing the asymmetric signing key in the
23
+ **SMART JSON Web Key Set (JWKS)** input as either a URL that resolves
24
+ to a JWKS or a raw JWKS in JSON format. Additionally, testers may provide
25
+ a **Client Id** if they want their client assigned a specific one.
26
+
27
+ Once the client has been registered, it will need to obtain an access token
28
+ following the SMART Backend Services flow and use it when making bulk data requests.
29
+ If the client is not able to obtain an access token, see the *Demonstration* section
30
+ below for how to use the SMART server tests to obtain an access token that the client can use.
31
+
32
+ ### Demonstration
33
+
34
+ You can run the Data Provider and Data Consumer suites against each other to
35
+ see how the tests work:
36
+
37
+ 1. Start a session in the Bulk Data Access v4.0.0-ballot Submit - Provider suite.
38
+ 1. Select the "Demo: Run Data Provider Against Inferno Data Consumer" preset.
39
+ 1. Click the "Run All Tests" button.
40
+ 1. In another browser tab, start a session in the Bulk Data Access v4.0.0-ballot
41
+ Submit - Consumer suite.
42
+ 1. Select the "Demo: Run Data Consumer Against Inferno Data Provider" preset.
43
+ 1. Click the "Run All Tests" button.
44
+ 1. When the Data Consumer suite pauses after sending `completed`, advance the
45
+ Data Provider suite from its initial wait.
46
+ 1. The Data Provider suite will retrieve the manifest and file synchronously,
47
+ then pause while waiting for the final status poll.
48
+ 1. Advance the Data Consumer suite. It will make one final status poll.
49
+ 1. After that poll completes, advance the Data Provider suite to finish the
50
+ paired workflow.
@@ -0,0 +1,90 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'smart_app_launch_test_kit'
4
+ require_relative '../helpers'
5
+
6
+ module BulkDataTestKit
7
+ module BulkDataV400
8
+ module Submit
9
+ module Provider
10
+ module Endpoints
11
+ class Poll < Inferno::DSL::SuiteEndpoint
12
+ include URLs
13
+ include Helpers
14
+
15
+ def test_run_identifier
16
+ return request.params[:session_path] if request.params[:session_path].present?
17
+
18
+ SMARTAppLaunch::MockSMARTServer.issued_token_to_client_id(
19
+ request.headers['authorization']&.delete_prefix('Bearer ')
20
+ )
21
+ end
22
+
23
+ def make_response
24
+ case submission_outcome[:status]
25
+ when 'succeeded'
26
+ make_completed_response
27
+ when 'failed'
28
+ make_error_response(500, submission_outcome[:diagnostic])
29
+ else
30
+ make_in_progress_response
31
+ end
32
+ end
33
+
34
+ def submission_id
35
+ submission_outcome[:submission_id]
36
+ end
37
+
38
+ def submission_outcome
39
+ @submission_outcome ||= submission_outcomes
40
+ .fetch(request.params[:submission_key].to_s, {})
41
+ .deep_symbolize_keys
42
+ end
43
+
44
+ def submission_outcomes
45
+ raw_outcomes = Inferno::Repositories::SessionData.new.load(
46
+ test_session_id: test_run.test_session_id,
47
+ name: :provider_submission_outcomes
48
+ )
49
+
50
+ JSON.parse(raw_outcomes.presence || '{}')
51
+ rescue JSON::ParserError
52
+ {}
53
+ end
54
+
55
+ def make_in_progress_response
56
+ response.status = 202
57
+ response.headers['Retry-After'] = '1'
58
+ end
59
+
60
+ def make_completed_response
61
+ response.status = 200
62
+ response.headers['Content-Type'] = 'application/json'
63
+ response.body = complete_manifest(submission_id).to_json
64
+ end
65
+
66
+ def make_error_response(status, diagnostic)
67
+ operation_outcome = FHIR::R4::OperationOutcome.new(
68
+ issue: [
69
+ FHIR::R4::OperationOutcome::Issue.new(
70
+ severity: 'error',
71
+ code: 'processing',
72
+ diagnostics: diagnostic
73
+ )
74
+ ]
75
+ )
76
+
77
+ response.status = status
78
+ response.headers['Content-Type'] = 'application/fhir+json'
79
+ response.body = operation_outcome.to_json
80
+ end
81
+
82
+ def tags
83
+ [POLL_TAG]
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'smart_app_launch_test_kit'
4
+ require_relative '../helpers'
5
+ require_relative '../../shared/submission_status'
6
+
7
+ module BulkDataTestKit
8
+ module BulkDataV400
9
+ module Submit
10
+ module Provider
11
+ module Endpoints
12
+ class Status < Inferno::DSL::SuiteEndpoint
13
+ include URLs
14
+ include Helpers
15
+
16
+ def test_run_identifier
17
+ return request.params[:session_path] if request.params[:session_path].present?
18
+
19
+ SMARTAppLaunch::MockSMARTServer.issued_token_to_client_id(
20
+ request.headers['authorization']&.delete_prefix('Bearer ')
21
+ )
22
+ end
23
+
24
+ def make_response
25
+ if identity.nil?
26
+ response.status = 400
27
+ response.headers['Content-Type'] = 'application/fhir+json'
28
+ response.body = FHIR::R4::OperationOutcome.new(
29
+ issue: [
30
+ FHIR::R4::OperationOutcome::Issue.new(
31
+ severity: 'error',
32
+ code: 'invalid',
33
+ diagnostics: 'The request did not identify a Bulk Submit submission.'
34
+ )
35
+ ]
36
+ ).to_json
37
+ else
38
+ response.status = 202
39
+ response.headers['Content-Location'] = poll_url(submission_key(identity))
40
+ end
41
+ end
42
+
43
+ def identity
44
+ @identity ||= submission_identity(request_parameters)
45
+ end
46
+
47
+ def request_parameters
48
+ body = request.body.read
49
+ request.body.rewind
50
+ FHIR.from_contents(body)
51
+ rescue JSON::ParserError, TypeError
52
+ nil
53
+ end
54
+
55
+ def tags
56
+ [STATUS_SUBMIT_TAG]
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../tags'
4
+
5
+ require 'smart_app_launch_test_kit'
6
+
7
+ module BulkDataTestKit
8
+ module BulkDataV400
9
+ module Submit
10
+ module Provider
11
+ module Endpoints
12
+ class Submit < Inferno::DSL::SuiteEndpoint
13
+ include URLs
14
+
15
+ def test_run_identifier
16
+ return request.params[:session_path] if request.params[:session_path].present?
17
+
18
+ SMARTAppLaunch::MockSMARTServer.issued_token_to_client_id(
19
+ request.headers['authorization']&.delete_prefix('Bearer ')
20
+ )
21
+ end
22
+
23
+ def make_response
24
+ response.status = 200
25
+ end
26
+
27
+ def tags
28
+ [SUBMIT_TAG]
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,222 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'digest'
4
+ require_relative '../shared/submission_status'
5
+
6
+ module BulkDataTestKit
7
+ module BulkDataV400
8
+ module Submit
9
+ module Provider
10
+ module Helpers
11
+ TERMINAL_SUBMISSION_STATUSES = %w[completed stopped].freeze
12
+
13
+ def submission_identity(parameters)
14
+ parameter_list = parameters.respond_to?(:parameter) ? parameters.parameter : []
15
+ submitter = parameter_list.find { |parameter| parameter.name == 'submitter' }&.valueIdentifier
16
+ submission_id = parameter_list.find do |parameter|
17
+ parameter.name == 'submissionId'
18
+ end&.valueString
19
+
20
+ return if submitter&.system.blank? || submitter&.value.blank? || submission_id.blank?
21
+
22
+ {
23
+ submitter_system: submitter.system,
24
+ submitter_value: submitter.value,
25
+ submission_id:
26
+ }
27
+ end
28
+
29
+ def submission_status(parameters)
30
+ parameter_list = parameters.respond_to?(:parameter) ? parameters.parameter : []
31
+ status_parameters = parameter_list.select do |parameter|
32
+ parameter.name == 'submissionStatus'
33
+ end
34
+ return unless status_parameters.one?
35
+
36
+ coding = status_parameters.first.valueCoding
37
+
38
+ return unless coding&.system == Shared::SubmissionStatus::SYSTEM
39
+
40
+ coding.code
41
+ end
42
+
43
+ def parsed_submission_parameters(requests)
44
+ requests.filter_map do |request|
45
+ parameters = FHIR.from_contents(request.request_body)
46
+ parameters if parameters.is_a?(FHIR::R4::Parameters)
47
+ rescue JSON::ParserError, TypeError
48
+ nil
49
+ end
50
+ end
51
+
52
+ def submission_timelines(parameters_list)
53
+ parameters_list.each_with_object({}) do |parameters, timelines|
54
+ identity = submission_identity(parameters)
55
+ next unless identity
56
+
57
+ (timelines[identity] ||= []) << parameters
58
+ end
59
+ end
60
+
61
+ def valid_completed_submission_timelines(parameters_list)
62
+ submission_timelines(parameters_list).select do |_identity, timeline|
63
+ terminal_indexes = timeline.each_index.select do |index|
64
+ TERMINAL_SUBMISSION_STATUSES.include?(submission_status(timeline[index]))
65
+ end
66
+
67
+ terminal_indexes == [timeline.length - 1] &&
68
+ submission_status(timeline.last) == 'completed'
69
+ end
70
+ end
71
+
72
+ def valid_completed_submission_parameters(requests)
73
+ valid_completed_submission_timelines(
74
+ parsed_submission_parameters(requests)
75
+ ).values.flatten
76
+ end
77
+
78
+ def validate_terminal_submission_order(requests)
79
+ assert requests.present?, 'No $bulk-submit requests were received.'
80
+
81
+ submission_timelines(parsed_submission_parameters(requests)).each_value do |timeline|
82
+ terminal_index = timeline.index do |parameters|
83
+ TERMINAL_SUBMISSION_STATUSES.include?(submission_status(parameters))
84
+ end
85
+
86
+ assert terminal_index.nil? || terminal_index == timeline.length - 1,
87
+ 'No `$bulk-submit` request may follow a terminal submission status ' \
88
+ 'for the same `submitter` and `submissionId`.'
89
+ end
90
+ end
91
+
92
+ def submission_key(identity)
93
+ Digest::SHA256.hexdigest(
94
+ [identity[:submitter_system], identity[:submitter_value], identity[:submission_id]].to_json
95
+ )
96
+ end
97
+
98
+ def complete_manifest(submission_id)
99
+ {
100
+ submissionId: submission_id,
101
+ transactionTime: DateTime.now,
102
+ requiresAccessToken: true
103
+ }
104
+ end
105
+
106
+ def validate_submitter_identifiers(requests, operation_name)
107
+ assert requests.present?, "No #{operation_name} requests were received."
108
+
109
+ requests.each do |request|
110
+ parameters = FHIR.from_contents(request.request_body)
111
+ submitter_parameters = parameters.parameter.select do |parameter|
112
+ parameter.name == 'submitter'
113
+ end
114
+
115
+ assert submitter_parameters.one?,
116
+ "Every #{operation_name} request must include exactly one `submitter` parameter."
117
+
118
+ submitter_identifier = submitter_parameters.first.valueIdentifier
119
+ assert submitter_identifier.present?,
120
+ 'The `submitter` parameter must have type `Identifier`.'
121
+ assert submitter_identifier.system.present?,
122
+ 'The `submitter` Identifier must include a system.'
123
+ assert submitter_identifier.value.present?,
124
+ 'The `submitter` Identifier must include a value.'
125
+
126
+ [submitter_identifier.system, submitter_identifier.value]
127
+ end
128
+ end
129
+
130
+ def validate_status_submission_identities(status_requests, submit_requests)
131
+ validate_submitter_identifiers(status_requests, '$bulk-submit-status')
132
+ validate_submission_ids(status_requests, '$bulk-submit-status')
133
+ validate_submitter_identifiers(submit_requests, '$bulk-submit')
134
+ validate_submission_ids(submit_requests, '$bulk-submit')
135
+
136
+ submitted_identities = submit_requests.map do |request|
137
+ submission_identity(FHIR.from_contents(request.request_body))
138
+ end
139
+ status_identities = status_requests.map do |request|
140
+ submission_identity(FHIR.from_contents(request.request_body))
141
+ end
142
+
143
+ assert status_identities.all? { |identity| submitted_identities.include?(identity) },
144
+ 'Every `$bulk-submit-status` request must match the `submitter` and ' \
145
+ '`submissionId` of a `$bulk-submit` request.'
146
+ end
147
+
148
+ def validate_submission_ids(requests, operation_name)
149
+ assert requests.present?, "No #{operation_name} requests were received."
150
+
151
+ requests.each do |request|
152
+ parameters = FHIR.from_contents(request.request_body)
153
+ submission_id_parameters = parameters.parameter.select do |parameter|
154
+ parameter.name == 'submissionId'
155
+ end
156
+
157
+ assert submission_id_parameters.one?,
158
+ "Every #{operation_name} request must include exactly one `submissionId` parameter."
159
+ assert submission_id_parameters.first.valueString.present?,
160
+ 'The `submissionId` parameter must have type `string` and contain a value.'
161
+ end
162
+ end
163
+
164
+ def submission_status_parameters(request)
165
+ FHIR.from_contents(request.request_body).parameter.select do |parameter|
166
+ parameter.name == 'submissionStatus'
167
+ end
168
+ end
169
+
170
+ def request_has_submission_status?(request, code)
171
+ status_parameters = submission_status_parameters(request)
172
+ return code == 'in-progress' if status_parameters.empty?
173
+ return false unless status_parameters.one?
174
+
175
+ coding = status_parameters.first.valueCoding
176
+ coding.present? &&
177
+ coding.system == Shared::SubmissionStatus::SYSTEM &&
178
+ coding.code == code
179
+ end
180
+
181
+ def validate_submission_statuses(requests)
182
+ assert requests.present?, 'No $bulk-submit requests were received.'
183
+
184
+ requests.each do |request|
185
+ status_parameters = submission_status_parameters(request)
186
+
187
+ assert status_parameters.length <= 1,
188
+ 'Every $bulk-submit request must include at most one `submissionStatus` parameter.'
189
+ next if status_parameters.empty?
190
+
191
+ coding = status_parameters.first.valueCoding
192
+ assert coding.present?, '`submissionStatus` must have type `Coding`.'
193
+ assert Shared::SubmissionStatus::CODES.include?(coding.code),
194
+ "#{coding.code} is not a valid submissionStatus code"
195
+ assert coding.system == Shared::SubmissionStatus::SYSTEM,
196
+ "#{coding.system} is not the submissionStatus code system"
197
+ end
198
+ end
199
+
200
+ def validate_optional_part_parameter(requests, parameter_name)
201
+ parameters = requests.flat_map do |request|
202
+ FHIR.from_contents(request.request_body).parameter
203
+ end.select do |parameter|
204
+ parameter.name == parameter_name
205
+ end
206
+
207
+ skip_if parameters.empty?,
208
+ "No submission included the optional `#{parameter_name}` parameter"
209
+
210
+ all_parts = parameters.all? do |parameter|
211
+ serialized = parameter.to_hash
212
+ parameter.part.present? &&
213
+ serialized.keys.none? { |key| key.start_with?('value') || key == 'resource' }
214
+ end
215
+
216
+ assert all_parts, "`#{parameter_name}` must have type `part`"
217
+ end
218
+ end
219
+ end
220
+ end
221
+ end
222
+ end
@@ -0,0 +1,165 @@
1
+ require_relative 'tags'
2
+
3
+ module BulkDataTestKit
4
+ module BulkDataV400
5
+ module Submit
6
+ module Provider
7
+ class ManifestAndFileRetrieval < Inferno::TestGroup
8
+ include Helpers
9
+
10
+ title 'Manifest and File Retrieval'
11
+ id :bulk_data_v400_submit_provider_manifest_file_retrieval
12
+
13
+ input :provider_submission_outcomes, type: :textarea, optional: true
14
+
15
+ test do
16
+ id :bulk_data_v400_submit_provider_completed_submissions_processed
17
+ title 'Completed submissions were processed successfully'
18
+ description <<~DESCRIPTION
19
+ This test verifies that Inferno successfully retrieved every
20
+ completed submission.
21
+ DESCRIPTION
22
+
23
+ run do
24
+ outcomes = JSON.parse(provider_submission_outcomes.presence || '{}')
25
+ skip_if outcomes.empty?, 'No completed submissions were processed'
26
+
27
+ failures = outcomes.values.select { |outcome| outcome['status'] == 'failed' }
28
+ assert failures.empty?, 'Inferno was unable to retrieve one or more completed submissions.'
29
+ end
30
+ end
31
+
32
+ test do
33
+ id :bulk_data_v400_submit_provider_oauth_metadata_retrieved
34
+ title 'OAuth metadata retrieved'
35
+ description <<~DESCRIPTION
36
+ This test verifies that the provider's OAuth metadata could be
37
+ retrieved.
38
+ DESCRIPTION
39
+
40
+ run do
41
+ submit_requests = load_tagged_requests Provider::SUBMIT_TAG
42
+ completed_submissions = valid_completed_submission_parameters(submit_requests)
43
+ oauth_metadata_submitted = completed_submissions.any? do |parameters|
44
+ manifest_submitted = parameters.parameter.any? do |parameter|
45
+ parameter.name == 'manifestUrl' && parameter.valueUrl.present?
46
+ end
47
+ oauth_metadata_url_submitted = parameters.parameter.any? do |parameter|
48
+ parameter.name == 'oauthMetadataUrl' && parameter.valueUrl.present?
49
+ end
50
+
51
+ manifest_submitted && oauth_metadata_url_submitted
52
+ end
53
+
54
+ skip_if !oauth_metadata_submitted, 'No OAuth metadata URL was submitted'
55
+
56
+ oauth_metadata_requests = load_tagged_requests Provider::OAUTH_METADATA_TAG
57
+
58
+ assert oauth_metadata_requests.present?, 'No OAuth Metadata request made'
59
+
60
+ failed_requests = oauth_metadata_requests.reject { |request| request.status.to_i == 200 }
61
+ assert failed_requests.empty?,
62
+ "OAuth metadata retrieval failed: #{failed_requests.map { |request| "#{request.url} returned #{request.status}" }.join(', ')}"
63
+ end
64
+ end
65
+
66
+ test do
67
+ id :bulk_data_v400_submit_provider_token_request_success
68
+ title 'Successful token request made'
69
+ description <<~DESCRIPTION
70
+ This test verifies that all files included in the manifest
71
+ were retrieved.
72
+ DESCRIPTION
73
+
74
+ run do
75
+ submit_requests = load_tagged_requests Provider::SUBMIT_TAG
76
+ completed_submissions = valid_completed_submission_parameters(submit_requests)
77
+ oauth_metadata_submitted = completed_submissions.any? do |parameters|
78
+ manifest_submitted = parameters.parameter.any? do |parameter|
79
+ parameter.name == 'manifestUrl' && parameter.valueUrl.present?
80
+ end
81
+ oauth_metadata_url_submitted = parameters.parameter.any? do |parameter|
82
+ parameter.name == 'oauthMetadataUrl' && parameter.valueUrl.present?
83
+ end
84
+
85
+ manifest_submitted && oauth_metadata_url_submitted
86
+ end
87
+
88
+ skip_if !oauth_metadata_submitted, 'No OAuth metadata URL was submitted'
89
+
90
+ token_requests = load_tagged_requests Provider::TOKEN_TAG
91
+
92
+ assert token_requests.present?, 'No token request made'
93
+
94
+ failed_requests = token_requests.reject { |request| request.status.to_i == 200 }
95
+ assert failed_requests.empty?,
96
+ "Token request failed: #{failed_requests.map { |request| "#{request.url} returned #{request.status}" }.join(', ')}"
97
+ end
98
+ end
99
+
100
+ test do
101
+ id :bulk_data_v400_submit_provider_manifests_retrieved
102
+ title 'Manifests for each submission were retrieved'
103
+ description <<~DESCRIPTION
104
+ This test verifies that all manifests included in the submissions
105
+ were retrieved.
106
+ DESCRIPTION
107
+
108
+ run do
109
+ submit_requests = load_tagged_requests Provider::SUBMIT_TAG
110
+ completed_submissions = valid_completed_submission_parameters(submit_requests)
111
+ expected_manifest_count = completed_submissions.sum do |parameters|
112
+ parameters.parameter.count do |parameter|
113
+ parameter.name == 'manifestUrl' && parameter.valueUrl.present?
114
+ end
115
+ end
116
+
117
+ skip_if expected_manifest_count.zero?, 'No manifest URLs were submitted'
118
+
119
+ manifest_requests = load_tagged_requests Provider::MANIFEST_TAG
120
+
121
+ assert manifest_requests.count == expected_manifest_count,
122
+ "Expected #{expected_manifest_count} manifest retrievals, " \
123
+ "but received #{manifest_requests.count}"
124
+
125
+ failed_requests = manifest_requests.reject { |request| request.status.to_i == 200 }
126
+ assert failed_requests.empty?,
127
+ "Manifest retrieval failed: #{failed_requests.map { |request| "#{request.url} returned #{request.status}" }.join(', ')}"
128
+ end
129
+ end
130
+
131
+ test do
132
+ id :bulk_data_v400_submit_provider_files_retrieved
133
+ title 'Files retrieved'
134
+ description <<~DESCRIPTION
135
+ This test verifies that all files included in the manifest
136
+ were retrieved.
137
+ DESCRIPTION
138
+
139
+ run do
140
+ manifest_requests = load_tagged_requests Provider::MANIFEST_TAG
141
+
142
+ expected_file_count = manifest_requests
143
+ .select { |request| request.status.to_i == 200 }
144
+ .sum do |request|
145
+ manifest = JSON.parse(request.response_body)
146
+ Array(manifest['output']).count
147
+ end
148
+
149
+ skip_if expected_file_count.zero?, 'No files were listed in the retrieved manifests'
150
+
151
+ file_requests = load_tagged_requests Provider::FILE_DOWNLOAD_TAG
152
+
153
+ assert file_requests.count == expected_file_count,
154
+ "Expected #{expected_file_count} file downloads, but received #{file_requests.count}"
155
+
156
+ failed_requests = file_requests.reject { |request| request.status.to_i == 200 }
157
+ assert failed_requests.empty?,
158
+ "File retrieval failed: #{failed_requests.map { |request| "#{request.url} returned #{request.status}" }.join(', ')}"
159
+ end
160
+ end
161
+ end
162
+ end
163
+ end
164
+ end
165
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BulkDataTestKit
4
+ module BulkDataV400
5
+ module Submit
6
+ module Provider
7
+ class PollGroup < Inferno::TestGroup
8
+ title 'Bulk Data Status Polling Request'
9
+
10
+ description %(
11
+ This group verifies that a provider successfully polled for status.
12
+ )
13
+
14
+ id :bulk_data_v400_submit_provider_poll
15
+
16
+ run_as_group
17
+
18
+ optional
19
+
20
+ test do
21
+ id :bulk_data_v400_submit_provider_poll_request
22
+ title 'Poll Request Was Made'
23
+
24
+ description %(
25
+ This test verifies that at least one poll request was made.
26
+ )
27
+
28
+ run do
29
+ assert load_tagged_requests(POLL_TAG).any?, 'Did not receive a poll request.'
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end