remotable 0.1.2 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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