diff --git a/config.ru b/config.ru index c64c6d4..8e7d6be 100644 --- a/config.ru +++ b/config.ru @@ -1,2 +1,2 @@ -require_relative 'lib/rack/conform/server' -run Rack::Conform::Server.new +require_relative 'lib/rack/conform/application' +run Rack::Conform::Application.new diff --git a/config/sus.rb b/config/sus.rb index a3da28f..65bf62c 100644 --- a/config/sus.rb +++ b/config/sus.rb @@ -3,17 +3,11 @@ # Released under the MIT License. # Copyright, 2022, by Samuel Williams. -require 'console' -require 'async' -require 'async/http/endpoint' -require 'async/http/client' +require 'rack/conform/server' def before_tests(assertions) - if rack_conform_server = ENV['RACK_CONFORM_SERVER'] - @server_pid = wait_for_server_start(assertions, rack_conform_server) - else - assertions.inform("Could not identify server!") - end + @server = Rack::Conform::Server.current + @server&.start(assertions) super end @@ -21,43 +15,9 @@ def before_tests(assertions) def after_tests(assertions) super - if @server_pid - Process.kill(:INT, @server_pid) - end + @server&.close end private -def wait_for_server_start(assertions, command, timeout: 10) - clock = Sus::Clock.start! - log = ::File.open("server.log", "w+") - pid = Process.spawn(command, out: log, err: log) - - Async do - endpoint = Async::HTTP::Endpoint.parse(ENV['RACK_CONFORM_ENDPOINT']) - client = Async::HTTP::Client.new(endpoint) - - begin - client.get("/status").finish - rescue Errno::ECONNREFUSED - sleep 0.001 - - if clock.duration > timeout - assertions.assert(false, "Server did not start within #{timeout} seconds!") - else - retry - end - end - - client.close - end - - assertions.inform("Server started in #{clock}.") - - return pid -rescue => error - Process.kill(:INT, pid) if pid - raise -ensure - log&.close -end + diff --git a/gems.rb b/gems.rb index f826bf3..3a1cc85 100644 --- a/gems.rb +++ b/gems.rb @@ -15,4 +15,7 @@ group :test do gem "bake-test" gem "bake-test-external" + + # For local testing... + gem "falcon" end diff --git a/lib/rack/conform.rb b/lib/rack/conform.rb index f42ff49..a2034d8 100644 --- a/lib/rack/conform.rb +++ b/lib/rack/conform.rb @@ -4,4 +4,4 @@ # Copyright, 2022, by Samuel Williams. require_relative 'conform/version' -require_relative 'conform/server' +require_relative 'conform/application' diff --git a/lib/rack/conform/application.rb b/lib/rack/conform/application.rb new file mode 100644 index 0000000..acfda97 --- /dev/null +++ b/lib/rack/conform/application.rb @@ -0,0 +1,86 @@ +# frozen_string_literal: true + +# Released under the MIT License. +# Copyright, 2022, by Samuel Williams. + +require 'json' + +module Rack + module Conform + class Application + def call(env) + self.public_send(test_method_for(env), env) + end + + def test(env) + [200, {}, ["Hello World"]] + end + + def test_status(env) + status = env.fetch('HTTP_STATUS', 200) + [status.to_i, {}, []] + end + + def test_echo(env) + [200, {}, env['rack.input']] + end + + def test_cookies(env) + cookies = JSON.parse(env['rack.input'].read) + + Rack::Response.new.tap do |response| + cookies.each do |key, value| + response.set_cookie(key, value) + end + end.to_a + end + + def test_headers(env) + headers = JSON.parse(env['rack.input'].read) + + Rack::Response.new.tap do |response| + headers.each do |key, value| + Array(value).each do |value| + response.add_header(key, value) + end + end + end.to_a + end + + def test_streaming_hijack(env) + if env['rack.hijack?'] + callback = proc do |stream| + stream.write "Hello World" + stream.close + end + + return [200, {'rack.hijack' => callback}, nil] + else + [404, {}, []] + end + end + + def test_streaming_body(env) + if Rack::RELEASE >= "3.0" + callback = proc do |stream| + stream.write "Hello World" + stream.close + end + + return [200, {}, callback] + else + [404, {}, []] + end + end + + private + + def test_method_for(env) + parts = env['PATH_INFO'].split('/') + parts[0] = "test" + + return parts.join('_').to_sym + end + end + end +end diff --git a/lib/rack/conform/server.rb b/lib/rack/conform/server.rb index 072cd5c..605e89c 100644 --- a/lib/rack/conform/server.rb +++ b/lib/rack/conform/server.rb @@ -3,83 +3,83 @@ # Released under the MIT License. # Copyright, 2022, by Samuel Williams. -require 'json' +require 'console' +require 'async' +require 'async/http/endpoint' +require 'async/http/client' module Rack module Conform class Server - def call(env) - self.public_send(test_method_for(env), env) - end - - def test(env) - [200, {}, ["Hello World"]] - end - - def test_status(env) - status = env.fetch('HTTP_STATUS', 200) - [status.to_i, {}, []] - end - - def test_echo(env) - [200, {}, env['rack.input']] + def self.current + command = ENV['RACK_CONFORM_SERVER'] + endpoint = ENV['RACK_CONFORM_ENDPOINT'] + + if command and endpoint + return self.new(command, endpoint) + end end - def test_cookies(env) - cookies = JSON.parse(env['rack.input'].read) + def initialize(command, endpoint) + @command = command + @endpoint = endpoint - Rack::Response.new.tap do |response| - cookies.each do |key, value| - response.set_cookie(key, value) - end - end.to_a + @pid = nil end - def test_headers(env) - headers = JSON.parse(env['rack.input'].read) - - Rack::Response.new.tap do |response| - headers.each do |key, value| - Array(value).each do |value| - response.add_header(key, value) - end - end - end.to_a + def print(output) + output.write "server ", :variable, @command.inspect, " ", @endpoint, :reset end - def test_streaming_hijack(env) - if env['rack.hijack?'] - callback = proc do |stream| - stream.write "Hello World" - stream.close - end - - return [200, {'rack.hijack' => callback}, nil] - else - [404, {}, []] + def start(assertions) + raise "Already started!" if @pid + + assertions.nested(self, isolated: true, measure: true) do |assertions| + assertions.inform("Starting server...") + @pid = self.startup(assertions) end end - def test_streaming_body(env) - if Rack::RELEASE >= "3.0" - callback = proc do |stream| - stream.write "Hello World" - stream.close - end - - return [200, {}, callback] - else - [404, {}, []] + def close + if @pid + Process.kill(:INT, @pid) end end private - def test_method_for(env) - parts = env['PATH_INFO'].split('/') - parts[0] = "test" + def startup(assertions, timeout: 10) + clock = Sus::Clock.start! + log = ::File.open("server.log", "w+") + pid = Process.spawn(@command, out: log, err: log) + + Async do + endpoint = Async::HTTP::Endpoint.parse(ENV['RACK_CONFORM_ENDPOINT']) + client = Async::HTTP::Client.new(endpoint) + + begin + client.get("/status").finish + rescue Errno::ECONNREFUSED + sleep 0.001 + + if clock.duration > timeout + assertions.assert(false, "Server did not start within #{timeout} seconds!") + else + retry + end + end + + client.close + end + + assertions.inform("Server started in #{clock}.") - return parts.join('_').to_sym + return pid + rescue => error + Process.kill(:INT, pid) if pid + raise + ensure + log&.close end end end diff --git a/test/rack/conform/streaming/body.rb b/test/rack/conform/streaming/body.rb index 063340e..6433f9f 100644 --- a/test/rack/conform/streaming/body.rb +++ b/test/rack/conform/streaming/body.rb @@ -6,7 +6,9 @@ require 'client_context' include ClientContext -if Rack::RESPONSE > "3.0" +require 'rack' + +if Rack::RELEASE > "3.0" it 'can stream a response' do response = client.get("/streaming/body")