PageRenderTime 53ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/test/routing_test.rb

https://gitlab.com/OBSERVER-DLL/sinatra
Ruby | 1175 lines | 983 code | 186 blank | 6 comment | 12 complexity | c6ecfe01814f965a6eed40923d540e74 MD5 | raw file
  1. # I like coding: UTF-8
  2. require File.expand_path('../helper', __FILE__)
  3. # Helper method for easy route pattern matching testing
  4. def route_def(pattern)
  5. mock_app { get(pattern) { } }
  6. end
  7. class RegexpLookAlike
  8. class MatchData
  9. def captures
  10. ["this", "is", "a", "test"]
  11. end
  12. end
  13. def match(string)
  14. ::RegexpLookAlike::MatchData.new if string == "/this/is/a/test/"
  15. end
  16. def keys
  17. ["one", "two", "three", "four"]
  18. end
  19. end
  20. class RoutingTest < Test::Unit::TestCase
  21. %w[get put post delete options patch].each do |verb|
  22. it "defines #{verb.upcase} request handlers with #{verb}" do
  23. mock_app {
  24. send verb, '/hello' do
  25. 'Hello World'
  26. end
  27. }
  28. request = Rack::MockRequest.new(@app)
  29. response = request.request(verb.upcase, '/hello', {})
  30. assert response.ok?
  31. assert_equal 'Hello World', response.body
  32. end
  33. end
  34. it "defines HEAD request handlers with HEAD" do
  35. mock_app {
  36. head '/hello' do
  37. response['X-Hello'] = 'World!'
  38. 'remove me'
  39. end
  40. }
  41. request = Rack::MockRequest.new(@app)
  42. response = request.request('HEAD', '/hello', {})
  43. assert response.ok?
  44. assert_equal 'World!', response['X-Hello']
  45. assert_equal '', response.body
  46. end
  47. it "404s when no route satisfies the request" do
  48. mock_app {
  49. get('/foo') { }
  50. }
  51. get '/bar'
  52. assert_equal 404, status
  53. end
  54. it "404s and sets X-Cascade header when no route satisfies the request" do
  55. mock_app {
  56. get('/foo') { }
  57. }
  58. get '/bar'
  59. assert_equal 404, status
  60. assert_equal 'pass', response.headers['X-Cascade']
  61. end
  62. it "allows using unicode" do
  63. mock_app do
  64. get('/föö') { }
  65. end
  66. get '/f%C3%B6%C3%B6'
  67. assert_equal 200, status
  68. end
  69. it "it handles encoded slashes correctly" do
  70. mock_app { get("/:a") { |a| a } }
  71. get '/foo%2Fbar'
  72. assert_equal 200, status
  73. assert_body "foo/bar"
  74. end
  75. it "overrides the content-type in error handlers" do
  76. mock_app {
  77. before { content_type 'text/plain' }
  78. error Sinatra::NotFound do
  79. content_type "text/html"
  80. "<h1>Not Found</h1>"
  81. end
  82. }
  83. get '/foo'
  84. assert_equal 404, status
  85. assert_equal 'text/html;charset=utf-8', response["Content-Type"]
  86. assert_equal "<h1>Not Found</h1>", response.body
  87. end
  88. it 'matches empty PATH_INFO to "/" if no route is defined for ""' do
  89. mock_app do
  90. get '/' do
  91. 'worked'
  92. end
  93. end
  94. get '/', {}, "PATH_INFO" => ""
  95. assert ok?
  96. assert_equal 'worked', body
  97. end
  98. it 'matches empty PATH_INFO to "" if a route is defined for ""' do
  99. mock_app do
  100. disable :protection
  101. get '/' do
  102. 'did not work'
  103. end
  104. get '' do
  105. 'worked'
  106. end
  107. end
  108. get '/', {}, "PATH_INFO" => ""
  109. assert ok?
  110. assert_equal 'worked', body
  111. end
  112. it 'takes multiple definitions of a route' do
  113. mock_app {
  114. user_agent(/Foo/)
  115. get '/foo' do
  116. 'foo'
  117. end
  118. get '/foo' do
  119. 'not foo'
  120. end
  121. }
  122. get '/foo', {}, 'HTTP_USER_AGENT' => 'Foo'
  123. assert ok?
  124. assert_equal 'foo', body
  125. get '/foo'
  126. assert ok?
  127. assert_equal 'not foo', body
  128. end
  129. it "exposes params with indifferent hash" do
  130. mock_app {
  131. get '/:foo' do
  132. assert_equal 'bar', params['foo']
  133. assert_equal 'bar', params[:foo]
  134. 'well, alright'
  135. end
  136. }
  137. get '/bar'
  138. assert_equal 'well, alright', body
  139. end
  140. it "merges named params and query string params in params" do
  141. mock_app {
  142. get '/:foo' do
  143. assert_equal 'bar', params['foo']
  144. assert_equal 'biz', params['baz']
  145. end
  146. }
  147. get '/bar?baz=biz'
  148. assert ok?
  149. end
  150. it "supports named params like /hello/:person" do
  151. mock_app {
  152. get '/hello/:person' do
  153. "Hello #{params['person']}"
  154. end
  155. }
  156. get '/hello/Frank'
  157. assert_equal 'Hello Frank', body
  158. end
  159. it "supports optional named params like /?:foo?/?:bar?" do
  160. mock_app {
  161. get '/?:foo?/?:bar?' do
  162. "foo=#{params[:foo]};bar=#{params[:bar]}"
  163. end
  164. }
  165. get '/hello/world'
  166. assert ok?
  167. assert_equal "foo=hello;bar=world", body
  168. get '/hello'
  169. assert ok?
  170. assert_equal "foo=hello;bar=", body
  171. get '/'
  172. assert ok?
  173. assert_equal "foo=;bar=", body
  174. end
  175. it "supports named captures like %r{/hello/(?<person>[^/?#]+)} on Ruby >= 1.9" do
  176. next if RUBY_VERSION < '1.9'
  177. mock_app {
  178. get Regexp.new('/hello/(?<person>[^/?#]+)') do
  179. "Hello #{params['person']}"
  180. end
  181. }
  182. get '/hello/Frank'
  183. assert_equal 'Hello Frank', body
  184. end
  185. it "supports optional named captures like %r{/page(?<format>.[^/?#]+)?} on Ruby >= 1.9" do
  186. next if RUBY_VERSION < '1.9'
  187. mock_app {
  188. get Regexp.new('/page(?<format>.[^/?#]+)?') do
  189. "format=#{params[:format]}"
  190. end
  191. }
  192. get '/page.html'
  193. assert ok?
  194. assert_equal "format=.html", body
  195. get '/page.xml'
  196. assert ok?
  197. assert_equal "format=.xml", body
  198. get '/page'
  199. assert ok?
  200. assert_equal "format=", body
  201. end
  202. it 'does not concatinate params with the same name' do
  203. mock_app { get('/:foo') { params[:foo] } }
  204. get '/a?foo=b'
  205. assert_body 'a'
  206. end
  207. it "supports single splat params like /*" do
  208. mock_app {
  209. get '/*' do
  210. assert params['splat'].kind_of?(Array)
  211. params['splat'].join "\n"
  212. end
  213. }
  214. get '/foo'
  215. assert_equal "foo", body
  216. get '/foo/bar/baz'
  217. assert_equal "foo/bar/baz", body
  218. end
  219. it "supports mixing multiple splat params like /*/foo/*/*" do
  220. mock_app {
  221. get '/*/foo/*/*' do
  222. assert params['splat'].kind_of?(Array)
  223. params['splat'].join "\n"
  224. end
  225. }
  226. get '/bar/foo/bling/baz/boom'
  227. assert_equal "bar\nbling\nbaz/boom", body
  228. get '/bar/foo/baz'
  229. assert not_found?
  230. end
  231. it "supports mixing named and splat params like /:foo/*" do
  232. mock_app {
  233. get '/:foo/*' do
  234. assert_equal 'foo', params['foo']
  235. assert_equal ['bar/baz'], params['splat']
  236. end
  237. }
  238. get '/foo/bar/baz'
  239. assert ok?
  240. end
  241. it "matches a dot ('.') as part of a named param" do
  242. mock_app {
  243. get '/:foo/:bar' do
  244. params[:foo]
  245. end
  246. }
  247. get '/user@example.com/name'
  248. assert_equal 200, response.status
  249. assert_equal 'user@example.com', body
  250. end
  251. it "matches a literal dot ('.') outside of named params" do
  252. mock_app {
  253. get '/:file.:ext' do
  254. assert_equal 'pony', params[:file]
  255. assert_equal 'jpg', params[:ext]
  256. 'right on'
  257. end
  258. }
  259. get '/pony.jpg'
  260. assert_equal 200, response.status
  261. assert_equal 'right on', body
  262. end
  263. it "literally matches dot in paths" do
  264. route_def '/test.bar'
  265. get '/test.bar'
  266. assert ok?
  267. get 'test0bar'
  268. assert not_found?
  269. end
  270. it "literally matches dollar sign in paths" do
  271. route_def '/test$/'
  272. get '/test$/'
  273. assert ok?
  274. end
  275. it "literally matches plus sign in paths" do
  276. route_def '/te+st/'
  277. get '/te%2Bst/'
  278. assert ok?
  279. get '/teeeeeeest/'
  280. assert not_found?
  281. end
  282. it "converts plus sign into space as the value of a named param" do
  283. mock_app {
  284. get '/:test' do
  285. params["test"]
  286. end
  287. }
  288. get '/bob+ross'
  289. assert ok?
  290. assert_equal 'bob ross', body
  291. end
  292. it "literally matches parens in paths" do
  293. route_def '/test(bar)/'
  294. get '/test(bar)/'
  295. assert ok?
  296. end
  297. it "supports basic nested params" do
  298. mock_app {
  299. get '/hi' do
  300. params["person"]["name"]
  301. end
  302. }
  303. get "/hi?person[name]=John+Doe"
  304. assert ok?
  305. assert_equal "John Doe", body
  306. end
  307. it "exposes nested params with indifferent hash" do
  308. mock_app {
  309. get '/testme' do
  310. assert_equal 'baz', params['bar']['foo']
  311. assert_equal 'baz', params['bar'][:foo]
  312. 'well, alright'
  313. end
  314. }
  315. get '/testme?bar[foo]=baz'
  316. assert_equal 'well, alright', body
  317. end
  318. it "exposes params nested within arrays with indifferent hash" do
  319. mock_app {
  320. get '/testme' do
  321. assert_equal 'baz', params['bar'][0]['foo']
  322. assert_equal 'baz', params['bar'][0][:foo]
  323. 'well, alright'
  324. end
  325. }
  326. get '/testme?bar[][foo]=baz'
  327. assert_equal 'well, alright', body
  328. end
  329. it "supports arrays within params" do
  330. mock_app {
  331. get '/foo' do
  332. assert_equal ['A', 'B'], params['bar']
  333. 'looks good'
  334. end
  335. }
  336. get '/foo?bar[]=A&bar[]=B'
  337. assert ok?
  338. assert_equal 'looks good', body
  339. end
  340. it "supports deeply nested params" do
  341. expected_params = {
  342. "emacs" => {
  343. "map" => { "goto-line" => "M-g g" },
  344. "version" => "22.3.1"
  345. },
  346. "browser" => {
  347. "firefox" => {"engine" => {"name"=>"spidermonkey", "version"=>"1.7.0"}},
  348. "chrome" => {"engine" => {"name"=>"V8", "version"=>"1.0"}}
  349. },
  350. "paste" => {"name"=>"hello world", "syntax"=>"ruby"}
  351. }
  352. mock_app {
  353. get '/foo' do
  354. assert_equal expected_params, params
  355. 'looks good'
  356. end
  357. }
  358. get '/foo', expected_params
  359. assert ok?
  360. assert_equal 'looks good', body
  361. end
  362. it "preserves non-nested params" do
  363. mock_app {
  364. get '/foo' do
  365. assert_equal "2", params["article_id"]
  366. assert_equal "awesome", params['comment']['body']
  367. assert_nil params['comment[body]']
  368. 'looks good'
  369. end
  370. }
  371. get '/foo?article_id=2&comment[body]=awesome'
  372. assert ok?
  373. assert_equal 'looks good', body
  374. end
  375. it "matches paths that include spaces encoded with %20" do
  376. mock_app {
  377. get '/path with spaces' do
  378. 'looks good'
  379. end
  380. }
  381. get '/path%20with%20spaces'
  382. assert ok?
  383. assert_equal 'looks good', body
  384. end
  385. it "matches paths that include spaces encoded with +" do
  386. mock_app {
  387. get '/path with spaces' do
  388. 'looks good'
  389. end
  390. }
  391. get '/path+with+spaces'
  392. assert ok?
  393. assert_equal 'looks good', body
  394. end
  395. it "matches paths that include ampersands" do
  396. mock_app {
  397. get '/:name' do
  398. 'looks good'
  399. end
  400. }
  401. get '/foo&bar'
  402. assert ok?
  403. assert_equal 'looks good', body
  404. end
  405. it "URL decodes named parameters and splats" do
  406. mock_app {
  407. get '/:foo/*' do
  408. assert_equal 'hello world', params['foo']
  409. assert_equal ['how are you'], params['splat']
  410. nil
  411. end
  412. }
  413. get '/hello%20world/how%20are%20you'
  414. assert ok?
  415. end
  416. it 'supports regular expressions' do
  417. mock_app {
  418. get(/^\/foo...\/bar$/) do
  419. 'Hello World'
  420. end
  421. }
  422. get '/foooom/bar'
  423. assert ok?
  424. assert_equal 'Hello World', body
  425. end
  426. it 'makes regular expression captures available in params[:captures]' do
  427. mock_app {
  428. get(/^\/fo(.*)\/ba(.*)/) do
  429. assert_equal ['orooomma', 'f'], params[:captures]
  430. 'right on'
  431. end
  432. }
  433. get '/foorooomma/baf'
  434. assert ok?
  435. assert_equal 'right on', body
  436. end
  437. it 'supports regular expression look-alike routes' do
  438. mock_app {
  439. get(RegexpLookAlike.new) do
  440. assert_equal 'this', params[:one]
  441. assert_equal 'is', params[:two]
  442. assert_equal 'a', params[:three]
  443. assert_equal 'test', params[:four]
  444. 'right on'
  445. end
  446. }
  447. get '/this/is/a/test/'
  448. assert ok?
  449. assert_equal 'right on', body
  450. end
  451. it 'raises a TypeError when pattern is not a String or Regexp' do
  452. assert_raise(TypeError) {
  453. mock_app { get(42){} }
  454. }
  455. end
  456. it "returns response immediately on halt" do
  457. mock_app {
  458. get '/' do
  459. halt 'Hello World'
  460. 'Boo-hoo World'
  461. end
  462. }
  463. get '/'
  464. assert ok?
  465. assert_equal 'Hello World', body
  466. end
  467. it "halts with a response tuple" do
  468. mock_app {
  469. get '/' do
  470. halt 295, {'Content-Type' => 'text/plain'}, 'Hello World'
  471. end
  472. }
  473. get '/'
  474. assert_equal 295, status
  475. assert_equal 'text/plain', response['Content-Type']
  476. assert_equal 'Hello World', body
  477. end
  478. it "halts with an array of strings" do
  479. mock_app {
  480. get '/' do
  481. halt %w[Hello World How Are You]
  482. end
  483. }
  484. get '/'
  485. assert_equal 'HelloWorldHowAreYou', body
  486. end
  487. it 'sets response.status with halt' do
  488. status_was = nil
  489. mock_app do
  490. after { status_was = status }
  491. get('/') { halt 500, 'error' }
  492. end
  493. get '/'
  494. assert_status 500
  495. assert_equal 500, status_was
  496. end
  497. it "transitions to the next matching route on pass" do
  498. mock_app {
  499. get '/:foo' do
  500. pass
  501. 'Hello Foo'
  502. end
  503. get '/*' do
  504. assert !params.include?('foo')
  505. 'Hello World'
  506. end
  507. }
  508. get '/bar'
  509. assert ok?
  510. assert_equal 'Hello World', body
  511. end
  512. it "transitions to 404 when passed and no subsequent route matches" do
  513. mock_app {
  514. get '/:foo' do
  515. pass
  516. 'Hello Foo'
  517. end
  518. }
  519. get '/bar'
  520. assert not_found?
  521. end
  522. it "transitions to 404 and sets X-Cascade header when passed and no subsequent route matches" do
  523. mock_app {
  524. get '/:foo' do
  525. pass
  526. 'Hello Foo'
  527. end
  528. get '/bar' do
  529. 'Hello Bar'
  530. end
  531. }
  532. get '/foo'
  533. assert not_found?
  534. assert_equal 'pass', response.headers['X-Cascade']
  535. end
  536. it "uses optional block passed to pass as route block if no other route is found" do
  537. mock_app {
  538. get "/" do
  539. pass do
  540. "this"
  541. end
  542. "not this"
  543. end
  544. }
  545. get "/"
  546. assert ok?
  547. assert "this", body
  548. end
  549. it "passes when matching condition returns false" do
  550. mock_app {
  551. condition { params[:foo] == 'bar' }
  552. get '/:foo' do
  553. 'Hello World'
  554. end
  555. }
  556. get '/bar'
  557. assert ok?
  558. assert_equal 'Hello World', body
  559. get '/foo'
  560. assert not_found?
  561. end
  562. it "does not pass when matching condition returns nil" do
  563. mock_app {
  564. condition { nil }
  565. get '/:foo' do
  566. 'Hello World'
  567. end
  568. }
  569. get '/bar'
  570. assert ok?
  571. assert_equal 'Hello World', body
  572. end
  573. it "passes to next route when condition calls pass explicitly" do
  574. mock_app {
  575. condition { pass unless params[:foo] == 'bar' }
  576. get '/:foo' do
  577. 'Hello World'
  578. end
  579. }
  580. get '/bar'
  581. assert ok?
  582. assert_equal 'Hello World', body
  583. get '/foo'
  584. assert not_found?
  585. end
  586. it "passes to the next route when host_name does not match" do
  587. mock_app {
  588. host_name 'example.com'
  589. get '/foo' do
  590. 'Hello World'
  591. end
  592. }
  593. get '/foo'
  594. assert not_found?
  595. get '/foo', {}, { 'HTTP_HOST' => 'example.com' }
  596. assert_equal 200, status
  597. assert_equal 'Hello World', body
  598. end
  599. it "passes to the next route when user_agent does not match" do
  600. mock_app {
  601. user_agent(/Foo/)
  602. get '/foo' do
  603. 'Hello World'
  604. end
  605. }
  606. get '/foo'
  607. assert not_found?
  608. get '/foo', {}, { 'HTTP_USER_AGENT' => 'Foo Bar' }
  609. assert_equal 200, status
  610. assert_equal 'Hello World', body
  611. end
  612. it "treats missing user agent like an empty string" do
  613. mock_app do
  614. user_agent(/.*/)
  615. get '/' do
  616. "Hello World"
  617. end
  618. end
  619. get '/'
  620. assert_equal 200, status
  621. assert_equal 'Hello World', body
  622. end
  623. it "makes captures in user agent pattern available in params[:agent]" do
  624. mock_app {
  625. user_agent(/Foo (.*)/)
  626. get '/foo' do
  627. 'Hello ' + params[:agent].first
  628. end
  629. }
  630. get '/foo', {}, { 'HTTP_USER_AGENT' => 'Foo Bar' }
  631. assert_equal 200, status
  632. assert_equal 'Hello Bar', body
  633. end
  634. it "filters by accept header" do
  635. mock_app {
  636. get '/', :provides => :xml do
  637. env['HTTP_ACCEPT']
  638. end
  639. get '/foo', :provides => :html do
  640. env['HTTP_ACCEPT']
  641. end
  642. }
  643. get '/', {}, { 'HTTP_ACCEPT' => 'application/xml' }
  644. assert ok?
  645. assert_equal 'application/xml', body
  646. assert_equal 'application/xml;charset=utf-8', response.headers['Content-Type']
  647. get '/', {}, { :accept => 'text/html' }
  648. assert !ok?
  649. get '/foo', {}, { 'HTTP_ACCEPT' => 'text/html;q=0.9' }
  650. assert ok?
  651. assert_equal 'text/html;q=0.9', body
  652. get '/foo', {}, { 'HTTP_ACCEPT' => '' }
  653. assert !ok?
  654. end
  655. it "filters by current Content-Type" do
  656. mock_app do
  657. before('/txt') { content_type :txt }
  658. get('*', :provides => :txt) { 'txt' }
  659. before('/html') { content_type :html }
  660. get('*', :provides => :html) { 'html' }
  661. end
  662. get '/', {}, { 'HTTP_ACCEPT' => '*' }
  663. assert ok?
  664. assert_equal 'text/plain;charset=utf-8', response.headers['Content-Type']
  665. assert_body 'txt'
  666. get '/txt', {}, { 'HTTP_ACCEPT' => 'text/plain' }
  667. assert ok?
  668. assert_equal 'text/plain;charset=utf-8', response.headers['Content-Type']
  669. assert_body 'txt'
  670. get '/', {}, { 'HTTP_ACCEPT' => 'text/html' }
  671. assert ok?
  672. assert_equal 'text/html;charset=utf-8', response.headers['Content-Type']
  673. assert_body 'html'
  674. end
  675. it "allows multiple mime types for accept header" do
  676. types = ['image/jpeg', 'image/pjpeg']
  677. mock_app {
  678. get '/', :provides => types do
  679. env['HTTP_ACCEPT']
  680. end
  681. }
  682. types.each do |type|
  683. get '/', {}, { 'HTTP_ACCEPT' => type }
  684. assert ok?
  685. assert_equal type, body
  686. assert_equal type, response.headers['Content-Type']
  687. end
  688. end
  689. it 'degrades gracefully when optional accept header is not provided' do
  690. mock_app {
  691. get '/', :provides => :xml do
  692. env['HTTP_ACCEPT']
  693. end
  694. get '/' do
  695. 'default'
  696. end
  697. }
  698. get '/'
  699. assert ok?
  700. assert_equal 'default', body
  701. end
  702. it 'respects user agent prefferences for the content type' do
  703. mock_app { get('/', :provides => [:png, :html]) { content_type }}
  704. get '/', {}, { 'HTTP_ACCEPT' => 'image/png;q=0.5,text/html;q=0.8' }
  705. assert_body 'text/html;charset=utf-8'
  706. get '/', {}, { 'HTTP_ACCEPT' => 'image/png;q=0.8,text/html;q=0.5' }
  707. assert_body 'image/png'
  708. end
  709. it 'accepts generic types' do
  710. mock_app do
  711. get('/', :provides => :xml) { content_type }
  712. get('/') { 'no match' }
  713. end
  714. get '/'
  715. assert_body 'no match'
  716. get '/', {}, { 'HTTP_ACCEPT' => 'foo/*' }
  717. assert_body 'no match'
  718. get '/', {}, { 'HTTP_ACCEPT' => 'application/*' }
  719. assert_body 'application/xml;charset=utf-8'
  720. get '/', {}, { 'HTTP_ACCEPT' => '*/*' }
  721. assert_body 'application/xml;charset=utf-8'
  722. end
  723. it 'prefers concrete over partly generic types' do
  724. mock_app { get('/', :provides => [:png, :html]) { content_type }}
  725. get '/', {}, { 'HTTP_ACCEPT' => 'image/*, text/html' }
  726. assert_body 'text/html;charset=utf-8'
  727. get '/', {}, { 'HTTP_ACCEPT' => 'image/png, text/*' }
  728. assert_body 'image/png'
  729. end
  730. it 'prefers concrete over fully generic types' do
  731. mock_app { get('/', :provides => [:png, :html]) { content_type }}
  732. get '/', {}, { 'HTTP_ACCEPT' => '*/*, text/html' }
  733. assert_body 'text/html;charset=utf-8'
  734. get '/', {}, { 'HTTP_ACCEPT' => 'image/png, */*' }
  735. assert_body 'image/png'
  736. end
  737. it 'prefers partly generic over fully generic types' do
  738. mock_app { get('/', :provides => [:png, :html]) { content_type }}
  739. get '/', {}, { 'HTTP_ACCEPT' => '*/*, text/*' }
  740. assert_body 'text/html;charset=utf-8'
  741. get '/', {}, { 'HTTP_ACCEPT' => 'image/*, */*' }
  742. assert_body 'image/png'
  743. end
  744. it 'respects quality with generic types' do
  745. mock_app { get('/', :provides => [:png, :html]) { content_type }}
  746. get '/', {}, { 'HTTP_ACCEPT' => 'image/*;q=1, text/html;q=0' }
  747. assert_body 'image/png'
  748. get '/', {}, { 'HTTP_ACCEPT' => 'image/png;q=0.5, text/*;q=0.7' }
  749. assert_body 'text/html;charset=utf-8'
  750. end
  751. it 'accepts both text/javascript and application/javascript for js' do
  752. mock_app { get('/', :provides => :js) { content_type }}
  753. get '/', {}, { 'HTTP_ACCEPT' => 'application/javascript' }
  754. assert_body 'application/javascript;charset=utf-8'
  755. get '/', {}, { 'HTTP_ACCEPT' => 'text/javascript' }
  756. assert_body 'text/javascript;charset=utf-8'
  757. end
  758. it 'accepts both text/xml and application/xml for xml' do
  759. mock_app { get('/', :provides => :xml) { content_type }}
  760. get '/', {}, { 'HTTP_ACCEPT' => 'application/xml' }
  761. assert_body 'application/xml;charset=utf-8'
  762. get '/', {}, { 'HTTP_ACCEPT' => 'text/xml' }
  763. assert_body 'text/xml;charset=utf-8'
  764. end
  765. it 'passes a single url param as block parameters when one param is specified' do
  766. mock_app {
  767. get '/:foo' do |foo|
  768. assert_equal 'bar', foo
  769. end
  770. }
  771. get '/bar'
  772. assert ok?
  773. end
  774. it 'passes multiple params as block parameters when many are specified' do
  775. mock_app {
  776. get '/:foo/:bar/:baz' do |foo, bar, baz|
  777. assert_equal 'abc', foo
  778. assert_equal 'def', bar
  779. assert_equal 'ghi', baz
  780. end
  781. }
  782. get '/abc/def/ghi'
  783. assert ok?
  784. end
  785. it 'passes regular expression captures as block parameters' do
  786. mock_app {
  787. get(/^\/fo(.*)\/ba(.*)/) do |foo, bar|
  788. assert_equal 'orooomma', foo
  789. assert_equal 'f', bar
  790. 'looks good'
  791. end
  792. }
  793. get '/foorooomma/baf'
  794. assert ok?
  795. assert_equal 'looks good', body
  796. end
  797. it "supports mixing multiple splat params like /*/foo/*/* as block parameters" do
  798. mock_app {
  799. get '/*/foo/*/*' do |foo, bar, baz|
  800. assert_equal 'bar', foo
  801. assert_equal 'bling', bar
  802. assert_equal 'baz/boom', baz
  803. 'looks good'
  804. end
  805. }
  806. get '/bar/foo/bling/baz/boom'
  807. assert ok?
  808. assert_equal 'looks good', body
  809. end
  810. it 'raises an ArgumentError with block arity > 1 and too many values' do
  811. mock_app do
  812. get '/:foo/:bar/:baz' do |foo, bar|
  813. 'quux'
  814. end
  815. end
  816. assert_raise(ArgumentError) { get '/a/b/c' }
  817. end
  818. it 'raises an ArgumentError with block param arity > 1 and too few values' do
  819. mock_app {
  820. get '/:foo/:bar' do |foo, bar, baz|
  821. 'quux'
  822. end
  823. }
  824. assert_raise(ArgumentError) { get '/a/b' }
  825. end
  826. it 'succeeds if no block parameters are specified' do
  827. mock_app {
  828. get '/:foo/:bar' do
  829. 'quux'
  830. end
  831. }
  832. get '/a/b'
  833. assert ok?
  834. assert_equal 'quux', body
  835. end
  836. it 'passes all params with block param arity -1 (splat args)' do
  837. mock_app {
  838. get '/:foo/:bar' do |*args|
  839. args.join
  840. end
  841. }
  842. get '/a/b'
  843. assert ok?
  844. assert_equal 'ab', body
  845. end
  846. it 'allows custom route-conditions to be set via route options' do
  847. protector = Module.new {
  848. def protect(*args)
  849. condition {
  850. unless authorize(params["user"], params["password"])
  851. halt 403, "go away"
  852. end
  853. }
  854. end
  855. }
  856. mock_app {
  857. register protector
  858. helpers do
  859. def authorize(username, password)
  860. username == "foo" && password == "bar"
  861. end
  862. end
  863. get "/", :protect => true do
  864. "hey"
  865. end
  866. }
  867. get "/"
  868. assert forbidden?
  869. assert_equal "go away", body
  870. get "/", :user => "foo", :password => "bar"
  871. assert ok?
  872. assert_equal "hey", body
  873. end
  874. # NOTE Block params behaves differently under 1.8 and 1.9. Under 1.8, block
  875. # param arity is lax: declaring a mismatched number of block params results
  876. # in a warning. Under 1.9, block param arity is strict: mismatched block
  877. # arity raises an ArgumentError.
  878. if RUBY_VERSION >= '1.9'
  879. it 'raises an ArgumentError with block param arity 1 and no values' do
  880. mock_app {
  881. get '/foo' do |foo|
  882. 'quux'
  883. end
  884. }
  885. assert_raise(ArgumentError) { get '/foo' }
  886. end
  887. it 'raises an ArgumentError with block param arity 1 and too many values' do
  888. mock_app {
  889. get '/:foo/:bar/:baz' do |foo|
  890. 'quux'
  891. end
  892. }
  893. assert_raise(ArgumentError) { get '/a/b/c' }
  894. end
  895. else
  896. it 'does not raise an ArgumentError with block param arity 1 and no values' do
  897. mock_app {
  898. get '/foo' do |foo|
  899. 'quux'
  900. end
  901. }
  902. silence_warnings { get '/foo' }
  903. assert ok?
  904. assert_equal 'quux', body
  905. end
  906. it 'does not raise an ArgumentError with block param arity 1 and too many values' do
  907. mock_app {
  908. get '/:foo/:bar/:baz' do |foo|
  909. 'quux'
  910. end
  911. }
  912. silence_warnings { get '/a/b/c' }
  913. assert ok?
  914. assert_equal 'quux', body
  915. end
  916. end
  917. it "matches routes defined in superclasses" do
  918. base = Class.new(Sinatra::Base)
  919. base.get('/foo') { 'foo in baseclass' }
  920. mock_app(base) {
  921. get('/bar') { 'bar in subclass' }
  922. }
  923. get '/foo'
  924. assert ok?
  925. assert_equal 'foo in baseclass', body
  926. get '/bar'
  927. assert ok?
  928. assert_equal 'bar in subclass', body
  929. end
  930. it "matches routes in subclasses before superclasses" do
  931. base = Class.new(Sinatra::Base)
  932. base.get('/foo') { 'foo in baseclass' }
  933. base.get('/bar') { 'bar in baseclass' }
  934. mock_app(base) {
  935. get('/foo') { 'foo in subclass' }
  936. }
  937. get '/foo'
  938. assert ok?
  939. assert_equal 'foo in subclass', body
  940. get '/bar'
  941. assert ok?
  942. assert_equal 'bar in baseclass', body
  943. end
  944. it "adds hostname condition when it is in options" do
  945. mock_app {
  946. get '/foo', :host => 'host' do
  947. 'foo'
  948. end
  949. }
  950. get '/foo'
  951. assert not_found?
  952. end
  953. it 'allows using call to fire another request internally' do
  954. mock_app do
  955. get '/foo' do
  956. status, headers, body = call env.merge("PATH_INFO" => '/bar')
  957. [status, headers, body.each.map(&:upcase)]
  958. end
  959. get '/bar' do
  960. "bar"
  961. end
  962. end
  963. get '/foo'
  964. assert ok?
  965. assert_body "BAR"
  966. end
  967. it 'plays well with other routing middleware' do
  968. middleware = Sinatra.new
  969. inner_app = Sinatra.new { get('/foo') { 'hello' } }
  970. builder = Rack::Builder.new do
  971. use middleware
  972. map('/test') { run inner_app }
  973. end
  974. @app = builder.to_app
  975. get '/test/foo'
  976. assert ok?
  977. assert_body 'hello'
  978. end
  979. it 'returns the route signature' do
  980. signature = list = nil
  981. mock_app do
  982. signature = post('/') { }
  983. list = routes['POST']
  984. end
  985. assert_equal Array, signature.class
  986. assert_equal 4, signature.length
  987. assert list.include?(signature)
  988. end
  989. end