remotable 0.1.2 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,197 @@
1
+ require "test_helper"
2
+ require "remotable"
3
+ require "support/bespoke"
4
+ require "rr"
5
+
6
+
7
+ class BespokeTest < ActiveSupport::TestCase
8
+ include RR::Adapters::TestUnit
9
+
10
+
11
+ teardown do
12
+ def model.new_resource
13
+ BespokeResource.new
14
+ end
15
+ def model.find_by(remote_attr, value)
16
+ nil
17
+ end
18
+ end
19
+
20
+
21
+
22
+ # ========================================================================= #
23
+ # Finding #
24
+ # ========================================================================= #
25
+
26
+ test "should be able to find resources by different attributes" do
27
+ new_tenant_slug = "not_found"
28
+ assert_equal 0, BespokeTenant.where(:slug => new_tenant_slug).count,
29
+ "There's not supposed to be a BespokeTenant with the slug #{new_tenant_slug}."
30
+
31
+ def model.find_by(attribute, value)
32
+ BespokeResource.new(:slug => value)
33
+ end
34
+
35
+ new_tenant = BespokeTenant.find_by_slug(new_tenant_slug)
36
+ assert_not_nil new_tenant, "A remote @tenant was not found with the slug #{new_tenant_slug.inspect}"
37
+ end
38
+
39
+ test "should create a record locally when fetching a new remote resource" do
40
+ new_tenant_slug = "17"
41
+ assert_equal 0, BespokeTenant.where(:slug => new_tenant_slug).count,
42
+ "There's not supposed to be a BespokeTenant with the slug #{new_tenant_slug}."
43
+
44
+ def model.find_by(attribute, value)
45
+ BespokeResource.new(:slug => value)
46
+ end
47
+
48
+ assert_difference "BespokeTenant.count", +1 do
49
+ new_tenant = BespokeTenant.find_by_slug(new_tenant_slug)
50
+ assert_not_nil new_tenant, "A remote tenant was not found with the id #{new_tenant_slug.inspect}"
51
+ end
52
+ end
53
+
54
+ test "if a resource is neither local nor remote, raise an exception with the bang method" do
55
+ new_tenant_slug = "not_found3"
56
+ assert_equal 0, BespokeTenant.where(:slug => new_tenant_slug).count,
57
+ "There's not supposed to be a BespokeTenant with the slug #{new_tenant_slug}."
58
+
59
+ assert_raises ActiveRecord::RecordNotFound do
60
+ BespokeTenant.find_by_slug!(new_tenant_slug)
61
+ end
62
+ end
63
+
64
+
65
+
66
+ # ========================================================================= #
67
+ # Updating #
68
+ # ========================================================================= #
69
+
70
+ test "should update a record remotely when updating one locally" do
71
+ @tenant = Factory(:bespoke_tenant)
72
+ new_name = "Totally Wonky"
73
+
74
+ # RemoteTenant.run_simulation do |s|
75
+ # s.show(@tenant.remote_id, {
76
+ # :id => @tenant.remote_id,
77
+ # :slug => @tenant.slug,
78
+ # :church_name => @tenant.name
79
+ # })
80
+ # s.update(@tenant.remote_id)
81
+ #
82
+ # @tenant.nosync = false
83
+ # @tenant.name = "Totally Wonky"
84
+ # assert_equal true, @tenant.any_remote_changes?
85
+ #
86
+ # @tenant.save!
87
+ #
88
+ # pending "Not sure how to test that an update happened"
89
+ # end
90
+ end
91
+
92
+ test "should fail to update a record locally when failing to update one remotely" do
93
+ @tenant = Factory(:bespoke_tenant, :nosync => false)
94
+
95
+ def model.find_by(attribute, value)
96
+ BespokeResource.new(attribute => value)
97
+ end
98
+
99
+ def resource.save
100
+ false
101
+ end
102
+
103
+ def resource.errors
104
+ {:name => ["is already taken"]}
105
+ end
106
+
107
+ @tenant.name = "Totally Wonky"
108
+ assert_raises(ActiveRecord::RecordInvalid) do
109
+ @tenant.save!
110
+ end
111
+ assert_equal ["is already taken"], @tenant.errors[:name]
112
+ end
113
+
114
+
115
+
116
+
117
+ # ========================================================================= #
118
+ # Creating #
119
+ # ========================================================================= #
120
+
121
+ test "should create a record remotely when creating one locally" do
122
+ @tenant = BespokeTenant.new({
123
+ :slug => "brand_new",
124
+ :name => "Brand New"
125
+ })
126
+
127
+ assert_nil @tenant.remote_resource
128
+ @tenant.save!
129
+ assert_not_nil @tenant.remote_resource
130
+ end
131
+
132
+ test "should fail to create a record locally when failing to create one remotely" do
133
+ @tenant = BespokeTenant.new({
134
+ :slug => "brand_new",
135
+ :name => "Brand New"
136
+ })
137
+
138
+ def model.new_resource
139
+ resource = BespokeResource.new
140
+ def resource.save; false; end
141
+ def resource.errors; {:name => ["is already taken"]}; end
142
+ resource
143
+ end
144
+
145
+ assert_raises(ActiveRecord::RecordInvalid) do
146
+ @tenant.save!
147
+ end
148
+
149
+ assert_equal ["is already taken"], @tenant.errors[:name]
150
+ end
151
+
152
+
153
+
154
+ # ========================================================================= #
155
+ # Destroying #
156
+ # ========================================================================= #
157
+
158
+ test "should destroy a record remotely when destroying one locally" do
159
+ @tenant = Factory(:bespoke_tenant, :nosync => false)
160
+ mock(resource).destroy { true }
161
+ @tenant.destroy
162
+ end
163
+
164
+ test "should fail to destroy a record locally when failing to destroy one remotely" do
165
+ @tenant = Factory(:bespoke_tenant, :nosync => false)
166
+ mock(resource).destroy { raise StandardError }
167
+ assert_raises(StandardError) do
168
+ @tenant.destroy
169
+ end
170
+ end
171
+
172
+ test "should delete a local record when a remote record has been deleted" do
173
+ @tenant = Factory(:bespoke_tenant, :expires_at => 1.year.ago)
174
+
175
+ def model.find_by(remote_attr, value)
176
+ nil
177
+ end
178
+
179
+ assert_difference "BespokeTenant.count", -1 do
180
+ @tenant = BespokeTenant.find_by_slug(@tenant.slug)
181
+ assert_equal nil, @tenant
182
+ end
183
+ end
184
+
185
+
186
+
187
+ private
188
+
189
+ def model
190
+ BespokeTenant.remote_model
191
+ end
192
+
193
+ def resource
194
+ @tenant.remote_resource
195
+ end
196
+
197
+ end
@@ -5,3 +5,11 @@ Factory.define :tenant do |f|
5
5
  f.expires_at 100.years.from_now
6
6
  f.nosync true
7
7
  end
8
+
9
+ Factory.define :bespoke_tenant do |f|
10
+ f.sequence(:slug) { |n| "test#{n}" }
11
+ f.name "Test"
12
+ f.sequence(:remote_id)
13
+ f.expires_at 100.years.from_now
14
+ f.nosync true
15
+ end
@@ -1,7 +1,6 @@
1
1
  require "test_helper"
2
2
  require "remotable"
3
- require "support/active_resource"
4
- require "active_resource_simulator"
3
+ require "support/bespoke"
5
4
 
6
5
 
7
6
  class RemotableTest < ActiveSupport::TestCase
@@ -19,290 +18,83 @@ class RemotableTest < ActiveSupport::TestCase
19
18
  end
20
19
 
21
20
  test "should be able to generate paths for with different attributes" do
22
- assert_equal "/api/accounts/by_slug/value.json", RemoteTenant.path_for(:slug, "value")
23
- assert_equal "/api/accounts/by_nombre/value.json", RemoteTenant.path_for(:name, "value")
21
+ assert_equal "by_slug/value", Tenant.remote_path_for(:slug, "value")
22
+ assert_equal "by_nombre/value", Tenant.remote_path_for(:name, "value")
24
23
  end
25
24
 
26
-
27
-
28
- test "should be able to find resources by different attributes" do
29
- new_tenant_slug = "not_found"
30
-
31
- assert_equal 0, Tenant.where(:slug => new_tenant_slug).count,
32
- "There's not supposed to be a Tenant with the slug #{new_tenant_slug}."
33
-
34
- assert_difference "Tenant.count", +1 do
35
- RemoteTenant.run_simulation do |s|
36
- s.show(nil, {
37
- :id => 46,
38
- :slug => new_tenant_slug,
39
- :church_name => "Not Found"
40
- }, :path => "/api/accounts/by_slug/#{new_tenant_slug}.json")
41
-
42
- new_tenant = Tenant.find_by_slug(new_tenant_slug)
43
- assert_not_nil new_tenant, "A remote tenant was not found with the slug #{new_tenant_slug.inspect}"
44
- end
45
- end
46
- end
47
-
48
- test "should be able to find resources with the bang method" do
49
- new_tenant_slug = "not_found2"
50
-
51
- assert_equal 0, Tenant.where(:slug => new_tenant_slug).count,
52
- "There's not supposed to be a Tenant with the slug #{new_tenant_slug}."
53
-
54
- assert_difference "Tenant.count", +1 do
55
- RemoteTenant.run_simulation do |s|
56
- s.show(nil, {
57
- :id => 46,
58
- :slug => new_tenant_slug,
59
- :church_name => "Not Found"
60
- }, :path => "/api/accounts/by_slug/#{new_tenant_slug}.json")
61
-
62
- new_tenant = Tenant.find_by_slug!(new_tenant_slug)
63
- assert_not_nil new_tenant, "A remote tenant was not found with the slug #{new_tenant_slug.inspect}"
64
- end
65
- end
66
- end
67
-
68
- test "if a resource is neither local nor remote, raise an exception with the bang method" do
69
- new_tenant_slug = "not_found3"
70
-
71
- assert_equal 0, Tenant.where(:slug => new_tenant_slug).count,
72
- "There's not supposed to be a Tenant with the slug #{new_tenant_slug}."
73
-
74
- RemoteTenant.run_simulation do |s|
75
- s.show(nil, nil, :status => 404, :path => "/api/accounts/by_slug/#{new_tenant_slug}.json")
76
-
77
- assert_raises ActiveRecord::RecordNotFound do
78
- Tenant.find_by_slug!(new_tenant_slug)
79
- end
80
- end
25
+ test "should be able to generate paths for composite keys" do
26
+ assert_equal "groups/5/tenants/test", RemoteWithCompositeKey.remote_path_for([:group_id, :slug], 5, "test")
81
27
  end
82
28
 
83
29
 
84
30
 
85
- test "should be able to find resources by different attributes and specify a path" do
86
- new_tenant_name = "JohnnyG"
87
-
88
- assert_equal 0, Tenant.where(:name => new_tenant_name).count,
89
- "There's not supposed to be a Tenant with the name #{new_tenant_name}."
90
-
91
- assert_difference "Tenant.count", +1 do
92
- RemoteTenant.run_simulation do |s|
93
- s.show(nil, {
94
- :id => 46,
95
- :slug => "not_found",
96
- :church_name => new_tenant_name
97
- }, :path => "/api/accounts/by_nombre/#{new_tenant_name}.json")
98
-
99
- new_tenant = Tenant.find_by_name(new_tenant_name)
100
- assert_not_nil new_tenant, "A remote tenant was not found with the name #{new_tenant_name.inspect}"
101
- end
102
- end
31
+ test "should support temporary models" do
32
+ assert_nil BespokeTenant.find_by_slug("404")
33
+ assert_not_nil BespokeTenant.with_remote_model(BespokeModel2.new) { BespokeTenant.find_by_slug("404") }
34
+ assert_nil BespokeTenant.find_by_slug("405")
103
35
  end
104
36
 
105
-
106
-
107
- test "should create a record locally when fetching a new remote resource" do
108
- new_tenant_id = 17
109
-
110
- assert_equal 0, Tenant.where(:remote_id => new_tenant_id).count,
111
- "There's not supposed to be a Tenant with the id #{new_tenant_id}."
112
-
113
- assert_difference "Tenant.count", +1 do
114
- RemoteTenant.run_simulation do |s|
115
- s.show(new_tenant_id, {
116
- :id => new_tenant_id,
117
- :slug => "not_found",
118
- :church_name => "Not Found"
119
- })
120
-
121
- new_tenant = Tenant.find_by_remote_id(new_tenant_id)
122
- assert_not_nil new_tenant, "A remote tenant was not found with the id #{new_tenant_id.inspect}"
123
- end
124
- end
37
+ test "should support temporary models and chainable syntax" do
38
+ assert_nil BespokeTenant.find_by_slug("404")
39
+ assert_not_nil BespokeTenant.with_remote_model(BespokeModel2.new).find_by_slug("404")
40
+ assert_nil BespokeTenant.find_by_slug("405")
125
41
  end
126
42
 
127
43
 
128
44
 
129
- test "should not fetch a remote record when a local record is not expired" do
130
- tenant = Factory(:tenant, :expires_at => 100.years.from_now)
131
- unexpected_name = "Totally Wonky"
132
-
133
- RemoteTenant.run_simulation do |s|
134
- s.show(tenant.remote_id, {
135
- :id => tenant.remote_id,
136
- :slug => tenant.slug,
137
- :church_name => unexpected_name
138
- })
139
-
140
- tenant = Tenant.find_by_remote_id(tenant.remote_id)
141
- assert_not_equal unexpected_name, tenant.name
142
- end
143
- end
144
-
145
-
45
+ # ========================================================================= #
46
+ # Finders #
47
+ # ========================================================================= #
146
48
 
147
- test "should fetch a remote record when a local record is expired" do
148
- tenant = Factory(:tenant, :expires_at => 1.year.ago)
149
- unexpected_name = "Totally Wonky"
150
-
151
- RemoteTenant.run_simulation do |s|
152
- s.show(tenant.remote_id, {
153
- :id => tenant.remote_id,
154
- :slug => tenant.slug,
155
- :church_name => unexpected_name
156
- })
157
-
158
- tenant = Tenant.find_by_remote_id(tenant.remote_id)
159
- assert_equal unexpected_name, tenant.name
160
- end
49
+ test "should create expected finders" do
50
+ assert_equal true, Tenant.respond_to?(:find_by_name)
51
+ assert_equal true, Tenant.respond_to?(:find_by_slug)
52
+ assert_equal true, RemoteWithoutKey.respond_to?(:find_by_id)
53
+ assert_equal true, RemoteWithCompositeKey.respond_to?(:find_by_group_id_and_slug)
161
54
  end
162
55
 
163
-
164
-
165
- test "should delete a local record when a remote record has been deleted" do
166
- tenant = Factory(:tenant, :expires_at => 1.year.ago)
167
-
168
- assert_difference "Tenant.count", -1 do
169
- RemoteTenant.run_simulation do |s|
170
- s.show(tenant.remote_id, nil, :status => 404)
171
-
172
- tenant = Tenant.find_by_remote_id(tenant.remote_id)
173
- assert_equal nil, tenant
174
- end
175
- end
56
+ test "should recognize a finder method with a single key" do
57
+ method_details = RemoteWithKey.recognize_remote_finder_method(:find_by_slug)
58
+ assert_not_equal false, method_details
59
+ assert_equal :slug, method_details[:remote_key]
176
60
  end
177
61
 
178
-
179
-
180
- test "should update a record remotely when updating one locally" do
181
- tenant = Factory(:tenant)
182
- new_name = "Totally Wonky"
183
-
184
- RemoteTenant.run_simulation do |s|
185
- s.show(tenant.remote_id, {
186
- :id => tenant.remote_id,
187
- :slug => tenant.slug,
188
- :church_name => tenant.name
189
- })
190
- s.update(tenant.remote_id)
191
-
192
- tenant.nosync = false
193
- tenant.name = "Totally Wonky"
194
- assert_equal true, tenant.any_remote_changes?
195
-
196
- tenant.save!
197
-
198
- pending "Not sure how to test that an update happened"
199
- end
62
+ test "should recognize a finder method with a composite key" do
63
+ method_details = RemoteWithCompositeKey.recognize_remote_finder_method(:find_by_group_id_and_slug)
64
+ assert_not_equal false, method_details
65
+ assert_equal [:group_id, :slug], method_details[:remote_key]
200
66
  end
201
67
 
202
- test "should fail to update a record locally when failing to update one remotely" do
203
- tenant = Factory(:tenant)
204
- new_name = "Totally Wonky"
205
-
206
- RemoteTenant.run_simulation do |s|
207
- s.show(tenant.remote_id, {
208
- :id => tenant.remote_id,
209
- :slug => tenant.slug,
210
- :church_name => tenant.name
211
- })
212
- s.update(tenant.remote_id, :status => 422, :body => {
213
- :errors => {:church_name => ["is already taken"]}
214
- })
215
-
216
- tenant.nosync = false
217
- tenant.name = new_name
218
- assert_raises(ActiveRecord::RecordInvalid) do
219
- tenant.save!
220
- end
221
- assert_equal ["is already taken"], tenant.errors[:name]
222
- end
223
- end
224
68
 
225
69
 
70
+ # ========================================================================= #
71
+ # Validating Models #
72
+ # ========================================================================= #
226
73
 
227
- test "should create a record remotely when creating one locally" do
228
- tenant = Tenant.new({
229
- :slug => "brand_new",
230
- :name => "Brand New"
231
- })
232
-
233
- RemoteTenant.run_simulation do |s|
234
- s.create({
235
- :id => 143,
236
- :slug => tenant.slug,
237
- :church_name => tenant.name
238
- })
239
-
240
- tenant.save!
241
-
242
- assert_equal true, tenant.remote_resource.persisted?
243
- end
74
+ test "should raise an exception if a remote model does not respond to all class methods" do
75
+ class Example1 < ActiveRecord::Base; set_table_name "tenants"; end
76
+ class RemoteModel1; def self.find_by(*args); end; end
77
+ assert_raise(Remotable::InvalidRemoteModel) { Example1.remote_model RemoteModel1 }
244
78
  end
245
79
 
246
- test "should fail to create a record locally when failing to create one remotely" do
247
- tenant = Tenant.new({
248
- :slug => "brand_new",
249
- :name => "Brand New"
250
- })
251
-
252
- RemoteTenant.run_simulation do |s|
253
- s.create({
254
- :errors => {
255
- :what => ["ever"],
256
- :church_name => ["is already taken"]}
257
- }, :status => 422)
258
-
259
- assert_raises(ActiveRecord::RecordInvalid) do
260
- tenant.save!
261
- end
262
-
263
- assert_equal ["is already taken"], tenant.errors[:name]
264
- end
80
+ test "should raise an exception if a remote resource does not respond to all instance methods" do
81
+ class Example2 < ActiveRecord::Base; set_table_name "tenants"; end
82
+ class RemoteModel2; def self.new_resource; Object.new; end; end
83
+ assert_raise(Remotable::InvalidRemoteModel) { Example2.remote_model RemoteModel2 }
265
84
  end
266
85
 
267
-
268
-
269
- test "should destroy a record remotely when destroying one locally" do
270
- tenant = Factory(:tenant)
271
-
272
- RemoteTenant.run_simulation do |s|
273
- s.show(tenant.remote_id, {
274
- :id => tenant.remote_id,
275
- :slug => tenant.slug,
276
- :church_name => tenant.name
277
- })
278
- s.destroy(tenant.remote_id)
279
-
280
- tenant.nosync = false
281
- tenant.destroy
282
-
283
- pending # how do I check for this?
86
+ test "should not raise an exception if remote models are not being validated" do
87
+ Remotable.without_validation do
88
+ class Example4 < ActiveRecord::Base; set_table_name "tenants"; end
89
+ class RemoteModel4; def self.find_by(*args); end; end
90
+ assert_nothing_raised { Example4.remote_model RemoteModel4 }
284
91
  end
285
92
  end
286
93
 
287
- test "should fail to destroy a record locally when failing to destroy one remotely" do
288
- tenant = Factory(:tenant)
289
-
290
- RemoteTenant.run_simulation do |s|
291
- s.show(tenant.remote_id, {
292
- :id => tenant.remote_id,
293
- :slug => tenant.slug,
294
- :church_name => tenant.name
295
- })
296
-
297
- s.destroy(tenant.remote_id, :status => 500)
298
-
299
- tenant.nosync = false
300
- assert_raises(ActiveResource::ServerError) do
301
- tenant.destroy
302
- end
303
- end
94
+ test "should not raise an exception if a remote model responds to all required methods" do
95
+ class Example3 < ActiveRecord::Base; set_table_name "tenants"; end
96
+ assert_nothing_raised { Example3.remote_model BespokeModel.new }
304
97
  end
305
98
 
306
99
 
307
-
308
100
  end