smart_app_launch_test_kit 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +201 -0
- data/lib/smart_app_launch/app_launch_test.rb +25 -0
- data/lib/smart_app_launch/app_redirect_test.rb +61 -0
- data/lib/smart_app_launch/code_received_test.rb +31 -0
- data/lib/smart_app_launch/discovery_group.rb +229 -0
- data/lib/smart_app_launch/ehr_launch_group.rb +117 -0
- data/lib/smart_app_launch/launch_received_test.rb +19 -0
- data/lib/smart_app_launch/openid_connect_group.rb +83 -0
- data/lib/smart_app_launch/openid_decode_id_token_test.rb +30 -0
- data/lib/smart_app_launch/openid_fhir_user_claim_test.rb +30 -0
- data/lib/smart_app_launch/openid_required_configuration_fields_test.rb +50 -0
- data/lib/smart_app_launch/openid_retrieve_configuration_test.rb +31 -0
- data/lib/smart_app_launch/openid_retrieve_jwks_test.rb +43 -0
- data/lib/smart_app_launch/openid_token_header_test.rb +39 -0
- data/lib/smart_app_launch/openid_token_payload_test.rb +64 -0
- data/lib/smart_app_launch/standalone_launch_group.rb +104 -0
- data/lib/smart_app_launch/token_exchange_test.rb +47 -0
- data/lib/smart_app_launch/token_payload_validation.rb +47 -0
- data/lib/smart_app_launch/token_refresh_body_test.rb +52 -0
- data/lib/smart_app_launch/token_refresh_group.rb +45 -0
- data/lib/smart_app_launch/token_refresh_test.rb +52 -0
- data/lib/smart_app_launch/token_response_body_test.rb +53 -0
- data/lib/smart_app_launch/token_response_headers_test.rb +27 -0
- data/lib/smart_app_launch_test_kit.rb +141 -0
- metadata +168 -0
@@ -0,0 +1,45 @@
|
|
1
|
+
require_relative 'token_refresh_test'
|
2
|
+
require_relative 'token_refresh_body_test'
|
3
|
+
require_relative 'token_response_headers_test'
|
4
|
+
|
5
|
+
module SMARTAppLaunch
|
6
|
+
class TokenRefreshGroup < Inferno::TestGroup
|
7
|
+
id :smart_token_refresh
|
8
|
+
title 'SMART Token Refresh'
|
9
|
+
description %(
|
10
|
+
# Background
|
11
|
+
|
12
|
+
The #{title} Sequence tests the ability of the system to successfuly
|
13
|
+
exchange a refresh token for an access token. Refresh tokens are typically
|
14
|
+
longer lived than access tokens and allow client applications to obtain a
|
15
|
+
new access token Refresh tokens themselves cannot provide access to
|
16
|
+
resources on the server.
|
17
|
+
|
18
|
+
Token refreshes are accomplished through a `POST` request to the token
|
19
|
+
exchange endpoint as described in the [SMART App Launch
|
20
|
+
Framework](http://www.hl7.org/fhir/smart-app-launch/#step-5-later-app-uses-a-refresh-token-to-obtain-a-new-access-token).
|
21
|
+
|
22
|
+
# Test Methodology
|
23
|
+
|
24
|
+
This test attempts to exchange the refresh token for a new access token
|
25
|
+
and verify that the information returned contains the required fields and
|
26
|
+
uses the proper headers.
|
27
|
+
|
28
|
+
For more information see:
|
29
|
+
|
30
|
+
* [The OAuth 2.0 Authorization
|
31
|
+
Framework](https://tools.ietf.org/html/rfc6749)
|
32
|
+
* [Using a refresh token to obtain a new access
|
33
|
+
token](http://hl7.org/fhir/smart-app-launch/#step-5-later-app-uses-a-refresh-token-to-obtain-a-new-access-token)
|
34
|
+
)
|
35
|
+
|
36
|
+
test from: :smart_token_refresh
|
37
|
+
test from: :smart_token_refresh_body
|
38
|
+
test from: :smart_token_response_headers,
|
39
|
+
config: {
|
40
|
+
requests: {
|
41
|
+
token: { name: :token_refresh }
|
42
|
+
}
|
43
|
+
}
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require_relative 'token_payload_validation'
|
2
|
+
|
3
|
+
module SMARTAppLaunch
|
4
|
+
class TokenRefreshTest < Inferno::Test
|
5
|
+
include TokenPayloadValidation
|
6
|
+
|
7
|
+
id :smart_token_refresh
|
8
|
+
title 'Server successfully refreshes the access token when optional scope parameter omitted'
|
9
|
+
description %(
|
10
|
+
Server successfully exchanges refresh token at OAuth token endpoint
|
11
|
+
without providing scope in the body of the request.
|
12
|
+
|
13
|
+
The EHR authorization server SHALL return a JSON structure that includes
|
14
|
+
an access token or a message indicating that the authorization request
|
15
|
+
has been denied. `access_token`, `expires_in`, `token_type`, and `scope` are
|
16
|
+
required. `access_token` must be `Bearer`.
|
17
|
+
|
18
|
+
Although not required in the token refresh portion of the SMART App
|
19
|
+
Launch Guide, the token refresh response should include the HTTP
|
20
|
+
Cache-Control response header field with a value of no-store, as well as
|
21
|
+
the Pragma response header field with a value of no-cache to be
|
22
|
+
consistent with the requirements of the inital access token exchange.
|
23
|
+
)
|
24
|
+
input :well_known_token_url, :refresh_token, :client_id, :received_scopes
|
25
|
+
input :client_secret, optional: true
|
26
|
+
makes_request :token_refresh
|
27
|
+
|
28
|
+
run do
|
29
|
+
skip_if refresh_token.blank?
|
30
|
+
|
31
|
+
oauth2_params = {
|
32
|
+
'grant_type' => 'refresh_token',
|
33
|
+
'refresh_token' => refresh_token
|
34
|
+
}
|
35
|
+
oauth2_headers = { 'Content-Type' => 'application/x-www-form-urlencoded' }
|
36
|
+
|
37
|
+
oauth2_params['scope'] = received_scopes if config.options[:include_scopes]
|
38
|
+
|
39
|
+
if client_secret.present?
|
40
|
+
credentials = Base64.strict_encode64("#{client_id}:#{client_secret}")
|
41
|
+
oauth2_headers['Authorization'] = "Basic #{credentials}"
|
42
|
+
else
|
43
|
+
oauth2_params['client_id'] = client_id
|
44
|
+
end
|
45
|
+
|
46
|
+
post(well_known_token_url, body: oauth2_params, name: :token_refresh, headers: oauth2_headers)
|
47
|
+
|
48
|
+
assert_response_status(200)
|
49
|
+
assert_valid_json(response[:body])
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require_relative 'token_payload_validation'
|
2
|
+
|
3
|
+
module SMARTAppLaunch
|
4
|
+
class TokenResponseBodyTest < Inferno::Test
|
5
|
+
include TokenPayloadValidation
|
6
|
+
|
7
|
+
title 'Token exchange response body contains required information encoded in JSON'
|
8
|
+
description %(
|
9
|
+
The EHR authorization server shall return a JSON structure that includes
|
10
|
+
an access token or a message indicating that the authorization request
|
11
|
+
has been denied. `access_token`, `token_type`, and `scope` are required.
|
12
|
+
`token_type` must be Bearer. `expires_in` is required for token
|
13
|
+
refreshes.
|
14
|
+
)
|
15
|
+
id :smart_token_response_body
|
16
|
+
|
17
|
+
input :requested_scopes
|
18
|
+
output :id_token,
|
19
|
+
:refresh_token,
|
20
|
+
:access_token,
|
21
|
+
:expires_in,
|
22
|
+
:patient_id,
|
23
|
+
:encounter_id,
|
24
|
+
:received_scopes,
|
25
|
+
:intent
|
26
|
+
uses_request :token
|
27
|
+
|
28
|
+
run do
|
29
|
+
skip_if request.status != 200, 'Token exchange was unsuccessful'
|
30
|
+
|
31
|
+
assert_valid_json(request.response_body)
|
32
|
+
token_response_body = JSON.parse(request.response_body)
|
33
|
+
|
34
|
+
output id_token: token_response_body['id_token'],
|
35
|
+
refresh_token: token_response_body['refresh_token'],
|
36
|
+
access_token: token_response_body['access_token'],
|
37
|
+
expires_in: token_response_body['expires_in'],
|
38
|
+
patient_id: token_response_body['patient'],
|
39
|
+
encounter_id: token_response_body['encounter'],
|
40
|
+
received_scopes: token_response_body['scope'],
|
41
|
+
intent: token_response_body['intent']
|
42
|
+
|
43
|
+
validate_required_fields_present(token_response_body, ['access_token', 'token_type', 'expires_in', 'scope'])
|
44
|
+
validate_token_field_types(token_response_body)
|
45
|
+
validate_token_type(token_response_body)
|
46
|
+
check_for_missing_scopes(requested_scopes, token_response_body)
|
47
|
+
|
48
|
+
assert access_token.present?, 'Token response did not contain an access token'
|
49
|
+
assert token_response_body['token_type']&.casecmp('Bearer')&.zero?,
|
50
|
+
'`token_type` field must have a value of `Bearer`'
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module SMARTAppLaunch
|
2
|
+
class TokenResponseHeadersTest < Inferno::Test
|
3
|
+
title 'Response includes correct HTTP Cache-Control and Pragma headers'
|
4
|
+
description %(
|
5
|
+
The authorization servers response must include the HTTP Cache-Control
|
6
|
+
response header field with a value of no-store, as well as the Pragma
|
7
|
+
response header field with a value of no-cache.
|
8
|
+
)
|
9
|
+
id :smart_token_response_headers
|
10
|
+
|
11
|
+
uses_request :token
|
12
|
+
|
13
|
+
run do
|
14
|
+
skip_if request.status != 200, 'Token exchange was unsuccessful'
|
15
|
+
|
16
|
+
cc_header = request.response_header('Cache-Control')&.value
|
17
|
+
|
18
|
+
assert cc_header&.downcase&.include?('no-store'),
|
19
|
+
'Token response must have `Cache-Control` header containing `no-store`.'
|
20
|
+
|
21
|
+
pragma_header = request.response_header('Pragma')&.value
|
22
|
+
|
23
|
+
assert pragma_header&.downcase&.include?('no-cache'),
|
24
|
+
'Token response must have `Pragma` header containing `no-cache`.'
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,141 @@
|
|
1
|
+
require_relative 'smart_app_launch/discovery_group'
|
2
|
+
require_relative 'smart_app_launch/standalone_launch_group'
|
3
|
+
require_relative 'smart_app_launch/ehr_launch_group'
|
4
|
+
require_relative 'smart_app_launch/openid_connect_group'
|
5
|
+
require_relative 'smart_app_launch/token_refresh_group'
|
6
|
+
|
7
|
+
module SMARTAppLaunch
|
8
|
+
class SMARTSuite < Inferno::TestSuite
|
9
|
+
id 'smart'
|
10
|
+
title 'SMART'
|
11
|
+
|
12
|
+
resume_test_route :get, '/launch' do
|
13
|
+
request.query_parameters['iss']
|
14
|
+
end
|
15
|
+
|
16
|
+
resume_test_route :get, '/redirect' do
|
17
|
+
request.query_parameters['state']
|
18
|
+
end
|
19
|
+
|
20
|
+
config options: {
|
21
|
+
redirect_uri: "#{Inferno::Application['inferno_host']}/custom/smart/redirect",
|
22
|
+
launch_uri: "#{Inferno::Application['inferno_host']}/custom/smart/launch"
|
23
|
+
}
|
24
|
+
|
25
|
+
group do
|
26
|
+
title 'Standalone Launch'
|
27
|
+
|
28
|
+
run_as_group
|
29
|
+
|
30
|
+
group from: :smart_discovery
|
31
|
+
|
32
|
+
group from: :smart_standalone_launch
|
33
|
+
|
34
|
+
group from: :smart_openid_connect,
|
35
|
+
config: {
|
36
|
+
inputs: {
|
37
|
+
id_token: { name: :standalone_id_token },
|
38
|
+
client_id: { name: :standalone_client_id },
|
39
|
+
requested_scopes: { name: :standalone_requested_scopes }
|
40
|
+
}
|
41
|
+
}
|
42
|
+
|
43
|
+
group from: :smart_token_refresh,
|
44
|
+
id: :smart_standalone_refresh_without_scopes,
|
45
|
+
title: 'SMART Token Refresh Without Scopes',
|
46
|
+
config: {
|
47
|
+
inputs: {
|
48
|
+
refresh_token: { name: :standalone_refresh_token },
|
49
|
+
client_id: { name: :standalone_client_id },
|
50
|
+
client_secret: { name: :standalone_client_secret },
|
51
|
+
received_scopes: { name: :standalone_received_scopes }
|
52
|
+
},
|
53
|
+
outputs: {
|
54
|
+
refresh_token: { name: :standalone_refresh_token },
|
55
|
+
received_scopes: { name: :standalone_received_scopes },
|
56
|
+
access_token: { name: :standalone_access_token },
|
57
|
+
token_retrieval_time: { name: :standalone_token_retrieval_time },
|
58
|
+
expires_in: { name: :standalone_expires_in }
|
59
|
+
}
|
60
|
+
}
|
61
|
+
|
62
|
+
group from: :smart_token_refresh,
|
63
|
+
id: :smart_standalone_refresh_with_scopes,
|
64
|
+
title: 'SMART Token Refresh With Scopes',
|
65
|
+
config: {
|
66
|
+
options: { include_scopes: true },
|
67
|
+
inputs: {
|
68
|
+
refresh_token: { name: :standalone_refresh_token },
|
69
|
+
client_id: { name: :standalone_client_id },
|
70
|
+
client_secret: { name: :standalone_client_secret },
|
71
|
+
received_scopes: { name: :standalone_received_scopes }
|
72
|
+
},
|
73
|
+
outputs: {
|
74
|
+
refresh_token: { name: :standalone_refresh_token },
|
75
|
+
received_scopes: { name: :standalone_received_scopes },
|
76
|
+
access_token: { name: :standalone_access_token },
|
77
|
+
token_retrieval_time: { name: :standalone_token_retrieval_time },
|
78
|
+
expires_in: { name: :standalone_expires_in }
|
79
|
+
}
|
80
|
+
}
|
81
|
+
end
|
82
|
+
|
83
|
+
group do
|
84
|
+
title 'EHR Launch'
|
85
|
+
|
86
|
+
run_as_group
|
87
|
+
|
88
|
+
group from: :smart_discovery
|
89
|
+
|
90
|
+
group from: :smart_ehr_launch
|
91
|
+
|
92
|
+
group from: :smart_openid_connect,
|
93
|
+
config: {
|
94
|
+
inputs: {
|
95
|
+
id_token: { name: :ehr_id_token },
|
96
|
+
client_id: { name: :ehr_client_id },
|
97
|
+
requested_scopes: { name: :standalone_requested_scopes }
|
98
|
+
}
|
99
|
+
}
|
100
|
+
|
101
|
+
group from: :smart_token_refresh,
|
102
|
+
id: :smart_ehr_refresh_without_scopes,
|
103
|
+
title: 'SMART Token Refresh Without Scopes',
|
104
|
+
config: {
|
105
|
+
inputs: {
|
106
|
+
refresh_token: { name: :ehr_refresh_token },
|
107
|
+
client_id: { name: :ehr_client_id },
|
108
|
+
client_secret: { name: :ehr_client_secret },
|
109
|
+
received_scopes: { name: :ehr_received_scopes }
|
110
|
+
},
|
111
|
+
outputs: {
|
112
|
+
refresh_token: { name: :ehr_refresh_token },
|
113
|
+
received_scopes: { name: :ehr_received_scopes },
|
114
|
+
access_token: { name: :ehr_access_token },
|
115
|
+
token_retrieval_time: { name: :ehr_token_retrieval_time },
|
116
|
+
expires_in: { name: :ehr_expires_in }
|
117
|
+
}
|
118
|
+
}
|
119
|
+
|
120
|
+
group from: :smart_token_refresh,
|
121
|
+
id: :smart_ehr_refresh_with_scopes,
|
122
|
+
title: 'SMART Token Refresh With Scopes',
|
123
|
+
config: {
|
124
|
+
options: { include_scopes: true },
|
125
|
+
inputs: {
|
126
|
+
refresh_token: { name: :ehr_refresh_token },
|
127
|
+
client_id: { name: :ehr_client_id },
|
128
|
+
client_secret: { name: :ehr_client_secret },
|
129
|
+
received_scopes: { name: :ehr_received_scopes }
|
130
|
+
},
|
131
|
+
outputs: {
|
132
|
+
refresh_token: { name: :ehr_refresh_token },
|
133
|
+
received_scopes: { name: :ehr_received_scopes },
|
134
|
+
access_token: { name: :ehr_access_token },
|
135
|
+
token_retrieval_time: { name: :ehr_token_retrieval_time },
|
136
|
+
expires_in: { name: :ehr_expires_in }
|
137
|
+
}
|
138
|
+
}
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
metadata
ADDED
@@ -0,0 +1,168 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: smart_app_launch_test_kit
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Stephen MacVicar
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2021-10-21 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: inferno_core
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.0.6
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.0.6
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: jwt
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 2.2.2
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 2.2.2
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: database_cleaner-sequel
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '1.8'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1.8'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: factory_bot
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '6.1'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '6.1'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rack-test
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: 1.1.0
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 1.1.0
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rspec
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '3.10'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '3.10'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: webmock
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '3.11'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '3.11'
|
111
|
+
description: Inferno Tests for the SMART Application Launch Framework Implementation
|
112
|
+
Guide
|
113
|
+
email:
|
114
|
+
- inferno@groups.mitre.org
|
115
|
+
executables: []
|
116
|
+
extensions: []
|
117
|
+
extra_rdoc_files: []
|
118
|
+
files:
|
119
|
+
- LICENSE
|
120
|
+
- lib/smart_app_launch/app_launch_test.rb
|
121
|
+
- lib/smart_app_launch/app_redirect_test.rb
|
122
|
+
- lib/smart_app_launch/code_received_test.rb
|
123
|
+
- lib/smart_app_launch/discovery_group.rb
|
124
|
+
- lib/smart_app_launch/ehr_launch_group.rb
|
125
|
+
- lib/smart_app_launch/launch_received_test.rb
|
126
|
+
- lib/smart_app_launch/openid_connect_group.rb
|
127
|
+
- lib/smart_app_launch/openid_decode_id_token_test.rb
|
128
|
+
- lib/smart_app_launch/openid_fhir_user_claim_test.rb
|
129
|
+
- lib/smart_app_launch/openid_required_configuration_fields_test.rb
|
130
|
+
- lib/smart_app_launch/openid_retrieve_configuration_test.rb
|
131
|
+
- lib/smart_app_launch/openid_retrieve_jwks_test.rb
|
132
|
+
- lib/smart_app_launch/openid_token_header_test.rb
|
133
|
+
- lib/smart_app_launch/openid_token_payload_test.rb
|
134
|
+
- lib/smart_app_launch/standalone_launch_group.rb
|
135
|
+
- lib/smart_app_launch/token_exchange_test.rb
|
136
|
+
- lib/smart_app_launch/token_payload_validation.rb
|
137
|
+
- lib/smart_app_launch/token_refresh_body_test.rb
|
138
|
+
- lib/smart_app_launch/token_refresh_group.rb
|
139
|
+
- lib/smart_app_launch/token_refresh_test.rb
|
140
|
+
- lib/smart_app_launch/token_response_body_test.rb
|
141
|
+
- lib/smart_app_launch/token_response_headers_test.rb
|
142
|
+
- lib/smart_app_launch_test_kit.rb
|
143
|
+
homepage: https://github.com/inferno_community/smart-app-launch-test-kit
|
144
|
+
licenses:
|
145
|
+
- Apache-2.0
|
146
|
+
metadata:
|
147
|
+
homepage_uri: https://github.com/inferno_community/smart-app-launch-test-kit
|
148
|
+
source_code_uri: https://github.com/inferno_community/smart-app-launch-test-kit
|
149
|
+
post_install_message:
|
150
|
+
rdoc_options: []
|
151
|
+
require_paths:
|
152
|
+
- lib
|
153
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
154
|
+
requirements:
|
155
|
+
- - ">="
|
156
|
+
- !ruby/object:Gem::Version
|
157
|
+
version: 2.7.0
|
158
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
159
|
+
requirements:
|
160
|
+
- - ">="
|
161
|
+
- !ruby/object:Gem::Version
|
162
|
+
version: '0'
|
163
|
+
requirements: []
|
164
|
+
rubygems_version: 3.1.6
|
165
|
+
signing_key:
|
166
|
+
specification_version: 4
|
167
|
+
summary: Inferno Tests for the SMART Application Launch Framework Implementation Guide
|
168
|
+
test_files: []
|