webmachine-test 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +6 -0
- data/README.md +100 -0
- data/Rakefile +7 -0
- data/lib/webmachine/test/backports/uri.rb +46 -0
- data/lib/webmachine/test/session.rb +90 -0
- data/lib/webmachine/test/version.rb +5 -0
- data/lib/webmachine/test.rb +22 -0
- data/spec/fixtures/test_resource.rb +11 -0
- data/spec/spec_helper.rb +19 -0
- data/spec/webmachine/test/session_spec.rb +145 -0
- data/webmachine-test.gemspec +25 -0
- metadata +73 -0
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,100 @@
|
|
1
|
+
# Webmachine::Test
|
2
|
+
|
3
|
+
[![travis](https://secure.travis-ci.org/bernd/webmachine-test.png)](http://travis-ci.org/bernd/webmachine-test)
|
4
|
+
|
5
|
+
## WARNING
|
6
|
+
|
7
|
+
This library is still under development and incomplete!
|
8
|
+
|
9
|
+
It will be merged into [webmachine-ruby](https://github.com/seancribbs/webmachine-ruby)
|
10
|
+
when it's ready.
|
11
|
+
|
12
|
+
## Description
|
13
|
+
|
14
|
+
Webmachine::Test provides a testing API for
|
15
|
+
[webmachine-ruby](https://github.com/seancribbs/webmachine-ruby) inspired by
|
16
|
+
[rack-test](https://github.com/brynary/rack-test).
|
17
|
+
|
18
|
+
## Example Usage
|
19
|
+
|
20
|
+
### Application
|
21
|
+
|
22
|
+
```ruby
|
23
|
+
require 'webmachine'
|
24
|
+
|
25
|
+
class MyResource < Webmachine::Resource
|
26
|
+
def content_types_provided
|
27
|
+
[['text/plain', :to_text]]
|
28
|
+
end
|
29
|
+
|
30
|
+
def to_text
|
31
|
+
'OK'
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
MyApp = Webmachine::Application.new.tap do |app|
|
36
|
+
app.add_route(['*'], MyResource)
|
37
|
+
end
|
38
|
+
|
39
|
+
# decouple runner from application so that adapter
|
40
|
+
# does not start and block test thread
|
41
|
+
#
|
42
|
+
# MyApp.run
|
43
|
+
```
|
44
|
+
|
45
|
+
### Test with Test::Unit
|
46
|
+
|
47
|
+
```ruby
|
48
|
+
class MyAppTest < Test::Unit::TestCase
|
49
|
+
include Webmachine::Test
|
50
|
+
|
51
|
+
def test_get_root_succeeds
|
52
|
+
get '/'
|
53
|
+
assert_equal 200, response.code
|
54
|
+
end
|
55
|
+
|
56
|
+
def test_get_root_replies_with_string_ok
|
57
|
+
get '/'
|
58
|
+
assert_equal 'OK', response.body
|
59
|
+
end
|
60
|
+
|
61
|
+
def test_get_root_replies_with_content_type_of_text_plain
|
62
|
+
get '/'
|
63
|
+
assert_equal 'text/plain', response.headers['Content-Type']
|
64
|
+
end
|
65
|
+
|
66
|
+
def app
|
67
|
+
MyApp
|
68
|
+
end
|
69
|
+
end
|
70
|
+
```
|
71
|
+
|
72
|
+
### Test with RSpec
|
73
|
+
|
74
|
+
```ruby
|
75
|
+
require 'webmachine/test'
|
76
|
+
require 'myapp'
|
77
|
+
|
78
|
+
describe MyApp do
|
79
|
+
include Webmachine::Test
|
80
|
+
|
81
|
+
let(:app) { MyApp }
|
82
|
+
|
83
|
+
describe 'GET /' do
|
84
|
+
it 'succeeds' do
|
85
|
+
get '/'
|
86
|
+
response.code.should == 200
|
87
|
+
end
|
88
|
+
|
89
|
+
it 'replies with the string OK' do
|
90
|
+
get '/'
|
91
|
+
response.body.should == 'OK'
|
92
|
+
end
|
93
|
+
|
94
|
+
it 'replies with a content type of text/plain' do
|
95
|
+
get '/'
|
96
|
+
response.headers['Content-Type'].should == 'text/plain'
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
```
|
data/Rakefile
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
# :stopdoc:
|
2
|
+
|
3
|
+
# Stolen from ruby core's uri/common.rb, with modifications to support 1.8.x
|
4
|
+
#
|
5
|
+
# https://github.com/ruby/ruby/blob/trunk/lib/uri/common.rb
|
6
|
+
#
|
7
|
+
# (via rack)
|
8
|
+
|
9
|
+
module URI
|
10
|
+
TBLENCWWWCOMP_ = {} # :nodoc:
|
11
|
+
256.times do |i|
|
12
|
+
TBLENCWWWCOMP_[i.chr] = '%%%02X' % i
|
13
|
+
end
|
14
|
+
TBLENCWWWCOMP_[' '] = '+'
|
15
|
+
TBLENCWWWCOMP_.freeze
|
16
|
+
TBLDECWWWCOMP_ = {} # :nodoc:
|
17
|
+
256.times do |i|
|
18
|
+
h, l = i>>4, i&15
|
19
|
+
TBLDECWWWCOMP_['%%%X%X' % [h, l]] = i.chr
|
20
|
+
TBLDECWWWCOMP_['%%%x%X' % [h, l]] = i.chr
|
21
|
+
TBLDECWWWCOMP_['%%%X%x' % [h, l]] = i.chr
|
22
|
+
TBLDECWWWCOMP_['%%%x%x' % [h, l]] = i.chr
|
23
|
+
end
|
24
|
+
TBLDECWWWCOMP_['+'] = ' '
|
25
|
+
TBLDECWWWCOMP_.freeze
|
26
|
+
|
27
|
+
# Encode given +s+ to URL-encoded form data.
|
28
|
+
#
|
29
|
+
# This method doesn't convert *, -, ., 0-9, A-Z, _, a-z, but does convert SP
|
30
|
+
# (ASCII space) to + and converts others to %XX.
|
31
|
+
#
|
32
|
+
# This is an implementation of
|
33
|
+
# http://www.w3.org/TR/html5/forms.html#url-encoded-form-data
|
34
|
+
#
|
35
|
+
# See URI.decode_www_form_component, URI.encode_www_form
|
36
|
+
def self.encode_www_form_component(s)
|
37
|
+
str = s.to_s
|
38
|
+
if RUBY_VERSION < "1.9" && $KCODE =~ /u/i
|
39
|
+
str.gsub(/([^ a-zA-Z0-9_.-]+)/) do
|
40
|
+
'%' + $1.unpack('H2' * Rack::Utils.bytesize($1)).join('%').upcase
|
41
|
+
end.tr(' ', '+')
|
42
|
+
else
|
43
|
+
str.gsub(/[^*\-.0-9A-Z_a-z]/) {|m| TBLENCWWWCOMP_[m]}
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
require 'stringio'
|
2
|
+
require 'uri'
|
3
|
+
|
4
|
+
# Ruby 1.8 does not have URI.encode_www_form_component.
|
5
|
+
unless URI.respond_to?(:encode_www_form_component)
|
6
|
+
require 'webmachine/test/backports/uri'
|
7
|
+
end
|
8
|
+
|
9
|
+
module Webmachine
|
10
|
+
module Test
|
11
|
+
class Session
|
12
|
+
HTTP_METHODS = %W(HEAD GET PUT POST PATCH DELETE OPTIONS)
|
13
|
+
|
14
|
+
def initialize(app)
|
15
|
+
@headers = Webmachine::Headers.new
|
16
|
+
@body = nil
|
17
|
+
@req = nil
|
18
|
+
@res = nil
|
19
|
+
@app = app
|
20
|
+
end
|
21
|
+
|
22
|
+
# Returns the request object.
|
23
|
+
def request
|
24
|
+
@req || webmachine_test_error('No request object yet. Issue a request first.')
|
25
|
+
end
|
26
|
+
|
27
|
+
# Returns the response object after a request has been made.
|
28
|
+
def response
|
29
|
+
@res || webmachine_test_error('No response yet. Issue a request first!')
|
30
|
+
end
|
31
|
+
|
32
|
+
# Set a single header for the next request.
|
33
|
+
def header(name, value)
|
34
|
+
@headers[name] = value
|
35
|
+
end
|
36
|
+
|
37
|
+
# Set the request body.
|
38
|
+
def body(value)
|
39
|
+
@body = value.respond_to?(:read) ? value : StringIO.new(value.to_s)
|
40
|
+
end
|
41
|
+
|
42
|
+
HTTP_METHODS.each do |method|
|
43
|
+
class_eval <<-__RUBY
|
44
|
+
def #{method.downcase}(uri, options = {})
|
45
|
+
do_request('#{method.upcase}', uri, options)
|
46
|
+
end
|
47
|
+
__RUBY
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
def webmachine_test_error(msg)
|
52
|
+
raise Webmachine::Test::Error.new(msg)
|
53
|
+
end
|
54
|
+
|
55
|
+
def do_request(method, uri, options)
|
56
|
+
uri = URI.parse(uri)
|
57
|
+
uri.scheme ||= 'http'
|
58
|
+
uri.host ||= 'localhost'
|
59
|
+
|
60
|
+
add_query_params(uri, options[:params])
|
61
|
+
|
62
|
+
# Set some default headers and merge the provided ones.
|
63
|
+
@headers['Host'] = [uri.host, uri.port].compact.join(':') unless @headers['Host']
|
64
|
+
@headers['Accept'] = '*/*' unless @headers['Accept']
|
65
|
+
|
66
|
+
options[:headers] ||= {}
|
67
|
+
options[:headers].each { |k, v| @headers[k] = v }
|
68
|
+
|
69
|
+
@body ||= options[:body] || StringIO.new
|
70
|
+
|
71
|
+
@req = Webmachine::Request.new(method, uri, @headers, @body)
|
72
|
+
@res = Webmachine::Response.new
|
73
|
+
|
74
|
+
@app.dispatcher.dispatch(@req, @res)
|
75
|
+
return @res
|
76
|
+
end
|
77
|
+
|
78
|
+
def add_query_params(uri, params)
|
79
|
+
if params
|
80
|
+
q = params.map do |k, v|
|
81
|
+
k, v = URI.encode_www_form_component(k), URI.encode_www_form_component(v)
|
82
|
+
"#{k}=#{v}"
|
83
|
+
end.join('&')
|
84
|
+
|
85
|
+
uri.query = uri.query ? [uri.query, q].join('&') : q
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
require 'webmachine'
|
3
|
+
require 'webmachine/test/session'
|
4
|
+
|
5
|
+
module Webmachine
|
6
|
+
module Test
|
7
|
+
extend Forwardable
|
8
|
+
|
9
|
+
# Exception class for Test errors.
|
10
|
+
class Error < StandardError
|
11
|
+
end
|
12
|
+
|
13
|
+
def current_session
|
14
|
+
@session ||= Webmachine::Test::Session.new(app)
|
15
|
+
end
|
16
|
+
|
17
|
+
def_delegators :current_session, :request, :header, :headers, :body, :response,
|
18
|
+
:get, :post, :put, :patch, :delete,
|
19
|
+
:options, :head
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler/setup'
|
3
|
+
|
4
|
+
require 'webmachine/application'
|
5
|
+
require 'webmachine/test'
|
6
|
+
require 'fixtures/test_resource'
|
7
|
+
|
8
|
+
module WebmachineTestApplication
|
9
|
+
def app
|
10
|
+
@app ||= Webmachine::Application.new.tap do |test_app|
|
11
|
+
test_app.add_route(['*'], TestResource)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
RSpec.configure do |c|
|
17
|
+
c.include WebmachineTestApplication
|
18
|
+
end
|
19
|
+
|
@@ -0,0 +1,145 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Webmachine::Test::Session do
|
4
|
+
include Webmachine::Test
|
5
|
+
|
6
|
+
describe "#request" do
|
7
|
+
it "returns the Webmachine::Request object" do
|
8
|
+
get '/'
|
9
|
+
request.should be_a(Webmachine::Request)
|
10
|
+
end
|
11
|
+
|
12
|
+
context "without a request" do
|
13
|
+
it "raises an exception" do
|
14
|
+
expect { request }.to raise_error(Webmachine::Test::Error)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
describe "#response" do
|
20
|
+
it "returns the Webmachine::Response object" do
|
21
|
+
get '/'
|
22
|
+
response.should be_a(Webmachine::Response)
|
23
|
+
end
|
24
|
+
|
25
|
+
context "without a request" do
|
26
|
+
it "raises an exception" do
|
27
|
+
expect { response }.to raise_error(Webmachine::Test::Error)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
shared_examples_for "a HTTP verb" do
|
33
|
+
it "executes a GET request" do
|
34
|
+
send verb, '/'
|
35
|
+
request.method.should == verb.upcase
|
36
|
+
end
|
37
|
+
|
38
|
+
it "sets the provided header" do
|
39
|
+
send verb, '/', :headers => {'Accept' => 'application/x-gunzip'}
|
40
|
+
request.headers['Accept'].should == 'application/x-gunzip'
|
41
|
+
end
|
42
|
+
|
43
|
+
context "with a complete URI" do
|
44
|
+
it "sets the correct host header" do
|
45
|
+
send verb, 'http://example.com:3000/foo'
|
46
|
+
request.headers['Host'].should == 'example.com:3000'
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
context "with an incomplete URI" do
|
51
|
+
it "sets the correct host header" do
|
52
|
+
send verb, '/foo'
|
53
|
+
request.headers['Host'].should == 'localhost'
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
it "accepts query parameters in the path" do
|
58
|
+
send verb,'/?lang=en&foo=bar'
|
59
|
+
request.query['lang'].should == 'en'
|
60
|
+
request.query['foo'].should == 'bar'
|
61
|
+
end
|
62
|
+
|
63
|
+
it "accepts query parameters in the options hash" do
|
64
|
+
send verb, '/?foo=bar', :params => {'lang' => 'en'}
|
65
|
+
request.query['lang'].should == 'en'
|
66
|
+
request.query['foo'].should == 'bar'
|
67
|
+
end
|
68
|
+
|
69
|
+
it "escapes the query parameters" do
|
70
|
+
expect {
|
71
|
+
send verb, '/', :params => {'test' => 'foo bar'}
|
72
|
+
}.to_not raise_error(URI::InvalidComponentError)
|
73
|
+
end
|
74
|
+
|
75
|
+
it "encodes the query key and value." do
|
76
|
+
send verb, '/', :params => { "foo=" => "bar=" }
|
77
|
+
request.uri.query.should == "foo%3D=bar%3D"
|
78
|
+
end
|
79
|
+
|
80
|
+
it "returns the Webmachine::Request object" do
|
81
|
+
send(verb, '/').should be_a(Webmachine::Response)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
describe "#get" do
|
86
|
+
let(:verb) { 'get' }
|
87
|
+
it_should_behave_like "a HTTP verb"
|
88
|
+
end
|
89
|
+
|
90
|
+
describe "#post" do
|
91
|
+
let(:verb) { 'post' }
|
92
|
+
it_should_behave_like "a HTTP verb"
|
93
|
+
end
|
94
|
+
|
95
|
+
describe "#put" do
|
96
|
+
let(:verb) { 'put' }
|
97
|
+
it_should_behave_like "a HTTP verb"
|
98
|
+
end
|
99
|
+
|
100
|
+
describe "#patch" do
|
101
|
+
let(:verb) { 'patch' }
|
102
|
+
it_should_behave_like "a HTTP verb"
|
103
|
+
end
|
104
|
+
|
105
|
+
describe "#delete" do
|
106
|
+
let(:verb) { 'delete' }
|
107
|
+
it_should_behave_like "a HTTP verb"
|
108
|
+
end
|
109
|
+
|
110
|
+
describe "#head" do
|
111
|
+
let(:verb) { 'head' }
|
112
|
+
it_should_behave_like "a HTTP verb"
|
113
|
+
end
|
114
|
+
|
115
|
+
describe "#options" do
|
116
|
+
let(:verb) { 'options' }
|
117
|
+
it_should_behave_like "a HTTP verb"
|
118
|
+
end
|
119
|
+
|
120
|
+
describe "#header" do
|
121
|
+
it "sets the given header value" do
|
122
|
+
header('Foo', 'bar-baz')
|
123
|
+
get '/'
|
124
|
+
request.headers['Foo'].should == 'bar-baz'
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
describe "#body" do
|
129
|
+
context "given a string" do
|
130
|
+
it "sets the body" do
|
131
|
+
body('test body')
|
132
|
+
get '/'
|
133
|
+
request.body.read.should == 'test body'
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
context "given an IO object" do
|
138
|
+
it "sets the body" do
|
139
|
+
body(StringIO.new('foo'))
|
140
|
+
get '/'
|
141
|
+
request.body.read.should == 'foo'
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "webmachine/test/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "webmachine-test"
|
7
|
+
s.version = Webmachine::Test::VERSION
|
8
|
+
s.authors = ["Bernd Ahlers"]
|
9
|
+
s.email = ["bernd@tuneafish.de"]
|
10
|
+
s.homepage = ""
|
11
|
+
s.summary = %q{Test API for webmachine-ruby}
|
12
|
+
s.description = <<-DESC.gsub(/\s+/, ' ')
|
13
|
+
Webmachine::Test provides a testing API for webmachine-ruby inspired
|
14
|
+
by rack-test.
|
15
|
+
DESC
|
16
|
+
|
17
|
+
s.rubyforge_project = "webmachine-test"
|
18
|
+
|
19
|
+
s.files = `git ls-files`.split("\n").reject {|f| f =~ /^\./ }
|
20
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
21
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
22
|
+
s.require_paths = ["lib"]
|
23
|
+
|
24
|
+
s.add_runtime_dependency(%q<webmachine>)
|
25
|
+
end
|
metadata
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: webmachine-test
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Bernd Ahlers
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-07-07 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: webmachine
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
description: ! ' Webmachine::Test provides a testing API for webmachine-ruby inspired
|
31
|
+
by rack-test. '
|
32
|
+
email:
|
33
|
+
- bernd@tuneafish.de
|
34
|
+
executables: []
|
35
|
+
extensions: []
|
36
|
+
extra_rdoc_files: []
|
37
|
+
files:
|
38
|
+
- Gemfile
|
39
|
+
- README.md
|
40
|
+
- Rakefile
|
41
|
+
- lib/webmachine/test.rb
|
42
|
+
- lib/webmachine/test/backports/uri.rb
|
43
|
+
- lib/webmachine/test/session.rb
|
44
|
+
- lib/webmachine/test/version.rb
|
45
|
+
- spec/fixtures/test_resource.rb
|
46
|
+
- spec/spec_helper.rb
|
47
|
+
- spec/webmachine/test/session_spec.rb
|
48
|
+
- webmachine-test.gemspec
|
49
|
+
homepage: ''
|
50
|
+
licenses: []
|
51
|
+
post_install_message:
|
52
|
+
rdoc_options: []
|
53
|
+
require_paths:
|
54
|
+
- lib
|
55
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
56
|
+
none: false
|
57
|
+
requirements:
|
58
|
+
- - ! '>='
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '0'
|
61
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
62
|
+
none: false
|
63
|
+
requirements:
|
64
|
+
- - ! '>='
|
65
|
+
- !ruby/object:Gem::Version
|
66
|
+
version: '0'
|
67
|
+
requirements: []
|
68
|
+
rubyforge_project: webmachine-test
|
69
|
+
rubygems_version: 1.8.24
|
70
|
+
signing_key:
|
71
|
+
specification_version: 3
|
72
|
+
summary: Test API for webmachine-ruby
|
73
|
+
test_files: []
|