offroad 0.0.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.
Files changed (62) hide show
  1. data/LICENSE +674 -0
  2. data/README.rdoc +29 -0
  3. data/Rakefile +75 -0
  4. data/TODO +42 -0
  5. data/lib/app/models/offroad/group_state.rb +85 -0
  6. data/lib/app/models/offroad/mirror_info.rb +53 -0
  7. data/lib/app/models/offroad/model_state.rb +36 -0
  8. data/lib/app/models/offroad/received_record_state.rb +109 -0
  9. data/lib/app/models/offroad/sendable_record_state.rb +91 -0
  10. data/lib/app/models/offroad/system_state.rb +33 -0
  11. data/lib/cargo_streamer.rb +222 -0
  12. data/lib/controller_extensions.rb +74 -0
  13. data/lib/exceptions.rb +16 -0
  14. data/lib/migrate/20100512164608_create_offroad_tables.rb +72 -0
  15. data/lib/mirror_data.rb +354 -0
  16. data/lib/model_extensions.rb +377 -0
  17. data/lib/module_funcs.rb +94 -0
  18. data/lib/offroad.rb +30 -0
  19. data/lib/version.rb +3 -0
  20. data/lib/view_helper.rb +7 -0
  21. data/templates/offline.rb +36 -0
  22. data/templates/offline_database.yml +7 -0
  23. data/templates/offroad.yml +6 -0
  24. data/test/app_root/app/controllers/application_controller.rb +2 -0
  25. data/test/app_root/app/controllers/group_controller.rb +28 -0
  26. data/test/app_root/app/models/global_record.rb +10 -0
  27. data/test/app_root/app/models/group.rb +12 -0
  28. data/test/app_root/app/models/group_owned_record.rb +68 -0
  29. data/test/app_root/app/models/guest.rb +7 -0
  30. data/test/app_root/app/models/subrecord.rb +12 -0
  31. data/test/app_root/app/models/unmirrored_record.rb +4 -0
  32. data/test/app_root/app/views/group/download_down_mirror.html.erb +4 -0
  33. data/test/app_root/app/views/group/download_initial_down_mirror.html.erb +4 -0
  34. data/test/app_root/app/views/group/download_up_mirror.html.erb +6 -0
  35. data/test/app_root/app/views/group/upload_down_mirror.html.erb +1 -0
  36. data/test/app_root/app/views/group/upload_up_mirror.html.erb +1 -0
  37. data/test/app_root/app/views/layouts/mirror.html.erb +9 -0
  38. data/test/app_root/config/boot.rb +115 -0
  39. data/test/app_root/config/database.yml +6 -0
  40. data/test/app_root/config/environment.rb +15 -0
  41. data/test/app_root/config/environments/test.rb +17 -0
  42. data/test/app_root/config/offroad.yml +6 -0
  43. data/test/app_root/config/routes.rb +4 -0
  44. data/test/app_root/db/migrate/20100529235049_create_tables.rb +64 -0
  45. data/test/app_root/lib/common_hobo.rb +15 -0
  46. data/test/app_root/vendor/plugins/offroad/init.rb +2 -0
  47. data/test/functional/mirror_operations_test.rb +148 -0
  48. data/test/test_helper.rb +405 -0
  49. data/test/unit/app_state_tracking_test.rb +275 -0
  50. data/test/unit/cargo_streamer_test.rb +332 -0
  51. data/test/unit/global_data_test.rb +102 -0
  52. data/test/unit/group_controller_test.rb +152 -0
  53. data/test/unit/group_data_test.rb +435 -0
  54. data/test/unit/group_single_test.rb +136 -0
  55. data/test/unit/hobo_permissions_test.rb +57 -0
  56. data/test/unit/mirror_data_test.rb +1271 -0
  57. data/test/unit/mirror_info_test.rb +31 -0
  58. data/test/unit/module_funcs_test.rb +37 -0
  59. data/test/unit/pathological_model_test.rb +62 -0
  60. data/test/unit/test_framework_test.rb +86 -0
  61. data/test/unit/unmirrored_data_test.rb +14 -0
  62. metadata +140 -0
@@ -0,0 +1,1271 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
2
+
3
+ class MirrorDataTest < Test::Unit::TestCase
4
+ online_test "cannot create MirrorData instance for online group" do
5
+ assert_raise Offroad::DataError do
6
+ Offroad::MirrorData.new(@online_group)
7
+ end
8
+ end
9
+
10
+ def all_records_from_section_named(cs, name)
11
+ recs = []
12
+ cs.each_cargo_section(name) do |batch|
13
+ recs += batch
14
+ end
15
+ return recs
16
+ end
17
+
18
+ def assert_single_cargo_section_named(cs, name)
19
+ count = 0
20
+ cs.each_cargo_section(name) do |data|
21
+ count += 1
22
+ end
23
+ assert_equal 1, count
24
+ end
25
+
26
+ def assert_no_cargo_sections_named(cs, name)
27
+ assert_nothing_raised do
28
+ cs.each_cargo_section(name) do |data|
29
+ raise "There shouldn't be any cargo sections with that name"
30
+ end
31
+ end
32
+ end
33
+
34
+ def assert_common_mirror_elements_appear_valid(cs, mode)
35
+ assert_single_cargo_section_named cs, "mirror_info"
36
+
37
+ mirror_info = cs.first_cargo_element("mirror_info")
38
+ assert_instance_of Offroad::MirrorInfo, mirror_info
39
+ migration_query = "SELECT version FROM schema_migrations ORDER BY version"
40
+ migrations = Group.connection.select_all(migration_query).map{ |r| r["version"] }
41
+ assert_equal migrations, mirror_info.schema_migrations.split(",").sort
42
+ assert_equal mirror_info.app, Offroad::app_name
43
+ assert Time.now - mirror_info.created_at < 30
44
+ assert mirror_info.app_mode.downcase.include?(mode.downcase)
45
+
46
+ assert_single_cargo_section_named cs, "group_state"
47
+ group_state = cs.first_cargo_element("group_state")
48
+ assert_instance_of Offroad::GroupState, group_state
49
+ assert_equal @offline_group.id, group_state.app_group_id
50
+ end
51
+
52
+ def assert_single_model_cargo_entry_matches(cs, record)
53
+ record.reload
54
+ data_name = Offroad::MirrorData.send(:data_cargo_name_for_model, record.class)
55
+ assert_single_cargo_section_named cs, data_name
56
+ assert_equal record.attributes, cs.first_cargo_element(data_name).attributes
57
+ end
58
+
59
+ def assert_record_not_present(cs, record)
60
+ record.reload
61
+ data_name = Offroad::MirrorData.send(:data_cargo_name_for_model, record.class)
62
+ assert_nothing_raised do
63
+ cs.each_cargo_section(data_name) do |batch|
64
+ batch.each do |cargo_record|
65
+ raise "Undesired record found" if record.attributes == cargo_record.attributes
66
+ end
67
+ end
68
+ end
69
+ end
70
+
71
+ cross_test "can pass data to MirrorData read methods as CargoStreamer, String, or IO" do
72
+ mirror_data = nil
73
+ in_online_app do
74
+ GlobalRecord.create(:title => "Foo Bar")
75
+ mirror_data = Offroad::MirrorData.new(@offline_group).write_downwards_data
76
+ end
77
+
78
+ sources = [
79
+ Offroad::CargoStreamer.new(mirror_data, "r"),
80
+ StringIO.new(mirror_data, "r"),
81
+ mirror_data
82
+ ]
83
+ sources.each do |source|
84
+ in_offline_app(true) do
85
+ assert_equal 0, GlobalRecord.count
86
+ Offroad::MirrorData.new(@offline_group).load_downwards_data(source)
87
+ assert GlobalRecord.find_by_title("Foo Bar")
88
+ end
89
+ end
90
+ end
91
+
92
+ offline_test "can have MirrorData write methods send to CargoStreamer, IO, or return value" do
93
+ cargo_streamer_sio = StringIO.new
94
+ cargo_streamer = Offroad::CargoStreamer.new(cargo_streamer_sio, "w")
95
+ direct_sio = StringIO.new
96
+
97
+ writer = Offroad::MirrorData.new(@offline_group)
98
+ writer.write_upwards_data(cargo_streamer)
99
+ writer.write_upwards_data(direct_sio)
100
+ str = writer.write_upwards_data
101
+
102
+ cargo_streamer_sio.rewind
103
+ direct_sio.rewind
104
+ result_a = Offroad::CargoStreamer.new(cargo_streamer_sio, "r").cargo_section_names
105
+ result_b = Offroad::CargoStreamer.new(direct_sio, "r").cargo_section_names
106
+ result_c = Offroad::CargoStreamer.new(StringIO.new(str), "r").cargo_section_names
107
+
108
+ assert result_a.size > 0
109
+ assert result_a == result_b
110
+ assert result_b == result_c
111
+ end
112
+
113
+ online_test "can generate a valid initial down mirror file for the offline group" do
114
+ global_record = GlobalRecord.create(:title => "Foo Bar")
115
+ global_record.reload # To clear the high time precision that is lost in the database
116
+
117
+ str = Offroad::MirrorData.new(@offline_group, :initial_mode => true).write_downwards_data
118
+ cs = Offroad::CargoStreamer.new(str, "r")
119
+ assert_common_mirror_elements_appear_valid cs, "online"
120
+ assert_single_model_cargo_entry_matches cs, global_record
121
+ assert_single_model_cargo_entry_matches cs, @offline_group
122
+ assert_single_model_cargo_entry_matches cs, @offline_group_data
123
+ end
124
+
125
+ online_test "can convert online group to an offline group and generate valid initial down mirror file" do
126
+ global_record = GlobalRecord.create(:title => "Foo Bar")
127
+ global_record.reload # To clear the high time precision that is lost in the database
128
+
129
+ @online_group.group_offline = true
130
+ str = Offroad::MirrorData.new(@online_group, :initial_mode => true).write_downwards_data
131
+ cs = Offroad::CargoStreamer.new(str, "r")
132
+ assert_single_model_cargo_entry_matches cs, global_record
133
+ assert_single_model_cargo_entry_matches cs, @online_group
134
+ assert_single_model_cargo_entry_matches cs, @online_group_data
135
+ end
136
+
137
+ online_test "can generate a valid down mirror file for the offline group" do
138
+ global_record = GlobalRecord.create(:title => "Foo Bar")
139
+ global_record.reload # To clear the high time precision that is lost in the database
140
+
141
+ str = Offroad::MirrorData.new(@offline_group).write_downwards_data
142
+ cs = Offroad::CargoStreamer.new(str, "r")
143
+ assert_common_mirror_elements_appear_valid cs, "online"
144
+ assert_single_model_cargo_entry_matches cs, global_record
145
+ assert_record_not_present cs, @offline_group
146
+ assert_record_not_present cs, @offline_group_data
147
+ end
148
+
149
+ online_test "initial down mirror files do not include irrelevant records" do
150
+ another_offline_group = Group.create!(:name => "Another Group")
151
+ another_group_data = GroupOwnedRecord.create!(:description => "Another Data", :group => another_offline_group)
152
+ another_offline_group.group_offline = true
153
+ [another_offline_group, another_group_data].each { |r| r.reload }
154
+
155
+ str = Offroad::MirrorData.new(@offline_group, :initial_mode => true).write_downwards_data
156
+ cs = Offroad::CargoStreamer.new(str, "r")
157
+ assert_record_not_present cs, another_offline_group
158
+ assert_record_not_present cs, another_group_data
159
+ assert_single_model_cargo_entry_matches cs, @offline_group
160
+ assert_single_model_cargo_entry_matches cs, @offline_group_data
161
+ end
162
+
163
+ online_test "down mirror files do not include irrelevant records" do
164
+ global_record = GlobalRecord.create(:title => "Foo Bar")
165
+ global_record.reload # To clear the high time precision that is lost in the database
166
+ another_offline_group = Group.create!(:name => "Another Group")
167
+ another_group_data = GroupOwnedRecord.create!(:description => "Another Data", :group => another_offline_group)
168
+ another_offline_group.group_offline = true
169
+ [another_offline_group, another_group_data].each { |r| r.reload }
170
+
171
+ str = Offroad::MirrorData.new(@offline_group).write_downwards_data
172
+ cs = Offroad::CargoStreamer.new(str, "r")
173
+ assert_record_not_present cs, another_offline_group
174
+ assert_record_not_present cs, another_group_data
175
+ assert_record_not_present cs, @offline_group
176
+ assert_record_not_present cs, @offline_group_data
177
+ assert_single_model_cargo_entry_matches cs, global_record
178
+ end
179
+
180
+ offline_test "can generate a valid up mirror file for the offline group" do
181
+ @offline_group.name = "Changed"
182
+ @offline_group.save!
183
+ @offline_group.reload
184
+ @offline_group_data.some_integer = 5551212
185
+ @offline_group_data.save!
186
+ @offline_group_data.reload
187
+ str = Offroad::MirrorData.new(@offline_group).write_upwards_data
188
+ cs = Offroad::CargoStreamer.new(str, "r")
189
+ assert_common_mirror_elements_appear_valid cs, "offline"
190
+ assert_single_model_cargo_entry_matches cs, @offline_group
191
+ assert_single_model_cargo_entry_matches cs, @offline_group_data
192
+ end
193
+
194
+ offline_test "up mirror files do not include irrelevant records" do
195
+ fake_global_data = GlobalRecord.new(:title => "Fake Stuff")
196
+ force_save_and_reload(fake_global_data)
197
+
198
+ str = Offroad::MirrorData.new(@offline_group).write_upwards_data
199
+ cs = Offroad::CargoStreamer.new(str, "r")
200
+ assert_record_not_present cs, fake_global_data
201
+ end
202
+
203
+ offline_test "cannot upload an invalid down mirror file" do
204
+ assert_raise Offroad::DataError do
205
+ Offroad::MirrorData.new(@offline_group).load_downwards_data("FOO BAR BLAH")
206
+ end
207
+ end
208
+
209
+ online_test "cannot upload an invalid up mirror file" do
210
+ assert_raise Offroad::DataError do
211
+ Offroad::MirrorData.new(@offline_group).load_upwards_data("FOO BAR BLAH")
212
+ end
213
+ end
214
+
215
+ offline_test "cannot use load_upwards_data in offline mode" do
216
+ assert_raise Offroad::PluginError do
217
+ Offroad::MirrorData.new(@offline_group).load_upwards_data("FOO BAR BLAH")
218
+ end
219
+ end
220
+
221
+ online_test "cannot use load_downwards_data in online mode" do
222
+ assert_raise Offroad::PluginError do
223
+ Offroad::MirrorData.new(@offline_group).load_downwards_data("FOO BAR BLAH")
224
+ end
225
+ end
226
+
227
+ def assert_record_states_all_valid
228
+ Offroad::ReceivedRecordState.all.each do |rrs|
229
+ if not rrs.valid?
230
+ flunk "Invalid RRS (#{rrs.attributes.map{|k,v| "#{k}:#{v}"}.join(",")}) : #{rrs.errors.full_messages.join(",")}"
231
+ end
232
+ end
233
+ Offroad::SendableRecordState.all.each do |srs|
234
+ if not srs.valid?
235
+ flunk "Invalid SRS (#{srs.attributes.map{|k,v| "#{k}:#{v}"}.join(",")}) : #{srs.errors.full_messages.join(",")}"
236
+ end
237
+ end
238
+ end
239
+
240
+ online_test "initial online record states all valid" do
241
+ assert_record_states_all_valid
242
+ end
243
+
244
+ offline_test "initial offline record states all valid" do
245
+ assert_record_states_all_valid
246
+ end
247
+
248
+ cross_test "can insert and update group data using an up mirror file" do
249
+ mirror_data = nil
250
+
251
+ in_offline_app do
252
+ @offline_group.name = "TEST 123"
253
+ @offline_group.save!
254
+ @offline_group_data.description = "TEST XYZ"
255
+ @offline_group_data.save!
256
+ GroupOwnedRecord.create!(:description => "TEST ABC", :group => @offline_group)
257
+ mirror_data = Offroad::MirrorData.new(@offline_group).write_upwards_data
258
+ end
259
+
260
+ in_online_app do
261
+ prior_rrs_count = Offroad::ReceivedRecordState.count
262
+ Offroad::MirrorData.new(@offline_group).load_upwards_data(mirror_data)
263
+ assert_record_states_all_valid
264
+ assert_equal prior_rrs_count+1, Offroad::ReceivedRecordState.count
265
+ assert_equal @offline_group.id, Group.find_by_name("TEST 123").id
266
+ assert GroupOwnedRecord.find_by_description("TEST ABC")
267
+ assert_equal @offline_group_data.id, GroupOwnedRecord.find_by_description("TEST XYZ").id
268
+ end
269
+ end
270
+
271
+ cross_test "records created by up mirror file did not have their callbacks called" do
272
+ mirror_data = nil
273
+ in_offline_app do
274
+ GroupOwnedRecord.create(:description => "TEST ABC", :group => @offline_group)
275
+ mirror_data = Offroad::MirrorData.new(@offline_group).write_upwards_data
276
+ end
277
+
278
+ in_online_app do
279
+ GroupOwnedRecord.reset_callback_called
280
+ Offroad::MirrorData.new(@offline_group).load_upwards_data(mirror_data)
281
+ assert !GroupOwnedRecord.callback_called
282
+ end
283
+ end
284
+
285
+ cross_test "records updated by up mirror file did not have their callbacks called" do
286
+ mirror_data = nil
287
+ in_offline_app do
288
+ @offline_group_data.description = "TEST XYZ"
289
+ @offline_group_data.save!
290
+ mirror_data = Offroad::MirrorData.new(@offline_group).write_upwards_data
291
+ end
292
+
293
+ in_online_app do
294
+ GroupOwnedRecord.reset_callback_called
295
+ Offroad::MirrorData.new(@offline_group).load_upwards_data(mirror_data)
296
+ assert !GroupOwnedRecord.callback_called
297
+ end
298
+ end
299
+
300
+ cross_test "records destroyed by up mirror file did not have their callbacks called" do
301
+ mirror_data = nil
302
+ in_offline_app do
303
+ @offline_group_data.destroy
304
+ mirror_data = Offroad::MirrorData.new(@offline_group).write_upwards_data
305
+ end
306
+
307
+ in_online_app do
308
+ GroupOwnedRecord.reset_callback_called
309
+ Offroad::MirrorData.new(@offline_group).load_upwards_data(mirror_data)
310
+ assert !GroupOwnedRecord.callback_called
311
+ end
312
+ end
313
+
314
+ cross_test "can delete group data using an up mirror file" do
315
+ mirror_data = nil
316
+
317
+ in_offline_app do
318
+ @offline_group_data.destroy
319
+ mirror_data = Offroad::MirrorData.new(@offline_group).write_upwards_data
320
+ end
321
+
322
+ in_online_app do
323
+ prior_rrs_count = Offroad::ReceivedRecordState.count
324
+ assert_equal 1, GroupOwnedRecord.count(:conditions => { :group_id => @offline_group.id })
325
+ Offroad::MirrorData.new(@offline_group).load_upwards_data(mirror_data)
326
+ assert_record_states_all_valid
327
+ assert_equal prior_rrs_count-1, Offroad::ReceivedRecordState.count
328
+ assert_equal 0, GroupOwnedRecord.count(:conditions => { :group_id => @offline_group.id })
329
+ end
330
+ end
331
+
332
+ cross_test "can insert and update and delete global records using a down mirror file" do
333
+ mirror_data = nil
334
+
335
+ in_online_app do
336
+ GlobalRecord.create(:title => "ABC")
337
+ GlobalRecord.create(:title => "123")
338
+ mirror_data = Offroad::MirrorData.new(@offline_group).write_downwards_data
339
+ end
340
+
341
+ offline_number_rec_id = nil
342
+ in_offline_app do
343
+ rrs_scope = Offroad::ReceivedRecordState.for_model(GlobalRecord)
344
+ assert_equal 0, rrs_scope.count
345
+ assert_equal 0, GlobalRecord.count
346
+ Offroad::MirrorData.new(@offline_group).load_downwards_data(mirror_data)
347
+ assert_record_states_all_valid
348
+ assert_equal 2, rrs_scope.count
349
+ assert_equal 2, GlobalRecord.count
350
+ assert_not_nil GlobalRecord.find_by_title("ABC")
351
+ assert_not_nil GlobalRecord.find_by_title("123")
352
+ offline_number_rec_id = GlobalRecord.find_by_title("123")
353
+ end
354
+
355
+ in_online_app do
356
+ number_rec = GlobalRecord.find_by_title("123")
357
+ number_rec.title = "789"
358
+ number_rec.save!
359
+
360
+ letter_rec = GlobalRecord.find_by_title("ABC")
361
+ letter_rec.destroy
362
+
363
+ mirror_data = Offroad::MirrorData.new(@offline_group).write_downwards_data
364
+ end
365
+
366
+ in_offline_app do
367
+ rrs_scope = Offroad::ReceivedRecordState.for_model(GlobalRecord)
368
+ Offroad::MirrorData.new(@offline_group).load_downwards_data(mirror_data)
369
+ assert_record_states_all_valid
370
+ assert_equal 1, rrs_scope.count
371
+ assert_equal 1, GlobalRecord.count
372
+ assert_nil GlobalRecord.find_by_title("ABC")
373
+ assert_nil GlobalRecord.find_by_title("123")
374
+ assert_not_nil GlobalRecord.find_by_title("789")
375
+ assert_equal offline_number_rec_id, GlobalRecord.find_by_title("789")
376
+ end
377
+ end
378
+
379
+ cross_test "can insert group records using an initial down mirror file" do
380
+ mirror_data = nil
381
+ in_online_app do
382
+ mirror_data = Offroad::MirrorData.new(@offline_group, :initial_mode => true).write_downwards_data
383
+ end
384
+
385
+ in_offline_app(false, true) do
386
+ assert_equal 0, Group.count
387
+ assert_equal 0, GroupOwnedRecord.count
388
+ assert_equal 0, Offroad::SendableRecordState.for_model(Group).count
389
+ assert_equal 0, Offroad::SendableRecordState.for_model(GroupOwnedRecord).count
390
+ Offroad::MirrorData.new(nil, :initial_mode => true).load_downwards_data(mirror_data)
391
+ assert_record_states_all_valid
392
+ assert_equal 1, Group.count
393
+ assert_equal 1, GroupOwnedRecord.count
394
+ assert_equal 1, Offroad::SendableRecordState.for_model(Group).count
395
+ assert_equal 1, Offroad::SendableRecordState.for_model(GroupOwnedRecord).count
396
+ end
397
+ end
398
+
399
+ cross_test "records created by initial down mirror file did not have their callbacks called" do
400
+ mirror_data = nil
401
+ in_online_app do
402
+ mirror_data = Offroad::MirrorData.new(@offline_group, :initial_mode => true).write_downwards_data
403
+ end
404
+
405
+ in_offline_app(false, true) do
406
+ GroupOwnedRecord.reset_callback_called
407
+ Offroad::MirrorData.new(nil, :initial_mode => true).load_downwards_data(mirror_data)
408
+ assert !GroupOwnedRecord.callback_called
409
+ end
410
+ end
411
+
412
+ cross_test "can insert global records using an initial down mirror file" do
413
+ mirror_data = nil
414
+ in_online_app do
415
+ GlobalRecord.create(:title => "Something")
416
+ mirror_data = Offroad::MirrorData.new(@offline_group, :initial_mode => true).write_downwards_data
417
+ end
418
+
419
+ in_offline_app(false, true) do
420
+ assert_equal 0, GlobalRecord.count
421
+ assert_equal 0, Offroad::SendableRecordState.for_model(GlobalRecord).count
422
+ assert_equal 0, Offroad::ReceivedRecordState.for_model(GlobalRecord).count
423
+ Offroad::MirrorData.new(nil, :initial_mode => true).load_downwards_data(mirror_data)
424
+ assert_record_states_all_valid
425
+ assert_equal 1, GlobalRecord.count
426
+ assert_not_nil GlobalRecord.find_by_title("Something")
427
+ assert_equal 0, Offroad::SendableRecordState.for_model(GlobalRecord).count
428
+ assert_equal 1, Offroad::ReceivedRecordState.for_model(GlobalRecord).count
429
+ end
430
+ end
431
+
432
+ cross_test "cannot load regular down mirror file in empty offline app" do
433
+ mirror_data = nil
434
+ in_online_app { mirror_data = Offroad::MirrorData.new(@offline_group).write_downwards_data }
435
+
436
+ in_offline_app(false, true) do
437
+ assert_raise Offroad::PluginError do
438
+ Offroad::MirrorData.new(nil).load_downwards_data(mirror_data)
439
+ end
440
+ end
441
+ end
442
+
443
+ cross_test "importing an initial down mirror file deletes all currently existing records" do
444
+ mirror_data = nil
445
+ in_online_app do
446
+ mirror_data = Offroad::MirrorData.new(@offline_group, :initial_mode => true).write_downwards_data
447
+ end
448
+
449
+ in_offline_app do
450
+ @offline_group.update_attribute(:name, "Old")
451
+ GroupOwnedRecord.create!(:description => "Old", :group => @offline_group)
452
+ UnmirroredRecord.create!(:content => "Old Old Old")
453
+ global_data = GlobalRecord.new(:title => "Old")
454
+ force_save_and_reload(global_data)
455
+
456
+ Offroad::MirrorData.new(nil, :initial_mode => true).load_downwards_data(mirror_data)
457
+
458
+ assert_equal nil, Group.find_by_name("Old")
459
+ assert_equal nil, GroupOwnedRecord.find_by_description("Old")
460
+ assert_equal 1, Group.count
461
+ assert_equal 1, GroupOwnedRecord.count
462
+ assert_equal 0, GlobalRecord.count
463
+ assert_equal 0, UnmirroredRecord.count
464
+ end
465
+ end
466
+
467
+ cross_test "importing an initial down mirror file resets autoincrement counters" do
468
+ mirror_data = nil
469
+ in_online_app do
470
+ mirror_data = Offroad::MirrorData.new(@offline_group, :initial_mode => true).write_downwards_data
471
+ end
472
+
473
+ in_offline_app do
474
+ global_rec_a = GlobalRecord.new(:title => "A")
475
+ global_rec_b = GlobalRecord.new(:title => "B")
476
+ global_rec_c = GlobalRecord.new(:title => "C")
477
+ force_save_and_reload(global_rec_a, global_rec_b, global_rec_c)
478
+
479
+ Offroad::MirrorData.new(nil, :initial_mode => true).load_downwards_data(mirror_data)
480
+
481
+ global_rec = GlobalRecord.new(:title => "Test")
482
+ force_save_and_reload(global_rec)
483
+ assert_equal 1, global_rec.id
484
+ end
485
+ end
486
+
487
+ cross_test "cannot upload an initial down mirror file unless passed :initial_mode => true to MirrorData.new" do
488
+ mirror_data = nil
489
+ in_online_app do
490
+ mirror_data = Offroad::MirrorData.new(@offline_group, :initial_mode => true).write_downwards_data
491
+ end
492
+
493
+ in_offline_app do
494
+ assert_raise Offroad::DataError do
495
+ Offroad::MirrorData.new(@offline_group).load_downwards_data(mirror_data)
496
+ end
497
+ end
498
+ end
499
+
500
+ cross_test "cannot upload a non-initial down mirror file after passing :initial_mode => true to MirrorData.new" do
501
+ mirror_data = nil
502
+ in_online_app { mirror_data = Offroad::MirrorData.new(@offline_group).write_downwards_data }
503
+
504
+ in_offline_app do
505
+ assert_raise Offroad::DataError do
506
+ Offroad::MirrorData.new(@offline_group, :initial_mode => true).load_downwards_data(mirror_data)
507
+ end
508
+ end
509
+ end
510
+
511
+ cross_test "cannot upload a non-initial down mirror file to a blank offline instance" do
512
+ mirror_data = nil
513
+ in_online_app { mirror_data = Offroad::MirrorData.new(@offline_group).write_downwards_data }
514
+
515
+ in_offline_app(false, true) do
516
+ assert_raise Offroad::DataError do
517
+ Offroad::MirrorData.new(@offline_group).load_downwards_data(mirror_data)
518
+ end
519
+ end
520
+ end
521
+
522
+ cross_test "cannot pass a down mirror file to load_upwards_data" do
523
+ mirror_data = nil
524
+
525
+ in_online_app do
526
+ mirror_data = Offroad::MirrorData.new(@offline_group).write_downwards_data
527
+
528
+ assert_raise Offroad::DataError do
529
+ Offroad::MirrorData.new(@offline_group).load_upwards_data(mirror_data)
530
+ end
531
+ end
532
+
533
+ in_offline_app do
534
+ assert_nothing_raised do
535
+ Offroad::MirrorData.new(@offline_group).load_downwards_data(mirror_data)
536
+ end
537
+ end
538
+ end
539
+
540
+ cross_test "cannot pass an up mirror file to load_downwards_data" do
541
+ mirror_data = nil
542
+
543
+ in_offline_app do
544
+ mirror_data = Offroad::MirrorData.new(@offline_group).write_upwards_data
545
+
546
+ assert_raise Offroad::DataError do
547
+ Offroad::MirrorData.new(@offline_group).load_downwards_data(mirror_data)
548
+ end
549
+ end
550
+
551
+ in_online_app do
552
+ assert_nothing_raised do
553
+ Offroad::MirrorData.new(@offline_group).load_upwards_data(mirror_data)
554
+ end
555
+ end
556
+ end
557
+
558
+ cross_test "transformed ids are handled properly when loading an up mirror file" do
559
+ in_online_app do
560
+ another_online_record = GroupOwnedRecord.create(:description => "Yet Another", :group => @online_group)
561
+ end
562
+
563
+ mirror_data = nil
564
+ offline_id_of_new_rec = nil
565
+ in_offline_app do
566
+ another_offline_rec = GroupOwnedRecord.create(:description => "One More", :group => @offline_group)
567
+ offline_id_of_new_rec = another_offline_rec.id
568
+ mirror_data = Offroad::MirrorData.new(@offline_group).write_upwards_data
569
+ end
570
+
571
+ in_online_app do
572
+ Offroad::MirrorData.new(@offline_group).load_upwards_data(mirror_data)
573
+ assert_record_states_all_valid
574
+ rec = GroupOwnedRecord.find_by_description("One More")
575
+ assert rec
576
+ assert_equal offline_id_of_new_rec, Offroad::ReceivedRecordState.for_record(rec).first.remote_record_id
577
+ end
578
+ end
579
+
580
+ cross_test "transformed ids are handled properly when loading an initial down mirror file" do
581
+ mirror_data = nil
582
+ online_id_of_offline_rec = nil
583
+ in_online_app do
584
+ online_id_of_offline_rec = @offline_group_data.id
585
+ mirror_data = Offroad::MirrorData.new(@offline_group, :initial_mode => true).write_downwards_data
586
+ end
587
+
588
+ in_offline_app do
589
+ Offroad::MirrorData.new(nil, :initial_mode => true).load_downwards_data(mirror_data)
590
+ assert_record_states_all_valid
591
+ rec = GroupOwnedRecord.find_by_description("Sam")
592
+ assert_equal online_id_of_offline_rec, rec.id
593
+ rec.description = "Samuel Jackson"
594
+ rec.save!
595
+ mirror_data = Offroad::MirrorData.new(@offline_group).write_upwards_data
596
+ end
597
+
598
+ in_online_app do
599
+ Offroad::MirrorData.new(@offline_group).load_upwards_data(mirror_data)
600
+ assert_equal nil, GroupOwnedRecord.find_by_description("Sam")
601
+ assert_equal "Samuel Jackson", GroupOwnedRecord.find(online_id_of_offline_rec).description
602
+ end
603
+ end
604
+
605
+ online_test "initial down mirror files do not include deletion entries" do
606
+ global_record = GlobalRecord.create(:title => "Something")
607
+ global_record.destroy
608
+
609
+ str = Offroad::MirrorData.new(@offline_group, :initial_mode => true).write_downwards_data
610
+ cs = Offroad::CargoStreamer.new(str, "r")
611
+ deletion_cargo_name = Offroad::MirrorData.send(:deletion_cargo_name_for_model, GlobalRecord)
612
+ assert_equal false, cs.has_cargo_named?(deletion_cargo_name)
613
+ end
614
+
615
+ offline_test "can only create mirror files containing invalid records when skip_validation is true" do
616
+ group_rec = GroupOwnedRecord.new(:description => "Invalid record", :group => @offline_group, :should_be_even => 3)
617
+ group_rec.save_without_validation
618
+ assert_raise Offroad::DataError do
619
+ Offroad::MirrorData.new(@offline_group).write_upwards_data
620
+ end
621
+ assert_nothing_raised do
622
+ Offroad::MirrorData.new(@offline_group, :skip_validation => true).write_upwards_data
623
+ end
624
+ end
625
+
626
+ cross_test "cannot import up mirror files with invalid records unless skip_validation is enabled" do
627
+ mirror_data = nil
628
+ in_offline_app do
629
+ group_rec = GroupOwnedRecord.new(:description => "Invalid record", :group => @offline_group, :should_be_even => 3)
630
+ group_rec.save_without_validation
631
+ mirror_data = Offroad::MirrorData.new(@offline_group, :skip_validation => true).write_upwards_data
632
+ end
633
+
634
+ in_online_app do
635
+ assert_raise Offroad::DataError do
636
+ Offroad::MirrorData.new(@offline_group).load_upwards_data(mirror_data)
637
+ end
638
+ assert_nothing_raised do
639
+ Offroad::MirrorData.new(@offline_group, :skip_validation => true).load_upwards_data(mirror_data)
640
+ end
641
+ end
642
+ end
643
+
644
+ cross_test "cannot import down mirror files with invalid records unless skip_validation is enabled" do
645
+ mirror_data = nil
646
+ in_online_app do
647
+ global_rec = GlobalRecord.new(:title => "Invalid record", :should_be_odd => 2)
648
+ global_rec.save_without_validation
649
+ mirror_data = Offroad::MirrorData.new(@offline_group, :skip_validation => true).write_downwards_data
650
+ end
651
+
652
+ in_offline_app do
653
+ assert_raise Offroad::DataError do
654
+ Offroad::MirrorData.new(@offline_group).load_downwards_data(mirror_data)
655
+ end
656
+ assert_nothing_raised do
657
+ Offroad::MirrorData.new(@offline_group, :skip_validation => true).load_downwards_data(mirror_data)
658
+ end
659
+ end
660
+ end
661
+
662
+ cross_test "cannot import initial down mirror files with invalid records unless skip_validation is enabled" do
663
+ mirror_data = nil
664
+ in_online_app do
665
+ group_rec = GroupOwnedRecord.new(:description => "Invalid record", :group => @online_group, :should_be_even => 3)
666
+ group_rec.save_without_validation
667
+ @online_group.group_offline = true
668
+ writer = Offroad::MirrorData.new(@online_group, :skip_validation => true, :initial_mode => true)
669
+ mirror_data = writer.write_downwards_data
670
+ end
671
+
672
+ in_offline_app(false, true) do
673
+ assert_raise Offroad::DataError do
674
+ Offroad::MirrorData.new(nil, :initial_mode => true).load_downwards_data(mirror_data)
675
+ end
676
+ assert_nothing_raised do
677
+ Offroad::MirrorData.new(nil, :initial_mode => true, :skip_validation => true).load_downwards_data(mirror_data)
678
+ end
679
+ end
680
+ end
681
+
682
+ cross_test "foreign keys are transformed correctly on up mirror" do
683
+ in_online_app do
684
+ # Perturb the autoincrement a bit
685
+ GroupOwnedRecord.create(:description => "Alice", :group => @online_group)
686
+ GroupOwnedRecord.create(:description => "Bob", :group => @online_group)
687
+ end
688
+
689
+ mirror_data = nil
690
+ in_offline_app do
691
+ parent = GroupOwnedRecord.create(:description => "Celia", :group => @offline_group)
692
+ child_a = GroupOwnedRecord.create(:description => "Daniel", :parent => parent, :group => @offline_group)
693
+ child_b = GroupOwnedRecord.create(:description => "Eric", :parent => parent, :group => @offline_group)
694
+ grandchild = GroupOwnedRecord.create(:description => "Fran", :parent => child_b, :group => @offline_group)
695
+ time_traveler = GroupOwnedRecord.create(:description => "Philip J. Fry", :group => @offline_group)
696
+ time_traveler.parent = time_traveler
697
+ time_traveler.save!
698
+ @offline_group.favorite = grandchild
699
+ @offline_group.save!
700
+ @offline_group_data.parent = grandchild
701
+ @offline_group_data.save!
702
+ mirror_data = Offroad::MirrorData.new(@offline_group).write_upwards_data
703
+ end
704
+
705
+ in_online_app do
706
+ Offroad::MirrorData.new(@offline_group).load_upwards_data(mirror_data)
707
+ assert_record_states_all_valid
708
+
709
+ @offline_group.reload
710
+ @offline_group_data.reload
711
+ parent = GroupOwnedRecord.find_by_description("Celia")
712
+ child_a = GroupOwnedRecord.find_by_description("Daniel")
713
+ child_b = GroupOwnedRecord.find_by_description("Eric")
714
+ grandchild = GroupOwnedRecord.find_by_description("Fran")
715
+ time_traveler = GroupOwnedRecord.find_by_description("Philip J. Fry")
716
+
717
+ assert_equal parent, child_a.parent
718
+ assert_equal parent, child_b.parent
719
+ assert_equal child_b, grandchild.parent
720
+ assert_equal grandchild, child_b.children.first
721
+ assert_equal grandchild, @offline_group.favorite
722
+ assert_equal @offline_group_data, grandchild.children.first
723
+ assert_equal grandchild, @offline_group_data.parent
724
+ assert_equal time_traveler, time_traveler.parent
725
+ end
726
+ end
727
+
728
+ cross_test "foreign keys are transformed correctly on down mirror" do
729
+ mirror_data = nil
730
+ in_online_app do
731
+ alice = GlobalRecord.create(:title => "Alice")
732
+ alice.friend = alice
733
+ alice.save!
734
+ bob = GlobalRecord.create(:title => "Bob", :friend => alice)
735
+ claire = GlobalRecord.create(:title => "Claire", :friend => bob)
736
+ mirror_data = Offroad::MirrorData.new(@offline_group).write_downwards_data
737
+ end
738
+
739
+ in_offline_app do
740
+ Offroad::MirrorData.new(@offline_group).load_downwards_data(mirror_data)
741
+ assert_record_states_all_valid
742
+ alice = GlobalRecord.find_by_title("Alice")
743
+ bob = GlobalRecord.find_by_title("Bob")
744
+ claire = GlobalRecord.find_by_title("Claire")
745
+ assert_equal alice, alice.friend
746
+ assert_equal alice, bob.friend
747
+ assert_equal bob, claire.friend
748
+ end
749
+ end
750
+
751
+ cross_test "loading up mirror file loads group state information" do
752
+ in_online_app do
753
+ assert_equal "Unknown", @offline_group.group_state.operating_system
754
+ end
755
+
756
+ mirror_data = nil
757
+ offline_os = ""
758
+ in_offline_app do
759
+ mirror_data = Offroad::MirrorData.new(@offline_group).write_upwards_data
760
+ offline_os = @offline_group.group_state.operating_system
761
+ end
762
+
763
+ in_online_app do
764
+ Offroad::MirrorData.new(@offline_group).load_upwards_data(mirror_data)
765
+ assert_equal offline_os, @offline_group.group_state.operating_system
766
+ end
767
+ end
768
+
769
+ offline_test "creating up mirror file increments current_mirror_version" do
770
+ prior_version = Offroad::SystemState::current_mirror_version
771
+ Offroad::MirrorData.new(@offline_group).write_upwards_data
772
+ assert_equal prior_version+1, Offroad::SystemState::current_mirror_version
773
+ end
774
+
775
+ online_test "creating down mirror file increments current_mirror_version" do
776
+ prior_version = Offroad::SystemState::current_mirror_version
777
+ Offroad::MirrorData.new(@offline_group).write_downwards_data
778
+ assert_equal prior_version+1, Offroad::SystemState::current_mirror_version
779
+ end
780
+
781
+ online_test "creating initial down mirror file increments current_mirror_version" do
782
+ prior_version = Offroad::SystemState::current_mirror_version
783
+ Offroad::MirrorData.new(@offline_group, :initial_mode => true).write_downwards_data
784
+ assert_equal prior_version+1, Offroad::SystemState::current_mirror_version
785
+ end
786
+
787
+ cross_test "receiving an up mirror file increments confirmed_group_data_version to the indicated value if larger" do
788
+ mirror_data = nil
789
+ in_offline_app do
790
+ @offline_group_data.description = "New Name"
791
+ @offline_group_data.save!
792
+
793
+ Offroad::SystemState::instance_record.update_attribute(:current_mirror_version, 42)
794
+
795
+ mirror_data = Offroad::MirrorData.new(@offline_group).write_upwards_data
796
+ end
797
+
798
+ in_online_app do
799
+ assert_equal 1, @offline_group.group_state.confirmed_group_data_version
800
+ Offroad::MirrorData.new(@offline_group).load_upwards_data(mirror_data)
801
+ assert_equal 42, @offline_group.group_state.confirmed_group_data_version
802
+ end
803
+ end
804
+
805
+ cross_test "received up mirror files are rejected if their version is equal to or lower than current version" do
806
+ [42, 41].each do |sending_version|
807
+ mirror_data = nil
808
+ in_offline_app(true) do
809
+ @offline_group_data.description = "New Name"
810
+ @offline_group_data.save!
811
+
812
+ Offroad::SystemState::instance_record.update_attribute(:current_mirror_version, sending_version)
813
+
814
+ mirror_data = Offroad::MirrorData.new(@offline_group).write_upwards_data
815
+ end
816
+
817
+ in_online_app do
818
+ group_state = @offline_group.group_state
819
+ group_state.confirmed_group_data_version = 42
820
+ group_state.save!
821
+
822
+ assert_raise Offroad::OldDataError do
823
+ Offroad::MirrorData.new(@offline_group).load_upwards_data(mirror_data)
824
+ end
825
+ end
826
+ end
827
+ end
828
+
829
+ cross_test "receiving a down mirror file increments confirmed_global_data_version to the indicated value if larger" do
830
+ mirror_data = nil
831
+ in_online_app do
832
+ GlobalRecord.create(:title => "Testing")
833
+
834
+ Offroad::SystemState::instance_record.update_attribute(:current_mirror_version, 42)
835
+
836
+ mirror_data = Offroad::MirrorData.new(@offline_group).write_downwards_data
837
+ end
838
+
839
+ in_offline_app do
840
+ assert_equal 1, @offline_group.group_state.confirmed_global_data_version
841
+ Offroad::MirrorData.new(@offline_group).load_downwards_data(mirror_data)
842
+ assert_equal 42, @offline_group.group_state.confirmed_global_data_version
843
+ end
844
+ end
845
+
846
+ cross_test "received down mirror files are rejected if their version is equal to or lower than current version" do
847
+ [42, 41].each do |sending_version|
848
+ mirror_data = nil
849
+ in_online_app do
850
+ GlobalRecord.create(:title => "Testing")
851
+
852
+ Offroad::SystemState::instance_record.update_attribute(:current_mirror_version, sending_version)
853
+
854
+ mirror_data = Offroad::MirrorData.new(@offline_group).write_downwards_data
855
+ end
856
+
857
+ in_offline_app do
858
+ group_state = @offline_group.group_state
859
+ group_state.confirmed_global_data_version = 42
860
+ group_state.save!
861
+
862
+ assert_raise Offroad::OldDataError do
863
+ Offroad::MirrorData.new(@offline_group).load_downwards_data(mirror_data)
864
+ end
865
+ end
866
+ end
867
+ end
868
+
869
+ cross_test "after loading initial down mirror file global_data_version matches online prior current_mirror_version" do
870
+ mirror_data = nil
871
+ online_version = nil
872
+ in_online_app do
873
+ Offroad::SystemState::instance_record.update_attribute(:current_mirror_version, 3)
874
+ online_version = Offroad::SystemState::current_mirror_version
875
+ mirror_data = Offroad::MirrorData.new(@offline_group, :initial_mode => true).write_downwards_data
876
+ end
877
+
878
+ in_offline_app(false, true) do
879
+ Offroad::MirrorData.new(nil, :initial_mode => true).load_downwards_data(mirror_data)
880
+ assert_equal online_version, @offline_group.group_state.confirmed_global_data_version
881
+ end
882
+ end
883
+
884
+ cross_test "down mirror files do not include records which offline is known to already have the latest version of" do
885
+ mirror_data = nil
886
+ in_online_app do
887
+ GlobalRecord.create!(:title => "Record A", :some_boolean => false)
888
+ GlobalRecord.create!(:title => "Record B", :some_boolean => false)
889
+ mirror_data = Offroad::MirrorData.new(@offline_group).write_downwards_data
890
+ end
891
+
892
+ in_offline_app do
893
+ Offroad::MirrorData.new(@offline_group).load_downwards_data(mirror_data)
894
+ mirror_data = Offroad::MirrorData.new(@offline_group).write_upwards_data
895
+ end
896
+
897
+ in_online_app do
898
+ Offroad::MirrorData.new(@offline_group).load_upwards_data(mirror_data)
899
+ rec_a = GlobalRecord.find_by_title("Record A")
900
+ rec_a.some_boolean = true
901
+ rec_a.save!
902
+ mirror_data = Offroad::MirrorData.new(@offline_group).write_downwards_data
903
+ end
904
+
905
+ cs = Offroad::CargoStreamer.new(mirror_data, "r")
906
+ recs = []
907
+ cs.each_cargo_section(Offroad::MirrorData.send(:data_cargo_name_for_model, GlobalRecord)) do |batch|
908
+ recs += batch
909
+ end
910
+ assert_equal 1, recs.size
911
+ assert_equal "Record A", recs[0].title
912
+ assert_equal true, recs[0].some_boolean
913
+ end
914
+
915
+ cross_test "up mirror files do not include records which online is known to already have the latest version of" do
916
+ mirror_data = nil
917
+ in_offline_app do
918
+ GroupOwnedRecord.create!(:description => "Another Record", :group => @offline_group)
919
+ @offline_group_data.description = "Changed"
920
+ @offline_group_data.save!
921
+ mirror_data = Offroad::MirrorData.new(@offline_group).write_upwards_data
922
+ end
923
+
924
+ in_online_app do
925
+ Offroad::MirrorData.new(@offline_group).load_upwards_data(mirror_data)
926
+ mirror_data = Offroad::MirrorData.new(@offline_group).write_downwards_data
927
+ end
928
+
929
+ in_offline_app do
930
+ Offroad::MirrorData.new(@offline_group).load_downwards_data(mirror_data)
931
+ @offline_group_data.description = "Changed Again"
932
+ @offline_group_data.save!
933
+ mirror_data = Offroad::MirrorData.new(@offline_group).write_upwards_data
934
+ end
935
+
936
+ cs = Offroad::CargoStreamer.new(mirror_data, "r")
937
+ recs = []
938
+ cs.each_cargo_section(Offroad::MirrorData.send(:data_cargo_name_for_model, GroupOwnedRecord)) do |batch|
939
+ recs += batch
940
+ end
941
+ assert_equal 1, recs.size
942
+ assert_equal "Changed Again", recs[0].description
943
+ end
944
+
945
+ offline_test "changed records are re-included in new up mirror files if their reception is not confirmed" do
946
+ @offline_group_data.description = "Changed"
947
+ @offline_group_data.save!
948
+
949
+ 2.times do
950
+ cs = Offroad::CargoStreamer.new(Offroad::MirrorData.new(@offline_group).write_upwards_data, "r")
951
+ assert_single_model_cargo_entry_matches(cs, @offline_group_data)
952
+ end
953
+ end
954
+
955
+ online_test "changed records are re-included in new down mirror files if their reception is not confirmed" do
956
+ global_rec = GlobalRecord.create(:title => "Testing")
957
+
958
+ 2.times do
959
+ cs = Offroad::CargoStreamer.new(Offroad::MirrorData.new(@offline_group).write_downwards_data, "r")
960
+ assert_single_model_cargo_entry_matches(cs, global_rec)
961
+ end
962
+ end
963
+
964
+ cross_test "up mirror files do not include deletion requests for records known to be deleted on online system" do
965
+ sec_name = Offroad::MirrorData.send(:deletion_cargo_name_for_model, GroupOwnedRecord)
966
+
967
+ mirror_data = nil
968
+ in_offline_app do
969
+ @offline_group_data.destroy
970
+ mirror_data = Offroad::MirrorData.new(@offline_group).write_upwards_data
971
+ end
972
+
973
+ assert_equal 1, all_records_from_section_named(Offroad::CargoStreamer.new(mirror_data, "r"), sec_name).size
974
+
975
+ in_online_app do
976
+ Offroad::MirrorData.new(@offline_group).load_upwards_data(mirror_data)
977
+ mirror_data = Offroad::MirrorData.new(@offline_group).write_downwards_data
978
+ end
979
+
980
+ in_offline_app do
981
+ Offroad::MirrorData.new(@offline_group).load_downwards_data(mirror_data)
982
+ mirror_data = Offroad::MirrorData.new(@offline_group).write_upwards_data
983
+ end
984
+
985
+ assert_equal 0, all_records_from_section_named(Offroad::CargoStreamer.new(mirror_data, "r"), sec_name).size
986
+ end
987
+
988
+ cross_test "down mirror files do not include deletion requests for records known to be deleted on offline system" do
989
+ sec_name = Offroad::MirrorData.send(:deletion_cargo_name_for_model, GlobalRecord)
990
+
991
+ mirror_data = nil
992
+ in_online_app do
993
+ GlobalRecord.create(:title => "Testing")
994
+ mirror_data = Offroad::MirrorData.new(@offline_group).write_downwards_data
995
+ end
996
+
997
+ in_offline_app do
998
+ Offroad::MirrorData.new(@offline_group).load_downwards_data(mirror_data)
999
+ mirror_data = Offroad::MirrorData.new(@offline_group).write_upwards_data
1000
+ end
1001
+
1002
+ in_online_app do
1003
+ Offroad::MirrorData.new(@offline_group).load_upwards_data(mirror_data)
1004
+ GlobalRecord.find_by_title("Testing").destroy
1005
+ mirror_data = Offroad::MirrorData.new(@offline_group).write_downwards_data
1006
+ end
1007
+
1008
+ assert_equal 1, all_records_from_section_named(Offroad::CargoStreamer.new(mirror_data, "r"), sec_name).size
1009
+
1010
+ in_offline_app do
1011
+ Offroad::MirrorData.new(@offline_group).load_downwards_data(mirror_data)
1012
+ mirror_data = Offroad::MirrorData.new(@offline_group).write_upwards_data
1013
+ end
1014
+
1015
+ in_online_app do
1016
+ Offroad::MirrorData.new(@offline_group).load_upwards_data(mirror_data)
1017
+ mirror_data = Offroad::MirrorData.new(@offline_group).write_downwards_data
1018
+ end
1019
+
1020
+ assert_equal 0, all_records_from_section_named(Offroad::CargoStreamer.new(mirror_data, "r"), sec_name).size
1021
+ end
1022
+
1023
+ offline_test "deletions are re-included in new up mirror files if their reception is not confirmed" do
1024
+ sec_name = Offroad::MirrorData.send(:deletion_cargo_name_for_model, GroupOwnedRecord)
1025
+ @offline_group_data.destroy
1026
+
1027
+ 2.times do
1028
+ cs = Offroad::CargoStreamer.new(Offroad::MirrorData.new(@offline_group).write_upwards_data, "r")
1029
+ assert_equal 1, all_records_from_section_named(cs, sec_name).size
1030
+ end
1031
+ end
1032
+
1033
+ cross_test "deletions are re-included in new down mirror files if their reception is not confirmed" do
1034
+ mirror_data = nil
1035
+ in_online_app do
1036
+ global_rec = GlobalRecord.create(:title => "Testing")
1037
+ mirror_data = Offroad::MirrorData.new(@offline_group).write_downwards_data
1038
+ end
1039
+
1040
+ in_offline_app do
1041
+ Offroad::MirrorData.new(@offline_group).load_downwards_data(mirror_data)
1042
+ mirror_data = Offroad::MirrorData.new(@offline_group).write_upwards_data
1043
+ end
1044
+
1045
+ in_online_app do
1046
+ Offroad::MirrorData.new(@offline_group).load_upwards_data(mirror_data)
1047
+
1048
+ GlobalRecord.find_by_title("Testing").destroy
1049
+ sec_name = Offroad::MirrorData.send(:deletion_cargo_name_for_model, GlobalRecord)
1050
+ 2.times do
1051
+ cs = Offroad::CargoStreamer.new(Offroad::MirrorData.new(@offline_group).write_downwards_data, "r")
1052
+ assert_equal 1, all_records_from_section_named(cs, sec_name).size
1053
+ end
1054
+ end
1055
+ end
1056
+
1057
+ online_test "records from other offline groups are not included in initial down mirror files" do
1058
+ another_offline_group = Group.create(:name => "One More Offline Group")
1059
+ data = GroupOwnedRecord.create(:description => "One More Offline Data", :group => another_offline_group)
1060
+ another_offline_group.group_offline = true
1061
+ mirror_data = Offroad::MirrorData.new(@offline_group, :initial_mode => true).write_downwards_data
1062
+ cs = Offroad::CargoStreamer.new(mirror_data, "r")
1063
+ assert_record_not_present(cs, data)
1064
+ end
1065
+
1066
+ cross_test "protected attributes can be updated from up mirror files" do
1067
+ mirror_data = nil
1068
+ in_offline_app do
1069
+ @offline_group_data.protected_integer = 123
1070
+ @offline_group_data.save!
1071
+ mirror_data = Offroad::MirrorData.new(@offline_group).write_upwards_data
1072
+ end
1073
+
1074
+ in_online_app do
1075
+ assert_not_equal 123, @offline_group_data.protected_integer
1076
+ Offroad::MirrorData.new(@offline_group).load_upwards_data(mirror_data)
1077
+ @offline_group_data.reload
1078
+ assert_equal 123, @offline_group_data.protected_integer
1079
+ end
1080
+ end
1081
+
1082
+ cross_test "protected attributes can be updated from down mirror files" do
1083
+ mirror_data = nil
1084
+ in_online_app do
1085
+ GlobalRecord.create(:title => "Testing")
1086
+ mirror_data = Offroad::MirrorData.new(@offline_group).write_downwards_data
1087
+ end
1088
+
1089
+ in_offline_app do
1090
+ Offroad::MirrorData.new(@offline_group).load_downwards_data(mirror_data)
1091
+ mirror_data = Offroad::MirrorData.new(@offline_group).write_upwards_data
1092
+ end
1093
+
1094
+ in_online_app do
1095
+ Offroad::MirrorData.new(@offline_group).load_upwards_data(mirror_data)
1096
+ grec = GlobalRecord.find_by_title("Testing")
1097
+ grec.protected_integer = 789
1098
+ grec.save!
1099
+ mirror_data = Offroad::MirrorData.new(@offline_group).write_downwards_data
1100
+ end
1101
+
1102
+ in_offline_app do
1103
+ assert_not_equal 789, GlobalRecord.find_by_title("Testing").protected_integer
1104
+ Offroad::MirrorData.new(@offline_group).load_downwards_data(mirror_data)
1105
+ assert_equal 789, GlobalRecord.find_by_title("Testing").protected_integer
1106
+ end
1107
+ end
1108
+
1109
+ cross_test "cannot use an up mirror file to delete the group record itself" do
1110
+ mirror_data = nil
1111
+ in_offline_app do
1112
+ mirror_data = StringIO.open do |sio|
1113
+ cs = Offroad::CargoStreamer.new(sio, "w")
1114
+ Offroad::MirrorData.new(@offline_group).write_upwards_data(cs)
1115
+
1116
+ sec_name = Offroad::MirrorData.send(:deletion_cargo_name_for_model, Group)
1117
+ deletion_srs = Offroad::SendableRecordState.for_record(@offline_group).first
1118
+ deletion_srs.deleted = true
1119
+ cs.write_cargo_section(sec_name, [deletion_srs])
1120
+
1121
+ sec_name = Offroad::MirrorData.send(:deletion_cargo_name_for_model, GroupOwnedRecord)
1122
+ deletion_srs = Offroad::SendableRecordState.for_record(@offline_group_data).first
1123
+ deletion_srs.deleted = true
1124
+ cs.write_cargo_section(sec_name, [deletion_srs])
1125
+
1126
+ sio.string
1127
+ end
1128
+ end
1129
+
1130
+ in_online_app do
1131
+ Offroad::MirrorData.new(@offline_group).load_upwards_data(mirror_data)
1132
+ assert_nil GroupOwnedRecord.find_by_description("Sam") # Make sure the deletion faking method actually works...
1133
+ assert_not_nil Group.find_by_name("An Offline Group") # Except on group base records
1134
+ end
1135
+ end
1136
+
1137
+ cross_test "after loading an initial down mirror file only changed records appear in up mirror" do
1138
+ mirror_data = nil
1139
+ in_online_app do
1140
+ mirror_data = Offroad::MirrorData.new(@offline_group, :initial_mode => true).write_downwards_data
1141
+ end
1142
+
1143
+ in_offline_app(false, true) do
1144
+ Offroad::MirrorData.new(nil, :initial_mode => true).load_downwards_data(mirror_data)
1145
+ group = Group.first
1146
+ group.name = "Weird Al"
1147
+ group.save!
1148
+ group.reload
1149
+ mirror_data = Offroad::MirrorData.new(group).write_upwards_data
1150
+ cs = Offroad::CargoStreamer.new(mirror_data, "r")
1151
+ assert_single_model_cargo_entry_matches(cs, group)
1152
+ assert_record_not_present(cs, GroupOwnedRecord.first)
1153
+ end
1154
+ end
1155
+
1156
+ cross_test "cannot affect group records in offline app using a non-initial down mirror file" do
1157
+ mirror_data = nil
1158
+ in_online_app do
1159
+ mirror_data = StringIO.open do |sio|
1160
+ cs = Offroad::CargoStreamer.new(sio, "w")
1161
+ Offroad::MirrorData.new(@offline_group).write_downwards_data(cs)
1162
+
1163
+ grec = GlobalRecord.create(:title => "Testing 123")
1164
+ sec_name = Offroad::MirrorData.send(:data_cargo_name_for_model, GlobalRecord)
1165
+ cs.write_cargo_section(sec_name, [grec])
1166
+
1167
+ sec_name = Offroad::MirrorData.send(:data_cargo_name_for_model, GroupOwnedRecord)
1168
+ new_rec = GroupOwnedRecord.new(:description => "Brand New Thing", :group => @offline_group)
1169
+ new_rec.id = 1234
1170
+ cs.write_cargo_section(sec_name, [new_rec])
1171
+
1172
+ sec_name = Offroad::MirrorData.send(:deletion_cargo_name_for_model, GroupOwnedRecord)
1173
+ deletion_srs = Offroad::SendableRecordState.for_record(@offline_group_data).new
1174
+ deletion_srs.deleted = true
1175
+ cs.write_cargo_section(sec_name, [deletion_srs])
1176
+
1177
+ sio.string
1178
+ end
1179
+ end
1180
+
1181
+ in_offline_app do
1182
+ Offroad::MirrorData.new(@offline_group).load_downwards_data(mirror_data)
1183
+
1184
+ # Make sure the section faking method actually works...
1185
+ assert_not_nil GlobalRecord.find_by_title("Testing 123")
1186
+
1187
+ # Except on group records
1188
+ assert_nil GroupOwnedRecord.find_by_description("Brand New Thing")
1189
+ assert_not_nil GroupOwnedRecord.find_by_description("Sam")
1190
+ end
1191
+ end
1192
+
1193
+ cross_test "can transfer self-referencing records" do
1194
+ mirror_data = nil
1195
+ in_offline_app do
1196
+ # Create a new self-referencing record
1197
+ new_self_ref = GroupOwnedRecord.create(:description => "Phillip J. Fry", :group => @offline_group)
1198
+ new_self_ref.parent = new_self_ref
1199
+ new_self_ref.save!
1200
+ assert_equal new_self_ref.id, new_self_ref.parent.id
1201
+
1202
+ # Alter an existing record to be self-referencing
1203
+ @offline_group_data.parent = @offline_group_data
1204
+ @offline_group_data.save!
1205
+
1206
+ mirror_data = Offroad::MirrorData.new(@offline_group).write_upwards_data
1207
+ end
1208
+
1209
+ in_online_app do
1210
+ Offroad::MirrorData.new(@offline_group).load_upwards_data(mirror_data)
1211
+ assert_record_states_all_valid
1212
+
1213
+ fry = GroupOwnedRecord.find_by_description("Phillip J. Fry")
1214
+ assert fry
1215
+ assert_equal fry.id, fry.parent.id
1216
+
1217
+ @offline_group_data.reload
1218
+ assert_equal @offline_group_data.id, @offline_group_data.parent.id
1219
+ end
1220
+ end
1221
+
1222
+ cross_test "when a locked group is loaded by an up mirror, it is brought online" do
1223
+ mirror_data = nil
1224
+ in_offline_app do
1225
+ @offline_group.offroad_group_lock!
1226
+ mirror_data = Offroad::MirrorData.new(@offline_group).write_upwards_data
1227
+ end
1228
+ in_online_app do
1229
+ Offroad::MirrorData.new(@offline_group).load_upwards_data(mirror_data)
1230
+ assert @offline_group.group_online?
1231
+ end
1232
+ end
1233
+
1234
+ cross_test "model method after_offroad_upload is called after it is uploaded to initial offline app" do
1235
+ mirror_data = nil
1236
+ in_online_app do
1237
+ mirror_data = Offroad::MirrorData.new(@offline_group, :initial_mode => true).write_downwards_data
1238
+ end
1239
+
1240
+ in_offline_app(false, true) do
1241
+ GroupOwnedRecord.reset_after_upload_count
1242
+ assert_equal 0, GroupOwnedRecord.after_upload_count
1243
+ Offroad::MirrorData.new(nil, :initial_mode => true).load_downwards_data(mirror_data)
1244
+ assert_equal GroupOwnedRecord.count, GroupOwnedRecord.after_upload_count
1245
+ end
1246
+ end
1247
+
1248
+ cross_test "model method after_offroad_upload is called after it is uploaded to online app" do
1249
+ mirror_data = nil
1250
+ in_offline_app do
1251
+ @offline_group_data.description = "The Mystery Spot"
1252
+ @offline_group_data.save!
1253
+ mirror_data = Offroad::MirrorData.new(@offline_group).write_upwards_data
1254
+ end
1255
+ end
1256
+
1257
+ cross_test "model method after_offroad_destroy is called after object destruction propagated to online app" do
1258
+ mirror_data = nil
1259
+ in_offline_app do
1260
+ @offline_group_data.destroy
1261
+ mirror_data = Offroad::MirrorData.new(@offline_group).write_upwards_data
1262
+ end
1263
+
1264
+ in_online_app do
1265
+ GroupOwnedRecord.reset_after_destroy_count
1266
+ assert_equal 0, GroupOwnedRecord.after_destroy_count
1267
+ Offroad::MirrorData.new(@offline_group).load_upwards_data(mirror_data)
1268
+ assert_equal 1, GroupOwnedRecord.after_destroy_count # One record destroyed
1269
+ end
1270
+ end
1271
+ end