davinci_crd_test_kit 0.9.0 → 0.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/lib/davinci_crd_test_kit/card_responses/propose_alternate_request.json +2 -52
  3. data/lib/davinci_crd_test_kit/card_responses/request_form_completion.json +46 -31
  4. data/lib/davinci_crd_test_kit/cards_validation.rb +8 -4
  5. data/lib/davinci_crd_test_kit/client_hooks_group.rb +22 -660
  6. data/lib/davinci_crd_test_kit/client_tests/appointment_book_receive_request_test.rb +17 -6
  7. data/lib/davinci_crd_test_kit/client_tests/client_appointment_book_group.rb +70 -0
  8. data/lib/davinci_crd_test_kit/client_tests/client_encounter_discharge_group.rb +71 -0
  9. data/lib/davinci_crd_test_kit/client_tests/client_encounter_start_group.rb +70 -0
  10. data/lib/davinci_crd_test_kit/client_tests/client_order_dispatch_group.rb +70 -0
  11. data/lib/davinci_crd_test_kit/client_tests/client_order_select_group.rb +72 -0
  12. data/lib/davinci_crd_test_kit/client_tests/client_order_sign_group.rb +71 -0
  13. data/lib/davinci_crd_test_kit/client_tests/decode_auth_token_test.rb +43 -23
  14. data/lib/davinci_crd_test_kit/client_tests/encounter_discharge_receive_request_test.rb +19 -6
  15. data/lib/davinci_crd_test_kit/client_tests/encounter_start_receive_request_test.rb +18 -6
  16. data/lib/davinci_crd_test_kit/client_tests/hook_request_optional_fields_test.rb +26 -10
  17. data/lib/davinci_crd_test_kit/client_tests/hook_request_required_fields_test.rb +20 -11
  18. data/lib/davinci_crd_test_kit/client_tests/hook_request_valid_context_test.rb +14 -10
  19. data/lib/davinci_crd_test_kit/client_tests/hook_request_valid_prefetch_test.rb +27 -110
  20. data/lib/davinci_crd_test_kit/client_tests/order_dispatch_receive_request_test.rb +18 -6
  21. data/lib/davinci_crd_test_kit/client_tests/order_select_receive_request_test.rb +18 -6
  22. data/lib/davinci_crd_test_kit/client_tests/order_sign_receive_request_test.rb +18 -6
  23. data/lib/davinci_crd_test_kit/client_tests/retrieve_jwks_test.rb +66 -29
  24. data/lib/davinci_crd_test_kit/client_tests/submitted_response_validation.rb +44 -0
  25. data/lib/davinci_crd_test_kit/client_tests/token_header_test.rb +45 -14
  26. data/lib/davinci_crd_test_kit/client_tests/token_payload_test.rb +43 -26
  27. data/lib/davinci_crd_test_kit/crd_client_suite.rb +0 -4
  28. data/lib/davinci_crd_test_kit/hook_request_field_validation.rb +240 -50
  29. data/lib/davinci_crd_test_kit/mock_service_response.rb +134 -120
  30. data/lib/davinci_crd_test_kit/routes/hook_request_endpoint.rb +26 -42
  31. data/lib/davinci_crd_test_kit/server_encounter_discharge_group.rb +24 -0
  32. data/lib/davinci_crd_test_kit/server_encounter_start_group.rb +24 -0
  33. data/lib/davinci_crd_test_kit/server_order_select_group.rb +24 -0
  34. data/lib/davinci_crd_test_kit/server_tests/coverage_information_system_action_received_test.rb +4 -1
  35. data/lib/davinci_crd_test_kit/server_tests/service_request_optional_fields_validation_test.rb +8 -10
  36. data/lib/davinci_crd_test_kit/server_tests/service_request_required_fields_validation_test.rb +5 -10
  37. data/lib/davinci_crd_test_kit/tags.rb +6 -6
  38. data/lib/davinci_crd_test_kit/version.rb +1 -1
  39. metadata +9 -2
@@ -1,5 +1,9 @@
1
+ require_relative '../client_hook_request_validation'
2
+
1
3
  module DaVinciCRDTestKit
2
4
  class RetrieveJWKSTest < Inferno::Test
5
+ include ClientHookRequestValidation
6
+
3
7
  id :crd_retrieve_jwks
4
8
  title 'JWKS can be retrieved'
5
9
  description %(
@@ -10,7 +14,7 @@ module DaVinciCRDTestKit
10
14
  submit the jwk_set as an input to the test.
11
15
  )
12
16
 
13
- input :auth_token_header_json
17
+ input :auth_token_headers_json
14
18
  input :jwk_set,
15
19
  title: "The Client's JWK Set containing it's public key",
16
20
  description: %(
@@ -20,46 +24,79 @@ module DaVinciCRDTestKit
20
24
  type: 'textarea',
21
25
  optional: true
22
26
  output :crd_jwks_json, :crd_jwks_keys_json
23
- makes_request :crd_client_jwks
24
27
 
25
28
  run do
26
- token_header = JSON.parse(auth_token_header_json)
27
- jku = token_header['jku']
29
+ auth_token_headers = JSON.parse(auth_token_headers_json)
30
+ skip_if auth_token_headers.empty?, 'No Authorization tokens produced from the previous test.'
28
31
 
29
- if jku.present?
30
- get(jku, name: :crd_client_jwks)
32
+ crd_jwks_json = []
33
+ crd_jwks_keys_json = []
34
+ auth_token_headers.each_with_index do |token_header, index|
35
+ @request_number = index + 1
31
36
 
32
- assert_response_status(200)
33
- assert_valid_json(response[:body])
34
- output crd_jwks_json: response[:body]
37
+ jku = JSON.parse(token_header)['jku']
38
+ if jku.present?
39
+ get(jku)
35
40
 
36
- jwks = JSON.parse(response[:body])
37
- else
38
- skip_if jwk_set.blank?,
39
- %(JWK Set must be inputted if Client's JWK Set is not available via a URL identified by the jku header
40
- field)
41
+ if response[:status] != 200
42
+ add_message('error', %(
43
+ #{request_number}Unexpected response status: expected 200, but received
44
+ #{response[:status]}))
45
+ next
46
+ end
41
47
 
42
- jwks = JSON.parse(jwk_set)
43
- end
48
+ @request_number = index + 1
49
+ jwks = json_parse(response[:body])
50
+ next if jwks.blank?
44
51
 
45
- keys = jwks['keys']
46
- assert keys.is_a?(Array), 'JWKS `keys` field must be an array'
52
+ crd_jwks_json << response[:body]
47
53
 
48
- assert keys.present?, 'The JWK set returned contains no public keys'
54
+ jwks = JSON.parse(response[:body])
55
+ else
56
+ skip_if jwk_set.blank?,
57
+ %(#{request_number}JWK Set must be inputted if Client's JWK Set is not available via a URL
58
+ identified by the jku header field)
49
59
 
50
- keys.each do |jwk|
51
- JWT::JWK.import(jwk.deep_symbolize_keys)
52
- rescue StandardError
53
- assert false, "Invalid JWK: #{jwk.to_json}"
54
- end
60
+ jwks = JSON.parse(jwk_set)
61
+ end
62
+
63
+ keys = jwks['keys']
64
+ unless keys.is_a?(Array)
65
+ add_message('error', "#{request_number}JWKS `keys` field must be an array")
66
+ next
67
+ end
55
68
 
56
- kid_presence = keys.all? { |key| key['kid'].present? }
57
- assert kid_presence, '`kid` field must be present in each key if JWKS contains multiple keys'
69
+ if keys.blank?
70
+ add_message('error', "#{request_number}The JWK set returned contains no public keys")
71
+ next
72
+ end
73
+
74
+ keys.each do |jwk|
75
+ JWT::JWK.import(jwk.deep_symbolize_keys)
76
+ rescue StandardError
77
+ add_message('error', "#{request_number}Invalid JWK: #{jwk.to_json}")
78
+ end
79
+
80
+ kid_presence = keys.all? { |key| key['kid'].present? }
81
+ if kid_presence.blank?
82
+ add_message('error',
83
+ "#{request_number}`kid` field must be present in each key if JWKS contains multiple keys")
84
+ next
85
+ end
86
+
87
+ kid_uniqueness = keys.map { |key| key['kid'] }.uniq.length == keys.length
88
+ if kid_uniqueness.blank?
89
+ add_message('error', "#{request_number}`kid` must be unique within the client's JWK Set.")
90
+ next
91
+ end
92
+
93
+ crd_jwks_keys_json << keys.to_json
94
+ end
58
95
 
59
- kid_uniqueness = keys.map { |key| key['kid'] }.uniq.length == keys.length
60
- assert kid_uniqueness, '`kid` must be unique within the client\' JWK Set.'
96
+ output crd_jwks_json: crd_jwks_json.to_json,
97
+ crd_jwks_keys_json: crd_jwks_keys_json.to_json
61
98
 
62
- output crd_jwks_keys_json: keys.to_json
99
+ no_error_validation('Retrieving JWKS failed.')
63
100
  end
64
101
  end
65
102
  end
@@ -0,0 +1,44 @@
1
+ module DaVinciCRDTestKit
2
+ class SubmittedResponseValidationTest < Inferno::Test
3
+ include CardsValidation
4
+
5
+ title 'Custom CDS Service Response is valid'
6
+ id :crd_submitted_response_validation
7
+
8
+ input :custom_response, optional: true
9
+
10
+ def hook_name
11
+ config.options[:hook_name]
12
+ end
13
+
14
+ def response_label(_index = nil)
15
+ 'Custom response'
16
+ end
17
+
18
+ def valid_cards
19
+ @valid_cards ||= []
20
+ end
21
+
22
+ def validate_system_actions(system_actions)
23
+ return if system_actions.nil?
24
+
25
+ system_actions.each do |action|
26
+ action_fields_validation(action)
27
+ end
28
+ end
29
+
30
+ run do
31
+ omit_if custom_response.blank?, 'Custom response was not provided'
32
+
33
+ assert_valid_json custom_response
34
+
35
+ custom_response_hash = JSON.parse(custom_response)
36
+
37
+ perform_cards_validation(custom_response_hash['cards'])
38
+
39
+ validate_system_actions(custom_response_hash['systemActions'])
40
+
41
+ no_error_validation('Custom response is not valid. Check messages for issues found.')
42
+ end
43
+ end
44
+ end
@@ -1,5 +1,9 @@
1
+ require_relative '../client_hook_request_validation'
2
+
1
3
  module DaVinciCRDTestKit
2
4
  class TokenHeaderTest < Inferno::Test
5
+ include ClientHookRequestValidation
6
+
3
7
  id :crd_token_header
4
8
  title 'Authorization token header contains required information'
5
9
  description %(
@@ -8,27 +12,54 @@ module DaVinciCRDTestKit
8
12
  that the key used to sign the token can be identified in the JWKS.
9
13
  )
10
14
 
11
- input :auth_token_header_json, :crd_jwks_keys_json
12
- output :auth_token_jwk_json
15
+ input :auth_token_headers_json, :crd_jwks_keys_json
16
+ output :auth_tokens_jwk_json
13
17
 
14
18
  run do
15
- header = JSON.parse(auth_token_header_json)
19
+ auth_token_headers = JSON.parse(auth_token_headers_json)
20
+ crd_jwks_keys = JSON.parse(crd_jwks_keys_json)
21
+ skip_if auth_token_headers.empty?, 'No Authorization tokens produced from the previous tests.'
22
+ skip_if crd_jwks_keys.empty?, 'No JWKS keys produced from the previous test.'
23
+
24
+ auth_tokens_jwk_json = []
25
+ auth_token_headers.each_with_index do |token_header, index|
26
+ @request_number = index + 1
27
+
28
+ header = JSON.parse(token_header)
29
+ algorithm = header['alg']
30
+
31
+ add_message('error', "#{request_number}Token header must have the `alg` field") if algorithm.blank?
32
+
33
+ add_message('error', "#{request_number}Token header `alg` field cannot be set to none") if algorithm == 'none'
34
+
35
+ if header['typ'].blank?
36
+ add_message('error', "#{request_number}Token header must have the `typ` field")
37
+ elsif header['typ'] != 'JWT'
38
+ add_message('error', %(
39
+ #{request_number}Token header `typ` field must be set to 'JWT', instead was
40
+ #{header['typ']}))
41
+ end
42
+
43
+ if header['kid'].blank?
44
+ add_message('error', "#{request_number}Token header must have the `kid` field")
45
+ next
46
+ end
16
47
 
17
- algorithm = header['alg']
18
- assert algorithm.present?, 'Token header must have the `alg` field'
19
- assert algorithm != 'none', 'Token header `alg` field cannot be set to none'
48
+ kid = header['kid']
49
+ keys = JSON.parse(crd_jwks_keys[index])
20
50
 
21
- assert header['typ'].present?, 'Token header must have the `typ` field'
22
- assert header['typ'] == 'JWT', "Token header `typ` field must be set to 'JWT', instead was #{header['typ']}"
51
+ jwk = keys.find { |key| key['kid'] == kid }
52
+ if jwk.blank?
53
+ add_message('error', "#{request_number}JWKS did not contain a public key with an id of `#{kid}`")
54
+ next
55
+ end
23
56
 
24
- assert header['kid'].present?, 'Token header must have the `kid` field'
25
- kid = header['kid']
26
- keys = JSON.parse(crd_jwks_keys_json)
57
+ auth_tokens_jwk_json << jwk.to_json
58
+ end
27
59
 
28
- jwk = keys.find { |key| key['kid'] == kid }
29
- assert jwk.present?, "JWKS did not contain a public key with an id of `#{kid}`"
60
+ output auth_tokens_jwk_json: auth_tokens_jwk_json.to_json
30
61
 
31
- output auth_token_jwk_json: jwk.to_json
62
+ no_error_validation('Token headers missing required information.')
32
63
  end
33
64
  end
34
65
  end
@@ -1,5 +1,8 @@
1
+ require_relative '../client_hook_request_validation'
2
+
1
3
  module DaVinciCRDTestKit
2
4
  class TokenPayloadTest < Inferno::Test
5
+ include ClientHookRequestValidation
3
6
  include URLs
4
7
  id :crd_token_payload
5
8
  title 'Authorization token payload has required claims and a valid signature'
@@ -25,37 +28,51 @@ module DaVinciCRDTestKit
25
28
  base_url + config.options[:hook_path]
26
29
  end
27
30
 
28
- input :auth_token,
29
- :auth_token_jwk_json,
31
+ input :auth_tokens,
32
+ :auth_tokens_jwk_json,
30
33
  :iss
31
34
 
32
35
  run do
33
- begin
34
- jwk = JSON.parse(auth_token_jwk_json).deep_symbolize_keys
35
-
36
- payload, =
37
- JWT.decode(
38
- auth_token,
39
- JWT::JWK.import(jwk).public_key,
40
- true,
41
- algorithms: [jwk[:alg]],
42
- exp_leeway: 60,
43
- iss:,
44
- aud: hook_url,
45
- verify_not_before: false,
46
- verify_iat: false,
47
- verify_jti: true,
48
- verify_iss: true,
49
- verify_aud: true
50
- )
51
- rescue StandardError => e
52
- assert false, "Token validation error: #{e.message}"
53
- end
36
+ auth_tokens_list = JSON.parse(auth_tokens)
37
+ auth_tokens_jwk = JSON.parse(auth_tokens_jwk_json)
38
+ skip_if auth_tokens_list.empty?, 'No Authorization tokens produced from the previous tests.'
39
+ skip_if auth_tokens_jwk.empty?, 'No Authorization token JWK produced from the previous test.'
40
+
41
+ auth_tokens_jwk.each_with_index do |auth_token_jwk, index|
42
+ @request_number = index + 1
54
43
 
55
- missing_claims = required_claims - payload.keys
56
- missing_claims_string = missing_claims.map { |claim| "`#{claim}`" }.join(', ')
44
+ begin
45
+ jwk = JSON.parse(auth_token_jwk).deep_symbolize_keys
57
46
 
58
- assert missing_claims.empty?, "JWT payload missing required claims: #{missing_claims_string}"
47
+ payload, =
48
+ JWT.decode(
49
+ auth_tokens_list[index],
50
+ JWT::JWK.import(jwk).public_key,
51
+ true,
52
+ algorithms: [jwk[:alg]],
53
+ exp_leeway: 60,
54
+ iss:,
55
+ aud: hook_url,
56
+ verify_not_before: false,
57
+ verify_iat: false,
58
+ verify_jti: true,
59
+ verify_iss: true,
60
+ verify_aud: true
61
+ )
62
+ rescue StandardError => e
63
+ add_message('error', "#{request_number}Token validation error: #{e.message}")
64
+ next
65
+ end
66
+
67
+ missing_claims = required_claims - payload.keys
68
+ missing_claims_string = missing_claims.map { |claim| "`#{claim}`" }.join(', ')
69
+
70
+ unless missing_claims.empty?
71
+ add_message('error', "#{request_number}JWT payload missing required claims: #{missing_claims_string}")
72
+ next
73
+ end
74
+ end
75
+ no_error_validation('Token payload is missing required claims or does not have a valid signiture.')
59
76
  end
60
77
  end
61
78
  end
@@ -121,10 +121,6 @@ module DaVinciCRDTestKit
121
121
  }
122
122
  ]
123
123
 
124
- def self.test_resumes?(test)
125
- !test.config.options[:accepts_multiple_requests]
126
- end
127
-
128
124
  def self.extract_token_from_query_params(request)
129
125
  request.query_parameters['token']
130
126
  end