cancer_pathology_data_sharing_test_kit 0.9.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 (48) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +201 -0
  3. data/config/presets/demo_report.json +14 -0
  4. data/config/presets/inferno_reference_server_preset.json.erb +39 -0
  5. data/lib/cancer_pathology_data_sharing_test_kit/bundle_parse.rb +53 -0
  6. data/lib/cancer_pathology_data_sharing_test_kit/docs/data_access_suite_description.md +83 -0
  7. data/lib/cancer_pathology_data_sharing_test_kit/docs/report_generation_suite_description.md +118 -0
  8. data/lib/cancer_pathology_data_sharing_test_kit/fhir_resource_navigation.rb +174 -0
  9. data/lib/cancer_pathology_data_sharing_test_kit/group_metadata.rb +82 -0
  10. data/lib/cancer_pathology_data_sharing_test_kit/igs/.keep +0 -0
  11. data/lib/cancer_pathology_data_sharing_test_kit/igs/README.md +21 -0
  12. data/lib/cancer_pathology_data_sharing_test_kit/metadata.rb +75 -0
  13. data/lib/cancer_pathology_data_sharing_test_kit/must_support_test.rb +239 -0
  14. data/lib/cancer_pathology_data_sharing_test_kit/primitive_type.rb +5 -0
  15. data/lib/cancer_pathology_data_sharing_test_kit/report_generation_suite/data_absent/data_absent_reason_code_system.rb +22 -0
  16. data/lib/cancer_pathology_data_sharing_test_kit/report_generation_suite/data_absent/data_absent_reason_extension.rb +20 -0
  17. data/lib/cancer_pathology_data_sharing_test_kit/report_generation_suite/data_absent/sending_hide_missing.rb +28 -0
  18. data/lib/cancer_pathology_data_sharing_test_kit/report_generation_suite/data_absent_group.rb +27 -0
  19. data/lib/cancer_pathology_data_sharing_test_kit/report_generation_suite/exchange_bundle/encounter/encounter_must_support_test.rb +53 -0
  20. data/lib/cancer_pathology_data_sharing_test_kit/report_generation_suite/exchange_bundle/encounter/encounter_validation_test.rb +48 -0
  21. data/lib/cancer_pathology_data_sharing_test_kit/report_generation_suite/exchange_bundle/encounter/metadata.yml +281 -0
  22. data/lib/cancer_pathology_data_sharing_test_kit/report_generation_suite/exchange_bundle/patient/metadata.yml +305 -0
  23. data/lib/cancer_pathology_data_sharing_test_kit/report_generation_suite/exchange_bundle/patient/patient_must_support_test.rb +58 -0
  24. data/lib/cancer_pathology_data_sharing_test_kit/report_generation_suite/exchange_bundle/patient/patient_validation_test.rb +47 -0
  25. data/lib/cancer_pathology_data_sharing_test_kit/report_generation_suite/exchange_bundle/practitioner_role/metadata.yml +44 -0
  26. data/lib/cancer_pathology_data_sharing_test_kit/report_generation_suite/exchange_bundle/practitioner_role/practitioner_role_must_support_test.rb +45 -0
  27. data/lib/cancer_pathology_data_sharing_test_kit/report_generation_suite/exchange_bundle/practitioner_role/practitioner_role_validation_test.rb +47 -0
  28. data/lib/cancer_pathology_data_sharing_test_kit/report_generation_suite/exchange_bundle/service_request/metadata.yml +75 -0
  29. data/lib/cancer_pathology_data_sharing_test_kit/report_generation_suite/exchange_bundle/service_request/service_request_must_support_test.rb +52 -0
  30. data/lib/cancer_pathology_data_sharing_test_kit/report_generation_suite/exchange_bundle/service_request/service_request_validation_test.rb +47 -0
  31. data/lib/cancer_pathology_data_sharing_test_kit/report_generation_suite/exchange_bundle/specimen/metadata.yml +51 -0
  32. data/lib/cancer_pathology_data_sharing_test_kit/report_generation_suite/exchange_bundle/specimen/specimen_must_support_test.rb +53 -0
  33. data/lib/cancer_pathology_data_sharing_test_kit/report_generation_suite/exchange_bundle/specimen/specimen_validation_test.rb +47 -0
  34. data/lib/cancer_pathology_data_sharing_test_kit/report_generation_suite/exchange_bundle/us_pathology_diagnostic_report/diagnostic_report_validation_test.rb +47 -0
  35. data/lib/cancer_pathology_data_sharing_test_kit/report_generation_suite/exchange_bundle/us_pathology_diagnostic_report/metadata.yml +77 -0
  36. data/lib/cancer_pathology_data_sharing_test_kit/report_generation_suite/exchange_bundle/us_pathology_diagnostic_report/us_pathology_diagnostic_report_must_support_test.rb +51 -0
  37. data/lib/cancer_pathology_data_sharing_test_kit/report_generation_suite/exchange_bundle/us_pathology_exchange_bundle/exchange_bundle_validation_test.rb +56 -0
  38. data/lib/cancer_pathology_data_sharing_test_kit/report_generation_suite/exchange_bundle/us_pathology_exchange_bundle/metadata.yml +78 -0
  39. data/lib/cancer_pathology_data_sharing_test_kit/report_generation_suite/exchange_bundle/us_pathology_exchange_bundle/us_pathology_exchange_bundle_must_support_test.rb +41 -0
  40. data/lib/cancer_pathology_data_sharing_test_kit/report_generation_suite/exchange_bundle_group.rb +104 -0
  41. data/lib/cancer_pathology_data_sharing_test_kit/report_generation_suite.rb +73 -0
  42. data/lib/cancer_pathology_data_sharing_test_kit/urls.rb +25 -0
  43. data/lib/cancer_pathology_data_sharing_test_kit/us_core_data_access_suite/us_core_group.rb +72 -0
  44. data/lib/cancer_pathology_data_sharing_test_kit/us_core_data_access_suite.rb +58 -0
  45. data/lib/cancer_pathology_data_sharing_test_kit/validation_test.rb +96 -0
  46. data/lib/cancer_pathology_data_sharing_test_kit/version.rb +4 -0
  47. data/lib/cancer_pathology_data_sharing_test_kit.rb +3 -0
  48. metadata +124 -0
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../../bundle_parse'
4
+ require_relative '../../../validation_test'
5
+
6
+ module CancerPathologyDataSharingTestKit
7
+ class ContentBundleValidationTest < Inferno::Test
8
+ include CancerPathologyDataSharingTestKit::ValidationTest
9
+ include CancerPathologyDataSharingTestKit::BundleParse
10
+ title 'Provided Bundle resources conform to the US Pathology Exchange Bundle Profile'
11
+ id :cpds_exchange_bundle_validation_test
12
+
13
+ description %(
14
+ Verify that all the generated report(s) are a conformant
15
+ [Pathology Exchange Bundle](http://hl7.org/fhir/us/cancer-reporting/StructureDefinition/us-pathology-exchange-bundle.html).
16
+ )
17
+
18
+ def resource_type
19
+ 'Bundle'
20
+ end
21
+
22
+ def add_to_scratch(bundles_array)
23
+ scratch[:cpds_resources] ||= {}
24
+ scratch[:cpds_exchange_bundles] ||= []
25
+ bundles_array.each do |report|
26
+ bundle = FHIR.from_contents(report.to_json)
27
+ assert_resource_type('Bundle', resource: bundle)
28
+ bundle_id = bundle.id
29
+ scratch[:cpds_exchange_bundles] << bundle
30
+ scratch[:cpds_resources][bundle_id] = parse_bundle(bundle)
31
+ end
32
+ end
33
+
34
+ run do
35
+ # Put reports into array to process
36
+ bundles_array = JSON.parse("[#{reports}]")
37
+ add_to_scratch(bundles_array)
38
+
39
+ invalid_bundles = []
40
+ profile_with_version = 'http://hl7.org/fhir/us/cancer-reporting/StructureDefinition/us-pathology-exchange-bundle|1.0.1'
41
+ bundles_array.each do |bundle|
42
+ fhir_bundle = FHIR.from_contents(bundle.to_json)
43
+ invalid_bundles << fhir_bundle.id unless resource_is_valid?(resource: fhir_bundle, profile_url: profile_with_version)
44
+ end
45
+
46
+ # filter errors related to entry resources and Bundle slices - these are handled elsewhere
47
+ messages.reject! { |message| /Bundle\.entry\[\d+\]\.resource/.match?(message[:message]) }
48
+ messages.reject! do |message|
49
+ /Bundle: Slice 'Bundle\.entry:[a-zA-Z]+': a matching slice is required/.match?(message[:message])
50
+ end
51
+
52
+ errors_found = messages.any? { |message| message[:type] == 'error' }
53
+ assert !errors_found, "Bundle(s) #{invalid_bundles.join(', ')} do not conform to the profile #{profile_with_version}"
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,78 @@
1
+ ---
2
+ :name: us_pathology_exchange_bundle
3
+ :class_name: UsPathologyExchangeBundleSequence
4
+ :version: v1.0.1
5
+ :reformatted_version: v101
6
+ :resource: Bundle
7
+ :profile_url: http://hl7.org/fhir/us/cancer-reporting/StructureDefinition/us-pathology-exchange-bundle
8
+ :profile_name: US Pathology Exchange Bundle
9
+ :profile_version: 1.0.1
10
+ :title: Bundle US Pathology Exchange
11
+ :short_description: Verify support for the server capabilities required by the US
12
+ Pathology Exchange Bundle.
13
+ :must_supports:
14
+ :extensions: []
15
+ :slices:
16
+ - :slice_id: Bundle.entry:us_core_patient
17
+ :slice_name: us_core_patient
18
+ :path: entry
19
+ :discriminator:
20
+ :type: type
21
+ :code: Patient
22
+ - :slice_id: Bundle.entry:us_core_patient
23
+ :slice_name: us_core_patient
24
+ :path: entry
25
+ :discriminator:
26
+ :type: type
27
+ :code: Patient
28
+ - :slice_id: Bundle.entry:us_core_encounter
29
+ :slice_name: us_core_encounter
30
+ :path: entry
31
+ :discriminator:
32
+ :type: type
33
+ :code: Encounter
34
+ - :slice_id: Bundle.entry:diagnosticreport
35
+ :slice_name: diagnosticreport
36
+ :path: entry
37
+ :discriminator:
38
+ :type: type
39
+ :code: DiagnosticReport
40
+ - :slice_id: Bundle.entry:specimen
41
+ :slice_name: specimen
42
+ :path: entry
43
+ :discriminator:
44
+ :type: type
45
+ :code: Specimen
46
+ - :slice_id: Bundle.entry:service-request
47
+ :slice_name: service-request
48
+ :path: entry
49
+ :discriminator:
50
+ :type: type
51
+ :code: ServiceRequest
52
+ - :slice_id: Bundle.entry:pathology-related-practitioner
53
+ :slice_name: pathology-related-practitioner
54
+ :path: entry
55
+ :discriminator:
56
+ :type: type
57
+ :code: Practitioner
58
+ :elements:
59
+ - :path: type
60
+ :fixed_value: transaction
61
+ - :path: timestamp
62
+ - :path: entry
63
+ - :path: entry:us_core_patient.resource
64
+ - :path: entry:us_core_encounter.resource
65
+ - :path: entry:service-request.resource
66
+ - :path: entry:pathology-related-practitioner.resource
67
+ :mandatory_elements:
68
+ - Bundle.type
69
+ - Bundle.link.relation
70
+ - Bundle.link.url
71
+ - Bundle.entry
72
+ - Bundle.entry.request.method
73
+ - Bundle.entry.request.url
74
+ - Bundle.entry.response.status
75
+ - Bundle.entry.resource
76
+ :tests: []
77
+ :id: v101_us_pathology_exchange_bundle
78
+ :file_name: us_pathology_exchange_bundle_group.rb
@@ -0,0 +1,41 @@
1
+ require_relative '../../../must_support_test'
2
+ require_relative '../../../group_metadata'
3
+
4
+ module CancerPathologyDataSharingTestKit
5
+ module V101
6
+ class UsPathologyExchangeBundleMustSupportTest < Inferno::Test
7
+ include CancerPathologyDataSharingTestKit::MustSupportTest
8
+
9
+ title 'All must support elements are provided in the US Pathology Exchange Bundle resources'
10
+ description %(
11
+ Report generators SHALL be capable of populating all must support data elements
12
+ defined in the [US Pathology ExchangeBundle profile](https://hl7.org/fhir/us/cancer-reporting/STU1.0.1/StructureDefinition-us-pathology-exchange-bundle.html)
13
+ and profiles it references. This test will look through the Bundle resources
14
+ found in the provided report Bundles for the following must support elements:
15
+
16
+ * Bundle.entry
17
+ * Bundle.entry:pathology-related-practitioner.resource
18
+ * Bundle.entry:service-request.resource
19
+ * Bundle.entry:us_core_encounter.resource
20
+ * Bundle.entry:us_core_patient.resource
21
+ * Bundle.timestamp
22
+ * Bundle.type
23
+ )
24
+
25
+ id :cpds_v101_us_pathology_exchange_bundle_must_support_test
26
+
27
+ def resource_type
28
+ 'Bundle'
29
+ end
30
+
31
+ def self.metadata
32
+ @metadata ||= GroupMetadata.new(YAML.load_file(File.join(__dir__, 'metadata.yml'), aliases: true))
33
+ end
34
+
35
+ run do
36
+ all_resources = scratch[:cpds_exchange_bundles]
37
+ perform_must_support_test(all_resources&.flatten)
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,104 @@
1
+ require_relative 'exchange_bundle/us_pathology_exchange_bundle/exchange_bundle_validation_test'
2
+ require_relative 'exchange_bundle/us_pathology_diagnostic_report/diagnostic_report_validation_test'
3
+ require_relative 'exchange_bundle/encounter/encounter_validation_test'
4
+ require_relative 'exchange_bundle/practitioner_role/practitioner_role_validation_test'
5
+ require_relative 'exchange_bundle/patient/patient_validation_test'
6
+ require_relative 'exchange_bundle/service_request/service_request_validation_test'
7
+ require_relative 'exchange_bundle/specimen/specimen_validation_test'
8
+
9
+ require_relative 'exchange_bundle/us_pathology_exchange_bundle/us_pathology_exchange_bundle_must_support_test'
10
+ require_relative 'exchange_bundle/patient/patient_must_support_test'
11
+ require_relative 'exchange_bundle/encounter/encounter_must_support_test'
12
+ require_relative 'exchange_bundle/us_pathology_diagnostic_report/us_pathology_diagnostic_report_must_support_test'
13
+ require_relative 'exchange_bundle/specimen/specimen_must_support_test'
14
+ require_relative 'exchange_bundle/service_request/service_request_must_support_test'
15
+ require_relative 'exchange_bundle/practitioner_role/practitioner_role_must_support_test'
16
+
17
+ require_relative 'data_absent_group'
18
+
19
+ module CancerPathologyDataSharingTestKit
20
+ class ExchangeBundleGroup < Inferno::TestGroup
21
+ title 'Exchange Bundle Group'
22
+ id :cpds_exchange_bundle_group
23
+ description %(
24
+ # Background
25
+
26
+ Theses tests verify that the system under test can generate conformant and complete cancer pathology reports
27
+ following the requirements in the [Cancer Pathology Exchange Bundle profile](https://hl7.org/fhir/us/cancer-reporting/STU1.0.1/StructureDefinition-us-pathology-exchange-bundle.html).
28
+ The tester will provide a list of Bundles generated by the system under test in the **Cancer Reports** input.
29
+ Inferno will verify the conformance of each report and that the set of reports contains examples
30
+ of all entry slices and each element that the Bundle profile and referenced profiles indicate must be supported.
31
+
32
+ # Testing Methodology
33
+ The provided Bundles are parsed and the individual resources within them are separated out
34
+ and assigned a target profile based on their resource type.
35
+
36
+ ## Profile Validation
37
+ Each resource extracted from the bundles is expected to conform to
38
+ its target profile. Each element in each resource is checked against
39
+ teminology binding and cardinality requirements by the HL7 FHIR validator.
40
+
41
+ While the [Cancer Pathology Exchange Bundle profile](https://hl7.org/fhir/us/cancer-reporting/STU1.0.1/StructureDefinition-us-pathology-exchange-bundle.html)
42
+ slices entries by profile, Inferno checks that any instances of the profiled resource type
43
+ conform to the indicated profile and there are not more instances of the profiled resource type than allowed
44
+ by the slice cardinality. Specifically, each report must contain:
45
+ - Exactly one Patient, which must conform to the [US Core Patient profile](http://hl7.org/fhir/us/core/STU5.0.1/StructureDefinition-us-core-patient.html).
46
+ - At most one Encounter, which, if present, must conform to the [US Core Encounter profile](http://hl7.org/fhir/us/core/STU5.0.1/StructureDefinition-us-core-encounter.html).
47
+ - Exactly one DiagnosticReport, which must conform to the [US Pathology Diagnostic Report profile](https://hl7.org/fhir/us/cancer-reporting/STU1.0.1/StructureDefinition-us-pathology-diagnostic-report.html).
48
+ - One or more Specimens, each of which must conform to the [US Pathology Specimen profile](https://hl7.org/fhir/us/cancer-reporting/STU1.0.1/StructureDefinition-us-pathology-specimen.html).
49
+
50
+ Additionally, each report may include:
51
+ - Any number of ServiceRequests, each of which must conform to the [US Pathology Service Requests profile](https://hl7.org/fhir/us/cancer-reporting/STU1.0.1/StructureDefinition-us-pathology-service-request.html).
52
+ - Any number of PractitionRoles, each of which must conform to the [US Pathology Related Practitioner Roles profile](https://hl7.org/fhir/us/cancer-reporting/STU1.0.1/StructureDefinition-us-pathology-related-practitioner-role.html).
53
+
54
+ ## Must Support
55
+ Must Support tests in this group check that each element of the target profile marked
56
+ as "must support" is observed at least once across instances assigned to that profile.
57
+ If one or more elements are not observed, the test will fail.
58
+ )
59
+ run_as_group
60
+
61
+ group do
62
+ title 'Report'
63
+ test from: :cpds_exchange_bundle_validation_test
64
+ test from: :cpds_v101_us_pathology_exchange_bundle_must_support_test
65
+ end
66
+
67
+ group do
68
+ title 'Patient'
69
+ test from: :cpds_patient_validation_test
70
+ test from: :cpds_v101_us_core_v311_patient_must_support_test
71
+ end
72
+
73
+ group do
74
+ title 'Encounter'
75
+ test from: :cpds_encounter_validation_test
76
+ test from: :cpds_v101_us_core_v311_encounter_must_support_test
77
+ end
78
+
79
+ group do
80
+ title 'Diagnostic Report'
81
+ test from: :cpds_diagnostic_report_validation_test
82
+ test from: :cpds_v101_us_pathology_diagnostic_report_must_support_test
83
+ end
84
+
85
+ group do
86
+ title 'Specimen'
87
+ test from: :cpds_specimen_validation_test
88
+ test from: :cpds_v101_specimen_must_support_test
89
+ end
90
+
91
+ group do
92
+ title 'Service Request'
93
+ test from: :cpds_service_request_validation_test
94
+ test from: :cpds_v101_service_request_must_support_test
95
+ end
96
+
97
+ group do
98
+ title 'Practitioner Role'
99
+ test from: :cpds_practitioner_role_validation_test
100
+ test from: :cpds_v101_practitioner_role_must_support_test
101
+ end
102
+ group from: :cpds_data_absent_reason
103
+ end
104
+ end
@@ -0,0 +1,73 @@
1
+ require 'inferno/dsl/oauth_credentials'
2
+ require_relative 'urls'
3
+ require_relative 'version'
4
+ require_relative 'report_generation_suite/exchange_bundle_group'
5
+
6
+ module CancerPathologyDataSharingTestKit
7
+ class ReportGenerationSuite < Inferno::TestSuite
8
+ include URLs
9
+ id :cpds_report_generation
10
+ title 'Cancer Pathology Data Sharing Report Generation Test Suite'
11
+ short_title 'CPDS Report Generation Test Suite'
12
+ description File.read(File.join(__dir__, 'docs', 'report_generation_suite_description.md'))
13
+ links [
14
+ {
15
+ label: 'Report Issue',
16
+ url: 'https://github.com/inferno-framework/cancer-pathology-data-sharing-test-kit/issues/'
17
+ },
18
+ {
19
+ label: 'Open Source',
20
+ url: 'https://github.com/inferno-framework/cancer-pathology-data-sharing-test-kit/'
21
+ },
22
+ {
23
+ label: 'Download',
24
+ url: 'https://github.com/inferno-framework/cancer-pathology-data-sharing-test-kit/releases'
25
+ },
26
+ {
27
+ label: 'Implementation Guide',
28
+ url: 'https://hl7.org/fhir/us/cancer-reporting/STU1.0.1/'
29
+ }
30
+ ]
31
+
32
+ GENERAL_MESSAGE_FILTERS = [
33
+ %r{Sub-extension url 'introspect' is not defined by the Extension http://fhir-registry\.smarthealthit\.org/StructureDefinition/oauth-uris},
34
+ %r{Sub-extension url 'revoke' is not defined by the Extension http://fhir-registry\.smarthealthit\.org/StructureDefinition/oauth-uris},
35
+ /Observation\.effective\.ofType\(Period\): .*vs-1:/, # Invalid invariant in FHIR v4.0.1
36
+ /Observation\.effective\.ofType\(Period\): .*us-core-1:/, # Invalid invariant in US Core v3.1.1
37
+ /Provenance.agent\[\d*\]: Constraint failed: provenance-1/, # Invalid invariant in US Core v5.0.1
38
+ %r{Unknown Code System 'http://hl7.org/fhir/us/core/CodeSystem/us-core-tags'}, # Validator has an issue with this US Core 5 code system in US Core 6 resource # rubocop:disable Layout/LineLength
39
+ %r{URL value 'http://hl7.org/fhir/us/core/CodeSystem/us-core-tags' does not resolve}, # Validator has an issue with this US Core 5 code system in US Core 6 resource # rubocop:disable Layout/LineLength
40
+ /\A\S+: \S+: URL value '.*' does not resolve/,
41
+ %r{Observation.component\[\d+\].value.ofType\(Quantity\): The code provided \(http://unitsofmeasure.org#L/min\) was not found in the value set 'Vital Signs Units'} # Known issue with the Pulse Oximetry Profile # rubocop:disable Layout/LineLength
42
+ ].freeze
43
+ VALIDATION_MESSAGE_FILTERS = GENERAL_MESSAGE_FILTERS
44
+
45
+ fhir_resource_validator do
46
+ igs 'hl7.fhir.us.cancer-reporting#1.0.1', 'hl7.fhir.us.core#5.0.1'
47
+ message_filters = VALIDATION_MESSAGE_FILTERS
48
+
49
+ exclude_message do |message|
50
+ message_filters.any? { |filter| filter.match? message.message }
51
+ end
52
+
53
+ perform_additional_validation do |resource, _profile_url|
54
+ ProvenanceValidator.validate(resource) if resource.instance_of?(FHIR::Provenance)
55
+ end
56
+ end
57
+
58
+ resume_test_route :get, RESUME_PASS_PATH do |request|
59
+ request.query_parameters['token']
60
+ end
61
+ resume_test_route :get, RESUME_FAIL_PATH, result: 'fail' do |request|
62
+ request.query_parameters['token']
63
+ end
64
+
65
+ input :reports,
66
+ title: 'Cancer Reports',
67
+ description: 'A comma-Separated list of one or more FHIR Pathology Cancer Exchange Bundles ' \
68
+ 'in JSON format generated by the system under test.',
69
+ type: 'textarea'
70
+
71
+ group from: :cpds_exchange_bundle_group
72
+ end
73
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CancerPathologyDataSharingTestKit
4
+ RESUME_PASS_PATH = '/resume_pass'
5
+ RESUME_FAIL_PATH = '/resume_fail'
6
+
7
+ # URLs
8
+ module URLs
9
+ def base_url
10
+ @base_url ||= "#{Inferno::Application['base_url']}/custom/#{suite_id}"
11
+ end
12
+
13
+ def resume_pass_url
14
+ @resume_pass_url ||= base_url + RESUME_PASS_PATH
15
+ end
16
+
17
+ def resume_fail_url
18
+ @resume_fail_url ||= base_url + RESUME_FAIL_PATH
19
+ end
20
+
21
+ def suite_id
22
+ self.class.suite.id
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,72 @@
1
+ require 'us_core_test_kit'
2
+
3
+ module CancerPathologyDataSharingTestKit
4
+ class USCoreDataAccessGroup < Inferno::TestGroup
5
+ id :cpds_us_core_data_access
6
+ title 'US Core FHIR API Tests'
7
+ short_description 'Verify that cancer patient data are available via US Core API.'
8
+ description %(
9
+ During these tests, Inferno will simulate a FHIR client and verify that it can use the target systems's
10
+ FHIR APIs to access [US Core v5](http://hl7.org/fhir/us/core/STU3.1.1/index.html) data.
11
+ )
12
+ input :url,
13
+ title: 'FHIR Endpoint',
14
+ description: 'URL of the FHIR endpoint'
15
+
16
+ input :smart_credentials,
17
+ title: 'OAuth Credentials',
18
+ type: :auth_info,
19
+ optional: true
20
+
21
+ fhir_client do
22
+ url :url
23
+ auth_info :smart_credentials
24
+ end
25
+
26
+ group from: :us_core_v501_capability_statement
27
+
28
+ group from: :us_core_v501_patient
29
+ group from: :us_core_v501_allergy_intolerance
30
+ group from: :us_core_v501_care_plan
31
+ group from: :us_core_v501_care_team
32
+ group from: :us_core_v501_condition_encounter_diagnosis
33
+ group from: :us_core_v501_condition_problems_health_concerns
34
+ group from: :us_core_v501_device
35
+ group from: :us_core_v501_diagnostic_report_note
36
+ group from: :us_core_v501_diagnostic_report_lab
37
+ group from: :us_core_v501_document_reference
38
+ group from: :us_core_v501_encounter
39
+ group from: :us_core_v501_goal
40
+ group from: :us_core_v501_immunization
41
+ group from: :us_core_v501_medication_request
42
+ group from: :us_core_v501_observation_lab
43
+ group from: :us_core_v501_observation_sdoh_assessment
44
+ group from: :us_core_v501_respiratory_rate
45
+ group from: :us_core_v501_observation_social_history
46
+ group from: :us_core_v501_heart_rate
47
+ group from: :us_core_v501_body_temperature
48
+ group from: :us_core_v501_pediatric_weight_for_height
49
+ group from: :us_core_v501_pulse_oximetry
50
+ group from: :us_core_v501_smokingstatus
51
+ group from: :us_core_v501_observation_sexual_orientation
52
+ group from: :us_core_v501_head_circumference
53
+ group from: :us_core_v501_body_height
54
+ group from: :us_core_v501_bmi
55
+ group from: :us_core_v501_blood_pressure
56
+ group from: :us_core_v501_observation_imaging
57
+ group from: :us_core_v501_observation_clinical_test
58
+ group from: :us_core_v501_pediatric_bmi_for_age
59
+ group from: :us_core_v501_head_circumference_percentile
60
+ group from: :us_core_v501_body_weight
61
+ group from: :us_core_v501_procedure
62
+ group from: :us_core_v501_questionnaire_response
63
+ group from: :us_core_v501_service_request
64
+ group from: :us_core_v501_organization
65
+ group from: :us_core_v501_practitioner
66
+ group from: :us_core_v501_practitioner_role
67
+ group from: :us_core_v501_provenance
68
+ group from: :us_core_v501_related_person
69
+ group from: :us_core_v400_clinical_notes_guidance
70
+ group from: :us_core_311_data_absent_reason
71
+ end
72
+ end
@@ -0,0 +1,58 @@
1
+ require_relative 'us_core_data_access_suite/us_core_group'
2
+ module CancerPathologyDataSharingTestKit
3
+ class USCoreDataAccessSuite < Inferno::TestSuite
4
+ id :cpds_data_access
5
+ title 'Cancer Pathology Data Sharing Data Access Test Suite'
6
+ short_title 'CPDS Data Access Suite'
7
+ description File.read(File.join(__dir__, 'docs', 'data_access_suite_description.md'))
8
+ links [
9
+ {
10
+ label: 'Report Issue',
11
+ url: 'https://github.com/inferno-framework/cancer-pathology-data-sharing-test-kit/issues/'
12
+ },
13
+ {
14
+ label: 'Open Source',
15
+ url: 'https://github.com/inferno-framework/cancer-pathology-data-sharing-test-kit/'
16
+ },
17
+ {
18
+ label: 'Download',
19
+ url: 'https://github.com/inferno-framework/cancer-pathology-data-sharing-test-kit/releases'
20
+ },
21
+ {
22
+ label: 'Implementation Guide',
23
+ url: 'https://hl7.org/fhir/us/cancer-reporting/STU1.0.1/'
24
+ }
25
+ ]
26
+
27
+ GENERAL_MESSAGE_FILTERS = [
28
+ %r{Sub-extension url 'introspect' is not defined by the Extension http://fhir-registry\.smarthealthit\.org/StructureDefinition/oauth-uris},
29
+ %r{Sub-extension url 'revoke' is not defined by the Extension http://fhir-registry\.smarthealthit\.org/StructureDefinition/oauth-uris},
30
+ /Observation\.effective\.ofType\(Period\): .*vs-1:/, # Invalid invariant in FHIR v4.0.1
31
+ /Observation\.effective\.ofType\(Period\): .*us-core-1:/, # Invalid invariant in US Core v3.1.1
32
+ /Provenance.agent\[\d*\]: Constraint failed: provenance-1/, # Invalid invariant in US Core v5.0.1
33
+ %r{Unknown Code System 'http://hl7.org/fhir/us/core/CodeSystem/us-core-tags'}, # Validator has an issue with this US Core 5 code system in US Core 6 resource # rubocop:disable Layout/LineLength
34
+ %r{URL value 'http://hl7.org/fhir/us/core/CodeSystem/us-core-tags' does not resolve}, # Validator has an issue with this US Core 5 code system in US Core 6 resource # rubocop:disable Layout/LineLength
35
+ /\A\S+: \S+: URL value '.*' does not resolve/,
36
+ %r{Observation.component\[\d+\].value.ofType\(Quantity\): The code provided \(http://unitsofmeasure.org#L/min\) was not found in the value set 'Vital Signs Units'} # Known issue with the Pulse Oximetry Profile # rubocop:disable Layout/LineLength
37
+ ].freeze
38
+
39
+ VERSION_SPECIFIC_MESSAGE_FILTERS = [].freeze
40
+
41
+ VALIDATION_MESSAGE_FILTERS = GENERAL_MESSAGE_FILTERS + VERSION_SPECIFIC_MESSAGE_FILTERS
42
+
43
+ fhir_resource_validator do
44
+ igs('hl7.fhir.us.core#5.0.1')
45
+ message_filters = VALIDATION_MESSAGE_FILTERS
46
+
47
+ exclude_message do |message|
48
+ message_filters.any? { |filter| filter.match? message.message }
49
+ end
50
+
51
+ perform_additional_validation do |resource, _profile_url|
52
+ USCoreTestKit::ProvenanceValidator.validate(resource) if resource.instance_of?(FHIR::Provenance)
53
+ end
54
+ end
55
+
56
+ group from: :cpds_us_core_data_access
57
+ end
58
+ end
@@ -0,0 +1,96 @@
1
+ module CancerPathologyDataSharingTestKit
2
+ module ValidationTest
3
+ DAR_CODE_SYSTEM_URL = 'http://terminology.hl7.org/CodeSystem/data-absent-reason'.freeze
4
+ DAR_EXTENSION_URL = 'http://hl7.org/fhir/StructureDefinition/data-absent-reason'.freeze
5
+
6
+ CARDINALITY_RESTRICTIONS = {
7
+ 'exactly_one' =>
8
+ ['==', 1,
9
+ 'There must be exactly one (1) %<resource_type>s resource per bundle. Bundle `%<bundle_id>s` has %<resources_length>s resource(s)'],
10
+ 'no_more_than_one' =>
11
+ ['<=', 1,
12
+ 'There must be no more than one (1) %<resource_type>s resource per bundle. Bundle `%<bundle_id>s` has %<resources_length>s resource(s)'],
13
+ 'at_least_one' =>
14
+ ['>=', 1,
15
+ 'There must be at least one (1) %<resource_type>s resource per bundle. Bundle `%<bundle_id>s` has %<resources_length>s resource(s)']
16
+ }.freeze
17
+
18
+ def perform_strict_validation_test(the_resource_type = resource_type, # rubocop:disable Metrics/CyclomaticComplexity,Style/OptionalArguments
19
+ bundle_id,
20
+ resources,
21
+ profile_url,
22
+ profile_version,
23
+ skip_if_empty: true,
24
+ restriction: nil)
25
+ if !restriction.nil? && !resources.length.public_send(CARDINALITY_RESTRICTIONS[restriction][0], CARDINALITY_RESTRICTIONS[restriction][1])
26
+ messages << {
27
+ type: 'error',
28
+ message: format(CARDINALITY_RESTRICTIONS[restriction][2], resource_type: resource_type, bundle_id: bundle_id,
29
+ resources_length: resources.length)
30
+ }
31
+ return true
32
+ end
33
+
34
+ skip_if skip_if_empty && resources.blank?,
35
+ "No #{the_resource_type} resources in bundle `#{bundle_id}` were provided so the #{profile_url} profile does not apply"
36
+
37
+ if resources.blank?
38
+ messages << {
39
+ type: 'info',
40
+ message: "No #{the_resource_type} resources in bundle `#{bundle_id}` were provided so the #{profile_url} profile does not apply"
41
+ }
42
+ return false
43
+ end
44
+
45
+ not_valid = false
46
+ profile_with_version = "#{profile_url}|#{profile_version}"
47
+ resources.each do |resource|
48
+ not_valid == true unless resource_is_valid?(resource: resource, profile_url: profile_with_version)
49
+ check_for_dar(resource)
50
+ end
51
+
52
+ if not_valid
53
+ messages << {
54
+ type: 'error',
55
+ message: "At least one of the #{the_resource_type} resource(s) in bundle `#{bundle_id}` does not " \
56
+ "conform to the profile #{profile_with_version}"
57
+ }
58
+ end
59
+
60
+ not_valid
61
+ end
62
+
63
+ def check_for_dar(resource)
64
+ unless scratch[:dar_code_found]
65
+ resource.each_element do |element, _meta, _path|
66
+ next unless element.is_a?(FHIR::Coding)
67
+
68
+ check_for_dar_code(element)
69
+ end
70
+ end
71
+
72
+ return if scratch[:dar_extension_found]
73
+
74
+ check_for_dar_extension(resource)
75
+ end
76
+
77
+ def check_for_dar_code(coding)
78
+ return unless coding.code == 'unknown' && coding.system == DAR_CODE_SYSTEM_URL
79
+
80
+ scratch[:dar_code_found] = true
81
+ output dar_code_found: 'true'
82
+ end
83
+
84
+ def check_for_dar_extension(resource)
85
+ return unless resource.source_contents&.include? DAR_EXTENSION_URL
86
+
87
+ scratch[:dar_extension_found] = true
88
+ output dar_extension_found: 'true'
89
+ end
90
+
91
+ def check_for_errors(invalid_bundles)
92
+ assert invalid_bundles.empty?, "Issues found in Bundle(s): #{invalid_bundles.join(', ')}"
93
+ assert messages.none? { |message| message[:type] == 'error' }, 'Non-conformant instances detected.'
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,4 @@
1
+ module CancerPathologyDataSharingTestKit
2
+ VERSION = '0.9.0'.freeze
3
+ LAST_UPDATED = '2025-05-07'.freeze # TODO: update next release
4
+ end
@@ -0,0 +1,3 @@
1
+ require_relative 'cancer_pathology_data_sharing_test_kit/metadata'
2
+ require_relative 'cancer_pathology_data_sharing_test_kit/us_core_data_access_suite'
3
+ require_relative 'cancer_pathology_data_sharing_test_kit/report_generation_suite'