Files
prism/test/test_middleware_cors.rb

151 lines
5.2 KiB
Ruby

require 'minitest/autorun'
require 'prism'
require 'support'
class MiddlewareCorsTest < Minitest::Test
include TestSupport
def setup
Prism::Cors.reset!
@downstream_calls = 0
downstream = lambda do |_env|
@downstream_calls += 1
[200, {'Content-Type' => 'application/json'}, ['{}']]
end
@app = Prism::Middleware::Cors.new(downstream)
end
def teardown
Prism::Cors.reset!
end
def configure_api(**resource_options)
Prism::Cors.configure do |config|
config.allow do
origins 'https://app.example.com'
resource '/api/*', **resource_options
end
end
end
def test_request_without_origin_header_passes_through_untouched
status, headers, = @app.call(env_for('GET', '/api/posts'))
assert_equal 200, status
assert_nil headers['Access-Control-Allow-Origin']
assert_equal 1, @downstream_calls
end
def test_request_with_unmatched_origin_passes_through_without_cors_headers
configure_api(methods: %w[GET])
env = env_for('GET', '/api/posts', headers: {'Origin' => 'https://evil.example.com'})
status, headers, = @app.call(env)
assert_equal 200, status
assert_nil headers['Access-Control-Allow-Origin']
assert_equal 1, @downstream_calls
end
def test_simple_request_with_allowed_origin_gets_cors_headers_and_reaches_the_app
configure_api(methods: %w[GET])
env = env_for('GET', '/api/posts', headers: {'Origin' => 'https://app.example.com'})
status, headers, = @app.call(env)
assert_equal 200, status
assert_equal 'https://app.example.com', headers['Access-Control-Allow-Origin']
assert_equal 'Origin', headers['Vary']
assert_equal 1, @downstream_calls
end
def test_preflight_request_is_answered_directly_without_reaching_the_app
configure_api(methods: %w[GET POST], headers: %w[Content-Type])
env = env_for('OPTIONS', '/api/posts', headers: {
'Origin' => 'https://app.example.com',
'Access-Control-Request-Method' => 'POST',
'Access-Control-Request-Headers' => 'Content-Type'
})
status, headers, body = @app.call(env)
assert_equal 204, status
assert_equal 'https://app.example.com', headers['Access-Control-Allow-Origin']
assert_equal 'GET, POST', headers['Access-Control-Allow-Methods']
assert_equal 'Content-Type', headers['Access-Control-Allow-Headers']
assert_empty body
assert_equal 0, @downstream_calls
end
def test_preflight_with_disallowed_method_is_rejected
configure_api(methods: %w[GET])
env = env_for('OPTIONS', '/api/posts', headers: {
'Origin' => 'https://app.example.com',
'Access-Control-Request-Method' => 'DELETE'
})
status, = @app.call(env)
assert_equal 403, status
assert_equal 0, @downstream_calls
end
def test_preflight_with_disallowed_header_is_rejected
configure_api(methods: %w[POST], headers: %w[Content-Type])
env = env_for('OPTIONS', '/api/posts', headers: {
'Origin' => 'https://app.example.com',
'Access-Control-Request-Method' => 'POST',
'Access-Control-Request-Headers' => 'X-Secret'
})
status, = @app.call(env)
assert_equal 403, status
end
def test_max_age_is_only_sent_when_configured
configure_api(methods: %w[GET], max_age: 600)
env = env_for('OPTIONS', '/api/posts', headers: {
'Origin' => 'https://app.example.com',
'Access-Control-Request-Method' => 'GET'
})
_, headers, = @app.call(env)
assert_equal '600', headers['Access-Control-Max-Age']
end
def test_credentials_true_echoes_origin_and_sets_allow_credentials
configure_api(methods: %w[GET], credentials: true)
env = env_for('GET', '/api/posts', headers: {'Origin' => 'https://app.example.com'})
_, headers, = @app.call(env)
assert_equal 'https://app.example.com', headers['Access-Control-Allow-Origin']
assert_equal 'true', headers['Access-Control-Allow-Credentials']
end
def test_expose_headers_only_sent_when_configured
configure_api(methods: %w[GET], expose: %w[X-Total-Count])
env = env_for('GET', '/api/posts', headers: {'Origin' => 'https://app.example.com'})
_, headers, = @app.call(env)
assert_equal 'X-Total-Count', headers['Access-Control-Expose-Headers']
end
def test_vary_origin_is_appended_to_an_existing_vary_header
Prism::Cors.reset!
downstream = lambda { |_env| [200, {'Vary' => 'Accept'}, ['{}']] }
app = Prism::Middleware::Cors.new(downstream)
configure_api(methods: %w[GET])
env = env_for('GET', '/api/posts', headers: {'Origin' => 'https://app.example.com'})
_, headers, = app.call(env)
assert_equal 'Accept, Origin', headers['Vary']
end
def test_a_resource_registered_without_a_format_extension_still_matches_a_dot_json_path
configure_api(methods: %w[GET])
env = env_for('GET', '/api/posts.json', headers: {'Origin' => 'https://app.example.com'})
status, headers, = @app.call(env)
assert_equal 200, status
assert_equal 'https://app.example.com', headers['Access-Control-Allow-Origin']
end
def test_options_without_request_method_header_is_treated_as_a_simple_request
configure_api(methods: %w[GET])
env = env_for('OPTIONS', '/api/posts', headers: {'Origin' => 'https://app.example.com'})
status, = @app.call(env)
assert_equal 200, status
assert_equal 1, @downstream_calls
end
end