This repository was archived by the owner on Nov 9, 2017. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 58
Expand file tree
/
Copy pathactive_record_test.rb
More file actions
489 lines (394 loc) · 14.9 KB
/
active_record_test.rb
File metadata and controls
489 lines (394 loc) · 14.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
require 'test/unit'
require 'stringio'
# require a specific AR version.
version = ENV['AR_VERSION']
gem 'activerecord', "~> #{version}" if version
require 'active_record'
require 'active_record/version'
version = ActiveRecord::VERSION::STRING
warn "Using activerecord #{version}"
# replicate must be loaded after AR
require 'replicate'
# create the sqlite db on disk
dbfile = File.expand_path('../db', __FILE__)
File.unlink dbfile if File.exist?(dbfile)
ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :database => dbfile)
# load schema
ActiveRecord::Migration.verbose = false
ActiveRecord::Schema.define do
create_table "users", :force => true do |t|
t.string "login"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "profiles", :force => true do |t|
t.integer "user_id"
t.string "name"
t.string "homepage"
end
create_table "emails", :force => true do |t|
t.integer "user_id"
t.string "email"
t.datetime "created_at"
end
if version[0,3] > '2.2'
create_table "domains", :force => true do |t|
t.string "host"
end
create_table "web_pages", :force => true do |t|
t.string "url"
t.string "domain_host"
end
end
end
# models
class User < ActiveRecord::Base
has_one :profile, :dependent => :destroy
has_many :emails, :dependent => :destroy, :order => 'id'
replicate_natural_key :login
end
class Profile < ActiveRecord::Base
belongs_to :user
replicate_natural_key :user_id
end
class Email < ActiveRecord::Base
belongs_to :user
replicate_natural_key :user_id, :email
end
if version[0,3] > '2.2'
class WebPage < ActiveRecord::Base
belongs_to :domain, :foreign_key => 'domain_host', :primary_key => 'host'
end
class Domain < ActiveRecord::Base
replicate_natural_key :host
end
end
# The test case loads some fixture data once and uses transaction rollback to
# reset fixture state for each test's setup.
class ActiveRecordTest < Test::Unit::TestCase
def setup
self.class.fixtures
ActiveRecord::Base.connection.increment_open_transactions
ActiveRecord::Base.connection.begin_db_transaction
@rtomayko = User.find_by_login('rtomayko')
@kneath = User.find_by_login('kneath')
@tmm1 = User.find_by_login('tmm1')
User.replicate_associations = []
@dumper = Replicate::Dumper.new
@loader = Replicate::Loader.new
end
def teardown
ActiveRecord::Base.connection.rollback_db_transaction
ActiveRecord::Base.connection.decrement_open_transactions
end
def self.fixtures
return if @fixtures
@fixtures = true
user = User.create! :login => 'rtomayko'
user.create_profile :name => 'Ryan Tomayko', :homepage => 'http://tomayko.com'
user.emails.create! :email => 'ryan@github.com'
user.emails.create! :email => 'rtomayko@gmail.com'
user = User.create! :login => 'kneath'
user.create_profile :name => 'Kyle Neath', :homepage => 'http://warpspire.com'
user.emails.create! :email => 'kyle@github.com'
user = User.create! :login => 'tmm1'
user.create_profile :name => 'tmm1', :homepage => 'https://github.com/tmm1'
if defined?(Domain)
github = Domain.create! :host => 'github.com'
github_about_page = WebPage.create! :url => 'http://github.com/about', :domain => github
end
end
def test_extension_modules_loaded
assert User.respond_to?(:load_replicant)
assert User.new.respond_to?(:dump_replicant)
end
def test_auto_dumping_belongs_to_associations
objects = []
@dumper.listen { |type, id, attrs, obj| objects << [type, id, attrs, obj] }
rtomayko = User.find_by_login('rtomayko')
@dumper.dump rtomayko.profile
assert_equal 2, objects.size
type, id, attrs, obj = objects.shift
assert_equal 'User', type
assert_equal rtomayko.id, id
assert_equal 'rtomayko', attrs['login']
assert_equal rtomayko.created_at, attrs['created_at']
assert_equal rtomayko, obj
type, id, attrs, obj = objects.shift
assert_equal 'Profile', type
assert_equal rtomayko.profile.id, id
assert_equal 'Ryan Tomayko', attrs['name']
assert_equal rtomayko.profile, obj
end
def test_omit_dumping_of_attribute
objects = []
@dumper.listen { |type, id, attrs, obj| objects << [type, id, attrs, obj] }
User.replicate_omit_attributes :created_at
rtomayko = User.find_by_login('rtomayko')
@dumper.dump rtomayko
assert_equal 2, objects.size
type, id, attrs, obj = objects.shift
assert_equal nil, attrs['created_at']
end
def test_omit_dumping_of_association
objects = []
@dumper.listen { |type, id, attrs, obj| objects << [type, id, attrs, obj] }
User.replicate_omit_attributes :profile
rtomayko = User.find_by_login('rtomayko')
@dumper.dump rtomayko
assert_equal 1, objects.size
type, id, attrs, obj = objects.shift
assert_equal 'User', type
end
if ActiveRecord::VERSION::STRING[0, 3] > '2.2'
def test_dump_and_load_non_standard_foreign_key_association
objects = []
@dumper.listen { |type, id, attrs, obj| objects << [type, id, attrs, obj] }
github_about_page = WebPage.find_by_url('http://github.com/about')
assert_equal "github.com", github_about_page.domain.host
@dumper.dump github_about_page
WebPage.delete_all
Domain.delete_all
# load everything back up
objects.each { |type, id, attrs, obj| @loader.feed type, id, attrs }
github_about_page = WebPage.find_by_url('http://github.com/about')
assert_equal "github.com", github_about_page.domain_host
assert_equal "github.com", github_about_page.domain.host
end
end
def test_auto_dumping_has_one_associations
objects = []
@dumper.listen { |type, id, attrs, obj| objects << [type, id, attrs, obj] }
rtomayko = User.find_by_login('rtomayko')
@dumper.dump rtomayko
assert_equal 2, objects.size
type, id, attrs, obj = objects.shift
assert_equal 'User', type
assert_equal rtomayko.id, id
assert_equal 'rtomayko', attrs['login']
assert_equal rtomayko.created_at, attrs['created_at']
assert_equal rtomayko, obj
type, id, attrs, obj = objects.shift
assert_equal 'Profile', type
assert_equal rtomayko.profile.id, id
assert_equal 'Ryan Tomayko', attrs['name']
assert_equal [:id, 'User', rtomayko.id], attrs['user_id']
assert_equal rtomayko.profile, obj
end
def test_dumping_has_many_associations
objects = []
@dumper.listen { |type, id, attrs, obj| objects << [type, id, attrs, obj] }
User.replicate_associations :emails
rtomayko = User.find_by_login('rtomayko')
@dumper.dump rtomayko
assert_equal 4, objects.size
type, id, attrs, obj = objects.shift
assert_equal 'User', type
assert_equal rtomayko.id, id
assert_equal 'rtomayko', attrs['login']
assert_equal rtomayko.created_at, attrs['created_at']
assert_equal rtomayko, obj
type, id, attrs, obj = objects.shift
assert_equal 'Profile', type
assert_equal rtomayko.profile.id, id
assert_equal 'Ryan Tomayko', attrs['name']
assert_equal rtomayko.profile, obj
type, id, attrs, obj = objects.shift
assert_equal 'Email', type
assert_equal 'ryan@github.com', attrs['email']
assert_equal [:id, 'User', rtomayko.id], attrs['user_id']
assert_equal rtomayko.emails.first, obj
type, id, attrs, obj = objects.shift
assert_equal 'Email', type
assert_equal 'rtomayko@gmail.com', attrs['email']
assert_equal [:id, 'User', rtomayko.id], attrs['user_id']
assert_equal rtomayko.emails.last, obj
end
def test_loading_everything
objects = []
@dumper.listen { |type, id, attrs, obj| objects << [type, id, attrs, obj] }
# dump all users and associated objects and destroy
User.replicate_associations :emails
dumped_users = {}
%w[rtomayko kneath tmm1].each do |login|
user = User.find_by_login(login)
@dumper.dump user
user.destroy
dumped_users[login] = user
end
assert_equal 9, objects.size
# insert another record to ensure id changes for loaded records
sr = User.create!(:login => 'sr')
sr.create_profile :name => 'Simon Rozet'
sr.emails.create :email => 'sr@github.com'
# load everything back up
objects.each { |type, id, attrs, obj| @loader.feed type, id, attrs }
# verify attributes are set perfectly again
user = User.find_by_login('rtomayko')
assert_equal 'rtomayko', user.login
assert_equal dumped_users['rtomayko'].created_at, user.created_at
assert_equal dumped_users['rtomayko'].updated_at, user.updated_at
assert_equal 'Ryan Tomayko', user.profile.name
assert_equal 2, user.emails.size
# make sure everything was recreated
%w[rtomayko kneath tmm1].each do |login|
user = User.find_by_login(login)
assert_not_nil user
assert_not_nil user.profile
assert !user.emails.empty?, "#{login} has no emails" if login != 'tmm1'
end
end
def test_only_find_natural_key_on_belongs_to
objects = []
@dumper.listen { |type, id, attrs, obj| objects << [type, id, attrs, obj] }
assert_equal(3, Profile.count)
assert_equal(3, User.count)
%w[rtomayko kneath tmm1].each do |login|
user = User.find_by_login(login)
@dumper.dump user
end
assert_equal 6, objects.size
User.find_by_login("kneath").profile.destroy
User.delete_all
assert_equal(0, User.count)
assert_equal(2, Profile.count)
# We only want to reattach profiles, not recreate them
Profile.replicate_natural_key :user_id, :only_find => true
# load everything back up
objects.each { |type, id, attrs, obj| @loader.feed type, id, attrs }
assert_equal(3, User.count)
assert_equal(2, Profile.count)
assert_nil User.find_by_login("kneath").profile
assert_not_nil User.find_by_login("rtomayko").profile
assert_not_nil User.find_by_login("tmm1").profile
end
def test_only_find_natural_key_on_has_many
objects = []
@dumper.listen { |type, id, attrs, obj| objects << [type, id, attrs, obj] }
User.replicate_associations :emails
Email.replicate_natural_key = []
Email.replicate_natural_key :email, :only_find => true
rtomayko = User.find_by_login('rtomayko')
@dumper.dump rtomayko
assert_equal 4, objects.size
emails = rtomayko.emails
Email.destroy_all
objects.each { |type, id, attrs, obj| @loader.feed type, id, attrs }
assert_equal(0, rtomayko.emails.count)
email = Email.create!(:email => emails.first.email)
assert_equal(0, rtomayko.emails.count)
objects.each { |type, id, attrs, obj| @loader.feed type, id, attrs }
assert_equal(1, rtomayko.emails.count)
assert_equal(email.email, rtomayko.reload.emails.first.email)
end
def test_loading_with_existing_records
objects = []
@dumper.listen { |type, id, attrs, obj| objects << [type, id, attrs, obj] }
# dump all users and associated objects and destroy
User.replicate_associations :emails
dumped_users = {}
%w[rtomayko kneath tmm1].each do |login|
user = User.find_by_login(login)
user.profile.update_attribute :name, 'CHANGED'
@dumper.dump user
dumped_users[login] = user
end
assert_equal 9, objects.size
# load everything back up
objects.each { |type, id, attrs, obj| @loader.feed type, id, attrs }
# ensure additional objects were not created
assert_equal 3, User.count
# verify attributes are set perfectly again
user = User.find_by_login('rtomayko')
assert_equal 'rtomayko', user.login
assert_equal dumped_users['rtomayko'].created_at, user.created_at
assert_equal dumped_users['rtomayko'].updated_at, user.updated_at
assert_equal 'CHANGED', user.profile.name
assert_equal 2, user.emails.size
# make sure everything was recreated
%w[rtomayko kneath tmm1].each do |login|
user = User.find_by_login(login)
assert_not_nil user
assert_not_nil user.profile
assert_equal 'CHANGED', user.profile.name
assert !user.emails.empty?, "#{login} has no emails" if login != 'tmm1'
end
end
def test_loading_with_replicating_id
objects = []
@dumper.listen do |type, id, attrs, obj|
objects << [type, id, attrs, obj] if type == 'User'
end
dumped_users = {}
%w[rtomayko kneath tmm1].each do |login|
user = User.find_by_login(login)
@dumper.dump user
dumped_users[login] = user
end
assert_equal 3, objects.size
User.destroy_all
User.replicate_id = false
# load everything back up
objects.each { |type, id, attrs, obj| User.load_replicant type, id, attrs }
user = User.find_by_login('rtomayko')
assert_not_equal dumped_users['rtomayko'].id, user.id
User.destroy_all
User.replicate_id = true
# load everything back up
objects.each { |type, id, attrs, obj| User.load_replicant type, id, attrs }
user = User.find_by_login('rtomayko')
assert_equal dumped_users['rtomayko'].id, user.id
end
def test_loader_saves_without_validations
# note when a record is saved with validations
ran_validations = false
User.class_eval { validate { ran_validations = true } }
# check our assumptions
user = User.create(:login => 'defunkt')
assert ran_validations, "should run validations here"
ran_validations = false
# load one and verify validations are not run
user = nil
@loader.listen { |type, id, attrs, obj| user = obj }
@loader.feed 'User', 1, 'login' => 'rtomayko'
assert_not_nil user
assert !ran_validations, 'validations should not run on save'
end
def test_loader_saves_without_callbacks
# note when a record is saved with callbacks
callbacks = false
User.class_eval { after_save { callbacks = true } }
# check our assumptions
user = User.create(:login => 'defunkt')
assert callbacks, "should run callbacks here"
callbacks = false
# load one and verify validations are not run
user = nil
@loader.listen { |type, id, attrs, obj| user = obj }
@loader.feed 'User', 1, 'login' => 'rtomayko'
assert_not_nil user
assert !callbacks, 'callbacks should not run on save'
end
def test_loader_saves_without_updating_created_at_timestamp
timestamp = Time.at((Time.now - (24 * 60 * 60)).to_i)
user = nil
@loader.listen { |type, id, attrs, obj| user = obj }
@loader.feed 'User', 23, 'login' => 'brianmario', 'created_at' => timestamp
assert_equal timestamp, user.created_at
user = User.find(user.id)
assert_equal timestamp, user.created_at
end
def test_loader_saves_without_updating_updated_at_timestamp
timestamp = Time.at((Time.now - (24 * 60 * 60)).to_i)
user = nil
@loader.listen { |type, id, attrs, obj| user = obj }
@loader.feed 'User', 29, 'login' => 'rtomayko', 'updated_at' => timestamp
assert_equal timestamp, user.updated_at
user = User.find(user.id)
assert_equal timestamp, user.updated_at
end
def test_enabling_active_record_query_cache
ActiveRecord::Base.connection.enable_query_cache!
ActiveRecord::Base.connection.disable_query_cache!
end
end