Skip to content

Commit

Permalink
FI-2723 Fix backend services discovery test versioning error (#29)
Browse files Browse the repository at this point in the history
* Test structure refactor

* Bulk Data V1 SMART Discovery Test contents implementation

* Removed commented out code

* Refactor to issue warnings for everything but token_endpoint

* Removed run_as_group and optional configs

* spec test for bulk data v1 discovery contents test

* Fixed run_as_group so auth tests are bundled

* Checked for failure messages in discovery v1 contents spec test

* Used consistent versioning syntax

* Versioning syntax in class names

* V2 syntax fixes as well

* Use assert_valid_http_uri to test correct URI format

* Test requires proper format/values for optional claims that are present
  • Loading branch information
alisawallace authored Jul 22, 2024
1 parent 04b4367 commit 8c8c5cf
Show file tree
Hide file tree
Showing 7 changed files with 229 additions and 9 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# frozen_string_literal: true

require 'smart_app_launch/smart_stu2_suite'
require_relative 'bulk_data_smart_discovery_v101_group'

module BulkDataTestKit
module BulkDataV101
class BulkDataSmartBackendServicesV101Group < Inferno::TestGroup
title 'SMART Backend Services'
id :bulk_data_smart_backend_services_v101
optional

group from: :bulk_data_smart_discovery_v101,
config: {
inputs: { url: { name: :bulk_server_url } }
}

group from: :backend_services_authorization, run_as_group: true
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
require 'pry'
module BulkDataTestKit
module BulkDataV101
class BulkDataSmartDiscoveryV101ContentsTest < Inferno::Test
title 'Well-known configuration contains the required fields'
id :bulk_data_smart_discovery_v101_contents

description %(
The [Bulk Data v1.0.1 SMART Backend Services IG](https://hl7.org/fhir/uv/bulkdata/STU1.0.1/authorization/index.html#advertising-server-conformance-with-smart-backend-services) states:
> A server MAY advertise its conformance with SMART Backend Services, by hosting a Well-Known Uniform Resource
> Identifiers (URIs) (RFC5785) JSON document as described at SMART App Launch Authorization Discovery. If
> advertising support, a server’s /.well-known/smart-configuration endpoint SHOULD include token_endpoint,
> scopes_supported, token_endpoint_auth_methods_supported (with values that include private_key_jwt), and
> token_endpoint_auth_signing_alg_values_supported (with values that include at least one of RS384, ES384)
> attributes for backend services. The response is a JSON document using the application/json mime type.
This test requires a valid `token_endpoint` claim to pass but issue a warning for any other recommended claims
that are not present.
However, any included claim must have the proper format and/or values indicated by the IG.
)

input :well_known_configuration

output :smart_token_url

def test_key(config, key, type)
assert config[key].present?, "Well-known configuration field `#{key}` is blank"
assert config[key].is_a?(type), "Well-known `#{key}` must be type: #{type.to_s.downcase}"
end

run do
config = JSON.parse(well_known_configuration)

# token_endpoint must be output for downstream tests to work
assert config.key?('token_endpoint'), 'Well-known configuration does not include `token_endpoint`'
test_key(config, 'token_endpoint', String)
token_endpoint = config['token_endpoint']
assert_valid_http_uri(token_endpoint, "`#{token_endpoint}` is not a valid URI")

output smart_token_url: token_endpoint

recommended_capabilities = [
'token_endpoint_auth_methods_supported',
'token_endpoint_auth_signing_alg_values_supported',
'scopes_supported'
]

present_capabilities = []
recommended_capabilities.each do |key|
if config.key?(key) then present_capabilities.append(key)
else
warning do
assert config.key?(key), "Well-known configuration does not include `#{key}`"
end
end
end

present_capabilities.each do |key|
test_key(config, key, Array)
end

if present_capabilities.include?('token_endpoint_auth_methods_supported')
assert config['token_endpoint_auth_methods_supported'].include?('private_key_jwt'),
'`token_endpoint_auth_methods_supported` does not include the value `private_key_jwt`'
end

if present_capabilities.include?('token_endpoint_auth_methods_supported')
supports_RS384 = config['token_endpoint_auth_signing_alg_values_supported'].include? 'RS384'
supports_ES384 = config['token_endpoint_auth_signing_alg_values_supported'].include? 'ES384'

assert (supports_RS384 || supports_ES384),
'`token_endpoint_auth_signing_alg_values_supported` does not include values for `RS384` or `ES384`'
end
end
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
require 'smart_app_launch/well_known_endpoint_test'
require_relative 'bulk_data_smart_discovery_v101_contents_test'

module BulkDataTestKit
module BulkDataV101
class BulkDataSmartDiscoveryV101Group < Inferno::TestGroup
title 'SMART on FHIR Discovery'
id :bulk_data_smart_discovery_v101
run_as_group

test from: :well_known_endpoint
test from: :bulk_data_smart_discovery_v101_contents
end
end
end
5 changes: 2 additions & 3 deletions lib/bulk_data_test_kit/v1.0.1/bulk_data_test_suite.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# frozen_string_literal: true

require_relative '../version'
require_relative 'bulk_data_smart_backend_services_group'
require_relative 'bulk_data_smart_backend_services_v101_group'
require_relative 'bulk_data_export_tests_test_group'

module BulkDataTestKit
Expand Down Expand Up @@ -158,8 +158,7 @@ def self.well_known_route_handler
url :bulk_server_url
end

group from: :bulk_data_smart_backend_services,
id: :bulk_data_smart_backend_services_v101
group from: :bulk_data_smart_backend_services_v101

group from: :bulk_data_export_tests_v101
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
require 'smart_app_launch/smart_stu2_suite'

module BulkDataTestKit
module BulkDataV101
class BulkDataSmartBackendServicesGroup < Inferno::TestGroup
module BulkDataV200
class BulkDataSmartBackendServicesV200Group < Inferno::TestGroup
title 'SMART Backend Services'
id :bulk_data_smart_backend_services
id :bulk_data_smart_backend_services_v200
run_as_group
optional

Expand Down
5 changes: 2 additions & 3 deletions lib/bulk_data_test_kit/v2.0.0/bulk_data_test_suite.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# frozen_string_literal: true

require_relative '../version'
require_relative '../v1.0.1/bulk_data_smart_backend_services_group'
require_relative 'bulk_data_smart_backend_services_v200_group'
require_relative 'bulk_data_export_tests_test_group'

module BulkDataTestKit
Expand Down Expand Up @@ -156,8 +156,7 @@ def self.well_known_route_handler
url :bulk_server_url
end

group from: :bulk_data_smart_backend_services,
id: :bulk_data_smart_backend_services_v200
group from: :bulk_data_smart_backend_services_v200

group from: :bulk_data_export_tests_v200
end
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
require_relative '../../lib/bulk_data_test_kit/v1.0.1/bulk_data_smart_discovery_v101_contents_test'
require 'pry'

RSpec.describe BulkDataTestKit::BulkDataV101::BulkDataSmartDiscoveryV101ContentsTest do
let(:runnable) { Inferno::Repositories::Tests.new.find('bulk_data_smart_discovery_v101_contents') }
let(:session_data_repo) { Inferno::Repositories::SessionData.new }
let(:results_repo) { Inferno::Repositories::Results.new }
let(:test_session) { repo_create(:test_session, test_suite_id: 'bulk_data_v101') }
let(:correct_metadata) {
{
'token_endpoint' => 'https://example.org/auth/token',
'token_endpoint_auth_methods_supported' => ['private_key_jwt'],
'token_endpoint_auth_signing_alg_values_supported' => [ 'RS384', 'ES384' ],
'scopes_supported' => ['system/*.read']
}
}

let(:recommended_capabilities) {
[
'token_endpoint_auth_methods_supported',
'token_endpoint_auth_signing_alg_values_supported',
'scopes_supported'
]
}
def run(runnable, inputs = {})
test_run_params = { test_session_id: test_session.id }.merge(runnable.reference_hash)
test_run = Inferno::Repositories::TestRuns.new.create(test_run_params)
inputs.each do |name, value|
session_data_repo.save(
test_session_id: test_session.id,
name:,
value:,
type: runnable.config.input_type(name)
)
end
Inferno::TestRunner.new(test_session:, test_run:).run(runnable)
end

it 'skips if well-known metadata is not present' do
result = run(runnable, well_known_configuration: '')

expect(result.result).to eq('skip')
end

it 'passes with all required and recommended fields' do
result = run(runnable, well_known_configuration: JSON.generate(correct_metadata))

expect(result.result).to eq('pass')
end

it 'passes with required field but missing recommended fields' do
recommended_capabilities.each do |key|
metadata = correct_metadata
metadata.delete(key)
result = run(runnable, well_known_configuration: JSON.generate(metadata))
expect(result.result).to eq('pass')
end
end

it 'fails when token_endpoint field is missing' do
metadata = correct_metadata
metadata.delete('token_endpoint')
result = run(runnable, well_known_configuration: JSON.generate(metadata))
expect(result.result).to eq('fail')
expect(result.result_message).to match(/does not include `token_endpoint`/)
end

it 'fails with incorrect token_endpoint contents' do
incorrect_token_endpoint_values = {
'' => '`token_endpoint` is blank',
'not_a_uri' => 'not a valid URI',
['https://example.org/auth/token'] => '`token_endpoint` must be type'
}

metadata = correct_metadata

incorrect_token_endpoint_values.each do |key, value|
metadata['token_endpoint'] = key
result = run(runnable, well_known_configuration: JSON.generate(metadata))
expect(result.result).to eq('fail')
expect(result.result_message).to match(value)
end
end

it 'fails when recommended claims are present but have improper format' do
recommended_capabilities.each do |key|
metadata = correct_metadata.clone
# should be an array for all
metadata[key] = ''
result = run(runnable, well_known_configuration: JSON.generate(metadata))
expect(result.result).to eq('fail')
expect(result.result_message).to match(key)
end
end

it 'fails when token_endpoint_auth_methods_supported value is incorrect' do
correct_metadata['token_endpoint_auth_methods_supported'] = ['invalid']
result = run(runnable, well_known_configuration: JSON.generate(correct_metadata))
expect(result.result).to eq('fail')
expect(result.result_message).to match('private_key_jwt')
end

it 'fails when token_endpoint_auth_signing_alg_values_supported value is incorrect' do
correct_metadata['token_endpoint_auth_signing_alg_values_supported'] = ['invalid']
result = run(runnable, well_known_configuration: JSON.generate(correct_metadata))
expect(result.result).to eq('fail')
expect(result.result_message).to match('`RS384` or `ES384`')
end
end

0 comments on commit 8c8c5cf

Please sign in to comment.