offroad 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. data/LICENSE +674 -674
  2. data/README.rdoc +29 -29
  3. data/Rakefile +75 -75
  4. data/TODO +42 -42
  5. data/lib/app/models/offroad/group_state.rb +85 -85
  6. data/lib/app/models/offroad/mirror_info.rb +53 -53
  7. data/lib/app/models/offroad/model_state.rb +36 -36
  8. data/lib/app/models/offroad/received_record_state.rb +115 -115
  9. data/lib/app/models/offroad/sendable_record_state.rb +91 -91
  10. data/lib/app/models/offroad/system_state.rb +33 -33
  11. data/lib/cargo_streamer.rb +222 -222
  12. data/lib/controller_extensions.rb +74 -74
  13. data/lib/exceptions.rb +16 -16
  14. data/lib/migrate/20100512164608_create_offroad_tables.rb +72 -72
  15. data/lib/mirror_data.rb +376 -376
  16. data/lib/model_extensions.rb +378 -377
  17. data/lib/module_funcs.rb +94 -94
  18. data/lib/offroad.rb +41 -41
  19. data/lib/version.rb +3 -3
  20. data/lib/view_helper.rb +7 -7
  21. data/templates/offline.rb +36 -36
  22. data/templates/offline_database.yml +7 -7
  23. data/templates/offroad.yml +6 -6
  24. data/test/app_root/app/controllers/application_controller.rb +2 -2
  25. data/test/app_root/app/controllers/group_controller.rb +28 -28
  26. data/test/app_root/app/models/global_record.rb +10 -10
  27. data/test/app_root/app/models/group.rb +12 -12
  28. data/test/app_root/app/models/group_owned_record.rb +68 -68
  29. data/test/app_root/app/models/guest.rb +7 -7
  30. data/test/app_root/app/models/subrecord.rb +12 -12
  31. data/test/app_root/app/models/unmirrored_record.rb +4 -4
  32. data/test/app_root/app/views/group/download_down_mirror.html.erb +3 -3
  33. data/test/app_root/app/views/group/download_initial_down_mirror.html.erb +3 -3
  34. data/test/app_root/app/views/group/download_up_mirror.html.erb +5 -5
  35. data/test/app_root/app/views/layouts/mirror.html.erb +8 -8
  36. data/test/app_root/config/boot.rb +115 -115
  37. data/test/app_root/config/database-pg.yml +8 -8
  38. data/test/app_root/config/database.yml +5 -5
  39. data/test/app_root/config/environment.rb +24 -24
  40. data/test/app_root/config/environments/test.rb +17 -17
  41. data/test/app_root/config/offroad.yml +6 -6
  42. data/test/app_root/config/routes.rb +4 -4
  43. data/test/app_root/db/migrate/20100529235049_create_tables.rb +64 -64
  44. data/test/app_root/lib/common_hobo.rb +15 -15
  45. data/test/app_root/vendor/plugins/offroad/init.rb +2 -2
  46. data/test/functional/mirror_operations_test.rb +148 -148
  47. data/test/test_helper.rb +453 -453
  48. data/test/unit/app_state_tracking_test.rb +275 -275
  49. data/test/unit/cargo_streamer_test.rb +332 -332
  50. data/test/unit/global_data_test.rb +102 -102
  51. data/test/unit/group_controller_test.rb +152 -152
  52. data/test/unit/group_data_test.rb +442 -435
  53. data/test/unit/group_single_test.rb +136 -136
  54. data/test/unit/hobo_permissions_test.rb +57 -57
  55. data/test/unit/mirror_data_test.rb +1283 -1283
  56. data/test/unit/mirror_info_test.rb +31 -31
  57. data/test/unit/module_funcs_test.rb +37 -37
  58. data/test/unit/pathological_model_test.rb +62 -62
  59. data/test/unit/test_framework_test.rb +86 -86
  60. data/test/unit/unmirrored_data_test.rb +14 -14
  61. metadata +6 -8
@@ -1,332 +1,332 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
2
-
3
- class CargoStreamerTest < Test::Unit::TestCase
4
- # Based on the pattern found here: http://stackoverflow.com/questions/315850/rails-model-without-database
5
- class TestModel < ActiveRecord::Base
6
- self.abstract_class = true
7
-
8
- def self.columns
9
- @columns ||= []
10
- end
11
-
12
- columns << ActiveRecord::ConnectionAdapters::Column.new("id", nil, "integer", true)
13
- columns << ActiveRecord::ConnectionAdapters::Column.new("data", "", "string", false)
14
-
15
- has_one(:fake_association)
16
- attr_accessor :fake_association
17
-
18
- validates_presence_of :data
19
-
20
- @@safety_switch = true
21
-
22
- def self.safe_to_load_from_cargo_stream?
23
- @@safety_switch
24
- end
25
-
26
- def self.set_safe
27
- @@safety_switch = true
28
- end
29
-
30
- def self.set_unsafe
31
- @@safety_switch = false
32
- end
33
- end
34
-
35
- def generate_cargo_string(hash, skip_validation = false)
36
- return StringIO.open do |sio|
37
- writer = Offroad::CargoStreamer.new(sio, "w")
38
- hash.each do |key, arr|
39
- arr.each do |elem|
40
- writer.write_cargo_section(key, elem, :skip_validation => skip_validation)
41
- end
42
- end
43
-
44
- sio.string
45
- end
46
- end
47
-
48
- def retrieve_cargo_from_string(str)
49
- hash = {}
50
-
51
- reader = Offroad::CargoStreamer.new(str, "r")
52
- reader.cargo_section_names.each do |key|
53
- hash[key] = []
54
- reader.each_cargo_section(key) do |elem|
55
- hash[key] << elem
56
- end
57
- end
58
-
59
- return hash
60
- end
61
-
62
- def round_trip(hash = {})
63
- retrieve_cargo_from_string(generate_cargo_string(hash))
64
- end
65
-
66
- # Asserts content equality between two hashes of arrays of arrays of records ("haar"s)
67
- # Cannot just do new_hash == hash, because ActiveRecord#== always false when comparing two unsaved records.
68
- def assert_haar_equality(first_hash, second_hash)
69
- assert_nothing_raised do
70
- # Assert that they are subsets of each other
71
- [[first_hash, second_hash], [second_hash, first_hash]].each do |hash_a, hash_b|
72
- hash_a.each do |key, arr|
73
- arr.each_with_index do |subarr, i|
74
- subarr.each_with_index do |rec, j|
75
- rec.attributes.each_pair do |attr_key, attr_value|
76
- raise "Mismatch" unless attr_value == hash_b[key][i][j].attributes[attr_key]
77
- end
78
- end
79
- end
80
- end
81
- end
82
- end
83
- end
84
-
85
- def assert_round_trip_equality(hash = {})
86
- assert_haar_equality(hash, round_trip(hash))
87
- end
88
-
89
- def test_rec(str)
90
- TestModel.new(:data => str)
91
- end
92
-
93
- agnostic_test "can encode and retrieve a model instances in an array" do
94
- assert_round_trip_equality "test" => [[test_rec("A"), test_rec("B")]]
95
- end
96
-
97
- agnostic_test "can encode and retrieve model instances with first-level association data" do
98
- r1 = test_rec("A")
99
- r1.fake_association = test_rec("B")
100
- r2 = test_rec("XYZ") # Making sure we can also encode a model instance that doesn't have the assoc data
101
-
102
- str = StringIO.open do |sio|
103
- writer = Offroad::CargoStreamer.new(sio, "w")
104
- writer.write_cargo_section("abc", [r1, r2], :include => [:fake_association])
105
- sio.string
106
- end
107
-
108
- decoded_rec = StringIO.open(str) do |sio|
109
- cs = Offroad::CargoStreamer.new(sio, "r")
110
- cs.first_cargo_section("abc")[0]
111
- end
112
-
113
- assert_equal "B", decoded_rec.fake_association.data
114
- end
115
-
116
- agnostic_test "encoded models do not lose their id" do
117
- rec = test_rec("ABC")
118
- rec.id = 45
119
- decoded = round_trip "test" => [[rec]]
120
- assert_equal 45, decoded["test"][0][0].id
121
- end
122
-
123
- agnostic_test "cannot encode and retrieve non-model data" do
124
- assert_raise Offroad::CargoStreamerError do
125
- generate_cargo_string "a" => [[1]]
126
- end
127
- end
128
-
129
- agnostic_test "cannot encode a model that is not in an array" do
130
- assert_raise Offroad::CargoStreamerError do
131
- # This is not "in an array" for CargoStreamer; look at how generate_cargo_string is implemented
132
- generate_cargo_string "a" => [test_rec("Test")]
133
- end
134
- end
135
-
136
- agnostic_test "can decode cargo data even if there is other stuff around it" do
137
- test_hash = {"foo bar narf bork" => [[test_rec("Test")]]}
138
- str = "BLAH BLAH BLAH" + generate_cargo_string(test_hash) + "BAR BAR BAR"
139
- assert_haar_equality test_hash, retrieve_cargo_from_string(str)
140
- end
141
-
142
- agnostic_test "can correctly identify the names of the cargo sections" do
143
- test_hash = {"abc" => [[test_rec("A")]], "xyz" => [[test_rec("X")]]}
144
- cs = Offroad::CargoStreamer.new(generate_cargo_string(test_hash), "r")
145
- assert_equal test_hash.keys, cs.cargo_section_names
146
- assert cs.has_cargo_named?("abc")
147
- assert_equal false, cs.has_cargo_named?("foobar")
148
- end
149
-
150
- agnostic_test "can create and retrieve multiple ordered cargo sections with the same name" do
151
- test_data = [[test_rec("a"), test_rec("b")], [test_rec("c"), test_rec("d")], [test_rec("e"), test_rec("f")]]
152
-
153
- str = StringIO.open do |sio|
154
- cs = Offroad::CargoStreamer.new(sio, "w")
155
- test_data.each do |dat|
156
- cs.write_cargo_section("xyz", dat)
157
- end
158
- sio.string
159
- end
160
-
161
- result_data = []
162
- cs = Offroad::CargoStreamer.new(str, "r")
163
- cs.each_cargo_section "xyz" do |dat|
164
- result_data << dat
165
- end
166
-
167
- assert_haar_equality({"test" => test_data}, {"test" => result_data})
168
- end
169
-
170
- agnostic_test "can use first_cargo_section to get only the first section with a given name" do
171
- result = StringIO.open do |sio|
172
- cs = Offroad::CargoStreamer.new(sio, "w")
173
- for i in 1..3 do
174
- cs.write_cargo_section("testing", [test_rec("item number #{i}")])
175
- end
176
- sio.string
177
- end
178
-
179
- cs = Offroad::CargoStreamer.new(result, "r")
180
- assert_equal test_rec("item number 1").attributes, cs.first_cargo_section("testing")[0].attributes
181
- assert_equal nil, cs.first_cargo_section("no-such-section")
182
- end
183
-
184
- agnostic_test "can use first_cargo_element to get the first element of the first section with a given name" do
185
- result = StringIO.open do |sio|
186
- cs = Offroad::CargoStreamer.new(sio, "w")
187
- [10, 20, 30].each do |i|
188
- cs.write_cargo_section("testing", [test_rec("item number #{i}"), test_rec("item number #{i+1}")])
189
- end
190
- sio.string
191
- end
192
-
193
- cs = Offroad::CargoStreamer.new(result, "r")
194
- assert_equal test_rec("item number 10").attributes, cs.first_cargo_element("testing").attributes
195
- assert_equal nil, cs.first_cargo_element("no-such-section")
196
- end
197
-
198
- agnostic_test "can use :human_readable to include a string version of a record" do
199
- test_str = "ABCD\n123"
200
- rec = test_rec(test_str)
201
-
202
- result = StringIO.open do |sio|
203
- cs = Offroad::CargoStreamer.new(sio, "w")
204
- cs.write_cargo_section("test", [rec], :human_readable => false)
205
- sio.string
206
- end
207
- assert_equal false, result.include?(test_str)
208
-
209
- result = StringIO.open do |sio|
210
- cs = Offroad::CargoStreamer.new(sio, "w")
211
- cs.write_cargo_section("test", [rec], :human_readable => true)
212
- sio.string
213
- end
214
- assert result.include?(test_str)
215
- end
216
-
217
- agnostic_test "uses an md5 fingerprint to detect corruption" do
218
- str = generate_cargo_string "test" => [[test_rec("abc")]]
219
-
220
- md5sum = nil
221
- if str =~ /\b([0-9a-f]{32})\b/
222
- md5sum = $1
223
- else
224
- flunk "Unable to find an md5sum in the generated string"
225
- end
226
- assert_raise Offroad::CargoStreamerError, "Changing fingerprint causes exception to be raised" do
227
- retrieve_cargo_from_string(str.gsub md5sum, "a"*md5sum.size)
228
- end
229
-
230
- assert_raise Offroad::CargoStreamerError, "Changing base64 content causes exception to be raised" do
231
- # This is somewhat of an implementation-dependent test; I checked manually that the data has these strings.
232
- # It's safe, though, as changing the implementation should cause false neg, not false pos.
233
- retrieve_cargo_from_string(str.sub("X", "x"))
234
- end
235
- end
236
-
237
- agnostic_test "modes r and w work, other modes do not" do
238
- assert_nothing_raised "Mode r works" do
239
- Offroad::CargoStreamer.new(StringIO.new(), "r")
240
- end
241
-
242
- assert_nothing_raised "Mode w works" do
243
- Offroad::CargoStreamer.new(StringIO.new(), "w")
244
- end
245
-
246
- assert_raise Offroad::CargoStreamerError, "Mode a doesn't work" do
247
- Offroad::CargoStreamer.new(StringIO.new(), "a")
248
- end
249
- end
250
-
251
- agnostic_test "cannot write cargo in read mode" do
252
- assert_raise Offroad::CargoStreamerError do
253
- cs = Offroad::CargoStreamer.new(StringIO.new, "r")
254
- cs.write_cargo_section("test", [test_rec("test")])
255
- end
256
- end
257
-
258
- agnostic_test "cannot use invalid cargo section names" do
259
- cs = Offroad::CargoStreamer.new(StringIO.new, "w")
260
-
261
- assert_raise Offroad::CargoStreamerError, "Expect exception for symbol cargo name" do
262
- cs.write_cargo_section(:test, [test_rec("test")])
263
- end
264
-
265
- assert_raise Offroad::CargoStreamerError, "Expect exception for cargo name that's bad in HTML comments" do
266
- cs.write_cargo_section("whatever--foobar", [test_rec("test")])
267
- end
268
-
269
- assert_raise Offroad::CargoStreamerError, "Expect exception for cargo name that's multiline" do
270
- cs.write_cargo_section("whatever\nfoobar", [test_rec("test")])
271
- end
272
- end
273
-
274
- agnostic_test "cannot encode invalid records except with :skip_validation option" do
275
- rec = TestModel.new() # Nothing set to the required "data" field
276
- assert_equal false, rec.valid?
277
- assert_raise Offroad::CargoStreamerError do
278
- generate_cargo_string "foo" => [[rec]]
279
- end
280
- assert_nothing_raised Offroad::CargoStreamerError do
281
- generate_cargo_string({"foo" => [[rec]]}, true)
282
- end
283
- rec.data = "Something"
284
- assert_nothing_raised do
285
- generate_cargo_string "foo" => [[rec]]
286
- end
287
- end
288
-
289
- agnostic_test "cannot encode a non-safe model class" do
290
- begin
291
- TestModel.set_safe
292
- assert_nothing_raised do
293
- str = generate_cargo_string "test" => [[test_rec("ABC")]]
294
- end
295
- TestModel.set_unsafe
296
- assert_raise Offroad::CargoStreamerError do
297
- str = generate_cargo_string "test" => [[test_rec("ABC")]]
298
- end
299
- ensure
300
- TestModel.set_safe
301
- end
302
- end
303
-
304
- agnostic_test "cannot trick cargo streamer into decoding a non-safe model class" do
305
- begin
306
- TestModel.set_safe
307
- str = generate_cargo_string "test" => [[test_rec("Stuff")]]
308
-
309
- assert_nothing_raised do
310
- retrieve_cargo_from_string(str)
311
- end
312
- TestModel.set_unsafe
313
- assert_raise Offroad::CargoStreamerError do
314
- retrieve_cargo_from_string(str)
315
- end
316
- ensure
317
- TestModel.set_safe
318
- end
319
- end
320
-
321
- agnostic_test "cargo streamer can read directly from a passed in string" do
322
- str = generate_cargo_string("test" => [[test_rec("abc")]])
323
- cs = Offroad::CargoStreamer.new(str, "r")
324
- assert cs.has_cargo_named?("test")
325
- end
326
-
327
- agnostic_test "cannot have cargo streamer write to a passed in string" do
328
- assert_raise Offroad::CargoStreamerError do
329
- Offroad::CargoStreamer.new("", "w")
330
- end
331
- end
332
- end
1
+ require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
2
+
3
+ class CargoStreamerTest < Test::Unit::TestCase
4
+ # Based on the pattern found here: http://stackoverflow.com/questions/315850/rails-model-without-database
5
+ class TestModel < ActiveRecord::Base
6
+ self.abstract_class = true
7
+
8
+ def self.columns
9
+ @columns ||= []
10
+ end
11
+
12
+ columns << ActiveRecord::ConnectionAdapters::Column.new("id", nil, "integer", true)
13
+ columns << ActiveRecord::ConnectionAdapters::Column.new("data", "", "string", false)
14
+
15
+ has_one(:fake_association)
16
+ attr_accessor :fake_association
17
+
18
+ validates_presence_of :data
19
+
20
+ @@safety_switch = true
21
+
22
+ def self.safe_to_load_from_cargo_stream?
23
+ @@safety_switch
24
+ end
25
+
26
+ def self.set_safe
27
+ @@safety_switch = true
28
+ end
29
+
30
+ def self.set_unsafe
31
+ @@safety_switch = false
32
+ end
33
+ end
34
+
35
+ def generate_cargo_string(hash, skip_validation = false)
36
+ return StringIO.open do |sio|
37
+ writer = Offroad::CargoStreamer.new(sio, "w")
38
+ hash.each do |key, arr|
39
+ arr.each do |elem|
40
+ writer.write_cargo_section(key, elem, :skip_validation => skip_validation)
41
+ end
42
+ end
43
+
44
+ sio.string
45
+ end
46
+ end
47
+
48
+ def retrieve_cargo_from_string(str)
49
+ hash = {}
50
+
51
+ reader = Offroad::CargoStreamer.new(str, "r")
52
+ reader.cargo_section_names.each do |key|
53
+ hash[key] = []
54
+ reader.each_cargo_section(key) do |elem|
55
+ hash[key] << elem
56
+ end
57
+ end
58
+
59
+ return hash
60
+ end
61
+
62
+ def round_trip(hash = {})
63
+ retrieve_cargo_from_string(generate_cargo_string(hash))
64
+ end
65
+
66
+ # Asserts content equality between two hashes of arrays of arrays of records ("haar"s)
67
+ # Cannot just do new_hash == hash, because ActiveRecord#== always false when comparing two unsaved records.
68
+ def assert_haar_equality(first_hash, second_hash)
69
+ assert_nothing_raised do
70
+ # Assert that they are subsets of each other
71
+ [[first_hash, second_hash], [second_hash, first_hash]].each do |hash_a, hash_b|
72
+ hash_a.each do |key, arr|
73
+ arr.each_with_index do |subarr, i|
74
+ subarr.each_with_index do |rec, j|
75
+ rec.attributes.each_pair do |attr_key, attr_value|
76
+ raise "Mismatch" unless attr_value == hash_b[key][i][j].attributes[attr_key]
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
84
+
85
+ def assert_round_trip_equality(hash = {})
86
+ assert_haar_equality(hash, round_trip(hash))
87
+ end
88
+
89
+ def test_rec(str)
90
+ TestModel.new(:data => str)
91
+ end
92
+
93
+ agnostic_test "can encode and retrieve a model instances in an array" do
94
+ assert_round_trip_equality "test" => [[test_rec("A"), test_rec("B")]]
95
+ end
96
+
97
+ agnostic_test "can encode and retrieve model instances with first-level association data" do
98
+ r1 = test_rec("A")
99
+ r1.fake_association = test_rec("B")
100
+ r2 = test_rec("XYZ") # Making sure we can also encode a model instance that doesn't have the assoc data
101
+
102
+ str = StringIO.open do |sio|
103
+ writer = Offroad::CargoStreamer.new(sio, "w")
104
+ writer.write_cargo_section("abc", [r1, r2], :include => [:fake_association])
105
+ sio.string
106
+ end
107
+
108
+ decoded_rec = StringIO.open(str) do |sio|
109
+ cs = Offroad::CargoStreamer.new(sio, "r")
110
+ cs.first_cargo_section("abc")[0]
111
+ end
112
+
113
+ assert_equal "B", decoded_rec.fake_association.data
114
+ end
115
+
116
+ agnostic_test "encoded models do not lose their id" do
117
+ rec = test_rec("ABC")
118
+ rec.id = 45
119
+ decoded = round_trip "test" => [[rec]]
120
+ assert_equal 45, decoded["test"][0][0].id
121
+ end
122
+
123
+ agnostic_test "cannot encode and retrieve non-model data" do
124
+ assert_raise Offroad::CargoStreamerError do
125
+ generate_cargo_string "a" => [[1]]
126
+ end
127
+ end
128
+
129
+ agnostic_test "cannot encode a model that is not in an array" do
130
+ assert_raise Offroad::CargoStreamerError do
131
+ # This is not "in an array" for CargoStreamer; look at how generate_cargo_string is implemented
132
+ generate_cargo_string "a" => [test_rec("Test")]
133
+ end
134
+ end
135
+
136
+ agnostic_test "can decode cargo data even if there is other stuff around it" do
137
+ test_hash = {"foo bar narf bork" => [[test_rec("Test")]]}
138
+ str = "BLAH BLAH BLAH" + generate_cargo_string(test_hash) + "BAR BAR BAR"
139
+ assert_haar_equality test_hash, retrieve_cargo_from_string(str)
140
+ end
141
+
142
+ agnostic_test "can correctly identify the names of the cargo sections" do
143
+ test_hash = {"abc" => [[test_rec("A")]], "xyz" => [[test_rec("X")]]}
144
+ cs = Offroad::CargoStreamer.new(generate_cargo_string(test_hash), "r")
145
+ assert_equal test_hash.keys, cs.cargo_section_names
146
+ assert cs.has_cargo_named?("abc")
147
+ assert_equal false, cs.has_cargo_named?("foobar")
148
+ end
149
+
150
+ agnostic_test "can create and retrieve multiple ordered cargo sections with the same name" do
151
+ test_data = [[test_rec("a"), test_rec("b")], [test_rec("c"), test_rec("d")], [test_rec("e"), test_rec("f")]]
152
+
153
+ str = StringIO.open do |sio|
154
+ cs = Offroad::CargoStreamer.new(sio, "w")
155
+ test_data.each do |dat|
156
+ cs.write_cargo_section("xyz", dat)
157
+ end
158
+ sio.string
159
+ end
160
+
161
+ result_data = []
162
+ cs = Offroad::CargoStreamer.new(str, "r")
163
+ cs.each_cargo_section "xyz" do |dat|
164
+ result_data << dat
165
+ end
166
+
167
+ assert_haar_equality({"test" => test_data}, {"test" => result_data})
168
+ end
169
+
170
+ agnostic_test "can use first_cargo_section to get only the first section with a given name" do
171
+ result = StringIO.open do |sio|
172
+ cs = Offroad::CargoStreamer.new(sio, "w")
173
+ for i in 1..3 do
174
+ cs.write_cargo_section("testing", [test_rec("item number #{i}")])
175
+ end
176
+ sio.string
177
+ end
178
+
179
+ cs = Offroad::CargoStreamer.new(result, "r")
180
+ assert_equal test_rec("item number 1").attributes, cs.first_cargo_section("testing")[0].attributes
181
+ assert_equal nil, cs.first_cargo_section("no-such-section")
182
+ end
183
+
184
+ agnostic_test "can use first_cargo_element to get the first element of the first section with a given name" do
185
+ result = StringIO.open do |sio|
186
+ cs = Offroad::CargoStreamer.new(sio, "w")
187
+ [10, 20, 30].each do |i|
188
+ cs.write_cargo_section("testing", [test_rec("item number #{i}"), test_rec("item number #{i+1}")])
189
+ end
190
+ sio.string
191
+ end
192
+
193
+ cs = Offroad::CargoStreamer.new(result, "r")
194
+ assert_equal test_rec("item number 10").attributes, cs.first_cargo_element("testing").attributes
195
+ assert_equal nil, cs.first_cargo_element("no-such-section")
196
+ end
197
+
198
+ agnostic_test "can use :human_readable to include a string version of a record" do
199
+ test_str = "ABCD\n123"
200
+ rec = test_rec(test_str)
201
+
202
+ result = StringIO.open do |sio|
203
+ cs = Offroad::CargoStreamer.new(sio, "w")
204
+ cs.write_cargo_section("test", [rec], :human_readable => false)
205
+ sio.string
206
+ end
207
+ assert_equal false, result.include?(test_str)
208
+
209
+ result = StringIO.open do |sio|
210
+ cs = Offroad::CargoStreamer.new(sio, "w")
211
+ cs.write_cargo_section("test", [rec], :human_readable => true)
212
+ sio.string
213
+ end
214
+ assert result.include?(test_str)
215
+ end
216
+
217
+ agnostic_test "uses an md5 fingerprint to detect corruption" do
218
+ str = generate_cargo_string "test" => [[test_rec("abc")]]
219
+
220
+ md5sum = nil
221
+ if str =~ /\b([0-9a-f]{32})\b/
222
+ md5sum = $1
223
+ else
224
+ flunk "Unable to find an md5sum in the generated string"
225
+ end
226
+ assert_raise Offroad::CargoStreamerError, "Changing fingerprint causes exception to be raised" do
227
+ retrieve_cargo_from_string(str.gsub md5sum, "a"*md5sum.size)
228
+ end
229
+
230
+ assert_raise Offroad::CargoStreamerError, "Changing base64 content causes exception to be raised" do
231
+ # This is somewhat of an implementation-dependent test; I checked manually that the data has these strings.
232
+ # It's safe, though, as changing the implementation should cause false neg, not false pos.
233
+ retrieve_cargo_from_string(str.sub("X", "x"))
234
+ end
235
+ end
236
+
237
+ agnostic_test "modes r and w work, other modes do not" do
238
+ assert_nothing_raised "Mode r works" do
239
+ Offroad::CargoStreamer.new(StringIO.new(), "r")
240
+ end
241
+
242
+ assert_nothing_raised "Mode w works" do
243
+ Offroad::CargoStreamer.new(StringIO.new(), "w")
244
+ end
245
+
246
+ assert_raise Offroad::CargoStreamerError, "Mode a doesn't work" do
247
+ Offroad::CargoStreamer.new(StringIO.new(), "a")
248
+ end
249
+ end
250
+
251
+ agnostic_test "cannot write cargo in read mode" do
252
+ assert_raise Offroad::CargoStreamerError do
253
+ cs = Offroad::CargoStreamer.new(StringIO.new, "r")
254
+ cs.write_cargo_section("test", [test_rec("test")])
255
+ end
256
+ end
257
+
258
+ agnostic_test "cannot use invalid cargo section names" do
259
+ cs = Offroad::CargoStreamer.new(StringIO.new, "w")
260
+
261
+ assert_raise Offroad::CargoStreamerError, "Expect exception for symbol cargo name" do
262
+ cs.write_cargo_section(:test, [test_rec("test")])
263
+ end
264
+
265
+ assert_raise Offroad::CargoStreamerError, "Expect exception for cargo name that's bad in HTML comments" do
266
+ cs.write_cargo_section("whatever--foobar", [test_rec("test")])
267
+ end
268
+
269
+ assert_raise Offroad::CargoStreamerError, "Expect exception for cargo name that's multiline" do
270
+ cs.write_cargo_section("whatever\nfoobar", [test_rec("test")])
271
+ end
272
+ end
273
+
274
+ agnostic_test "cannot encode invalid records except with :skip_validation option" do
275
+ rec = TestModel.new() # Nothing set to the required "data" field
276
+ assert_equal false, rec.valid?
277
+ assert_raise Offroad::CargoStreamerError do
278
+ generate_cargo_string "foo" => [[rec]]
279
+ end
280
+ assert_nothing_raised Offroad::CargoStreamerError do
281
+ generate_cargo_string({"foo" => [[rec]]}, true)
282
+ end
283
+ rec.data = "Something"
284
+ assert_nothing_raised do
285
+ generate_cargo_string "foo" => [[rec]]
286
+ end
287
+ end
288
+
289
+ agnostic_test "cannot encode a non-safe model class" do
290
+ begin
291
+ TestModel.set_safe
292
+ assert_nothing_raised do
293
+ str = generate_cargo_string "test" => [[test_rec("ABC")]]
294
+ end
295
+ TestModel.set_unsafe
296
+ assert_raise Offroad::CargoStreamerError do
297
+ str = generate_cargo_string "test" => [[test_rec("ABC")]]
298
+ end
299
+ ensure
300
+ TestModel.set_safe
301
+ end
302
+ end
303
+
304
+ agnostic_test "cannot trick cargo streamer into decoding a non-safe model class" do
305
+ begin
306
+ TestModel.set_safe
307
+ str = generate_cargo_string "test" => [[test_rec("Stuff")]]
308
+
309
+ assert_nothing_raised do
310
+ retrieve_cargo_from_string(str)
311
+ end
312
+ TestModel.set_unsafe
313
+ assert_raise Offroad::CargoStreamerError do
314
+ retrieve_cargo_from_string(str)
315
+ end
316
+ ensure
317
+ TestModel.set_safe
318
+ end
319
+ end
320
+
321
+ agnostic_test "cargo streamer can read directly from a passed in string" do
322
+ str = generate_cargo_string("test" => [[test_rec("abc")]])
323
+ cs = Offroad::CargoStreamer.new(str, "r")
324
+ assert cs.has_cargo_named?("test")
325
+ end
326
+
327
+ agnostic_test "cannot have cargo streamer write to a passed in string" do
328
+ assert_raise Offroad::CargoStreamerError do
329
+ Offroad::CargoStreamer.new("", "w")
330
+ end
331
+ end
332
+ end