spec_utils.rb 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524
  1. # -*- encoding: utf-8 -*-
  2. require 'rack/utils'
  3. require 'rack/mock'
  4. describe Rack::Utils do
  5. # A helper method which checks
  6. # if certain query parameters
  7. # are equal.
  8. def equal_query_to(query)
  9. parts = query.split('&')
  10. lambda{|other| (parts & other.split('&')) == parts }
  11. end
  12. def kcodeu
  13. one8 = RUBY_VERSION.to_f < 1.9
  14. default_kcode, $KCODE = $KCODE, 'U' if one8
  15. yield
  16. ensure
  17. $KCODE = default_kcode if one8
  18. end
  19. should "round trip binary data" do
  20. r = [218, 0].pack 'CC'
  21. if defined?(::Encoding)
  22. z = Rack::Utils.unescape(Rack::Utils.escape(r), Encoding::BINARY)
  23. else
  24. z = Rack::Utils.unescape(Rack::Utils.escape(r))
  25. end
  26. r.should.equal z
  27. end
  28. should "escape correctly" do
  29. Rack::Utils.escape("fo<o>bar").should.equal "fo%3Co%3Ebar"
  30. Rack::Utils.escape("a space").should.equal "a+space"
  31. Rack::Utils.escape("q1!2\"'w$5&7/z8)?\\").
  32. should.equal "q1%212%22%27w%245%267%2Fz8%29%3F%5C"
  33. end
  34. should "escape correctly for multibyte characters" do
  35. matz_name = "\xE3\x81\xBE\xE3\x81\xA4\xE3\x82\x82\xE3\x81\xA8".unpack("a*")[0] # Matsumoto
  36. matz_name.force_encoding("UTF-8") if matz_name.respond_to? :force_encoding
  37. Rack::Utils.escape(matz_name).should.equal '%E3%81%BE%E3%81%A4%E3%82%82%E3%81%A8'
  38. matz_name_sep = "\xE3\x81\xBE\xE3\x81\xA4 \xE3\x82\x82\xE3\x81\xA8".unpack("a*")[0] # Matsu moto
  39. matz_name_sep.force_encoding("UTF-8") if matz_name_sep.respond_to? :force_encoding
  40. Rack::Utils.escape(matz_name_sep).should.equal '%E3%81%BE%E3%81%A4+%E3%82%82%E3%81%A8'
  41. end
  42. if RUBY_VERSION[/^\d+\.\d+/] == '1.8'
  43. should "escape correctly for multibyte characters if $KCODE is set to 'U'" do
  44. kcodeu do
  45. matz_name = "\xE3\x81\xBE\xE3\x81\xA4\xE3\x82\x82\xE3\x81\xA8".unpack("a*")[0] # Matsumoto
  46. matz_name.force_encoding("UTF-8") if matz_name.respond_to? :force_encoding
  47. Rack::Utils.escape(matz_name).should.equal '%E3%81%BE%E3%81%A4%E3%82%82%E3%81%A8'
  48. matz_name_sep = "\xE3\x81\xBE\xE3\x81\xA4 \xE3\x82\x82\xE3\x81\xA8".unpack("a*")[0] # Matsu moto
  49. matz_name_sep.force_encoding("UTF-8") if matz_name_sep.respond_to? :force_encoding
  50. Rack::Utils.escape(matz_name_sep).should.equal '%E3%81%BE%E3%81%A4+%E3%82%82%E3%81%A8'
  51. end
  52. end
  53. should "unescape multibyte characters correctly if $KCODE is set to 'U'" do
  54. kcodeu do
  55. Rack::Utils.unescape('%E3%81%BE%E3%81%A4+%E3%82%82%E3%81%A8').should.equal(
  56. "\xE3\x81\xBE\xE3\x81\xA4 \xE3\x82\x82\xE3\x81\xA8".unpack("a*")[0])
  57. end
  58. end
  59. end
  60. should "escape objects that responds to to_s" do
  61. kcodeu do
  62. Rack::Utils.escape(:id).should.equal "id"
  63. end
  64. end
  65. if "".respond_to?(:encode)
  66. should "escape non-UTF8 strings" do
  67. Rack::Utils.escape("ø".encode("ISO-8859-1")).should.equal "%F8"
  68. end
  69. end
  70. should "not hang on escaping long strings that end in % (http://redmine.ruby-lang.org/issues/5149)" do
  71. lambda {
  72. timeout(1) do
  73. lambda {
  74. URI.decode_www_form_component "A string that causes catastrophic backtracking as it gets longer %"
  75. }.should.raise(ArgumentError)
  76. end
  77. }.should.not.raise(Timeout::Error)
  78. end
  79. should "escape path spaces with %20" do
  80. Rack::Utils.escape_path("foo bar").should.equal "foo%20bar"
  81. end
  82. should "unescape correctly" do
  83. Rack::Utils.unescape("fo%3Co%3Ebar").should.equal "fo<o>bar"
  84. Rack::Utils.unescape("a+space").should.equal "a space"
  85. Rack::Utils.unescape("a%20space").should.equal "a space"
  86. Rack::Utils.unescape("q1%212%22%27w%245%267%2Fz8%29%3F%5C").
  87. should.equal "q1!2\"'w$5&7/z8)?\\"
  88. end
  89. should "parse query strings correctly" do
  90. Rack::Utils.parse_query("foo=bar").
  91. should.equal "foo" => "bar"
  92. Rack::Utils.parse_query("foo=\"bar\"").
  93. should.equal "foo" => "\"bar\""
  94. Rack::Utils.parse_query("foo=bar&foo=quux").
  95. should.equal "foo" => ["bar", "quux"]
  96. Rack::Utils.parse_query("foo=1&bar=2").
  97. should.equal "foo" => "1", "bar" => "2"
  98. Rack::Utils.parse_query("my+weird+field=q1%212%22%27w%245%267%2Fz8%29%3F").
  99. should.equal "my weird field" => "q1!2\"'w$5&7/z8)?"
  100. Rack::Utils.parse_query("foo%3Dbaz=bar").should.equal "foo=baz" => "bar"
  101. end
  102. should "parse nested query strings correctly" do
  103. Rack::Utils.parse_nested_query("foo").
  104. should.equal "foo" => nil
  105. Rack::Utils.parse_nested_query("foo=").
  106. should.equal "foo" => ""
  107. Rack::Utils.parse_nested_query("foo=bar").
  108. should.equal "foo" => "bar"
  109. Rack::Utils.parse_nested_query("foo=\"bar\"").
  110. should.equal "foo" => "\"bar\""
  111. Rack::Utils.parse_nested_query("foo=bar&foo=quux").
  112. should.equal "foo" => "quux"
  113. Rack::Utils.parse_nested_query("foo&foo=").
  114. should.equal "foo" => ""
  115. Rack::Utils.parse_nested_query("foo=1&bar=2").
  116. should.equal "foo" => "1", "bar" => "2"
  117. Rack::Utils.parse_nested_query("&foo=1&&bar=2").
  118. should.equal "foo" => "1", "bar" => "2"
  119. Rack::Utils.parse_nested_query("foo&bar=").
  120. should.equal "foo" => nil, "bar" => ""
  121. Rack::Utils.parse_nested_query("foo=bar&baz=").
  122. should.equal "foo" => "bar", "baz" => ""
  123. Rack::Utils.parse_nested_query("my+weird+field=q1%212%22%27w%245%267%2Fz8%29%3F").
  124. should.equal "my weird field" => "q1!2\"'w$5&7/z8)?"
  125. Rack::Utils.parse_nested_query("a=b&pid%3D1234=1023").
  126. should.equal "pid=1234" => "1023", "a" => "b"
  127. Rack::Utils.parse_nested_query("foo[]").
  128. should.equal "foo" => [nil]
  129. Rack::Utils.parse_nested_query("foo[]=").
  130. should.equal "foo" => [""]
  131. Rack::Utils.parse_nested_query("foo[]=bar").
  132. should.equal "foo" => ["bar"]
  133. Rack::Utils.parse_nested_query("foo[]=1&foo[]=2").
  134. should.equal "foo" => ["1", "2"]
  135. Rack::Utils.parse_nested_query("foo=bar&baz[]=1&baz[]=2&baz[]=3").
  136. should.equal "foo" => "bar", "baz" => ["1", "2", "3"]
  137. Rack::Utils.parse_nested_query("foo[]=bar&baz[]=1&baz[]=2&baz[]=3").
  138. should.equal "foo" => ["bar"], "baz" => ["1", "2", "3"]
  139. Rack::Utils.parse_nested_query("x[y][z]=1").
  140. should.equal "x" => {"y" => {"z" => "1"}}
  141. Rack::Utils.parse_nested_query("x[y][z][]=1").
  142. should.equal "x" => {"y" => {"z" => ["1"]}}
  143. Rack::Utils.parse_nested_query("x[y][z]=1&x[y][z]=2").
  144. should.equal "x" => {"y" => {"z" => "2"}}
  145. Rack::Utils.parse_nested_query("x[y][z][]=1&x[y][z][]=2").
  146. should.equal "x" => {"y" => {"z" => ["1", "2"]}}
  147. Rack::Utils.parse_nested_query("x[y][][z]=1").
  148. should.equal "x" => {"y" => [{"z" => "1"}]}
  149. Rack::Utils.parse_nested_query("x[y][][z][]=1").
  150. should.equal "x" => {"y" => [{"z" => ["1"]}]}
  151. Rack::Utils.parse_nested_query("x[y][][z]=1&x[y][][w]=2").
  152. should.equal "x" => {"y" => [{"z" => "1", "w" => "2"}]}
  153. Rack::Utils.parse_nested_query("x[y][][v][w]=1").
  154. should.equal "x" => {"y" => [{"v" => {"w" => "1"}}]}
  155. Rack::Utils.parse_nested_query("x[y][][z]=1&x[y][][v][w]=2").
  156. should.equal "x" => {"y" => [{"z" => "1", "v" => {"w" => "2"}}]}
  157. Rack::Utils.parse_nested_query("x[y][][z]=1&x[y][][z]=2").
  158. should.equal "x" => {"y" => [{"z" => "1"}, {"z" => "2"}]}
  159. Rack::Utils.parse_nested_query("x[y][][z]=1&x[y][][w]=a&x[y][][z]=2&x[y][][w]=3").
  160. should.equal "x" => {"y" => [{"z" => "1", "w" => "a"}, {"z" => "2", "w" => "3"}]}
  161. lambda { Rack::Utils.parse_nested_query("x[y]=1&x[y]z=2") }.
  162. should.raise(TypeError).
  163. message.should.equal "expected Hash (got String) for param `y'"
  164. lambda { Rack::Utils.parse_nested_query("x[y]=1&x[]=1") }.
  165. should.raise(TypeError).
  166. message.should.match /expected Array \(got [^)]*\) for param `x'/
  167. lambda { Rack::Utils.parse_nested_query("x[y]=1&x[y][][w]=2") }.
  168. should.raise(TypeError).
  169. message.should.equal "expected Array (got String) for param `y'"
  170. end
  171. should "build query strings correctly" do
  172. Rack::Utils.build_query("foo" => "bar").should.be equal_query_to("foo=bar")
  173. Rack::Utils.build_query("foo" => ["bar", "quux"]).
  174. should.be equal_query_to("foo=bar&foo=quux")
  175. Rack::Utils.build_query("foo" => "1", "bar" => "2").
  176. should.be equal_query_to("foo=1&bar=2")
  177. Rack::Utils.build_query("my weird field" => "q1!2\"'w$5&7/z8)?").
  178. should.be equal_query_to("my+weird+field=q1%212%22%27w%245%267%2Fz8%29%3F")
  179. end
  180. should "build nested query strings correctly" do
  181. Rack::Utils.build_nested_query("foo" => nil).should.equal "foo"
  182. Rack::Utils.build_nested_query("foo" => "").should.equal "foo="
  183. Rack::Utils.build_nested_query("foo" => "bar").should.equal "foo=bar"
  184. Rack::Utils.build_nested_query("foo" => "1", "bar" => "2").
  185. should.be equal_query_to("foo=1&bar=2")
  186. Rack::Utils.build_nested_query("my weird field" => "q1!2\"'w$5&7/z8)?").
  187. should.be equal_query_to("my+weird+field=q1%212%22%27w%245%267%2Fz8%29%3F")
  188. Rack::Utils.build_nested_query("foo" => [nil]).
  189. should.equal "foo[]"
  190. Rack::Utils.build_nested_query("foo" => [""]).
  191. should.equal "foo[]="
  192. Rack::Utils.build_nested_query("foo" => ["bar"]).
  193. should.equal "foo[]=bar"
  194. # The ordering of the output query string is unpredictable with 1.8's
  195. # unordered hash. Test that build_nested_query performs the inverse
  196. # function of parse_nested_query.
  197. [{"foo" => nil, "bar" => ""},
  198. {"foo" => "bar", "baz" => ""},
  199. {"foo" => ["1", "2"]},
  200. {"foo" => "bar", "baz" => ["1", "2", "3"]},
  201. {"foo" => ["bar"], "baz" => ["1", "2", "3"]},
  202. {"foo" => ["1", "2"]},
  203. {"foo" => "bar", "baz" => ["1", "2", "3"]},
  204. {"x" => {"y" => {"z" => "1"}}},
  205. {"x" => {"y" => {"z" => ["1"]}}},
  206. {"x" => {"y" => {"z" => ["1", "2"]}}},
  207. {"x" => {"y" => [{"z" => "1"}]}},
  208. {"x" => {"y" => [{"z" => ["1"]}]}},
  209. {"x" => {"y" => [{"z" => "1", "w" => "2"}]}},
  210. {"x" => {"y" => [{"v" => {"w" => "1"}}]}},
  211. {"x" => {"y" => [{"z" => "1", "v" => {"w" => "2"}}]}},
  212. {"x" => {"y" => [{"z" => "1"}, {"z" => "2"}]}},
  213. {"x" => {"y" => [{"z" => "1", "w" => "a"}, {"z" => "2", "w" => "3"}]}}
  214. ].each { |params|
  215. qs = Rack::Utils.build_nested_query(params)
  216. Rack::Utils.parse_nested_query(qs).should.equal params
  217. }
  218. lambda { Rack::Utils.build_nested_query("foo=bar") }.
  219. should.raise(ArgumentError).
  220. message.should.equal "value must be a Hash"
  221. end
  222. should "parse query strings that have a non-existent value" do
  223. key = "post/2011/08/27/Deux-%22rat%C3%A9s%22-de-l-Universit"
  224. Rack::Utils.parse_query(key).should.equal Rack::Utils.unescape(key) => nil
  225. end
  226. should "build query strings without = with non-existent values" do
  227. key = "post/2011/08/27/Deux-%22rat%C3%A9s%22-de-l-Universit"
  228. key = Rack::Utils.unescape(key)
  229. Rack::Utils.build_query(key => nil).should.equal Rack::Utils.escape(key)
  230. end
  231. should "escape html entities [&><'\"/]" do
  232. Rack::Utils.escape_html("foo").should.equal "foo"
  233. Rack::Utils.escape_html("f&o").should.equal "f&amp;o"
  234. Rack::Utils.escape_html("f<o").should.equal "f&lt;o"
  235. Rack::Utils.escape_html("f>o").should.equal "f&gt;o"
  236. Rack::Utils.escape_html("f'o").should.equal "f&#x27;o"
  237. Rack::Utils.escape_html('f"o').should.equal "f&quot;o"
  238. Rack::Utils.escape_html("f/o").should.equal "f&#x2F;o"
  239. Rack::Utils.escape_html("<foo></foo>").should.equal "&lt;foo&gt;&lt;&#x2F;foo&gt;"
  240. end
  241. should "escape html entities even on MRI when it's bugged" do
  242. test_escape = lambda do
  243. kcodeu do
  244. Rack::Utils.escape_html("\300<").should.equal "\300&lt;"
  245. end
  246. end
  247. if RUBY_VERSION.to_f < 1.9
  248. test_escape.call
  249. else
  250. test_escape.should.raise(ArgumentError)
  251. end
  252. end
  253. if "".respond_to?(:encode)
  254. should "escape html entities in unicode strings" do
  255. # the following will cause warnings if the regex is poorly encoded:
  256. Rack::Utils.escape_html("☃").should.equal "☃"
  257. end
  258. end
  259. should "figure out which encodings are acceptable" do
  260. helper = lambda do |a, b|
  261. Rack::Request.new(Rack::MockRequest.env_for("", "HTTP_ACCEPT_ENCODING" => a))
  262. Rack::Utils.select_best_encoding(a, b)
  263. end
  264. helper.call(%w(), [["x", 1]]).should.equal(nil)
  265. helper.call(%w(identity), [["identity", 0.0]]).should.equal(nil)
  266. helper.call(%w(identity), [["*", 0.0]]).should.equal(nil)
  267. helper.call(%w(identity), [["compress", 1.0], ["gzip", 1.0]]).should.equal("identity")
  268. helper.call(%w(compress gzip identity), [["compress", 1.0], ["gzip", 1.0]]).should.equal("compress")
  269. helper.call(%w(compress gzip identity), [["compress", 0.5], ["gzip", 1.0]]).should.equal("gzip")
  270. helper.call(%w(foo bar identity), []).should.equal("identity")
  271. helper.call(%w(foo bar identity), [["*", 1.0]]).should.equal("foo")
  272. helper.call(%w(foo bar identity), [["*", 1.0], ["foo", 0.9]]).should.equal("bar")
  273. helper.call(%w(foo bar identity), [["foo", 0], ["bar", 0]]).should.equal("identity")
  274. helper.call(%w(foo bar baz identity), [["*", 0], ["identity", 0.1]]).should.equal("identity")
  275. end
  276. should "return the bytesize of String" do
  277. Rack::Utils.bytesize("FOO\xE2\x82\xAC").should.equal 6
  278. end
  279. should "return status code for integer" do
  280. Rack::Utils.status_code(200).should.equal 200
  281. end
  282. should "return status code for string" do
  283. Rack::Utils.status_code("200").should.equal 200
  284. end
  285. should "return status code for symbol" do
  286. Rack::Utils.status_code(:ok).should.equal 200
  287. end
  288. end
  289. describe Rack::Utils, "byte_range" do
  290. should "ignore missing or syntactically invalid byte ranges" do
  291. Rack::Utils.byte_ranges({},500).should.equal nil
  292. Rack::Utils.byte_ranges({"HTTP_RANGE" => "foobar"},500).should.equal nil
  293. Rack::Utils.byte_ranges({"HTTP_RANGE" => "furlongs=123-456"},500).should.equal nil
  294. Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes="},500).should.equal nil
  295. Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=-"},500).should.equal nil
  296. Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=123,456"},500).should.equal nil
  297. # A range of non-positive length is syntactically invalid and ignored:
  298. Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=456-123"},500).should.equal nil
  299. Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=456-455"},500).should.equal nil
  300. end
  301. should "parse simple byte ranges" do
  302. Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=123-456"},500).should.equal [(123..456)]
  303. Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=123-"},500).should.equal [(123..499)]
  304. Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=-100"},500).should.equal [(400..499)]
  305. Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=0-0"},500).should.equal [(0..0)]
  306. Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=499-499"},500).should.equal [(499..499)]
  307. end
  308. should "truncate byte ranges" do
  309. Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=123-999"},500).should.equal [(123..499)]
  310. Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=600-999"},500).should.equal []
  311. Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=-999"},500).should.equal [(0..499)]
  312. end
  313. should "ignore unsatisfiable byte ranges" do
  314. Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=500-501"},500).should.equal []
  315. Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=500-"},500).should.equal []
  316. Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=999-"},500).should.equal []
  317. Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=-0"},500).should.equal []
  318. end
  319. should "handle byte ranges of empty files" do
  320. Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=123-456"},0).should.equal []
  321. Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=0-"},0).should.equal []
  322. Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=-100"},0).should.equal []
  323. Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=0-0"},0).should.equal []
  324. Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=-0"},0).should.equal []
  325. end
  326. end
  327. describe Rack::Utils::HeaderHash do
  328. should "retain header case" do
  329. h = Rack::Utils::HeaderHash.new("Content-MD5" => "d5ff4e2a0 ...")
  330. h['ETag'] = 'Boo!'
  331. h.to_hash.should.equal "Content-MD5" => "d5ff4e2a0 ...", "ETag" => 'Boo!'
  332. end
  333. should "check existence of keys case insensitively" do
  334. h = Rack::Utils::HeaderHash.new("Content-MD5" => "d5ff4e2a0 ...")
  335. h.should.include 'content-md5'
  336. h.should.not.include 'ETag'
  337. end
  338. should "merge case-insensitively" do
  339. h = Rack::Utils::HeaderHash.new("ETag" => 'HELLO', "content-length" => '123')
  340. merged = h.merge("Etag" => 'WORLD', 'Content-Length' => '321', "Foo" => 'BAR')
  341. merged.should.equal "Etag"=>'WORLD', "Content-Length"=>'321', "Foo"=>'BAR'
  342. end
  343. should "overwrite case insensitively and assume the new key's case" do
  344. h = Rack::Utils::HeaderHash.new("Foo-Bar" => "baz")
  345. h["foo-bar"] = "bizzle"
  346. h["FOO-BAR"].should.equal "bizzle"
  347. h.length.should.equal 1
  348. h.to_hash.should.equal "foo-bar" => "bizzle"
  349. end
  350. should "be converted to real Hash" do
  351. h = Rack::Utils::HeaderHash.new("foo" => "bar")
  352. h.to_hash.should.be.instance_of Hash
  353. end
  354. should "convert Array values to Strings when converting to Hash" do
  355. h = Rack::Utils::HeaderHash.new("foo" => ["bar", "baz"])
  356. h.to_hash.should.equal({ "foo" => "bar\nbaz" })
  357. end
  358. should "replace hashes correctly" do
  359. h = Rack::Utils::HeaderHash.new("Foo-Bar" => "baz")
  360. j = {"foo" => "bar"}
  361. h.replace(j)
  362. h["foo"].should.equal "bar"
  363. end
  364. should "be able to delete the given key case-sensitively" do
  365. h = Rack::Utils::HeaderHash.new("foo" => "bar")
  366. h.delete("foo")
  367. h["foo"].should.be.nil
  368. h["FOO"].should.be.nil
  369. end
  370. should "be able to delete the given key case-insensitively" do
  371. h = Rack::Utils::HeaderHash.new("foo" => "bar")
  372. h.delete("FOO")
  373. h["foo"].should.be.nil
  374. h["FOO"].should.be.nil
  375. end
  376. should "return the deleted value when #delete is called on an existing key" do
  377. h = Rack::Utils::HeaderHash.new("foo" => "bar")
  378. h.delete("Foo").should.equal("bar")
  379. end
  380. should "return nil when #delete is called on a non-existant key" do
  381. h = Rack::Utils::HeaderHash.new("foo" => "bar")
  382. h.delete("Hello").should.be.nil
  383. end
  384. should "avoid unnecessary object creation if possible" do
  385. a = Rack::Utils::HeaderHash.new("foo" => "bar")
  386. b = Rack::Utils::HeaderHash.new(a)
  387. b.object_id.should.equal(a.object_id)
  388. b.should.equal(a)
  389. end
  390. should "convert Array values to Strings when responding to #each" do
  391. h = Rack::Utils::HeaderHash.new("foo" => ["bar", "baz"])
  392. h.each do |k,v|
  393. k.should.equal("foo")
  394. v.should.equal("bar\nbaz")
  395. end
  396. end
  397. should "not create headers out of thin air" do
  398. h = Rack::Utils::HeaderHash.new
  399. h['foo']
  400. h['foo'].should.be.nil
  401. h.should.not.include 'foo'
  402. end
  403. end
  404. describe Rack::Utils::Context do
  405. class ContextTest
  406. attr_reader :app
  407. def initialize app; @app=app; end
  408. def call env; context env; end
  409. def context env, app=@app; app.call(env); end
  410. end
  411. test_target1 = proc{|e| e.to_s+' world' }
  412. test_target2 = proc{|e| e.to_i+2 }
  413. test_target3 = proc{|e| nil }
  414. test_target4 = proc{|e| [200,{'Content-Type'=>'text/plain', 'Content-Length'=>'0'},['']] }
  415. test_app = ContextTest.new test_target4
  416. should "set context correctly" do
  417. test_app.app.should.equal test_target4
  418. c1 = Rack::Utils::Context.new(test_app, test_target1)
  419. c1.for.should.equal test_app
  420. c1.app.should.equal test_target1
  421. c2 = Rack::Utils::Context.new(test_app, test_target2)
  422. c2.for.should.equal test_app
  423. c2.app.should.equal test_target2
  424. end
  425. should "alter app on recontexting" do
  426. c1 = Rack::Utils::Context.new(test_app, test_target1)
  427. c2 = c1.recontext(test_target2)
  428. c2.for.should.equal test_app
  429. c2.app.should.equal test_target2
  430. c3 = c2.recontext(test_target3)
  431. c3.for.should.equal test_app
  432. c3.app.should.equal test_target3
  433. end
  434. should "run different apps" do
  435. c1 = Rack::Utils::Context.new test_app, test_target1
  436. c2 = c1.recontext test_target2
  437. c3 = c2.recontext test_target3
  438. c4 = c3.recontext test_target4
  439. a4 = Rack::Lint.new c4
  440. a5 = Rack::Lint.new test_app
  441. r1 = c1.call('hello')
  442. r1.should.equal 'hello world'
  443. r2 = c2.call(2)
  444. r2.should.equal 4
  445. r3 = c3.call(:misc_symbol)
  446. r3.should.be.nil
  447. r4 = Rack::MockRequest.new(a4).get('/')
  448. r4.status.should.equal 200
  449. r5 = Rack::MockRequest.new(a5).get('/')
  450. r5.status.should.equal 200
  451. r4.body.should.equal r5.body
  452. end
  453. end