rack-client_spec 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: de9e5964309bcddf739863f1e58aa0f7ca6c248c
4
+ data.tar.gz: 62edd421cfb14b26b66848b5abec62d325e42b95
5
+ SHA512:
6
+ metadata.gz: 59459a7f8c6056ba7ff9390327bc9cab83e9ddccd69a397155f02a13a02495bde6da706a02e47b6241b3948c155ab2b6f5cec29d3682d87eee7252ce383878d9
7
+ data.tar.gz: 90e1a37bc3ff6c65c59501ceaef054609761002d18b5d9b7140607d17360708c3d906c0b6356b3ffd5983a68626ae4fae8176fb65d9f10f09e5eb8c51ba459a8
data/.gitignore ADDED
@@ -0,0 +1,14 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in rack-client_spec.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 harukasan
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,70 @@
1
+ # Rack::ClientSpec
2
+
3
+ Rack::ClientSpec can test your client.
4
+
5
+ ## TODO
6
+
7
+ Current implementation is "it just works" for prototyping, not enough for release.
8
+
9
+ - request flow
10
+ - run multiple test methods
11
+ - non-existence test
12
+ - bug fix
13
+
14
+ ## Installation
15
+
16
+ Add this line to your application's Gemfile:
17
+
18
+ ```ruby
19
+ gem 'rack-client_spec'
20
+ ```
21
+
22
+ And then execute:
23
+
24
+ $ bundle
25
+
26
+ ## Usage
27
+
28
+ Write your client spec like below (this code is included in [lib/lobster_spec.rb](https://github.com/harukasan/rack-client_spec/blob/master/lib/lobster_spec.rb)):
29
+
30
+ ```lobster_spec.rb
31
+ require 'rack/client_spec'
32
+
33
+ class LobsterSpec < Rack::ClientSpec::TestCase # <-- test case
34
+ def test_flip_referer # <-- test method
35
+ get '/' do |req, res| # <-- expect request
36
+ assert { res.status == 200 } # <-- assertion
37
+ end
38
+
39
+ get '/?flip=left' do |req, res| # <-- expect request
40
+ assert { req['HTTP_REFERER'] == 'http://localhost:9292/' } # <-- assertion
41
+ end
42
+
43
+ get '/?flip=right' do |req, res| # <-- expect request
44
+ assert { req['HTTP_REFERER'] == 'http://localhost:9292/?flip=left' }
45
+ end
46
+ end
47
+ end
48
+ ```
49
+
50
+ Then, use ClientSpec in your config.ru:
51
+
52
+ ```lobster.ru
53
+ require 'rack'
54
+ require 'rack/lobster'
55
+ require 'rack/client_spec'
56
+ require 'lobster_spec'
57
+
58
+ use Rack::ClientSpec, LobsterSpec
59
+ run Rack::Lobster.new
60
+ ```
61
+
62
+ Finally, rackup your server, and you can test a client (eg. your favorite web browser).
63
+
64
+ ## Contributing
65
+
66
+ 1. [Fork it](https://github.com/harukasan/rack-client_spec/fork).
67
+ 2. Create your feature branch (`git checkout -b my-new-feature`).
68
+ 3. Commit your changes (`git commit -am 'Add some feature'`).
69
+ 4. Push to the branch (`git push origin my-new-feature`).
70
+ 5. Create a new Pull Request.
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
@@ -0,0 +1,7 @@
1
+ require 'rack'
2
+ require 'rack/lobster'
3
+ require 'rack/client_spec'
4
+ require 'lobster_spec'
5
+
6
+ use Rack::ClientSpec, LobsterSpec
7
+ run Rack::Lobster.new
@@ -0,0 +1,18 @@
1
+ require 'rack/client_spec'
2
+
3
+ class LobsterSpec < Rack::ClientSpec::TestCase # <-- test case
4
+ def test_flip # <-- test method
5
+ get '/' do |req, res| # <-- expect request
6
+ assert { res.status == 200 } # <-- assertion
7
+ end
8
+
9
+ get '/?flip=left' do |req, res| # <-- expect request
10
+ assert { req['HTTP_REFERER'] == 'http://localhost:9292/' } # <-- assertion
11
+ end
12
+
13
+ get '/?flip=right' do |req, res| # <-- expect request
14
+ assert { req['HTTP_REFERER'] == 'http://localhost:9292/?flip=left' }
15
+ end
16
+ end
17
+ end
18
+
@@ -0,0 +1,19 @@
1
+ require 'power_assert'
2
+
3
+ module Rack
4
+ class ClientSpec
5
+ class Assertion < Exception
6
+ end
7
+
8
+ module Assertions
9
+ def assert(&block)
10
+ PowerAssert.start(block, assertion_method: __callee__) do |pa|
11
+ unless pa.yield
12
+ raise Assertion, pa.message_proc.call
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
19
+
@@ -0,0 +1,33 @@
1
+ module Rack
2
+ class ClientSpec
3
+ class ExpectRequest
4
+ attr_reader :path, :env
5
+
6
+ def initialize(method, path, env = {})
7
+ env["REQUEST_METHOD"] = method
8
+ @path = path
9
+ @env = env
10
+ end
11
+
12
+ def match?(env)
13
+ match_path?(env) && match_env?(env)
14
+ end
15
+
16
+ def match_path?(env)
17
+ path = [env['PATH_INFO'], env['QUERY_STRING']].join '?'
18
+ path = path.chop if path[-1] == "?"
19
+ @path === path
20
+ end
21
+
22
+ def match_env?(env)
23
+ !@env.each{|k, v| break unless env[k] == v}.nil?
24
+ end
25
+
26
+ def desc
27
+ "#{@env['REQUEST_METHOD']} #{@path}"
28
+ end
29
+ end
30
+ end
31
+ end
32
+
33
+
@@ -0,0 +1,33 @@
1
+ module Rack
2
+ class ClientSpec
3
+ class GotRequest
4
+ class Response
5
+ def inspect
6
+ "#<Response>"
7
+ end
8
+ def initialize(response)
9
+ @response = response
10
+ end
11
+ def status
12
+ @response[0]
13
+ end
14
+ def headers
15
+ @response[1]
16
+ end
17
+ def body
18
+ @response[2]
19
+ end
20
+ def [](i)
21
+ @response[i]
22
+ end
23
+ end
24
+
25
+ attr_reader :env, :response
26
+
27
+ def initialize(env, response)
28
+ @env = env
29
+ @response = Response.new(response)
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,44 @@
1
+ require 'ansi'
2
+
3
+ module Rack
4
+ class ClientSpec
5
+ module Printer
6
+ def print_initialized
7
+ puts ANSI.white_on_blue{ " ClientSpec " } + ANSI.black_on_green{ " version #{Rack::ClientSpec::VERSION} " }
8
+ end
9
+
10
+ def print_states(states)
11
+ puts ANSI.white_on_blue{ " ClientSpec " } + ANSI.black_on_yellow{ " EXPECT REQUEST SEQUENCE (#{states.size}) " }
12
+ states.each do |name, sequence|
13
+ state = ""
14
+ sequence.each do |req|
15
+ state += ANSI.yellow{ " -> " }
16
+ if req[:passed]
17
+ state += ANSI.black_on_green { req[:request].desc }
18
+ else
19
+ state += ANSI.white { req[:request].desc }
20
+ end
21
+ end
22
+ puts " - #{name}:#{state}"
23
+ end
24
+
25
+ puts ""
26
+ end
27
+
28
+ def print_success(name)
29
+ puts ANSI.black_on_green{ " SUCCESS " } + " #{name}"
30
+ end
31
+
32
+ def print_fail(name, result)
33
+ puts ANSI.black_on_red{ " FAIL " } + " #{name}"
34
+ puts result.error.to_s
35
+ end
36
+
37
+ module_function :print_initialized
38
+ module_function :print_states
39
+ module_function :print_success
40
+ module_function :print_fail
41
+ end
42
+ end
43
+ end
44
+
@@ -0,0 +1,53 @@
1
+ module Rack
2
+ class ClientSpec
3
+ class RequestTrace
4
+ attr_reader :match
5
+
6
+ def initialize(sequences)
7
+ @pointers = {}
8
+ @sequences = {}
9
+ @match = nil
10
+ sequences.each do |name, sequence|
11
+ set_sequence name, sequence
12
+ end
13
+ end
14
+
15
+ def set_sequence(name, sequence)
16
+ @sequences[name] = sequence
17
+ @pointers[name] = 0
18
+ end
19
+
20
+ def reset(name)
21
+ @pointers[name] = 0
22
+ end
23
+
24
+ def trace(env)
25
+ @sequences.each do |name, sequence|
26
+ next unless sequence[@pointers[name]].match?(env)
27
+ @pointers[name] += 1
28
+ if @pointers[name] == sequence.length
29
+ @match = name
30
+ break
31
+ end
32
+ end
33
+ !@match.nil?
34
+ end
35
+
36
+ def states
37
+ states = {}
38
+ @sequences.each do |name, sequence|
39
+ states[name] = []
40
+ sequence.each_with_index do |request, i|
41
+ states[name] << {
42
+ passed: @pointers[name] > i,
43
+ request: request,
44
+ }
45
+ end
46
+ end
47
+
48
+ states
49
+ end
50
+ end
51
+ end
52
+ end
53
+
@@ -0,0 +1,17 @@
1
+
2
+ module Rack
3
+ class ClientSpec
4
+ class Result
5
+ attr_reader :error
6
+
7
+ def initialize(error = nil)
8
+ @error = error
9
+ end
10
+
11
+ def success?
12
+ @error.nil?
13
+ end
14
+ end
15
+ end
16
+ end
17
+
@@ -0,0 +1,89 @@
1
+ require 'power_assert'
2
+ require 'rack/client_spec/assertions'
3
+ require 'rack/client_spec/expect_request'
4
+ require 'rack/client_spec/result'
5
+
6
+ module Rack
7
+ class ClientSpec
8
+ class TestCase
9
+ include Rack::ClientSpec::Assertions
10
+
11
+ def self.make_sequences()
12
+ sequences = {}
13
+ self.instance_methods.grep(/^test_/).map do |name|
14
+ sequences[name] = self.new(nil).call_test_method(name, test: false).sequence
15
+ end
16
+ sequences
17
+ end
18
+
19
+ def self.run_sequence(name, sequence)
20
+ r = nil
21
+ begin
22
+ self.new(sequence).call_test_method(name, test: true)
23
+ rescue Assertion => e
24
+ r = e
25
+ end
26
+ Result.new r
27
+ end
28
+
29
+ attr_reader :sequence
30
+
31
+ def initialize(got_sequence = nil)
32
+ @got_sequence = got_sequence
33
+ @run_test = !got_sequence.nil?
34
+ @sequence = []
35
+ end
36
+
37
+ def assert_request(expect_request, &block)
38
+ got = @got_sequence.shift
39
+ unless expect_request.match? got.env
40
+ raise ArgumentError, "Request is not matched with the expected request"
41
+ end
42
+ block.yield got.env, got.response
43
+ end
44
+
45
+ def call_test_method(name, test: nil)
46
+ @run_test = test unless test.nil?
47
+ setup if respond_to? :setup
48
+ send name
49
+ self
50
+ end
51
+
52
+ def route(method, path, env, &block)
53
+ request = ExpectRequest.new(method, path, env)
54
+ @sequence << request
55
+ assert_request request, &block if @run_test
56
+ end
57
+
58
+ def get(path, **env, &block)
59
+ route "GET", path, env, &block
60
+ end
61
+ def head(path, **env, &block)
62
+ route "HEAD", path, env, &block
63
+ end
64
+ def put(path, **env, &block)
65
+ route "PUT", path, env, &block
66
+ end
67
+ def post(path, **env, &block)
68
+ route "POST", path, env, &block
69
+ end
70
+ def delete(path, **env, &block)
71
+ route "DELETE", path, env, &block
72
+ end
73
+ def options(path, **env, &block)
74
+ route "OPTIONS", path, env, &block
75
+ end
76
+ def patch(path, **env, &block)
77
+ route "PATCH", path, env, &block
78
+ end
79
+ def link(path, **env, &block)
80
+ route "LINK", path, env, &block
81
+ end
82
+ def unlink(path, **env, &block)
83
+ route "UNLINK", path, env, &block
84
+ end
85
+
86
+ private :get, :head, :put, :post, :delete, :options, :patch, :link, :unlink
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,5 @@
1
+ module Rack
2
+ class ClientSpec
3
+ VERSION = "0.0.1"
4
+ end
5
+ end
@@ -0,0 +1,47 @@
1
+ require 'rack/client_spec/version'
2
+ require 'rack/client_spec/test_case'
3
+ require 'rack/client_spec/request_trace'
4
+ require 'rack/client_spec/got_request'
5
+ require 'rack/client_spec/printer'
6
+
7
+ module Rack
8
+ class ClientSpec
9
+ def initialize(app, test_case)
10
+ @app = app
11
+ @test_case = test_case
12
+ @tracer = RequestTrace.new(test_case.make_sequences)
13
+ @sequence = []
14
+ Printer.print_initialized
15
+ Printer.print_states @tracer.states
16
+ end
17
+
18
+ def call(env)
19
+ response = @app.call(env)
20
+ begin
21
+ do_test(env, response)
22
+ rescue => e
23
+ p e # TODO error inspection
24
+ end
25
+ return response
26
+ end
27
+
28
+ def do_test(env, response)
29
+ @sequence << GotRequest.new(env, response)
30
+ matched = @tracer.trace(env)
31
+ if matched
32
+ match_function = @tracer.match
33
+ result = @test_case.run_sequence(match_function, @sequence)
34
+ @tracer.reset(match_function)
35
+ end
36
+
37
+ Printer.print_states @tracer.states
38
+ if result
39
+ if result.success?
40
+ Printer.print_success match_function
41
+ else
42
+ Printer.print_fail match_function, result
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
data/lib/rack.rb ADDED
@@ -0,0 +1,3 @@
1
+ module Rack
2
+ autoload :ClientSpec, 'rack/client_spec'
3
+ end
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'rack/client_spec/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "rack-client_spec"
8
+ spec.version = Rack::ClientSpec::VERSION
9
+ spec.authors = ["harukasan"]
10
+ spec.email = ["harukasan@me.com"]
11
+ spec.summary = %q{Rack middleware for test client behavior}
12
+ spec.description = %q{Rack middleware for test client behavior. It can test client request to your rack applications. }
13
+ spec.homepage = "https://github.com/harukasan/rack-client_spec"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.7"
22
+ spec.add_development_dependency "rake", "~> 10.0"
23
+
24
+ spec.add_dependency "rack", "~> 1.5"
25
+ spec.add_dependency "power_assert", "~> 0.2"
26
+ spec.add_dependency "ansi", "~> 1.4.3"
27
+ end
metadata ADDED
@@ -0,0 +1,133 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rack-client_spec
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - harukasan
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-11-17 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.7'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.7'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rack
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.5'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.5'
55
+ - !ruby/object:Gem::Dependency
56
+ name: power_assert
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '0.2'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '0.2'
69
+ - !ruby/object:Gem::Dependency
70
+ name: ansi
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 1.4.3
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: 1.4.3
83
+ description: 'Rack middleware for test client behavior. It can test client request
84
+ to your rack applications. '
85
+ email:
86
+ - harukasan@me.com
87
+ executables: []
88
+ extensions: []
89
+ extra_rdoc_files: []
90
+ files:
91
+ - ".gitignore"
92
+ - Gemfile
93
+ - LICENSE.txt
94
+ - README.md
95
+ - Rakefile
96
+ - examples/lobster.ru
97
+ - lib/lobster_spec.rb
98
+ - lib/rack.rb
99
+ - lib/rack/client_spec.rb
100
+ - lib/rack/client_spec/assertions.rb
101
+ - lib/rack/client_spec/expect_request.rb
102
+ - lib/rack/client_spec/got_request.rb
103
+ - lib/rack/client_spec/printer.rb
104
+ - lib/rack/client_spec/request_trace.rb
105
+ - lib/rack/client_spec/result.rb
106
+ - lib/rack/client_spec/test_case.rb
107
+ - lib/rack/client_spec/version.rb
108
+ - rack-client_spec.gemspec
109
+ homepage: https://github.com/harukasan/rack-client_spec
110
+ licenses:
111
+ - MIT
112
+ metadata: {}
113
+ post_install_message:
114
+ rdoc_options: []
115
+ require_paths:
116
+ - lib
117
+ required_ruby_version: !ruby/object:Gem::Requirement
118
+ requirements:
119
+ - - ">="
120
+ - !ruby/object:Gem::Version
121
+ version: '0'
122
+ required_rubygems_version: !ruby/object:Gem::Requirement
123
+ requirements:
124
+ - - ">="
125
+ - !ruby/object:Gem::Version
126
+ version: '0'
127
+ requirements: []
128
+ rubyforge_project:
129
+ rubygems_version: 2.2.2
130
+ signing_key:
131
+ specification_version: 4
132
+ summary: Rack middleware for test client behavior
133
+ test_files: []