rspec-httpd 0.0.4 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ce5a7956af7f5d0fa605929340963f4b212b1eca01a638bb52829fbe190716b5
4
- data.tar.gz: 426cf96fa555de1949629f65cb1f619f8be7b4980fde0e47e2ed9b5d1432cc7a
3
+ metadata.gz: e88c3329bebe29bcae34340feeb0585974fdc35b5a8b35f0451d7208e51f9c4f
4
+ data.tar.gz: a56d6764673da02062227b36b141ba8bdc163cb864489688f988a0f47a63f036
5
5
  SHA512:
6
- metadata.gz: d2c05620102049c75517f09cdc042e068503600d4633b87228adb2be70bd75a85cc0372876dd3bee2404b301f6d3c61adb2358daea265eff6ea3691355abe837
7
- data.tar.gz: ad091863872c48f7f356afa9422a83fc8d25bdd8fa80f6bd53682228f962940e9d64f72426af4c751b987517c63d364f106c46e5a45bd645a3013dfcc2c9dec6
6
+ metadata.gz: 9e1ac270f0c7e49cb2a923964bdc3ebbb7b0f91e64442768dff60a04063051ac4cce35d68cd3ab8860b0c4103c55c79c5526e3d0a834e987e32a405c3a212d13
7
+ data.tar.gz: 13a98bb4462fcdebb3cc33f40975e15a658a73f1c9e70e299dfa8c9a21b8ef407d316b3b24fca8cf5909c79d486d8e1040dc3a21ec454383cd690a9ac3e6c9a8
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.4
1
+ 0.0.5
@@ -0,0 +1,78 @@
1
+ require "rspec/core"
2
+ require "forwardable"
3
+ require "logger"
4
+
5
+ module RSpec::Httpd
6
+ end
7
+
8
+ require_relative "httpd/version"
9
+ require_relative "httpd/server"
10
+ require_relative "httpd/client"
11
+ require_relative "httpd/config"
12
+ require_relative "httpd/expectation"
13
+
14
+ module RSpec::Httpd
15
+ extend self
16
+
17
+ attr_reader :config
18
+
19
+ # Set the configuration for the default client.
20
+ #
21
+ # See also: RSpec::Httpd.http
22
+ def configure(&block)
23
+ @config = Config.new.tap(&block)
24
+ end
25
+
26
+ def logger #:nodoc:
27
+ @logger ||= Logger.new(STDERR).tap { |logger| logger.level = Logger::INFO }
28
+ end
29
+
30
+ # builds and returns a client.
31
+ #
32
+ # You can use this method to retrieve a client connection to a server
33
+ # specified via host:, port:, and, optionally, a command.
34
+ def client(host:, port:, command: nil)
35
+ @clients ||= {}
36
+ @clients[[host, port, command]] ||= begin
37
+ Server.start!(host: host, port: port, command: command) if command
38
+ Client.new host: host, port: port
39
+ end
40
+ end
41
+
42
+ private
43
+
44
+ # returns the default client
45
+ #
46
+ # The default client is the one configured via RSpec::Httpd.configure.
47
+ def http
48
+ config = ::RSpec::Httpd.config ||
49
+ raise("RSpec::Httpd configuration missing; run RSpec::Httpd.configure { |config| ... }")
50
+
51
+ client(host: config.host, port: config.port, command: config.command)
52
+ end
53
+
54
+ public
55
+
56
+ def expect_response(expected = nil, status: nil, client: nil)
57
+ client ||= http
58
+
59
+ # only check status? This lets us write
60
+ #
61
+ # expect_response 201
62
+ #
63
+ if expected.is_a?(Integer) && status.nil?
64
+ expect(client.status).to eq(expected)
65
+ return
66
+ end
67
+
68
+ # do_expect_last_request is implemented in RSpec::Httpd::Expectation, and mixed in
69
+ # here, because it needs access to the expect() implementation.
70
+
71
+ expect(client.status).to eq(status || 200)
72
+ unless expected.nil?
73
+ do_expect_last_request(expected: expected, client: client)
74
+ end
75
+ end
76
+
77
+ include RSpec::Httpd::Expectation
78
+ end
@@ -0,0 +1,122 @@
1
+ require "net/http"
2
+ require "json"
3
+
4
+ module RSpec::Httpd
5
+ class Client
6
+ # host and port. Set at initialization
7
+ attr_reader :host
8
+ attr_reader :port
9
+
10
+ def initialize(host:, port:)
11
+ @host, @port = host, port
12
+ end
13
+
14
+ # request, response, and status, set during a request/response cycle
15
+ attr_reader :request
16
+ attr_reader :response
17
+
18
+ def status
19
+ Integer(response.code)
20
+ end
21
+
22
+ # returns the headers of the latest response
23
+ def headers
24
+ @headers ||= HeadersHash.new(response)
25
+ end
26
+
27
+ # returns the parsed response of the latest request
28
+ def result
29
+ @result ||= ResponseParser.parse(response)
30
+ end
31
+
32
+ # A GET request
33
+ def get(url, headers: {})
34
+ run_request(::Net::HTTP::Get, url, body: nil, headers: headers)
35
+ end
36
+
37
+ # A POST request
38
+ def post(url, body, headers: {})
39
+ run_request(::Net::HTTP::Post, url, body: body, headers: headers)
40
+ end
41
+
42
+ # A PUT request
43
+ def put(url, body, headers: {})
44
+ run_request(::Net::HTTP::Put, url, body: body || {}, headers: headers)
45
+ end
46
+
47
+ # A DELETE request
48
+ def delete(url, headers: {})
49
+ run_request(::Net::HTTP::Delete, url, body: nil, headers: headers)
50
+ end
51
+
52
+ private
53
+
54
+ def run_request(request_klass, url, body:, headers:)
55
+ @result = nil
56
+
57
+ build_request!(request_klass, url, body: body, headers: headers)
58
+ log_request!
59
+ @response = Net::HTTP.start(host, port) { |http| http.request(request) }
60
+ end
61
+
62
+ def build_request!(request_klass, url, body:, headers:)
63
+ @request = request_klass.new url, headers
64
+ if body
65
+ @request["Content-Type"] = "application/json"
66
+ @request.body = JSON.generate body
67
+ end
68
+ end
69
+
70
+ def log_request!
71
+ if request.body
72
+ RSpec::Httpd.logger.info "#{request.method} #{request.uri} #{body.inspect[0..100]}"
73
+ else
74
+ RSpec::Httpd.logger.info "#{request.method} #{request.uri}"
75
+ end
76
+ end
77
+
78
+ class HeadersHash < Hash
79
+ def initialize(response)
80
+ response.each_header do |k, v|
81
+ case self[k]
82
+ when Array then self[k].concat v
83
+ when nil then self[k] = v
84
+ else self[k].concat(v)
85
+ end
86
+ end
87
+ end
88
+
89
+ def [](key)
90
+ super key.downcase
91
+ end
92
+
93
+ private
94
+
95
+ def []=(key, value)
96
+ super key.downcase, value
97
+ end
98
+ end
99
+
100
+ module ResponseParser
101
+ def self.parse(response)
102
+ content_type = response["content-type"]
103
+
104
+ result = if content_type&.include?("application/json")
105
+ JSON.parse(response.body)
106
+ else
107
+ response.body.force_encoding("utf-8").encode
108
+ end
109
+
110
+ result.extend(self)
111
+ result.__response__ = response
112
+ result
113
+ end
114
+
115
+ attr_accessor :__response__
116
+
117
+ def status
118
+ Integer(__response__.code)
119
+ end
120
+ end
121
+ end
122
+ end
@@ -0,0 +1,11 @@
1
+ class RSpec::Httpd::Config
2
+ attr_accessor :host
3
+ attr_accessor :port
4
+ attr_accessor :command
5
+
6
+ def initialize
7
+ self.host = "127.0.0.1"
8
+ self.port = 12_345
9
+ self.command = "bundle exec rackup -E test -p 12345"
10
+ end
11
+ end
@@ -0,0 +1,40 @@
1
+ require "rspec/core"
2
+ require "forwardable"
3
+ require "logger"
4
+
5
+ module RSpec::Httpd::Expectation
6
+ def do_expect_last_request(expected:, client:)
7
+ actual = client.result
8
+
9
+ ctx = {
10
+ request: client.request, actual: actual, expected: expected
11
+ }
12
+
13
+ case expected
14
+ when Regexp then expect(actual).to match(expected), -> { format_failure SHOULD_MATCH_ERROR, ctx }
15
+ when Hash then expect(actual).to include(expected), -> { format_failure SHOULD_INCLUDE_ERROR, ctx }
16
+ else expect(actual).to eq(expected), -> { format_failure SHOULD_EQUAL_ERROR, ctx }
17
+ end
18
+ end
19
+
20
+ SHOULD_MATCH_ERROR = "%request -- %actual should match %expected".freeze
21
+ SHOULD_INCLUDE_ERROR = "%request -- %actual should include %expected".freeze
22
+ SHOULD_EQUAL_ERROR = "%request -- %actual should equal %expected".freeze
23
+
24
+ private
25
+
26
+ def format_failure(format, ctx)
27
+ format.gsub(/\%(\S+)/) do
28
+ value = ctx.fetch(Regexp.last_match(1).to_sym)
29
+ case value
30
+ when Net::HTTPGenericRequest
31
+ request = value
32
+ s = "#{request.method} #{request.path}"
33
+ s += " body:#{request.body.inspect}" if request.body
34
+ s
35
+ else
36
+ value.inspect
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,72 @@
1
+ # rubocop:disable Lint/HandleExceptions
2
+
3
+ require "socket"
4
+ require "timeout"
5
+
6
+ module RSpec::Httpd
7
+ module Server
8
+ MAX_STARTUP_TIME = 10
9
+
10
+ extend self
11
+
12
+ # builds and returns a server object.
13
+ #
14
+ # You can use this method to retrieve a client connection to a server
15
+ # specified via host:, port:, and, optionally, a command.
16
+ def start!(host:, port:, command:)
17
+ @servers ||= {}
18
+ @servers[[host, port, command]] ||= do_start(host, port, command)
19
+ end
20
+
21
+ private
22
+
23
+ def do_start(host, port, command)
24
+ logger = RSpec::Httpd.logger
25
+ logger.debug "Starting server: #{command}"
26
+
27
+ pid = spawn(command)
28
+
29
+ at_exit do
30
+ begin
31
+ logger.debug "Stopping server at pid #{pid}: #{command}"
32
+ Process.kill("KILL", pid)
33
+ sleep 0.2
34
+ rescue Errno::ESRCH
35
+ end
36
+
37
+ die "Cannot stop server at pid #{pid}: #{command}" if port_open?(host, port)
38
+ end
39
+
40
+ unless wait_for_server(host: host, port: port, pid: pid, timeout: MAX_STARTUP_TIME)
41
+ logger.error "server didn't start at http://#{host}:#{port} pid #{pid}: #{command}"
42
+ exit 1
43
+ end
44
+
45
+ logger.info "Started server at pid #{pid}: #{command}"
46
+ pid
47
+ end
48
+
49
+ def wait_for_server(host:, port:, pid:, timeout:)
50
+ while timeout > 0
51
+ sleep 0.1
52
+ return true if port_open?(host, port)
53
+ return false if Process.waitpid(pid, Process::WNOHANG)
54
+
55
+ timeout -= 0.1
56
+ next if timeout > 0
57
+
58
+ return false
59
+ end
60
+ end
61
+
62
+ def port_open?(host, port)
63
+ Timeout.timeout(0.01) do
64
+ s = TCPSocket.new(host, port)
65
+ s.close
66
+ return true
67
+ end
68
+ rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH, Timeout::Error
69
+ false
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,26 @@
1
+ module RSpec::Httpd
2
+ module GemHelper
3
+ extend self
4
+
5
+ def version(name)
6
+ spec = Gem.loaded_specs[name]
7
+ return "unreleased" unless spec
8
+
9
+ version = spec.version.to_s
10
+ version += "+unreleased" if unreleased?(spec)
11
+ version
12
+ end
13
+
14
+ private
15
+
16
+ def unreleased?(spec)
17
+ return false unless defined?(Bundler::Source::Gemspec)
18
+ return true if spec.source.is_a?(::Bundler::Source::Gemspec)
19
+ return true if spec.source.is_a?(::Bundler::Source::Path)
20
+
21
+ false
22
+ end
23
+ end
24
+
25
+ VERSION = GemHelper.version "rspec-httpd"
26
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rspec-httpd
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.4
4
+ version: 0.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Enrico Thierbach
@@ -58,16 +58,15 @@ executables: []
58
58
  extensions: []
59
59
  extra_rdoc_files: []
60
60
  files:
61
- - Gemfile
62
61
  - README.md
63
- - Rakefile
64
62
  - VERSION
65
- - bin/console
66
- - bin/rubocop
67
63
  - lib/rspec-httpd.rb
68
- - rspec-httpd.gemspec
69
- - scripts/release
70
- - scripts/release.rb
64
+ - lib/rspec/httpd.rb
65
+ - lib/rspec/httpd/client.rb
66
+ - lib/rspec/httpd/config.rb
67
+ - lib/rspec/httpd/expectation.rb
68
+ - lib/rspec/httpd/server.rb
69
+ - lib/rspec/httpd/version.rb
71
70
  homepage: https://github.com/radiospiel/rspec-httpd
72
71
  licenses:
73
72
  - Nonstandard
data/Gemfile DELETED
@@ -1,3 +0,0 @@
1
- source "https://rubygems.org"
2
-
3
- gemspec
data/Rakefile DELETED
@@ -1,18 +0,0 @@
1
- require "rubocop/rake_task"
2
- require "rspec/core/rake_task"
3
-
4
- RSpec::Core::RakeTask.new(:spec)
5
- RuboCop::RakeTask.new(:rubocop)
6
-
7
- desc "Run rspec and then rubocop"
8
- task default: [:spec, :rubocop]
9
-
10
- desc "release a new development gem version"
11
- task :release do
12
- sh "scripts/release.rb"
13
- end
14
-
15
- desc "release a new stable gem version"
16
- task "release:stable" do
17
- sh "BRANCH=stable scripts/release.rb"
18
- end
data/bin/console DELETED
@@ -1,13 +0,0 @@
1
- #!/usr/bin/env ruby
2
- root=File.expand_path "#{File.dirname(__FILE__)}/.."
3
- $: << "#{root}/lib"
4
-
5
- require "bundler"
6
- Bundler.require
7
-
8
- require "irb"
9
- require "rspec-httpd"
10
-
11
-
12
- IRB.start
13
-
data/bin/rubocop DELETED
@@ -1,29 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- #
5
- # This file was generated by Bundler.
6
- #
7
- # The application 'rubocop' is installed as part of a gem, and
8
- # this file is here to facilitate running it.
9
- #
10
-
11
- require "pathname"
12
- ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
13
- Pathname.new(__FILE__).realpath)
14
-
15
- bundle_binstub = File.expand_path("../bundle", __FILE__)
16
-
17
- if File.file?(bundle_binstub)
18
- if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
19
- load(bundle_binstub)
20
- else
21
- abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
22
- Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
23
- end
24
- end
25
-
26
- require "rubygems"
27
- require "bundler/setup"
28
-
29
- load Gem.bin_path("rubocop", "rubocop")
data/rspec-httpd.gemspec DELETED
@@ -1,23 +0,0 @@
1
- Gem::Specification.new do |s|
2
- s.name = "rspec-httpd"
3
- s.version = File.read("VERSION").chomp
4
- s.date = "2019-03-01"
5
- s.summary = "RSpec testing for HTTP requests"
6
- s.description = "RSpec testing for HTTP requests"
7
- s.authors = ["Enrico Thierbach"]
8
- s.email = "eno@open-lab.org"
9
- s.homepage = "https://github.com/radiospiel/rspec-httpd"
10
- s.license = "Nonstandard"
11
- s.files = `git ls-files`.split($OUTPUT_RECORD_SEPARATOR).reject { |f| f.match(%r{tasks/|spec/|log/|^\.}) }
12
-
13
- # Runtime dependencies of this gem:
14
- # s.add_dependency 'activesupport', '~> 4.2.10'
15
- # s.add_dependency 'expectation', '1.1.1'
16
- # s.add_dependency 'faraday', '> 0.9'
17
- # s.add_dependency 'uri_template', '~> 0.7', '>= 0.7.0'
18
-
19
- # Gems for dev and test env:
20
- s.add_development_dependency "rake", "~> 12.0"
21
- s.add_development_dependency "rspec", "~> 3.4"
22
- s.add_development_dependency "rubocop", "~> 0.65.0"
23
- end
data/scripts/release DELETED
@@ -1,2 +0,0 @@
1
- #!/bin/bash
2
- $0.rb "$@"
data/scripts/release.rb DELETED
@@ -1,92 +0,0 @@
1
- #!/usr/bin/env ruby
2
-
3
- # -- helpers ------------------------------------------------------------------
4
-
5
- def sys(cmd)
6
- STDERR.puts "> #{cmd}"
7
- system cmd
8
- return true if $?.success?
9
-
10
- STDERR.puts "> #{cmd} returned with exitstatus #{$?.exitstatus}"
11
- $?.success?
12
- end
13
-
14
- def sys!(cmd, error: nil)
15
- return true if sys(cmd)
16
- STDERR.puts error if error
17
- exit 1
18
- end
19
-
20
- def die!(msg)
21
- STDERR.puts msg
22
- exit 1
23
- end
24
-
25
- ROOT = File.expand_path("#{File.dirname(__FILE__)}/..")
26
-
27
- GEMSPEC = Dir.glob("*.gemspec").first || die!("Missing gemspec file.")
28
-
29
- # -- Version reading and bumping ----------------------------------------------
30
-
31
- module Version
32
- extend self
33
-
34
- VERSION_FILE = "#{Dir.getwd}/VERSION"
35
-
36
- def read_version
37
- version = File.exist?(VERSION_FILE) ? File.read(VERSION_FILE) : "0.0.1"
38
- version.chomp!
39
- raise "Invalid version number in #{VERSION_FILE}" unless version =~ /^\d+\.\d+\.\d+$/
40
- version
41
- end
42
-
43
- def auto_version_bump
44
- old_version_number = read_version
45
- old = old_version_number.split('.')
46
-
47
- current = old[0..-2] << old[-1].next
48
- current.join('.')
49
- end
50
-
51
- def bump_version
52
- next_version = ENV["VERSION"] || auto_version_bump
53
- File.open(VERSION_FILE, "w") { |io| io.write next_version }
54
- end
55
- end
56
-
57
- # -- check, bump, release a new gem version -----------------------------------
58
-
59
- Dir.chdir ROOT
60
- $BASE_BRANCH = ENV['BRANCH'] || 'master'
61
-
62
- # ENV["BUNDLE_GEMFILE"] = "#{Dir.getwd}/Gemfile"
63
- # sys! "bundle install"
64
-
65
- sys! "git diff --exit-code > /dev/null", error: 'There are unstaged changes in your working directory'
66
- sys! "git diff --cached --exit-code > /dev/null", error: 'There are staged but uncommitted changes'
67
-
68
- sys! "git checkout #{$BASE_BRANCH}"
69
- sys! "git pull"
70
-
71
- Version.bump_version
72
- version = Version.read_version
73
-
74
- sys! "git add VERSION"
75
- sys! "git commit -m \"bump gem to v#{version}\""
76
- sys! "git tag -a v#{version} -m \"Tag #{version}\""
77
-
78
- sys! "gem build #{GEMSPEC}"
79
-
80
- sys! "git push origin #{$BASE_BRANCH}"
81
- sys! 'git push --tags --force'
82
-
83
- sys! "gem push #{Dir.glob('*.gem').first}"
84
-
85
- sys! "mkdir -p pkg"
86
- sys! "mv *.gem pkg"
87
-
88
- STDERR.puts <<-MSG
89
- ================================================================================
90
- Thank you for releasing a new gem version. You made my day.
91
- ================================================================================
92
- MSG