seed_dump 3.4.0 → 3.4.1

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.
@@ -1,613 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe SeedDump do
4
- describe '.dump_using_environment' do
5
- # Schema creation and model loading are handled in spec_helper's before(:suite).
6
-
7
- before(:each) do
8
- # Clean DB and create a fresh sample before each example
9
- DatabaseCleaner.start
10
- FactoryBot.create(:sample)
11
- end
12
-
13
- after(:each) do
14
- # Clean DB after each example
15
- DatabaseCleaner.clean
16
- end
17
-
18
-
19
- describe 'APPEND' do
20
- it "should specify append as true if the APPEND env var is 'true'" do
21
- expect(SeedDump).to receive(:dump).with(anything, include(append: true))
22
- # Need to stub dump for other models if they exist in this context
23
- allow(SeedDump).to receive(:dump).with(AnotherSample, anything) if defined?(AnotherSample)
24
- allow(SeedDump).to receive(:dump).with(YetAnotherSample, anything) if defined?(YetAnotherSample)
25
- SeedDump.dump_using_environment('APPEND' => 'true')
26
- end
27
-
28
- it "should specify append as true if the APPEND env var is 'TRUE'" do
29
- expect(SeedDump).to receive(:dump).with(anything, include(append: true))
30
- allow(SeedDump).to receive(:dump).with(AnotherSample, anything) if defined?(AnotherSample)
31
- allow(SeedDump).to receive(:dump).with(YetAnotherSample, anything) if defined?(YetAnotherSample)
32
- SeedDump.dump_using_environment('APPEND' => 'TRUE')
33
- end
34
-
35
- it "should specify append as false the first time if the APPEND env var is not 'true' (and true after that)" do
36
- FactoryBot.create(:another_sample)
37
- expect(SeedDump).to receive(:dump).with(Sample, include(append: false)).ordered
38
- expect(SeedDump).to receive(:dump).with(AnotherSample, include(append: true)).ordered
39
- # Explicitly set MODELS to control order and prevent other models interfering
40
- SeedDump.dump_using_environment('APPEND' => 'false', 'MODELS' => 'Sample,AnotherSample')
41
- end
42
- end
43
-
44
- describe 'BATCH_SIZE' do
45
- it 'should pass along the specified batch size' do
46
- expect(SeedDump).to receive(:dump).with(anything, include(batch_size: 17))
47
- allow(SeedDump).to receive(:dump).with(AnotherSample, anything) if defined?(AnotherSample)
48
- allow(SeedDump).to receive(:dump).with(YetAnotherSample, anything) if defined?(YetAnotherSample)
49
- SeedDump.dump_using_environment('BATCH_SIZE' => '17')
50
- end
51
-
52
- it 'should pass along a nil batch size if BATCH_SIZE is not specified' do
53
- expect(SeedDump).to receive(:dump).with(anything, include(batch_size: nil))
54
- allow(SeedDump).to receive(:dump).with(AnotherSample, anything) if defined?(AnotherSample)
55
- allow(SeedDump).to receive(:dump).with(YetAnotherSample, anything) if defined?(YetAnotherSample)
56
- SeedDump.dump_using_environment
57
- end
58
- end
59
-
60
- describe 'EXCLUDE' do
61
- it 'should pass along any attributes to be excluded' do
62
- expect(SeedDump).to receive(:dump).with(anything, include(exclude: [:baggins, :saggins]))
63
- allow(SeedDump).to receive(:dump).with(AnotherSample, anything) if defined?(AnotherSample)
64
- allow(SeedDump).to receive(:dump).with(YetAnotherSample, anything) if defined?(YetAnotherSample)
65
- SeedDump.dump_using_environment('EXCLUDE' => 'baggins,saggins')
66
- end
67
-
68
- it 'should pass an empty array when EXCLUDE is set to empty string (issue #147)' do
69
- expect(SeedDump).to receive(:dump).with(anything, include(exclude: []))
70
- allow(SeedDump).to receive(:dump).with(AnotherSample, anything) if defined?(AnotherSample)
71
- allow(SeedDump).to receive(:dump).with(YetAnotherSample, anything) if defined?(YetAnotherSample)
72
- SeedDump.dump_using_environment('EXCLUDE' => '')
73
- end
74
-
75
- it 'should pass nil when EXCLUDE is not set (to use default excludes)' do
76
- expect(SeedDump).to receive(:dump).with(anything, include(exclude: nil))
77
- allow(SeedDump).to receive(:dump).with(AnotherSample, anything) if defined?(AnotherSample)
78
- allow(SeedDump).to receive(:dump).with(YetAnotherSample, anything) if defined?(YetAnotherSample)
79
- SeedDump.dump_using_environment
80
- end
81
-
82
- it 'should pass an empty array when INCLUDE_ALL is true (issue #147)' do
83
- expect(SeedDump).to receive(:dump).with(anything, include(exclude: []))
84
- allow(SeedDump).to receive(:dump).with(AnotherSample, anything) if defined?(AnotherSample)
85
- allow(SeedDump).to receive(:dump).with(YetAnotherSample, anything) if defined?(YetAnotherSample)
86
- SeedDump.dump_using_environment('INCLUDE_ALL' => 'true')
87
- end
88
-
89
- it 'should let explicit EXCLUDE override INCLUDE_ALL' do
90
- expect(SeedDump).to receive(:dump).with(anything, include(exclude: [:some_field]))
91
- allow(SeedDump).to receive(:dump).with(AnotherSample, anything) if defined?(AnotherSample)
92
- allow(SeedDump).to receive(:dump).with(YetAnotherSample, anything) if defined?(YetAnotherSample)
93
- SeedDump.dump_using_environment('INCLUDE_ALL' => 'true', 'EXCLUDE' => 'some_field')
94
- end
95
- end
96
-
97
- describe 'FILE' do
98
- it 'should pass the FILE parameter to the dump method correctly' do
99
- expect(SeedDump).to receive(:dump).with(anything, include(file: 'blargle'))
100
- allow(SeedDump).to receive(:dump).with(AnotherSample, anything) if defined?(AnotherSample)
101
- allow(SeedDump).to receive(:dump).with(YetAnotherSample, anything) if defined?(YetAnotherSample)
102
- SeedDump.dump_using_environment('FILE' => 'blargle')
103
- end
104
-
105
- it 'should pass db/seeds.rb as the file parameter if no FILE is specified' do
106
- expect(SeedDump).to receive(:dump).with(anything, include(file: 'db/seeds.rb'))
107
- allow(SeedDump).to receive(:dump).with(AnotherSample, anything) if defined?(AnotherSample)
108
- allow(SeedDump).to receive(:dump).with(YetAnotherSample, anything) if defined?(YetAnotherSample)
109
- SeedDump.dump_using_environment
110
- end
111
- end
112
-
113
- describe 'LIMIT' do
114
- it 'should apply the specified limit to the records' do
115
- relation_double = double('ActiveRecord relation double')
116
- allow(Sample).to receive(:limit).with(5).and_return(relation_double)
117
- expect(SeedDump).to receive(:dump).with(relation_double, anything)
118
- # Allow other calls if necessary
119
- allow(SeedDump).to receive(:dump).with(instance_of(Class), anything) unless relation_double.is_a?(Class)
120
-
121
-
122
- SeedDump.dump_using_environment('LIMIT' => '5')
123
- end
124
- end
125
-
126
- describe 'MODEL_LIMITS (issue #142)' do
127
- # MODEL_LIMITS allows per-model limit overrides to prevent LIMIT from breaking
128
- # associations. For example, if Teacher has_many Students, you can set
129
- # MODEL_LIMITS=Teacher:0 to dump all teachers while limiting other models.
130
-
131
- it 'should apply per-model limit when specified' do
132
- FactoryBot.create(:another_sample)
133
-
134
- sample_relation = double('Sample relation')
135
- another_sample_relation = double('AnotherSample relation')
136
-
137
- allow(Sample).to receive(:limit).with(5).and_return(sample_relation)
138
- allow(AnotherSample).to receive(:limit).with(20).and_return(another_sample_relation)
139
-
140
- expect(SeedDump).to receive(:dump).with(sample_relation, anything)
141
- expect(SeedDump).to receive(:dump).with(another_sample_relation, anything)
142
-
143
- SeedDump.dump_using_environment(
144
- 'MODELS' => 'Sample,AnotherSample',
145
- 'MODEL_LIMITS' => 'Sample:5,AnotherSample:20'
146
- )
147
- end
148
-
149
- it 'should interpret 0 as unlimited (dump all records)' do
150
- # When MODEL_LIMITS=Sample:0, Sample should not have limit applied
151
- expect(Sample).not_to receive(:limit)
152
- expect(SeedDump).to receive(:dump).with(Sample, anything)
153
-
154
- SeedDump.dump_using_environment(
155
- 'MODELS' => 'Sample',
156
- 'MODEL_LIMITS' => 'Sample:0'
157
- )
158
- end
159
-
160
- it 'should fall back to global LIMIT for models not in MODEL_LIMITS' do
161
- FactoryBot.create(:another_sample)
162
-
163
- # Sample has specific limit of 5, AnotherSample falls back to global LIMIT of 10
164
- sample_relation = double('Sample relation')
165
- another_sample_relation = double('AnotherSample relation')
166
-
167
- allow(Sample).to receive(:limit).with(5).and_return(sample_relation)
168
- allow(AnotherSample).to receive(:limit).with(10).and_return(another_sample_relation)
169
-
170
- expect(SeedDump).to receive(:dump).with(sample_relation, anything)
171
- expect(SeedDump).to receive(:dump).with(another_sample_relation, anything)
172
-
173
- SeedDump.dump_using_environment(
174
- 'MODELS' => 'Sample,AnotherSample',
175
- 'LIMIT' => '10',
176
- 'MODEL_LIMITS' => 'Sample:5'
177
- )
178
- end
179
-
180
- it 'should work with MODEL_LIMITS alone (no global LIMIT)' do
181
- FactoryBot.create(:another_sample)
182
-
183
- # Sample has limit of 5, AnotherSample has no limit (dumps all)
184
- sample_relation = double('Sample relation')
185
-
186
- allow(Sample).to receive(:limit).with(5).and_return(sample_relation)
187
- expect(AnotherSample).not_to receive(:limit)
188
-
189
- expect(SeedDump).to receive(:dump).with(sample_relation, anything)
190
- expect(SeedDump).to receive(:dump).with(AnotherSample, anything)
191
-
192
- SeedDump.dump_using_environment(
193
- 'MODELS' => 'Sample,AnotherSample',
194
- 'MODEL_LIMITS' => 'Sample:5'
195
- )
196
- end
197
-
198
- it 'should handle whitespace in MODEL_LIMITS' do
199
- sample_relation = double('Sample relation')
200
- allow(Sample).to receive(:limit).with(5).and_return(sample_relation)
201
- expect(SeedDump).to receive(:dump).with(sample_relation, anything)
202
-
203
- SeedDump.dump_using_environment(
204
- 'MODELS' => 'Sample',
205
- 'MODEL_LIMITS' => ' Sample : 5 '
206
- )
207
- end
208
- end
209
-
210
- ['', 'S'].each do |model_suffix|
211
- model_env = 'MODEL' + model_suffix
212
-
213
- describe model_env do
214
- context "if #{model_env} is not specified" do
215
- it "should dump all non-empty models" do
216
- FactoryBot.create(:another_sample)
217
- expect(SeedDump).to receive(:dump).with(Sample, anything)
218
- expect(SeedDump).to receive(:dump).with(AnotherSample, anything)
219
- SeedDump.dump_using_environment
220
- end
221
- end
222
-
223
- context "if #{model_env} is specified" do
224
- it "should dump only the specified model" do
225
- FactoryBot.create(:another_sample)
226
- expect(SeedDump).to receive(:dump).with(Sample, anything)
227
- # Ensure the other model is NOT dumped
228
- expect(SeedDump).not_to receive(:dump).with(AnotherSample, anything)
229
- SeedDump.dump_using_environment(model_env => 'Sample')
230
- end
231
-
232
- it "should not dump empty models" do
233
- expect(SeedDump).not_to receive(:dump).with(EmptyModel, anything)
234
- # Ensure Sample is still dumped
235
- expect(SeedDump).to receive(:dump).with(Sample, anything)
236
- SeedDump.dump_using_environment(model_env => 'EmptyModel, Sample')
237
- end
238
- end
239
- end
240
- end
241
-
242
- describe "MODELS_EXCLUDE" do
243
- it "should dump all non-empty models except the specified models" do
244
- FactoryBot.create(:another_sample)
245
- expect(SeedDump).to receive(:dump).with(Sample, anything)
246
- # Ensure the excluded model is NOT dumped
247
- expect(SeedDump).not_to receive(:dump).with(AnotherSample, anything)
248
- SeedDump.dump_using_environment('MODELS_EXCLUDE' => 'AnotherSample')
249
- end
250
- end
251
-
252
- describe 'model names ending in s (issue #121)' do
253
- # Model names like "Boss" are incorrectly singularized to "Bos" when
254
- # processing MODELS=Boss, causing NameError: uninitialized constant Bos.
255
- # The fix should use the exact model name if it resolves to a valid constant.
256
-
257
- it 'should handle model name "Boss" without extra s' do
258
- FactoryBot.create(:boss)
259
- expect(SeedDump).to receive(:dump).with(Boss, anything)
260
- SeedDump.dump_using_environment('MODELS' => 'Boss')
261
- end
262
-
263
- it 'should handle model name "boss" (lowercase) without extra s' do
264
- FactoryBot.create(:boss)
265
- expect(SeedDump).to receive(:dump).with(Boss, anything)
266
- SeedDump.dump_using_environment('MODELS' => 'boss')
267
- end
268
-
269
- it 'should handle MODELS_EXCLUDE with model names ending in s' do
270
- FactoryBot.create(:boss)
271
- expect(SeedDump).to receive(:dump).with(Sample, anything)
272
- expect(SeedDump).not_to receive(:dump).with(Boss, anything)
273
- SeedDump.dump_using_environment('MODELS_EXCLUDE' => 'Boss')
274
- end
275
-
276
- it 'should still handle plural model names (e.g., "samples" -> Sample)' do
277
- expect(SeedDump).to receive(:dump).with(Sample, anything)
278
- SeedDump.dump_using_environment('MODELS' => 'samples')
279
- end
280
-
281
- it 'should still handle plural model names in MODELS_EXCLUDE' do
282
- FactoryBot.create(:another_sample)
283
- expect(SeedDump).to receive(:dump).with(AnotherSample, anything)
284
- expect(SeedDump).not_to receive(:dump).with(Sample, anything)
285
- SeedDump.dump_using_environment('MODELS_EXCLUDE' => 'samples')
286
- end
287
- end
288
-
289
- describe 'INSERT_ALL (issue #153)' do
290
- it "should specify insert_all as true if the INSERT_ALL env var is 'true'" do
291
- expect(SeedDump).to receive(:dump).with(anything, include(insert_all: true))
292
- SeedDump.dump_using_environment('INSERT_ALL' => 'true')
293
- end
294
-
295
- it "should specify insert_all as true if the INSERT_ALL env var is 'TRUE'" do
296
- expect(SeedDump).to receive(:dump).with(anything, include(insert_all: true))
297
- SeedDump.dump_using_environment('INSERT_ALL' => 'TRUE')
298
- end
299
-
300
- it "should specify insert_all as false if the INSERT_ALL env var is not 'true'" do
301
- expect(SeedDump).to receive(:dump).with(anything, include(insert_all: false))
302
- SeedDump.dump_using_environment('INSERT_ALL' => 'false')
303
- end
304
-
305
- it "should specify insert_all as false if the INSERT_ALL env var is not set" do
306
- expect(SeedDump).to receive(:dump).with(anything, include(insert_all: false))
307
- SeedDump.dump_using_environment
308
- end
309
- end
310
-
311
- describe 'HEADER (issue #126 - comment header in seed file)' do
312
- # HEADER adds a comment at the top of the seed file showing that seed_dump
313
- # was used and what options were specified for traceability.
314
-
315
- it "should specify header as true if the HEADER env var is 'true'" do
316
- expect(SeedDump).to receive(:dump).with(anything, include(header: true))
317
- SeedDump.dump_using_environment('HEADER' => 'true')
318
- end
319
-
320
- it "should specify header as true if the HEADER env var is 'TRUE'" do
321
- expect(SeedDump).to receive(:dump).with(anything, include(header: true))
322
- SeedDump.dump_using_environment('HEADER' => 'TRUE')
323
- end
324
-
325
- it "should specify header as false if the HEADER env var is not 'true'" do
326
- expect(SeedDump).to receive(:dump).with(anything, include(header: false))
327
- SeedDump.dump_using_environment('HEADER' => 'false')
328
- end
329
-
330
- it "should specify header as false if the HEADER env var is not set" do
331
- expect(SeedDump).to receive(:dump).with(anything, include(header: false))
332
- SeedDump.dump_using_environment
333
- end
334
- end
335
-
336
- describe 'UPSERT_ALL (issue #104 - non-continuous IDs / foreign key preservation)' do
337
- # UPSERT_ALL solves the problem where deleted rows cause foreign key
338
- # references to become invalid after reimporting seeds. When IDs are
339
- # preserved via upsert_all, foreign key references remain correct.
340
-
341
- it "should specify upsert_all as true if the UPSERT_ALL env var is 'true'" do
342
- expect(SeedDump).to receive(:dump).with(anything, include(upsert_all: true))
343
- SeedDump.dump_using_environment('UPSERT_ALL' => 'true')
344
- end
345
-
346
- it "should specify upsert_all as true if the UPSERT_ALL env var is 'TRUE'" do
347
- expect(SeedDump).to receive(:dump).with(anything, include(upsert_all: true))
348
- SeedDump.dump_using_environment('UPSERT_ALL' => 'TRUE')
349
- end
350
-
351
- it "should specify upsert_all as false if the UPSERT_ALL env var is not 'true'" do
352
- expect(SeedDump).to receive(:dump).with(anything, include(upsert_all: false))
353
- SeedDump.dump_using_environment('UPSERT_ALL' => 'false')
354
- end
355
-
356
- it "should specify upsert_all as false if the UPSERT_ALL env var is not set" do
357
- expect(SeedDump).to receive(:dump).with(anything, include(upsert_all: false))
358
- SeedDump.dump_using_environment
359
- end
360
- end
361
-
362
- it 'should handle non-model classes in ActiveRecord::Base.descendants (issue #112)' do
363
- # Create a class that inherits from ActiveRecord::Base but doesn't respond to exists?
364
- # This simulates edge cases like abstract classes or improperly configured models
365
- non_model_class = Class.new(ActiveRecord::Base) do
366
- def self.exists?
367
- raise NoMethodError, "undefined method `exists?' for #{self}"
368
- end
369
-
370
- def self.table_exists?
371
- raise NoMethodError, "undefined method `table_exists?' for #{self}"
372
- end
373
- end
374
- Object.const_set('NonModelClass', non_model_class)
375
-
376
- allow(SeedDump).to receive(:dump)
377
-
378
- begin
379
- expect { SeedDump.dump_using_environment }.not_to raise_error
380
- ensure
381
- Object.send(:remove_const, :NonModelClass)
382
- end
383
- end
384
-
385
- it 'should run ok without ActiveRecord::SchemaMigration being set (needed for Rails Engines)' do
386
- # Ensure Sample model exists before trying to remove SchemaMigration
387
- expect(defined?(Sample)).to be_truthy
388
- schema_migration_defined = defined?(ActiveRecord::SchemaMigration)
389
- schema_migration = ActiveRecord::SchemaMigration if schema_migration_defined
390
-
391
- # Stub the dump method before removing the constant
392
- allow(SeedDump).to receive(:dump)
393
-
394
- # Use remove_const carefully only if it's defined
395
- ActiveRecord.send(:remove_const, :SchemaMigration) if schema_migration_defined
396
-
397
- begin
398
- expect { SeedDump.dump_using_environment }.not_to raise_error
399
- ensure
400
- # Ensure the constant is restored only if it was originally defined
401
- ActiveRecord.const_set(:SchemaMigration, schema_migration) if schema_migration_defined && !defined?(ActiveRecord::SchemaMigration)
402
- end
403
- end
404
-
405
- describe 'HABTM deduplication (issues #26, #114)' do
406
- # When using has_and_belongs_to_many, Rails creates two auto-generated models
407
- # that point to the same join table (e.g., User::HABTM_Roles and Role::HABTM_Users).
408
- # We should only dump one of them to avoid duplicate seed data.
409
-
410
- it 'should deduplicate HABTM models that share the same table' do
411
- # Create mock HABTM classes that share the same table_name
412
- habtm_class_1 = Class.new(ActiveRecord::Base) do
413
- self.table_name = 'roles_users'
414
- def self.name; 'User::HABTM_Roles'; end
415
- def self.to_s; name; end
416
- end
417
-
418
- habtm_class_2 = Class.new(ActiveRecord::Base) do
419
- self.table_name = 'roles_users'
420
- def self.name; 'Role::HABTM_Users'; end
421
- def self.to_s; name; end
422
- end
423
-
424
- # Temporarily add these to AR descendants by setting constants
425
- User = Class.new unless defined?(User)
426
- Role = Class.new unless defined?(Role)
427
- User.const_set('HABTM_Roles', habtm_class_1)
428
- Role.const_set('HABTM_Users', habtm_class_2)
429
-
430
- begin
431
- # Stub exists? and table_exists? to return true
432
- allow(habtm_class_1).to receive(:table_exists?).and_return(true)
433
- allow(habtm_class_1).to receive(:exists?).and_return(true)
434
- allow(habtm_class_2).to receive(:table_exists?).and_return(true)
435
- allow(habtm_class_2).to receive(:exists?).and_return(true)
436
-
437
- # Track which models get dumped
438
- dumped_models = []
439
- allow(SeedDump).to receive(:dump) do |model, _options|
440
- dumped_models << model.to_s
441
- end
442
-
443
- SeedDump.dump_using_environment
444
-
445
- # Only one of the HABTM models should be dumped, not both
446
- habtm_dumps = dumped_models.select { |m| m.include?('HABTM_') }
447
- habtm_tables = habtm_dumps.map { |m| m.include?('HABTM_Roles') ? 'roles_users' : 'roles_users' }
448
-
449
- expect(habtm_dumps.size).to eq(1), "Expected 1 HABTM model to be dumped, got #{habtm_dumps.size}: #{habtm_dumps}"
450
- ensure
451
- User.send(:remove_const, 'HABTM_Roles') if defined?(User::HABTM_Roles)
452
- Role.send(:remove_const, 'HABTM_Users') if defined?(Role::HABTM_Users)
453
- Object.send(:remove_const, 'User') if defined?(User) && User.is_a?(Class) && User.superclass == Object
454
- Object.send(:remove_const, 'Role') if defined?(Role) && Role.is_a?(Class) && Role.superclass == Object
455
- end
456
- end
457
-
458
- it 'should not affect non-HABTM models with different tables' do
459
- # Sample and AnotherSample have different tables, so both should dump
460
- allow(SeedDump).to receive(:dump)
461
-
462
- FactoryBot.create(:another_sample)
463
- expect(SeedDump).to receive(:dump).with(Sample, anything)
464
- expect(SeedDump).to receive(:dump).with(AnotherSample, anything)
465
-
466
- SeedDump.dump_using_environment
467
- end
468
- end
469
-
470
- describe 'foreign key dependency ordering (issues #78, #83)' do
471
- # Models with foreign key dependencies should be dumped in the correct order
472
- # so that seeds can be imported without foreign key violations.
473
- # For example: Author -> Book -> Review means Author should be dumped first,
474
- # then Book, then Review.
475
-
476
- it 'should order models by foreign key dependencies' do
477
- # Create records with dependencies
478
- author = FactoryBot.create(:author)
479
- book = FactoryBot.create(:book, author: author)
480
- FactoryBot.create(:review, book: book)
481
-
482
- # Track which models get dumped and in what order
483
- dumped_models = []
484
- allow(SeedDump).to receive(:dump) do |model, _options|
485
- dumped_models << model.to_s
486
- end
487
-
488
- SeedDump.dump_using_environment('MODELS' => 'Review,Book,Author')
489
-
490
- # Verify the order: Author must come before Book, Book must come before Review
491
- author_index = dumped_models.index('Author')
492
- book_index = dumped_models.index('Book')
493
- review_index = dumped_models.index('Review')
494
-
495
- expect(author_index).not_to be_nil, "Author should be in the dump"
496
- expect(book_index).not_to be_nil, "Book should be in the dump"
497
- expect(review_index).not_to be_nil, "Review should be in the dump"
498
-
499
- expect(author_index).to be < book_index,
500
- "Author (index #{author_index}) should be dumped before Book (index #{book_index})"
501
- expect(book_index).to be < review_index,
502
- "Book (index #{book_index}) should be dumped before Review (index #{review_index})"
503
- end
504
-
505
- it 'should handle models without foreign key dependencies' do
506
- # Sample has no foreign keys, should still be dumped normally
507
- FactoryBot.create(:author)
508
-
509
- dumped_models = []
510
- allow(SeedDump).to receive(:dump) do |model, _options|
511
- dumped_models << model.to_s
512
- end
513
-
514
- SeedDump.dump_using_environment('MODELS' => 'Sample,Author')
515
-
516
- expect(dumped_models).to include('Sample')
517
- expect(dumped_models).to include('Author')
518
- end
519
-
520
- it 'should handle circular dependencies gracefully' do
521
- # Create models with circular dependency for testing
522
- # PersonA belongs_to PersonB, PersonB belongs_to PersonA
523
- person_a_class = Class.new(ActiveRecord::Base) do
524
- self.table_name = 'person_as'
525
- end
526
- person_b_class = Class.new(ActiveRecord::Base) do
527
- self.table_name = 'person_bs'
528
- end
529
- Object.const_set('PersonA', person_a_class)
530
- Object.const_set('PersonB', person_b_class)
531
-
532
- # Add circular associations after both classes exist
533
- PersonA.belongs_to :person_b, optional: true
534
- PersonB.belongs_to :person_a, optional: true
535
-
536
- # Create tables
537
- ActiveRecord::Schema.define do
538
- create_table 'person_as', force: true do |t|
539
- t.references :person_b
540
- end
541
- create_table 'person_bs', force: true do |t|
542
- t.references :person_a
543
- end
544
- end
545
-
546
- # Create records
547
- PersonA.create!
548
- PersonB.create!
549
-
550
- begin
551
- dumped_models = []
552
- allow(SeedDump).to receive(:dump) do |model, _options|
553
- dumped_models << model.to_s
554
- end
555
-
556
- # Should not raise an error despite circular dependency
557
- expect {
558
- SeedDump.dump_using_environment('MODELS' => 'PersonA,PersonB')
559
- }.not_to raise_error
560
-
561
- # Both models should be dumped
562
- expect(dumped_models).to include('PersonA')
563
- expect(dumped_models).to include('PersonB')
564
- ensure
565
- Object.send(:remove_const, :PersonA)
566
- Object.send(:remove_const, :PersonB)
567
- end
568
- end
569
- end
570
-
571
- describe 'STI deduplication (issue #120)' do
572
- # When using STI (Single Table Inheritance), multiple model classes share
573
- # the same database table. For example, AdminUser < BaseUser and
574
- # GuestUser < BaseUser all use the 'base_users' table.
575
- # Without deduplication, each STI subclass would be dumped separately,
576
- # creating duplicate records in the seeds file.
577
-
578
- it 'should deduplicate STI models by keeping only the base class' do
579
- # Create records of different STI types
580
- FactoryBot.create(:admin_user)
581
- FactoryBot.create(:guest_user)
582
-
583
- # Track which models get dumped
584
- dumped_models = []
585
- allow(SeedDump).to receive(:dump) do |model, _options|
586
- dumped_models << model.to_s
587
- end
588
-
589
- SeedDump.dump_using_environment
590
-
591
- # Only BaseUser should be dumped, not AdminUser or GuestUser
592
- expect(dumped_models).to include('BaseUser')
593
- expect(dumped_models).not_to include('AdminUser')
594
- expect(dumped_models).not_to include('GuestUser')
595
- end
596
-
597
- it 'should dump all records through the base class' do
598
- # Create records of different STI types
599
- FactoryBot.create(:admin_user)
600
- FactoryBot.create(:guest_user)
601
- FactoryBot.create(:base_user)
602
-
603
- # The base class should have access to all records
604
- result = SeedDump.dump(BaseUser)
605
-
606
- # All three records should be in the output
607
- expect(result).to include('AdminUser')
608
- expect(result).to include('GuestUser')
609
- expect(result).to include('BaseUser')
610
- end
611
- end
612
- end
613
- end
@@ -1,21 +0,0 @@
1
- FactoryBot.define do
2
- factory :another_sample do
3
- # Use block syntax for attribute definitions
4
- string { 'string' }
5
- text { 'text' }
6
- integer { 42 }
7
- float { 3.14 }
8
- # BigDecimal needs to be initialized within the block
9
- decimal { BigDecimal('2.72') }
10
- # Use standard Ruby DateTime/Time parse methods directly
11
- datetime { DateTime.parse('July 4, 1776 7:14pm UTC') }
12
- time { Time.parse('3:15am UTC') }
13
- date { Date.parse('November 19, 1863') }
14
- binary { 'binary' }
15
- boolean { false }
16
- # created_at and updated_at are typically handled by ActiveRecord timestamps
17
- # but can be defined explicitly if needed for specific tests.
18
- # created_at { Time.now }
19
- # updated_at { Time.now }
20
- end
21
- end
@@ -1,5 +0,0 @@
1
- FactoryBot.define do
2
- factory :author do
3
- name { 'Test Author' }
4
- end
5
- end
@@ -1,16 +0,0 @@
1
- FactoryBot.define do
2
- factory :base_user do
3
- name { 'Test User' }
4
- email { 'user@example.com' }
5
- end
6
-
7
- factory :admin_user do
8
- name { 'Admin User' }
9
- email { 'admin@example.com' }
10
- end
11
-
12
- factory :guest_user do
13
- name { 'Guest User' }
14
- email { 'guest@example.com' }
15
- end
16
- end
@@ -1,6 +0,0 @@
1
- FactoryBot.define do
2
- factory :book do
3
- title { 'Test Book' }
4
- association :author
5
- end
6
- end
@@ -1,5 +0,0 @@
1
- FactoryBot.define do
2
- factory :boss do
3
- name { 'The Boss' }
4
- end
5
- end
@@ -1,7 +0,0 @@
1
- FactoryBot.define do
2
- factory :review do
3
- content { 'Great book!' }
4
- rating { 5 }
5
- association :book
6
- end
7
- end