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
@@ -0,0 +1,245 @@
1
+ require_relative 'request_count_gating'
2
+
3
+ module DaVinciPASTestKit
4
+ module ClientBundleValidationHelper
5
+ include RequestCountGating
6
+
7
+ ###########################################################################
8
+ # Tags and Request Loading
9
+ ###########################################################################
10
+ def request_type_tag
11
+ case operation_name
12
+ when 'inquire'
13
+ INQUIRE_TAG
14
+ when 'submit'
15
+ SUBMIT_TAG
16
+ else
17
+ raise Inferno::Exceptions::TestSuiteImplementationException.new(
18
+ 'PAS Client Bundle Validation Test',
19
+ "invalid operation name #{operation_name}"
20
+ )
21
+ end
22
+ end
23
+
24
+ def workflow_tag
25
+ config.options[:workflow_tag]
26
+ end
27
+
28
+ # workflow_tag may be a single tag or an Array of tags; normalized to an Array so fetch_requests
29
+ # doesn't need its own is_a?(Array) check.
30
+ def workflow_tags
31
+ Array(workflow_tag)
32
+ end
33
+
34
+ def fetch_requests
35
+ tags = workflow_tags.presence || [nil]
36
+ tags.flat_map { |tag| load_tagged_requests(*[request_type_tag, tag].compact) }
37
+ end
38
+
39
+ ###########################################################################
40
+ # Request Count
41
+ ###########################################################################
42
+
43
+ # Raises if there are multiple requests and the test doesn't allow that.
44
+ # Skips if there are no requests and the test doesn't allow that.
45
+ # @param requests [Array<Inferno::Entities::Request>] the requests loaded for this test.
46
+ # @param description [String] identifies the requests in the raised message, e.g.
47
+ # "submit requests".
48
+ def check_request_count(requests, description)
49
+ if requests.length > 1 && !multiple_requests_ok?
50
+ raise Inferno::Exceptions::TestSuiteImplementationException.new(
51
+ 'multiple request handling',
52
+ "#{requests.length} #{description} were tagged for validation by this test, which does not expect " \
53
+ 'more than one.'
54
+ )
55
+ elsif requests.empty?
56
+ if no_requests_ok?
57
+ pass "No #{description} analyzed."
58
+ else
59
+ skip "No #{description} received."
60
+ end
61
+ end
62
+ end
63
+
64
+ ###########################################################################
65
+ # Extraction logic
66
+ ###########################################################################
67
+
68
+ def message_contents(request)
69
+ case message_direction_name
70
+ when 'request'
71
+ request.request_body
72
+ when 'response'
73
+ request.response_body
74
+ else
75
+ raise Inferno::Exceptions::TestSuiteImplementationException.new(
76
+ 'PAS Client Bundle Validation Test',
77
+ "invalid message direction name #{message_direction_name}"
78
+ )
79
+ end
80
+ end
81
+
82
+ def parameters_target_parameter_name
83
+ case message_direction_name
84
+ when 'request'
85
+ 'resource'
86
+ when 'response'
87
+ 'return'
88
+ else
89
+ raise Inferno::Exceptions::TestSuiteImplementationException.new(
90
+ 'PAS Client Bundle Validation Test',
91
+ "invalid message direction name #{message_direction_name}"
92
+ )
93
+ end
94
+ end
95
+
96
+ ###########################################################################
97
+ # Validation logic
98
+ ###########################################################################
99
+
100
+ # Finds Bundles in the requests and determines which ones are non-conformant.
101
+ #
102
+ # Assumes this is the only thing logging to `messages` during this run and that it's
103
+ # called at most once, so every message present afterward is one it logged itself.
104
+ def non_conformant_bundles
105
+ requests = fetch_requests
106
+ check_request_count(requests, "$#{operation_name} #{message_direction_name}s")
107
+ requests.each_with_index { |request, index| validate_request(request, index) }
108
+
109
+ messages
110
+ .select { |message| message[:type] == 'error' }
111
+ .map { |message| message[:message].split(':', 2).first }
112
+ .uniq
113
+ end
114
+
115
+ # Validates one request/response, extracting and checking the Bundle(s) it contains and
116
+ # logging any problem found along the way - this only logs, callers determine what failed
117
+ # from `messages` afterward (see non_conformant_bundles).
118
+ def validate_request(request, index)
119
+ message_label = entity_label("#{message_direction_name.capitalize} #{index + 1}")
120
+ contents = message_contents(request)
121
+ message_resource = resource_from_message_contents(contents, message_label)
122
+ return if message_resource.blank?
123
+
124
+ # extraction varies - provided by test class if not using the default. May contain nil
125
+ # placeholders for positions that held something other than a Bundle (already reported),
126
+ # so Bundle labels keep their original position.
127
+ bundles = bundles_from_message_resource(message_resource, message_label)
128
+ parameters_has_errors?(message_resource, message_label) if response_parameters?(message_resource)
129
+ echoed_from = echo_source_bundle(request)
130
+
131
+ if bundles.length > 1
132
+ bundles.each_with_index do |bundle, bundle_index|
133
+ next if bundle.nil?
134
+
135
+ bundle_label = entity_label("#{message_label.delete_suffix(':')} Bundle #{bundle_index + 1}")
136
+ bundle_has_errors?(bundle, bundle_label, echoed_from)
137
+ end
138
+ else
139
+ bundles.compact.each { |bundle| bundle_has_errors?(bundle, message_label, echoed_from) }
140
+ end
141
+ end
142
+
143
+ # A label identifying one request/response, or a Bundle within one, for use as a message
144
+ # prefix. Always ends in ':' so a logged message's entity can be recovered by splitting on
145
+ # its first ':' - see non_conformant_bundles.
146
+ def entity_label(description)
147
+ "#{description}:"
148
+ end
149
+
150
+ def resource_from_message_contents(contents, label)
151
+ resource = FHIR.from_contents(contents)
152
+ messages << { type: 'error', message: "#{label} Not a FHIR resource." } unless resource.present?
153
+ resource
154
+ rescue StandardError
155
+ messages << { type: 'error', message: "#{label} Invalid JSON." }
156
+ nil
157
+ end
158
+
159
+ # standard Bundle extraction: 1 Bundle, not wrapped in Parameters
160
+ def bundles_from_message_resource(message_resource, message_label)
161
+ case message_resource
162
+ when FHIR::Bundle
163
+ [message_resource]
164
+ when FHIR::Parameters
165
+ messages << { type: 'error',
166
+ message: "#{message_label} expected a Bundle resource, got Parameters." }
167
+ target_parameter_name = parameters_target_parameter_name
168
+ matching_resources = extract_resources_from_parameters(message_resource, target_parameter_name)
169
+
170
+ if matching_resources.empty?
171
+ messages << { type: 'error',
172
+ message: "#{message_label} Parameters resource had no Bundle " \
173
+ "in a '#{target_parameter_name}' entry." }
174
+ []
175
+ elsif matching_resources.length > 1
176
+ messages << { type: 'error',
177
+ message: "#{message_label} Parameters resource had multiple '#{target_parameter_name}' " \
178
+ 'entries, but only one allowed (Bundles not validated).' }
179
+ []
180
+ elsif matching_resources.first.is_a?(FHIR::Bundle)
181
+ [matching_resources.first]
182
+ else
183
+ messages << { type: 'error',
184
+ message: "#{message_label} Parameters resource '#{target_parameter_name}' entry expected to " \
185
+ "contain a Bundle, got #{matching_resources.first&.resourceType}" }
186
+ []
187
+ end
188
+
189
+ else
190
+ messages << { type: 'error',
191
+ message: "#{message_label} expected a Bundle resource, got #{message_resource.resourceType}." }
192
+ []
193
+ end
194
+ end
195
+
196
+ # The request Bundle a response answers, so resources the response echoes back from it may keep
197
+ # the extensions they had in it - see PasBundleValidation#perform_no_custom_extensions_validation. Unwraps
198
+ # Parameters-wrapped requests (v2.2.1 $inquire). Nil when validating requests, or when the
199
+ # request has no Bundle.
200
+ def echo_source_bundle(request)
201
+ return unless message_direction_name == 'response'
202
+
203
+ request_resource = FHIR.from_contents(request.request_body)
204
+ if request_resource.is_a?(FHIR::Parameters)
205
+ request_resource = extract_resources_from_parameters(request_resource, 'resource').first
206
+ end
207
+ request_resource if request_resource.is_a?(FHIR::Bundle)
208
+ rescue StandardError
209
+ nil
210
+ end
211
+
212
+ # A Parameters wrapper Inferno sends around the response Bundles (v2.2.1 $inquire), which may be
213
+ # tester-provided, so it is checked for custom extensions too.
214
+ def response_parameters?(message_resource)
215
+ message_direction_name == 'response' && message_resource.is_a?(FHIR::Parameters)
216
+ end
217
+
218
+ def parameters_has_errors?(parameters, label)
219
+ parameters_messages = perform_no_custom_extensions_parameters_validation(parameters)
220
+ parameters_messages.each { |m| messages << { type: m[:type], message: "#{label} #{m[:message]}" } }
221
+ parameters_messages.any? { |m| m[:type] == 'error' }
222
+ rescue NoMethodError, TypeError, ArgumentError => e
223
+ messages << { type: 'error', message: "#{label} Parameters could not be validated: #{e.message}" }
224
+ true
225
+ end
226
+
227
+ def bundle_has_errors?(bundle, label, echoed_from = nil)
228
+ # Responses are sent by Inferno and may be tester-provided, so custom extensions aren't allowed.
229
+ bundle_messages =
230
+ if message_direction_name == 'response'
231
+ perform_no_custom_extensions_validation(bundle, operation_name, message_direction_name, ig_version,
232
+ echoed_from:)
233
+ else
234
+ perform_bundle_validation(bundle, operation_name, message_direction_name, ig_version)
235
+ end
236
+ bundle_messages.each { |m| messages << { type: m[:type], message: "#{label} #{m[:message]}" } }
237
+ bundle_messages.any? { |m| m[:type] == 'error' }
238
+ rescue NoMethodError, TypeError, ArgumentError => e
239
+ # a malformed Bundle (e.g., an entry with no resource) shouldn't stop the remaining
240
+ # requests/Bundles from being validated
241
+ messages << { type: 'error', message: "#{label} could not be validated: #{e.message}" }
242
+ true
243
+ end
244
+ end
245
+ end
@@ -2,38 +2,104 @@
2
2
 
3
3
  module DaVinciPASTestKit
4
4
  INPUT_CLIENT_ID_LOCKED =
5
- 'The client\'s registered Client Id for use in obtaining access tokens. ' \
5
+ 'The PAS client\'s registered Client Id for use in obtaining access tokens. ' \
6
6
  'Run the **1** Client Registration group to configure this input.'
7
7
  INPUT_SESSION_URL_PATH_LOCKED =
8
8
  'The additional path used to create session-specific endpoints. Run the ' \
9
9
  '**1** Client Registration group to configure this input.'
10
10
 
11
- # Shared documentation for the must support response inputs, which differ only
12
- # in the operation whose responses they configure ('$submit' or '$inquire').
11
+ # Shared text for the "Demonstrate Must Support Coverage" group and its combined $submit/$inquire
12
+ # response_attest test, identical across PAS versions in each client/vX/pas_client_must_support_group.rb
13
+ # other than the requirement each version's attest test verifies.
14
+ MUST_SUPPORT_RECEIVE_GROUP_TITLE = 'Demonstrate Must Support Coverage'
15
+ MUST_SUPPORT_RECEIVE_GROUP_DESCRIPTION =
16
+ %(
17
+ During this group, Inferno will wait while the tester uses the PAS client to
18
+ make `$submit` and `$inquire` operation requests to Inferno demonstrating coverage
19
+ of must support elements not yet demonstrated.
20
+
21
+ Inferno then asks for confirmation that all responses were handled
22
+ without error and verifies the conformance of all requests and responses.
23
+ )
24
+ MUST_SUPPORT_SUBMIT_INQUIRE_ATTEST_TITLE =
25
+ 'PAS client handled the $submit and $inquire responses without erroring'
26
+ MUST_SUPPORT_SUBMIT_INQUIRE_ATTEST_DESCRIPTION =
27
+ %(
28
+ During this test, the tester will verify that the PAS client handled
29
+ the `$submit` and `$inquire` operation responses, making the result available to
30
+ the user without failing or erroring.
31
+ )
32
+ MUST_SUPPORT_SUBMIT_INQUIRE_ATTEST_MESSAGE =
33
+ 'I attest that the PAS client correctly handled the `$submit` and `$inquire` ' \
34
+ 'operation responses received from Inferno during this test, making the details ' \
35
+ 'available to users without errors.'
36
+
37
+ # Text for the v2.2.1 must support group, where the analysis is a separate group from the receive group
38
+ MUST_SUPPORT_COVERAGE_GROUP_TITLE = 'Must Support Coverage'
39
+ MUST_SUPPORT_COVERAGE_GROUP_DESCRIPTION =
40
+ %(
41
+ During this group, Inferno checks that the requests and responses exchanged during the
42
+ PAS scenario group and the most recent run of the "Demonstrate Must Support Coverage" group
43
+ include all required profiles and must support elements.
44
+
45
+ This group can be re-run on its own, for example after re-running another group, without
46
+ repeating the "Demonstrate Must Support Coverage" group or losing the requests made
47
+ during it.
48
+ )
49
+ MUST_SUPPORT_SEPARATE_GROUPS_DESCRIPTION =
50
+ <<~DESCRIPTION
51
+ The "Demonstrate Must Support Coverage" and "Must Support Coverage" groups can be run
52
+ separately. Re-running "Demonstrate Must Support Coverage" replaces the requests made during
53
+ its previous run, while "Must Support Coverage" can be re-run on its own to re-evaluate
54
+ coverage without making those requests again.
55
+ DESCRIPTION
56
+
57
+ def self.must_support_group_description(ig_version, wiki_anchor)
58
+ <<~DESCRIPTION
59
+ During these tests, Inferno will check that the PAS client demonstrates support for
60
+ all required profiles and must support elements. When looking for demonstration of
61
+ these profiles and elements, Inferno will consider requests and responses from
62
+ interactions performed during the PAS scenario group as well as additional ones
63
+ made when executing this group.
64
+
65
+ For additional details on these tests, what they check for, and the requirements
66
+ underlying them, see the ["Client Must Support Tests" section](https://github.com/inferno-framework/davinci-pas-test-kit/wiki/Client-Must-Support)
67
+ of the Da Vinci PAS Test Kit wiki, specifically
68
+ - [Which messages Inferno considers when looking for demonstration of must support elements](https://github.com/inferno-framework/davinci-pas-test-kit/wiki/Client-Must-Support#evaluated-messages).
69
+ - [What PAS clients must demonstrate to pass these #{ig_version} client must support tests](https://github.com/inferno-framework/davinci-pas-test-kit/wiki/Client-Must-Support##{wiki_anchor}).
70
+ DESCRIPTION
71
+ end
72
+
13
73
  def self.ms_responses_input_description(operation)
14
74
  <<~DESCRIPTION
15
- An optional JSON value specifying response Bundles for Inferno to return when
16
- responding to #{operation} requests during must support testing. Provide a single entry
17
- or a list of entries, where each entry is either a FHIR Bundle or a wrapper object
18
- of the form `{"criteria": {...}, "bundle": {...}}` pairing a FHIR Bundle with the
19
- selection criteria that a request must meet for that Bundle to be returned.
20
-
21
- For each request, Inferno will respond with the Bundle of the first entry whose
22
- criteria are all met. An entry with no criteria (including a bare Bundle) matches
23
- any request, and an entry with multiple criteria must meet all of them. If no entry
24
- matches, or none are provided, Inferno will generate a default response. Supported
25
- `criteria` keys:
26
-
27
- - `requestRange`: a string of comma-separated request numbers or ranges, e.g.,
28
- `"1-2,4"`. Met when the number of #{operation} requests received during this test,
29
- counting this one, is among the listed values.
30
- - `fhirpath`: a FHIRPath expression evaluated against the incoming request Bundle.
31
- Met when the expression evaluates to a single truthy value.
32
-
33
- A Bundle may also contain tokens of the form `{{fhirpath}}`, e.g.,
34
- `{{Bundle.entry.first().resource.id}}`. Inferno replaces each token in the returned
35
- Bundle with the result of evaluating the FHIRPath expression against the incoming
36
- request Bundle.
75
+ A JSON list of objects ([format](https://github.com/inferno-framework/davinci-pas-test-kit/wiki/Controlling-Simulated-Responses#response-option-format))
76
+ that Inferno will use to respond to #{operation} requests during the
77
+ *Demonstrate Must Support Coverage* group. For each request, Inferno will
78
+ [select](https://github.com/inferno-framework/davinci-pas-test-kit/wiki/Controlling-Simulated-Responses#response-option-evaluation)
79
+ and [instantiate](https://github.com/inferno-framework/davinci-pas-test-kit/wiki/Controlling-Simulated-Responses#response-instantiation)
80
+ a response to return from this list. If no entries match or instantiation fails, Inferno will
81
+ [mock](https://github.com/inferno-framework/davinci-pas-test-kit/wiki/Controlling-Simulated-Responses#mocked-responses)
82
+ a response.
83
+ DESCRIPTION
84
+ end
85
+
86
+ def self.user_response_input_description(operation, workflow, group_name)
87
+ timing_description = "during the *#{group_name}* group to indicate that the request has been #{workflow}"
88
+ user_response_input_description_base(operation, workflow, timing_description)
89
+ end
90
+
91
+ def self.user_response_input_description_for_update_tests(operation, default_decision, test_name)
92
+ timing_description = "during the *#{test_name}* test in the *Claim Updates* group"
93
+ user_response_input_description_base(operation, default_decision, timing_description)
94
+ end
95
+
96
+ def self.user_response_input_description_base(operation, workflow, timing_description)
97
+ <<~DESCRIPTION
98
+ If provided, this JSON will be [instantiated](https://github.com/inferno-framework/davinci-pas-test-kit/wiki/Controlling-Simulated-Responses#response-instantiation)
99
+ and sent in response to the #{operation} request received #{timing_description}.
100
+ If not provided or if instantiation fails, Inferno will [mock](https://github.com/inferno-framework/davinci-pas-test-kit/wiki/Controlling-Simulated-Responses#mocked-responses)
101
+ the response based on the submitted Claim and indicate it has been #{workflow}. In either
102
+ case, the response will be validated against the PAS Response Bundle profile.
37
103
  DESCRIPTION
38
104
  end
39
105
  end
@@ -1,7 +1,9 @@
1
1
  require_relative '../user_input_response'
2
+ require_relative 'endpoint_session_identification'
3
+ require_relative 'expired_token'
2
4
  require_relative '../response_generator'
5
+ require_relative '../subscription_notification_trigger'
3
6
  require_relative '../client_urls'
4
- require_relative '../jobs/send_pas_subscription_notification'
5
7
  require_relative '../../cross_suite/fhirpath_utils'
6
8
  require_relative '../../cross_suite/response_selection_utils'
7
9
  require 'subscriptions_test_kit'
@@ -11,24 +13,25 @@ module DaVinciPASTestKit
11
13
  class ClaimEndpoint < Inferno::DSL::SuiteEndpoint
12
14
  include SubscriptionsTestKit::SubscriptionsR5BackportR4Client::SubscriptionSimulationUtils
13
15
  include ResponseGenerator
16
+ include SubscriptionNotificationTrigger
14
17
  include ClientURLs
15
18
  include FhirpathUtils
16
19
  include ResponseSelectionUtils
20
+ include EndpointSessionIdentification
21
+ include ExpiredToken
17
22
 
18
23
  # override the one from URLs to make it version specific
19
24
  def suite_id
20
25
  request.path.split('/custom/')[1].split('/')[0] # request.path = {base inferno path}/custom/{suite_id}/...
21
26
  end
22
27
 
23
- def test_run_identifier
24
- return request.params[:session_path] if request.params[:session_path].present?
25
-
26
- UDAPSecurityTestKit::MockUDAPServer.issued_token_to_client_id(
27
- request.headers['authorization']&.delete_prefix('Bearer ')
28
- )
29
- end
28
+ error_response_format :operation_outcome
30
29
 
31
30
  def tags
31
+ # Requests rejected for an expired token or a rejected $inquire are not treated as
32
+ # submissions of any workflow.
33
+ return [] if expired_token? || !operation_enabled?
34
+
32
35
  operation_tag = operation == 'submit' ? SUBMIT_TAG : INQUIRE_TAG
33
36
  workflow_tag = WORKFLOW_TAG_MAP[workflow]
34
37
 
@@ -52,6 +55,13 @@ module DaVinciPASTestKit
52
55
  test.config.options[:suppress_notifications] == true
53
56
  end
54
57
 
58
+ # Tests must opt in to each operation with the submit_enabled and inquire_enabled options. Inferno responds
59
+ # to every request for an operation that is not enabled with an OperationOutcome, without tagging the
60
+ # request or continuing the test.
61
+ def operation_enabled?
62
+ %w[inquire submit].include?(operation) && test.config.options[:"#{operation}_enabled"] == true
63
+ end
64
+
55
65
  def workflow
56
66
  case test.id
57
67
  when /.*pended.*/
@@ -70,7 +80,7 @@ module DaVinciPASTestKit
70
80
  end
71
81
 
72
82
  def must_support_workflow?
73
- test.id =~ /.*must_support.*/ || test.id =~ /.*gather_must_support.*/
83
+ test.id =~ /.*must_support.*/
74
84
  end
75
85
 
76
86
  WORKFLOW_TAG_MAP = {
@@ -87,6 +97,11 @@ module DaVinciPASTestKit
87
97
 
88
98
  response.format = :json
89
99
 
100
+ unless operation_enabled?
101
+ make_rejected_operation_response
102
+ return
103
+ end
104
+
90
105
  # Handle the operation failure and processing error workflows, which require a user-provided response.
91
106
  if workflow == :operation_failure
92
107
  make_operation_failure_response
@@ -136,10 +151,11 @@ module DaVinciPASTestKit
136
151
  end
137
152
 
138
153
  def update_result
139
- if UDAPSecurityTestKit::MockUDAPServer.request_has_expired_token?(request)
154
+ if expired_token?
140
155
  UDAPSecurityTestKit::MockUDAPServer.update_response_for_expired_token(response, 'Bearer token')
141
156
  return
142
157
  end
158
+ return unless operation_enabled? # keep waiting for the request that the test is looking for
143
159
 
144
160
  results_repo.update_result(result.id, 'pass') unless test.config.options[:accepts_multiple_requests]
145
161
  end
@@ -152,19 +168,29 @@ module DaVinciPASTestKit
152
168
  private
153
169
 
154
170
  # Resolves the user-provided response, using criteria-based selection for must support
155
- # workflows or the single-response approach for other workflows.
171
+ # workflows or the single-response approach for other workflows
172
+ # and replaces {{fhirpath}} tokens with values from the request.
156
173
  def resolve_user_response(req_bundle)
157
- if must_support_workflow?
158
- select_must_support_response(req_bundle)
159
- else
160
- UserInputResponse.user_inputted_response(test, operation, result)
161
- end
174
+ user_response = if must_support_workflow?
175
+ select_must_support_response(req_bundle)
176
+ else
177
+ UserInputResponse.user_inputted_response(test, operation, result)
178
+ end
179
+
180
+ return nil unless user_response.present?
181
+
182
+ replace_tokens(user_response, req_bundle)
183
+ rescue FhirpathUtils::FhirpathServiceError => e
184
+ add_result_warning(
185
+ 'Unable to instantiate a tester-provided response, so Inferno will generate a default response: ' \
186
+ "#{e.message}"
187
+ )
188
+ nil
162
189
  end
163
190
 
164
191
  # Selects the first tester-provided response candidate whose selection criteria all
165
192
  # match the incoming request, extracts its response Bundle (unwrapping it when the
166
- # candidate pairs the Bundle with criteria), and replaces {{fhirpath}} tokens with
167
- # values from the request. Returns nil, causing Inferno to generate a default
193
+ # candidate pairs the Bundle with criteria). Returns nil, causing Inferno to generate a default
168
194
  # response, if no candidates are provided or none match. Problems with the
169
195
  # tester-provided input or the FHIRPath service also result in nil, with a warning
170
196
  # on the waiting test so that the tester can see what went wrong.
@@ -188,20 +214,22 @@ module DaVinciPASTestKit
188
214
  "Selected tester-provided response bundle #{selected_index + 1} of #{candidates.length} " \
189
215
  "for #{operation_url_suffix} request ##{request_number}."
190
216
  )
191
- replace_tokens(entity_bundle(candidates[selected_index]), req_bundle)
217
+ entity_response(candidates[selected_index])
192
218
  rescue UserInputResponse::InvalidInputError, FhirpathUtils::FhirpathServiceError => e
193
219
  add_result_warning(
194
- "Unable to select a tester-provided response, so Inferno will generate a default response. #{e.message}"
220
+ 'Unable to select a tester-provided response, so Inferno will generate a default response: ' \
221
+ "#{e.message}"
195
222
  )
196
223
  nil
197
224
  end
198
225
 
199
226
  # Replaces {{fhirpath}} tokens using values from the incoming request, round-tripping
200
- # the result through the FHIR model to normalize it. If a replaced value breaks the
201
- # JSON structure, the result cannot be returned as a FHIR response, so nil is returned
202
- # with a warning on the waiting test and Inferno generates a default response.
227
+ # the result through the FHIR model to normalize it. The response may be a parsed hash
228
+ # (must support workflow) or the raw JSON string of a single input. If a replaced value
229
+ # breaks the JSON structure, the result cannot be returned as a FHIR response, so nil is
230
+ # returned with a warning on the waiting test and Inferno generates a default response.
203
231
  def replace_tokens(bundle_hash, req_bundle)
204
- bundle_json = bundle_hash.to_json
232
+ bundle_json = bundle_hash.is_a?(String) ? bundle_hash : bundle_hash.to_json
205
233
  replaced = replace_tokens_in_string(bundle_json, req_bundle)
206
234
  return bundle_json if replaced.equal?(bundle_json)
207
235
 
@@ -252,68 +280,80 @@ module DaVinciPASTestKit
252
280
  end
253
281
 
254
282
  def make_processing_error_response
255
- user_provided_bundle = UserInputResponse.user_inputted_response(test, operation, result)
256
- unless user_provided_bundle.present?
257
- response.status = 400
258
- response.body = FHIR::OperationOutcome.new(
259
- issue: FHIR::OperationOutcome::Issue.new(
260
- severity: 'fatal', code: 'required',
261
- details: FHIR::CodeableConcept.new(
262
- text: 'The processing_error_response input is required for this test and was not provided.'
263
- )
264
- )
265
- ).to_json
283
+ unless UserInputResponse.user_inputted_response(test, operation, result).present?
284
+ error_outcome_response('The processing_error_response input is required for this test and was not provided.')
266
285
  return
267
286
  end
268
287
 
288
+ req_bundle = FHIR.from_contents(request.body.string)
289
+ if req_bundle.blank?
290
+ handle_missing_required_elements(nil, response)
291
+ return
292
+ end
293
+
294
+ # Unlike other workflows, there is no mocked response to fall back to when instantiation fails.
295
+ instantiated_response = resolve_user_response(req_bundle)
296
+ if instantiated_response.blank?
297
+ error_outcome_response('The processing_error_response input could not be instantiated. ' \
298
+ 'See the warnings on the waiting test for details.')
299
+ return
300
+ end
301
+
302
+ claim_entry = req_bundle.entry&.find { |e| e&.resource&.resourceType == 'Claim' }
269
303
  response.status = 200
270
- response.body = user_provided_bundle
304
+ response.body = update_tester_provided_response(instantiated_response, claim_entry&.fullUrl, operation,
305
+ ig_version)
306
+ end
307
+
308
+ def make_rejected_operation_response
309
+ response.status = 501
310
+ response.body = FHIR::OperationOutcome.new(
311
+ issue: FHIR::OperationOutcome::Issue.new(
312
+ severity: 'error', code: 'not-supported',
313
+ details: FHIR::CodeableConcept.new(
314
+ text: "Inferno does not support the $#{operation} operation during this test."
315
+ )
316
+ )
317
+ ).to_json
318
+ end
319
+
320
+ def error_outcome_response(text)
321
+ response.status = 400
322
+ response.body = FHIR::OperationOutcome.new(
323
+ issue: FHIR::OperationOutcome::Issue.new(
324
+ severity: 'fatal', code: 'required', details: FHIR::CodeableConcept.new(text:)
325
+ )
326
+ ).to_json
271
327
  end
272
328
 
273
329
  def operation
274
330
  request.path.split('$').last
275
331
  end
276
332
 
277
- def start_notification_job(response_bundle_json, decision, generated_claim_response_uuid)
278
- notification_bearer_token = client_access_token_input(result)
279
- notification_contents = notification_json(response_bundle_json, decision, generated_claim_response_uuid)
333
+ # Helper methods for SubscriptionNotificationTrigger
280
334
 
281
- Inferno::Jobs.perform(Jobs::SendPASSubscriptionNotification, test_run.id, test_run.test_session_id, result.id,
282
- notification_bearer_token, notification_contents, test_run_identifier, suite_id,
283
- ig_version)
335
+ def notification_test_run_id
336
+ test_run.id
284
337
  end
285
338
 
286
- def notification_json(response_bundle_json, decision, generated_claim_response_uuid)
287
- user_inputted_notification_json =
288
- JSON.parse(result.input_json).find { |i| i['name'] == 'notification_bundle' }['value']
339
+ def notification_test_session_id
340
+ test_run.test_session_id
341
+ end
289
342
 
290
- if user_inputted_notification_json.present?
291
- update_tester_provided_notification(user_inputted_notification_json, generated_claim_response_uuid)
292
- else
293
- generate_notification(response_bundle_json, decision)
294
- end
343
+ def notification_result_id
344
+ result.id
295
345
  end
296
346
 
297
- def generate_notification(response_bundle_json, decision)
298
- subscription = find_subscription(test_run.test_session_id, as_json: true)
299
- subscription_reference = "#{fhir_subscription_url}/#{subscription['id']}"
300
- subscription_topic = subscription['criteria']
301
-
302
- if find_subscription_content_type(subscription) == 'full-resource'
303
- mock_full_resource_notification_bundle(response_bundle_json, subscription_reference, subscription_topic,
304
- decision, ig_version)
305
- else # assume id-only since empty not allowed - if asked for empty, other failures will occur
306
- mock_id_only_notification_bundle(response_bundle_json, subscription_reference, subscription_topic,
307
- ig_version)
308
- end
347
+ def notification_bearer_token
348
+ client_access_token_input(result)
349
+ end
350
+
351
+ def tester_notification_bundle
352
+ notification_bundle_input(result)
309
353
  end
310
354
 
311
- def find_subscription_content_type(subscription)
312
- content_ext = subscription.dig('channel', '_payload', 'extension')
313
- &.find do |ext|
314
- ext['url'] == 'http://hl7.org/fhir/uv/subscriptions-backport/StructureDefinition/backport-payload-content'
315
- end
316
- content_ext&.dig('valueCode')
355
+ def client_subscription_json
356
+ find_subscription(test_run.test_session_id, as_json: true)
317
357
  end
318
358
  end
319
359
  end