forked from zdennis/activerecord-import
-
Notifications
You must be signed in to change notification settings - Fork 0
/
import_test.rb
349 lines (296 loc) · 12.4 KB
/
import_test.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
require File.expand_path('../test_helper', __FILE__)
describe "#import" do
it "should return the number of inserts performed" do
# see ActiveRecord::ConnectionAdapters::AbstractAdapter test for more specifics
assert_difference "Topic.count", +10 do
result = Topic.import Build(3, :topics)
assert result.num_inserts > 0
result = Topic.import Build(7, :topics)
assert result.num_inserts > 0
end
end
it "should not produce an error when importing empty arrays" do
assert_nothing_raised do
Topic.import []
Topic.import %w(title author_name), []
end
end
describe "with non-default ActiveRecord models" do
context "that have a non-standard primary key (that is no sequence)" do
it "should import models successfully" do
assert_difference "Widget.count", +3 do
Widget.import Build(3, :widgets)
end
end
end
end
context "with :validation option" do
let(:columns) { %w(title author_name) }
let(:valid_values) { [[ "LDAP", "Jerry Carter"], ["Rails Recipes", "Chad Fowler"]] }
let(:invalid_values) { [[ "The RSpec Book", ""], ["Agile+UX", ""]] }
context "with validation checks turned off" do
it "should import valid data" do
assert_difference "Topic.count", +2 do
result = Topic.import columns, valid_values, :validate => false
end
end
it "should import invalid data" do
assert_difference "Topic.count", +2 do
result = Topic.import columns, invalid_values, :validate => false
end
end
it 'should raise a specific error if a column does not exist' do
assert_raises ActiveRecord::Import::MissingColumnError do
Topic.import ['foo'], [['bar']], :validate => false
end
end
end
context "with validation checks turned on" do
it "should import valid data" do
assert_difference "Topic.count", +2 do
result = Topic.import columns, valid_values, :validate => true
end
end
it "should not import invalid data" do
assert_no_difference "Topic.count" do
result = Topic.import columns, invalid_values, :validate => true
end
end
it "should report the failed instances" do
results = Topic.import columns, invalid_values, :validate => true
assert_equal invalid_values.size, results.failed_instances.size
results.failed_instances.each{ |e| assert_kind_of Topic, e }
end
it "should import valid data when mixed with invalid data" do
assert_difference "Topic.count", +2 do
result = Topic.import columns, valid_values + invalid_values, :validate => true
end
assert_equal 0, Topic.find_all_by_title(invalid_values.map(&:first)).count
end
end
end
context "with :all_or_none option" do
let(:columns) { %w(title author_name) }
let(:valid_values) { [[ "LDAP", "Jerry Carter"], ["Rails Recipes", "Chad Fowler"]] }
let(:invalid_values) { [[ "The RSpec Book", ""], ["Agile+UX", ""]] }
let(:mixed_values) { valid_values + invalid_values }
context "with validation checks turned on" do
it "should import valid data" do
assert_difference "Topic.count", +2 do
result = Topic.import columns, valid_values, :all_or_none => true
end
end
it "should not import invalid data" do
assert_no_difference "Topic.count" do
result = Topic.import columns, invalid_values, :all_or_none => true
end
end
it "should not import valid data when mixed with invalid data" do
assert_no_difference "Topic.count" do
result = Topic.import columns, mixed_values, :all_or_none => true
end
end
it "should report the failed instances" do
results = Topic.import columns, mixed_values, :all_or_none => true
assert_equal invalid_values.size, results.failed_instances.size
results.failed_instances.each { |e| assert_kind_of Topic, e }
end
it "should report the zero inserts" do
results = Topic.import columns, mixed_values, :all_or_none => true
assert_equal 0, results.num_inserts
end
end
end
context "with :synchronize option" do
context "synchronizing on new records" do
let(:new_topics) { Build(3, :topics) }
it "doesn't reload any data (doesn't work)" do
Topic.import new_topics, :synchronize => new_topics
assert new_topics.all?(&:new_record?), "No record should have been reloaded"
end
end
context "synchronizing on new records with explicit conditions" do
let(:new_topics) { Build(3, :topics) }
it "reloads data for existing in-memory instances" do
Topic.import(new_topics, :synchronize => new_topics, :synchronize_keys => [:title] )
assert new_topics.all?(&:persisted?), "Records should have been reloaded"
end
end
context "synchronizing on destroyed records with explicit conditions" do
let(:new_topics) { Generate(3, :topics) }
it "reloads data for existing in-memory instances" do
new_topics.each &:destroy
Topic.import(new_topics, :synchronize => new_topics, :synchronize_keys => [:title] )
assert new_topics.all?(&:persisted?), "Records should have been reloaded"
end
end
end
context "with an array of unsaved model instances" do
let(:topic) { Build(:topic, :title => "The RSpec Book", :author_name => "David Chelimsky")}
let(:topics) { Build(9, :topics) }
let(:invalid_topics){ Build(7, :invalid_topics)}
it "should import records based on those model's attributes" do
assert_difference "Topic.count", +9 do
result = Topic.import topics
end
Topic.import [topic]
assert Topic.find_by_title_and_author_name("The RSpec Book", "David Chelimsky")
end
it "should not overwrite existing records" do
topic = Generate(:topic, :title => "foobar")
assert_no_difference "Topic.count" do
begin
Topic.transaction do
topic.title = "baz"
Topic.import [topic]
end
rescue Exception
# PostgreSQL raises PgError due to key constraints
# I don't know why ActiveRecord doesn't catch these. *sigh*
end
end
assert_equal "foobar", topic.reload.title
end
context "with validation checks turned on" do
it "should import valid models" do
assert_difference "Topic.count", +9 do
result = Topic.import topics, :validate => true
end
end
it "should not import invalid models" do
assert_no_difference "Topic.count" do
result = Topic.import invalid_topics, :validate => true
end
end
end
context "with validation checks turned off" do
it "should import invalid models" do
assert_difference "Topic.count", +7 do
result = Topic.import invalid_topics, :validate => false
end
end
end
end
context "with an array of columns and an array of unsaved model instances" do
let(:topics) { Build(2, :topics) }
it "should import records populating the supplied columns with the corresponding model instance attributes" do
assert_difference "Topic.count", +2 do
result = Topic.import [:author_name, :title], topics
end
# imported topics should be findable by their imported attributes
assert Topic.find_by_author_name(topics.first.author_name)
assert Topic.find_by_author_name(topics.last.author_name)
end
it "should not populate fields for columns not imported" do
topics.first.author_email_address = "zach.dennis@gmail.com"
assert_difference "Topic.count", +2 do
result = Topic.import [:author_name, :title], topics
end
assert !Topic.find_by_author_email_address("zach.dennis@gmail.com")
end
end
context "with an array of columns and an array of values" do
it "should import ids when specified" do
Topic.import [:id, :author_name, :title], [[99, "Bob Jones", "Topic 99"]]
assert_equal 99, Topic.last.id
end
end
context "ActiveRecord timestamps" do
context "when the timestamps columns are present" do
setup do
Delorean.time_travel_to("5 minutes ago") do
assert_difference "Book.count", +1 do
result = Book.import [:title, :author_name, :publisher], [["LDAP", "Big Bird", "Del Rey"]]
end
end
@book = Book.last
end
it "should set the created_at column for new records" do
assert_equal 5.minutes.ago.strftime("%H:%M"), @book.created_at.strftime("%H:%M")
end
it "should set the created_on column for new records" do
assert_equal 5.minutes.ago.strftime("%H:%M"), @book.created_on.strftime("%H:%M")
end
it "should set the updated_at column for new records" do
assert_equal 5.minutes.ago.strftime("%H:%M"), @book.updated_at.strftime("%H:%M")
end
it "should set the updated_on column for new records" do
assert_equal 5.minutes.ago.strftime("%H:%M"), @book.updated_on.strftime("%H:%M")
end
end
context "when a custom time zone is set" do
setup do
original_timezone = ActiveRecord::Base.default_timezone
ActiveRecord::Base.default_timezone = :utc
Delorean.time_travel_to("5 minutes ago") do
assert_difference "Book.count", +1 do
result = Book.import [:title, :author_name, :publisher], [["LDAP", "Big Bird", "Del Rey"]]
end
end
ActiveRecord::Base.default_timezone = original_timezone
@book = Book.last
end
it "should set the created_at and created_on timestamps for new records" do
assert_equal 5.minutes.ago.utc.strftime("%H:%M"), @book.created_at.strftime("%H:%M")
assert_equal 5.minutes.ago.utc.strftime("%H:%M"), @book.created_on.strftime("%H:%M")
end
it "should set the updated_at and updated_on timestamps for new records" do
assert_equal 5.minutes.ago.utc.strftime("%H:%M"), @book.updated_at.strftime("%H:%M")
assert_equal 5.minutes.ago.utc.strftime("%H:%M"), @book.updated_on.strftime("%H:%M")
end
end
end
context "importing with database reserved words" do
let(:group) { Build(:group, :order => "superx") }
it "should import just fine" do
assert_difference "Group.count", +1 do
result = Group.import [group]
end
assert_equal "superx", Group.first.order
end
end
context "importing a datetime field" do
it "should import a date with YYYY/MM/DD format just fine" do
Topic.import [:author_name, :title, :last_read], [["Bob Jones", "Topic 2", "2010/05/14"]]
assert_equal "2010/05/14".to_date, Topic.last.last_read.to_date
end
end
context "importing through an association scope" do
[ true, false ].each do |b|
context "when validation is " + (b ? "enabled" : "disabled") do
it "should automatically set the foreign key column" do
books = [[ "David Chelimsky", "The RSpec Book" ], [ "Chad Fowler", "Rails Recipes" ]]
topic = Factory.create :topic
topic.books.import [ :author_name, :title ], books, :validate => b
assert_equal 2, topic.books.count
assert topic.books.all? { |b| b.topic_id == topic.id }
end
end
end
end
describe "importing when model has default_scope" do
it "doesn't import the default scope values" do
assert_difference "Widget.unscoped.count", +2 do
Widget.import [:w_id], [[1], [2]]
end
default_scope_value = Widget.scope_attributes[:active]
assert_not_equal default_scope_value, Widget.unscoped.find_by_w_id(1)
assert_not_equal default_scope_value, Widget.unscoped.find_by_w_id(2)
end
it "imports columns that are a part of the default scope using the value specified" do
assert_difference "Widget.unscoped.count", +2 do
Widget.import [:w_id, :active], [[1, true], [2, false]]
end
assert_not_equal true, Widget.unscoped.find_by_w_id(1)
assert_not_equal false, Widget.unscoped.find_by_w_id(2)
end
end
describe "importing serialized fields" do
it "imports values for serialized fields" do
assert_difference "Widget.unscoped.count", +1 do
Widget.import [:w_id, :data], [[1, {:a => :b}]]
end
assert_equal({:a => :b}, Widget.find_by_w_id(1).data)
end
end
end