cmoran92-paranoia 2.0.2a

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,760 @@
1
+ require 'active_record'
2
+
3
+ test_framework = if ActiveRecord::VERSION::STRING >= "4.1"
4
+ require 'minitest/autorun'
5
+ MiniTest::Test
6
+ else
7
+ require 'test/unit'
8
+ Test::Unit::TestCase
9
+ end
10
+ require File.expand_path(File.dirname(__FILE__) + "/../lib/paranoia")
11
+
12
+ def connect!
13
+ ActiveRecord::Base.establish_connection :adapter => 'sqlite3', database: ':memory:'
14
+ ActiveRecord::Base.connection.execute 'CREATE TABLE parent_models (id INTEGER NOT NULL PRIMARY KEY, deleted_at DATETIME)'
15
+ ActiveRecord::Base.connection.execute 'CREATE TABLE paranoid_models (id INTEGER NOT NULL PRIMARY KEY, parent_model_id INTEGER, deleted_at DATETIME)'
16
+ ActiveRecord::Base.connection.execute 'CREATE TABLE paranoid_model_with_belongs (id INTEGER NOT NULL PRIMARY KEY, parent_model_id INTEGER, deleted_at DATETIME, paranoid_model_with_has_one_id INTEGER)'
17
+ ActiveRecord::Base.connection.execute 'CREATE TABLE paranoid_model_with_anthor_class_name_belongs (id INTEGER NOT NULL PRIMARY KEY, parent_model_id INTEGER, deleted_at DATETIME, paranoid_model_with_has_one_id INTEGER)'
18
+ ActiveRecord::Base.connection.execute 'CREATE TABLE paranoid_model_with_foreign_key_belongs (id INTEGER NOT NULL PRIMARY KEY, parent_model_id INTEGER, deleted_at DATETIME, has_one_foreign_key_id INTEGER)'
19
+ ActiveRecord::Base.connection.execute 'CREATE TABLE featureful_models (id INTEGER NOT NULL PRIMARY KEY, deleted_at DATETIME, name VARCHAR(32))'
20
+ ActiveRecord::Base.connection.execute 'CREATE TABLE plain_models (id INTEGER NOT NULL PRIMARY KEY, deleted_at DATETIME)'
21
+ ActiveRecord::Base.connection.execute 'CREATE TABLE callback_models (id INTEGER NOT NULL PRIMARY KEY, deleted_at DATETIME)'
22
+ ActiveRecord::Base.connection.execute 'CREATE TABLE fail_callback_models (id INTEGER NOT NULL PRIMARY KEY, deleted_at DATETIME)'
23
+ ActiveRecord::Base.connection.execute 'CREATE TABLE related_models (id INTEGER NOT NULL PRIMARY KEY, parent_model_id INTEGER NOT NULL, deleted_at DATETIME)'
24
+ ActiveRecord::Base.connection.execute 'CREATE TABLE asplode_models (id INTEGER NOT NULL PRIMARY KEY, parent_model_id INTEGER, deleted_at DATETIME)'
25
+ ActiveRecord::Base.connection.execute 'CREATE TABLE employers (id INTEGER NOT NULL PRIMARY KEY, deleted_at DATETIME)'
26
+ ActiveRecord::Base.connection.execute 'CREATE TABLE employees (id INTEGER NOT NULL PRIMARY KEY, deleted_at DATETIME)'
27
+ ActiveRecord::Base.connection.execute 'CREATE TABLE jobs (id INTEGER NOT NULL PRIMARY KEY, employer_id INTEGER NOT NULL, employee_id INTEGER NOT NULL, deleted_at DATETIME)'
28
+ ActiveRecord::Base.connection.execute 'CREATE TABLE custom_column_models (id INTEGER NOT NULL PRIMARY KEY, destroyed_at DATETIME)'
29
+ ActiveRecord::Base.connection.execute 'CREATE TABLE custom_sentinel_models (id INTEGER NOT NULL PRIMARY KEY, deleted_at DATETIME NOT NULL)'
30
+ ActiveRecord::Base.connection.execute 'CREATE TABLE non_paranoid_models (id INTEGER NOT NULL PRIMARY KEY, parent_model_id INTEGER)'
31
+ end
32
+
33
+ class WithDifferentConnection < ActiveRecord::Base
34
+ establish_connection adapter: 'sqlite3', database: ':memory:'
35
+ connection.execute 'CREATE TABLE with_different_connections (id INTEGER NOT NULL PRIMARY KEY, deleted_at DATETIME)'
36
+ acts_as_paranoid
37
+ end
38
+
39
+ connect!
40
+
41
+ class ParanoiaTest < test_framework
42
+ def setup
43
+ ActiveRecord::Base.connection.tables.each do |table|
44
+ ActiveRecord::Base.connection.execute "DELETE FROM #{table}"
45
+ end
46
+ end
47
+
48
+ def test_plain_model_class_is_not_paranoid
49
+ assert_equal false, PlainModel.paranoid?
50
+ end
51
+
52
+ def test_paranoid_model_class_is_paranoid
53
+ assert_equal true, ParanoidModel.paranoid?
54
+ end
55
+
56
+ def test_plain_models_are_not_paranoid
57
+ assert_equal false, PlainModel.new.paranoid?
58
+ end
59
+
60
+ def test_paranoid_models_are_paranoid
61
+ assert_equal true, ParanoidModel.new.paranoid?
62
+ end
63
+
64
+ def test_paranoid_models_to_param
65
+ model = ParanoidModel.new
66
+ model.save
67
+ to_param = model.to_param
68
+
69
+ model.destroy
70
+
71
+ assert model.to_param
72
+ assert_equal to_param, model.to_param
73
+ end
74
+
75
+ def test_destroy_behavior_for_plain_models
76
+ model = PlainModel.new
77
+ assert_equal 0, model.class.count
78
+ model.save!
79
+ assert_equal 1, model.class.count
80
+ model.destroy
81
+
82
+ assert_equal true, model.deleted_at.nil?
83
+
84
+ assert_equal 0, model.class.count
85
+ assert_equal 0, model.class.unscoped.count
86
+ end
87
+
88
+ # Anti-regression test for #81, which would've introduced a bug to break this test.
89
+ def test_destroy_behavior_for_plain_models_callbacks
90
+ model = CallbackModel.new
91
+ model.save
92
+ model.remove_called_variables # clear called callback flags
93
+ model.destroy
94
+
95
+ assert_equal nil, model.instance_variable_get(:@update_callback_called)
96
+ assert_equal nil, model.instance_variable_get(:@save_callback_called)
97
+ assert_equal nil, model.instance_variable_get(:@validate_called)
98
+
99
+ assert model.instance_variable_get(:@destroy_callback_called)
100
+ assert model.instance_variable_get(:@after_destroy_callback_called)
101
+ assert model.instance_variable_get(:@after_commit_callback_called)
102
+ end
103
+
104
+
105
+ def test_delete_behavior_for_plain_models_callbacks
106
+ model = CallbackModel.new
107
+ model.save
108
+ model.remove_called_variables # clear called callback flags
109
+ model.delete
110
+
111
+ assert_equal nil, model.instance_variable_get(:@update_callback_called)
112
+ assert_equal nil, model.instance_variable_get(:@save_callback_called)
113
+ assert_equal nil, model.instance_variable_get(:@validate_called)
114
+ assert_equal nil, model.instance_variable_get(:@destroy_callback_called)
115
+ assert_equal nil, model.instance_variable_get(:@after_destroy_callback_called)
116
+ assert model.instance_variable_get(:@after_commit_callback_called)
117
+ end
118
+
119
+ def test_destroy_behavior_for_paranoid_models
120
+ model = ParanoidModel.new
121
+ assert_equal 0, model.class.count
122
+ model.save!
123
+ assert_equal 1, model.class.count
124
+ model.destroy
125
+
126
+ assert_equal false, model.deleted_at.nil?
127
+
128
+ assert_equal 0, model.class.count
129
+ assert_equal 1, model.class.unscoped.count
130
+ end
131
+
132
+ def test_scoping_behavior_for_paranoid_models
133
+ parent1 = ParentModel.create
134
+ parent2 = ParentModel.create
135
+ p1 = ParanoidModel.create(:parent_model => parent1)
136
+ p2 = ParanoidModel.create(:parent_model => parent2)
137
+ p1.destroy
138
+ p2.destroy
139
+ assert_equal 0, parent1.paranoid_models.count
140
+ assert_equal 1, parent1.paranoid_models.only_deleted.count
141
+ assert_equal 1, parent1.paranoid_models.deleted.count
142
+ p3 = ParanoidModel.create(:parent_model => parent1)
143
+ assert_equal 2, parent1.paranoid_models.with_deleted.count
144
+ assert_equal [p1,p3], parent1.paranoid_models.with_deleted
145
+ end
146
+
147
+ def test_destroy_behavior_for_custom_column_models
148
+ model = CustomColumnModel.new
149
+ assert_equal 0, model.class.count
150
+ model.save!
151
+ assert_nil model.destroyed_at
152
+ assert_equal 1, model.class.count
153
+ model.destroy
154
+
155
+ assert_equal false, model.destroyed_at.nil?
156
+ assert model.destroyed?
157
+
158
+ assert_equal 0, model.class.count
159
+ assert_equal 1, model.class.unscoped.count
160
+ assert_equal 1, model.class.only_deleted.count
161
+ assert_equal 1, model.class.deleted.count
162
+ end
163
+
164
+ def test_default_sentinel_value
165
+ assert_equal nil, ParanoidModel.paranoia_sentinel_value
166
+ end
167
+
168
+ def test_sentinel_value_for_custom_sentinel_models
169
+ model = CustomSentinelModel.new
170
+ assert_equal 0, model.class.count
171
+ model.save!
172
+ assert_equal DateTime.new(0), model.deleted_at
173
+ assert_equal 1, model.class.count
174
+ model.destroy
175
+
176
+ assert DateTime.new(0) != model.deleted_at
177
+ assert model.destroyed?
178
+
179
+ assert_equal 0, model.class.count
180
+ assert_equal 1, model.class.unscoped.count
181
+ assert_equal 1, model.class.only_deleted.count
182
+ assert_equal 1, model.class.deleted.count
183
+
184
+ model.restore
185
+ assert_equal DateTime.new(0), model.deleted_at
186
+ assert !model.destroyed?
187
+
188
+ assert_equal 1, model.class.count
189
+ assert_equal 1, model.class.unscoped.count
190
+ assert_equal 0, model.class.only_deleted.count
191
+ assert_equal 0, model.class.deleted.count
192
+ end
193
+
194
+ def test_destroy_behavior_for_featureful_paranoid_models
195
+ model = get_featureful_model
196
+ assert_equal 0, model.class.count
197
+ model.save!
198
+ assert_equal 1, model.class.count
199
+ model.destroy
200
+
201
+ assert_equal false, model.deleted_at.nil?
202
+
203
+ assert_equal 0, model.class.count
204
+ assert_equal 1, model.class.unscoped.count
205
+ end
206
+
207
+ # Regression test for #24
208
+ def test_chaining_for_paranoid_models
209
+ scope = FeaturefulModel.where(:name => "foo").only_deleted
210
+ assert_equal "foo", scope.where_values_hash['name']
211
+ assert_equal 2, scope.where_values.count
212
+ end
213
+
214
+ def test_only_destroyed_scope_for_paranoid_models
215
+ model = ParanoidModel.new
216
+ model.save
217
+ model.destroy
218
+ model2 = ParanoidModel.new
219
+ model2.save
220
+
221
+ assert_equal model, ParanoidModel.only_deleted.last
222
+ assert_equal false, ParanoidModel.only_deleted.include?(model2)
223
+ end
224
+
225
+ def test_default_scope_for_has_many_relationships
226
+ parent = ParentModel.create
227
+ assert_equal 0, parent.related_models.count
228
+
229
+ child = parent.related_models.create
230
+ assert_equal 1, parent.related_models.count
231
+
232
+ child.destroy
233
+ assert_equal false, child.deleted_at.nil?
234
+
235
+ assert_equal 0, parent.related_models.count
236
+ assert_equal 1, parent.related_models.unscoped.count
237
+ end
238
+
239
+ def test_default_scope_for_has_many_through_relationships
240
+ employer = Employer.create
241
+ employee = Employee.create
242
+ assert_equal 0, employer.jobs.count
243
+ assert_equal 0, employer.employees.count
244
+ assert_equal 0, employee.jobs.count
245
+ assert_equal 0, employee.employers.count
246
+
247
+ job = Job.create :employer => employer, :employee => employee
248
+ assert_equal 1, employer.jobs.count
249
+ assert_equal 1, employer.employees.count
250
+ assert_equal 1, employee.jobs.count
251
+ assert_equal 1, employee.employers.count
252
+
253
+ employee2 = Employee.create
254
+ job2 = Job.create :employer => employer, :employee => employee2
255
+ employee2.destroy
256
+ assert_equal 2, employer.jobs.count
257
+ assert_equal 1, employer.employees.count
258
+
259
+ job.destroy
260
+ assert_equal 1, employer.jobs.count
261
+ assert_equal 0, employer.employees.count
262
+ assert_equal 0, employee.jobs.count
263
+ assert_equal 0, employee.employers.count
264
+ end
265
+
266
+ def test_delete_behavior_for_callbacks
267
+ model = CallbackModel.new
268
+ model.save
269
+ model.delete
270
+ assert_equal nil, model.instance_variable_get(:@destroy_callback_called)
271
+ end
272
+
273
+ def test_destroy_behavior_for_callbacks
274
+ model = CallbackModel.new
275
+ model.save
276
+ model.destroy
277
+ assert model.instance_variable_get(:@destroy_callback_called)
278
+ end
279
+
280
+ def test_restore
281
+ model = ParanoidModel.new
282
+ model.save
283
+ id = model.id
284
+ model.destroy
285
+
286
+ assert model.destroyed?
287
+
288
+ model = ParanoidModel.only_deleted.find(id)
289
+ model.restore!
290
+ model.reload
291
+
292
+ assert_equal false, model.destroyed?
293
+ end
294
+
295
+ def test_restore_on_object_return_self
296
+ model = ParanoidModel.create
297
+ model.destroy
298
+
299
+ assert_equal model.class, model.restore.class
300
+ end
301
+
302
+ # Regression test for #92
303
+ def test_destroy_twice
304
+ model = ParanoidModel.new
305
+ model.save
306
+ model.destroy
307
+ model.destroy
308
+
309
+ assert_equal 1, ParanoidModel.unscoped.where(id: model.id).count
310
+ end
311
+
312
+ def test_destroy_return_value_on_success
313
+ model = ParanoidModel.create
314
+ return_value = model.destroy
315
+
316
+ assert_equal(return_value, model)
317
+ end
318
+
319
+ def test_destroy_return_value_on_failure
320
+ model = FailCallbackModel.create
321
+ return_value = model.destroy
322
+
323
+ assert_equal(return_value, false)
324
+ end
325
+
326
+ def test_restore_behavior_for_callbacks
327
+ model = CallbackModel.new
328
+ model.save
329
+ id = model.id
330
+ model.destroy
331
+
332
+ assert model.destroyed?
333
+
334
+ model = CallbackModel.only_deleted.find(id)
335
+ model.restore!
336
+ model.reload
337
+
338
+ assert model.instance_variable_get(:@restore_callback_called)
339
+ end
340
+
341
+ def test_really_destroy
342
+ model = ParanoidModel.new
343
+ model.save
344
+ model.really_destroy!
345
+ refute ParanoidModel.unscoped.exists?(model.id)
346
+ end
347
+
348
+ def test_real_destroy_dependent_destroy
349
+ parent = ParentModel.create
350
+ child = parent.very_related_models.create
351
+ parent.really_destroy!
352
+ refute RelatedModel.unscoped.exists?(child.id)
353
+ end
354
+
355
+ def test_real_destroy_dependent_destroy_after_normal_destroy
356
+ parent = ParentModel.create
357
+ child = parent.very_related_models.create
358
+ parent.destroy
359
+ parent.really_destroy!
360
+ refute RelatedModel.unscoped.exists?(child.id)
361
+ end
362
+
363
+ def test_real_destroy_dependent_destroy_after_normal_destroy_does_not_delete_other_children
364
+ parent_1 = ParentModel.create
365
+ child_1 = parent_1.very_related_models.create
366
+
367
+ parent_2 = ParentModel.create
368
+ child_2 = parent_2.very_related_models.create
369
+ parent_1.destroy
370
+ parent_1.really_destroy!
371
+ assert RelatedModel.unscoped.exists?(child_2.id)
372
+ end
373
+
374
+ if ActiveRecord::VERSION::STRING < "4.1"
375
+ def test_real_destroy
376
+ model = ParanoidModel.new
377
+ model.save
378
+ model.destroy!
379
+ refute ParanoidModel.unscoped.exists?(model.id)
380
+ end
381
+ end
382
+
383
+ def test_real_delete
384
+ model = ParanoidModel.new
385
+ model.save
386
+ model.delete!
387
+
388
+ refute ParanoidModel.unscoped.exists?(model.id)
389
+ end
390
+
391
+ def test_multiple_restore
392
+ a = ParanoidModel.new
393
+ a.save
394
+ a_id = a.id
395
+ a.destroy
396
+
397
+ b = ParanoidModel.new
398
+ b.save
399
+ b_id = b.id
400
+ b.destroy
401
+
402
+ c = ParanoidModel.new
403
+ c.save
404
+ c_id = c.id
405
+ c.destroy
406
+
407
+ ParanoidModel.restore([a_id, c_id])
408
+
409
+ a.reload
410
+ b.reload
411
+ c.reload
412
+
413
+ refute a.destroyed?
414
+ assert b.destroyed?
415
+ refute c.destroyed?
416
+ end
417
+
418
+ def test_restore_with_associations
419
+ parent = ParentModel.create
420
+ first_child = parent.very_related_models.create
421
+ second_child = parent.non_paranoid_models.create
422
+
423
+ parent.destroy
424
+ assert_equal false, parent.deleted_at.nil?
425
+ assert_equal false, first_child.reload.deleted_at.nil?
426
+ assert_equal true, second_child.destroyed?
427
+
428
+ parent.restore!
429
+ assert_equal true, parent.deleted_at.nil?
430
+ assert_equal false, first_child.reload.deleted_at.nil?
431
+ assert_equal true, second_child.destroyed?
432
+
433
+ parent.destroy
434
+ parent.restore(:recursive => true)
435
+ assert_equal true, parent.deleted_at.nil?
436
+ assert_equal true, first_child.reload.deleted_at.nil?
437
+ assert_equal true, second_child.destroyed?
438
+
439
+ parent.destroy
440
+ ParentModel.restore(parent.id, :recursive => true)
441
+ assert_equal true, parent.reload.deleted_at.nil?
442
+ assert_equal true, first_child.reload.deleted_at.nil?
443
+ assert_equal true, second_child.destroyed?
444
+ end
445
+
446
+ # regression tests for #118
447
+ def test_restore_with_has_one_association
448
+ # setup and destroy test objects
449
+ hasOne = ParanoidModelWithHasOne.create
450
+ belongsTo = ParanoidModelWithBelong.create
451
+ anthorClassName = ParanoidModelWithAnthorClassNameBelong.create
452
+ foreignKey = ParanoidModelWithForeignKeyBelong.create
453
+ hasOne.paranoid_model_with_belong = belongsTo
454
+ hasOne.class_name_belong = anthorClassName
455
+ hasOne.paranoid_model_with_foreign_key_belong = foreignKey
456
+ hasOne.save!
457
+
458
+ hasOne.destroy
459
+ assert_equal false, hasOne.deleted_at.nil?
460
+ assert_equal false, belongsTo.deleted_at.nil?
461
+
462
+ # Does it restore has_one associations?
463
+ hasOne.restore(:recursive => true)
464
+ hasOne.save!
465
+
466
+ assert_equal true, hasOne.reload.deleted_at.nil?
467
+ assert_equal true, belongsTo.reload.deleted_at.nil?, "#{belongsTo.deleted_at}"
468
+ assert ParanoidModelWithBelong.with_deleted.reload.count != 0, "There should be a record"
469
+ assert ParanoidModelWithAnthorClassNameBelong.with_deleted.reload.count != 0, "There should be an other record"
470
+ assert ParanoidModelWithForeignKeyBelong.with_deleted.reload.count != 0, "There should be a foreign_key record"
471
+ end
472
+
473
+ def test_new_restore_with_has_one_association
474
+ # setup and destroy test objects
475
+ hasOne = ParanoidModelWithHasOne.create
476
+ belongsTo = ParanoidModelWithBelong.create
477
+ anthorClassName = ParanoidModelWithAnthorClassNameBelong.create
478
+ foreignKey = ParanoidModelWithForeignKeyBelong.create
479
+ hasOne.paranoid_model_with_belong = belongsTo
480
+ hasOne.class_name_belong = anthorClassName
481
+ hasOne.paranoid_model_with_foreign_key_belong = foreignKey
482
+ hasOne.save!
483
+
484
+ hasOne.destroy
485
+ assert_equal false, hasOne.deleted_at.nil?
486
+ assert_equal false, belongsTo.deleted_at.nil?
487
+
488
+ # Does it restore has_one associations?
489
+ newHasOne = ParanoidModelWithHasOne.with_deleted.find(hasOne.id)
490
+ newHasOne.restore(:recursive => true)
491
+ newHasOne.save!
492
+
493
+ assert_equal true, hasOne.reload.deleted_at.nil?
494
+ assert_equal true, belongsTo.reload.deleted_at.nil?, "#{belongsTo.deleted_at}"
495
+ assert ParanoidModelWithBelong.with_deleted.reload.count != 0, "There should be a record"
496
+ assert ParanoidModelWithAnthorClassNameBelong.with_deleted.reload.count != 0, "There should be an other record"
497
+ assert ParanoidModelWithForeignKeyBelong.with_deleted.reload.count != 0, "There should be a foreign_key record"
498
+ end
499
+
500
+ def test_model_restore_with_has_one_association
501
+ # setup and destroy test objects
502
+ hasOne = ParanoidModelWithHasOne.create
503
+ belongsTo = ParanoidModelWithBelong.create
504
+ anthorClassName = ParanoidModelWithAnthorClassNameBelong.create
505
+ foreignKey = ParanoidModelWithForeignKeyBelong.create
506
+ hasOne.paranoid_model_with_belong = belongsTo
507
+ hasOne.class_name_belong = anthorClassName
508
+ hasOne.paranoid_model_with_foreign_key_belong = foreignKey
509
+ hasOne.save!
510
+
511
+ hasOne.destroy
512
+ assert_equal false, hasOne.deleted_at.nil?
513
+ assert_equal false, belongsTo.deleted_at.nil?
514
+
515
+ # Does it restore has_one associations?
516
+ ParanoidModelWithHasOne.restore(hasOne.id, :recursive => true)
517
+ hasOne.save!
518
+
519
+ assert_equal true, hasOne.reload.deleted_at.nil?
520
+ assert_equal true, belongsTo.reload.deleted_at.nil?, "#{belongsTo.deleted_at}"
521
+ assert ParanoidModelWithBelong.with_deleted.reload.count != 0, "There should be a record"
522
+ assert ParanoidModelWithAnthorClassNameBelong.with_deleted.reload.count != 0, "There should be an other record"
523
+ assert ParanoidModelWithForeignKeyBelong.with_deleted.reload.count != 0, "There should be a foreign_key record"
524
+ end
525
+
526
+ def test_restore_with_nil_has_one_association
527
+ # setup and destroy test object
528
+ hasOne = ParanoidModelWithHasOne.create
529
+ hasOne.destroy
530
+ assert_equal false, hasOne.reload.deleted_at.nil?
531
+
532
+ # Does it raise NoMethodException on restore of nil
533
+ hasOne.restore(:recursive => true)
534
+
535
+ assert hasOne.reload.deleted_at.nil?
536
+ end
537
+
538
+ # covers #131
539
+ def test_has_one_really_destroy_with_nil
540
+ model = ParanoidModelWithHasOne.create
541
+ model.really_destroy!
542
+
543
+ refute ParanoidModelWithBelong.unscoped.exists?(model.id)
544
+ end
545
+
546
+ def test_has_one_really_destroy_with_record
547
+ model = ParanoidModelWithHasOne.create { |record| record.build_paranoid_model_with_belong }
548
+ model.really_destroy!
549
+
550
+ refute ParanoidModelWithBelong.unscoped.exists?(model.id)
551
+ end
552
+
553
+ def test_observers_notified
554
+ a = ParanoidModelWithObservers.create
555
+ a.destroy
556
+ a.restore!
557
+
558
+ assert a.observers_notified.select {|args| args == [:before_restore, a]}
559
+ assert a.observers_notified.select {|args| args == [:after_restore, a]}
560
+ end
561
+
562
+ def test_observers_not_notified_if_not_supported
563
+ a = ParanoidModelWithObservers.create
564
+ a.destroy
565
+ a.restore!
566
+ # essentially, we're just ensuring that this doesn't crash
567
+ end
568
+
569
+ def test_i_am_the_destroyer
570
+ output = capture(:stdout) { ParanoidModel.I_AM_THE_DESTROYER! }
571
+ assert_equal %Q{
572
+ Sharon: "There should be a method called I_AM_THE_DESTROYER!"
573
+ Ryan: "What should this method do?"
574
+ Sharon: "It should fix all the spelling errors on the page!"
575
+ }, output
576
+ end
577
+
578
+ def test_destroy_fails_if_callback_raises_exception
579
+ parent = AsplodeModel.create
580
+
581
+ assert_raises(StandardError) { parent.destroy }
582
+
583
+ #transaction should be rolled back, so parent NOT deleted
584
+ refute parent.destroyed?, 'Parent record was destroyed, even though AR callback threw exception'
585
+ end
586
+
587
+ def test_destroy_fails_if_association_callback_raises_exception
588
+ parent = ParentModel.create
589
+ children = []
590
+ 3.times { children << parent.asplode_models.create }
591
+
592
+ assert_raises(StandardError) { parent.destroy }
593
+
594
+ #transaction should be rolled back, so parent and children NOT deleted
595
+ refute parent.destroyed?, 'Parent record was destroyed, even though AR callback threw exception'
596
+ refute children.any?(&:destroyed?), 'Child record was destroyed, even though AR callback threw exception'
597
+ end
598
+
599
+ def test_restore_model_with_different_connection
600
+ ActiveRecord::Base.remove_connection # Disconnect the main connection
601
+ a = WithDifferentConnection.create
602
+ a.destroy!
603
+ a.restore!
604
+ # This test passes if no exception is raised
605
+ connect! # Reconnect the main connection
606
+ end
607
+
608
+ def test_restore_clear_association_cache_if_associations_present
609
+ parent = ParentModel.create
610
+ 3.times { parent.very_related_models.create }
611
+
612
+ parent.destroy
613
+
614
+ assert_equal 0, parent.very_related_models.count
615
+ assert_equal 0, parent.very_related_models.size
616
+
617
+ parent.restore(recursive: true)
618
+
619
+ assert_equal 3, parent.very_related_models.count
620
+ assert_equal 3, parent.very_related_models.size
621
+ end
622
+
623
+ private
624
+ def get_featureful_model
625
+ FeaturefulModel.new(:name => "not empty")
626
+ end
627
+ end
628
+
629
+ # Helper classes
630
+
631
+ class ParanoidModel < ActiveRecord::Base
632
+ belongs_to :parent_model
633
+ acts_as_paranoid
634
+ end
635
+
636
+ class FailCallbackModel < ActiveRecord::Base
637
+ belongs_to :parent_model
638
+ acts_as_paranoid
639
+
640
+ before_destroy { |_| false }
641
+ end
642
+
643
+ class FeaturefulModel < ActiveRecord::Base
644
+ acts_as_paranoid
645
+ validates :name, :presence => true, :uniqueness => true
646
+ end
647
+
648
+ class PlainModel < ActiveRecord::Base
649
+ end
650
+
651
+ class CallbackModel < ActiveRecord::Base
652
+ acts_as_paranoid
653
+ before_destroy {|model| model.instance_variable_set :@destroy_callback_called, true }
654
+ before_restore {|model| model.instance_variable_set :@restore_callback_called, true }
655
+ before_update {|model| model.instance_variable_set :@update_callback_called, true }
656
+ before_save {|model| model.instance_variable_set :@save_callback_called, true}
657
+
658
+ after_destroy {|model| model.instance_variable_set :@after_destroy_callback_called, true }
659
+ after_commit {|model| model.instance_variable_set :@after_commit_callback_called, true }
660
+
661
+ validate {|model| model.instance_variable_set :@validate_called, true }
662
+
663
+ def remove_called_variables
664
+ instance_variables.each {|name| (name.to_s.end_with?('_called')) ? remove_instance_variable(name) : nil}
665
+ end
666
+ end
667
+
668
+ class ParentModel < ActiveRecord::Base
669
+ acts_as_paranoid
670
+ has_many :paranoid_models
671
+ has_many :related_models
672
+ has_many :very_related_models, :class_name => 'RelatedModel', dependent: :destroy
673
+ has_many :non_paranoid_models, dependent: :destroy
674
+ has_many :asplode_models, dependent: :destroy
675
+ end
676
+
677
+ class RelatedModel < ActiveRecord::Base
678
+ acts_as_paranoid
679
+ belongs_to :parent_model
680
+ end
681
+
682
+ class Employer < ActiveRecord::Base
683
+ acts_as_paranoid
684
+ has_many :jobs
685
+ has_many :employees, :through => :jobs
686
+ end
687
+
688
+ class Employee < ActiveRecord::Base
689
+ acts_as_paranoid
690
+ has_many :jobs
691
+ has_many :employers, :through => :jobs
692
+ end
693
+
694
+ class Job < ActiveRecord::Base
695
+ acts_as_paranoid
696
+ belongs_to :employer
697
+ belongs_to :employee
698
+ end
699
+
700
+ class CustomColumnModel < ActiveRecord::Base
701
+ acts_as_paranoid column: :destroyed_at
702
+ end
703
+
704
+ class CustomSentinelModel < ActiveRecord::Base
705
+ acts_as_paranoid sentinel_value: DateTime.new(0)
706
+ end
707
+
708
+ class NonParanoidModel < ActiveRecord::Base
709
+ end
710
+
711
+ class ParanoidModelWithObservers < ParanoidModel
712
+ def observers_notified
713
+ @observers_notified ||= []
714
+ end
715
+
716
+ def self.notify_observer(*args)
717
+ observers_notified << args
718
+ end
719
+ end
720
+
721
+ class ParanoidModelWithoutObservers < ParanoidModel
722
+ self.class.send(remove_method :notify_observers) if method_defined?(:notify_observers)
723
+ end
724
+
725
+ # refer back to regression test for #118
726
+ class ParanoidModelWithHasOne < ParanoidModel
727
+ has_one :paranoid_model_with_belong, :dependent => :destroy
728
+ has_one :class_name_belong, :dependent => :destroy, :class_name => "ParanoidModelWithAnthorClassNameBelong"
729
+ has_one :paranoid_model_with_foreign_key_belong, :dependent => :destroy, :foreign_key => "has_one_foreign_key_id"
730
+ end
731
+
732
+ class ParanoidModelWithBelong < ActiveRecord::Base
733
+ acts_as_paranoid
734
+ belongs_to :paranoid_model_with_has_one
735
+ end
736
+
737
+ class ParanoidModelWithAnthorClassNameBelong < ActiveRecord::Base
738
+ acts_as_paranoid
739
+ belongs_to :paranoid_model_with_has_one
740
+ end
741
+
742
+ class ParanoidModelWithForeignKeyBelong < ActiveRecord::Base
743
+ acts_as_paranoid
744
+ belongs_to :paranoid_model_with_has_one
745
+ end
746
+
747
+ class FlaggedModel < PlainModel
748
+ acts_as_paranoid :flag_column => :is_deleted
749
+ end
750
+
751
+ class FlaggedModelWithCustomIndex < PlainModel
752
+ acts_as_paranoid :flag_column => :is_deleted, :indexed_column => :is_deleted
753
+ end
754
+
755
+ class AsplodeModel < ActiveRecord::Base
756
+ acts_as_paranoid
757
+ before_destroy do |r|
758
+ raise StandardError, 'ASPLODE!'
759
+ end
760
+ end