mirror of
				https://gitea.invidious.io/iv-org/shard-kemal.git
				synced 2024-08-15 00:53:36 +00:00 
			
		
		
		
	Decouple specs from global state to isolated tests
This commit is contained in:
		
							parent
							
								
									29b18c927c
								
							
						
					
					
						commit
						2e42b3f48c
					
				
					 19 changed files with 336 additions and 304 deletions
				
			
		|  | @ -1,81 +1,62 @@ | ||||||
| require "./spec_helper" | require "./spec_helper" | ||||||
| 
 | 
 | ||||||
| describe "Context" do | describe "Context" do | ||||||
|   context "headers" do |  | ||||||
|   it "sets content type" do |   it "sets content type" do | ||||||
|       get "/" do |env| |     app = Kemal::Base.new | ||||||
|  |     app.get "/" do |env| | ||||||
|       env.response.content_type = "application/json" |       env.response.content_type = "application/json" | ||||||
|       "Hello" |       "Hello" | ||||||
|     end |     end | ||||||
|     request = HTTP::Request.new("GET", "/") |     request = HTTP::Request.new("GET", "/") | ||||||
|       client_response = call_request_on_app(request) |     client_response = call_request_on_app(app, request) | ||||||
|     client_response.headers["Content-Type"].should eq("application/json") |     client_response.headers["Content-Type"].should eq("application/json") | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   it "parses headers" do |   it "parses headers" do | ||||||
|       get "/" do |env| |     app = Kemal::Base.new | ||||||
|  |     app.get "/" do |env| | ||||||
|       name = env.request.headers["name"] |       name = env.request.headers["name"] | ||||||
|       "Hello #{name}" |       "Hello #{name}" | ||||||
|     end |     end | ||||||
|     headers = HTTP::Headers.new |     headers = HTTP::Headers.new | ||||||
|     headers["name"] = "kemal" |     headers["name"] = "kemal" | ||||||
|     request = HTTP::Request.new("GET", "/", headers) |     request = HTTP::Request.new("GET", "/", headers) | ||||||
|       client_response = call_request_on_app(request) |     client_response = call_request_on_app(app, request) | ||||||
|     client_response.body.should eq "Hello kemal" |     client_response.body.should eq "Hello kemal" | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   it "sets response headers" do |   it "sets response headers" do | ||||||
|       get "/" do |env| |     app = Kemal::Base.new | ||||||
|  |     app.get "/" do |env| | ||||||
|       env.response.headers.add "Accept-Language", "tr" |       env.response.headers.add "Accept-Language", "tr" | ||||||
|     end |     end | ||||||
|     request = HTTP::Request.new("GET", "/") |     request = HTTP::Request.new("GET", "/") | ||||||
|       client_response = call_request_on_app(request) |     client_response = call_request_on_app(app, request) | ||||||
|     client_response.headers["Accept-Language"].should eq "tr" |     client_response.headers["Accept-Language"].should eq "tr" | ||||||
|   end |   end | ||||||
|   end |  | ||||||
| 
 | 
 | ||||||
|   context "storage" do |   it "can store variables" do | ||||||
|     it "can store primitive types" do |     app = Kemal::Base.new | ||||||
|       before_get "/" do |env| |     app.before_get "/" do |env| | ||||||
|         env.set "before_get", "Kemal" |  | ||||||
|         env.set "before_get_int", 123 |  | ||||||
|         env.set "before_get_float", 3.5 |  | ||||||
|       end |  | ||||||
| 
 |  | ||||||
|       get "/" do |env| |  | ||||||
|         { |  | ||||||
|           before_get:       env.get("before_get"), |  | ||||||
|           before_get_int:   env.get("before_get_int"), |  | ||||||
|           before_get_float: env.get("before_get_float"), |  | ||||||
|         } |  | ||||||
|       end |  | ||||||
| 
 |  | ||||||
|       request = HTTP::Request.new("GET", "/") |  | ||||||
|       io = IO::Memory.new |  | ||||||
|       response = HTTP::Server::Response.new(io) |  | ||||||
|       context = HTTP::Server::Context.new(request, response) |  | ||||||
|       Kemal::FilterHandler::INSTANCE.call(context) |  | ||||||
|       Kemal::RouteHandler::INSTANCE.call(context) |  | ||||||
| 
 |  | ||||||
|       context.get("before_get").should eq "Kemal" |  | ||||||
|       context.get("before_get_int").should eq 123 |  | ||||||
|       context.get("before_get_float").should eq 3.5 |  | ||||||
|     end |  | ||||||
| 
 |  | ||||||
|     it "can store custom types" do |  | ||||||
|       before_get "/" do |env| |  | ||||||
|       t = TestContextStorageType.new |       t = TestContextStorageType.new | ||||||
|       t.id = 32 |       t.id = 32 | ||||||
|       a = AnotherContextStorageType.new |       a = AnotherContextStorageType.new | ||||||
| 
 |       env.set "key", "value" | ||||||
|  |       env.set "before_get", "Kemal" | ||||||
|  |       env.set "before_get_int", 123 | ||||||
|       env.set "before_get_context_test", t |       env.set "before_get_context_test", t | ||||||
|       env.set "another_context_test", a |       env.set "another_context_test", a | ||||||
|  |       env.set "before_get_float", 3.5 | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|       get "/" do |env| |     app.get "/" do |env| | ||||||
|  |       env.set "key", "value" | ||||||
|       { |       { | ||||||
|  |         key:                     env.get("key"), | ||||||
|  |         before_get:              env.get("before_get"), | ||||||
|  |         before_get_int:          env.get("before_get_int"), | ||||||
|  |         before_get_float:        env.get("before_get_float"), | ||||||
|         before_get_context_test: env.get("before_get_context_test"), |         before_get_context_test: env.get("before_get_context_test"), | ||||||
|           another_context_test:    env.get("another_context_test"), |  | ||||||
|       } |       } | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|  | @ -83,20 +64,9 @@ describe "Context" do | ||||||
|     io = IO::Memory.new |     io = IO::Memory.new | ||||||
|     response = HTTP::Server::Response.new(io) |     response = HTTP::Server::Response.new(io) | ||||||
|     context = HTTP::Server::Context.new(request, response) |     context = HTTP::Server::Context.new(request, response) | ||||||
|       Kemal::FilterHandler::INSTANCE.call(context) |     context.app = app | ||||||
|       Kemal::RouteHandler::INSTANCE.call(context) |     app.filter_handler.call(context) | ||||||
| 
 |     app.route_handler.call(context) | ||||||
|       context.get("before_get_context_test").as(TestContextStorageType).id.should eq 32 |  | ||||||
|       context.get("another_context_test").as(AnotherContextStorageType).name.should eq "kemal-context" |  | ||||||
|     end |  | ||||||
| 
 |  | ||||||
|     request = HTTP::Request.new("GET", "/") |  | ||||||
|     io = IO::Memory.new |  | ||||||
|     response = HTTP::Server::Response.new(io) |  | ||||||
|     context = HTTP::Server::Context.new(request, response) |  | ||||||
|     context.app = Kemal.application |  | ||||||
|     Kemal.application.filter_handler.call(context) |  | ||||||
|     Kemal.application.route_handler.call(context) |  | ||||||
|     context.store["key"].should eq "value" |     context.store["key"].should eq "value" | ||||||
|     context.store["before_get"].should eq "Kemal" |     context.store["before_get"].should eq "Kemal" | ||||||
|     context.store["before_get_int"].should eq 123 |     context.store["before_get_int"].should eq 123 | ||||||
|  |  | ||||||
							
								
								
									
										58
									
								
								spec/dsl_helper.cr
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								spec/dsl_helper.cr
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,58 @@ | ||||||
|  | require "./spec_helper" | ||||||
|  | require "../src/kemal/dsl" | ||||||
|  | 
 | ||||||
|  | include Kemal | ||||||
|  | 
 | ||||||
|  | class CustomLogHandler < Kemal::BaseLogHandler | ||||||
|  |   def call(env) | ||||||
|  |     call_next env | ||||||
|  |   end | ||||||
|  | 
 | ||||||
|  |   def write(message) | ||||||
|  |   end | ||||||
|  | end | ||||||
|  | 
 | ||||||
|  | class TestContextStorageType | ||||||
|  |   property id | ||||||
|  |   @id = 1 | ||||||
|  | 
 | ||||||
|  |   def to_s | ||||||
|  |     @id | ||||||
|  |   end | ||||||
|  | end | ||||||
|  | 
 | ||||||
|  | class AnotherContextStorageType | ||||||
|  |   property name | ||||||
|  |   @name = "kemal-context" | ||||||
|  | end | ||||||
|  | 
 | ||||||
|  | add_context_storage_type(TestContextStorageType) | ||||||
|  | add_context_storage_type(AnotherContextStorageType) | ||||||
|  | 
 | ||||||
|  | def create_request_and_return_io(handler, request) | ||||||
|  |   io = IO::Memory.new | ||||||
|  |   response = HTTP::Server::Response.new(io) | ||||||
|  |   context = HTTP::Server::Context.new(request, response) | ||||||
|  |   context.app = Kemal.application | ||||||
|  |   handler.call(context) | ||||||
|  |   response.close | ||||||
|  |   io.rewind | ||||||
|  |   io | ||||||
|  | end | ||||||
|  | 
 | ||||||
|  | def call_request_on_app(request) | ||||||
|  |   call_request_on_app(Kemal.application, request) | ||||||
|  | end | ||||||
|  | 
 | ||||||
|  | def build_main_handler | ||||||
|  |   build_main_handler(Kemal.application) | ||||||
|  | end | ||||||
|  | 
 | ||||||
|  | Spec.before_each do | ||||||
|  |   config = Kemal.config | ||||||
|  |   config.env = "development" | ||||||
|  | end | ||||||
|  | 
 | ||||||
|  | Spec.after_each do | ||||||
|  |   Kemal.application.clear | ||||||
|  | end | ||||||
|  | @ -1,18 +1,13 @@ | ||||||
| require "./spec_helper" | require "./dsl_helper" | ||||||
| 
 |  | ||||||
| private INSTANCE = Kemal::ExceptionHandler.new |  | ||||||
| 
 | 
 | ||||||
| describe "Kemal::ExceptionHandler" do | describe "Kemal::ExceptionHandler" do | ||||||
|   it "renders 404 on route not found" do |   it "renders 404 on route not found" do | ||||||
|     get "/" do |  | ||||||
|       "Hello" |  | ||||||
|     end |  | ||||||
| 
 |  | ||||||
|     request = HTTP::Request.new("GET", "/asd") |     request = HTTP::Request.new("GET", "/asd") | ||||||
|     io = IO::Memory.new |     io = IO::Memory.new | ||||||
|     response = HTTP::Server::Response.new(io) |     response = HTTP::Server::Response.new(io) | ||||||
|     context = HTTP::Server::Context.new(request, response) |     context = HTTP::Server::Context.new(request, response) | ||||||
|     INSTANCE.call(context) |     subject = Kemal::ExceptionHandler.new(Kemal::Base.new) | ||||||
|  |     subject.call(context) | ||||||
|     response.close |     response.close | ||||||
|     io.rewind |     io.rewind | ||||||
|     response = HTTP::Client::Response.from_io(io, decompress: false) |     response = HTTP::Client::Response.from_io(io, decompress: false) | ||||||
|  | @ -20,19 +15,21 @@ describe "Kemal::ExceptionHandler" do | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   it "renders custom error" do |   it "renders custom error" do | ||||||
|     error 403 do |  | ||||||
|       "403 error" |  | ||||||
|     end |  | ||||||
|     get "/" do |env| |  | ||||||
|       env.response.status_code = 403 |  | ||||||
|     end |  | ||||||
|     request = HTTP::Request.new("GET", "/") |     request = HTTP::Request.new("GET", "/") | ||||||
|     io = IO::Memory.new |     io = IO::Memory.new | ||||||
|     response = HTTP::Server::Response.new(io) |     response = HTTP::Server::Response.new(io) | ||||||
|     context = HTTP::Server::Context.new(request, response) |     context = HTTP::Server::Context.new(request, response) | ||||||
|     context.app = Kemal.application |     app = Kemal::Base.new | ||||||
|     INSTANCE.next = Kemal::RouteHandler.new |     app.error 403 do | ||||||
|     INSTANCE.call(context) |       "403 error" | ||||||
|  |     end | ||||||
|  |     app.get "/" do |env| | ||||||
|  |       env.response.status_code = 403 | ||||||
|  |     end | ||||||
|  |     context.app = app | ||||||
|  |     subject = Kemal::ExceptionHandler.new(app) | ||||||
|  |     subject.next = Kemal::RouteHandler.new | ||||||
|  |     subject.call(context) | ||||||
|     response.close |     response.close | ||||||
|     io.rewind |     io.rewind | ||||||
|     response = HTTP::Client::Response.from_io(io, decompress: false) |     response = HTTP::Client::Response.from_io(io, decompress: false) | ||||||
|  | @ -42,19 +39,21 @@ describe "Kemal::ExceptionHandler" do | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   it "renders custom 500 error" do |   it "renders custom 500 error" do | ||||||
|     error 500 do |  | ||||||
|       "Something happened" |  | ||||||
|     end |  | ||||||
|     get "/" do |env| |  | ||||||
|       env.response.status_code = 500 |  | ||||||
|     end |  | ||||||
|     request = HTTP::Request.new("GET", "/") |     request = HTTP::Request.new("GET", "/") | ||||||
|     io = IO::Memory.new |     io = IO::Memory.new | ||||||
|     response = HTTP::Server::Response.new(io) |     response = HTTP::Server::Response.new(io) | ||||||
|     context = HTTP::Server::Context.new(request, response) |     context = HTTP::Server::Context.new(request, response) | ||||||
|     context.app = Kemal.application |     app = Kemal::Base.new | ||||||
|     INSTANCE.next = Kemal::RouteHandler.new |     app.error 500 do |env| | ||||||
|     INSTANCE.call(context) |       "Something happened" | ||||||
|  |     end | ||||||
|  |     app.get "/" do |env| | ||||||
|  |       env.response.status_code = 500 | ||||||
|  |     end | ||||||
|  |     context.app = app | ||||||
|  |     subject = Kemal::ExceptionHandler.new(app) | ||||||
|  |     subject.next = Kemal::RouteHandler.new | ||||||
|  |     subject.call(context) | ||||||
|     response.close |     response.close | ||||||
|     io.rewind |     io.rewind | ||||||
|     response = HTTP::Client::Response.from_io(io, decompress: false) |     response = HTTP::Client::Response.from_io(io, decompress: false) | ||||||
|  | @ -64,20 +63,22 @@ describe "Kemal::ExceptionHandler" do | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   it "keeps the specified error Content-Type" do |   it "keeps the specified error Content-Type" do | ||||||
|     error 500 do |  | ||||||
|       "Something happened" |  | ||||||
|     end |  | ||||||
|     get "/" do |env| |  | ||||||
|       env.response.content_type = "application/json" |  | ||||||
|       env.response.status_code = 500 |  | ||||||
|     end |  | ||||||
|     request = HTTP::Request.new("GET", "/") |     request = HTTP::Request.new("GET", "/") | ||||||
|     io = IO::Memory.new |     io = IO::Memory.new | ||||||
|     response = HTTP::Server::Response.new(io) |     response = HTTP::Server::Response.new(io) | ||||||
|     context = HTTP::Server::Context.new(request, response) |     context = HTTP::Server::Context.new(request, response) | ||||||
|     context.app = Kemal.application |     app = Kemal::Base.new | ||||||
|     INSTANCE.next = Kemal::RouteHandler.new |     app.error 500 do |env| | ||||||
|     INSTANCE.call(context) |       "Something happened" | ||||||
|  |     end | ||||||
|  |     app.get "/" do |env| | ||||||
|  |       env.response.content_type = "application/json" | ||||||
|  |       env.response.status_code = 500 | ||||||
|  |     end | ||||||
|  |     context.app = app | ||||||
|  |     subject = Kemal::ExceptionHandler.new(app) | ||||||
|  |     subject.next = Kemal::RouteHandler.new | ||||||
|  |     subject.call(context) | ||||||
|     response.close |     response.close | ||||||
|     io.rewind |     io.rewind | ||||||
|     response = HTTP::Client::Response.from_io(io, decompress: false) |     response = HTTP::Client::Response.from_io(io, decompress: false) | ||||||
|  | @ -87,20 +88,22 @@ describe "Kemal::ExceptionHandler" do | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   it "renders custom error with env and error" do |   it "renders custom error with env and error" do | ||||||
|     error 500 do |_, err| |  | ||||||
|       err.message |  | ||||||
|     end |  | ||||||
|     get "/" do |env| |  | ||||||
|       env.response.content_type = "application/json" |  | ||||||
|       env.response.status_code = 500 |  | ||||||
|     end |  | ||||||
|     request = HTTP::Request.new("GET", "/") |     request = HTTP::Request.new("GET", "/") | ||||||
|     io = IO::Memory.new |     io = IO::Memory.new | ||||||
|     response = HTTP::Server::Response.new(io) |     response = HTTP::Server::Response.new(io) | ||||||
|     context = HTTP::Server::Context.new(request, response) |     context = HTTP::Server::Context.new(request, response) | ||||||
|     context.app = Kemal.application |     app = Kemal::Base.new | ||||||
|     INSTANCE.next = Kemal::RouteHandler.new |     app.error 500 do |env, err| | ||||||
|     INSTANCE.call(context) |       err.message | ||||||
|  |     end | ||||||
|  |     app.get "/" do |env| | ||||||
|  |       env.response.content_type = "application/json" | ||||||
|  |       env.response.status_code = 500 | ||||||
|  |     end | ||||||
|  |     context.app = app | ||||||
|  |     subject = Kemal::ExceptionHandler.new(Kemal::Base.new) | ||||||
|  |     subject.next = Kemal::RouteHandler.new | ||||||
|  |     subject.call(context) | ||||||
|     response.close |     response.close | ||||||
|     io.rewind |     io.rewind | ||||||
|     response = HTTP::Client::Response.from_io(io, decompress: false) |     response = HTTP::Client::Response.from_io(io, decompress: false) | ||||||
|  |  | ||||||
|  | @ -78,81 +78,88 @@ describe "Handler" do | ||||||
|     filter_middleware._add_route_filter("GET", "/", :before) do |env| |     filter_middleware._add_route_filter("GET", "/", :before) do |env| | ||||||
|       env.response << " so" |       env.response << " so" | ||||||
|     end |     end | ||||||
|     Kemal.application.add_filter_handler filter_middleware |     app = Kemal::Base.new | ||||||
|  |     app.add_filter_handler filter_middleware | ||||||
| 
 | 
 | ||||||
|     add_handler CustomTestHandler.new |     app.add_handler CustomTestHandler.new | ||||||
| 
 | 
 | ||||||
|     get "/" do |     app.get "/" do |env| | ||||||
|       " Great" |       " Great" | ||||||
|     end |     end | ||||||
|     request = HTTP::Request.new("GET", "/") |     request = HTTP::Request.new("GET", "/") | ||||||
|     client_response = call_request_on_app(request) |     client_response = call_request_on_app(app, request) | ||||||
|     client_response.status_code.should eq(200) |     client_response.status_code.should eq(200) | ||||||
|     client_response.body.should eq("Kemal is so Great") |     client_response.body.should eq("Kemal is so Great") | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   it "runs specified only_routes in middleware" do |   it "runs specified only_routes in middleware" do | ||||||
|     get "/only" do |     app = Kemal::Base.new | ||||||
|  |     app.get "/only" do |env| | ||||||
|       "Get" |       "Get" | ||||||
|     end |     end | ||||||
|     add_handler OnlyHandler.new |     app.add_handler OnlyHandler.new | ||||||
|     request = HTTP::Request.new("GET", "/only") |     request = HTTP::Request.new("GET", "/only") | ||||||
|     client_response = call_request_on_app(request) |     client_response = call_request_on_app(app, request) | ||||||
|     client_response.body.should eq "OnlyGet" |     client_response.body.should eq "OnlyGet" | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   it "doesn't run specified exclude_routes in middleware" do |   it "doesn't run specified exclude_routes in middleware" do | ||||||
|     get "/" do |     app = Kemal::Base.new | ||||||
|  |     app.get "/" do |env| | ||||||
|       "Get" |       "Get" | ||||||
|     end |     end | ||||||
|     get "/exclude" do |     app.get "/exclude" do | ||||||
|       "Exclude" |       "Exclude" | ||||||
|     end |     end | ||||||
|     add_handler ExcludeHandler.new |     app.add_handler ExcludeHandler.new | ||||||
|     request = HTTP::Request.new("GET", "/") |     request = HTTP::Request.new("GET", "/") | ||||||
|     client_response = call_request_on_app(request) |     client_response = call_request_on_app(app, request) | ||||||
|     client_response.body.should eq "ExcludeGet" |     client_response.body.should eq "ExcludeGet" | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   it "runs specified only_routes with method in middleware" do |   it "runs specified only_routes with method in middleware" do | ||||||
|     post "/only" do |     app = Kemal::Base.new | ||||||
|  |     app.post "/only" do | ||||||
|       "Post" |       "Post" | ||||||
|     end |     end | ||||||
|     get "/only" do |     app.get "/only" do | ||||||
|       "Get" |       "Get" | ||||||
|     end |     end | ||||||
|     add_handler PostOnlyHandler.new |     app.add_handler PostOnlyHandler.new | ||||||
|     request = HTTP::Request.new("POST", "/only") |     request = HTTP::Request.new("POST", "/only") | ||||||
|     client_response = call_request_on_app(request) |     client_response = call_request_on_app(app, request) | ||||||
|     client_response.body.should eq "OnlyPost" |     client_response.body.should eq "OnlyPost" | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   it "doesn't run specified exclude_routes with method in middleware" do |   it "doesn't run specified exclude_routes with method in middleware" do | ||||||
|     post "/exclude" do |     app = Kemal::Base.new | ||||||
|  |     app.post "/exclude" do | ||||||
|       "Post" |       "Post" | ||||||
|     end |     end | ||||||
|     post "/only" do |     app.post "/only" do | ||||||
|       "Post" |       "Post" | ||||||
|     end |     end | ||||||
|     add_handler PostOnlyHandler.new |     app.add_handler PostOnlyHandler.new | ||||||
|     add_handler PostExcludeHandler.new |     app.add_handler PostExcludeHandler.new | ||||||
|     request = HTTP::Request.new("POST", "/only") |     request = HTTP::Request.new("POST", "/only") | ||||||
|     client_response = call_request_on_app(request) |     client_response = call_request_on_app(app, request) | ||||||
|     client_response.body.should eq "OnlyExcludePost" |     client_response.body.should eq "OnlyExcludePost" | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   it "adds a handler at given position" do |   it "adds a handler at given position" do | ||||||
|     post_handler = PostOnlyHandler.new |     post_handler = PostOnlyHandler.new | ||||||
|     add_handler post_handler, 1 |     app = Kemal::Base.new | ||||||
|     Kemal.application.setup |     app.add_handler post_handler, 1 | ||||||
|     Kemal.application.handlers[1].should eq post_handler |     app.setup | ||||||
|  |     app.handlers[1].should eq post_handler | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   it "assigns custom handlers" do |   it "assigns custom handlers" do | ||||||
|     post_only_handler = PostOnlyHandler.new |     post_only_handler = PostOnlyHandler.new | ||||||
|     post_exclude_handler = PostExcludeHandler.new |     post_exclude_handler = PostExcludeHandler.new | ||||||
|     Kemal.application.handlers = [post_only_handler, post_exclude_handler] |     app = Kemal::Base.new | ||||||
|     Kemal.application.handlers.should eq [post_only_handler, post_exclude_handler] |     app.handlers = [post_only_handler, post_exclude_handler] | ||||||
|  |     app.handlers.should eq [post_only_handler, post_exclude_handler] | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   it "is able to use %w in macros" do |   it "is able to use %w in macros" do | ||||||
|  |  | ||||||
|  | @ -10,9 +10,10 @@ describe "Macros" do | ||||||
| 
 | 
 | ||||||
|   describe "#add_handler" do |   describe "#add_handler" do | ||||||
|     it "adds a custom handler" do |     it "adds a custom handler" do | ||||||
|       add_handler CustomTestHandler.new |       app = Kemal::Application.new | ||||||
|       Kemal.application.setup |       app.add_handler CustomTestHandler.new | ||||||
|       Kemal.application.handlers.size.should eq 8 |       app.setup | ||||||
|  |       app.handlers.size.should eq 8 | ||||||
|     end |     end | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|  | @ -23,7 +24,6 @@ describe "Macros" do | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|     it "sets a custom logger" do |     it "sets a custom logger" do | ||||||
|       config = Kemal.config |  | ||||||
|       logger CustomLogHandler.new |       logger CustomLogHandler.new | ||||||
|       Kemal.application.logger.should be_a(CustomLogHandler) |       Kemal.application.logger.should be_a(CustomLogHandler) | ||||||
|     end |     end | ||||||
|  | @ -31,32 +31,34 @@ describe "Macros" do | ||||||
| 
 | 
 | ||||||
|   describe "#halt" do |   describe "#halt" do | ||||||
|     it "can break block with halt macro" do |     it "can break block with halt macro" do | ||||||
|       get "/non-breaking" do |       app = Kemal::Base.new | ||||||
|  |       app.get "/non-breaking" do |env| | ||||||
|         "hello" |         "hello" | ||||||
|         "world" |         "world" | ||||||
|       end |       end | ||||||
|       request = HTTP::Request.new("GET", "/non-breaking") |       request = HTTP::Request.new("GET", "/non-breaking") | ||||||
|       client_response = call_request_on_app(request) |       client_response = call_request_on_app(app, request) | ||||||
|       client_response.status_code.should eq(200) |       client_response.status_code.should eq(200) | ||||||
|       client_response.body.should eq("world") |       client_response.body.should eq("world") | ||||||
| 
 | 
 | ||||||
|       get "/breaking" do |env| |       app.get "/breaking" do |env| | ||||||
|         halt env, 404, "hello" |         halt env, 404, "hello" | ||||||
|         "world" |         "world" | ||||||
|       end |       end | ||||||
|       request = HTTP::Request.new("GET", "/breaking") |       request = HTTP::Request.new("GET", "/breaking") | ||||||
|       client_response = call_request_on_app(request) |       client_response = call_request_on_app(app, request) | ||||||
|       client_response.status_code.should eq(404) |       client_response.status_code.should eq(404) | ||||||
|       client_response.body.should eq("hello") |       client_response.body.should eq("hello") | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|     it "can break block with halt macro using default values" do |     it "can break block with halt macro using default values" do | ||||||
|       get "/" do |env| |       app = Kemal::Base.new | ||||||
|  |       app.get "/" do |env| | ||||||
|         halt env |         halt env | ||||||
|         "world" |         "world" | ||||||
|       end |       end | ||||||
|       request = HTTP::Request.new("GET", "/") |       request = HTTP::Request.new("GET", "/") | ||||||
|       client_response = call_request_on_app(request) |       client_response = call_request_on_app(app, request) | ||||||
|       client_response.status_code.should eq(200) |       client_response.status_code.should eq(200) | ||||||
|       client_response.body.should eq("") |       client_response.body.should eq("") | ||||||
|     end |     end | ||||||
|  | @ -64,7 +66,8 @@ describe "Macros" do | ||||||
| 
 | 
 | ||||||
|   describe "#headers" do |   describe "#headers" do | ||||||
|     it "can add headers" do |     it "can add headers" do | ||||||
|       get "/headers" do |env| |       app = Kemal::Base.new | ||||||
|  |       app.get "/headers" do |env| | ||||||
|         env.response.headers.add "Content-Type", "image/png" |         env.response.headers.add "Content-Type", "image/png" | ||||||
|         headers env, { |         headers env, { | ||||||
|           "Access-Control-Allow-Origin" => "*", |           "Access-Control-Allow-Origin" => "*", | ||||||
|  | @ -72,7 +75,7 @@ describe "Macros" do | ||||||
|         } |         } | ||||||
|       end |       end | ||||||
|       request = HTTP::Request.new("GET", "/headers") |       request = HTTP::Request.new("GET", "/headers") | ||||||
|       response = call_request_on_app(request) |       response = call_request_on_app(app, request) | ||||||
|       response.headers["Access-Control-Allow-Origin"].should eq("*") |       response.headers["Access-Control-Allow-Origin"].should eq("*") | ||||||
|       response.headers["Content-Type"].should eq("text/plain") |       response.headers["Content-Type"].should eq("text/plain") | ||||||
|     end |     end | ||||||
|  | @ -80,36 +83,39 @@ describe "Macros" do | ||||||
| 
 | 
 | ||||||
|   describe "#send_file" do |   describe "#send_file" do | ||||||
|     it "sends file with given path and default mime-type" do |     it "sends file with given path and default mime-type" do | ||||||
|       get "/" do |env| |       app = Kemal::Base.new | ||||||
|  |       app.get "/" do |env| | ||||||
|         send_file env, "./spec/asset/hello.ecr" |         send_file env, "./spec/asset/hello.ecr" | ||||||
|       end |       end | ||||||
| 
 | 
 | ||||||
|       request = HTTP::Request.new("GET", "/") |       request = HTTP::Request.new("GET", "/") | ||||||
|       response = call_request_on_app(request) |       response = call_request_on_app(app, request) | ||||||
|       response.status_code.should eq(200) |       response.status_code.should eq(200) | ||||||
|       response.headers["Content-Type"].should eq("application/octet-stream") |       response.headers["Content-Type"].should eq("application/octet-stream") | ||||||
|       response.headers["Content-Length"].should eq("18") |       response.headers["Content-Length"].should eq("18") | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|     it "sends file with given path and given mime-type" do |     it "sends file with given path and given mime-type" do | ||||||
|       get "/" do |env| |       app = Kemal::Base.new | ||||||
|  |       app.get "/" do |env| | ||||||
|         send_file env, "./spec/asset/hello.ecr", "image/jpeg" |         send_file env, "./spec/asset/hello.ecr", "image/jpeg" | ||||||
|       end |       end | ||||||
| 
 | 
 | ||||||
|       request = HTTP::Request.new("GET", "/") |       request = HTTP::Request.new("GET", "/") | ||||||
|       response = call_request_on_app(request) |       response = call_request_on_app(app, request) | ||||||
|       response.status_code.should eq(200) |       response.status_code.should eq(200) | ||||||
|       response.headers["Content-Type"].should eq("image/jpeg") |       response.headers["Content-Type"].should eq("image/jpeg") | ||||||
|       response.headers["Content-Length"].should eq("18") |       response.headers["Content-Length"].should eq("18") | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|     it "sends file with binary stream" do |     it "sends file with binary stream" do | ||||||
|       get "/" do |env| |       app = Kemal::Base.new | ||||||
|  |       app.get "/" do |env| | ||||||
|         send_file env, "Serdar".to_slice |         send_file env, "Serdar".to_slice | ||||||
|       end |       end | ||||||
| 
 | 
 | ||||||
|       request = HTTP::Request.new("GET", "/") |       request = HTTP::Request.new("GET", "/") | ||||||
|       response = call_request_on_app(request) |       response = call_request_on_app(app, request) | ||||||
|       response.status_code.should eq(200) |       response.status_code.should eq(200) | ||||||
|       response.headers["Content-Type"].should eq("application/octet-stream") |       response.headers["Content-Type"].should eq("application/octet-stream") | ||||||
|       response.headers["Content-Length"].should eq("6") |       response.headers["Content-Length"].should eq("6") | ||||||
|  |  | ||||||
|  | @ -1,4 +1,4 @@ | ||||||
| require "./spec_helper" | require "./dsl_helper" | ||||||
| 
 | 
 | ||||||
| describe "ParamParser" do | describe "ParamParser" do | ||||||
|   it "parses query params" do |   it "parses query params" do | ||||||
|  |  | ||||||
|  | @ -1,4 +1,4 @@ | ||||||
| require "./spec_helper" | require "./dsl_helper" | ||||||
| 
 | 
 | ||||||
| describe "Kemal::RouteHandler" do | describe "Kemal::RouteHandler" do | ||||||
|   it "routes" do |   it "routes" do | ||||||
|  |  | ||||||
|  | @ -1,4 +1,4 @@ | ||||||
| require "./spec_helper" | require "./dsl_helper" | ||||||
| 
 | 
 | ||||||
| describe "Route" do | describe "Route" do | ||||||
|   describe "match?" do |   describe "match?" do | ||||||
|  |  | ||||||
|  | @ -1,4 +1,4 @@ | ||||||
| require "./spec_helper" | require "./dsl_helper" | ||||||
| 
 | 
 | ||||||
| private def run(code) | private def run(code) | ||||||
|   code = <<-CR |   code = <<-CR | ||||||
|  |  | ||||||
|  | @ -1,89 +1,24 @@ | ||||||
| require "spec" | require "spec" | ||||||
| require "../src/**" | require "../src/kemal" | ||||||
| require "../src/kemal/base" |  | ||||||
| require "../src/kemal/dsl" |  | ||||||
| 
 | 
 | ||||||
| include Kemal | def call_request_on_app(app, request) | ||||||
| 
 |  | ||||||
| class CustomLogHandler < Kemal::BaseLogHandler |  | ||||||
|   def call(env) |  | ||||||
|     call_next env |  | ||||||
|   end |  | ||||||
| 
 |  | ||||||
|   def write(message) |  | ||||||
|   end |  | ||||||
| end |  | ||||||
| 
 |  | ||||||
| class TestContextStorageType |  | ||||||
|   property id |  | ||||||
|   @id = 1 |  | ||||||
| 
 |  | ||||||
|   def to_s |  | ||||||
|     @id |  | ||||||
|   end |  | ||||||
| end |  | ||||||
| 
 |  | ||||||
| class AnotherContextStorageType |  | ||||||
|   property name |  | ||||||
|   @name = "kemal-context" |  | ||||||
| end |  | ||||||
| 
 |  | ||||||
| add_context_storage_type(TestContextStorageType) |  | ||||||
| add_context_storage_type(AnotherContextStorageType) |  | ||||||
| 
 |  | ||||||
| def create_request_and_return_io_and_context(handler, request) |  | ||||||
|   io = IO::Memory.new |   io = IO::Memory.new | ||||||
|   response = HTTP::Server::Response.new(io) |   response = HTTP::Server::Response.new(io) | ||||||
|   context = HTTP::Server::Context.new(request, response) |   context = HTTP::Server::Context.new(request, response) | ||||||
|   context.app = Kemal.application |   main_handler = build_main_handler(app) | ||||||
|   handler.call(context) |  | ||||||
|   response.close |  | ||||||
|   io.rewind |  | ||||||
|   {io, context} |  | ||||||
| end |  | ||||||
| 
 |  | ||||||
| def create_ws_request_and_return_io_and_context(handler, request) |  | ||||||
|   io = IO::Memory.new |  | ||||||
|   response = HTTP::Server::Response.new(io) |  | ||||||
|   context = HTTP::Server::Context.new(request, response) |  | ||||||
|   context.app = Kemal.application |  | ||||||
|   begin |  | ||||||
|     handler.call context |  | ||||||
|   rescue IO::Error |  | ||||||
|     # Raises because the IO::Memory is empty |  | ||||||
|   end |  | ||||||
|   io.rewind |  | ||||||
|   {io, context} |  | ||||||
| end |  | ||||||
| 
 |  | ||||||
| def call_request_on_app(request) |  | ||||||
|   io = IO::Memory.new |  | ||||||
|   response = HTTP::Server::Response.new(io) |  | ||||||
|   context = HTTP::Server::Context.new(request, response) |  | ||||||
|   main_handler = build_main_handler |  | ||||||
|   main_handler.call context |   main_handler.call context | ||||||
|   response.close |   response.close | ||||||
|   io.rewind |   io.rewind | ||||||
|   HTTP::Client::Response.from_io(io, decompress: false) |   HTTP::Client::Response.from_io(io, decompress: false) | ||||||
| end | end | ||||||
| 
 | 
 | ||||||
| def build_main_handler | def build_main_handler(app) | ||||||
|   Kemal.application.setup |   app.setup | ||||||
|   main_handler = Kemal.application.handlers.first |   main_handler = app.handlers.first | ||||||
|   current_handler = main_handler |   current_handler = main_handler | ||||||
|   Kemal.application.handlers.each_with_index do |handler, index| |   app.handlers.each_with_index do |handler, index| | ||||||
|     current_handler.next = handler |     current_handler.next = handler | ||||||
|     current_handler = handler |     current_handler = handler | ||||||
|   end |   end | ||||||
|   main_handler |   main_handler | ||||||
| end | end | ||||||
| 
 |  | ||||||
| Spec.before_each do |  | ||||||
|   config = Kemal.config |  | ||||||
|   config.env = "development" |  | ||||||
|   config.logging = false |  | ||||||
| end |  | ||||||
| 
 |  | ||||||
| Spec.after_each do |  | ||||||
|   Kemal.application.clear |  | ||||||
| end |  | ||||||
|  |  | ||||||
|  | @ -1,17 +1,22 @@ | ||||||
| require "./spec_helper" | require "./spec_helper" | ||||||
| 
 | 
 | ||||||
| private def handle(request, fallthrough = true) | private def handle(request, config = default_config, fallthrough = true) | ||||||
|   io = IO::Memory.new |   io = IO::Memory.new | ||||||
|   response = HTTP::Server::Response.new(io) |   response = HTTP::Server::Response.new(io) | ||||||
|   context = HTTP::Server::Context.new(request, response) |   context = HTTP::Server::Context.new(request, response) | ||||||
|   context.app = Kemal.application |   handler = Kemal::StaticFileHandler.new config, fallthrough | ||||||
|   handler = Kemal::StaticFileHandler.new "#{__DIR__}/static", fallthrough |  | ||||||
|   handler.call context |   handler.call context | ||||||
|   response.close |   response.close | ||||||
|   io.rewind |   io.rewind | ||||||
|   HTTP::Client::Response.from_io(io) |   HTTP::Client::Response.from_io(io) | ||||||
| end | end | ||||||
| 
 | 
 | ||||||
|  | private def default_config | ||||||
|  |   Kemal::Config.new.tap do |config| | ||||||
|  |     config.public_folder = "#{__DIR__}/static" | ||||||
|  |   end | ||||||
|  | end | ||||||
|  | 
 | ||||||
| describe Kemal::StaticFileHandler do | describe Kemal::StaticFileHandler do | ||||||
|   file = File.open "#{__DIR__}/static/dir/test.txt" |   file = File.open "#{__DIR__}/static/dir/test.txt" | ||||||
|   file_size = file.size |   file_size = file.size | ||||||
|  | @ -37,38 +42,43 @@ describe Kemal::StaticFileHandler do | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   it "should not list directory's entries" do |   it "should not list directory's entries" do | ||||||
|     serve_static({"gzip" => true, "dir_listing" => false}) |     config = default_config | ||||||
|     response = handle HTTP::Request.new("GET", "/dir/") |     config.serve_static = {"gzip" => true, "dir_listing" => false} | ||||||
|  |     response = handle HTTP::Request.new("GET", "/dir/"), config | ||||||
|     response.status_code.should eq(404) |     response.status_code.should eq(404) | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   it "should list directory's entries when config is set" do |   it "should list directory's entries when config is set" do | ||||||
|     serve_static({"gzip" => true, "dir_listing" => true}) |     config = default_config | ||||||
|     response = handle HTTP::Request.new("GET", "/dir/") |     config.serve_static = {"gzip" => true, "dir_listing" => true} | ||||||
|  |     response = handle HTTP::Request.new("GET", "/dir/"), config | ||||||
|     response.status_code.should eq(200) |     response.status_code.should eq(200) | ||||||
|     response.body.should match(/test.txt/) |     response.body.should match(/test.txt/) | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   it "should gzip a file if config is true, headers accept gzip and file is > 880 bytes" do |   it "should gzip a file if config is true, headers accept gzip and file is > 880 bytes" do | ||||||
|     serve_static({"gzip" => true, "dir_listing" => true}) |     config = default_config | ||||||
|  |     config.serve_static = {"gzip" => true, "dir_listing" => true} | ||||||
|     headers = HTTP::Headers{"Accept-Encoding" => "gzip, deflate, sdch, br"} |     headers = HTTP::Headers{"Accept-Encoding" => "gzip, deflate, sdch, br"} | ||||||
|     response = handle HTTP::Request.new("GET", "/dir/bigger.txt", headers) |     response = handle HTTP::Request.new("GET", "/dir/bigger.txt", headers), config | ||||||
|     response.status_code.should eq(200) |     response.status_code.should eq(200) | ||||||
|     response.headers["Content-Encoding"].should eq "gzip" |     response.headers["Content-Encoding"].should eq "gzip" | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   it "should not gzip a file if config is true, headers accept gzip and file is < 880 bytes" do |   it "should not gzip a file if config is true, headers accept gzip and file is < 880 bytes" do | ||||||
|     serve_static({"gzip" => true, "dir_listing" => true}) |     config = default_config | ||||||
|  |     config.serve_static = {"gzip" => true, "dir_listing" => true} | ||||||
|     headers = HTTP::Headers{"Accept-Encoding" => "gzip, deflate, sdch, br"} |     headers = HTTP::Headers{"Accept-Encoding" => "gzip, deflate, sdch, br"} | ||||||
|     response = handle HTTP::Request.new("GET", "/dir/test.txt", headers) |     response = handle HTTP::Request.new("GET", "/dir/test.txt", headers), config | ||||||
|     response.status_code.should eq(200) |     response.status_code.should eq(200) | ||||||
|     response.headers["Content-Encoding"]?.should be_nil |     response.headers["Content-Encoding"]?.should be_nil | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   it "should not gzip a file if config is false, headers accept gzip and file is > 880 bytes" do |   it "should not gzip a file if config is false, headers accept gzip and file is > 880 bytes" do | ||||||
|     serve_static({"gzip" => false, "dir_listing" => true}) |     config = default_config | ||||||
|  |     config.serve_static = {"gzip" => false, "dir_listing" => true} | ||||||
|     headers = HTTP::Headers{"Accept-Encoding" => "gzip, deflate, sdch, br"} |     headers = HTTP::Headers{"Accept-Encoding" => "gzip, deflate, sdch, br"} | ||||||
|     response = handle HTTP::Request.new("GET", "/dir/bigger.txt", headers) |     response = handle HTTP::Request.new("GET", "/dir/bigger.txt", headers), config | ||||||
|     response.status_code.should eq(200) |     response.status_code.should eq(200) | ||||||
|     response.headers["Content-Encoding"]?.should be_nil |     response.headers["Content-Encoding"]?.should be_nil | ||||||
|   end |   end | ||||||
|  | @ -97,7 +107,7 @@ describe Kemal::StaticFileHandler do | ||||||
|     %w(POST PUT DELETE).each do |method| |     %w(POST PUT DELETE).each do |method| | ||||||
|       response = handle HTTP::Request.new(method, "/dir/test.txt") |       response = handle HTTP::Request.new(method, "/dir/test.txt") | ||||||
|       response.status_code.should eq(404) |       response.status_code.should eq(404) | ||||||
|       response = handle HTTP::Request.new(method, "/dir/test.txt"), false |       response = handle HTTP::Request.new(method, "/dir/test.txt"), fallthrough: false | ||||||
|       response.status_code.should eq(405) |       response.status_code.should eq(405) | ||||||
|       response.headers["Allow"].should eq("GET, HEAD") |       response.headers["Allow"].should eq("GET, HEAD") | ||||||
|     end |     end | ||||||
|  | @ -133,22 +143,21 @@ describe Kemal::StaticFileHandler do | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   it "should handle setting custom headers" do |   it "should handle setting custom headers" do | ||||||
|     headers = Proc(HTTP::Server::Response, String, File::Info, Void).new do |response, path, stat| |     config = default_config | ||||||
|  |     config.static_headers = Proc(HTTP::Server::Response, String, File::Info, Void).new do |response, path, stat| | ||||||
|       if path =~ /\.html$/ |       if path =~ /\.html$/ | ||||||
|         response.headers.add("Access-Control-Allow-Origin", "*") |         response.headers.add("Access-Control-Allow-Origin", "*") | ||||||
|       end |       end | ||||||
|       response.headers.add("Content-Size", stat.size.to_s) |       response.headers.add("Content-Size", stat.size.to_s) | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|     static_headers(&headers) |     response = handle HTTP::Request.new("GET", "/dir/test.txt"), config | ||||||
| 
 |  | ||||||
|     response = handle HTTP::Request.new("GET", "/dir/test.txt") |  | ||||||
|     response.headers.has_key?("Access-Control-Allow-Origin").should be_false |     response.headers.has_key?("Access-Control-Allow-Origin").should be_false | ||||||
|     response.headers["Content-Size"].should eq( |     response.headers["Content-Size"].should eq( | ||||||
|       File.info("#{__DIR__}/static/dir/test.txt").size.to_s |       File.info("#{__DIR__}/static/dir/test.txt").size.to_s | ||||||
|     ) |     ) | ||||||
| 
 | 
 | ||||||
|     response = handle HTTP::Request.new("GET", "/dir/index.html") |     response = handle HTTP::Request.new("GET", "/dir/index.html"), config | ||||||
|     response.headers["Access-Control-Allow-Origin"].should eq("*") |     response.headers["Access-Control-Allow-Origin"].should eq("*") | ||||||
|   end |   end | ||||||
| end | end | ||||||
|  |  | ||||||
|  | @ -1,4 +1,4 @@ | ||||||
| require "./spec_helper" | require "./dsl_helper" | ||||||
| 
 | 
 | ||||||
| macro render_with_base_and_layout(filename) | macro render_with_base_and_layout(filename) | ||||||
|   render "spec/asset/#{{{filename}}}", "spec/asset/layout.ecr" |   render "spec/asset/#{{{filename}}}", "spec/asset/layout.ecr" | ||||||
|  | @ -6,56 +6,61 @@ end | ||||||
| 
 | 
 | ||||||
| describe "Views" do | describe "Views" do | ||||||
|   it "renders file" do |   it "renders file" do | ||||||
|     get "/view/:name" do |env| |     app = Kemal::Base.new | ||||||
|  |     app.get "/view/:name" do |env| | ||||||
|       name = env.params.url["name"] |       name = env.params.url["name"] | ||||||
|       render "spec/asset/hello.ecr" |       render "spec/asset/hello.ecr" | ||||||
|     end |     end | ||||||
|     request = HTTP::Request.new("GET", "/view/world") |     request = HTTP::Request.new("GET", "/view/world") | ||||||
|     client_response = call_request_on_app(request) |     client_response = call_request_on_app(app, request) | ||||||
|     client_response.body.should contain("Hello world") |     client_response.body.should contain("Hello world") | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   it "renders file with dynamic variables" do |   it "renders file with dynamic variables" do | ||||||
|     get "/view/:name" do |env| |     app = Kemal::Base.new | ||||||
|  |     app.get "/view/:name" do |env| | ||||||
|       name = env.params.url["name"] |       name = env.params.url["name"] | ||||||
|       render_with_base_and_layout "hello.ecr" |       render_with_base_and_layout "hello.ecr" | ||||||
|     end |     end | ||||||
|     request = HTTP::Request.new("GET", "/view/world") |     request = HTTP::Request.new("GET", "/view/world") | ||||||
|     client_response = call_request_on_app(request) |     client_response = call_request_on_app(app, request) | ||||||
|     client_response.body.should contain("Hello world") |     client_response.body.should contain("Hello world") | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   it "renders layout" do |   it "renders layout" do | ||||||
|     get "/view/:name" do |env| |     app = Kemal::Base.new | ||||||
|  |     app.get "/view/:name" do |env| | ||||||
|       name = env.params.url["name"] |       name = env.params.url["name"] | ||||||
|       render "spec/asset/hello.ecr", "spec/asset/layout.ecr" |       render "spec/asset/hello.ecr", "spec/asset/layout.ecr" | ||||||
|     end |     end | ||||||
|     request = HTTP::Request.new("GET", "/view/world") |     request = HTTP::Request.new("GET", "/view/world") | ||||||
|     client_response = call_request_on_app(request) |     client_response = call_request_on_app(app, request) | ||||||
|     client_response.body.should contain("<html>Hello world") |     client_response.body.should contain("<html>Hello world") | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   it "renders layout with variables" do |   it "renders layout with variables" do | ||||||
|     get "/view/:name" do |env| |     app = Kemal::Base.new | ||||||
|  |     app.get "/view/:name" do |env| | ||||||
|       name = env.params.url["name"] |       name = env.params.url["name"] | ||||||
|       var1 = "serdar" |       var1 = "serdar" | ||||||
|       var2 = "kemal" |       var2 = "kemal" | ||||||
|       render "spec/asset/hello_with_content_for.ecr", "spec/asset/layout_with_yield_and_vars.ecr" |       render "spec/asset/hello_with_content_for.ecr", "spec/asset/layout_with_yield_and_vars.ecr" | ||||||
|     end |     end | ||||||
|     request = HTTP::Request.new("GET", "/view/world") |     request = HTTP::Request.new("GET", "/view/world") | ||||||
|     client_response = call_request_on_app(request) |     client_response = call_request_on_app(app, request) | ||||||
|     client_response.body.should contain("Hello world") |     client_response.body.should contain("Hello world") | ||||||
|     client_response.body.should contain("serdar") |     client_response.body.should contain("serdar") | ||||||
|     client_response.body.should contain("kemal") |     client_response.body.should contain("kemal") | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   it "renders layout with content_for" do |   it "renders layout with content_for" do | ||||||
|     get "/view/:name" do |env| |     app = Kemal::Base.new | ||||||
|  |     app.get "/view/:name" do |env| | ||||||
|       name = env.params.url["name"] |       name = env.params.url["name"] | ||||||
|       render "spec/asset/hello_with_content_for.ecr", "spec/asset/layout_with_yield.ecr" |       render "spec/asset/hello_with_content_for.ecr", "spec/asset/layout_with_yield.ecr" | ||||||
|     end |     end | ||||||
|     request = HTTP::Request.new("GET", "/view/world") |     request = HTTP::Request.new("GET", "/view/world") | ||||||
|     client_response = call_request_on_app(request) |     client_response = call_request_on_app(app, request) | ||||||
|     client_response.body.should contain("Hello world") |     client_response.body.should contain("Hello world") | ||||||
|     client_response.body.should contain("<h1>Hello from otherside</h1>") |     client_response.body.should contain("<h1>Hello from otherside</h1>") | ||||||
|   end |   end | ||||||
|  |  | ||||||
|  | @ -1,10 +1,24 @@ | ||||||
| require "./spec_helper" | require "./spec_helper" | ||||||
| 
 | 
 | ||||||
|  | private def create_ws_request_and_return_io(handler, request, app) | ||||||
|  |   io = IO::Memory.new | ||||||
|  |   response = HTTP::Server::Response.new(io) | ||||||
|  |   context = HTTP::Server::Context.new(request, response) | ||||||
|  |   context.app = app | ||||||
|  |   begin | ||||||
|  |     handler.call context | ||||||
|  |   rescue IO::Error | ||||||
|  |     # Raises because the IO::Memory is empty | ||||||
|  |   end | ||||||
|  |   io | ||||||
|  | end | ||||||
|  | 
 | ||||||
| describe "Kemal::WebSocketHandler" do | describe "Kemal::WebSocketHandler" do | ||||||
|   it "doesn't match on wrong route" do |   it "doesn't match on wrong route" do | ||||||
|  |     app = Kemal::Base.new | ||||||
|     handler = Kemal::WebSocketHandler.new |     handler = Kemal::WebSocketHandler.new | ||||||
|     handler.next = Kemal::RouteHandler.new |     handler.next = Kemal::RouteHandler.new | ||||||
|     ws "/" { } |     app.ws "/" { } | ||||||
|     headers = HTTP::Headers{ |     headers = HTTP::Headers{ | ||||||
|       "Upgrade"           => "websocket", |       "Upgrade"           => "websocket", | ||||||
|       "Connection"        => "Upgrade", |       "Connection"        => "Upgrade", | ||||||
|  | @ -14,7 +28,7 @@ describe "Kemal::WebSocketHandler" do | ||||||
|     io = IO::Memory.new |     io = IO::Memory.new | ||||||
|     response = HTTP::Server::Response.new(io) |     response = HTTP::Server::Response.new(io) | ||||||
|     context = HTTP::Server::Context.new(request, response) |     context = HTTP::Server::Context.new(request, response) | ||||||
|     context.app = Kemal.application |     context.app = app | ||||||
| 
 | 
 | ||||||
|     expect_raises(Kemal::Exceptions::RouteNotFound) do |     expect_raises(Kemal::Exceptions::RouteNotFound) do | ||||||
|       handler.call context |       handler.call context | ||||||
|  | @ -22,9 +36,10 @@ describe "Kemal::WebSocketHandler" do | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   it "matches on given route" do |   it "matches on given route" do | ||||||
|  |     app = Kemal::Base.new | ||||||
|     handler = Kemal::WebSocketHandler.new |     handler = Kemal::WebSocketHandler.new | ||||||
|     ws "/" { |socket, context| socket.send("Match") } |     app.ws "/" { |socket, context| socket.send("Match") } | ||||||
|     ws "/no_match" { |socket, context| socket.send "No Match" } |     app.ws "/no_match" { |socket, context| socket.send "No Match" } | ||||||
|     headers = HTTP::Headers{ |     headers = HTTP::Headers{ | ||||||
|       "Upgrade"               => "websocket", |       "Upgrade"               => "websocket", | ||||||
|       "Connection"            => "Upgrade", |       "Connection"            => "Upgrade", | ||||||
|  | @ -33,13 +48,14 @@ describe "Kemal::WebSocketHandler" do | ||||||
|     } |     } | ||||||
|     request = HTTP::Request.new("GET", "/", headers) |     request = HTTP::Request.new("GET", "/", headers) | ||||||
| 
 | 
 | ||||||
|     io_with_context = create_ws_request_and_return_io_and_context(handler, request)[0] |     io_with_context = create_ws_request_and_return_io(handler, request, app) | ||||||
|     io_with_context.to_s.should eq("HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n\r\n\x81\u0005Match") |     io_with_context.to_s.should eq("HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-Websocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n\r\n\x81\u0005Match") | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   it "fetches named url parameters" do |   it "fetches named url parameters" do | ||||||
|  |     app = Kemal::Base.new | ||||||
|     handler = Kemal::WebSocketHandler.new |     handler = Kemal::WebSocketHandler.new | ||||||
|     ws "/:id" { |s, c| c.params.url["id"] } |     app.ws "/:id" { |s, c| c.params.url["id"] } | ||||||
|     headers = HTTP::Headers{ |     headers = HTTP::Headers{ | ||||||
|       "Upgrade"               => "websocket", |       "Upgrade"               => "websocket", | ||||||
|       "Connection"            => "Upgrade", |       "Connection"            => "Upgrade", | ||||||
|  | @ -47,20 +63,21 @@ describe "Kemal::WebSocketHandler" do | ||||||
|       "Sec-WebSocket-Version" => "13", |       "Sec-WebSocket-Version" => "13", | ||||||
|     } |     } | ||||||
|     request = HTTP::Request.new("GET", "/1234", headers) |     request = HTTP::Request.new("GET", "/1234", headers) | ||||||
|     io_with_context = create_ws_request_and_return_io_and_context(handler, request)[0] |     io_with_context = create_ws_request_and_return_io(handler, request, app) | ||||||
|     io_with_context.to_s.should eq("HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n\r\n") |     io_with_context.to_s.should eq("HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-Websocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n\r\n") | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   it "matches correct verb" do |   it "matches correct verb" do | ||||||
|  |     app = Kemal::Base.new | ||||||
|     handler = Kemal::WebSocketHandler.new |     handler = Kemal::WebSocketHandler.new | ||||||
|     handler.next = Kemal::RouteHandler.new |     handler.next = Kemal::RouteHandler.new | ||||||
|     ws "/" { } |     app.ws "/" { } | ||||||
|     get "/" { "get" } |     app.get "/" { "get" } | ||||||
|     request = HTTP::Request.new("GET", "/") |     request = HTTP::Request.new("GET", "/") | ||||||
|     io = IO::Memory.new |     io = IO::Memory.new | ||||||
|     response = HTTP::Server::Response.new(io) |     response = HTTP::Server::Response.new(io) | ||||||
|     context = HTTP::Server::Context.new(request, response) |     context = HTTP::Server::Context.new(request, response) | ||||||
|     context.app = Kemal.application |     context.app = app | ||||||
|     handler.call(context) |     handler.call(context) | ||||||
|     response.close |     response.close | ||||||
|     io.rewind |     io.rewind | ||||||
|  |  | ||||||
|  | @ -99,4 +99,8 @@ class Kemal::Base | ||||||
|       raise "Kemal is already stopped." |       raise "Kemal is already stopped." | ||||||
|     end |     end | ||||||
|   end |   end | ||||||
|  | 
 | ||||||
|  |   def log(message) | ||||||
|  |     logger.write "#{message}\n" | ||||||
|  |   end | ||||||
| end | end | ||||||
|  |  | ||||||
|  | @ -71,7 +71,7 @@ class Kemal::Base | ||||||
| 
 | 
 | ||||||
|     private def setup_error_handler |     private def setup_error_handler | ||||||
|       if @config.always_rescue? |       if @config.always_rescue? | ||||||
|         error_handler = @error_handler ||= Kemal::ExceptionHandler.new |         error_handler = @error_handler ||= Kemal::ExceptionHandler.new(self) | ||||||
|         @handlers.insert(@handler_position, error_handler) |         @handlers.insert(@handler_position, error_handler) | ||||||
|         @handler_position += 1 |         @handler_position += 1 | ||||||
|       end |       end | ||||||
|  | @ -79,7 +79,7 @@ class Kemal::Base | ||||||
| 
 | 
 | ||||||
|     private def setup_static_file_handler |     private def setup_static_file_handler | ||||||
|       if @config.serve_static.is_a?(Hash) |       if @config.serve_static.is_a?(Hash) | ||||||
|         @handlers.insert(@handler_position, Kemal::StaticFileHandler.new(@config.public_folder)) |         @handlers.insert(@handler_position, Kemal::StaticFileHandler.new(@config)) | ||||||
|         @handler_position += 1 |         @handler_position += 1 | ||||||
|       end |       end | ||||||
|     end |     end | ||||||
|  |  | ||||||
|  | @ -57,6 +57,10 @@ module Kemal | ||||||
|     def extra_options(&@extra_options : OptionParser ->) |     def extra_options(&@extra_options : OptionParser ->) | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|  |     def serve_static?(key) | ||||||
|  |       (h = @serve_static).is_a?(Hash) && h[key]? == true | ||||||
|  |     end | ||||||
|  | 
 | ||||||
|     # Create a config with default values |     # Create a config with default values | ||||||
|     def self.default |     def self.default | ||||||
|       new |       new | ||||||
|  |  | ||||||
|  | @ -3,6 +3,13 @@ module Kemal | ||||||
|   class ExceptionHandler |   class ExceptionHandler | ||||||
|     include HTTP::Handler |     include HTTP::Handler | ||||||
| 
 | 
 | ||||||
|  |     getter app : Kemal::Base | ||||||
|  | 
 | ||||||
|  |     def initialize(@app) | ||||||
|  |     end | ||||||
|  | 
 | ||||||
|  |     delegate log, to: app | ||||||
|  | 
 | ||||||
|     def call(context : HTTP::Server::Context) |     def call(context : HTTP::Server::Context) | ||||||
|       begin |       begin | ||||||
|         call_next(context) |         call_next(context) | ||||||
|  | @ -14,7 +21,7 @@ module Kemal | ||||||
|         log("Exception: #{ex.inspect_with_backtrace}") |         log("Exception: #{ex.inspect_with_backtrace}") | ||||||
|         return call_exception_with_status_code(context, ex, 500) if context.app.error_handlers.has_key?(500) |         return call_exception_with_status_code(context, ex, 500) if context.app.error_handlers.has_key?(500) | ||||||
|         verbosity = context.app.config.env == "production" ? false : true |         verbosity = context.app.config.env == "production" ? false : true | ||||||
|         return render_500(context, ex.inspect_with_backtrace, verbosity) |         return app.render_500(context, ex.inspect_with_backtrace, verbosity) | ||||||
|       end |       end | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1,8 +1,5 @@ | ||||||
| module Kemal::FileHelpers | module Kemal::FileHelpers | ||||||
|   def log(message) |   extend self | ||||||
|     logger.write "#{message}\n" |  | ||||||
|   end |  | ||||||
| 
 |  | ||||||
|   # Send a file with given path and base the mime-type on the file extension |   # Send a file with given path and base the mime-type on the file extension | ||||||
|   # or default `application/octet-stream` mime_type. |   # or default `application/octet-stream` mime_type. | ||||||
|   # |   # | ||||||
|  | @ -15,8 +12,7 @@ module Kemal::FileHelpers | ||||||
|   # ``` |   # ``` | ||||||
|   # send_file env, "./path/to/file", "image/jpeg" |   # send_file env, "./path/to/file", "image/jpeg" | ||||||
|   # ``` |   # ``` | ||||||
|   def send_file(env : HTTP::Server::Context, path : String, mime_type : String? = nil) |   def send_file(env : HTTP::Server::Context, path : String, config : Kemal::Config, mime_type : String? = nil) | ||||||
|     config = env.app.config |  | ||||||
|     file_path = File.expand_path(path, Dir.current) |     file_path = File.expand_path(path, Dir.current) | ||||||
|     mime_type ||= Kemal::Utils.mime_type(file_path) |     mime_type ||= Kemal::Utils.mime_type(file_path) | ||||||
|     env.response.content_type = mime_type |     env.response.content_type = mime_type | ||||||
|  | @ -28,17 +24,18 @@ module Kemal::FileHelpers | ||||||
|     filestat = File.stat(file_path) |     filestat = File.stat(file_path) | ||||||
| 
 | 
 | ||||||
|     config.static_headers.try(&.call(env.response, file_path, filestat)) |     config.static_headers.try(&.call(env.response, file_path, filestat)) | ||||||
|  |     gzip = config.serve_static?("gzip") | ||||||
| 
 | 
 | ||||||
|     File.open(file_path) do |file| |     File.open(file_path) do |file| | ||||||
|       if env.request.method == "GET" && env.request.headers.has_key?("Range") |       if env.request.method == "GET" && env.request.headers.has_key?("Range") | ||||||
|         next multipart(file, env) |         next multipart(file, env) | ||||||
|       end |       end | ||||||
|       if request_headers.includes_word?("Accept-Encoding", "gzip") && config.serve_static?("gzip") && filesize > minsize && Kemal::Utils.zip_types(file_path) |       if request_headers.includes_word?("Accept-Encoding", "gzip") && gzip && filesize > minsize && Kemal::Utils.zip_types(file_path) | ||||||
|         env.response.headers["Content-Encoding"] = "gzip" |         env.response.headers["Content-Encoding"] = "gzip" | ||||||
|         Gzip::Writer.open(env.response) do |deflate| |         Gzip::Writer.open(env.response) do |deflate| | ||||||
|           IO.copy(file, deflate) |           IO.copy(file, deflate) | ||||||
|         end |         end | ||||||
|       elsif request_headers.includes_word?("Accept-Encoding", "deflate") && config.serve_static?("gzip") && filesize > minsize && Kemal::Utils.zip_types(file_path) |       elsif request_headers.includes_word?("Accept-Encoding", "deflate") && gzip && filesize > minsize && Kemal::Utils.zip_types(file_path) | ||||||
|         env.response.headers["Content-Encoding"] = "deflate" |         env.response.headers["Content-Encoding"] = "deflate" | ||||||
|         Flate::Writer.open(env.response) do |deflate| |         Flate::Writer.open(env.response) do |deflate| | ||||||
|           IO.copy(file, deflate) |           IO.copy(file, deflate) | ||||||
|  | @ -51,6 +48,10 @@ module Kemal::FileHelpers | ||||||
|     return |     return | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|  |   def send_file(env, path : String, mime_type : String? = nil) | ||||||
|  |     send_file(env, path, env.app.config, mime_type) | ||||||
|  |   end | ||||||
|  | 
 | ||||||
|   private def multipart(file, env : HTTP::Server::Context) |   private def multipart(file, env : HTTP::Server::Context) | ||||||
|     # See http://httpwg.org/specs/rfc7233.html |     # See http://httpwg.org/specs/rfc7233.html | ||||||
|     fileb = file.size |     fileb = file.size | ||||||
|  |  | ||||||
|  | @ -4,6 +4,12 @@ | ||||||
| 
 | 
 | ||||||
| module Kemal | module Kemal | ||||||
|   class StaticFileHandler < HTTP::StaticFileHandler |   class StaticFileHandler < HTTP::StaticFileHandler | ||||||
|  |     getter config : Kemal::Config | ||||||
|  | 
 | ||||||
|  |     def initialize(@config, fallthrough = true) | ||||||
|  |       super(@config.public_folder, fallthrough) | ||||||
|  |     end | ||||||
|  | 
 | ||||||
|     def call(context : HTTP::Server::Context) |     def call(context : HTTP::Server::Context) | ||||||
|       return call_next(context) if context.request.path.not_nil! == "/" |       return call_next(context) if context.request.path.not_nil! == "/" | ||||||
| 
 | 
 | ||||||
|  | @ -19,7 +25,6 @@ module Kemal | ||||||
|         return |         return | ||||||
|       end |       end | ||||||
| 
 | 
 | ||||||
|       config = Kemal.config.serve_static |  | ||||||
|       original_path = context.request.path.not_nil! |       original_path = context.request.path.not_nil! | ||||||
|       request_path = URI.unescape(original_path) |       request_path = URI.unescape(original_path) | ||||||
| 
 | 
 | ||||||
|  | @ -48,7 +53,7 @@ module Kemal | ||||||
|       end |       end | ||||||
| 
 | 
 | ||||||
|       if Dir.exists?(file_path) |       if Dir.exists?(file_path) | ||||||
|         if config.is_a?(Hash) && config["dir_listing"] == true |         if @config.serve_static?("dir_listing") | ||||||
|           context.response.content_type = "text/html" |           context.response.content_type = "text/html" | ||||||
|           directory_listing(context.response, request_path, file_path) |           directory_listing(context.response, request_path, file_path) | ||||||
|         else |         else | ||||||
|  | @ -62,7 +67,8 @@ module Kemal | ||||||
|           context.response.status_code = 304 |           context.response.status_code = 304 | ||||||
|           return |           return | ||||||
|         end |         end | ||||||
|         send_file(context, file_path) | 
 | ||||||
|  |         FileHelpers.send_file(context, file_path, config) | ||||||
|       else |       else | ||||||
|         call_next(context) |         call_next(context) | ||||||
|       end |       end | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue