mosquito 0.1.2 → 0.1.3
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +22 -0
- data/Manifest.txt +10 -16
- data/README.txt +54 -29
- data/Rakefile +29 -2
- data/lib/mosquito.rb +444 -87
- data/public/bare.rb +71 -0
- data/public/blog.rb +1 -6
- data/public/blog/controllers.rb +157 -110
- data/public/blog/models.rb +8 -9
- data/public/blog/views.rb +59 -61
- data/test/sage_advice_cases/parsing_arrays.rb +25 -0
- data/test/test_bare.rb +66 -0
- data/test/test_blog.rb +140 -26
- data/test/test_helpers.rb +20 -0
- data/test/test_mock_request.rb +292 -0
- data/test/test_mock_upload.rb +55 -0
- metadata +31 -18
- data/public/homepage.rb +0 -64
- data/test/test_homepage.rb +0 -23
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'camping'
|
3
|
+
|
4
|
+
Camping.goes :ParsingArrays
|
5
|
+
|
6
|
+
class ParsingArrays::Controllers::Klonk < ParsingArrays::Controllers::R('/')
|
7
|
+
def get; render :foam; end
|
8
|
+
def post; input.inspect; end
|
9
|
+
end
|
10
|
+
|
11
|
+
module ParsingArrays::Views
|
12
|
+
def foam
|
13
|
+
h2 "This is multipart with arrays"
|
14
|
+
form(:method => :post, :enctype => 'multipart/form-data') { _inputs }
|
15
|
+
h2 "This is urlencoded with arrays"
|
16
|
+
form(:method => :post, :enctype => 'application/x-www-form-urlencoded') { _inputs }
|
17
|
+
end
|
18
|
+
|
19
|
+
def _inputs
|
20
|
+
input :type => :text, :name => "array", :value => '1'
|
21
|
+
input :type => :text, :name => "array", :value => '2'
|
22
|
+
input :type => :text, :name => "array", :value => '3'
|
23
|
+
input :type => :submit, :name => 'flush', :value => 'Observe'
|
24
|
+
end
|
25
|
+
end
|
data/test/test_bare.rb
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/../lib/mosquito"
|
2
|
+
require File.dirname(__FILE__) + "/../public/bare"
|
3
|
+
|
4
|
+
# When you got a few Camping apps in one process you actually install the session
|
5
|
+
# for all of them simply by including. We want to test operation without sessions so we
|
6
|
+
# call this app Bare, which comes before Blog.
|
7
|
+
class TestBare < Camping::FunctionalTest
|
8
|
+
|
9
|
+
test "should get index with success" do
|
10
|
+
get '/'
|
11
|
+
assert_response :success
|
12
|
+
assert_no_session
|
13
|
+
assert_match_body %r!Charles!
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_get_without_arguments_should_give_us_the_index_page
|
17
|
+
get
|
18
|
+
assert_response :success
|
19
|
+
assert_match_body %r!Charles!
|
20
|
+
end
|
21
|
+
|
22
|
+
test "should get page with success" do
|
23
|
+
get '/sample'
|
24
|
+
assert_response :success
|
25
|
+
assert_no_session
|
26
|
+
assert_match_body %r!<p>A sample page</p>!
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_request_uri_preserves_query_vars
|
30
|
+
get '/sample', :somevar => 10
|
31
|
+
assert_equal '/bare/sample?somevar=10', @request['REQUEST_URI']
|
32
|
+
end
|
33
|
+
|
34
|
+
test "should assert_no_match_body" do
|
35
|
+
get '/sample'
|
36
|
+
assert_no_match_body /Rubber\s+Bubblegum\s+Burt Reynolds\s+Hippopotamus/
|
37
|
+
end
|
38
|
+
|
39
|
+
test "should return error" do
|
40
|
+
get '/error'
|
41
|
+
assert_response :error
|
42
|
+
end
|
43
|
+
|
44
|
+
test "should return 404 error" do
|
45
|
+
get '/error404'
|
46
|
+
assert_response 404
|
47
|
+
end
|
48
|
+
|
49
|
+
def test_assigning_verbatim_post_payload
|
50
|
+
post '/sample', 'foo=bar&plain=flat'
|
51
|
+
@request.body.rewind
|
52
|
+
assert_equal 'foo=bar&plain=flat', @request.body.read
|
53
|
+
end
|
54
|
+
|
55
|
+
test "should redirect" do
|
56
|
+
get '/redirect'
|
57
|
+
assert_redirected_to '/faq'
|
58
|
+
end
|
59
|
+
|
60
|
+
# test "should send file" do
|
61
|
+
# get '/file'
|
62
|
+
# assert_response :success
|
63
|
+
# # TODO
|
64
|
+
# end
|
65
|
+
|
66
|
+
end
|
data/test/test_blog.rb
CHANGED
@@ -1,31 +1,51 @@
|
|
1
1
|
require File.dirname(__FILE__) + "/../lib/mosquito"
|
2
2
|
require File.dirname(__FILE__) + "/../public/blog"
|
3
|
-
|
4
3
|
Blog.create
|
5
4
|
include Blog::Models
|
6
5
|
|
7
6
|
class TestBlog < Camping::FunctionalTest
|
8
|
-
|
9
7
|
fixtures :blog_posts, :blog_users, :blog_comments
|
10
|
-
|
8
|
+
|
11
9
|
def setup
|
12
10
|
super
|
13
|
-
#
|
11
|
+
# We inject the session into the blog here to prevent it from attaching to Bare as well. Normally this
|
12
|
+
# should not happen but we need to take sides to test for sessioneless Camping compliance.
|
13
|
+
unless @sesion_set
|
14
|
+
Blog.send(:include, Camping::Session)
|
15
|
+
ActiveRecord::Migration.suppress_messages do
|
16
|
+
::Camping::Models::Session.create_schema
|
17
|
+
end
|
18
|
+
@sesion_set = true
|
19
|
+
end
|
14
20
|
end
|
15
|
-
|
21
|
+
|
16
22
|
def test_cookies
|
17
23
|
get '/cookies'
|
18
|
-
assert_cookie 'awesome_cookie', 'camping
|
24
|
+
assert_cookie 'awesome_cookie', 'camping for good'
|
25
|
+
assert_equal @state.awesome_data, 'camping for good'
|
26
|
+
get '/'
|
19
27
|
assert_equal @state.awesome_data, 'camping for good'
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_cookies_persisted_across_requests_and_escaping_properly_handled
|
31
|
+
@cookies["asgård"] = 'Wøbble'
|
32
|
+
get '/cookies'
|
33
|
+
assert_equal 'asgård=W%C3%B8bble', @request['HTTP_COOKIE'], "The cookie val shouldbe escaped"
|
34
|
+
assert_response :success
|
35
|
+
assert_equal 'Wøbble', @cookies["asgård"]
|
36
|
+
|
20
37
|
get '/'
|
21
38
|
assert_equal @state.awesome_data, 'camping for good'
|
39
|
+
assert_equal 'Wøbble', @cookies["asgård"]
|
22
40
|
end
|
23
|
-
|
41
|
+
|
24
42
|
def test_index
|
25
43
|
get
|
26
44
|
assert_response :success
|
27
45
|
assert_match_body %r!>blog<!
|
28
46
|
assert_not_equal @state.awesome_data, 'camping for good'
|
47
|
+
assert_kind_of Array, @assigns[:posts]
|
48
|
+
assert_kind_of Post, assigns[:posts].first
|
29
49
|
end
|
30
50
|
|
31
51
|
def test_view
|
@@ -33,7 +53,7 @@ class TestBlog < Camping::FunctionalTest
|
|
33
53
|
assert_response :success
|
34
54
|
assert_match_body %r!The quick fox jumped over the lazy dog!
|
35
55
|
end
|
36
|
-
|
56
|
+
|
37
57
|
def test_styles
|
38
58
|
get 'styles.css'
|
39
59
|
assert_match_body %r!Utopia!
|
@@ -52,20 +72,105 @@ class TestBlog < Camping::FunctionalTest
|
|
52
72
|
|
53
73
|
def test_comment
|
54
74
|
assert_difference(Comment) {
|
55
|
-
post 'comment',
|
56
|
-
|
57
|
-
|
75
|
+
post 'comment', {
|
76
|
+
:post_username => 'jim',
|
77
|
+
:post_body => 'Nice article.',
|
78
|
+
:post_id => 1
|
79
|
+
}
|
58
80
|
assert_response :redirect
|
59
81
|
assert_redirected_to '/view/1'
|
60
82
|
}
|
61
83
|
end
|
62
84
|
|
85
|
+
def test_sage_advice_raised_when_getting_with_files
|
86
|
+
assert_raise(Mosquito::SageAdvice) do
|
87
|
+
get '/view/1', :afile => Mosquito::MockUpload.new("apic.jpg")
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def test_session_roundtrip_across_successive_requests
|
92
|
+
get '/session-roundtrip'
|
93
|
+
assert @state.has_key?(:flag_in_session)
|
94
|
+
|
95
|
+
assert_session_started
|
96
|
+
post '/session-roundtrip'
|
97
|
+
assert @state.has_key?(:second_flag), "The :second_flag key in the session gets set only if the previous flag was present"
|
98
|
+
assert_session_started
|
99
|
+
end
|
100
|
+
|
101
|
+
def test_request_uri_has_no_double_slashes
|
102
|
+
get '/session-roundtrip'
|
103
|
+
assert_equal "/blog/session-roundtrip", @request['REQUEST_URI']
|
104
|
+
end
|
105
|
+
|
106
|
+
def test_follow_redirect
|
107
|
+
get '/redirector'
|
108
|
+
assert_response :redirect
|
109
|
+
|
110
|
+
assert_redirected_to '/sniffer?one=two'
|
111
|
+
|
112
|
+
follow_redirect
|
113
|
+
roundtipped_params = YAML::load(StringIO.new(@response.body))
|
114
|
+
ref = {"one" => "two"}.with_indifferent_access
|
115
|
+
assert_equal ref, roundtipped_params
|
116
|
+
end
|
117
|
+
|
118
|
+
def test_request_honors_verbatim_query_string_and_passed_params
|
119
|
+
assert_nothing_raised do
|
120
|
+
get '/sniffer?one=2&foo=baz', :taing => 44
|
121
|
+
end
|
122
|
+
|
123
|
+
roundtripped = YAML::load(StringIO.new(@response.body.to_s))
|
124
|
+
ref = {"taing"=>"44", "one" => "2", "foo" => "baz"}
|
125
|
+
assert_equal ref, roundtripped
|
126
|
+
end
|
127
|
+
|
128
|
+
def test_uplaod_gets_a_quick_uplaod_handle
|
129
|
+
file = upload("pic.jpg")
|
130
|
+
assert_kind_of Mosquito::MockUpload, file
|
131
|
+
end
|
132
|
+
|
133
|
+
def test_intrinsic_methods
|
134
|
+
# This WILL use mocks when we get to it from more pressing matters
|
135
|
+
delete '/rest'
|
136
|
+
assert_equal 'Called delete', @response.body
|
137
|
+
|
138
|
+
put '/rest'
|
139
|
+
assert_equal 'Called put', @response.body
|
140
|
+
end
|
141
|
+
|
142
|
+
def calling_with_an_absolute_url_should_relativize
|
143
|
+
assert_equal 'test.host', @request.domain
|
144
|
+
put 'http://test.host/blog/rest'
|
145
|
+
assert_nothing_raised do
|
146
|
+
assert_equal 'Called put', @response.body
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
def test_calling_with_an_absolute_url_outside_of_the_default_test_host_must_raise
|
151
|
+
assert_equal 'test.host', @request.domain
|
152
|
+
assert_raise(Mosquito::NonLocalRequest) do
|
153
|
+
put 'http://yahoo.com/blog/rest'
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
def test_calling_with_an_absolute_url_outside_of_the_custom_test_host_must_raise
|
158
|
+
@request.domain = 'foo.bar'
|
159
|
+
assert_raise(Mosquito::NonLocalRequest) do
|
160
|
+
put 'http://test.host/blog/rest'
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
63
164
|
end
|
64
165
|
|
65
|
-
class TestPost < Camping::
|
166
|
+
class TestPost < Camping::ModelTest
|
66
167
|
|
67
168
|
fixtures :blog_posts, :blog_users, :blog_comments
|
68
|
-
|
169
|
+
|
170
|
+
def test_fixtures_path_is_relative_to_the_testcase
|
171
|
+
assert_equal 'test/fixtures/', self.class.fixture_path
|
172
|
+
end
|
173
|
+
|
69
174
|
def test_create
|
70
175
|
post = create
|
71
176
|
assert post.valid?
|
@@ -83,19 +188,21 @@ class TestPost < Camping::UnitTest
|
|
83
188
|
assert_equal original_count - 1, Post.count
|
84
189
|
end
|
85
190
|
|
86
|
-
private
|
191
|
+
private
|
87
192
|
|
88
193
|
def create(options={})
|
89
|
-
Post.create({
|
90
|
-
|
91
|
-
|
194
|
+
Post.create({
|
195
|
+
:user_id => 1,
|
196
|
+
:title => "Title",
|
197
|
+
:body => "Body"
|
198
|
+
}.merge(options))
|
92
199
|
end
|
93
|
-
|
200
|
+
|
94
201
|
end
|
95
202
|
|
96
|
-
class TestUser < Camping::
|
203
|
+
class TestUser < Camping::ModelTest
|
97
204
|
|
98
|
-
fixtures :blog_posts, :blog_users, :blog_comments
|
205
|
+
fixtures :blog_posts, :blog_users, :blog_comments
|
99
206
|
|
100
207
|
def test_create
|
101
208
|
user = create
|
@@ -108,12 +215,19 @@ class TestUser < Camping::UnitTest
|
|
108
215
|
assert_not_nil user.errors.on(:username)
|
109
216
|
end
|
110
217
|
|
111
|
-
|
112
|
-
|
218
|
+
test "should require username" do
|
219
|
+
assert_no_difference(User, :count) do
|
220
|
+
User.create(:username => nil)
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
private
|
225
|
+
|
113
226
|
def create(options={})
|
114
|
-
User.create({
|
115
|
-
|
227
|
+
User.create({
|
228
|
+
:username => 'godfrey',
|
229
|
+
:password => 'password'
|
230
|
+
}.merge(options))
|
116
231
|
end
|
117
|
-
|
118
|
-
end
|
119
232
|
|
233
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/../lib/mosquito"
|
2
|
+
|
3
|
+
class TestHelpers < Camping::ModelTest
|
4
|
+
# http://rubyforge.org/tracker/index.php?func=detail&aid=8921&group_id=351&atid=1416
|
5
|
+
def test_supports_old_style_and_new_style_fixture_generation
|
6
|
+
assert self.respond_to?(:create_fixtures), "Oldstyle method should work"
|
7
|
+
assert self.class.respond_to?(:fixtures), "Newstyle method should work"
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_stash_and_unstash
|
11
|
+
someval = {:foo => "bar"}
|
12
|
+
assert_nothing_raised do
|
13
|
+
assert_equal someval, Mosquito::stash(someval)
|
14
|
+
end
|
15
|
+
|
16
|
+
retr = Mosquito.unstash
|
17
|
+
assert_nil Mosquito.unstash, "There is nothing stashed now"
|
18
|
+
assert_equal retr, someval, "The value should be retrieved"
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,292 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/../lib/mosquito"
|
2
|
+
|
3
|
+
$KCODE = 'u'
|
4
|
+
# Sadly, Camping does not mandate UTF-8 - but we will use it here to torment the
|
5
|
+
# URL-escaping routines wïs ze ümlåuts.
|
6
|
+
|
7
|
+
# We use Sniffer to check if our request is parsed properly. After being called Sniffer
|
8
|
+
# will raise a Messenger exception with the @input inside.
|
9
|
+
Camping.goes :Sniffer
|
10
|
+
class Sniffer::Controllers::ParamPeeker < Sniffer::Controllers::R('/')
|
11
|
+
class Messenger < RuntimeError
|
12
|
+
attr_reader :packet
|
13
|
+
def initialize(packet)
|
14
|
+
@packet = packet
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def post
|
19
|
+
raise Messenger.new(input.dup)
|
20
|
+
end
|
21
|
+
alias_method :get, :post
|
22
|
+
end
|
23
|
+
|
24
|
+
class Sniffer::Controllers::ServerError
|
25
|
+
def get(*a)
|
26
|
+
raise a.pop
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
class TestMockRequest < Test::Unit::TestCase
|
31
|
+
include Mosquito
|
32
|
+
|
33
|
+
def setup
|
34
|
+
@parsed_input = nil
|
35
|
+
@req = MockRequest.new
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_default_domain_and_port
|
39
|
+
assert_equal 'test.host', @req.to_hash['SERVER_NAME']
|
40
|
+
assert_equal 'test.host', @req.to_hash['HTTP_HOST']
|
41
|
+
end
|
42
|
+
|
43
|
+
def test_envars_translate_to_readers
|
44
|
+
%w( server_name path_info accept_encoding user_agent
|
45
|
+
script_name server_protocol cache_control accept_language
|
46
|
+
host remote_addr server_software keep_alive referer accept_charset
|
47
|
+
version request_uri server_port gateway_interface accept connection
|
48
|
+
request_method).map do | envar |
|
49
|
+
true_value = (@req[envar.upcase] || @req["HTTP_" + envar.upcase])
|
50
|
+
assert_not_nil true_value,
|
51
|
+
"The environment of the default request should provide #{envar.upcase} or HTTP_#{envar.upcase}"
|
52
|
+
assert_nothing_raised do
|
53
|
+
assert_equal true_value, @req.send(envar), "The request should understand the reader for #{envar}"
|
54
|
+
assert_equal true_value, @req.send(envar.upcase), "The request should understand the reader for #{envar.upcase} " +
|
55
|
+
"for backwards compatibility"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def test_accessor_for_query_string
|
61
|
+
assert @req.respond_to?(:query_string), "The request must have a reader for query string"
|
62
|
+
assert @req.respond_to?(:query_string=), "The request must have a writer for query string"
|
63
|
+
|
64
|
+
qs = "one=two&foo=bar"
|
65
|
+
assert_nothing_raised { @req.query_string = qs }
|
66
|
+
assert_equal "?one=two&foo=bar", @req.query_string,
|
67
|
+
"The request should return the query string segment with a question mark"
|
68
|
+
|
69
|
+
qs = "?one=two&foo=bar"
|
70
|
+
assert_equal "?one=two&foo=bar", @req.query_string,
|
71
|
+
"The request should return the query string segment with one and only one question mark"
|
72
|
+
|
73
|
+
assert_equal '/?one=two&foo=bar', @req['REQUEST_URI'],
|
74
|
+
"The assigned query string should be propargated to REQUEST_URI"
|
75
|
+
|
76
|
+
assert_equal 'one=two&foo=bar', @req['QUERY_STRING'],
|
77
|
+
"The assigned query string should be propargated to QUERY_STRING"
|
78
|
+
|
79
|
+
@req.query_string = ''
|
80
|
+
assert_equal '/', @req['REQUEST_URI'], "The query part should be removed from the REQUEST_URI along with the qmark"
|
81
|
+
end
|
82
|
+
|
83
|
+
def test_query_string_composition_from_params
|
84
|
+
composable_hash = {
|
85
|
+
:foo => "bar",
|
86
|
+
:user => {
|
87
|
+
:login => "boss",
|
88
|
+
:password => "secret",
|
89
|
+
:friend_ids => [1, 2, 3]
|
90
|
+
}
|
91
|
+
}
|
92
|
+
|
93
|
+
assert @req.respond_to?(:query_string_params=)
|
94
|
+
@req.query_string_params = composable_hash
|
95
|
+
|
96
|
+
ref = '?foo=bar&user[friend_ids]=1&user[friend_ids]=2&user[friend_ids]=3&user[login]=boss&user[password]=secret'
|
97
|
+
assert_equal normalize_qs(ref), normalize_qs(@req.query_string)
|
98
|
+
assert_equal "/"+ref, @req['REQUEST_URI'], "The query string parameters should be propagated to the " +
|
99
|
+
"request uri and be in their sorted form"
|
100
|
+
assert_equal normalize_qs(ref), normalize_qs(@req["QUERY_STRING"]), "The query string should also land in QUERY_STRING"
|
101
|
+
end
|
102
|
+
|
103
|
+
def test_qs_assignment_with_empty_hash_unsets_envar
|
104
|
+
@req.query_string_params = {}
|
105
|
+
assert_equal '', @req.query_string, "When an empty hash is assigned the query string should be empty"
|
106
|
+
assert_nil @req.to_hash['QUERY_STRING'], "The key for QUERY_STRING should be unset in the environment"
|
107
|
+
end
|
108
|
+
|
109
|
+
def test_multipart_boundary_generation
|
110
|
+
boundaries = (1..40).map do
|
111
|
+
@req.generate_boundary
|
112
|
+
end
|
113
|
+
assert_equal boundaries.uniq.length, boundaries.length, "All boundaries generated should be unique"
|
114
|
+
assert_equal boundaries, boundaries.grep(/^msqto\-/), "All boundaries should be prepended with msqto-"
|
115
|
+
end
|
116
|
+
|
117
|
+
def test_method_missing_is_indeed_missing
|
118
|
+
assert_raise(NoMethodError) { @req.kaboodle! }
|
119
|
+
end
|
120
|
+
|
121
|
+
def test_extract_values
|
122
|
+
t = {
|
123
|
+
:foo => "bar",
|
124
|
+
:bar => {
|
125
|
+
:baz => [1,2,3],
|
126
|
+
:bam => "trunk",
|
127
|
+
},
|
128
|
+
:sequence => "xyz"
|
129
|
+
}
|
130
|
+
assert_equal ["bar", "1", "2" , "3", "trunk", "xyz"].sort, @req.send(:extract_values, t).map(&:to_s).sort,
|
131
|
+
"should properly extract infinitely deeply nested values"
|
132
|
+
end
|
133
|
+
|
134
|
+
def test_post_composition_with_urlencoding
|
135
|
+
assert_equal 'GET', @req['REQUEST_METHOD'], "The default request method is GET"
|
136
|
+
|
137
|
+
@req.post_params = {:hello => "welæcome", :name => "john", :data => {:values => [1,2,3] } }
|
138
|
+
assert_kind_of StringIO, @req.body, "The request body is an IO"
|
139
|
+
assert_equal 'POST', @req['REQUEST_METHOD'],
|
140
|
+
"When the parameters are assigned the request should be switched to POST"
|
141
|
+
assert_equal "application/x-www-form-urlencoded", @req['CONTENT_TYPE'],
|
142
|
+
"The content-type should be switched accordingly"
|
143
|
+
assert_equal normalize_qs('data[values]=1&data[values]=2&data[values]=3&hello=wel%C3%A6come&name=john'), normalize_qs(@req.body.read),
|
144
|
+
"The body should now contain URL-encoded form parameters"
|
145
|
+
end
|
146
|
+
|
147
|
+
def test_post_composition_accepts_verbatim_strings_as_payload
|
148
|
+
assert_equal 'GET', @req['REQUEST_METHOD'], "The default request method is GET"
|
149
|
+
@req.post_params = 'foo=bar&baz=bad'
|
150
|
+
assert_equal 'POST', @req['REQUEST_METHOD']
|
151
|
+
assert_equal "application/x-www-form-urlencoded", @req['CONTENT_TYPE'],
|
152
|
+
"The content-type should be switched accordingly"
|
153
|
+
assert_equal 'foo=bar&baz=bad', @req.body.read, "The payload should have been assigned directly"
|
154
|
+
end
|
155
|
+
|
156
|
+
def test_append_to_query_string
|
157
|
+
@req.append_to_query_string "x=y&boo=2"
|
158
|
+
@req.append_to_query_string "zaz=taing&schmoo=tweed"
|
159
|
+
assert_equal "x=y&boo=2&zaz=taing&schmoo=tweed", @req["QUERY_STRING"]
|
160
|
+
end
|
161
|
+
|
162
|
+
# We could let that one slip but why? If someone does TDD he deserves gratification
|
163
|
+
def test_post_composition_requiring_multipart_with_arrays_warns_the_noble_developer_and_everyone_stays_happy
|
164
|
+
assert_raise(Mosquito::SageAdvice) do
|
165
|
+
@req.post_params = {:hello => "welcome", :name => "john", :arrayed => [1, 2, 3], :somefile => MockUpload.new("pic.jpg") }
|
166
|
+
end
|
167
|
+
|
168
|
+
assert_nothing_raised do
|
169
|
+
@req.post_params = {:hello => "welcome", :name => "john", :arrayed => [1, 2, 3], :not_a_file => "shtaink" }
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
# We could let that one slip but why? If someone does TDD he deserves gratification
|
174
|
+
def test_get_composition_with_files_warns_the_noble_developer_and_he_quickly_corrects_himself
|
175
|
+
assert_raise(Mosquito::SageAdvice) do
|
176
|
+
@req.query_string_params = {:hello => "welcome", :somefile => MockUpload.new("pic.jpg") }
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
def test_post_composition_from_values_requiring_multipart
|
181
|
+
assert_equal 'GET', @req['REQUEST_METHOD'], "The default request method is GET"
|
182
|
+
|
183
|
+
@req.post_params = {:hello => "welcome", :name => "john", :somefile => MockUpload.new("pic.jpg") }
|
184
|
+
|
185
|
+
assert_kind_of StringIO, @req.body, "The request body is an IO"
|
186
|
+
assert_equal 'POST', @req['REQUEST_METHOD'], "When the parameters are assigned the request should be switched to POST"
|
187
|
+
ctype, boundary = @req['CONTENT_TYPE'].split(/=/)
|
188
|
+
boundary_with_prefix = "--" + boundary
|
189
|
+
assert_equal "multipart/form-data; boundary", ctype, "The content-type should be switched accordingly"
|
190
|
+
|
191
|
+
@req.body.rewind
|
192
|
+
output = @req.body.read.split("\r")
|
193
|
+
ref_segments = [
|
194
|
+
"--#{boundary}",
|
195
|
+
"\nContent-Disposition: form-data; name=\"hello\"",
|
196
|
+
"\n", "\nwelcome", "\n--#{boundary}",
|
197
|
+
"\nContent-Disposition: form-data; name=\"name\"",
|
198
|
+
"\n",
|
199
|
+
"\njohn",
|
200
|
+
"\n--#{boundary}",
|
201
|
+
"\nContent-Disposition: form-data; name=\"somefile\"; filename=\"pic.jpg\"",
|
202
|
+
"\nContent-Type: image/jpeg", "\nContent-Length: 120",
|
203
|
+
"\n",
|
204
|
+
/\nStub file pic\.jpg \n([AZ-az]{120})\n/,
|
205
|
+
"\n--#{boundary}--"
|
206
|
+
]
|
207
|
+
|
208
|
+
ref_segments.each_with_index do | ref, idx |
|
209
|
+
if ref == String
|
210
|
+
assert_equal ref, output[idx], "The segment #{idx} should be #{ref}"
|
211
|
+
elsif ref == Regexp
|
212
|
+
assert_match ref, output[idx], "The segment #{idx} should match #{ref}"
|
213
|
+
end
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
private
|
218
|
+
# Remove the question mark, sort the pairs
|
219
|
+
def normalize_qs(query_string)
|
220
|
+
"?" + query_string.to_s.gsub(/^\?/, '').split(/&/).sort.join('&')
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
|
225
|
+
class TestMockRequestWithRoundtrip < Test::Unit::TestCase
|
226
|
+
include Mosquito
|
227
|
+
def setup
|
228
|
+
@parsed_input = nil
|
229
|
+
@req = MockRequest.new
|
230
|
+
end
|
231
|
+
|
232
|
+
def test_multipart_post_properly_roundtripped
|
233
|
+
@req.post_params = {:hello => "welcome", :name => "john", :somefile => MockUpload.new("pic.jpg") }
|
234
|
+
|
235
|
+
run_request!
|
236
|
+
|
237
|
+
# Reference is something like this
|
238
|
+
# {"name"=>"john", "somefile"=>{"name"=>"somefile", "type"=>"image/jpeg",
|
239
|
+
# "tempfile"=>#<File:/tmp/C.9366.0>, "filename"=>"pic.jpg"}, "hello"=>"welcome"}
|
240
|
+
|
241
|
+
assert_equal "john", @parsed_input["name"]
|
242
|
+
assert_kind_of Hash, @parsed_input["somefile"]
|
243
|
+
assert_equal "somefile", @parsed_input["somefile"]["name"]
|
244
|
+
assert_equal "pic.jpg", @parsed_input["somefile"]["filename"]
|
245
|
+
assert_equal "image/jpeg", @parsed_input["somefile"]["type"]
|
246
|
+
assert_kind_of Tempfile, @parsed_input["somefile"]["tempfile"]
|
247
|
+
|
248
|
+
|
249
|
+
@req.post_params = {:hello => "welcome", :name => "john", :arrayed => [1, 2, 3], :somefile => "instead" }
|
250
|
+
assert_kind_of StringIO, @req.body, "The request body is an IO"
|
251
|
+
assert_match /urlencoded/, @req['CONTENT_TYPE'], "The ctype should have been switched back to urlencoded"
|
252
|
+
|
253
|
+
run_request!
|
254
|
+
|
255
|
+
ref = {"name"=>"john", "arrayed"=>["1", "2", "3"], "somefile"=>"instead", "hello"=>"welcome"}
|
256
|
+
assert_equal ref, @parsed_input, "Camping should have parsed our params just like so"
|
257
|
+
end
|
258
|
+
|
259
|
+
def test_reader_and_writer_for_domain
|
260
|
+
assert_nothing_raised { assert_equal 'test.host', @req.domain }
|
261
|
+
assert_nothing_raised { @req.domain = "foo.dzing" }
|
262
|
+
|
263
|
+
assert_equal 'foo.dzing', @req.domain
|
264
|
+
assert_equal 'foo.dzing', @req['SERVER_NAME']
|
265
|
+
assert_equal 'foo.dzing', @req['HTTP_HOST']
|
266
|
+
end
|
267
|
+
|
268
|
+
def test_query_string_properly_roundtripped
|
269
|
+
parsed = {"user"=> {"friend_äidéis"=>["1", "2", "3"], "paßwort"=>"secret", "login"=>"boss"}, "foo"=>"bar"}
|
270
|
+
@req.query_string_params = parsed
|
271
|
+
run_request!
|
272
|
+
assert_equal parsed, @parsed_input
|
273
|
+
end
|
274
|
+
|
275
|
+
def test_urlencoded_post_properly_roundtripped
|
276
|
+
assert_equal 'GET', @req['REQUEST_METHOD'], "The default request method is GET"
|
277
|
+
|
278
|
+
@req.post_params = {:hello => "welæcome", :name => "john", :data => {:values => [1,2,3] } }
|
279
|
+
run_request!
|
280
|
+
ref = {"name"=>"john", "hello"=>"welæcome", "data"=>{"values"=>["1", "2", "3"]}}
|
281
|
+
assert_equal ref, @parsed_input, "Camping should have parsed our input like so"
|
282
|
+
end
|
283
|
+
private
|
284
|
+
def run_request!
|
285
|
+
@req.body.rewind
|
286
|
+
begin
|
287
|
+
Sniffer.run(@req.body, @req.to_hash)
|
288
|
+
rescue Sniffer::Controllers::ParamPeeker::Messenger => e
|
289
|
+
@parsed_input = e.packet
|
290
|
+
end
|
291
|
+
end
|
292
|
+
end
|