test_track_rails_client 0.9.7 → 0.9.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (113) hide show
  1. checksums.yaml +8 -8
  2. data/README.md +11 -8
  3. data/lib/test_track_rails_client/version.rb +1 -1
  4. data/vendor/gems/fakeable_her/fakeable_her.gemspec +22 -0
  5. data/vendor/gems/fakeable_her/lib/fakeable_her/model.rb +148 -0
  6. data/vendor/gems/fakeable_her/lib/fakeable_her/version.rb +3 -0
  7. data/vendor/gems/fakeable_her/lib/fakeable_her.rb +5 -0
  8. data/vendor/gems/her/CONTRIBUTING.md +26 -0
  9. data/vendor/gems/her/Gemfile +10 -0
  10. data/vendor/gems/her/LICENSE +7 -0
  11. data/vendor/gems/her/README.md +1023 -0
  12. data/vendor/gems/her/Rakefile +11 -0
  13. data/vendor/gems/her/UPGRADE.md +101 -0
  14. data/vendor/gems/her/gemfiles/Gemfile.activemodel-3.2.x +7 -0
  15. data/vendor/gems/her/gemfiles/Gemfile.activemodel-4.0 +7 -0
  16. data/vendor/gems/her/gemfiles/Gemfile.activemodel-4.1 +7 -0
  17. data/vendor/gems/her/gemfiles/Gemfile.activemodel-4.2 +7 -0
  18. data/vendor/gems/her/her.gemspec +31 -0
  19. data/vendor/gems/her/lib/her/api.rb +119 -0
  20. data/vendor/gems/her/lib/her/collection.rb +12 -0
  21. data/vendor/gems/her/lib/her/error_collection.rb +15 -0
  22. data/vendor/gems/her/lib/her/errors.rb +40 -0
  23. data/vendor/gems/her/lib/her/json_api/model.rb +46 -0
  24. data/vendor/gems/her/lib/her/middleware/accept_json.rb +17 -0
  25. data/vendor/gems/her/lib/her/middleware/first_level_parse_json.rb +36 -0
  26. data/vendor/gems/her/lib/her/middleware/json_api_parser.rb +36 -0
  27. data/vendor/gems/her/lib/her/middleware/parse_json.rb +21 -0
  28. data/vendor/gems/her/lib/her/middleware/second_level_parse_json.rb +36 -0
  29. data/vendor/gems/her/lib/her/middleware.rb +12 -0
  30. data/vendor/gems/her/lib/her/model/active_model_overrides.rb +13 -0
  31. data/vendor/gems/her/lib/her/model/associations/association.rb +106 -0
  32. data/vendor/gems/her/lib/her/model/associations/association_proxy.rb +46 -0
  33. data/vendor/gems/her/lib/her/model/associations/belongs_to_association.rb +96 -0
  34. data/vendor/gems/her/lib/her/model/associations/has_many_association.rb +100 -0
  35. data/vendor/gems/her/lib/her/model/associations/has_one_association.rb +79 -0
  36. data/vendor/gems/her/lib/her/model/associations.rb +141 -0
  37. data/vendor/gems/her/lib/her/model/attributes.rb +304 -0
  38. data/vendor/gems/her/lib/her/model/base.rb +33 -0
  39. data/vendor/gems/her/lib/her/model/deprecated_methods.rb +61 -0
  40. data/vendor/gems/her/lib/her/model/http.rb +117 -0
  41. data/vendor/gems/her/lib/her/model/introspection.rb +65 -0
  42. data/vendor/gems/her/lib/her/model/nested_attributes.rb +45 -0
  43. data/vendor/gems/her/lib/her/model/orm.rb +219 -0
  44. data/vendor/gems/her/lib/her/model/parse.rb +215 -0
  45. data/vendor/gems/her/lib/her/model/paths.rb +126 -0
  46. data/vendor/gems/her/lib/her/model/relation.rb +251 -0
  47. data/vendor/gems/her/lib/her/model.rb +81 -0
  48. data/vendor/gems/her/lib/her/version.rb +3 -0
  49. data/vendor/gems/her/lib/her.rb +20 -0
  50. data/vendor/gems/her/spec/api_spec.rb +114 -0
  51. data/vendor/gems/her/spec/collection_spec.rb +26 -0
  52. data/vendor/gems/her/spec/error_collection_spec.rb +33 -0
  53. data/vendor/gems/her/spec/json_api/model_spec.rb +168 -0
  54. data/vendor/gems/her/spec/middleware/accept_json_spec.rb +10 -0
  55. data/vendor/gems/her/spec/middleware/first_level_parse_json_spec.rb +62 -0
  56. data/vendor/gems/her/spec/middleware/json_api_parser_spec.rb +32 -0
  57. data/vendor/gems/her/spec/middleware/second_level_parse_json_spec.rb +35 -0
  58. data/vendor/gems/her/spec/model/associations/association_proxy_spec.rb +31 -0
  59. data/vendor/gems/her/spec/model/associations_spec.rb +504 -0
  60. data/vendor/gems/her/spec/model/attributes_spec.rb +404 -0
  61. data/vendor/gems/her/spec/model/callbacks_spec.rb +145 -0
  62. data/vendor/gems/her/spec/model/dirty_spec.rb +110 -0
  63. data/vendor/gems/her/spec/model/http_spec.rb +165 -0
  64. data/vendor/gems/her/spec/model/introspection_spec.rb +76 -0
  65. data/vendor/gems/her/spec/model/nested_attributes_spec.rb +134 -0
  66. data/vendor/gems/her/spec/model/orm_spec.rb +791 -0
  67. data/vendor/gems/her/spec/model/parse_spec.rb +372 -0
  68. data/vendor/gems/her/spec/model/paths_spec.rb +347 -0
  69. data/vendor/gems/her/spec/model/relation_spec.rb +226 -0
  70. data/vendor/gems/her/spec/model/validations_spec.rb +42 -0
  71. data/vendor/gems/her/spec/model_spec.rb +31 -0
  72. data/vendor/gems/her/spec/spec_helper.rb +27 -0
  73. data/vendor/gems/her/spec/support/extensions/array.rb +5 -0
  74. data/vendor/gems/her/spec/support/extensions/hash.rb +5 -0
  75. data/vendor/gems/her/spec/support/macros/her_macros.rb +17 -0
  76. data/vendor/gems/her/spec/support/macros/model_macros.rb +36 -0
  77. data/vendor/gems/her/spec/support/macros/request_macros.rb +27 -0
  78. data/vendor/gems/publicsuffix-ruby/CHANGELOG.md +236 -0
  79. data/vendor/gems/publicsuffix-ruby/Gemfile +3 -0
  80. data/vendor/gems/publicsuffix-ruby/LICENSE.txt +22 -0
  81. data/vendor/gems/publicsuffix-ruby/README.md +151 -0
  82. data/vendor/gems/publicsuffix-ruby/Rakefile +109 -0
  83. data/vendor/gems/publicsuffix-ruby/lib/definitions.txt +11467 -0
  84. data/vendor/gems/publicsuffix-ruby/lib/public_suffix/domain.rb +387 -0
  85. data/vendor/gems/publicsuffix-ruby/lib/public_suffix/errors.rb +53 -0
  86. data/vendor/gems/publicsuffix-ruby/lib/public_suffix/list.rb +302 -0
  87. data/vendor/gems/publicsuffix-ruby/lib/public_suffix/rule.rb +373 -0
  88. data/vendor/gems/publicsuffix-ruby/lib/public_suffix/version.rb +23 -0
  89. data/vendor/gems/publicsuffix-ruby/lib/public_suffix.rb +131 -0
  90. data/vendor/gems/publicsuffix-ruby/public_suffix.gemspec +39 -0
  91. data/vendor/gems/publicsuffix-ruby/test/acceptance_test.rb +42 -0
  92. data/vendor/gems/publicsuffix-ruby/test/test_helper.rb +6 -0
  93. data/vendor/gems/publicsuffix-ruby/test/unit/domain_test.rb +170 -0
  94. data/vendor/gems/publicsuffix-ruby/test/unit/errors_test.rb +23 -0
  95. data/vendor/gems/publicsuffix-ruby/test/unit/list_test.rb +179 -0
  96. data/vendor/gems/publicsuffix-ruby/test/unit/public_suffix_test.rb +115 -0
  97. data/vendor/gems/publicsuffix-ruby/test/unit/rule_test.rb +307 -0
  98. data/vendor/gems/ruby_spec_helpers/lib/ruby_spec_helpers/capybara_configuration.rb +98 -0
  99. data/vendor/gems/ruby_spec_helpers/lib/ruby_spec_helpers/matchers.rb +151 -0
  100. data/vendor/gems/ruby_spec_helpers/lib/ruby_spec_helpers/rspec_configuration.rb +34 -0
  101. data/vendor/gems/ruby_spec_helpers/lib/ruby_spec_helpers/rubocop/cop/betterment/html_safe.rb +15 -0
  102. data/vendor/gems/ruby_spec_helpers/lib/ruby_spec_helpers/rubocop/cop/betterment/raw.rb +15 -0
  103. data/vendor/gems/ruby_spec_helpers/lib/ruby_spec_helpers/rubocop/cop/betterment/safe_concat.rb +15 -0
  104. data/vendor/gems/ruby_spec_helpers/lib/ruby_spec_helpers/rubocop.rb +3 -0
  105. data/vendor/gems/ruby_spec_helpers/lib/ruby_spec_helpers/shared_examples/betterment_application_examples.rb +47 -0
  106. data/vendor/gems/ruby_spec_helpers/lib/ruby_spec_helpers/shared_examples.rb +1 -0
  107. data/vendor/gems/ruby_spec_helpers/lib/ruby_spec_helpers/site_prism_configuration.rb +42 -0
  108. data/vendor/gems/ruby_spec_helpers/lib/ruby_spec_helpers/site_prism_dropdown.rb +17 -0
  109. data/vendor/gems/ruby_spec_helpers/lib/ruby_spec_helpers/version.rb +3 -0
  110. data/vendor/gems/ruby_spec_helpers/lib/ruby_spec_helpers/webmock_configuration.rb +8 -0
  111. data/vendor/gems/ruby_spec_helpers/lib/ruby_spec_helpers.rb +2 -0
  112. data/vendor/gems/ruby_spec_helpers/ruby_spec_helpers.gemspec +25 -0
  113. metadata +110 -1
@@ -0,0 +1,791 @@
1
+ # encoding: utf-8
2
+ require File.join(File.dirname(__FILE__), "../spec_helper.rb")
3
+
4
+ describe Her::Model::ORM do
5
+ context "mapping data to Ruby objects" do
6
+ before do
7
+ api = Her::API.new
8
+ api.setup :url => "https://api.example.com" do |builder|
9
+ builder.use Her::Middleware::FirstLevelParseJSON
10
+ builder.use Faraday::Request::UrlEncoded
11
+ builder.adapter :test do |stub|
12
+ stub.get("/users/1") { |env| [200, {}, { :id => 1, :name => "Tobias Fünke" }.to_json] }
13
+ stub.get("/users") { |env| [200, {}, [{ :id => 1, :name => "Tobias Fünke" }, { :id => 2, :name => "Lindsay Fünke" }].to_json] }
14
+ stub.get("/admin_users") { |env| [200, {}, [{ :admin_id => 1, :name => "Tobias Fünke" }, { :admin_id => 2, :name => "Lindsay Fünke" }].to_json] }
15
+ stub.get("/admin_users/1") { |env| [200, {}, { :admin_id => 1, :name => "Tobias Fünke" }.to_json] }
16
+ end
17
+ end
18
+
19
+ spawn_model "Foo::User" do
20
+ uses_api api
21
+ end
22
+
23
+ spawn_model "Foo::AdminUser" do
24
+ uses_api api
25
+ primary_key :admin_id
26
+ end
27
+ end
28
+
29
+ it "maps a single resource to a Ruby object" do
30
+ @user = Foo::User.find(1)
31
+ @user.id.should == 1
32
+ @user.name.should == "Tobias Fünke"
33
+
34
+ @admin = Foo::AdminUser.find(1)
35
+ @admin.id.should == 1
36
+ @admin.name.should == "Tobias Fünke"
37
+ end
38
+
39
+ it "maps a collection of resources to an array of Ruby objects" do
40
+ @users = Foo::User.all
41
+ @users.length.should == 2
42
+ @users.first.name.should == "Tobias Fünke"
43
+
44
+ @users = Foo::AdminUser.all
45
+ @users.length.should == 2
46
+ @users.first.name.should == "Tobias Fünke"
47
+ end
48
+
49
+ it "handles new resource" do
50
+ @new_user = Foo::User.new(:fullname => "Tobias Fünke")
51
+ @new_user.new?.should be_truthy
52
+ @new_user.new_record?.should be_truthy
53
+ @new_user.fullname.should == "Tobias Fünke"
54
+
55
+ @existing_user = Foo::User.find(1)
56
+ @existing_user.new?.should be_falsey
57
+ @existing_user.new_record?.should be_falsey
58
+ end
59
+
60
+ it 'handles new resource with custom primary key' do
61
+ @new_user = Foo::AdminUser.new(:fullname => 'Lindsay Fünke', :id => -1)
62
+ @new_user.should be_new
63
+
64
+ @existing_user = Foo::AdminUser.find(1)
65
+ @existing_user.should_not be_new
66
+ end
67
+ end
68
+
69
+ context "mapping data, metadata and error data to Ruby objects" do
70
+ before do
71
+ api = Her::API.new
72
+ api.setup :url => "https://api.example.com" do |builder|
73
+ builder.use Her::Middleware::SecondLevelParseJSON
74
+ builder.use Faraday::Request::UrlEncoded
75
+ builder.adapter :test do |stub|
76
+ stub.get("/users") { |env| [200, {}, { :data => [{ :id => 1, :name => "Tobias Fünke" }, { :id => 2, :name => "Lindsay Fünke" }], :metadata => { :total_pages => 10, :next_page => 2 }, :errors => ["Oh", "My", "God"] }.to_json] }
77
+ stub.post("/users") { |env| [200, {}, { :data => { :name => "George Michael Bluth" }, :metadata => { :foo => "bar" }, :errors => ["Yes", "Sir"] }.to_json] }
78
+ end
79
+ end
80
+
81
+ spawn_model :User do
82
+ uses_api api
83
+ end
84
+ end
85
+
86
+ it "handles metadata on a collection" do
87
+ @users = User.all
88
+ @users.metadata[:total_pages].should == 10
89
+ end
90
+
91
+ it "handles error data on a collection" do
92
+ @users = User.all
93
+ @users.errors.length.should == 3
94
+ end
95
+
96
+ it "handles metadata on a resource" do
97
+ @user = User.create(:name => "George Michael Bluth")
98
+ @user.metadata[:foo].should == "bar"
99
+ end
100
+
101
+ it "handles error data on a resource" do
102
+ @user = User.create(:name => "George Michael Bluth")
103
+ @user.response_errors.should == ["Yes", "Sir"]
104
+ end
105
+ end
106
+
107
+ context "mapping data, metadata and error data in string keys to Ruby objects" do
108
+ before do
109
+ api = Her::API.new
110
+ api.setup :url => "https://api.example.com" do |builder|
111
+ builder.use Her::Middleware::SecondLevelParseJSON
112
+ builder.use Faraday::Request::UrlEncoded
113
+ builder.adapter :test do |stub|
114
+ stub.get("/users") { |env| [200, {}, { 'data' => [{ :id => 1, :name => "Tobias Fünke" }, { :id => 2, :name => "Lindsay Fünke" }], 'metadata' => { :total_pages => 10, :next_page => 2 }, 'errors' => ["Oh", "My", "God"] }.to_json] }
115
+ stub.post("/users") { |env| [200, {}, { 'data' => { :name => "George Michael Bluth" }, 'metadata' => { :foo => "bar" }, 'errors' => ["Yes", "Sir"] }.to_json] }
116
+ end
117
+ end
118
+
119
+ spawn_model :User do
120
+ uses_api api
121
+ end
122
+ end
123
+
124
+ it "handles metadata on a collection" do
125
+ @users = User.all
126
+ @users.metadata[:total_pages].should == 10
127
+ end
128
+
129
+ it "handles error data on a collection" do
130
+ @users = User.all
131
+ @users.errors.length.should == 3
132
+ end
133
+
134
+ it "handles metadata on a resource" do
135
+ @user = User.create(:name => "George Michael Bluth")
136
+ @user.metadata[:foo].should == "bar"
137
+ end
138
+
139
+ it "handles error data on a resource" do
140
+ @user = User.create(:name => "George Michael Bluth")
141
+ @user.response_errors.should == ["Yes", "Sir"]
142
+ end
143
+ end
144
+
145
+ context "defining custom getters and setters" do
146
+ before do
147
+ api = Her::API.new
148
+ api.setup :url => "https://api.example.com" do |builder|
149
+ builder.use Her::Middleware::FirstLevelParseJSON
150
+ builder.use Faraday::Request::UrlEncoded
151
+ builder.adapter :test do |stub|
152
+ stub.get("/users/1") { |env| [200, {}, { :id => 1, :friends => ["Maeby", "GOB", "Anne"] }.to_json] }
153
+ stub.get("/users/2") { |env| [200, {}, { :id => 1 }.to_json] }
154
+ end
155
+ end
156
+
157
+ spawn_model :User do
158
+ uses_api api
159
+ belongs_to :organization
160
+
161
+ def friends=(val)
162
+ val = val.gsub("\r", "").split("\n").map { |friend| friend.gsub(/^\s*\*\s*/, "") } if val and val.is_a?(String)
163
+ @attributes[:friends] = val
164
+ end
165
+
166
+ def friends
167
+ @attributes[:friends].map { |friend| "* #{friend}" }.join("\n")
168
+ end
169
+ end
170
+ end
171
+
172
+ it "handles custom setters" do
173
+ @user = User.find(1)
174
+ @user.friends.should == "* Maeby\n* GOB\n* Anne"
175
+ @user.instance_eval do
176
+ @attributes[:friends] = ["Maeby", "GOB", "Anne"]
177
+ end
178
+ end
179
+
180
+ it "handles custom getters" do
181
+ @user = User.new
182
+ @user.friends = "* George\n* Oscar\n* Lucille"
183
+ @user.friends.should == "* George\n* Oscar\n* Lucille"
184
+ @user.instance_eval do
185
+ @attributes[:friends] = ["George", "Oscar", "Lucille"]
186
+ end
187
+ end
188
+ end
189
+
190
+ context "finding resources" do
191
+ before do
192
+ api = Her::API.new
193
+ api.setup :url => "https://api.example.com" do |builder|
194
+ builder.use Her::Middleware::FirstLevelParseJSON
195
+ builder.use Faraday::Request::UrlEncoded
196
+ builder.adapter :test do |stub|
197
+ stub.get("/users/1") { |env| [200, {}, { :id => 1, :age => 42 }.to_json] }
198
+ stub.get("/users/2") { |env| [200, {}, { :id => 2, :age => 34 }.to_json] }
199
+ stub.get("/users/3") { |env| [404, {}, nil] }
200
+ stub.get("/users?id[]=1&id[]=2") { |env| [200, {}, [{ :id => 1, :age => 42 }, { :id => 2, :age => 34 }].to_json] }
201
+ stub.get("/users?age=42&foo=bar") { |env| [200, {}, [{ :id => 3, :age => 42 }].to_json] }
202
+ stub.get("/users?bad_request=true") { |env| [400, {}, { errors: ["bad request params"] }.to_json] }
203
+ stub.get("/users?age=42") { |env| [200, {}, [{ :id => 1, :age => 42 }].to_json] }
204
+ stub.get("/users?age=40") { |env| [200, {}, [{ :id => 1, :age => 40 }].to_json] }
205
+ end
206
+ end
207
+
208
+ spawn_model :User do
209
+ uses_api api
210
+ end
211
+ end
212
+
213
+ it "handles finding by a single id" do
214
+ @user = User.find(1)
215
+ @user.id.should == 1
216
+ end
217
+
218
+ it "handles finding by multiple ids" do
219
+ @users = User.find(1, 2)
220
+ @users.should be_kind_of(Array)
221
+ @users.length.should == 2
222
+ @users[0].id.should == 1
223
+ @users[1].id.should == 2
224
+ end
225
+
226
+ it "handles finding by an array of ids" do
227
+ @users = User.find([1, 2])
228
+ @users.should be_kind_of(Array)
229
+ @users.length.should == 2
230
+ @users[0].id.should == 1
231
+ @users[1].id.should == 2
232
+ end
233
+
234
+ it "handles finding by an array of ids of length 1" do
235
+ @users = User.find([1])
236
+ @users.should be_kind_of(Array)
237
+ @users.length.should == 1
238
+ @users[0].id.should == 1
239
+ end
240
+
241
+ it "handles finding by an array id param of length 2" do
242
+ @users = User.find(id: [1, 2])
243
+ @users.should be_kind_of(Array)
244
+ @users.length.should == 2
245
+ @users[0].id.should == 1
246
+ @users[1].id.should == 2
247
+ end
248
+
249
+ it 'handles finding with id parameter as an array' do
250
+ @users = User.where(id: [1, 2])
251
+ @users.should be_kind_of(Array)
252
+ @users.length.should == 2
253
+ @users[0].id.should == 1
254
+ @users[1].id.should == 2
255
+ end
256
+
257
+ it "handles finding with other parameters" do
258
+ @users = User.where(:age => 42, :foo => "bar").all
259
+ @users.should be_kind_of(Array)
260
+ @users.first.id.should == 3
261
+ end
262
+
263
+ it "handles finding with other parameters and scoped" do
264
+ @users = User.scoped
265
+ @users.where(:age => 42).should be_all { |u| u.age == 42 }
266
+ @users.where(:age => 40).should be_all { |u| u.age == 40 }
267
+ end
268
+
269
+ it "throws when a given resource does not exist" do
270
+ expect { User.find(3) }.to raise_error(Her::Errors::RecordNotFound, /User returned a 404/)
271
+ end
272
+
273
+ it "throws when accessing collection of a resource that does not exist" do
274
+ @users = User.where(bad_request: true).all
275
+ expect { @users.first }.to raise_error(Her::Errors::ResponseError, "Cannot access collection, Request returned an error")
276
+ end
277
+ end
278
+
279
+ describe :find_by do
280
+ before do
281
+ api = Her::API.new
282
+ api.setup :url => "https://api.example.com" do |builder|
283
+ builder.use Her::Middleware::FirstLevelParseJSON
284
+ builder.use Faraday::Request::UrlEncoded
285
+ builder.adapter :test do |stub|
286
+ stub.get("/users/1") { |env| [200, {}, { :id => 1, :age => 42 }.to_json] }
287
+ end
288
+ end
289
+
290
+ spawn_model :User do
291
+ uses_api api
292
+ end
293
+ end
294
+
295
+ it "supports the same basic behavior of find with an id" do
296
+ expect(User.find_by(id: 1).id).to eq 1
297
+ end
298
+ end
299
+
300
+ describe :first do
301
+ before do
302
+ api = Her::API.new
303
+ api.setup :url => "https://api.example.com" do |builder|
304
+ builder.use Her::Middleware::FirstLevelParseJSON
305
+ builder.use Faraday::Request::UrlEncoded
306
+ builder.adapter :test do |stub|
307
+ stub.get("/users/1") { |env| [200, {}, { :id => 1, :age => 42 }.to_json] }
308
+ stub.get("/users/2") { |env| [404, {}, nil] }
309
+ stub.get("/users/3") { |env| [410, {}, nil] }
310
+ stub.get("/users/8") { |env| [401, {}, nil] }
311
+ stub.get("/users/4?age=42&foo=bar") { |env| [200, {}, { :id => 4, :age => 42 }.to_json] }
312
+ stub.get("/users?age=50&color=blue") { |env| [200, {}, [{ :id => 5, :age => 50, :color => 'blue' }].to_json] }
313
+ stub.get("/users?id[]=6&id[]=7") { |env| [200, {}, [{ :id => 6 }].to_json] }
314
+ end
315
+ end
316
+
317
+ spawn_model :User do
318
+ uses_api api
319
+ end
320
+ end
321
+
322
+ it "supports the same basic behavior of find with an id" do
323
+ expect(User.where(id: 1).first.id).to eq 1
324
+ end
325
+
326
+ it "returns nil when a given resource returns 404" do
327
+ expect(User.where(id: 2).first).to be_nil
328
+ end
329
+
330
+ it "returns nil when a given resource returns 410" do
331
+ expect(User.where(id: 3).first).to be_nil
332
+ end
333
+
334
+ it "raises on any other 4xx" do
335
+ expect { User.where(id: 8).first }.to raise_error(Her::Errors::ResponseError, /returned a 401/)
336
+ end
337
+
338
+ it "handles finding with an id AND other parameters" do
339
+ expect(User.where(:id => 4, :age => 42, :foo => "bar").first.id).to eq 4
340
+ end
341
+
342
+ it "handles finding with other parameters" do
343
+ expect(User.where(:age => 50, :color => "blue").first.id).to eq 5
344
+ end
345
+
346
+ it "accepts multiple id values" do
347
+ expect(User.where(id: [6, 7]).first.id).to eq 6
348
+ end
349
+ end
350
+
351
+ context "building resources" do
352
+ context "when request_new_object_on_build is not set (default)" do
353
+ before do
354
+ spawn_model("Foo::User")
355
+ end
356
+
357
+ it "builds a new resource without requesting it" do
358
+ Foo::User.should_not_receive(:request)
359
+ @new_user = Foo::User.build(:fullname => "Tobias Fünke")
360
+ @new_user.new?.should be_truthy
361
+ @new_user.fullname.should == "Tobias Fünke"
362
+ end
363
+ end
364
+
365
+ context "when request_new_object_on_build is set" do
366
+ before do
367
+ Her::API.setup :url => "https://api.example.com" do |builder|
368
+ builder.use Her::Middleware::FirstLevelParseJSON
369
+ builder.use Faraday::Request::UrlEncoded
370
+ builder.adapter :test do |stub|
371
+ stub.get("/users/new") { |env| ok! :id => nil, :fullname => params(env)[:fullname], :email => "tobias@bluthcompany.com" }
372
+ end
373
+ end
374
+
375
+ spawn_model("Foo::User") { request_new_object_on_build true }
376
+ end
377
+
378
+ it "requests a new resource" do
379
+ Foo::User.should_receive(:request).once.and_call_original
380
+ @new_user = Foo::User.build(:fullname => "Tobias Fünke")
381
+ @new_user.new?.should be_truthy
382
+ @new_user.fullname.should == "Tobias Fünke"
383
+ @new_user.email.should == "tobias@bluthcompany.com"
384
+ end
385
+ end
386
+ end
387
+
388
+ context "creating resources" do
389
+ before do
390
+ Her::API.setup :url => "https://api.example.com" do |builder|
391
+ builder.use Her::Middleware::FirstLevelParseJSON
392
+ builder.use Faraday::Request::UrlEncoded
393
+ builder.adapter :test do |stub|
394
+ stub.post("/users") { |env| [200, {}, { :id => 1, :fullname => Faraday::Utils.parse_query(env[:body])['fullname'], :email => Faraday::Utils.parse_query(env[:body])['email'] }.to_json] }
395
+ stub.post("/companies") { |env| [422, {}, { :errors => { :name => ["is required", "must be alphabetical"], :birthday => ['is required'] } }.to_json] }
396
+ end
397
+ end
398
+
399
+ spawn_model "Foo::User"
400
+ spawn_model "Foo::Company"
401
+ end
402
+
403
+ it "handle one-line resource creation" do
404
+ @user = Foo::User.create(:fullname => "Tobias Fünke", :email => "tobias@bluth.com")
405
+ @user.id.should == 1
406
+ @user.fullname.should == "Tobias Fünke"
407
+ @user.email.should == "tobias@bluth.com"
408
+ end
409
+
410
+ it "handle resource creation through Model.new + #save" do
411
+ @user = Foo::User.new(:fullname => "Tobias Fünke")
412
+ @user.save.should be true
413
+ @user.fullname.should == "Tobias Fünke"
414
+ end
415
+
416
+ it "handle resource creation through Model.new + #save!" do
417
+ @user = Foo::User.new(:fullname => "Tobias Fünke")
418
+ @user.save!.should be true
419
+ @user.fullname.should == "Tobias Fünke"
420
+ end
421
+
422
+ it "returns false when #save gets errors" do
423
+ @company = Foo::Company.new
424
+ @company.save.should be false
425
+ end
426
+
427
+ it "raises RecordInvalid when #save! gets errors" do
428
+ @company = Foo::Company.new
429
+ expect { @company.save! }.to raise_error Her::Errors::RecordInvalid, "Remote validation of Foo::Company failed with a 422: name is required, must be alphabetical. birthday is required"
430
+ end
431
+
432
+ it "contains 1 copy of any error message when the server responds with validation errors" do
433
+ @company = Foo::Company.new
434
+ @company.save
435
+ @company.save
436
+ expect(@company.errors.messages).to include :name => ['is required', 'must be alphabetical']
437
+ end
438
+
439
+ it "don't overwrite data if response is empty" do
440
+ @company = Foo::Company.new(:name => 'Company Inc.')
441
+ @company.save.should be false
442
+ @company.name.should == "Company Inc."
443
+ end
444
+ end
445
+
446
+ context "updating resources" do
447
+ before do
448
+ Her::API.setup :url => "https://api.example.com" do |builder|
449
+ builder.use Her::Middleware::FirstLevelParseJSON
450
+ builder.use Faraday::Request::UrlEncoded
451
+ builder.adapter :test do |stub|
452
+ stub.get("/users/1") { |env| [200, {}, { :id => 1, :fullname => "Tobias Fünke" }.to_json] }
453
+ stub.put("/users/1") { |env| [200, {}, { :id => 1, :fullname => "Lindsay Fünke" }.to_json] }
454
+ end
455
+ end
456
+
457
+ spawn_model "Foo::User"
458
+ end
459
+
460
+ it "handle resource data update without saving it" do
461
+ @user = Foo::User.find(1)
462
+ @user.fullname.should == "Tobias Fünke"
463
+ @user.fullname = "Kittie Sanchez"
464
+ @user.fullname.should == "Kittie Sanchez"
465
+ end
466
+
467
+ it "handle resource update through the .update class method" do
468
+ @user = Foo::User.save_existing(1, { :fullname => "Lindsay Fünke" })
469
+ @user.fullname.should == "Lindsay Fünke"
470
+ end
471
+
472
+ it "handle resource update through #save on an existing resource" do
473
+ @user = Foo::User.find(1)
474
+ @user.fullname = "Lindsay Fünke"
475
+ @user.save
476
+ @user.fullname.should == "Lindsay Fünke"
477
+ end
478
+ end
479
+
480
+ context "deleting resources" do
481
+ describe "successful deletion requests" do
482
+ before do
483
+ Her::API.setup :url => "https://api.example.com" do |builder|
484
+ builder.use Her::Middleware::FirstLevelParseJSON
485
+ builder.use Faraday::Request::UrlEncoded
486
+ builder.adapter :test do |stub|
487
+ stub.get("/users/1") { |env| [200, {}, { :id => 1, :fullname => "Tobias Fünke", :active => true }.to_json] }
488
+ stub.delete("/users/1") { |env| [200, {}, { :id => 1, :fullname => "Lindsay Fünke", :active => false }.to_json] }
489
+ end
490
+ end
491
+
492
+ spawn_model "Foo::User"
493
+ end
494
+
495
+ it "handle resource deletion through the .destroy class method" do
496
+ @user = Foo::User.destroy_existing(1)
497
+ @user.active.should be_falsey
498
+ @user.should be_destroyed
499
+ end
500
+
501
+ it "handle resource deletion through #destroy on an existing resource" do
502
+ @user = Foo::User.find(1)
503
+ @user.destroy
504
+ @user.active.should be_falsey
505
+ @user.should be_destroyed
506
+ end
507
+ end
508
+
509
+ describe "failed deletion requests (400-space responses)" do
510
+ before do
511
+ Her::API.setup :url => "https://api.example.com" do |builder|
512
+ builder.use Her::Middleware::FirstLevelParseJSON
513
+ builder.use Faraday::Request::UrlEncoded
514
+ builder.adapter :test do |stub|
515
+ stub.get("/users/1") { |env| [200, {}, { :id => 1, :fullname => "Tobias Fünke", :active => true }.to_json] }
516
+ stub.get("/users/2") { |env| [200, {}, { :id => 2, :fullname => "Tobias Fünke", :active => true }.to_json] }
517
+ stub.get("/users/3") { |env| [200, {}, { :id => 3, :fullname => "Tobias Fünke", :active => true }.to_json] }
518
+ stub.delete("/users/1") { |env| [404, {}, { :id => 1, :fullname => "Lindsay Fünke", :active => false }.to_json] }
519
+ stub.delete("/users/2") { |env| [411, {}, { :id => 2, :fullname => "Lindsay Fünke", :active => false }.to_json] }
520
+ stub.delete("/users/3") { |env| [422, {}, { :id => 3, :fullname => "Lindsay Fünke", :active => false }.to_json] }
521
+ end
522
+ end
523
+
524
+ spawn_model "Foo::User"
525
+ end
526
+
527
+ it "throws RecordNotFound exceptions for .destroy class method when a 404 is returned" do
528
+ expect { Foo::User.destroy_existing(1) }.to raise_error(Her::Errors::RecordNotFound, /Foo::User returned a 404/)
529
+ end
530
+
531
+ it "throws RecordInvalid exceptions for .destroy class method when a 409 is returned" do
532
+ expect { Foo::User.destroy_existing(2) }.to raise_error(Her::Errors::ResponseError, /Foo::User returned a 411/)
533
+ end
534
+
535
+ it "throws RecordInvalid exceptions for .destroy class method when a 422 is returned" do
536
+ expect { Foo::User.destroy_existing(3) }.to raise_error(Her::Errors::RecordInvalid, /Foo::User returned a 422/)
537
+ end
538
+
539
+ it "throws RecordNotFound exceptions for #destroy on an existing resource when a 404 is returned" do
540
+ @user = Foo::User.find(1)
541
+ expect { @user.destroy }.to raise_error(Her::Errors::RecordNotFound, /Foo::User returned a 404/)
542
+ end
543
+
544
+ it "throws RecordInvalid exceptions for #destroy on an existing resource when a 409 is returned" do
545
+ @user = Foo::User.find(2)
546
+ expect { @user.destroy }.to raise_error(Her::Errors::ResponseError, /Foo::User returned a 411/)
547
+ end
548
+
549
+ it "throws RecordInvalid exceptions for #destroy on an existing resource when a 422 is returned" do
550
+ @user = Foo::User.find(3)
551
+ expect { @user.destroy }.to raise_error(Her::Errors::RecordInvalid, /Foo::User returned a 422/)
552
+ end
553
+ end
554
+
555
+ describe "failed deletion requests (500-space responses)" do
556
+ before do
557
+ Her::API.setup :url => "https://api.example.com" do |builder|
558
+ builder.use Her::Middleware::FirstLevelParseJSON
559
+ builder.use Faraday::Request::UrlEncoded
560
+ builder.adapter :test do |stub|
561
+ stub.get("/users/1") { |env| [200, {}, { :id => 1, :fullname => "Tobias Fünke", :active => true }.to_json] }
562
+ stub.delete("/users/1") { |env| [500, {}, { :id => 1, :fullname => "Lindsay Fünke", :active => false }.to_json] }
563
+ end
564
+ end
565
+
566
+ spawn_model "Foo::User"
567
+ end
568
+
569
+ it "handles resource deletion through the .destroy class method" do
570
+ expect { Foo::User.destroy_existing(1) }.to raise_error(Her::Errors::RemoteServerError, /Foo::User returned a 500/)
571
+ end
572
+
573
+ it "handles resource deletion through #destroy on an existing resource" do
574
+ @user = Foo::User.find(1)
575
+ expect { @user.destroy }.to raise_error(Her::Errors::RemoteServerError, /Foo::User returned a 500/)
576
+ end
577
+ end
578
+ end
579
+
580
+ describe '.with_request_headers, #request_headers and #request_options' do
581
+ before do
582
+ @headers = {}
583
+ Her::API.setup :url => "https://api.example.com" do |builder|
584
+ builder.use Her::Middleware::FirstLevelParseJSON
585
+ builder.use Faraday::Request::UrlEncoded
586
+ builder.options[:timeout] = 100
587
+ builder.adapter :test do |stub|
588
+ stub.get("/users/1") { |env|
589
+ @headers = env[:request_headers]
590
+ @timeout = env[:request][:timeout]
591
+ [200, {}, { :id => 1, :fullname => "Tobias Fünke", :active => true }.to_json]
592
+ }
593
+ stub.get("/users?active=true") { |env|
594
+ @headers = env[:request_headers]
595
+ @timeout = env[:request][:timeout]
596
+ [200, {}, [{ :id => 1, :fullname => "Tobias Fünke", :active => true }].to_json]
597
+ }
598
+ stub.get("/users") { |env|
599
+ @headers = env[:request_headers]
600
+ @timeout = env[:request][:timeout]
601
+ [200, {}, [{ :id => 1, :fullname => "Tobias Fünke", :active => true }].to_json]
602
+ }
603
+ stub.put("/users/1") { |env|
604
+ @headers = env[:request_headers]
605
+ @timeout = env[:request][:timeout]
606
+ [200, {}, { :id => 1, :fullname => "Tobias Fünke", :active => true }.to_json]
607
+ }
608
+ stub.post("/users") { |env|
609
+ @headers = env[:request_headers]
610
+ @timeout = env[:request][:timeout]
611
+ [200, {}, { :id => 1, :fullname => "Tobias Fünke", :active => true }.to_json]
612
+ }
613
+ stub.delete("/users/1") { |env|
614
+ @headers = env[:request_headers]
615
+ @timeout = env[:request][:timeout]
616
+ [200, {}, nil]
617
+ }
618
+ end
619
+ end
620
+
621
+ spawn_model "Foo::User"
622
+ end
623
+
624
+ it "includes headers added via a relation chain" do
625
+ user = Foo::User.where(active: true).with_request_headers("Authorization" => "Basic FooBar").all.first
626
+ expect(@headers).to include "Authorization" => "Basic FooBar"
627
+ end
628
+
629
+ it "sets timeout added via a relation chain" do
630
+ user = Foo::User.where(active: true).with_request_options(timeout: 1).all.first
631
+ expect(@timeout).to eq 1
632
+ end
633
+
634
+ it "includes headers added via start of a relation chain" do
635
+ user = Foo::User.with_request_headers("Authorization" => "Basic FooBar").find(1)
636
+ expect(@headers).to include "Authorization" => "Basic FooBar"
637
+ end
638
+
639
+ it "sets timeout added via start of a relation chain" do
640
+ user = Foo::User.with_request_options(timeout: 2).find(1)
641
+ expect(@timeout).to eq 2
642
+ end
643
+
644
+ it "includes headers added via start of a relation chain when creating" do
645
+ user = Foo::User.with_request_headers("Authorization" => "Basic FooBar").create
646
+ expect(@headers).to include "Authorization" => "Basic FooBar"
647
+ end
648
+
649
+ it "includes timeout added via start of a relation chain when creating" do
650
+ user = Foo::User.with_request_options(timeout: 3).create
651
+ expect(@timeout).to eq 3
652
+ end
653
+
654
+ it "includes headers added to an instance when saving" do
655
+ user = Foo::User.find(1)
656
+ user.request_headers = { "Authorization" => "Basic FooBar" }
657
+ user.save
658
+ expect(@headers).to include "Authorization" => "Basic FooBar"
659
+ end
660
+
661
+ it "includes headers added to an instance when destroying" do
662
+ user = Foo::User.find(1)
663
+ user.request_headers = { "Authorization" => "Basic FooBar" }
664
+ user.destroy
665
+ expect(@headers).to include "Authorization" => "Basic FooBar"
666
+ end
667
+
668
+ it "includes headers added when destroying existing" do
669
+ Foo::User.destroy_existing(1, {}, { "Authorization" => "Basic FooBar" })
670
+ expect(@headers).to include "Authorization" => "Basic FooBar"
671
+ end
672
+
673
+ it "includes timeout added when destroying existing" do
674
+ Foo::User.destroy_existing(1, {}, {}, timeout: 4)
675
+ expect(@timeout).to eq 4
676
+ end
677
+
678
+ it "includes headers added via start of a relation chain when saving existing" do
679
+ user = Foo::User.save_existing(1, {}, { "Authorization" => "Basic FooBar" })
680
+ expect(@headers).to include "Authorization" => "Basic FooBar"
681
+ end
682
+
683
+ it "includes timeout added via start of a relation chain when saving existing" do
684
+ user = Foo::User.save_existing(1, {}, {}, timeout: 5)
685
+ expect(@timeout).to eq 5
686
+ end
687
+
688
+
689
+ it "overrides the timeout when added to an existing model's request_options" do
690
+ user = Foo::User.find(1)
691
+ user.request_options = { timeout: 6 }
692
+ user.save
693
+
694
+ expect(@timeout).to eq 6
695
+ end
696
+
697
+ it "overrides the timeout when added to a new model's request_options" do
698
+ user = Foo::User.new
699
+ user.request_options = { timeout: 7 }
700
+ user.save
701
+
702
+ expect(@timeout).to eq 7
703
+ end
704
+
705
+ it "overrides the timeout when added to destroying a model with request_options" do
706
+ user = Foo::User.find(1)
707
+ user.request_options = { timeout: 8 }
708
+ user.destroy
709
+
710
+ expect(@timeout).to eq 8
711
+ end
712
+
713
+ it "does not remove timeout set up by the api" do
714
+ user = Foo::User.find(1)
715
+ user.destroy
716
+
717
+ expect(@timeout).to eq 100
718
+ end
719
+
720
+ it "raises an exception when setting unimplemented options" do
721
+ user = Foo::User.find(1)
722
+ user.request_options = { not_implemented: 123, 500 => 'crazy' }
723
+
724
+ expect { user.save }.to raise_error "options not implemented: not_implemented, 500"
725
+ end
726
+ end
727
+
728
+ context 'customizing HTTP methods' do
729
+ before do
730
+ Her::API.setup :url => "https://api.example.com" do |builder|
731
+ builder.use Her::Middleware::FirstLevelParseJSON
732
+ builder.use Faraday::Request::UrlEncoded
733
+ end
734
+ end
735
+
736
+ context 'create' do
737
+ before do
738
+ Her::API.default_api.connection.adapter :test do |stub|
739
+ stub.put('/users') { |env| [200, {}, { :id => 1, :fullname => 'Tobias Fünke' }.to_json] }
740
+ end
741
+ spawn_model 'Foo::User' do
742
+ attributes :fullname, :email
743
+ method_for :create, 'PUT'
744
+ end
745
+ end
746
+
747
+ context 'for top-level class' do
748
+ it 'uses the custom method (PUT) instead of default method (POST)' do
749
+ user = Foo::User.new(:fullname => 'Tobias Fünke')
750
+ user.should be_new
751
+ user.save.should be true
752
+ end
753
+ end
754
+
755
+ context 'for children class' do
756
+ before do
757
+ class User < Foo::User; end
758
+ @spawned_models << :User
759
+ end
760
+
761
+ it 'uses the custom method (PUT) instead of default method (POST)' do
762
+ user = User.new(:fullname => 'Tobias Fünke')
763
+ user.should be_new
764
+ user.save.should be true
765
+ end
766
+ end
767
+ end
768
+
769
+ context 'update' do
770
+ before do
771
+ Her::API.default_api.connection.adapter :test do |stub|
772
+ stub.get('/users/1') { |env| [200, {}, { :id => 1, :fullname => 'Lindsay Fünke' }.to_json] }
773
+ stub.post('/users/1') { |env| [200, {}, { :id => 1, :fullname => 'Tobias Fünke' }.to_json] }
774
+ end
775
+
776
+ spawn_model 'Foo::User' do
777
+ attributes :fullname, :email
778
+ method_for :update, :post
779
+ end
780
+ end
781
+
782
+ it 'uses the custom method (POST) instead of default method (PUT)' do
783
+ user = Foo::User.find(1)
784
+ user.fullname.should eq 'Lindsay Fünke'
785
+ user.fullname = 'Toby Fünke'
786
+ user.save
787
+ user.fullname.should eq 'Tobias Fünke'
788
+ end
789
+ end
790
+ end
791
+ end