offroad 0.0.2 → 0.0.3
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.
- data/LICENSE +674 -674
- data/README.rdoc +29 -29
- data/Rakefile +75 -75
- data/TODO +42 -42
- data/lib/app/models/offroad/group_state.rb +85 -85
- data/lib/app/models/offroad/mirror_info.rb +53 -53
- data/lib/app/models/offroad/model_state.rb +36 -36
- data/lib/app/models/offroad/received_record_state.rb +115 -115
- data/lib/app/models/offroad/sendable_record_state.rb +91 -91
- data/lib/app/models/offroad/system_state.rb +33 -33
- data/lib/cargo_streamer.rb +222 -222
- data/lib/controller_extensions.rb +74 -74
- data/lib/exceptions.rb +16 -16
- data/lib/migrate/20100512164608_create_offroad_tables.rb +72 -72
- data/lib/mirror_data.rb +376 -376
- data/lib/model_extensions.rb +378 -377
- data/lib/module_funcs.rb +94 -94
- data/lib/offroad.rb +41 -41
- data/lib/version.rb +3 -3
- data/lib/view_helper.rb +7 -7
- data/templates/offline.rb +36 -36
- data/templates/offline_database.yml +7 -7
- data/templates/offroad.yml +6 -6
- data/test/app_root/app/controllers/application_controller.rb +2 -2
- data/test/app_root/app/controllers/group_controller.rb +28 -28
- data/test/app_root/app/models/global_record.rb +10 -10
- data/test/app_root/app/models/group.rb +12 -12
- data/test/app_root/app/models/group_owned_record.rb +68 -68
- data/test/app_root/app/models/guest.rb +7 -7
- data/test/app_root/app/models/subrecord.rb +12 -12
- data/test/app_root/app/models/unmirrored_record.rb +4 -4
- data/test/app_root/app/views/group/download_down_mirror.html.erb +3 -3
- data/test/app_root/app/views/group/download_initial_down_mirror.html.erb +3 -3
- data/test/app_root/app/views/group/download_up_mirror.html.erb +5 -5
- data/test/app_root/app/views/layouts/mirror.html.erb +8 -8
- data/test/app_root/config/boot.rb +115 -115
- data/test/app_root/config/database-pg.yml +8 -8
- data/test/app_root/config/database.yml +5 -5
- data/test/app_root/config/environment.rb +24 -24
- data/test/app_root/config/environments/test.rb +17 -17
- data/test/app_root/config/offroad.yml +6 -6
- data/test/app_root/config/routes.rb +4 -4
- data/test/app_root/db/migrate/20100529235049_create_tables.rb +64 -64
- data/test/app_root/lib/common_hobo.rb +15 -15
- data/test/app_root/vendor/plugins/offroad/init.rb +2 -2
- data/test/functional/mirror_operations_test.rb +148 -148
- data/test/test_helper.rb +453 -453
- data/test/unit/app_state_tracking_test.rb +275 -275
- data/test/unit/cargo_streamer_test.rb +332 -332
- data/test/unit/global_data_test.rb +102 -102
- data/test/unit/group_controller_test.rb +152 -152
- data/test/unit/group_data_test.rb +442 -435
- data/test/unit/group_single_test.rb +136 -136
- data/test/unit/hobo_permissions_test.rb +57 -57
- data/test/unit/mirror_data_test.rb +1283 -1283
- data/test/unit/mirror_info_test.rb +31 -31
- data/test/unit/module_funcs_test.rb +37 -37
- data/test/unit/pathological_model_test.rb +62 -62
- data/test/unit/test_framework_test.rb +86 -86
- data/test/unit/unmirrored_data_test.rb +14 -14
- 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
|