~michaelforrest/use-case-mapper/trunk

« back to all changes in this revision

Viewing changes to vendor/rails/activerecord/test/cases/autosave_association_test.rb

  • Committer: Richard Lee (Canonical)
  • Date: 2010-10-15 15:17:58 UTC
  • mfrom: (190.1.3 use-case-mapper)
  • Revision ID: richard.lee@canonical.com-20101015151758-wcvmfxrexsongf9d
Merge

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
require 'cases/helper'
2
 
require 'models/bird'
3
 
require 'models/company'
4
 
require 'models/customer'
5
 
require 'models/developer'
6
 
require 'models/order'
7
 
require 'models/parrot'
8
 
require 'models/person'
9
 
require 'models/pirate'
10
 
require 'models/post'
11
 
require 'models/reader'
12
 
require 'models/ship'
13
 
require 'models/ship_part'
14
 
require 'models/treasure'
15
 
 
16
 
class TestAutosaveAssociationsInGeneral < ActiveRecord::TestCase
17
 
  def test_autosave_should_be_a_valid_option_for_has_one
18
 
    assert base.valid_keys_for_has_one_association.include?(:autosave)
19
 
  end
20
 
 
21
 
  def test_autosave_should_be_a_valid_option_for_belongs_to
22
 
    assert base.valid_keys_for_belongs_to_association.include?(:autosave)
23
 
  end
24
 
 
25
 
  def test_autosave_should_be_a_valid_option_for_has_many
26
 
    assert base.valid_keys_for_has_many_association.include?(:autosave)
27
 
  end
28
 
 
29
 
  def test_autosave_should_be_a_valid_option_for_has_and_belongs_to_many
30
 
    assert base.valid_keys_for_has_and_belongs_to_many_association.include?(:autosave)
31
 
  end
32
 
 
33
 
  private
34
 
 
35
 
  def base
36
 
    ActiveRecord::Base
37
 
  end
38
 
end
39
 
 
40
 
class TestDefaultAutosaveAssociationOnAHasOneAssociation < ActiveRecord::TestCase
41
 
  def test_should_save_parent_but_not_invalid_child
42
 
    firm = Firm.new(:name => 'GlobalMegaCorp')
43
 
    assert firm.valid?
44
 
 
45
 
    firm.build_account_using_primary_key
46
 
    assert !firm.build_account_using_primary_key.valid?
47
 
 
48
 
    assert firm.save
49
 
    assert firm.account_using_primary_key.new_record?
50
 
  end
51
 
 
52
 
  def test_save_fails_for_invalid_has_one
53
 
    firm = Firm.find(:first)
54
 
    assert firm.valid?
55
 
 
56
 
    firm.account = Account.new
57
 
 
58
 
    assert !firm.account.valid?
59
 
    assert !firm.valid?
60
 
    assert !firm.save
61
 
    assert_equal "is invalid", firm.errors.on("account")
62
 
  end
63
 
 
64
 
  def test_save_succeeds_for_invalid_has_one_with_validate_false
65
 
    firm = Firm.find(:first)
66
 
    assert firm.valid?
67
 
 
68
 
    firm.unvalidated_account = Account.new
69
 
 
70
 
    assert !firm.unvalidated_account.valid?
71
 
    assert firm.valid?
72
 
    assert firm.save
73
 
  end
74
 
 
75
 
  def test_build_before_child_saved
76
 
    firm = Firm.find(1)
77
 
 
78
 
    account = firm.account.build("credit_limit" => 1000)
79
 
    assert_equal account, firm.account
80
 
    assert account.new_record?
81
 
    assert firm.save
82
 
    assert_equal account, firm.account
83
 
    assert !account.new_record?
84
 
  end
85
 
 
86
 
  def test_build_before_either_saved
87
 
    firm = Firm.new("name" => "GlobalMegaCorp")
88
 
 
89
 
    firm.account = account = Account.new("credit_limit" => 1000)
90
 
    assert_equal account, firm.account
91
 
    assert account.new_record?
92
 
    assert firm.save
93
 
    assert_equal account, firm.account
94
 
    assert !account.new_record?
95
 
  end
96
 
 
97
 
  def test_assignment_before_parent_saved
98
 
    firm = Firm.new("name" => "GlobalMegaCorp")
99
 
    firm.account = a = Account.find(1)
100
 
    assert firm.new_record?
101
 
    assert_equal a, firm.account
102
 
    assert firm.save
103
 
    assert_equal a, firm.account
104
 
    assert_equal a, firm.account(true)
105
 
  end
106
 
 
107
 
  def test_assignment_before_either_saved
108
 
    firm = Firm.new("name" => "GlobalMegaCorp")
109
 
    firm.account = a = Account.new("credit_limit" => 1000)
110
 
    assert firm.new_record?
111
 
    assert a.new_record?
112
 
    assert_equal a, firm.account
113
 
    assert firm.save
114
 
    assert !firm.new_record?
115
 
    assert !a.new_record?
116
 
    assert_equal a, firm.account
117
 
    assert_equal a, firm.account(true)
118
 
  end
119
 
 
120
 
  def test_not_resaved_when_unchanged
121
 
    firm = Firm.find(:first, :include => :account)
122
 
    firm.name += '-changed'
123
 
    assert_queries(1) { firm.save! }
124
 
 
125
 
    firm = Firm.find(:first)
126
 
    firm.account = Account.find(:first)
127
 
    assert_queries(Firm.partial_updates? ? 0 : 1) { firm.save! }
128
 
 
129
 
    firm = Firm.find(:first).clone
130
 
    firm.account = Account.find(:first)
131
 
    assert_queries(2) { firm.save! }
132
 
 
133
 
    firm = Firm.find(:first).clone
134
 
    firm.account = Account.find(:first).clone
135
 
    assert_queries(2) { firm.save! }
136
 
  end
137
 
end
138
 
 
139
 
class TestDefaultAutosaveAssociationOnABelongsToAssociation < ActiveRecord::TestCase
140
 
  def test_should_save_parent_but_not_invalid_child
141
 
    client = Client.new(:name => 'Joe (the Plumber)')
142
 
    assert client.valid?
143
 
 
144
 
    client.build_firm
145
 
    assert !client.firm.valid?
146
 
 
147
 
    assert client.save
148
 
    assert client.firm.new_record?
149
 
  end
150
 
 
151
 
  def test_save_fails_for_invalid_belongs_to
152
 
    assert log = AuditLog.create(:developer_id => 0, :message => "")
153
 
 
154
 
    log.developer = Developer.new
155
 
    assert !log.developer.valid?
156
 
    assert !log.valid?
157
 
    assert !log.save
158
 
    assert_equal "is invalid", log.errors.on("developer")
159
 
  end
160
 
 
161
 
  def test_save_succeeds_for_invalid_belongs_to_with_validate_false
162
 
    assert log = AuditLog.create(:developer_id => 0, :message=> "")
163
 
 
164
 
    log.unvalidated_developer = Developer.new
165
 
    assert !log.unvalidated_developer.valid?
166
 
    assert log.valid?
167
 
    assert log.save
168
 
  end
169
 
 
170
 
  def test_assignment_before_parent_saved
171
 
    client = Client.find(:first)
172
 
    apple = Firm.new("name" => "Apple")
173
 
    client.firm = apple
174
 
    assert_equal apple, client.firm
175
 
    assert apple.new_record?
176
 
    assert client.save
177
 
    assert apple.save
178
 
    assert !apple.new_record?
179
 
    assert_equal apple, client.firm
180
 
    assert_equal apple, client.firm(true)
181
 
  end
182
 
 
183
 
  def test_assignment_before_either_saved
184
 
    final_cut = Client.new("name" => "Final Cut")
185
 
    apple = Firm.new("name" => "Apple")
186
 
    final_cut.firm = apple
187
 
    assert final_cut.new_record?
188
 
    assert apple.new_record?
189
 
    assert final_cut.save
190
 
    assert !final_cut.new_record?
191
 
    assert !apple.new_record?
192
 
    assert_equal apple, final_cut.firm
193
 
    assert_equal apple, final_cut.firm(true)
194
 
  end
195
 
 
196
 
  def test_store_two_association_with_one_save
197
 
    num_orders = Order.count
198
 
    num_customers = Customer.count
199
 
    order = Order.new
200
 
 
201
 
    customer1 = order.billing = Customer.new
202
 
    customer2 = order.shipping = Customer.new
203
 
    assert order.save
204
 
    assert_equal customer1, order.billing
205
 
    assert_equal customer2, order.shipping
206
 
 
207
 
    order.reload
208
 
 
209
 
    assert_equal customer1, order.billing
210
 
    assert_equal customer2, order.shipping
211
 
 
212
 
    assert_equal num_orders +1, Order.count
213
 
    assert_equal num_customers +2, Customer.count
214
 
  end
215
 
 
216
 
  def test_store_association_in_two_relations_with_one_save
217
 
    num_orders = Order.count
218
 
    num_customers = Customer.count
219
 
    order = Order.new
220
 
 
221
 
    customer = order.billing = order.shipping = Customer.new
222
 
    assert order.save
223
 
    assert_equal customer, order.billing
224
 
    assert_equal customer, order.shipping
225
 
 
226
 
    order.reload
227
 
 
228
 
    assert_equal customer, order.billing
229
 
    assert_equal customer, order.shipping
230
 
 
231
 
    assert_equal num_orders +1, Order.count
232
 
    assert_equal num_customers +1, Customer.count
233
 
  end
234
 
 
235
 
  def test_store_association_in_two_relations_with_one_save_in_existing_object
236
 
    num_orders = Order.count
237
 
    num_customers = Customer.count
238
 
    order = Order.create
239
 
 
240
 
    customer = order.billing = order.shipping = Customer.new
241
 
    assert order.save
242
 
    assert_equal customer, order.billing
243
 
    assert_equal customer, order.shipping
244
 
 
245
 
    order.reload
246
 
 
247
 
    assert_equal customer, order.billing
248
 
    assert_equal customer, order.shipping
249
 
 
250
 
    assert_equal num_orders +1, Order.count
251
 
    assert_equal num_customers +1, Customer.count
252
 
  end
253
 
 
254
 
  def test_store_association_in_two_relations_with_one_save_in_existing_object_with_values
255
 
    num_orders = Order.count
256
 
    num_customers = Customer.count
257
 
    order = Order.create
258
 
 
259
 
    customer = order.billing = order.shipping = Customer.new
260
 
    assert order.save
261
 
    assert_equal customer, order.billing
262
 
    assert_equal customer, order.shipping
263
 
 
264
 
    order.reload
265
 
 
266
 
    customer = order.billing = order.shipping = Customer.new
267
 
 
268
 
    assert order.save
269
 
    order.reload
270
 
 
271
 
    assert_equal customer, order.billing
272
 
    assert_equal customer, order.shipping
273
 
 
274
 
    assert_equal num_orders +1, Order.count
275
 
    assert_equal num_customers +2, Customer.count
276
 
  end
277
 
end
278
 
 
279
 
class TestDefaultAutosaveAssociationOnAHasManyAssociation < ActiveRecord::TestCase
280
 
  fixtures :companies, :people
281
 
 
282
 
  def test_invalid_adding
283
 
    firm = Firm.find(1)
284
 
    assert !(firm.clients_of_firm << c = Client.new)
285
 
    assert c.new_record?
286
 
    assert !firm.valid?
287
 
    assert !firm.save
288
 
    assert c.new_record?
289
 
  end
290
 
 
291
 
  def test_invalid_adding_before_save
292
 
    no_of_firms = Firm.count
293
 
    no_of_clients = Client.count
294
 
    new_firm = Firm.new("name" => "A New Firm, Inc")
295
 
    new_firm.clients_of_firm.concat([c = Client.new, Client.new("name" => "Apple")])
296
 
    assert c.new_record?
297
 
    assert !c.valid?
298
 
    assert !new_firm.valid?
299
 
    assert !new_firm.save
300
 
    assert c.new_record?
301
 
    assert new_firm.new_record?
302
 
  end
303
 
 
304
 
  def test_invalid_adding_with_validate_false
305
 
    firm = Firm.find(:first)
306
 
    client = Client.new
307
 
    firm.unvalidated_clients_of_firm << client
308
 
 
309
 
    assert firm.valid?
310
 
    assert !client.valid?
311
 
    assert firm.save
312
 
    assert client.new_record?
313
 
  end
314
 
 
315
 
  def test_valid_adding_with_validate_false
316
 
    no_of_clients = Client.count
317
 
 
318
 
    firm = Firm.find(:first)
319
 
    client = Client.new("name" => "Apple")
320
 
 
321
 
    assert firm.valid?
322
 
    assert client.valid?
323
 
    assert client.new_record?
324
 
 
325
 
    firm.unvalidated_clients_of_firm << client
326
 
 
327
 
    assert firm.save
328
 
    assert !client.new_record?
329
 
    assert_equal no_of_clients+1, Client.count
330
 
  end
331
 
 
332
 
  def test_invalid_build
333
 
    new_client = companies(:first_firm).clients_of_firm.build
334
 
    assert new_client.new_record?
335
 
    assert !new_client.valid?
336
 
    assert_equal new_client, companies(:first_firm).clients_of_firm.last
337
 
    assert !companies(:first_firm).save
338
 
    assert new_client.new_record?
339
 
    assert_equal 1, companies(:first_firm).clients_of_firm(true).size
340
 
  end
341
 
 
342
 
  def test_adding_before_save
343
 
    no_of_firms = Firm.count
344
 
    no_of_clients = Client.count
345
 
 
346
 
    new_firm = Firm.new("name" => "A New Firm, Inc")
347
 
    c = Client.new("name" => "Apple")
348
 
 
349
 
    new_firm.clients_of_firm.push Client.new("name" => "Natural Company")
350
 
    assert_equal 1, new_firm.clients_of_firm.size
351
 
    new_firm.clients_of_firm << c
352
 
    assert_equal 2, new_firm.clients_of_firm.size
353
 
 
354
 
    assert_equal no_of_firms, Firm.count      # Firm was not saved to database.
355
 
    assert_equal no_of_clients, Client.count  # Clients were not saved to database.
356
 
    assert new_firm.save
357
 
    assert !new_firm.new_record?
358
 
    assert !c.new_record?
359
 
    assert_equal new_firm, c.firm
360
 
    assert_equal no_of_firms+1, Firm.count      # Firm was saved to database.
361
 
    assert_equal no_of_clients+2, Client.count  # Clients were saved to database.
362
 
 
363
 
    assert_equal 2, new_firm.clients_of_firm.size
364
 
    assert_equal 2, new_firm.clients_of_firm(true).size
365
 
  end
366
 
 
367
 
  def test_assign_ids
368
 
    firm = Firm.new("name" => "Apple")
369
 
    firm.client_ids = [companies(:first_client).id, companies(:second_client).id]
370
 
    firm.save
371
 
    firm.reload
372
 
    assert_equal 2, firm.clients.length
373
 
    assert firm.clients.include?(companies(:second_client))
374
 
  end
375
 
 
376
 
  def test_assign_ids_for_through_a_belongs_to
377
 
    post = Post.new(:title => "Assigning IDs works!", :body => "You heared it here first, folks!")
378
 
    post.person_ids = [people(:david).id, people(:michael).id]
379
 
    post.save
380
 
    post.reload
381
 
    assert_equal 2, post.people.length
382
 
    assert post.people.include?(people(:david))
383
 
  end
384
 
 
385
 
  def test_build_before_save
386
 
    company = companies(:first_firm)
387
 
    new_client = assert_no_queries { company.clients_of_firm.build("name" => "Another Client") }
388
 
    assert !company.clients_of_firm.loaded?
389
 
 
390
 
    company.name += '-changed'
391
 
    assert_queries(2) { assert company.save }
392
 
    assert !new_client.new_record?
393
 
    assert_equal 2, company.clients_of_firm(true).size
394
 
  end
395
 
 
396
 
  def test_build_many_before_save
397
 
    company = companies(:first_firm)
398
 
    new_clients = assert_no_queries { company.clients_of_firm.build([{"name" => "Another Client"}, {"name" => "Another Client II"}]) }
399
 
 
400
 
    company.name += '-changed'
401
 
    assert_queries(3) { assert company.save }
402
 
    assert_equal 3, company.clients_of_firm(true).size
403
 
  end
404
 
 
405
 
  def test_build_via_block_before_save
406
 
    company = companies(:first_firm)
407
 
    new_client = assert_no_queries { company.clients_of_firm.build {|client| client.name = "Another Client" } }
408
 
    assert !company.clients_of_firm.loaded?
409
 
 
410
 
    company.name += '-changed'
411
 
    assert_queries(2) { assert company.save }
412
 
    assert !new_client.new_record?
413
 
    assert_equal 2, company.clients_of_firm(true).size
414
 
  end
415
 
 
416
 
  def test_build_many_via_block_before_save
417
 
    company = companies(:first_firm)
418
 
    new_clients = assert_no_queries do
419
 
      company.clients_of_firm.build([{"name" => "Another Client"}, {"name" => "Another Client II"}]) do |client|
420
 
        client.name = "changed"
421
 
      end
422
 
    end
423
 
 
424
 
    company.name += '-changed'
425
 
    assert_queries(3) { assert company.save }
426
 
    assert_equal 3, company.clients_of_firm(true).size
427
 
  end
428
 
 
429
 
  def test_replace_on_new_object
430
 
    firm = Firm.new("name" => "New Firm")
431
 
    firm.clients = [companies(:second_client), Client.new("name" => "New Client")]
432
 
    assert firm.save
433
 
    firm.reload
434
 
    assert_equal 2, firm.clients.length
435
 
    assert firm.clients.include?(Client.find_by_name("New Client"))
436
 
  end
437
 
end
438
 
 
439
 
class TestDefaultAutosaveAssociationOnNewRecord < ActiveRecord::TestCase
440
 
  def test_autosave_new_record_on_belongs_to_can_be_disabled_per_relationship
441
 
    new_account = Account.new("credit_limit" => 1000)
442
 
    new_firm = Firm.new("name" => "some firm")
443
 
 
444
 
    assert new_firm.new_record?
445
 
    new_account.firm = new_firm
446
 
    new_account.save!
447
 
 
448
 
    assert !new_firm.new_record?
449
 
 
450
 
    new_account = Account.new("credit_limit" => 1000)
451
 
    new_autosaved_firm = Firm.new("name" => "some firm")
452
 
 
453
 
    assert new_autosaved_firm.new_record?
454
 
    new_account.unautosaved_firm = new_autosaved_firm
455
 
    new_account.save!
456
 
 
457
 
    assert new_autosaved_firm.new_record?
458
 
  end
459
 
 
460
 
  def test_autosave_new_record_on_has_one_can_be_disabled_per_relationship
461
 
    firm = Firm.new("name" => "some firm")
462
 
    account = Account.new("credit_limit" => 1000)
463
 
 
464
 
    assert account.new_record?
465
 
    firm.account = account
466
 
    firm.save!
467
 
 
468
 
    assert !account.new_record?
469
 
 
470
 
    firm = Firm.new("name" => "some firm")
471
 
    account = Account.new("credit_limit" => 1000)
472
 
 
473
 
    firm.unautosaved_account = account
474
 
 
475
 
    assert account.new_record?
476
 
    firm.unautosaved_account = account
477
 
    firm.save!
478
 
 
479
 
    assert account.new_record?
480
 
  end
481
 
 
482
 
  def test_autosave_new_record_on_has_many_can_be_disabled_per_relationship
483
 
    firm = Firm.new("name" => "some firm")
484
 
    account = Account.new("credit_limit" => 1000)
485
 
 
486
 
    assert account.new_record?
487
 
    firm.accounts << account
488
 
 
489
 
    firm.save!
490
 
    assert !account.new_record?
491
 
 
492
 
    firm = Firm.new("name" => "some firm")
493
 
    account = Account.new("credit_limit" => 1000)
494
 
 
495
 
    assert account.new_record?
496
 
    firm.unautosaved_accounts << account
497
 
 
498
 
    firm.save!
499
 
    assert account.new_record?
500
 
  end
501
 
end
502
 
 
503
 
class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase
504
 
  self.use_transactional_fixtures = false
505
 
 
506
 
  def setup
507
 
    @pirate = Pirate.create(:catchphrase => "Don' botharrr talkin' like one, savvy?")
508
 
    @ship = @pirate.create_ship(:name => 'Nights Dirty Lightning')
509
 
  end
510
 
 
511
 
  # reload
512
 
  def test_a_marked_for_destruction_record_should_not_be_be_marked_after_reload
513
 
    @pirate.mark_for_destruction
514
 
    @pirate.ship.mark_for_destruction
515
 
 
516
 
    assert !@pirate.reload.marked_for_destruction?
517
 
    assert !@pirate.ship.marked_for_destruction?
518
 
  end
519
 
 
520
 
  # has_one
521
 
  def test_should_destroy_a_child_association_as_part_of_the_save_transaction_if_it_was_marked_for_destroyal
522
 
    assert !@pirate.ship.marked_for_destruction?
523
 
 
524
 
    @pirate.ship.mark_for_destruction
525
 
    id = @pirate.ship.id
526
 
 
527
 
    assert @pirate.ship.marked_for_destruction?
528
 
    assert Ship.find_by_id(id)
529
 
 
530
 
    @pirate.save
531
 
    assert_nil @pirate.reload.ship
532
 
    assert_nil Ship.find_by_id(id)
533
 
  end
534
 
 
535
 
  def test_should_skip_validation_on_a_child_association_if_marked_for_destruction
536
 
    @pirate.ship.name = ''
537
 
    assert !@pirate.valid?
538
 
 
539
 
    @pirate.ship.mark_for_destruction
540
 
    @pirate.ship.expects(:valid?).never
541
 
    assert_difference('Ship.count', -1) { @pirate.save! }
542
 
  end
543
 
 
544
 
  def test_a_child_marked_for_destruction_should_not_be_destroyed_twice
545
 
    @pirate.ship.mark_for_destruction
546
 
    assert @pirate.save
547
 
    @pirate.ship.expects(:destroy).never
548
 
    assert @pirate.save
549
 
  end
550
 
 
551
 
  def test_should_rollback_destructions_if_an_exception_occurred_while_saving_a_child
552
 
    # Stub the save method of the @pirate.ship instance to destroy and then raise an exception
553
 
    class << @pirate.ship
554
 
      def save(*args)
555
 
        super
556
 
        destroy
557
 
        raise 'Oh noes!'
558
 
      end
559
 
    end
560
 
 
561
 
    assert_raise(RuntimeError) { assert !@pirate.save }
562
 
    assert_not_nil @pirate.reload.ship
563
 
  end
564
 
 
565
 
  # belongs_to
566
 
  def test_should_destroy_a_parent_association_as_part_of_the_save_transaction_if_it_was_marked_for_destroyal
567
 
    assert !@ship.pirate.marked_for_destruction?
568
 
 
569
 
    @ship.pirate.mark_for_destruction
570
 
    id = @ship.pirate.id
571
 
 
572
 
    assert @ship.pirate.marked_for_destruction?
573
 
    assert Pirate.find_by_id(id)
574
 
 
575
 
    @ship.save
576
 
    assert_nil @ship.reload.pirate
577
 
    assert_nil Pirate.find_by_id(id)
578
 
  end
579
 
 
580
 
  def test_should_skip_validation_on_a_parent_association_if_marked_for_destruction
581
 
    @ship.pirate.catchphrase = ''
582
 
    assert !@ship.valid?
583
 
 
584
 
    @ship.pirate.mark_for_destruction
585
 
    @ship.pirate.expects(:valid?).never
586
 
    assert_difference('Pirate.count', -1) { @ship.save! }
587
 
  end
588
 
 
589
 
  def test_a_parent_marked_for_destruction_should_not_be_destroyed_twice
590
 
    @ship.pirate.mark_for_destruction
591
 
    assert @ship.save
592
 
    @ship.pirate.expects(:destroy).never
593
 
    assert @ship.save
594
 
  end
595
 
 
596
 
  def test_should_rollback_destructions_if_an_exception_occurred_while_saving_a_parent
597
 
    # Stub the save method of the @ship.pirate instance to destroy and then raise an exception
598
 
    class << @ship.pirate
599
 
      def save(*args)
600
 
        super
601
 
        destroy
602
 
        raise 'Oh noes!'
603
 
      end
604
 
    end
605
 
 
606
 
    assert_raise(RuntimeError) { assert !@ship.save }
607
 
    assert_not_nil @ship.reload.pirate
608
 
  end
609
 
 
610
 
  # has_many & has_and_belongs_to
611
 
  %w{ parrots birds }.each do |association_name|
612
 
    define_method("test_should_destroy_#{association_name}_as_part_of_the_save_transaction_if_they_were_marked_for_destroyal") do
613
 
      2.times { |i| @pirate.send(association_name).create!(:name => "#{association_name}_#{i}") }
614
 
 
615
 
      assert !@pirate.send(association_name).any? { |child| child.marked_for_destruction? }
616
 
 
617
 
      @pirate.send(association_name).each { |child| child.mark_for_destruction }
618
 
      klass = @pirate.send(association_name).first.class
619
 
      ids = @pirate.send(association_name).map(&:id)
620
 
 
621
 
      assert @pirate.send(association_name).all? { |child| child.marked_for_destruction? }
622
 
      ids.each { |id| assert klass.find_by_id(id) }
623
 
 
624
 
      @pirate.save
625
 
      assert @pirate.reload.send(association_name).empty?
626
 
      ids.each { |id| assert_nil klass.find_by_id(id) }
627
 
    end
628
 
 
629
 
    define_method("test_should_skip_validation_on_the_#{association_name}_association_if_marked_for_destruction") do
630
 
      2.times { |i| @pirate.send(association_name).create!(:name => "#{association_name}_#{i}") }
631
 
      children = @pirate.send(association_name)
632
 
 
633
 
      children.each { |child| child.name = '' }
634
 
      assert !@pirate.valid?
635
 
 
636
 
      children.each do |child|
637
 
        child.mark_for_destruction
638
 
        child.expects(:valid?).never
639
 
      end
640
 
      assert_difference("#{association_name.classify}.count", -2) { @pirate.save! }
641
 
    end
642
 
    
643
 
    define_method("test_should_skip_validation_on_the_#{association_name}_association_if_destroyed") do
644
 
      @pirate.send(association_name).create!(:name => "#{association_name}_1")
645
 
      children = @pirate.send(association_name)
646
 
 
647
 
      children.each { |child| child.name = '' }
648
 
      assert !@pirate.valid?
649
 
 
650
 
      children.each { |child| child.destroy }
651
 
      assert @pirate.valid?
652
 
    end
653
 
 
654
 
    define_method("test_a_child_marked_for_destruction_should_not_be_destroyed_twice_while_saving_#{association_name}") do
655
 
      @pirate.send(association_name).create!(:name => "#{association_name}_1")
656
 
      children = @pirate.send(association_name)
657
 
 
658
 
      children.each { |child| child.mark_for_destruction }
659
 
      assert @pirate.save
660
 
      children.each { |child| child.expects(:destroy).never }
661
 
      assert @pirate.save
662
 
    end
663
 
 
664
 
    define_method("test_should_rollback_destructions_if_an_exception_occurred_while_saving_#{association_name}") do
665
 
      2.times { |i| @pirate.send(association_name).create!(:name => "#{association_name}_#{i}") }
666
 
      before = @pirate.send(association_name).map { |c| c }
667
 
 
668
 
      # Stub the save method of the first child to destroy and the second to raise an exception
669
 
      class << before.first
670
 
        def save(*args)
671
 
          super
672
 
          destroy
673
 
        end
674
 
      end
675
 
      class << before.last
676
 
        def save(*args)
677
 
          super
678
 
          raise 'Oh noes!'
679
 
        end
680
 
      end
681
 
 
682
 
      assert_raise(RuntimeError) { assert !@pirate.save }
683
 
      assert_equal before, @pirate.reload.send(association_name)
684
 
    end
685
 
 
686
 
    # Add and remove callbacks tests for association collections.
687
 
    %w{ method proc }.each do |callback_type|
688
 
      define_method("test_should_run_add_callback_#{callback_type}s_for_#{association_name}") do
689
 
        association_name_with_callbacks = "#{association_name}_with_#{callback_type}_callbacks"
690
 
 
691
 
        pirate = Pirate.new(:catchphrase => "Arr")
692
 
        pirate.send(association_name_with_callbacks).build(:name => "Crowe the One-Eyed")
693
 
 
694
 
        expected = [
695
 
          "before_adding_#{callback_type}_#{association_name.singularize}_<new>",
696
 
          "after_adding_#{callback_type}_#{association_name.singularize}_<new>"
697
 
        ]
698
 
 
699
 
        assert_equal expected, pirate.ship_log
700
 
      end
701
 
 
702
 
      define_method("test_should_run_remove_callback_#{callback_type}s_for_#{association_name}") do
703
 
        association_name_with_callbacks = "#{association_name}_with_#{callback_type}_callbacks"
704
 
 
705
 
        @pirate.send(association_name_with_callbacks).create!(:name => "Crowe the One-Eyed")
706
 
        @pirate.send(association_name_with_callbacks).each { |c| c.mark_for_destruction }
707
 
        child_id = @pirate.send(association_name_with_callbacks).first.id
708
 
 
709
 
        @pirate.ship_log.clear
710
 
        @pirate.save
711
 
 
712
 
        expected = [
713
 
          "before_removing_#{callback_type}_#{association_name.singularize}_#{child_id}",
714
 
          "after_removing_#{callback_type}_#{association_name.singularize}_#{child_id}"
715
 
        ]
716
 
 
717
 
        assert_equal expected, @pirate.ship_log
718
 
      end
719
 
    end
720
 
  end
721
 
end
722
 
 
723
 
class TestAutosaveAssociationOnAHasOneAssociation < ActiveRecord::TestCase
724
 
  self.use_transactional_fixtures = false
725
 
 
726
 
  def setup
727
 
    @pirate = Pirate.create(:catchphrase => "Don' botharrr talkin' like one, savvy?")
728
 
    @ship = @pirate.create_ship(:name => 'Nights Dirty Lightning')
729
 
  end
730
 
 
731
 
  def test_should_still_work_without_an_associated_model
732
 
    @ship.destroy
733
 
    @pirate.reload.catchphrase = "Arr"
734
 
    @pirate.save
735
 
    assert 'Arr', @pirate.reload.catchphrase
736
 
  end
737
 
 
738
 
  def test_should_automatically_save_the_associated_model
739
 
    @pirate.ship.name = 'The Vile Insanity'
740
 
    @pirate.save
741
 
    assert_equal 'The Vile Insanity', @pirate.reload.ship.name
742
 
  end
743
 
 
744
 
  def test_should_automatically_save_bang_the_associated_model
745
 
    @pirate.ship.name = 'The Vile Insanity'
746
 
    @pirate.save!
747
 
    assert_equal 'The Vile Insanity', @pirate.reload.ship.name
748
 
  end
749
 
 
750
 
  def test_should_automatically_validate_the_associated_model
751
 
    @pirate.ship.name = ''
752
 
    assert !@pirate.valid?
753
 
    assert_equal "can't be blank", @pirate.errors.on(:"ship.name")
754
 
  end
755
 
 
756
 
  def test_should_merge_errors_on_the_associated_models_onto_the_parent_even_if_it_is_not_valid
757
 
    @pirate.ship.name   = nil
758
 
    @pirate.catchphrase = nil
759
 
    assert !@pirate.valid?
760
 
    assert @pirate.errors.full_messages.include?("Name can't be blank")
761
 
    assert @pirate.errors.full_messages.include?("Catchphrase can't be blank")
762
 
  end
763
 
 
764
 
  def test_should_still_allow_to_bypass_validations_on_the_associated_model
765
 
    @pirate.catchphrase = ''
766
 
    @pirate.ship.name = ''
767
 
    @pirate.save(false)
768
 
    assert_equal ['', ''], [@pirate.reload.catchphrase, @pirate.ship.name]
769
 
  end
770
 
 
771
 
  def test_should_allow_to_bypass_validations_on_associated_models_at_any_depth
772
 
    2.times { |i| @pirate.ship.parts.create!(:name => "part #{i}") }
773
 
 
774
 
    @pirate.catchphrase = ''
775
 
    @pirate.ship.name = ''
776
 
    @pirate.ship.parts.each { |part| part.name = '' }
777
 
    @pirate.save(false)
778
 
 
779
 
    values = [@pirate.reload.catchphrase, @pirate.ship.name, *@pirate.ship.parts.map(&:name)]
780
 
    assert_equal ['', '', '', ''], values
781
 
  end
782
 
 
783
 
  def test_should_still_raise_an_ActiveRecordRecord_Invalid_exception_if_we_want_that
784
 
    @pirate.ship.name = ''
785
 
    assert_raise(ActiveRecord::RecordInvalid) do
786
 
      @pirate.save!
787
 
    end
788
 
  end
789
 
 
790
 
  def test_should_rollback_any_changes_if_an_exception_occurred_while_saving
791
 
    before = [@pirate.catchphrase, @pirate.ship.name]
792
 
 
793
 
    @pirate.catchphrase = 'Arr'
794
 
    @pirate.ship.name = 'The Vile Insanity'
795
 
 
796
 
    # Stub the save method of the @pirate.ship instance to raise an exception
797
 
    class << @pirate.ship
798
 
      def save(*args)
799
 
        super
800
 
        raise 'Oh noes!'
801
 
      end
802
 
    end
803
 
 
804
 
    assert_raise(RuntimeError) { assert !@pirate.save }
805
 
    assert_equal before, [@pirate.reload.catchphrase, @pirate.ship.name]
806
 
  end
807
 
 
808
 
  def test_should_not_load_the_associated_model
809
 
    assert_queries(1) { @pirate.catchphrase = 'Arr'; @pirate.save! }
810
 
  end
811
 
end
812
 
 
813
 
class TestAutosaveAssociationOnABelongsToAssociation < ActiveRecord::TestCase
814
 
  self.use_transactional_fixtures = false
815
 
 
816
 
  def setup
817
 
    @ship = Ship.create(:name => 'Nights Dirty Lightning')
818
 
    @pirate = @ship.create_pirate(:catchphrase => "Don' botharrr talkin' like one, savvy?")
819
 
  end
820
 
 
821
 
  def test_should_still_work_without_an_associated_model
822
 
    @pirate.destroy
823
 
    @ship.reload.name = "The Vile Insanity"
824
 
    @ship.save
825
 
    assert 'The Vile Insanity', @ship.reload.name
826
 
  end
827
 
 
828
 
  def test_should_automatically_save_the_associated_model
829
 
    @ship.pirate.catchphrase = 'Arr'
830
 
    @ship.save
831
 
    assert_equal 'Arr', @ship.reload.pirate.catchphrase
832
 
  end
833
 
 
834
 
  def test_should_automatically_save_bang_the_associated_model
835
 
    @ship.pirate.catchphrase = 'Arr'
836
 
    @ship.save!
837
 
    assert_equal 'Arr', @ship.reload.pirate.catchphrase
838
 
  end
839
 
 
840
 
  def test_should_automatically_validate_the_associated_model
841
 
    @ship.pirate.catchphrase = ''
842
 
    assert !@ship.valid?
843
 
    assert_equal "can't be blank", @ship.errors.on(:"pirate.catchphrase")
844
 
  end
845
 
 
846
 
  def test_should_merge_errors_on_the_associated_model_onto_the_parent_even_if_it_is_not_valid
847
 
    @ship.name = nil
848
 
    @ship.pirate.catchphrase = nil
849
 
    assert !@ship.valid?
850
 
    assert @ship.errors.full_messages.include?("Name can't be blank")
851
 
    assert @ship.errors.full_messages.include?("Catchphrase can't be blank")
852
 
  end
853
 
 
854
 
  def test_should_still_allow_to_bypass_validations_on_the_associated_model
855
 
    @ship.pirate.catchphrase = ''
856
 
    @ship.name = ''
857
 
    @ship.save(false)
858
 
    assert_equal ['', ''], [@ship.reload.name, @ship.pirate.catchphrase]
859
 
  end
860
 
 
861
 
  def test_should_still_raise_an_ActiveRecordRecord_Invalid_exception_if_we_want_that
862
 
    @ship.pirate.catchphrase = ''
863
 
    assert_raise(ActiveRecord::RecordInvalid) do
864
 
      @ship.save!
865
 
    end
866
 
  end
867
 
 
868
 
  def test_should_rollback_any_changes_if_an_exception_occurred_while_saving
869
 
    before = [@ship.pirate.catchphrase, @ship.name]
870
 
 
871
 
    @ship.pirate.catchphrase = 'Arr'
872
 
    @ship.name = 'The Vile Insanity'
873
 
 
874
 
    # Stub the save method of the @ship.pirate instance to raise an exception
875
 
    class << @ship.pirate
876
 
      def save(*args)
877
 
        super
878
 
        raise 'Oh noes!'
879
 
      end
880
 
    end
881
 
 
882
 
    assert_raise(RuntimeError) { assert !@ship.save }
883
 
    # TODO: Why does using reload on @ship looses the associated pirate?
884
 
    assert_equal before, [@ship.pirate.reload.catchphrase, @ship.reload.name]
885
 
  end
886
 
 
887
 
  def test_should_not_load_the_associated_model
888
 
    assert_queries(1) { @ship.name = 'The Vile Insanity'; @ship.save! }
889
 
  end
890
 
end
891
 
 
892
 
module AutosaveAssociationOnACollectionAssociationTests
893
 
  def test_should_automatically_save_the_associated_models
894
 
    new_names = ['Grace OMalley', 'Privateers Greed']
895
 
    @pirate.send(@association_name).each_with_index { |child, i| child.name = new_names[i] }
896
 
 
897
 
    @pirate.save
898
 
    assert_equal new_names, @pirate.reload.send(@association_name).map(&:name)
899
 
  end
900
 
 
901
 
  def test_should_automatically_save_bang_the_associated_models
902
 
    new_names = ['Grace OMalley', 'Privateers Greed']
903
 
    @pirate.send(@association_name).each_with_index { |child, i| child.name = new_names[i] }
904
 
 
905
 
    @pirate.save!
906
 
    assert_equal new_names, @pirate.reload.send(@association_name).map(&:name)
907
 
  end
908
 
 
909
 
  def test_should_automatically_validate_the_associated_models
910
 
    @pirate.send(@association_name).each { |child| child.name = '' }
911
 
 
912
 
    assert !@pirate.valid?
913
 
    assert @pirate.errors.full_messages.include?("Name can't be blank")
914
 
    assert @pirate.errors.on(@association_name).blank?
915
 
  end
916
 
 
917
 
  def test_should_not_use_default_invalid_error_on_associated_models
918
 
    @pirate.send(@association_name).build(:name => '')
919
 
 
920
 
    assert !@pirate.valid?
921
 
    assert_equal "can't be blank", @pirate.errors.on("#{@association_name}.name")
922
 
    assert @pirate.errors.on(@association_name).blank?
923
 
  end
924
 
 
925
 
  def test_should_merge_errors_on_the_associated_models_onto_the_parent_even_if_it_is_not_valid
926
 
    @pirate.send(@association_name).each { |child| child.name = '' }
927
 
    @pirate.catchphrase = nil
928
 
 
929
 
    assert !@pirate.valid?
930
 
    assert_equal "can't be blank", @pirate.errors.on("#{@association_name}.name")
931
 
    assert !@pirate.errors.on(:catchphrase).blank?
932
 
  end
933
 
 
934
 
  def test_should_allow_to_bypass_validations_on_the_associated_models_on_update
935
 
    @pirate.catchphrase = ''
936
 
    @pirate.send(@association_name).each { |child| child.name = '' }
937
 
 
938
 
    assert @pirate.save(false)
939
 
    assert_equal ['', '', ''], [
940
 
      @pirate.reload.catchphrase,
941
 
      @pirate.send(@association_name).first.name,
942
 
      @pirate.send(@association_name).last.name
943
 
    ]
944
 
  end
945
 
 
946
 
  def test_should_validation_the_associated_models_on_create
947
 
    assert_no_difference("#{ @association_name == :birds ? 'Bird' : 'Parrot' }.count") do
948
 
      2.times { @pirate.send(@association_name).build }
949
 
      @pirate.save(true)
950
 
    end
951
 
  end
952
 
 
953
 
  def test_should_allow_to_bypass_validations_on_the_associated_models_on_create
954
 
    assert_difference("#{ @association_name == :birds ? 'Bird' : 'Parrot' }.count", +2) do
955
 
      2.times { @pirate.send(@association_name).build }
956
 
      @pirate.save(false)
957
 
    end
958
 
  end
959
 
 
960
 
  def test_should_rollback_any_changes_if_an_exception_occurred_while_saving
961
 
    before = [@pirate.catchphrase, *@pirate.send(@association_name).map(&:name)]
962
 
    new_names = ['Grace OMalley', 'Privateers Greed']
963
 
 
964
 
    @pirate.catchphrase = 'Arr'
965
 
    @pirate.send(@association_name).each_with_index { |child, i| child.name = new_names[i] }
966
 
 
967
 
    # Stub the save method of the first child instance to raise an exception
968
 
    class << @pirate.send(@association_name).first
969
 
      def save(*args)
970
 
        super
971
 
        raise 'Oh noes!'
972
 
      end
973
 
    end
974
 
 
975
 
    assert_raise(RuntimeError) { assert !@pirate.save }
976
 
    assert_equal before, [@pirate.reload.catchphrase, *@pirate.send(@association_name).map(&:name)]
977
 
  end
978
 
 
979
 
  def test_should_still_raise_an_ActiveRecordRecord_Invalid_exception_if_we_want_that
980
 
    @pirate.send(@association_name).each { |child| child.name = '' }
981
 
    assert_raise(ActiveRecord::RecordInvalid) do
982
 
      @pirate.save!
983
 
    end
984
 
  end
985
 
 
986
 
  def test_should_not_load_the_associated_models_if_they_were_not_loaded_yet
987
 
    assert_queries(1) { @pirate.catchphrase = 'Arr'; @pirate.save! }
988
 
 
989
 
    @pirate.send(@association_name).class # hack to load the target
990
 
 
991
 
    assert_queries(3) do
992
 
      @pirate.catchphrase = 'Yarr'
993
 
      new_names = ['Grace OMalley', 'Privateers Greed']
994
 
      @pirate.send(@association_name).each_with_index { |child, i| child.name = new_names[i] }
995
 
      @pirate.save!
996
 
    end
997
 
  end
998
 
end
999
 
 
1000
 
class TestAutosaveAssociationOnAHasManyAssociation < ActiveRecord::TestCase
1001
 
  self.use_transactional_fixtures = false
1002
 
 
1003
 
  def setup
1004
 
    @association_name = :birds
1005
 
 
1006
 
    @pirate = Pirate.create(:catchphrase => "Don' botharrr talkin' like one, savvy?")
1007
 
    @child_1 = @pirate.birds.create(:name => 'Posideons Killer')
1008
 
    @child_2 = @pirate.birds.create(:name => 'Killer bandita Dionne')
1009
 
  end
1010
 
 
1011
 
  include AutosaveAssociationOnACollectionAssociationTests
1012
 
end
1013
 
 
1014
 
class TestAutosaveAssociationOnAHasAndBelongsToManyAssociation < ActiveRecord::TestCase
1015
 
  self.use_transactional_fixtures = false
1016
 
 
1017
 
  def setup
1018
 
    @association_name = :parrots
1019
 
    @habtm = true
1020
 
 
1021
 
    @pirate = Pirate.create(:catchphrase => "Don' botharrr talkin' like one, savvy?")
1022
 
    @child_1 = @pirate.parrots.create(:name => 'Posideons Killer')
1023
 
    @child_2 = @pirate.parrots.create(:name => 'Killer bandita Dionne')
1024
 
  end
1025
 
 
1026
 
  include AutosaveAssociationOnACollectionAssociationTests
1027
 
end
1028
 
 
1029
 
class TestAutosaveAssociationValidationsOnAHasManyAssocication < ActiveRecord::TestCase
1030
 
  self.use_transactional_fixtures = false
1031
 
 
1032
 
  def setup
1033
 
    @pirate = Pirate.create(:catchphrase => "Don' botharrr talkin' like one, savvy?")
1034
 
    @pirate.birds.create(:name => 'cookoo')
1035
 
  end
1036
 
 
1037
 
  test "should automatically validate associations" do
1038
 
    assert @pirate.valid?
1039
 
    @pirate.birds.each { |bird| bird.name = '' }
1040
 
 
1041
 
    assert !@pirate.valid?
1042
 
  end
1043
 
end
1044
 
 
1045
 
class TestAutosaveAssociationValidationsOnAHasOneAssocication < ActiveRecord::TestCase
1046
 
  self.use_transactional_fixtures = false
1047
 
 
1048
 
  def setup
1049
 
    @pirate = Pirate.create(:catchphrase => "Don' botharrr talkin' like one, savvy?")
1050
 
    @pirate.create_ship(:name => 'titanic')
1051
 
  end
1052
 
 
1053
 
  test "should automatically validate associations with :validate => true" do
1054
 
    assert @pirate.valid?
1055
 
    @pirate.ship.name = ''
1056
 
    assert !@pirate.valid?
1057
 
  end
1058
 
 
1059
 
  test "should not automatically validate associations without :validate => true" do
1060
 
    assert @pirate.valid?
1061
 
    @pirate.non_validated_ship.name = ''
1062
 
    assert @pirate.valid?
1063
 
  end
1064
 
end
1065
 
 
1066
 
class TestAutosaveAssociationValidationsOnABelongsToAssocication < ActiveRecord::TestCase
1067
 
  self.use_transactional_fixtures = false
1068
 
 
1069
 
  def setup
1070
 
    @pirate = Pirate.create(:catchphrase => "Don' botharrr talkin' like one, savvy?")
1071
 
  end
1072
 
 
1073
 
  test "should automatically validate associations with :validate => true" do
1074
 
    assert @pirate.valid?
1075
 
    @pirate.parrot = Parrot.new(:name => '')
1076
 
    assert !@pirate.valid?
1077
 
  end
1078
 
 
1079
 
  test "should not automatically validate associations without :validate => true" do
1080
 
    assert @pirate.valid?
1081
 
    @pirate.non_validated_parrot = Parrot.new(:name => '')
1082
 
    assert @pirate.valid?
1083
 
  end
1084
 
end
1085
 
 
1086
 
class TestAutosaveAssociationValidationsOnAHABTMAssocication < ActiveRecord::TestCase
1087
 
  self.use_transactional_fixtures = false
1088
 
 
1089
 
  def setup
1090
 
    @pirate = Pirate.create(:catchphrase => "Don' botharrr talkin' like one, savvy?")
1091
 
  end
1092
 
 
1093
 
  test "should automatically validate associations with :validate => true" do
1094
 
    assert @pirate.valid?
1095
 
    @pirate.parrots = [ Parrot.new(:name => 'popuga') ]
1096
 
    @pirate.parrots.each { |parrot| parrot.name = '' }
1097
 
    assert !@pirate.valid?
1098
 
  end
1099
 
 
1100
 
  test "should not automatically validate associations without :validate => true" do
1101
 
    assert @pirate.valid?
1102
 
    @pirate.non_validated_parrots = [ Parrot.new(:name => 'popuga') ]
1103
 
    @pirate.non_validated_parrots.each { |parrot| parrot.name = '' }
1104
 
    assert @pirate.valid?
1105
 
  end
1106
 
end
1107
 
 
1108
 
class TestAutosaveAssociationValidationMethodsGeneration < ActiveRecord::TestCase
1109
 
  self.use_transactional_fixtures = false
1110
 
 
1111
 
  def setup
1112
 
    @pirate = Pirate.new
1113
 
  end
1114
 
 
1115
 
  test "should generate validation methods for has_many associations" do
1116
 
    assert @pirate.respond_to?(:validate_associated_records_for_birds)
1117
 
  end
1118
 
 
1119
 
  test "should generate validation methods for has_one associations with :validate => true" do
1120
 
    assert @pirate.respond_to?(:validate_associated_records_for_ship)
1121
 
  end
1122
 
 
1123
 
  test "should not generate validation methods for has_one associations without :validate => true" do
1124
 
    assert !@pirate.respond_to?(:validate_associated_records_for_non_validated_ship)
1125
 
  end
1126
 
 
1127
 
  test "should generate validation methods for belongs_to associations with :validate => true" do
1128
 
    assert @pirate.respond_to?(:validate_associated_records_for_parrot)
1129
 
  end
1130
 
 
1131
 
  test "should not generate validation methods for belongs_to associations without :validate => true" do
1132
 
    assert !@pirate.respond_to?(:validate_associated_records_for_non_validated_parrot)
1133
 
  end
1134
 
 
1135
 
  test "should generate validation methods for HABTM associations with :validate => true" do
1136
 
    assert @pirate.respond_to?(:validate_associated_records_for_parrots)
1137
 
  end
1138
 
 
1139
 
  test "should not generate validation methods for HABTM associations without :validate => true" do
1140
 
    assert !@pirate.respond_to?(:validate_associated_records_for_non_validated_parrots)
1141
 
  end
1142
 
end