adavidev_batch_api 0.2.1.pre.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (81) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/Rakefile +30 -0
  4. data/changelog.md +60 -0
  5. data/lib/adavidev_batch_api.rb +28 -0
  6. data/lib/batch_api/batch_error.rb +41 -0
  7. data/lib/batch_api/configuration.rb +36 -0
  8. data/lib/batch_api/error_wrapper.rb +44 -0
  9. data/lib/batch_api/internal_middleware/decode_json_body.rb +24 -0
  10. data/lib/batch_api/internal_middleware/response_filter.rb +27 -0
  11. data/lib/batch_api/internal_middleware.rb +87 -0
  12. data/lib/batch_api/operation/rack.rb +74 -0
  13. data/lib/batch_api/operation/rails.rb +42 -0
  14. data/lib/batch_api/operation.rb +2 -0
  15. data/lib/batch_api/processor/executor.rb +18 -0
  16. data/lib/batch_api/processor/sequential.rb +29 -0
  17. data/lib/batch_api/processor.rb +114 -0
  18. data/lib/batch_api/rack_middleware.rb +37 -0
  19. data/lib/batch_api/response.rb +38 -0
  20. data/lib/batch_api/utils.rb +17 -0
  21. data/lib/batch_api/version.rb +3 -0
  22. data/lib/batch_api.rb +28 -0
  23. data/lib/tasks/batch_api_tasks.rake +4 -0
  24. data/readme.md +243 -0
  25. data/spec/dummy/README.rdoc +261 -0
  26. data/spec/dummy/Rakefile +15 -0
  27. data/spec/dummy/app/assets/javascripts/application.js +15 -0
  28. data/spec/dummy/app/assets/javascripts/endpoints.js +2 -0
  29. data/spec/dummy/app/assets/stylesheets/application.css +13 -0
  30. data/spec/dummy/app/assets/stylesheets/endpoints.css +4 -0
  31. data/spec/dummy/app/controllers/application_controller.rb +3 -0
  32. data/spec/dummy/app/controllers/endpoints_controller.rb +36 -0
  33. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  34. data/spec/dummy/app/helpers/endpoints_helper.rb +2 -0
  35. data/spec/dummy/app/views/endpoints/get.html.erb +2 -0
  36. data/spec/dummy/app/views/endpoints/post.html.erb +2 -0
  37. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  38. data/spec/dummy/config/application.rb +63 -0
  39. data/spec/dummy/config/boot.rb +10 -0
  40. data/spec/dummy/config/database.yml +25 -0
  41. data/spec/dummy/config/environment.rb +5 -0
  42. data/spec/dummy/config/environments/development.rb +37 -0
  43. data/spec/dummy/config/environments/production.rb +67 -0
  44. data/spec/dummy/config/environments/test.rb +37 -0
  45. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  46. data/spec/dummy/config/initializers/inflections.rb +15 -0
  47. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  48. data/spec/dummy/config/initializers/secret_token.rb +7 -0
  49. data/spec/dummy/config/initializers/session_store.rb +8 -0
  50. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  51. data/spec/dummy/config/locales/en.yml +5 -0
  52. data/spec/dummy/config/routes.rb +64 -0
  53. data/spec/dummy/config.ru +4 -0
  54. data/spec/dummy/db/test.sqlite3 +0 -0
  55. data/spec/dummy/public/404.html +26 -0
  56. data/spec/dummy/public/422.html +26 -0
  57. data/spec/dummy/public/500.html +25 -0
  58. data/spec/dummy/public/favicon.ico +0 -0
  59. data/spec/dummy/script/rails +6 -0
  60. data/spec/dummy/test/functional/endpoints_controller_test.rb +14 -0
  61. data/spec/dummy/test/unit/helpers/endpoints_helper_test.rb +4 -0
  62. data/spec/integration/rails_spec.rb +10 -0
  63. data/spec/integration/shared_examples.rb +267 -0
  64. data/spec/integration/sinatra_integration_spec.rb +14 -0
  65. data/spec/lib/batch_api_spec.rb +20 -0
  66. data/spec/lib/batch_error_spec.rb +23 -0
  67. data/spec/lib/configuration_spec.rb +30 -0
  68. data/spec/lib/error_wrapper_spec.rb +68 -0
  69. data/spec/lib/internal_middleware/decode_json_body_spec.rb +37 -0
  70. data/spec/lib/internal_middleware/response_filter_spec.rb +61 -0
  71. data/spec/lib/internal_middleware_spec.rb +91 -0
  72. data/spec/lib/operation/rack_spec.rb +240 -0
  73. data/spec/lib/operation/rails_spec.rb +100 -0
  74. data/spec/lib/processor/executor_spec.rb +22 -0
  75. data/spec/lib/processor/sequential_spec.rb +39 -0
  76. data/spec/lib/processor_spec.rb +134 -0
  77. data/spec/lib/rack_middleware_spec.rb +103 -0
  78. data/spec/lib/response_spec.rb +53 -0
  79. data/spec/spec_helper.rb +28 -0
  80. data/spec/support/sinatra_app.rb +54 -0
  81. metadata +264 -0
@@ -0,0 +1,7 @@
1
+ # Be sure to restart your server when you modify this file.
2
+
3
+ # Your secret key for verifying the integrity of signed cookies.
4
+ # If you change this key, all old signed cookies will become invalid!
5
+ # Make sure the secret is at least 30 characters and all random,
6
+ # no regular words or you'll be exposed to dictionary attacks.
7
+ Dummy::Application.config.secret_token = '62dfab0df1ef66ae95f30772dda6387acdb66116bfdae29d824033e2a5b7d713a9fe4fb7735d5e9931207e73774af200ec9008d12ab1d3df41e8c7744194a6c0'
@@ -0,0 +1,8 @@
1
+ # Be sure to restart your server when you modify this file.
2
+
3
+ Dummy::Application.config.session_store :cookie_store, key: '_dummy_session'
4
+
5
+ # Use the database for sessions instead of the cookie-based default,
6
+ # which shouldn't be used to store highly confidential information
7
+ # (create the session table with "rails generate session_migration")
8
+ # Dummy::Application.config.session_store :active_record_store
@@ -0,0 +1,14 @@
1
+ # Be sure to restart your server when you modify this file.
2
+ #
3
+ # This file contains settings for ActionController::ParamsWrapper which
4
+ # is enabled by default.
5
+
6
+ # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array.
7
+ ActiveSupport.on_load(:action_controller) do
8
+ wrap_parameters format: [:json]
9
+ end
10
+
11
+ # Disable root element in JSON by default.
12
+ ActiveSupport.on_load(:active_record) do
13
+ self.include_root_in_json = false
14
+ end
@@ -0,0 +1,5 @@
1
+ # Sample localization file for English. Add more files in this directory for other locales.
2
+ # See https://github.com/svenfuchs/rails-i18n/tree/master/rails%2Flocale for starting points.
3
+
4
+ en:
5
+ hello: "Hello world"
@@ -0,0 +1,64 @@
1
+ Dummy::Application.routes.draw do
2
+
3
+ post "/endpoint" => "endpoints#post"
4
+ get "/endpoint" => "endpoints#get"
5
+ get "/endpoint/error" => "endpoints#error"
6
+ get "/endpoint/capture/:captured" => "endpoints#capture"
7
+
8
+ # The priority is based upon order of creation:
9
+ # first created -> highest priority.
10
+
11
+ # Sample of regular route:
12
+ # match 'products/:id' => 'catalog#view'
13
+ # Keep in mind you can assign values other than :controller and :action
14
+
15
+ # Sample of named route:
16
+ # match 'products/:id/purchase' => 'catalog#purchase', :as => :purchase
17
+ # This route can be invoked with purchase_url(:id => product.id)
18
+
19
+ # Sample resource route (maps HTTP verbs to controller actions automatically):
20
+ # resources :products
21
+
22
+ # Sample resource route with options:
23
+ # resources :products do
24
+ # member do
25
+ # get 'short'
26
+ # post 'toggle'
27
+ # end
28
+ #
29
+ # collection do
30
+ # get 'sold'
31
+ # end
32
+ # end
33
+
34
+ # Sample resource route with sub-resources:
35
+ # resources :products do
36
+ # resources :comments, :sales
37
+ # resource :seller
38
+ # end
39
+
40
+ # Sample resource route with more complex sub-resources
41
+ # resources :products do
42
+ # resources :comments
43
+ # resources :sales do
44
+ # get 'recent', :on => :collection
45
+ # end
46
+ # end
47
+
48
+ # Sample resource route within a namespace:
49
+ # namespace :admin do
50
+ # # Directs /admin/products/* to Admin::ProductsController
51
+ # # (app/controllers/admin/products_controller.rb)
52
+ # resources :products
53
+ # end
54
+
55
+ # You can have the root of your site routed with "root"
56
+ # just remember to delete public/index.html.
57
+ # root :to => 'welcome#index'
58
+
59
+ # See how all your routes lay out with "rake routes"
60
+
61
+ # This is a legacy wild controller route that's not recommended for RESTful applications.
62
+ # Note: This route will make all actions in every controller accessible via GET requests.
63
+ # match ':controller(/:action(/:id))(.:format)'
64
+ end
@@ -0,0 +1,4 @@
1
+ # This file is used by Rack-based servers to start the application.
2
+
3
+ require ::File.expand_path('../config/environment', __FILE__)
4
+ run Dummy::Application
File without changes
@@ -0,0 +1,26 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>The page you were looking for doesn't exist (404)</title>
5
+ <style type="text/css">
6
+ body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
7
+ div.dialog {
8
+ width: 25em;
9
+ padding: 0 4em;
10
+ margin: 4em auto 0 auto;
11
+ border: 1px solid #ccc;
12
+ border-right-color: #999;
13
+ border-bottom-color: #999;
14
+ }
15
+ h1 { font-size: 100%; color: #f00; line-height: 1.5em; }
16
+ </style>
17
+ </head>
18
+
19
+ <body>
20
+ <!-- This file lives in public/404.html -->
21
+ <div class="dialog">
22
+ <h1>The page you were looking for doesn't exist.</h1>
23
+ <p>You may have mistyped the address or the page may have moved.</p>
24
+ </div>
25
+ </body>
26
+ </html>
@@ -0,0 +1,26 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>The change you wanted was rejected (422)</title>
5
+ <style type="text/css">
6
+ body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
7
+ div.dialog {
8
+ width: 25em;
9
+ padding: 0 4em;
10
+ margin: 4em auto 0 auto;
11
+ border: 1px solid #ccc;
12
+ border-right-color: #999;
13
+ border-bottom-color: #999;
14
+ }
15
+ h1 { font-size: 100%; color: #f00; line-height: 1.5em; }
16
+ </style>
17
+ </head>
18
+
19
+ <body>
20
+ <!-- This file lives in public/422.html -->
21
+ <div class="dialog">
22
+ <h1>The change you wanted was rejected.</h1>
23
+ <p>Maybe you tried to change something you didn't have access to.</p>
24
+ </div>
25
+ </body>
26
+ </html>
@@ -0,0 +1,25 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>We're sorry, but something went wrong (500)</title>
5
+ <style type="text/css">
6
+ body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
7
+ div.dialog {
8
+ width: 25em;
9
+ padding: 0 4em;
10
+ margin: 4em auto 0 auto;
11
+ border: 1px solid #ccc;
12
+ border-right-color: #999;
13
+ border-bottom-color: #999;
14
+ }
15
+ h1 { font-size: 100%; color: #f00; line-height: 1.5em; }
16
+ </style>
17
+ </head>
18
+
19
+ <body>
20
+ <!-- This file lives in public/500.html -->
21
+ <div class="dialog">
22
+ <h1>We're sorry, but something went wrong.</h1>
23
+ </div>
24
+ </body>
25
+ </html>
File without changes
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ # This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application.
3
+
4
+ APP_PATH = File.expand_path('../../config/application', __FILE__)
5
+ require File.expand_path('../../config/boot', __FILE__)
6
+ require 'rails/commands'
@@ -0,0 +1,14 @@
1
+ require 'test_helper'
2
+
3
+ class EndpointsControllerTest < ActionController::TestCase
4
+ test "should get get" do
5
+ get :get
6
+ assert_response :success
7
+ end
8
+
9
+ test "should get post" do
10
+ get :post
11
+ assert_response :success
12
+ end
13
+
14
+ end
@@ -0,0 +1,4 @@
1
+ require 'test_helper'
2
+
3
+ class EndpointsHelperTest < ActionView::TestCase
4
+ end
@@ -0,0 +1,10 @@
1
+ require 'spec_helper'
2
+ require_relative './shared_examples'
3
+
4
+ describe "Rails integration specs" do
5
+ before :each do
6
+ BatchApi.stub(:rails?).and_return(true)
7
+ end
8
+
9
+ it_should_behave_like "integrating with a server"
10
+ end
@@ -0,0 +1,267 @@
1
+ shared_examples_for "a get request" do
2
+ it "returns the body as objects" do
3
+ @result = JSON.parse(response.body)["results"][0]
4
+ @result["body"].should == get_result[:body]
5
+ end
6
+
7
+ it "returns the expected status" do
8
+ @result["status"].should == get_result[:status]
9
+ end
10
+
11
+ it "returns the expected headers" do
12
+ @result["headers"].should include(get_result[:headers])
13
+ end
14
+
15
+ it "verifies that the right headers were received" do
16
+ @result["headers"]["REQUEST_HEADERS"].should include(
17
+ headerize(get_headers)
18
+ )
19
+ end
20
+ end
21
+
22
+ shared_examples_for "integrating with a server" do
23
+ def headerize(hash)
24
+ Hash[hash.map do |k, v|
25
+ ["HTTP_#{k.to_s.upcase}", v.to_s]
26
+ end]
27
+ end
28
+
29
+ before :all do
30
+ BatchApi.config.endpoint = "/batch"
31
+ BatchApi.config.verb = :post
32
+ end
33
+
34
+ before :each do
35
+ BatchApi::ErrorWrapper.stub(:expose_backtrace?).and_return(false)
36
+ end
37
+
38
+ # these are defined in the dummy app's endpoints controller
39
+ let(:get_headers) { {"foo" => "bar"} }
40
+ let(:get_params) { {"other" => "value" } }
41
+
42
+ let(:get_request) { {
43
+ url: "/endpoint",
44
+ method: "get",
45
+ headers: get_headers,
46
+ params: get_params
47
+ } }
48
+
49
+ let(:get_by_default_request) { {
50
+ url: "/endpoint",
51
+ headers: get_headers,
52
+ params: get_params
53
+ } }
54
+
55
+ let(:get_result) { {
56
+ status: 422,
57
+ body: {
58
+ "result" => "GET OK",
59
+ "params" => get_params.merge(
60
+ BatchApi.rails? ? {
61
+ "controller" => "endpoints",
62
+ "action" => "get"
63
+ } : {}
64
+ )
65
+ },
66
+ headers: { "GET" => "hello" }
67
+ } }
68
+
69
+ # these are defined in the dummy app's endpoints controller
70
+ let(:post_headers) { {"foo" => "bar"} }
71
+ let(:post_params) { {"other" => "value"} }
72
+
73
+ let(:post_request) { {
74
+ url: "/endpoint",
75
+ method: "post",
76
+ headers: post_headers,
77
+ params: post_params
78
+ } }
79
+
80
+ let(:post_result) { {
81
+ status: 203,
82
+ body: {
83
+ "result" => "POST OK",
84
+ "params" => post_params.merge(
85
+ BatchApi.rails? ? {
86
+ "controller" => "endpoints",
87
+ "action" => "post"
88
+ } : {}
89
+ )
90
+ },
91
+ headers: { "POST" => "guten tag" }
92
+ } }
93
+
94
+ let(:error_request) { {
95
+ url: "/endpoint/error",
96
+ method: "get"
97
+ } }
98
+
99
+ let(:error_response) { {
100
+ status: 500,
101
+ body: { "error" => { "message" => "StandardError" } }
102
+ } }
103
+
104
+ let(:missing_request) { {
105
+ url: "/dont/work",
106
+ method: "delete"
107
+ } }
108
+
109
+ let(:missing_response) { {
110
+ status: 404,
111
+ body: {}
112
+ } }
113
+
114
+ let(:parameter) {
115
+ (rand * 10000).to_i
116
+ }
117
+
118
+ let(:parameter_request) { {
119
+ url: "/endpoint/capture/#{parameter}",
120
+ method: "get"
121
+ } }
122
+
123
+ let(:parameter_result) { {
124
+ body: {
125
+ "result" => parameter.to_s
126
+ }
127
+ } }
128
+
129
+ let(:silent_request) { {
130
+ url: "/endpoint",
131
+ method: "post",
132
+ silent: true
133
+ } }
134
+
135
+ let(:failed_silent_request) {
136
+ error_request.merge(silent: true)
137
+ }
138
+
139
+ let(:failed_silent_result) {
140
+ error_response
141
+ }
142
+
143
+ before :each do
144
+ @t = Time.now
145
+ xhr :post, "/batch", {
146
+ ops: [
147
+ get_request,
148
+ post_request,
149
+ error_request,
150
+ missing_request,
151
+ parameter_request,
152
+ silent_request,
153
+ failed_silent_request,
154
+ get_by_default_request
155
+ ],
156
+ sequential: true
157
+ }.to_json, "CONTENT_TYPE" => "application/json"
158
+ end
159
+
160
+ it "returns a 200" do
161
+ response.status.should == 200
162
+ end
163
+
164
+ it "includes results" do
165
+ JSON.parse(response.body)["results"].should be_a(Array)
166
+ end
167
+
168
+ context "for a get request" do
169
+ describe "with an explicit get" do
170
+ before :each do
171
+ @result = JSON.parse(response.body)["results"][0]
172
+ end
173
+
174
+ it_should_behave_like "a get request"
175
+ end
176
+
177
+ describe "with no method" do
178
+ before :each do
179
+ @result = JSON.parse(response.body)["results"][7]
180
+ end
181
+
182
+ it_should_behave_like "a get request"
183
+ end
184
+ end
185
+
186
+ context "for a request with parameters" do
187
+ describe "the response" do
188
+ before :each do
189
+ @result = JSON.parse(response.body)["results"][4]
190
+ end
191
+
192
+ it "properly parses the URL segment as a paramer" do
193
+ @result["body"].should == parameter_result[:body]
194
+ end
195
+ end
196
+ end
197
+
198
+ context "for a post request" do
199
+ describe "the response" do
200
+ before :each do
201
+ @result = JSON.parse(response.body)["results"][1]
202
+ end
203
+
204
+ it "returns the body as objects (since DecodeJsonBody is default)" do
205
+ @result["body"].should == post_result[:body]
206
+ end
207
+
208
+ it "returns the expected status" do
209
+ @result["status"].should == post_result[:status]
210
+ end
211
+
212
+ it "returns the expected headers" do
213
+ @result["headers"].should include(post_result[:headers])
214
+ end
215
+
216
+ it "verifies that the right headers were received" do
217
+ @result["headers"]["REQUEST_HEADERS"].should include(headerize(post_headers))
218
+ end
219
+ end
220
+ end
221
+
222
+ context "for a request that returns an error" do
223
+ before :each do
224
+ @result = JSON.parse(response.body)["results"][2]
225
+ end
226
+
227
+ it "returns the right status" do
228
+ @result["status"].should == error_response[:status]
229
+ end
230
+
231
+ it "returns the right error information" do
232
+ # we don't care about the backtrace,
233
+ # the main thing is that the messsage arrives
234
+ @result["body"]["error"].should include(error_response[:body]["error"])
235
+ end
236
+ end
237
+
238
+ context "for a request that returns error" do
239
+ before :each do
240
+ @result = JSON.parse(response.body)["results"][3]
241
+ end
242
+
243
+ it "returns the right status" do
244
+ @result["status"].should == 404
245
+ end
246
+ end
247
+
248
+ context "for a silent request" do
249
+ before :each do
250
+ @result = JSON.parse(response.body)["results"][5]
251
+ end
252
+
253
+ it "returns nothing" do
254
+ @result.should == {}
255
+ end
256
+ end
257
+
258
+ context "for a silent request that causes an error" do
259
+ before :each do
260
+ @result = JSON.parse(response.body)["results"][6]
261
+ end
262
+
263
+ it "returns a regular result" do
264
+ @result.keys.should_not be_empty
265
+ end
266
+ end
267
+ end
@@ -0,0 +1,14 @@
1
+ require 'spec_helper'
2
+ require 'support/sinatra_app'
3
+ require 'rack/test'
4
+ require_relative './shared_examples'
5
+
6
+ describe "Sinatra integration" do
7
+ include Rack::Test::Methods
8
+
9
+ def app
10
+ SinatraApp
11
+ end
12
+
13
+ it_should_behave_like "integrating with a server"
14
+ end
@@ -0,0 +1,20 @@
1
+ require 'spec_helper'
2
+ require 'batch_api'
3
+
4
+ describe BatchApi do
5
+ describe ".config" do
6
+ it "has a reader for config" do
7
+ BatchApi.config.should_not be_nil
8
+ end
9
+
10
+ it "provides a default config" do
11
+ BatchApi.config.should be_a(BatchApi::Configuration)
12
+ end
13
+ end
14
+
15
+ describe ".rails?" do
16
+ it "returns a value we can't test based on whether Rails is defined" do
17
+ BatchApi.rails?.should_not be_nil
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,23 @@
1
+ require 'spec_helper'
2
+
3
+ describe BatchApi::Errors::BatchError do
4
+
5
+ [
6
+ BatchApi::Errors::OperationLimitExceeded,
7
+ BatchApi::Errors::BadOptionError,
8
+ BatchApi::Errors::NoOperationsError,
9
+ BatchApi::Errors::MalformedOperationError
10
+ ].each do |klass|
11
+ it "provides a #{klass} error based on ArgumentError" do
12
+ klass.superclass.should == ArgumentError
13
+ end
14
+
15
+ it "is is also a BatchError" do
16
+ klass.new.should be_a(BatchApi::Errors::BatchError)
17
+ end
18
+
19
+ it "has a status code of 422" do
20
+ klass.new.status_code.should == 422
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,30 @@
1
+ require 'spec_helper'
2
+
3
+ module BatchApi
4
+ describe Configuration do
5
+ let(:config) { Configuration.new }
6
+
7
+ describe "options" do
8
+ {
9
+ verb: :post,
10
+ endpoint: "/batch",
11
+ limit: 50,
12
+ batch_middleware: InternalMiddleware::DEFAULT_BATCH_MIDDLEWARE,
13
+ operation_middleware: InternalMiddleware::DEFAULT_OPERATION_MIDDLEWARE
14
+ }.each_pair do |option, default|
15
+ opt, defa = option, default
16
+ describe "##{opt}" do
17
+ it "has an accessor for #{opt}" do
18
+ stubby = stub
19
+ config.send("#{opt}=", stubby)
20
+ config.send(opt).should == stubby
21
+ end
22
+
23
+ it "defaults #{opt} to #{defa.inspect}" do
24
+ config.send(opt).should == defa
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,68 @@
1
+ require 'spec_helper'
2
+ require 'batch_api/error_wrapper'
3
+
4
+ describe BatchApi::ErrorWrapper do
5
+ let(:exception) {
6
+ StandardError.new(Faker::Lorem.words(3)).tap do |e|
7
+ e.set_backtrace(Kernel.caller)
8
+ end
9
+ }
10
+
11
+ let(:error) { BatchApi::ErrorWrapper.new(exception) }
12
+
13
+ describe "#body" do
14
+ it "includes the message in the body" do
15
+ error.body[:error][:message].should == exception.message
16
+ end
17
+
18
+ it "includes the backtrace if it should be there" do
19
+ error.stub(:expose_backtrace?).and_return(true)
20
+ error.body[:error][:backtrace].should == exception.backtrace
21
+ end
22
+
23
+ it "includes the backtrace if it should be there" do
24
+ error.stub(:expose_backtrace?).and_return(false)
25
+ error.body[:backtrace].should be_nil
26
+ end
27
+ end
28
+
29
+ describe "#render" do
30
+ it "returns the appropriate status" do
31
+ status = stub
32
+ error.stub(:status_code).and_return(status)
33
+ error.render[0].should == status
34
+ end
35
+
36
+ it "returns appropriate content type" do
37
+ ctype = stub
38
+ BatchApi::RackMiddleware.stub(:content_type).and_return(ctype)
39
+ error.render[1].should == ctype
40
+ end
41
+
42
+ it "returns the JSONified body as the 2nd" do
43
+ error.render[2].should == [MultiJson.dump(error.body)]
44
+ end
45
+ end
46
+
47
+ describe "#status_code" do
48
+ it "returns 500 by default" do
49
+ error.status_code.should == 500
50
+ end
51
+
52
+ it "returns another status code if the error supports that" do
53
+ err = StandardError.new
54
+ code = stub
55
+ err.stub(:status_code).and_return(code)
56
+ BatchApi::ErrorWrapper.new(err).status_code.should == code
57
+ end
58
+ end
59
+
60
+ describe ".expose_backtrace?" do
61
+ it "returns false if Rails.env.production?" do
62
+ Rails.env.stub(:production?).and_return(true)
63
+ BatchApi::ErrorWrapper.expose_backtrace?.should be_false
64
+ Rails.env.stub(:production?).and_return(false)
65
+ BatchApi::ErrorWrapper.expose_backtrace?.should be_true
66
+ end
67
+ end
68
+ end