spec/integration/rack_app/middleware_spec.rb in hanami-2.0.0.beta4 vs spec/integration/rack_app/middleware_spec.rb in hanami-2.0.0.rc1
- old
+ new
@@ -1,8 +1,9 @@
# frozen_string_literal: true
require "rack/test"
+require "stringio"
RSpec.describe "Hanami web app", :app_integration do
include Rack::Test::Methods
let(:app) { Hanami.app }
@@ -48,11 +49,11 @@
write "config/app.rb", <<~RUBY
require "hanami"
module TestApp
class App < Hanami::App
- config.logger.stream = File.new("/dev/null", "w")
+ config.logger.stream = StringIO.new
config.middleware.use Middlewares::AppendOne
config.middleware.use Middlewares::Prepare, before: Middlewares::AppendOne
config.middleware.use Middlewares::AppendTwo, after: Middlewares::AppendOne
end
@@ -99,11 +100,11 @@
write "config/app.rb", <<~RUBY
require "hanami"
module TestApp
class App < Hanami::App
- config.logger.stream = File.new("/dev/null", "w")
+ config.logger.stream = StringIO.new
end
end
RUBY
write "config/routes.rb", <<~RUBY
@@ -162,11 +163,11 @@
@app.call(env)
end
end
class App < Hanami::App
- config.logger.stream = File.new("/dev/null", "w")
+ config.logger.stream = StringIO.new
config.middleware.use(TestApp::TestMiddleware) { |env| env["tested"] = "yes" }
end
end
RUBY
@@ -230,8 +231,431 @@
Class.new(Hanami::App) do
config.middleware.namespaces.delete(Hanami::Middleware)
config.middleware.use(:body_parser)
end
}.to raise_error(Hanami::UnsupportedMiddlewareSpecError)
+ end
+ end
+
+ context "with simple app" do
+ before do
+ write "config/app.rb", <<~RUBY
+ require "hanami"
+
+ module TestApp
+ class App < Hanami::App
+ config.logger.stream = File.new("/dev/null", "w")
+ end
+ end
+ RUBY
+
+ write "lib/test_app/middleware/authentication.rb", <<~RUBY
+ module TestApp
+ module Middleware
+ class Authentication
+ def self.inspect
+ "<Middleware::Auth>"
+ end
+
+ def initialize(app)
+ @app = app
+ end
+
+ def call(env)
+ env["AUTH_USER_ID"] = user_id = "23"
+ status, headers, body = @app.call(env)
+ headers["X-Auth-User-ID"] = user_id
+
+ [status, headers, body]
+ end
+ end
+ end
+ end
+ RUBY
+
+ write "config/routes.rb", <<~RUBY
+ require "test_app/middleware/authentication"
+
+ module TestApp
+ class Routes < Hanami::Routes
+ root to: ->(*) { [200, {"Content-Length" => "4"}, ["Home"]] }
+
+ slice :admin, at: "/admin" do
+ use TestApp::Middleware::Authentication
+
+ root to: "home.show"
+ end
+ end
+ end
+ RUBY
+
+ write "slices/admin/actions/home/show.rb", <<~RUBY
+ module Admin
+ module Actions
+ module Home
+ class Show < Hanami::Action
+ def handle(req, res)
+ res.body = "Hello from admin (User ID " + req.env['AUTH_USER_ID'] + ")"
+ end
+ end
+ end
+ end
+ end
+ RUBY
+
+ require "hanami/boot"
+ end
+
+ it "excludes root scope" do
+ get "/"
+
+ expect(last_response.status).to eq 200
+ expect(last_response.body).to eq "Home"
+ expect(last_response.headers).to_not have_key("X-Auth-User-ID")
+ end
+
+ it "excludes not found routes in root scope" do
+ get "/foo"
+
+ expect(last_response.status).to eq 404
+ expect(last_response.body).to eq "Not Found"
+ expect(last_response.headers).to_not have_key("X-Auth-User-ID")
+ end
+
+ context "within slice" do
+ it "uses Rack middleware" do
+ get "/admin"
+
+ expect(last_response.status).to eq 200
+ expect(last_response.body).to eq "Hello from admin (User ID 23)"
+ expect(last_response.headers).to have_key("X-Auth-User-ID")
+ end
+
+ it "uses Rack middleware for not found paths" do
+ get "/admin/users"
+
+ expect(last_response.status).to be(404)
+ expect(last_response.body).to eq "Not Found"
+ expect(last_response.headers).to have_key("X-Auth-User-ID")
+ end
+ end
+ end
+
+ context "with complex app" do
+ let(:app_modules) { %i[TestApp Admin APIV1] }
+
+ before do
+ write "config/app.rb", <<~RUBY
+ require "hanami"
+
+ module TestApp
+ class App < Hanami::App
+ config.logger.stream = File.new("/dev/null", "w")
+ end
+ end
+ RUBY
+
+ write "lib/test_app/middleware/elapsed.rb", <<~RUBY
+ module TestApp
+ module Middleware
+ class Elapsed
+ def self.inspect
+ "<Middleware::Elapsed>"
+ end
+
+ def initialize(app)
+ @app = app
+ end
+
+ def call(env)
+ with_time_instrumentation do
+ @app.call(env)
+ end
+ end
+
+ private
+
+ def with_time_instrumentation
+ starting = now
+ status, headers, body = yield
+ ending = now
+
+ headers["X-Elapsed"] = (ending - starting).round(5).to_s
+ [status, headers, body]
+ end
+
+ def now
+ Process.clock_gettime(Process::CLOCK_MONOTONIC)
+ end
+ end
+ end
+ end
+ RUBY
+
+ write "lib/test_app/middleware/authentication.rb", <<~RUBY
+ module TestApp
+ module Middleware
+ class Authentication
+ def self.inspect
+ "<Middleware::Auth>"
+ end
+
+ def initialize(app)
+ @app = app
+ end
+
+ def call(env)
+ env["AUTH_USER_ID"] = user_id = "23"
+ status, headers, body = @app.call(env)
+ headers["X-Auth-User-ID"] = user_id
+
+ [status, headers, body]
+ end
+ end
+ end
+ end
+ RUBY
+
+ write "lib/test_app/middleware/rate_limiter.rb", <<~RUBY
+ module TestApp
+ module Middleware
+ class RateLimiter
+ def self.inspect
+ "<Middleware::API::Limiter>"
+ end
+
+ def initialize(app)
+ @app = app
+ end
+
+ def call(env)
+ status, headers, body = @app.call(env)
+ headers["X-API-Rate-Limit-Quota"] = "4000"
+
+ [status, headers, body]
+ end
+ end
+ end
+ end
+ RUBY
+
+ write "lib/test_app/middleware/api_version.rb", <<~RUBY
+ module TestApp
+ module Middleware
+ class ApiVersion
+ def self.inspect
+ "<Middleware::API::Version>"
+ end
+
+ def initialize(app)
+ @app = app
+ end
+
+ def call(env)
+ status, headers, body = @app.call(env)
+ headers["X-API-Version"] = "1"
+
+ [status, headers, body]
+ end
+ end
+ end
+ end
+ RUBY
+
+ write "lib/test_app/middleware/api_deprecation.rb", <<~RUBY
+ module TestApp
+ module Middleware
+ class ApiDeprecation
+ def self.inspect
+ "<Middleware::API::Deprecation>"
+ end
+
+ def initialize(app)
+ @app = app
+ end
+
+ def call(env)
+ status, headers, body = @app.call(env)
+ headers["X-API-Deprecated"] = "API v1 is deprecated"
+
+ [status, headers, body]
+ end
+ end
+ end
+ end
+ RUBY
+
+ write "lib/test_app/middleware/scope_identifier.rb", <<~RUBY
+ module TestApp
+ module Middleware
+ class ScopeIdentifier
+ def self.inspect
+ "<Middleware::API::ScopeIdentifier>"
+ end
+
+ def initialize(app, scope)
+ @app = app
+ @scope = scope
+ end
+
+ def call(env)
+ status, header, body = @app.call(env)
+ header["X-Identifier-" + @scope] = "true"
+ [status, header, body]
+ end
+
+ def inspect
+ "Scope identifier: " + @scope.inspect
+ end
+ end
+ end
+ end
+ RUBY
+
+ write "config/routes.rb", <<~RUBY
+ require "test_app/middleware/elapsed"
+ require "test_app/middleware/authentication"
+ require "test_app/middleware/rate_limiter"
+ require "test_app/middleware/api_version"
+ require "test_app/middleware/api_deprecation"
+ require "test_app/middleware/scope_identifier"
+
+ module TestApp
+ class Routes < Hanami::Routes
+ use TestApp::Middleware::Elapsed
+ use TestApp::Middleware::ScopeIdentifier, "Root"
+ root to: ->(*) { [200, {"Content-Length" => "4"}, ["Home (complex app)"]] }
+
+ mount ->(*) { [200, {"Content-Length" => "7"}, ["Mounted"]] }, at: "/mounted"
+
+ slice :admin, at: "/admin" do
+ use TestApp::Middleware::Authentication
+ use TestApp::Middleware::ScopeIdentifier, "Admin"
+
+ root to: "home.show"
+ end
+
+ # Without leading slash
+ # See: https://github.com/hanami/api/issues/8
+ scope "api" do
+ use TestApp::Middleware::RateLimiter
+ use TestApp::Middleware::ScopeIdentifier, "API"
+
+ root to: ->(*) { [200, {"Content-Length" => "3"}, ["API"]] }
+
+ slice :api_v1, at: "/v1" do
+ use TestApp::Middleware::ApiVersion
+ use TestApp::Middleware::ApiDeprecation
+ use TestApp::Middleware::ScopeIdentifier, "API-V1"
+
+ root to: "home.show"
+ end
+ end
+ end
+ end
+ RUBY
+
+ write "slices/admin/actions/home/show.rb", <<~RUBY
+ module Admin
+ module Actions
+ module Home
+ class Show < Hanami::Action
+ def handle(req, res)
+ res.body = "Hello from admin (User ID " + req.env['AUTH_USER_ID'] + ")"
+ end
+ end
+ end
+ end
+ end
+ RUBY
+
+ write "slices/api_v1/actions/home/show.rb", <<~RUBY
+ module APIV1
+ module Actions
+ module Home
+ class Show < Hanami::Action
+ def handle(req, res)
+ res.body = "API v1"
+ end
+ end
+ end
+ end
+ end
+ RUBY
+
+ require "hanami/boot"
+ end
+
+ it "uses Rack middleware" do
+ get "/"
+
+ expect(last_response.status).to be(200)
+ expect(last_response.body).to eq("Home (complex app)")
+ expect(last_response.headers["X-Identifier-Root"]).to eq("true")
+ expect(last_response.headers).to have_key("X-Elapsed")
+ expect(last_response.headers).to_not have_key("X-Auth-User-ID")
+ expect(last_response.headers).to_not have_key("X-API-Rate-Limit-Quota")
+ expect(last_response.headers).to_not have_key("X-API-Version")
+ end
+
+ it "uses Rack middleware for other paths" do
+ get "/foo"
+
+ expect(last_response.status).to be(404)
+ expect(last_response.headers["X-Identifier-Root"]).to eq("true")
+ expect(last_response.headers).to have_key("X-Elapsed")
+ expect(last_response.headers).to_not have_key("X-Auth-User-ID")
+ expect(last_response.headers).to_not have_key("X-API-Rate-Limit-Quota")
+ expect(last_response.headers).to_not have_key("X-API-Version")
+ end
+
+ context "scoped" do
+ it "uses Rack middleware" do
+ get "/admin"
+
+ expect(last_response.status).to be(200)
+ expect(last_response.headers["X-Identifier-Admin"]).to eq("true")
+ expect(last_response.headers).to have_key("X-Elapsed")
+ expect(last_response.headers).to have_key("X-Auth-User-ID")
+ expect(last_response.headers).to_not have_key("X-API-Rate-Limit-Quota")
+ expect(last_response.headers).to_not have_key("X-API-Version")
+ end
+
+ it "uses Rack middleware for other paths" do
+ get "/admin/users"
+
+ expect(last_response.status).to be(404)
+ expect(last_response.headers["X-Identifier-Admin"]).to eq("true")
+ expect(last_response.headers).to have_key("X-Elapsed")
+ expect(last_response.headers).to have_key("X-Elapsed")
+ expect(last_response.headers).to have_key("X-Auth-User-ID")
+ expect(last_response.headers).to_not have_key("X-API-Rate-Limit-Quota")
+ expect(last_response.headers).to_not have_key("X-API-Version")
+ end
+
+ # See: https://github.com/hanami/api/issues/8
+ it "uses Rack middleware for scope w/o leading slash" do
+ get "/api"
+
+ expect(last_response.status).to be(200)
+ expect(last_response.headers["X-Identifier-Api"]).to eq("true")
+ expect(last_response.headers).to have_key("X-Elapsed")
+ expect(last_response.headers).to_not have_key("X-Auth-User-ID")
+ expect(last_response.headers).to have_key("X-API-Rate-Limit-Quota")
+ expect(last_response.headers).to_not have_key("X-API-Version")
+ end
+
+ # See: https://github.com/hanami/api/issues/8
+ it "uses Rack middleware for nested scope w/o leading slash" do
+ get "/api/v1"
+
+ expect(last_response.status).to be(200)
+ expect(last_response.headers["X-Identifier-API-V1"]).to eq("true")
+ expect(last_response.headers).to have_key("X-Elapsed")
+ expect(last_response.headers).to_not have_key("X-Auth-User-ID")
+ expect(last_response.headers).to have_key("X-API-Rate-Limit-Quota")
+ expect(last_response.headers).to have_key("X-API-Deprecated")
+ expect(last_response.headers["X-API-Version"]).to eq("1")
+ end
end
end
end