davinci_pas_test_kit 0.15.2 → 0.16.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 (271) hide show
  1. checksums.yaml +4 -4
  2. data/config/presets/g33_br_provider.json +13 -0
  3. data/lib/davinci_pas_test_kit/client/abstract_approval_submit_test.rb +28 -17
  4. data/lib/davinci_pas_test_kit/client/abstract_denial_submit_test.rb +26 -16
  5. data/lib/davinci_pas_test_kit/client/abstract_gather_must_support_test.rb +43 -26
  6. data/lib/davinci_pas_test_kit/client/abstract_modification_submit_test.rb +29 -19
  7. data/lib/davinci_pas_test_kit/client/abstract_response_attest.rb +75 -6
  8. data/lib/davinci_pas_test_kit/client/abstract_subscription_create_test.rb +26 -9
  9. data/lib/davinci_pas_test_kit/client/abstract_subscription_update_delete_test.rb +91 -0
  10. data/lib/davinci_pas_test_kit/client/client_bundle_validation_helper.rb +245 -0
  11. data/lib/davinci_pas_test_kit/client/client_input_descriptions.rb +91 -25
  12. data/lib/davinci_pas_test_kit/client/endpoints/claim_endpoint.rb +108 -68
  13. data/lib/davinci_pas_test_kit/client/endpoints/endpoint_session_identification.rb +24 -0
  14. data/lib/davinci_pas_test_kit/client/endpoints/expired_token.rb +40 -0
  15. data/lib/davinci_pas_test_kit/client/endpoints/subscription_create_endpoint.rb +7 -12
  16. data/lib/davinci_pas_test_kit/client/endpoints/subscription_delete_endpoint.rb +13 -0
  17. data/lib/davinci_pas_test_kit/client/endpoints/subscription_read_endpoint.rb +13 -0
  18. data/lib/davinci_pas_test_kit/client/endpoints/subscription_status_endpoint.rb +15 -20
  19. data/lib/davinci_pas_test_kit/client/endpoints/subscription_update_endpoint.rb +13 -0
  20. data/lib/davinci_pas_test_kit/client/endpoints/token_endpoint.rb +16 -0
  21. data/lib/davinci_pas_test_kit/client/generated/v2.0.1/beneficiary/client_inquire_request_must_support_beneficiary_test.rb +3 -3
  22. data/lib/davinci_pas_test_kit/client/generated/v2.0.1/beneficiary/client_inquire_response_must_support_beneficiary_test.rb +3 -3
  23. data/lib/davinci_pas_test_kit/client/generated/v2.0.1/beneficiary/client_submit_request_must_support_beneficiary_test.rb +3 -3
  24. data/lib/davinci_pas_test_kit/client/generated/v2.0.1/beneficiary/client_submit_response_must_support_beneficiary_test.rb +3 -3
  25. data/lib/davinci_pas_test_kit/client/generated/v2.0.1/claim_inquiry/client_inquire_request_must_support_claim_inquiry_test.rb +3 -3
  26. data/lib/davinci_pas_test_kit/client/generated/v2.0.1/claim_update/client_submit_request_must_support_claim_update_test.rb +3 -3
  27. data/lib/davinci_pas_test_kit/client/generated/v2.0.1/claiminquiryresponse/client_inquire_response_must_support_claiminquiryresponse_test.rb +3 -3
  28. data/lib/davinci_pas_test_kit/client/generated/v2.0.1/claimresponse/client_submit_response_must_support_claimresponse_test.rb +3 -3
  29. data/lib/davinci_pas_test_kit/client/generated/v2.0.1/communication_request/client_submit_response_must_support_communication_request_test.rb +3 -3
  30. data/lib/davinci_pas_test_kit/client/generated/v2.0.1/coverage/client_inquire_request_must_support_coverage_test.rb +3 -3
  31. data/lib/davinci_pas_test_kit/client/generated/v2.0.1/coverage/client_submit_request_must_support_coverage_test.rb +3 -3
  32. data/lib/davinci_pas_test_kit/client/generated/v2.0.1/encounter/client_submit_request_must_support_encounter_test.rb +3 -3
  33. data/lib/davinci_pas_test_kit/client/generated/v2.0.1/insurer/client_inquire_request_must_support_insurer_test.rb +3 -3
  34. data/lib/davinci_pas_test_kit/client/generated/v2.0.1/insurer/client_inquire_response_must_support_insurer_test.rb +3 -3
  35. data/lib/davinci_pas_test_kit/client/generated/v2.0.1/insurer/client_submit_request_must_support_insurer_test.rb +3 -3
  36. data/lib/davinci_pas_test_kit/client/generated/v2.0.1/insurer/client_submit_response_must_support_insurer_test.rb +3 -3
  37. data/lib/davinci_pas_test_kit/client/generated/v2.0.1/pas_client_inquire_must_support_group.rb +1 -1
  38. data/lib/davinci_pas_test_kit/client/generated/v2.0.1/pas_client_inquire_response_must_support_group.rb +9 -31
  39. data/lib/davinci_pas_test_kit/client/generated/v2.0.1/pas_client_submit_must_support_group.rb +1 -1
  40. data/lib/davinci_pas_test_kit/client/generated/v2.0.1/pas_client_submit_response_must_support_group.rb +10 -32
  41. data/lib/davinci_pas_test_kit/client/generated/v2.0.1/pas_inquiry_request_bundle/client_inquire_request_must_support_pas_inquiry_request_bundle_test.rb +3 -3
  42. data/lib/davinci_pas_test_kit/client/generated/v2.0.1/pas_inquiry_response_bundle/client_inquire_response_must_support_pas_inquiry_response_bundle_test.rb +3 -3
  43. data/lib/davinci_pas_test_kit/client/generated/v2.0.1/pas_request_bundle/client_submit_request_must_support_pas_request_bundle_test.rb +3 -3
  44. data/lib/davinci_pas_test_kit/client/generated/v2.0.1/pas_response_bundle/client_submit_response_must_support_pas_response_bundle_test.rb +3 -3
  45. data/lib/davinci_pas_test_kit/client/generated/v2.0.1/practitioner/client_inquire_request_must_support_practitioner_test.rb +3 -3
  46. data/lib/davinci_pas_test_kit/client/generated/v2.0.1/practitioner/client_inquire_response_must_support_practitioner_test.rb +3 -3
  47. data/lib/davinci_pas_test_kit/client/generated/v2.0.1/practitioner/client_submit_request_must_support_practitioner_test.rb +3 -3
  48. data/lib/davinci_pas_test_kit/client/generated/v2.0.1/practitioner/client_submit_response_must_support_practitioner_test.rb +3 -3
  49. data/lib/davinci_pas_test_kit/client/generated/v2.0.1/practitioner_role/client_inquire_request_must_support_practitioner_role_test.rb +3 -3
  50. data/lib/davinci_pas_test_kit/client/generated/v2.0.1/practitioner_role/client_inquire_response_must_support_practitioner_role_test.rb +3 -3
  51. data/lib/davinci_pas_test_kit/client/generated/v2.0.1/practitioner_role/client_submit_request_must_support_practitioner_role_test.rb +3 -3
  52. data/lib/davinci_pas_test_kit/client/generated/v2.0.1/practitioner_role/client_submit_response_must_support_practitioner_role_test.rb +3 -3
  53. data/lib/davinci_pas_test_kit/client/generated/v2.0.1/requestor/client_inquire_request_must_support_requestor_test.rb +3 -3
  54. data/lib/davinci_pas_test_kit/client/generated/v2.0.1/requestor/client_inquire_response_must_support_requestor_test.rb +3 -3
  55. data/lib/davinci_pas_test_kit/client/generated/v2.0.1/requestor/client_submit_request_must_support_requestor_test.rb +3 -3
  56. data/lib/davinci_pas_test_kit/client/generated/v2.0.1/requestor/client_submit_response_must_support_requestor_test.rb +3 -3
  57. data/lib/davinci_pas_test_kit/client/generated/v2.0.1/subscriber/client_inquire_request_must_support_subscriber_test.rb +3 -3
  58. data/lib/davinci_pas_test_kit/client/generated/v2.0.1/subscriber/client_submit_request_must_support_subscriber_test.rb +3 -3
  59. data/lib/davinci_pas_test_kit/client/generated/v2.2.1/beneficiary/client_inquire_response_must_support_beneficiary_test.rb +4 -3
  60. data/lib/davinci_pas_test_kit/client/generated/v2.2.1/beneficiary/client_submit_response_must_support_beneficiary_test.rb +4 -3
  61. data/lib/davinci_pas_test_kit/client/generated/v2.2.1/claim_inquiry/client_inquire_request_must_support_claim_inquiry_test.rb +3 -3
  62. data/lib/davinci_pas_test_kit/client/generated/v2.2.1/claim_update/client_submit_request_must_support_claim_update_test.rb +3 -3
  63. data/lib/davinci_pas_test_kit/client/generated/v2.2.1/claiminquiryresponse/client_inquire_response_must_support_claiminquiryresponse_test.rb +4 -3
  64. data/lib/davinci_pas_test_kit/client/generated/v2.2.1/claimresponse/client_submit_response_must_support_claimresponse_test.rb +4 -3
  65. data/lib/davinci_pas_test_kit/client/generated/v2.2.1/communication_request/client_submit_response_must_support_communication_request_test.rb +4 -3
  66. data/lib/davinci_pas_test_kit/client/generated/v2.2.1/insurer/client_inquire_response_must_support_insurer_test.rb +4 -3
  67. data/lib/davinci_pas_test_kit/client/generated/v2.2.1/insurer/client_submit_response_must_support_insurer_test.rb +4 -3
  68. data/lib/davinci_pas_test_kit/client/generated/v2.2.1/pas_client_inquire_must_support_group.rb +8 -6
  69. data/lib/davinci_pas_test_kit/client/generated/v2.2.1/pas_client_inquire_response_must_support_group.rb +9 -31
  70. data/lib/davinci_pas_test_kit/client/generated/v2.2.1/pas_client_submit_must_support_group.rb +13 -9
  71. data/lib/davinci_pas_test_kit/client/generated/v2.2.1/pas_client_submit_response_must_support_group.rb +10 -32
  72. data/lib/davinci_pas_test_kit/client/generated/v2.2.1/pas_inquiry_response_bundle/client_inquire_response_must_support_pas_inquiry_response_bundle_test.rb +4 -3
  73. data/lib/davinci_pas_test_kit/client/generated/v2.2.1/pas_response_bundle/client_submit_response_must_support_pas_response_bundle_test.rb +4 -3
  74. data/lib/davinci_pas_test_kit/client/generated/v2.2.1/practitioner/client_inquire_response_must_support_practitioner_test.rb +4 -3
  75. data/lib/davinci_pas_test_kit/client/generated/v2.2.1/practitioner/client_submit_response_must_support_practitioner_test.rb +4 -3
  76. data/lib/davinci_pas_test_kit/client/generated/v2.2.1/practitioner_role/client_inquire_response_must_support_practitioner_role_test.rb +4 -3
  77. data/lib/davinci_pas_test_kit/client/generated/v2.2.1/practitioner_role/client_submit_response_must_support_practitioner_role_test.rb +4 -3
  78. data/lib/davinci_pas_test_kit/client/generated/v2.2.1/requestor/client_inquire_response_must_support_requestor_test.rb +4 -3
  79. data/lib/davinci_pas_test_kit/client/generated/v2.2.1/requestor/client_submit_response_must_support_requestor_test.rb +4 -3
  80. data/lib/davinci_pas_test_kit/client/jobs/send_pas_subscription_notification.rb +64 -3
  81. data/lib/davinci_pas_test_kit/client/pas_client_subscription_pas_conformance_test.rb +4 -3
  82. data/lib/davinci_pas_test_kit/client/registration/abstract_configuration_other_display_test.rb +6 -4
  83. data/lib/davinci_pas_test_kit/client/registration/abstract_configuration_smart_display_test.rb +8 -4
  84. data/lib/davinci_pas_test_kit/client/registration/abstract_configuration_udap_display_test.rb +9 -4
  85. data/lib/davinci_pas_test_kit/client/registration/abstract_other_auth_attest_test.rb +5 -4
  86. data/lib/davinci_pas_test_kit/client/request_count_gating.rb +14 -0
  87. data/lib/davinci_pas_test_kit/client/response_generator.rb +50 -9
  88. data/lib/davinci_pas_test_kit/client/session_identification.rb +26 -7
  89. data/lib/davinci_pas_test_kit/client/subscription_notification_trigger.rb +80 -0
  90. data/lib/davinci_pas_test_kit/client/subscription_setup_requirement.rb +59 -0
  91. data/lib/davinci_pas_test_kit/client/user_input_response.rb +27 -6
  92. data/lib/davinci_pas_test_kit/client/v2.0.1/client_suite.rb +30 -15
  93. data/lib/davinci_pas_test_kit/client/v2.0.1/must_support/pas_client_must_support_request_profiles_test.rb +2 -2
  94. data/lib/davinci_pas_test_kit/client/v2.0.1/pas_client_approval_group.rb +8 -7
  95. data/lib/davinci_pas_test_kit/client/v2.0.1/pas_client_auth_smart_group.rb +4 -4
  96. data/lib/davinci_pas_test_kit/client/v2.0.1/pas_client_auth_udap_group.rb +4 -4
  97. data/lib/davinci_pas_test_kit/client/v2.0.1/pas_client_denial_group.rb +8 -7
  98. data/lib/davinci_pas_test_kit/client/v2.0.1/pas_client_must_support_group.rb +38 -52
  99. data/lib/davinci_pas_test_kit/client/v2.0.1/pas_client_pended_group.rb +28 -22
  100. data/lib/davinci_pas_test_kit/client/v2.0.1/pas_client_registration_group.rb +2 -2
  101. data/lib/davinci_pas_test_kit/client/v2.0.1/pas_client_subscription_setup_group.rb +2 -2
  102. data/lib/davinci_pas_test_kit/client/v2.0.1/pas_client_workflows_group.rb +2 -2
  103. data/lib/davinci_pas_test_kit/client/v2.0.1/workflows/pas_client_inquire_request_bundle_validation_test.rb +15 -21
  104. data/lib/davinci_pas_test_kit/client/v2.0.1/workflows/pas_client_inquire_response_bundle_validation_test.rb +29 -29
  105. data/lib/davinci_pas_test_kit/client/v2.0.1/workflows/pas_client_pended_submit_test.rb +49 -40
  106. data/lib/davinci_pas_test_kit/client/v2.0.1/workflows/pas_client_request_bundle_validation_test.rb +15 -21
  107. data/lib/davinci_pas_test_kit/client/v2.0.1/workflows/pas_client_response_bundle_validation_test.rb +29 -29
  108. data/lib/davinci_pas_test_kit/client/v2.2.1/attestations/attestation_instructions.rb +10 -0
  109. data/lib/davinci_pas_test_kit/client/v2.2.1/attestations/client_data_element_expectations_attestation_test.rb +44 -0
  110. data/lib/davinci_pas_test_kit/client/v2.2.1/attestations/client_provider_review_attestation_test.rb +45 -0
  111. data/lib/davinci_pas_test_kit/client/v2.2.1/attestations/client_security_attestation_test.rb +42 -0
  112. data/lib/davinci_pas_test_kit/client/v2.2.1/attestations/client_subscription_error_recovery_attestation_test.rb +42 -0
  113. data/lib/davinci_pas_test_kit/client/v2.2.1/client_suite.rb +64 -12
  114. data/lib/davinci_pas_test_kit/client/v2.2.1/error_handling/pas_client_operation_failure_submit_test.rb +20 -10
  115. data/lib/davinci_pas_test_kit/client/v2.2.1/error_handling/pas_client_operation_outcome_validation_test.rb +11 -5
  116. data/lib/davinci_pas_test_kit/client/v2.2.1/error_handling/pas_client_processing_error_response_validation_test.rb +21 -12
  117. data/lib/davinci_pas_test_kit/client/v2.2.1/error_handling/pas_client_processing_error_submit_test.rb +19 -8
  118. data/lib/davinci_pas_test_kit/client/v2.2.1/must_support/must_support_with_attestation_option.rb +90 -40
  119. data/lib/davinci_pas_test_kit/client/v2.2.1/pas_client_approval_group.rb +12 -11
  120. data/lib/davinci_pas_test_kit/client/v2.2.1/pas_client_attestations_group.rb +32 -0
  121. data/lib/davinci_pas_test_kit/client/v2.2.1/pas_client_auth_smart_group.rb +4 -4
  122. data/lib/davinci_pas_test_kit/client/v2.2.1/pas_client_auth_udap_group.rb +4 -4
  123. data/lib/davinci_pas_test_kit/client/v2.2.1/pas_client_claim_updates_group.rb +24 -6
  124. data/lib/davinci_pas_test_kit/client/v2.2.1/pas_client_denial_group.rb +12 -10
  125. data/lib/davinci_pas_test_kit/client/v2.2.1/pas_client_modification_group.rb +13 -14
  126. data/lib/davinci_pas_test_kit/client/v2.2.1/pas_client_must_support_group.rb +55 -59
  127. data/lib/davinci_pas_test_kit/client/v2.2.1/pas_client_operation_failure_group.rb +8 -5
  128. data/lib/davinci_pas_test_kit/client/v2.2.1/pas_client_pended_group.rb +53 -41
  129. data/lib/davinci_pas_test_kit/client/v2.2.1/pas_client_processing_error_group.rb +9 -7
  130. data/lib/davinci_pas_test_kit/client/v2.2.1/pas_client_registration_group.rb +2 -2
  131. data/lib/davinci_pas_test_kit/client/v2.2.1/pas_client_subscription_setup_group.rb +9 -4
  132. data/lib/davinci_pas_test_kit/client/v2.2.1/pas_client_subscription_update_delete_group.rb +57 -0
  133. data/lib/davinci_pas_test_kit/client/v2.2.1/pas_client_workflows_group.rb +22 -2
  134. data/lib/davinci_pas_test_kit/client/v2.2.1/subscription/pas_client_subscription_create_test.rb +2 -1
  135. data/lib/davinci_pas_test_kit/client/v2.2.1/subscription/pas_client_subscription_put_update_pas_conformance_test.rb +49 -0
  136. data/lib/davinci_pas_test_kit/client/v2.2.1/subscription/pas_client_subscription_update_delete_test.rb +12 -0
  137. data/lib/davinci_pas_test_kit/client/v2.2.1/workflows/pas_client_claim_update_referenced_resources_test.rb +5 -6
  138. data/lib/davinci_pas_test_kit/client/v2.2.1/workflows/pas_client_claim_update_submit_tests.rb +82 -56
  139. data/lib/davinci_pas_test_kit/client/v2.2.1/workflows/pas_client_inquire_request_bundle_validation_test.rb +17 -20
  140. data/lib/davinci_pas_test_kit/client/v2.2.1/workflows/pas_client_inquire_response_bundle_validation_test.rb +59 -27
  141. data/lib/davinci_pas_test_kit/client/v2.2.1/workflows/pas_client_modification_verification_test.rb +0 -3
  142. data/lib/davinci_pas_test_kit/client/v2.2.1/workflows/pas_client_pended_send_notification_test.rb +138 -0
  143. data/lib/davinci_pas_test_kit/client/v2.2.1/workflows/pas_client_pended_submit_test.rb +32 -85
  144. data/lib/davinci_pas_test_kit/client/v2.2.1/workflows/pas_client_request_bundle_validation_test.rb +16 -21
  145. data/lib/davinci_pas_test_kit/client/v2.2.1/workflows/pas_client_response_bundle_validation_test.rb +27 -27
  146. data/lib/davinci_pas_test_kit/cross_suite/generated/v2.0.1/task/metadata.yml +0 -4
  147. data/lib/davinci_pas_test_kit/cross_suite/generated/v2.2.1/metadata.yml +0 -1
  148. data/lib/davinci_pas_test_kit/cross_suite/generated/v2.2.1/task/metadata.yml +0 -4
  149. data/lib/davinci_pas_test_kit/cross_suite/pas_bundle_validation.rb +489 -289
  150. data/lib/davinci_pas_test_kit/cross_suite/pas_constants.rb +7 -0
  151. data/lib/davinci_pas_test_kit/cross_suite/pas_subscription_verification.rb +8 -4
  152. data/lib/davinci_pas_test_kit/cross_suite/referenced_resource_presence_validation.rb +141 -0
  153. data/lib/davinci_pas_test_kit/cross_suite/response_selection_utils.rb +13 -11
  154. data/lib/davinci_pas_test_kit/cross_suite/tags.rb +11 -6
  155. data/lib/davinci_pas_test_kit/cross_suite/validator_configuration.rb +16 -0
  156. data/lib/davinci_pas_test_kit/cross_suite/validator_suppressions.rb +4 -0
  157. data/lib/davinci_pas_test_kit/docs/demo/PAS Client Suite Demonstration.postman_collection.json +3 -3
  158. data/lib/davinci_pas_test_kit/ext/inferno_core/runnable.rb +1 -0
  159. data/lib/davinci_pas_test_kit/generator/client_must_support_group_generator.rb +47 -16
  160. data/lib/davinci_pas_test_kit/generator/must_support_target_profiles.rb +17 -0
  161. data/lib/davinci_pas_test_kit/generator/must_support_test_generator.rb +8 -2
  162. data/lib/davinci_pas_test_kit/generator/profile_metadata.rb +1 -1
  163. data/lib/davinci_pas_test_kit/generator/server_suite_generator.rb +4 -9
  164. data/lib/davinci_pas_test_kit/generator/templates/client_must_support_group.rb.erb +22 -3
  165. data/lib/davinci_pas_test_kit/generator/templates/client_must_support_response_group.rb.erb +7 -16
  166. data/lib/davinci_pas_test_kit/generator/templates/must_support.rb.erb +8 -8
  167. data/lib/davinci_pas_test_kit/generator/templates/server_suite.rb.erb +6 -7
  168. data/lib/davinci_pas_test_kit/generator/use_case_group_generator.rb +5 -5
  169. data/lib/davinci_pas_test_kit/parameters_helper.rb +13 -5
  170. data/lib/davinci_pas_test_kit/pas_notification_conformance_test.rb +2 -2
  171. data/lib/davinci_pas_test_kit/requirements/davinci_pas_test_kit_requirements.csv +14 -7
  172. data/lib/davinci_pas_test_kit/requirements/generated/davinci_pas_client_suite_v201_requirements_coverage.csv +21 -21
  173. data/lib/davinci_pas_test_kit/requirements/generated/davinci_pas_client_suite_v221_requirements_coverage.csv +30 -26
  174. data/lib/davinci_pas_test_kit/requirements/generated/davinci_pas_server_suite_v221_requirements_coverage.csv +12 -6
  175. data/lib/davinci_pas_test_kit/requirements/hl7.fhir.us.davinci-pas_2.2.1_requirements.xlsx +0 -0
  176. data/lib/davinci_pas_test_kit/server/claim_operation_logic.rb +1 -1
  177. data/lib/davinci_pas_test_kit/server/generated/v2.0.1/beneficiary/server_inquire_request_must_support_beneficiary_test.rb +1 -1
  178. data/lib/davinci_pas_test_kit/server/generated/v2.0.1/beneficiary/server_inquire_response_must_support_beneficiary_test.rb +1 -1
  179. data/lib/davinci_pas_test_kit/server/generated/v2.0.1/beneficiary/server_submit_request_must_support_beneficiary_test.rb +1 -1
  180. data/lib/davinci_pas_test_kit/server/generated/v2.0.1/beneficiary/server_submit_response_must_support_beneficiary_test.rb +1 -1
  181. data/lib/davinci_pas_test_kit/server/generated/v2.0.1/claim_inquiry/server_inquire_request_must_support_claim_inquiry_test.rb +1 -1
  182. data/lib/davinci_pas_test_kit/server/generated/v2.0.1/claim_update/server_submit_request_must_support_claim_update_test.rb +1 -1
  183. data/lib/davinci_pas_test_kit/server/generated/v2.0.1/claiminquiryresponse/server_inquire_response_must_support_claiminquiryresponse_test.rb +1 -1
  184. data/lib/davinci_pas_test_kit/server/generated/v2.0.1/claimresponse/server_submit_response_must_support_claimresponse_test.rb +1 -1
  185. data/lib/davinci_pas_test_kit/server/generated/v2.0.1/communication_request/server_submit_response_must_support_communication_request_test.rb +1 -1
  186. data/lib/davinci_pas_test_kit/server/generated/v2.0.1/coverage/server_inquire_request_must_support_coverage_test.rb +1 -1
  187. data/lib/davinci_pas_test_kit/server/generated/v2.0.1/coverage/server_submit_request_must_support_coverage_test.rb +1 -1
  188. data/lib/davinci_pas_test_kit/server/generated/v2.0.1/encounter/server_submit_request_must_support_encounter_test.rb +1 -1
  189. data/lib/davinci_pas_test_kit/server/generated/v2.0.1/insurer/server_inquire_request_must_support_insurer_test.rb +1 -1
  190. data/lib/davinci_pas_test_kit/server/generated/v2.0.1/insurer/server_inquire_response_must_support_insurer_test.rb +1 -1
  191. data/lib/davinci_pas_test_kit/server/generated/v2.0.1/insurer/server_submit_request_must_support_insurer_test.rb +1 -1
  192. data/lib/davinci_pas_test_kit/server/generated/v2.0.1/insurer/server_submit_response_must_support_insurer_test.rb +1 -1
  193. data/lib/davinci_pas_test_kit/server/generated/v2.0.1/pas_inquiry_request_bundle/server_inquire_request_must_support_pas_inquiry_request_bundle_test.rb +1 -1
  194. data/lib/davinci_pas_test_kit/server/generated/v2.0.1/pas_inquiry_response_bundle/server_inquire_response_must_support_pas_inquiry_response_bundle_test.rb +1 -1
  195. data/lib/davinci_pas_test_kit/server/generated/v2.0.1/pas_request_bundle/server_submit_request_must_support_pas_request_bundle_test.rb +1 -1
  196. data/lib/davinci_pas_test_kit/server/generated/v2.0.1/pas_response_bundle/server_submit_response_must_support_pas_response_bundle_test.rb +1 -1
  197. data/lib/davinci_pas_test_kit/server/generated/v2.0.1/pas_server_approval_use_case_group.rb +3 -3
  198. data/lib/davinci_pas_test_kit/server/generated/v2.0.1/pas_server_denial_use_case_group.rb +3 -3
  199. data/lib/davinci_pas_test_kit/server/generated/v2.0.1/pas_server_pended_use_case_group.rb +6 -6
  200. data/lib/davinci_pas_test_kit/server/generated/v2.0.1/practitioner/server_inquire_request_must_support_practitioner_test.rb +1 -1
  201. data/lib/davinci_pas_test_kit/server/generated/v2.0.1/practitioner/server_inquire_response_must_support_practitioner_test.rb +1 -1
  202. data/lib/davinci_pas_test_kit/server/generated/v2.0.1/practitioner/server_submit_request_must_support_practitioner_test.rb +1 -1
  203. data/lib/davinci_pas_test_kit/server/generated/v2.0.1/practitioner/server_submit_response_must_support_practitioner_test.rb +1 -1
  204. data/lib/davinci_pas_test_kit/server/generated/v2.0.1/practitioner_role/server_inquire_request_must_support_practitioner_role_test.rb +1 -1
  205. data/lib/davinci_pas_test_kit/server/generated/v2.0.1/practitioner_role/server_inquire_response_must_support_practitioner_role_test.rb +1 -1
  206. data/lib/davinci_pas_test_kit/server/generated/v2.0.1/practitioner_role/server_submit_request_must_support_practitioner_role_test.rb +1 -1
  207. data/lib/davinci_pas_test_kit/server/generated/v2.0.1/practitioner_role/server_submit_response_must_support_practitioner_role_test.rb +1 -1
  208. data/lib/davinci_pas_test_kit/server/generated/v2.0.1/requestor/server_inquire_request_must_support_requestor_test.rb +1 -1
  209. data/lib/davinci_pas_test_kit/server/generated/v2.0.1/requestor/server_inquire_response_must_support_requestor_test.rb +1 -1
  210. data/lib/davinci_pas_test_kit/server/generated/v2.0.1/requestor/server_submit_request_must_support_requestor_test.rb +1 -1
  211. data/lib/davinci_pas_test_kit/server/generated/v2.0.1/requestor/server_submit_response_must_support_requestor_test.rb +1 -1
  212. data/lib/davinci_pas_test_kit/server/generated/v2.0.1/server_suite.rb +6 -7
  213. data/lib/davinci_pas_test_kit/server/generated/v2.0.1/subscriber/server_inquire_request_must_support_subscriber_test.rb +1 -1
  214. data/lib/davinci_pas_test_kit/server/generated/v2.0.1/subscriber/server_submit_request_must_support_subscriber_test.rb +1 -1
  215. data/lib/davinci_pas_test_kit/server/generated/v2.0.1/task/server_inquire_response_must_support_task_test.rb +1 -1
  216. data/lib/davinci_pas_test_kit/server/generated/v2.0.1/task/server_submit_response_must_support_task_test.rb +1 -1
  217. data/lib/davinci_pas_test_kit/server/generated/v2.2.1/beneficiary/server_inquire_request_must_support_beneficiary_test.rb +1 -1
  218. data/lib/davinci_pas_test_kit/server/generated/v2.2.1/beneficiary/server_inquire_response_must_support_beneficiary_test.rb +1 -1
  219. data/lib/davinci_pas_test_kit/server/generated/v2.2.1/beneficiary/server_submit_request_must_support_beneficiary_test.rb +1 -1
  220. data/lib/davinci_pas_test_kit/server/generated/v2.2.1/beneficiary/server_submit_response_must_support_beneficiary_test.rb +1 -1
  221. data/lib/davinci_pas_test_kit/server/generated/v2.2.1/claim_inquiry/server_inquire_request_must_support_claim_inquiry_test.rb +1 -1
  222. data/lib/davinci_pas_test_kit/server/generated/v2.2.1/claim_update/server_submit_request_must_support_claim_update_test.rb +1 -1
  223. data/lib/davinci_pas_test_kit/server/generated/v2.2.1/claiminquiryresponse/server_inquire_response_must_support_claiminquiryresponse_test.rb +1 -1
  224. data/lib/davinci_pas_test_kit/server/generated/v2.2.1/claimresponse/server_submit_response_must_support_claimresponse_test.rb +1 -1
  225. data/lib/davinci_pas_test_kit/server/generated/v2.2.1/communication_request/server_submit_response_must_support_communication_request_test.rb +1 -1
  226. data/lib/davinci_pas_test_kit/server/generated/v2.2.1/coverage/server_inquire_request_must_support_coverage_test.rb +1 -1
  227. data/lib/davinci_pas_test_kit/server/generated/v2.2.1/coverage/server_submit_request_must_support_coverage_test.rb +1 -1
  228. data/lib/davinci_pas_test_kit/server/generated/v2.2.1/encounter/server_submit_request_must_support_encounter_test.rb +1 -1
  229. data/lib/davinci_pas_test_kit/server/generated/v2.2.1/insurer/server_inquire_request_must_support_insurer_test.rb +1 -1
  230. data/lib/davinci_pas_test_kit/server/generated/v2.2.1/insurer/server_inquire_response_must_support_insurer_test.rb +1 -1
  231. data/lib/davinci_pas_test_kit/server/generated/v2.2.1/insurer/server_submit_request_must_support_insurer_test.rb +1 -1
  232. data/lib/davinci_pas_test_kit/server/generated/v2.2.1/insurer/server_submit_response_must_support_insurer_test.rb +1 -1
  233. data/lib/davinci_pas_test_kit/server/generated/v2.2.1/pas_inquiry_request_bundle/server_inquire_request_must_support_pas_inquiry_request_bundle_test.rb +1 -1
  234. data/lib/davinci_pas_test_kit/server/generated/v2.2.1/pas_inquiry_response_bundle/server_inquire_response_must_support_pas_inquiry_response_bundle_test.rb +1 -1
  235. data/lib/davinci_pas_test_kit/server/generated/v2.2.1/pas_request_bundle/server_submit_request_must_support_pas_request_bundle_test.rb +1 -1
  236. data/lib/davinci_pas_test_kit/server/generated/v2.2.1/pas_response_bundle/server_submit_response_must_support_pas_response_bundle_test.rb +1 -1
  237. data/lib/davinci_pas_test_kit/server/generated/v2.2.1/pas_server_approval_use_case_group.rb +3 -3
  238. data/lib/davinci_pas_test_kit/server/generated/v2.2.1/pas_server_denial_use_case_group.rb +3 -3
  239. data/lib/davinci_pas_test_kit/server/generated/v2.2.1/pas_server_pended_use_case_group.rb +4 -4
  240. data/lib/davinci_pas_test_kit/server/generated/v2.2.1/practitioner/server_inquire_request_must_support_practitioner_test.rb +1 -1
  241. data/lib/davinci_pas_test_kit/server/generated/v2.2.1/practitioner/server_inquire_response_must_support_practitioner_test.rb +1 -1
  242. data/lib/davinci_pas_test_kit/server/generated/v2.2.1/practitioner/server_submit_request_must_support_practitioner_test.rb +1 -1
  243. data/lib/davinci_pas_test_kit/server/generated/v2.2.1/practitioner/server_submit_response_must_support_practitioner_test.rb +1 -1
  244. data/lib/davinci_pas_test_kit/server/generated/v2.2.1/practitioner_role/server_inquire_request_must_support_practitioner_role_test.rb +1 -1
  245. data/lib/davinci_pas_test_kit/server/generated/v2.2.1/practitioner_role/server_inquire_response_must_support_practitioner_role_test.rb +1 -1
  246. data/lib/davinci_pas_test_kit/server/generated/v2.2.1/practitioner_role/server_submit_request_must_support_practitioner_role_test.rb +1 -1
  247. data/lib/davinci_pas_test_kit/server/generated/v2.2.1/practitioner_role/server_submit_response_must_support_practitioner_role_test.rb +1 -1
  248. data/lib/davinci_pas_test_kit/server/generated/v2.2.1/requestor/server_inquire_request_must_support_requestor_test.rb +1 -1
  249. data/lib/davinci_pas_test_kit/server/generated/v2.2.1/requestor/server_inquire_response_must_support_requestor_test.rb +1 -1
  250. data/lib/davinci_pas_test_kit/server/generated/v2.2.1/requestor/server_submit_request_must_support_requestor_test.rb +1 -1
  251. data/lib/davinci_pas_test_kit/server/generated/v2.2.1/requestor/server_submit_response_must_support_requestor_test.rb +1 -1
  252. data/lib/davinci_pas_test_kit/server/generated/v2.2.1/server_suite.rb +6 -7
  253. data/lib/davinci_pas_test_kit/server/generated/v2.2.1/subscriber/server_inquire_request_must_support_subscriber_test.rb +1 -1
  254. data/lib/davinci_pas_test_kit/server/generated/v2.2.1/subscriber/server_submit_request_must_support_subscriber_test.rb +1 -1
  255. data/lib/davinci_pas_test_kit/server/generated/v2.2.1/task/server_inquire_response_must_support_task_test.rb +1 -1
  256. data/lib/davinci_pas_test_kit/server/generated/v2.2.1/task/server_submit_response_must_support_task_test.rb +1 -1
  257. data/lib/davinci_pas_test_kit/server/pas_claim_response_decision_test.rb +2 -2
  258. data/lib/davinci_pas_test_kit/server/server_request_bundle_validation_test.rb +4 -6
  259. data/lib/davinci_pas_test_kit/server/server_response_bundle_validation_test.rb +4 -6
  260. data/lib/davinci_pas_test_kit/server/v2.0.1/must_support/pas_server_must_support_request_profiles_test.rb +1 -1
  261. data/lib/davinci_pas_test_kit/server/v2.0.1/pas_server_subscription_setup.rb +1 -1
  262. data/lib/davinci_pas_test_kit/server/v2.2.1/must_support/pas_server_must_support_request_profiles_test.rb +1 -1
  263. data/lib/davinci_pas_test_kit/server/v2.2.1/pas_server_claim_updates_group.rb +1 -1
  264. data/lib/davinci_pas_test_kit/server/v2.2.1/pas_server_subscription_setup.rb +1 -1
  265. data/lib/davinci_pas_test_kit/version.rb +2 -2
  266. metadata +31 -13
  267. data/lib/davinci_pas_test_kit/client/generated/v2.0.1/task/client_inquire_response_must_support_task_test.rb +0 -45
  268. data/lib/davinci_pas_test_kit/client/generated/v2.0.1/task/client_submit_response_must_support_task_test.rb +0 -45
  269. data/lib/davinci_pas_test_kit/client/generated/v2.2.1/task/client_inquire_response_must_support_task_test.rb +0 -55
  270. data/lib/davinci_pas_test_kit/client/generated/v2.2.1/task/client_submit_response_must_support_task_test.rb +0 -55
  271. data/lib/davinci_pas_test_kit/client/v2.2.1/pas_client_error_handling_group.rb +0 -20
@@ -7,6 +7,13 @@ module DaVinciPASTestKit
7
7
  CLAIM_INQUIRY_RESPONSE_PROFILE = 'http://hl7.org/fhir/us/davinci-pas/StructureDefinition/profile-claiminquiryresponse'.freeze
8
8
  DATA_ABSENT_REASON_EXTENSION_URL = 'http://hl7.org/fhir/StructureDefinition/data-absent-reason'.freeze
9
9
 
10
+ # US Core versions that PAS v2.2 lets supporting information resources conform to
11
+ US_CORE_VERSION_3_1_1 = '3.1.1'.freeze
12
+ US_CORE_VERSION_6_1_0 = '6.1.0'.freeze
13
+ US_CORE_VERSION_7_0_0 = '7.0.0'.freeze
14
+ # Validated against by suites that do not offer the US Core Version option (the v2.2 server suite)
15
+ DEFAULT_US_CORE_VERSION = US_CORE_VERSION_6_1_0
16
+
10
17
  BUNDLE_PROFILES_FOR_OPERATION_TYPE = {
11
18
  submit_request: 'http://hl7.org/fhir/us/davinci-pas/StructureDefinition/profile-pas-request-bundle',
12
19
  submit_response: 'http://hl7.org/fhir/us/davinci-pas/StructureDefinition/profile-pas-response-bundle',
@@ -7,16 +7,20 @@ module DaVinciPASTestKit
7
7
 
8
8
  def verify_pas_subscription(subscription_json_str, ig_version: 'v2.0.1')
9
9
  assert_valid_json(subscription_json_str)
10
- subscription = JSON.parse(subscription_json_str)
10
+ add_pas_subscription_messages(JSON.parse(subscription_json_str), ig_version:)
11
11
 
12
+ assert messages.none? { |msg| msg[:type] == 'error' },
13
+ 'The Subscription does not conform to PAS requirements - see messages for details.'
14
+ end
15
+
16
+ # Adds a message for each PAS requirement that the parsed Subscription does not meet without failing the
17
+ # test, so that callers checking several Subscriptions can report on all of them.
18
+ def add_pas_subscription_messages(subscription, ig_version: 'v2.0.1')
12
19
  if ig_version == 'v2.0.1'
13
20
  verify_pas_subscription_v201(subscription)
14
21
  else
15
22
  verify_pas_subscription_v221(subscription)
16
23
  end
17
-
18
- assert messages.none? { |msg| msg[:type] == 'error' },
19
- 'The Subscription does not conform to PAS requirements - see messages for details.'
20
24
  end
21
25
 
22
26
  private
@@ -0,0 +1,141 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DaVinciPASTestKit
4
+ # Validates that resources referenced within a FHIR resource are present exactly once in a
5
+ # given set of bundle entries. Kept independent of PasBundleValidation's profile-conformance
6
+ # logic so it can be included on its own (e.g. by tests that only need reference-presence
7
+ # checks, such as PASClientClaimUpdateReferencedResourcesTest).
8
+ module ReferencedResourcePresenceValidation
9
+ # This method traverses references within a FHIR resource, ensuring that referenced resources
10
+ # are populated in the bundle. It also enforces that a referenced resource appears only once in the bundle,
11
+ # as required by the PAS IG.
12
+ # @param target_resource [FHIR::Model] The FHIR resource to traverse and validate.
13
+ # @param base_url [String] The server base url.
14
+ # @param resources_to_match [Array<FHIR:Bundle:Entry] The list of FHIR bundle entries to match references against.
15
+ # @param skip_claim_related [Boolean] When true, a Claim's `related` element is not traversed. This is set
16
+ # for any Claim reached by following a reference (i.e. a non-primary Claim in a Claim Update chain), whose
17
+ # own Claim.related.claim (the grandparent) is deliberately omitted from the Bundle per spec-65/66. The
18
+ # primary Claim passed in by the caller keeps `skip_claim_related: false`, so its parent reference - and the
19
+ # parent's own referenced resources - are still checked.
20
+ # @return [Array<String>] The error messages found during traversal.
21
+ def check_presence_of_referenced_resources(target_resource, base_url, resources_to_match,
22
+ skip_claim_related: false)
23
+ # Indexed once per call rather than linearly scanned per reference encountered during the
24
+ # traversal below. Grouped (not a plain Hash) so a fullUrl appearing on more than one entry
25
+ # is still detected as an error instead of being silently collapsed to the last match.
26
+ entries_by_full_url = resources_to_match.group_by(&:fullUrl)
27
+ traverse_referenced_resources(target_resource, base_url, entries_by_full_url,
28
+ skip_claim_related:, errors: [])
29
+ end
30
+
31
+ # ClaimResponse.request is a back-reference to the submitted Claim. The PAS IG response bundle
32
+ # profile (profile-pas-response-bundle) has no required Claim entry slice, so the Claim need
33
+ # not be present in the response bundle. Skipping here matches the identical guard in
34
+ # ResponseGenerator#referenced_entities, which also skips ClaimResponse.request when
35
+ # building mock response bundles.
36
+ def claim_response_request_attr?(resource, attr)
37
+ attr.to_s == 'request' &&
38
+ resource.respond_to?(:resourceType) &&
39
+ resource.resourceType == 'ClaimResponse'
40
+ end
41
+
42
+ # Claim.related.claim points to the Claim being updated. For a non-primary Claim in a Claim Update
43
+ # chain (one reached by following a reference), that prior Claim is the grandparent, which spec-65/66
44
+ # require to be omitted from the Bundle. Used with the `skip_claim_related` flag so the generic
45
+ # reference-presence check does not flag the deliberately-omitted grandparent as missing.
46
+ def claim_related_attr?(resource, attr)
47
+ attr.to_s == 'related' &&
48
+ resource.respond_to?(:resourceType) &&
49
+ resource.resourceType == 'Claim'
50
+ end
51
+
52
+ # Generates a message for a resource that appears more than once in a bundle.
53
+ #
54
+ # @param reference_resource_type [String] The resource type being referenced.
55
+ # @param reference_resource_id [String] The resource ID being referenced.
56
+ # @param total_matches [Integer] The total number of matches found in the bundle.
57
+ #
58
+ # This method generates an error message when a referenced resource appears more than once
59
+ # in a FHIR bundle, which is not allowed.
60
+ def resource_shall_appear_once_message(absolute_ref, total_matches)
61
+ " The referenced #{absolute_ref} resource SHALL appear exactly once in the Bundle, but found #{total_matches}."
62
+ end
63
+
64
+ def absolute_url(reference, base_url)
65
+ return if reference.blank?
66
+ return reference if base_url.blank? || reference.starts_with?('urn:uuid:') || URI(reference).absolute?
67
+
68
+ "#{base_url}/#{reference}"
69
+ rescue URI::InvalidURIError
70
+ # left as-is so it is reported as not found in the Bundle rather than erroring the validation
71
+ reference
72
+ end
73
+
74
+ # Extracts the base URL from an absolute URL by removing the resource type and ID.
75
+ # Expects a non-versioned [base]/[type]/[id] url, as Bundle fullUrls must be (bdl-8).
76
+ # @param absolute_url [String] The absolute URL.
77
+ # @return [String] The base URL, or an empty string if the url is not an absolute url with a host
78
+ # (e.g., a urn:uuid, a relative url, or an invalid url - reported separately by the fullUrl checks).
79
+ def extract_base_url(absolute_url)
80
+ return '' if absolute_url.blank?
81
+
82
+ # Use the raw components rather than URI#authority, which drops an explicit default port (e.g., :443)
83
+ # and userinfo, so that the base URL is exactly what the client sent and relative references resolved
84
+ # against it match the Bundle's fullUrls.
85
+ scheme, userinfo, host, port, _registry, path = URI::RFC3986_PARSER.split(absolute_url)
86
+ return '' if scheme.blank? || host.blank?
87
+
88
+ authority = "#{"#{userinfo}@" if userinfo.present?}#{host}#{":#{port}" if port.present?}"
89
+
90
+ # Split the path segments and remove the last two segments (resource type and id)
91
+ base_path = path.split('/')[0...-2].join('/')
92
+
93
+ "#{scheme}://#{authority}#{base_path}"
94
+ rescue URI::InvalidURIError
95
+ ''
96
+ end
97
+
98
+ private
99
+
100
+ # Recursive worker for check_presence_of_referenced_resources; errors is threaded through the
101
+ # recursive calls and is private so callers can't pass in (and so corrupt) their own accumulator.
102
+ # entries_by_full_url is a fullUrl => Array<Bundle::Entry> index built once by the public method,
103
+ # so each reference encountered is an O(1) lookup instead of a linear scan of every entry.
104
+ def traverse_referenced_resources(target_resource, base_url, entries_by_full_url, skip_claim_related:, errors:)
105
+ return errors if target_resource.blank?
106
+
107
+ if target_resource.is_a?(FHIR::Reference) && target_resource.reference.present?
108
+ ref = target_resource.reference
109
+ absolute_ref = absolute_url(ref, base_url)
110
+ matching_resources = entries_by_full_url[absolute_ref] || []
111
+
112
+ if matching_resources.length != 1
113
+ errors << resource_shall_appear_once_message(absolute_ref, matching_resources.length)
114
+ end
115
+
116
+ if matching_resources.length.positive?
117
+ # A resource reached by following a reference is an included resource, not the primary Claim
118
+ # being validated; if it is itself a Claim Update, its referenced grandparent Claim is omitted.
119
+ traverse_referenced_resources(matching_resources.first.resource, base_url, entries_by_full_url,
120
+ skip_claim_related: true, errors:)
121
+ end
122
+ else
123
+ target_resource.source_hash.each_key do |attr|
124
+ next if claim_response_request_attr?(target_resource, attr)
125
+ next if skip_claim_related && claim_related_attr?(target_resource, attr)
126
+
127
+ value = target_resource.send(attr.to_sym)
128
+ if value.is_a?(FHIR::Model)
129
+ traverse_referenced_resources(value, base_url, entries_by_full_url, skip_claim_related:, errors:)
130
+ elsif value.is_a?(Array) && value.all?(FHIR::Model)
131
+ value.each do |elmt|
132
+ traverse_referenced_resources(elmt, base_url, entries_by_full_url, skip_claim_related:, errors:)
133
+ end
134
+ end
135
+ end
136
+ end
137
+
138
+ errors
139
+ end
140
+ end
141
+ end
@@ -2,16 +2,18 @@ require_relative 'fhirpath_utils'
2
2
 
3
3
  module DaVinciPASTestKit
4
4
  # Selection of a response from a tester-provided list of candidates. Each candidate
5
- # is either a bare FHIR Bundle or a plain JSON wrapper object pairing the response
6
- # Bundle ("bundle") with the selection criteria ("criteria") that the incoming
7
- # request must meet for that Bundle to be returned. The wrapper is plain JSON
8
- # rather than FHIR because Bundle resources cannot carry top-level extensions.
9
- # Candidates are handled as parsed JSON hashes; the inner Bundle is a clean FHIR
10
- # resource and needs no modification before use.
5
+ # is either a bare response resource - a FHIR Bundle, or a Parameters wrapping Bundles
6
+ # (e.g., a v2.2.1 $inquire response) - or a plain JSON wrapper object pairing the
7
+ # response resource ("bundle") with the selection criteria ("criteria") that the
8
+ # incoming request must meet for that response to be returned. The wrapper is plain
9
+ # JSON rather than FHIR because Bundle resources cannot carry top-level extensions.
10
+ # Candidates are handled as parsed JSON hashes; the inner response resource is a clean
11
+ # FHIR resource and needs no modification before use.
11
12
  # Requires FhirpathUtils and access to `result` (Inferno::Entities::Result).
12
13
  module ResponseSelectionUtils
13
14
  CRITERIA_KEY = 'criteria'.freeze
14
15
  BUNDLE_KEY = 'bundle'.freeze
16
+ RESPONSE_RESOURCE_TYPES = %w[Bundle Parameters].freeze
15
17
  REQUEST_RANGE_KEY = 'requestRange'.freeze
16
18
  FHIRPATH_KEY = 'fhirpath'.freeze
17
19
 
@@ -19,16 +21,16 @@ module DaVinciPASTestKit
19
21
  # Candidate handling
20
22
  # ***********************************************************************
21
23
 
22
- def bare_bundle?(entity)
23
- entity.is_a?(Hash) && entity['resourceType'] == 'Bundle'
24
+ def bare_response?(entity)
25
+ entity.is_a?(Hash) && RESPONSE_RESOURCE_TYPES.include?(entity['resourceType'])
24
26
  end
25
27
 
26
- def entity_bundle(entity)
27
- bare_bundle?(entity) ? entity : entity[BUNDLE_KEY]
28
+ def entity_response(entity)
29
+ bare_response?(entity) ? entity : entity[BUNDLE_KEY]
28
30
  end
29
31
 
30
32
  def entity_criteria(entity)
31
- criteria = bare_bundle?(entity) ? nil : entity[CRITERIA_KEY]
33
+ criteria = bare_response?(entity) ? nil : entity[CRITERIA_KEY]
32
34
  criteria.is_a?(Hash) ? criteria : {}
33
35
  end
34
36
 
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'subscriptions_test_kit/tags'
4
+
3
5
  module DaVinciPASTestKit
4
6
  AUTH_TAG = 'pas_auth'
5
7
  SUBMIT_TAG = 'pas_submit'
@@ -15,12 +17,15 @@ module DaVinciPASTestKit
15
17
  CLAIM_UPDATE_ADD_ITEM_TAG = 'pas_claim_update_add_item'
16
18
  CLAIM_UPDATE_MODIFY_CANCEL_TAG = 'pas_claim_update_modify_cancel'
17
19
  CLAIM_UPDATE_CANCEL_ALL_TAG = 'pas_claim_update_cancel_all'
18
- NOTIFICATION_TAG = 'subscription_notification'
19
- SUBSCRIPTION_CREATE_TAG = 'subscription_create'
20
- SUBSCRIPTION_READ_TAG = 'subscription_read'
21
- SUBSCRIPTION_STATUS_TAG = 'subscription_status'
22
- REST_HOOK_HANDSHAKE_NOTIFICATION_TAG = 'rest_hook_handshake_notification'
23
- REST_HOOK_EVENT_NOTIFICATION_TAG = 'rest_hook_event_notification'
20
+ # Subscription tags must match the Subscriptions Test Kit, whose tests load requests tagged by PAS endpoints
21
+ NOTIFICATION_TAG = SubscriptionsTestKit::NOTIFICATION_TAG
22
+ SUBSCRIPTION_CREATE_TAG = SubscriptionsTestKit::SUBSCRIPTION_CREATE_TAG
23
+ SUBSCRIPTION_READ_TAG = SubscriptionsTestKit::SUBSCRIPTION_READ_TAG
24
+ SUBSCRIPTION_UPDATE_TAG = SubscriptionsTestKit::SUBSCRIPTION_UPDATE_TAG
25
+ SUBSCRIPTION_DELETE_TAG = SubscriptionsTestKit::SUBSCRIPTION_DELETE_TAG
26
+ SUBSCRIPTION_STATUS_TAG = SubscriptionsTestKit::SUBSCRIPTION_STATUS_TAG
27
+ REST_HOOK_HANDSHAKE_NOTIFICATION_TAG = SubscriptionsTestKit::REST_HOOK_HANDSHAKE_NOTIFICATION_TAG
28
+ REST_HOOK_EVENT_NOTIFICATION_TAG = SubscriptionsTestKit::REST_HOOK_EVENT_NOTIFICATION_TAG
24
29
  OTHER_AUTH_TAG = 'other_auth'
25
30
  OPERATION_FAILURE_WORKFLOW_TAG = 'pas_operation_failure_workflow'
26
31
  PROCESSING_ERROR_WORKFLOW_TAG = 'pas_processing_error_workflow'
@@ -0,0 +1,16 @@
1
+ module DaVinciPASTestKit
2
+ # Shared FHIR resource validator configuration for the PAS suites.
3
+
4
+ # IGs loaded by the validators in each version's suites.
5
+ V201_VALIDATOR_IGS = ['hl7.fhir.us.davinci-pas#2.0.1'].freeze
6
+ V221_VALIDATOR_IGS = ['hl7.fhir.us.davinci-pas#2.2.1', 'hl7.fhir.us.core#6.1.0'].freeze
7
+
8
+ # The validator for resources Inferno sends to the system under test, which may be tester-provided.
9
+ # It doesn't allow extensions beyond those defined in the IGs it loads, so that the system under
10
+ # test drives its behavior only from standard information. Currently defined by the client suites.
11
+ # Bundles should be validated with it using PasBundleValidation#perform_no_custom_extensions_validation.
12
+ NO_CUSTOM_EXTENSIONS_VALIDATOR = :no_custom_extensions
13
+
14
+ # SNOMED CT US Edition, used by the validators for SNOMED CT code validation.
15
+ SNOMED_CT_US_EDITION = '731000124108'.freeze
16
+ end
@@ -222,6 +222,10 @@ module DaVinciPASTestKit
222
222
  ].freeze
223
223
  # rubocop:enable Layout/LineLength
224
224
 
225
+ # Validator messages are of the form `<ResourceType>: <FHIRPath>: <message>`. This prefix is
226
+ # stripped before matching messages against the suppressions.
227
+ VALIDATOR_MESSAGE_LOCATION_PREFIX = /\A\S+: \S+: /
228
+
225
229
  V221_SUPPRESSED_MESSAGES = Regexp.union(V221_SUPPRESSIONS)
226
230
  V201_SUPPRESSED_MESSAGES = Regexp.union(LEGACY_SUPPRESSIONS + V221_SUPPRESSIONS)
227
231
  end
@@ -41,7 +41,7 @@
41
41
  ]
42
42
  },
43
43
  {
44
- "name": "Approval Workflow",
44
+ "name": "Approval Scenario",
45
45
  "item": [
46
46
  {
47
47
  "name": "Prior Auth Request For Approval",
@@ -81,7 +81,7 @@
81
81
  ]
82
82
  },
83
83
  {
84
- "name": "Denial Workflow",
84
+ "name": "Denial Scenario",
85
85
  "item": [
86
86
  {
87
87
  "name": "Prior Auth Request For Denial",
@@ -121,7 +121,7 @@
121
121
  ]
122
122
  },
123
123
  {
124
- "name": "Pended Workflow",
124
+ "name": "Pended Scenario",
125
125
  "item": [
126
126
  {
127
127
  "name": "Prior Auth Request For Pended",
@@ -6,6 +6,7 @@ module Inferno
6
6
  200,
7
7
  {
8
8
  'Access-Control-Allow-Origin' => '*',
9
+ 'Access-Control-Allow-Methods' => 'GET, POST, PUT, DELETE, OPTIONS',
9
10
  'Access-Control-Allow-Headers' => 'Content-Type, Authorization'
10
11
  },
11
12
  ['']
@@ -12,7 +12,7 @@ module DaVinciPASTestKit
12
12
  new(ig_metadata, 'submit', submit_request_profiles, base_client_output_dir).generate
13
13
 
14
14
  submit_response_profiles = ig_metadata.profiles.select do |profile|
15
- MustSupportTargetProfiles.submit_response_profile?(profile)
15
+ MustSupportTargetProfiles.client_submit_response_profile?(profile)
16
16
  end
17
17
  new(ig_metadata, 'submit', submit_response_profiles, base_client_output_dir, 'response').generate
18
18
 
@@ -22,7 +22,7 @@ module DaVinciPASTestKit
22
22
  new(ig_metadata, 'inquire', inquire_request_profiles, base_client_output_dir).generate
23
23
 
24
24
  inquire_response_profiles = ig_metadata.profiles.select do |profile|
25
- MustSupportTargetProfiles.inquire_response_profile?(profile)
25
+ MustSupportTargetProfiles.client_inquire_response_profile?(profile)
26
26
  end
27
27
  new(ig_metadata, 'inquire', inquire_response_profiles, base_client_output_dir, 'response').generate
28
28
  end
@@ -147,17 +147,25 @@ module DaVinciPASTestKit
147
147
  ig_version == 'v2.2.1'
148
148
  end
149
149
 
150
+ # for now, disabling the attestation option and optional response tests. they are expected to be
151
+ # re-enabled for some ig versions in some form in the future, so leaving the structure in place.
152
+ def enable_lax_checks?
153
+ false
154
+ end
155
+
150
156
  # The Claim (request) / ClaimResponse (response) profiles remain mandatory standalone tests.
151
157
  def mandatory_profile?(profile_metadata)
152
158
  %w[Claim ClaimResponse].include?(profile_metadata.resource)
153
159
  end
154
160
 
155
- # Response groups: pairs each per-profile test id with whether it should be marked optional.
161
+ # Response groups: pairs each per-profile test id with whether it should be marked optional and
162
+ # whether it verifies response_verifies_requirement (only the ClaimResponse profile does).
156
163
  def profile_tests
157
164
  profiles.map do |profile_metadata|
158
165
  {
159
166
  id: test_id_for_profile(profile_metadata),
160
- optional: optional_must_support_enabled? && !mandatory_profile?(profile_metadata)
167
+ optional: optional_must_support_enabled? && !mandatory_profile?(profile_metadata) && enable_lax_checks?,
168
+ verifies_response_requirement: mandatory_profile?(profile_metadata)
161
169
  }
162
170
  end
163
171
  end
@@ -191,24 +199,47 @@ module DaVinciPASTestKit
191
199
  target_profiles.map { |profile_metadata| test_file_for_profile(profile_metadata) }
192
200
  end
193
201
 
202
+ # Requirement verified by the ClaimResponse per-profile test in response groups
203
+ # (clients SHALL be capable of receiving must support elements).
204
+ def response_verifies_requirement
205
+ case ig_version
206
+ when 'v2.2.1' then 'hl7.fhir.us.davinci-pas_2.2.1@conf-7'
207
+ when 'v2.0.1' then "hl7.fhir.us.davinci-pas_2.0.1@#{operation == 'submit' ? '39' : '40'}"
208
+ end
209
+ end
210
+
194
211
  def verifies_requirements
195
- case "#{operation}_#{ig_version}"
196
- when 'submit_v2.0.1'
197
- return nil if type == 'response'
212
+ case "#{operation}_#{type}_#{ig_version}"
213
+ when 'submit_request_v2.2.1', 'inquire_request_v2.2.1'
214
+ ['hl7.fhir.us.davinci-pas_2.2.1@hrex-conf-1']
215
+ end
216
+ end
198
217
 
199
- ['hl7.fhir.us.davinci-pas_2.0.1@58', 'hl7.fhir.us.davinci-pas_2.0.1@62',
200
- 'hl7.fhir.us.davinci-pas_2.0.1@70', 'hl7.fhir.us.davinci-pas_2.0.1@202']
218
+ # Names of the profiles that must always be demonstrated, e.g. "PAS Claim Inquiry".
219
+ def mandatory_profile_names
220
+ target_profiles = type == 'response' ? profiles : required_profiles
221
+ target_profiles.select { |profile_metadata| mandatory_profile?(profile_metadata) }
222
+ .map(&:profile_name).to_sentence
223
+ end
224
+
225
+ # Only v2.2.1 has optional response tests and the request attestation option.
226
+ def optional_profiles_description
227
+ return '' unless optional_must_support_enabled? && enable_lax_checks?
228
+
229
+ if type == 'response'
230
+ "\nDemonstration of the #{mandatory_profile_names} profile is strictly required\n" \
231
+ 'while all others are optional.'
232
+ else
233
+ "\nFor all profiles other than #{mandatory_profile_names}, testers can attest\n" \
234
+ 'that the missing elements are not supported by their PAS client to pass the tests.'
201
235
  end
202
236
  end
203
237
 
204
238
  def description
205
239
  if type == 'response'
206
240
  <<~DESCRIPTION
207
- Check that `$#{operation}` responses provided to the client contain
208
- all PAS-defined profiles and their must support elements.
209
-
210
- **USER INPUT VALIDATION**: These tests validate responses provided by the tester,
211
- not the system under test. Errors will be treated as skips instead of failures.
241
+ Check that `$#{operation}` responses provided to the PAS client contain
242
+ all PAS-defined profiles and their must support elements.#{optional_profiles_description}
212
243
 
213
244
  For `$#{operation}` responses, this includes the following profiles:
214
245
 
@@ -216,8 +247,8 @@ module DaVinciPASTestKit
216
247
  DESCRIPTION
217
248
  else
218
249
  <<~DESCRIPTION
219
- Check that the client can demonstrate `$#{operation}` requests that contain
220
- all PAS-defined profiles and their must support elements.
250
+ Check that the PAS client demonstrated `$#{operation}` requests that contain
251
+ all PAS-defined profiles and their must support elements.#{optional_profiles_description}
221
252
 
222
253
  For `$#{operation}` requests, this includes the following profiles:
223
254
 
@@ -53,6 +53,11 @@ module DaVinciPASTestKit
53
53
  'PAS Task'
54
54
  ].freeze
55
55
 
56
+ # Response profiles not included in the client must support tests (server tests still include them).
57
+ CLIENT_RESPONSE_EXCLUDED_PROFILES = [
58
+ 'PAS Task'
59
+ ].freeze
60
+
56
61
  REQUEST_PROFILES = [
57
62
  'PAS Medication Request',
58
63
  'PAS Service Request',
@@ -77,6 +82,18 @@ module DaVinciPASTestKit
77
82
  INQUIRY_RESPONSE_REQUIRED_PROFILES.include?(profile_metadata.profile_name)
78
83
  end
79
84
 
85
+ def client_submit_response_profile?(profile_metadata)
86
+ submit_response_profile?(profile_metadata) && !client_response_excluded_profile?(profile_metadata)
87
+ end
88
+
89
+ def client_inquire_response_profile?(profile_metadata)
90
+ inquire_response_profile?(profile_metadata) && !client_response_excluded_profile?(profile_metadata)
91
+ end
92
+
93
+ def client_response_excluded_profile?(profile_metadata)
94
+ CLIENT_RESPONSE_EXCLUDED_PROFILES.include?(profile_metadata.profile_name)
95
+ end
96
+
80
97
  def request_profile?(profile_metadata)
81
98
  REQUEST_PROFILES.include?(profile_metadata.profile_name)
82
99
  end
@@ -26,6 +26,8 @@ module DaVinciPASTestKit
26
26
  end
27
27
  submit_response_profiles.each do |profile|
28
28
  new(ig_metadata, profile, base_server_output_dir, 'response', 'submit').generate
29
+ next if MustSupportTargetProfiles.client_response_excluded_profile?(profile)
30
+
29
31
  new(ig_metadata, profile, base_client_output_dir, 'response', 'submit', 'client').generate
30
32
  end
31
33
 
@@ -37,13 +39,15 @@ module DaVinciPASTestKit
37
39
  end
38
40
  inquiry_response_profiles.each do |profile|
39
41
  new(ig_metadata, profile, base_server_output_dir, 'response', 'inquire').generate
42
+ next if MustSupportTargetProfiles.client_response_excluded_profile?(profile)
43
+
40
44
  new(ig_metadata, profile, base_client_output_dir, 'response', 'inquire', 'client').generate
41
45
  end
42
46
  end
43
47
 
44
- # For v2.2.1 the client request must support tests are collapsed into the attestation tests
48
+ # For v2.2.1 the client request must support tests are collapsed into shared tests
45
49
  # (see ClientMustSupportGroupGenerator), so only the mandatory Claim profile keeps a
46
- # standalone per-profile client request test. Other profiles are covered by the attestation
50
+ # standalone per-profile client request test. Other profiles are covered by the shared
47
51
  # tests and their per-profile client request tests are not generated. Server tests and
48
52
  # earlier IG versions are unaffected.
49
53
  def generate_client_request_test?(ig_metadata, profile)
@@ -127,6 +131,8 @@ module DaVinciPASTestKit
127
131
  ['hl7.fhir.us.davinci-pas_2.0.1@37', 'hl7.fhir.us.davinci-pas_2.0.1@110']
128
132
  when 'server_inquire_response_v2.0.1'
129
133
  ['hl7.fhir.us.davinci-pas_2.0.1@38']
134
+ when 'client_inquire_response_v2.2.1', 'client_submit_response_v2.2.1'
135
+ ['hl7.fhir.us.davinci-pas_2.2.1@hrex-conf-2']
130
136
  end
131
137
  end
132
138
 
@@ -42,7 +42,7 @@ module DaVinciPASTestKit
42
42
 
43
43
  # A sorted, bulleted markdown list of this profile's must support elements (slices, elements,
44
44
  # extensions, and choices), each line prefixed with `indent` spaces. Shared by the per-profile
45
- # must support tests and the collapsed attestation tests so both stay in sync with the metadata.
45
+ # must support tests and the collapsed tests so both stay in sync with the metadata.
46
46
  def must_support_list_string(indent: 8)
47
47
  slice_names = must_supports[:slices].map { |slice| slice[:slice_id] }
48
48
  element_names = must_supports[:elements].map { |element| "#{resource}.#{element[:path]}" }
@@ -46,15 +46,10 @@ module DaVinciPASTestKit
46
46
  "Da Vinci PAS Server Suite #{ig_metadata.ig_version}"
47
47
  end
48
48
 
49
- def ig_identifier
50
- version = ig_metadata.ig_version[1..] # Remove leading 'v'
51
- "hl7.fhir.us.davinci-pas##{version}"
52
- end
53
-
54
- def validator_igs_expression
55
- identifiers = [ig_identifier]
56
- identifiers << 'hl7.fhir.us.core#6.1.0' if ig_metadata.ig_version.start_with?('v2.2')
57
- identifiers.map(&:inspect).join(', ')
49
+ # The validator_configuration.rb constant listing the IGs for this version, e.g. V221_VALIDATOR_IGS.
50
+ # A version without one fails when the generated suite loads.
51
+ def validator_igs_constant
52
+ "V#{ig_metadata.ig_version.delete('v.')}_VALIDATOR_IGS"
58
53
  end
59
54
 
60
55
  # v2.2+ suites use the targeted suppression list; v2.0.1 keeps the legacy list plus the
@@ -24,8 +24,13 @@ module DaVinciPASTestKit
24
24
 
25
25
  <%- if optional_must_support_enabled? -%>
26
26
  <%- if operation == 'submit' -%>
27
+ <%- if enable_lax_checks? -%>
27
28
  # At least one of the PAS request profiles must be present; unobserved must support
28
29
  # elements on the request profiles that are present may be attested as not collected.
30
+ <%- else -%>
31
+ # At least one of the PAS request profiles must be present, with all of its must support
32
+ # elements observed.
33
+ <%- end -%>
29
34
  test from: :pas_client_<%= ig_version_for_id %>_must_support_with_attestation_option do
30
35
  id :pas_client_<%= ig_version_for_id %>_submit_request_profiles_must_support_with_attestation_option
31
36
  title 'At least one instance of a request profile (PAS Medication Request, PAS Service Request, PAS Device Request, or PAS Nutrition Order) is observed with all of its must support elements'
@@ -37,20 +42,30 @@ module DaVinciPASTestKit
37
42
  ],
38
43
  ig_version: '<%= ig_version %>',
39
44
  type: 'request',
40
- operation: '<%= operation %>'
45
+ operation: '<%= operation %>',
46
+ disable_attestation: <%= !enable_lax_checks? %>
41
47
  }
42
48
  )
43
49
  description MustSupportWithAttestationOption.build_description(config.options)
50
+ <%- if verifies_requirements -%>
51
+ verifies_requirements(*verifies_requirements, <%= verifies_requirements.map { |r| "'#{r}'" }.join(', ') %>)
52
+ <%- end -%>
44
53
  end
45
54
 
46
55
  <%- end -%>
47
56
  <%- claim_profiles.each do |profile_metadata| -%>
48
57
  # Mandatory - the <%= profile_metadata.profile_name %> profile must always be demonstrated.
49
- test from: :<%= test_id_for_profile(profile_metadata) %>
58
+ test from: :<%= test_id_for_profile(profile_metadata) %> <%- if verifies_requirements -%> do
59
+ verifies_requirements(*verifies_requirements, <%= verifies_requirements.map { |r| "'#{r}'" }.join(', ') %>)
60
+ end<%- end -%>
50
61
 
51
62
  <%- end -%>
63
+ <%- if enable_lax_checks? -%>
52
64
  # All other <%= operation %> request profiles - unobserved must support elements may be
53
65
  # attested as not collected by the client system.
66
+ <%- else -%>
67
+ # All other <%= operation %> request profiles - all must support elements must be observed.
68
+ <%- end -%>
54
69
  test from: :pas_client_<%= ig_version_for_id %>_must_support_with_attestation_option do
55
70
  id :pas_client_<%= ig_version_for_id %>_<%= operation %>_request_other_must_support_with_attestation_option
56
71
  title '<%= other_profiles_title %>'
@@ -62,10 +77,14 @@ module DaVinciPASTestKit
62
77
  ],
63
78
  ig_version: '<%= ig_version %>',
64
79
  type: 'request',
65
- operation: '<%= operation %>'
80
+ operation: '<%= operation %>',
81
+ disable_attestation: <%= !enable_lax_checks? %>
66
82
  }
67
83
  )
68
84
  description MustSupportWithAttestationOption.build_description(config.options)
85
+ <%- if verifies_requirements -%>
86
+ verifies_requirements(*verifies_requirements, <%= verifies_requirements.map { |r| "'#{r}'" }.join(', ') %>)
87
+ <%- end -%>
69
88
  end
70
89
  <%- else -%>
71
90
  <%- if operation == 'submit' -%>
@@ -1,7 +1,6 @@
1
1
  <%- profile_test_files.each do |file_name| -%>
2
2
  require_relative '<%= file_name %>'
3
3
  <%- end -%>
4
- require_relative '../../<%= ig_version %>/workflows/pas_client_response_attest'
5
4
 
6
5
  module DaVinciPASTestKit
7
6
  module <%= module_name %>
@@ -17,27 +16,19 @@ module DaVinciPASTestKit
17
16
  <%- if profile_test[:optional] -%>
18
17
  test from: :<%= profile_test[:id] %> do
19
18
  optional
19
+ simulation_verification
20
+ end
21
+ <%- elsif profile_test[:verifies_response_requirement] -%>
22
+ test from: :<%= profile_test[:id] %> do
23
+ verifies_requirements(*verifies_requirements, '<%= response_verifies_requirement %>')
24
+ simulation_verification
20
25
  end
21
26
  <%- else -%>
22
27
  test from: :<%= profile_test[:id] %> do
23
- verifies_requirements 'hl7.fhir.us.davinci-pas_2.2.1@conf-7'
28
+ simulation_verification
24
29
  end
25
30
  <%- end -%>
26
31
  <%- end -%>
27
- test from: :pas_client_<%= ig_version_for_id %>_response_attest,
28
- title: 'PAS client handled the $<%= operation %> response must support elements',
29
- description: %(
30
- This test provides the tester an opportunity to verify that their client
31
- correctly processed and used the must support elements present in the
32
- $<%= operation %> responses received from Inferno during these tests.
33
- ),
34
- config: { options: {
35
- workflow_tag: MUST_SUPPORT_WORKFLOW_TAG,
36
- attest_message: "I attest that the client system correctly processed the must support elements " \
37
- "contained in the $<%= operation %> responses received from Inferno and did not error."
38
- } } do
39
- verifies_requirements 'hl7.fhir.us.davinci-pas_2.2.1@conf-8'
40
- end
41
32
  end
42
33
  end
43
34
  end