davinci_crd_test_kit 0.9.0 → 0.9.1

Sign up to get free protection for your applications and to get access to all the features.
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