mongo 1.0.9 → 1.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.
@@ -0,0 +1,15 @@
1
+ # encoding:utf-8
2
+ require './test/test_helper'
3
+
4
+ class BinaryTest < Test::Unit::TestCase
5
+ context "Inspecting" do
6
+ setup do
7
+ @data = ("THIS IS BINARY " * 50).unpack("c*")
8
+ end
9
+
10
+ should "not display actual data" do
11
+ binary = BSON::Binary.new(@data)
12
+ assert_equal "<BSON::Binary:#{binary.object_id}>", binary.inspect
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,528 @@
1
+ # encoding:utf-8
2
+ require './test/test_helper'
3
+ require 'complex'
4
+ require 'bigdecimal'
5
+ require 'rational'
6
+
7
+ begin
8
+ require 'active_support/core_ext'
9
+ Time.zone = "Pacific Time (US & Canada)"
10
+ Zone = Time.zone.now
11
+ rescue LoadError
12
+ warn 'Mocking time with zone'
13
+ module ActiveSupport
14
+ class TimeWithZone
15
+ end
16
+ end
17
+ Zone = ActiveSupport::TimeWithZone.new
18
+ end
19
+
20
+ class BSONTest < Test::Unit::TestCase
21
+
22
+ include BSON
23
+
24
+ # This setup allows us to change the decoders for
25
+ # cross-coder compatibility testing
26
+ def setup
27
+ @encoder = BSON::BSON_CODER
28
+ @decoder = @encoder
29
+ end
30
+
31
+ def assert_doc_pass(doc, options={})
32
+ bson = @encoder.serialize(doc)
33
+ if options[:debug]
34
+ puts "DEBUGGIN DOC:"
35
+ p bson.to_a
36
+ puts "DESERIALIZES TO:"
37
+ p @decoder.deserialize(bson)
38
+ end
39
+ assert_equal @decoder.serialize(doc).to_a, bson.to_a
40
+ assert_equal doc, @decoder.deserialize(bson)
41
+ end
42
+
43
+ def test_require_hash
44
+ assert_raise_error InvalidDocument, "takes a Hash" do
45
+ BSON.serialize('foo')
46
+ end
47
+
48
+ assert_raise_error InvalidDocument, "takes a Hash" do
49
+ BSON.serialize(Object.new)
50
+ end
51
+
52
+ assert_raise_error InvalidDocument, "takes a Hash" do
53
+ BSON.serialize(Set.new)
54
+ end
55
+ end
56
+
57
+ def test_string
58
+ doc = {'doc' => 'hello, world'}
59
+ assert_doc_pass(doc)
60
+ end
61
+
62
+ def test_valid_utf8_string
63
+ doc = {'doc' => 'aé'}
64
+ assert_doc_pass(doc)
65
+ end
66
+
67
+ def test_valid_utf8_key
68
+ doc = {'aé' => 'hello'}
69
+ assert_doc_pass(doc)
70
+ end
71
+
72
+ def test_document_length
73
+ doc = {'name' => 'a' * 5 * 1024 * 1024}
74
+ assert_raise InvalidDocument do
75
+ assert @encoder.serialize(doc)
76
+ end
77
+ end
78
+
79
+ # In 1.8 we test that other string encodings raise an exception.
80
+ # In 1.9 we test that they get auto-converted.
81
+ if RUBY_VERSION < '1.9'
82
+ require 'iconv'
83
+ def test_invalid_string
84
+ string = Iconv.conv('iso-8859-1', 'utf-8', 'aé')
85
+ doc = {'doc' => string}
86
+ assert_raise InvalidStringEncoding do
87
+ @encoder.serialize(doc)
88
+ end
89
+ end
90
+
91
+ def test_invalid_key
92
+ key = Iconv.conv('iso-8859-1', 'utf-8', 'aé')
93
+ doc = {key => 'hello'}
94
+ assert_raise InvalidStringEncoding do
95
+ @encoder.serialize(doc)
96
+ end
97
+ end
98
+ else
99
+ def test_non_utf8_string
100
+ bson = BSON::BSON_CODER.serialize({'str' => 'aé'.encode('iso-8859-1')})
101
+ result = BSON::BSON_CODER.deserialize(bson)['str']
102
+ assert_equal 'aé', result
103
+ assert_equal 'UTF-8', result.encoding.name
104
+ end
105
+
106
+ def test_non_utf8_key
107
+ bson = BSON::BSON_CODER.serialize({'aé'.encode('iso-8859-1') => 'hello'})
108
+ assert_equal 'hello', BSON::BSON_CODER.deserialize(bson)['aé']
109
+ end
110
+
111
+ # Based on a test from sqlite3-ruby
112
+ def test_default_internal_is_honored
113
+ before_enc = Encoding.default_internal
114
+
115
+ str = "壁に耳あり、障子に目あり"
116
+ bson = BSON::BSON_CODER.serialize("x" => str)
117
+
118
+ Encoding.default_internal = 'EUC-JP'
119
+ out = BSON::BSON_CODER.deserialize(bson)["x"]
120
+
121
+ assert_equal Encoding.default_internal, out.encoding
122
+ assert_equal str.encode('EUC-JP'), out
123
+ assert_equal str, out.encode(str.encoding)
124
+ ensure
125
+ Encoding.default_internal = before_enc
126
+ end
127
+ end
128
+
129
+ def test_code
130
+ doc = {'$where' => Code.new('this.a.b < this.b')}
131
+ assert_doc_pass(doc)
132
+ end
133
+
134
+ def test_code_with_scope
135
+ doc = {'$where' => Code.new('this.a.b < this.b', {'foo' => 1})}
136
+ assert_doc_pass(doc)
137
+ end
138
+
139
+ def test_double
140
+ doc = {'doc' => 41.25}
141
+ assert_doc_pass(doc)
142
+ end
143
+
144
+ def test_int
145
+ doc = {'doc' => 42}
146
+ assert_doc_pass(doc)
147
+
148
+ doc = {"doc" => -5600}
149
+ assert_doc_pass(doc)
150
+
151
+ doc = {"doc" => 2147483647}
152
+ assert_doc_pass(doc)
153
+
154
+ doc = {"doc" => -2147483648}
155
+ assert_doc_pass(doc)
156
+ end
157
+
158
+ def test_ordered_hash
159
+ doc = BSON::OrderedHash.new
160
+ doc["b"] = 1
161
+ doc["a"] = 2
162
+ doc["c"] = 3
163
+ doc["d"] = 4
164
+ assert_doc_pass(doc)
165
+ end
166
+
167
+ def test_object
168
+ doc = {'doc' => {'age' => 42, 'name' => 'Spongebob', 'shoe_size' => 9.5}}
169
+ assert_doc_pass(doc)
170
+ bson = BSON::BSON_CODER.serialize(doc)
171
+ end
172
+
173
+ def test_oid
174
+ doc = {'doc' => ObjectId.new}
175
+ assert_doc_pass(doc)
176
+ end
177
+
178
+ def test_array
179
+ doc = {'doc' => [1, 2, 'a', 'b']}
180
+ assert_doc_pass(doc)
181
+ end
182
+
183
+ def test_regex
184
+ doc = {'doc' => /foobar/i}
185
+ assert_doc_pass(doc)
186
+ end
187
+
188
+ def test_boolean
189
+ doc = {'doc' => true}
190
+ assert_doc_pass(doc)
191
+ end
192
+
193
+ def test_date
194
+ doc = {'date' => Time.now}
195
+ bson = @encoder.serialize(doc)
196
+ doc2 = @decoder.deserialize(bson)
197
+ # Mongo only stores up to the millisecond
198
+ assert_in_delta doc['date'], doc2['date'], 0.001
199
+ end
200
+
201
+ def test_date_returns_as_utc
202
+ doc = {'date' => Time.now}
203
+ bson = @encoder.serialize(doc)
204
+ doc2 = @decoder.deserialize(bson)
205
+ assert doc2['date'].utc?
206
+ end
207
+
208
+ def test_date_before_epoch
209
+ begin
210
+ doc = {'date' => Time.utc(1600)}
211
+ bson = @encoder.serialize(doc)
212
+ doc2 = @decoder.deserialize(bson)
213
+ # Mongo only stores up to the millisecond
214
+ assert_in_delta doc['date'], doc2['date'], 2
215
+ rescue ArgumentError
216
+ # some versions of Ruby won't let you create pre-epoch Time instances
217
+ #
218
+ # TODO figure out how that will work if somebady has saved data
219
+ # w/ early dates already and is just querying for it.
220
+ end
221
+ end
222
+
223
+ def test_exeption_on_using_unsupported_date_class
224
+ [DateTime.now, Date.today, Zone].each do |invalid_date|
225
+ doc = {:date => invalid_date}
226
+ begin
227
+ bson = BSON::BSON_CODER.serialize(doc)
228
+ rescue => e
229
+ ensure
230
+ if !invalid_date.is_a? Time
231
+ assert_equal InvalidDocument, e.class
232
+ assert_match /UTC Time/, e.message
233
+ end
234
+ end
235
+ end
236
+ end
237
+
238
+ def test_dbref
239
+ oid = ObjectId.new
240
+ doc = {}
241
+ doc['dbref'] = DBRef.new('namespace', oid)
242
+ bson = @encoder.serialize(doc)
243
+ doc2 = @decoder.deserialize(bson)
244
+
245
+ # Java doesn't deserialize to DBRefs
246
+ if RUBY_PLATFORM =~ /java/
247
+ assert_equal 'namespace', doc2['dbref']['$ns']
248
+ assert_equal oid, doc2['dbref']['$id']
249
+ else
250
+ assert_equal 'namespace', doc2['dbref'].namespace
251
+ assert_equal oid, doc2['dbref'].object_id
252
+ end
253
+ end
254
+
255
+ def test_symbol
256
+ doc = {'sym' => :foo}
257
+ bson = @encoder.serialize(doc)
258
+ doc2 = @decoder.deserialize(bson)
259
+ assert_equal :foo, doc2['sym']
260
+ end
261
+
262
+ def test_binary
263
+ bin = Binary.new
264
+ 'binstring'.each_byte { |b| bin.put(b) }
265
+
266
+ doc = {'bin' => bin}
267
+ bson = @encoder.serialize(doc)
268
+ doc2 = @decoder.deserialize(bson)
269
+ bin2 = doc2['bin']
270
+ assert_kind_of Binary, bin2
271
+ assert_equal 'binstring', bin2.to_s
272
+ assert_equal Binary::SUBTYPE_BYTES, bin2.subtype
273
+ end
274
+
275
+ def test_binary_with_string
276
+ b = Binary.new('somebinarystring')
277
+ doc = {'bin' => b}
278
+ bson = @encoder.serialize(doc)
279
+ doc2 = @decoder.deserialize(bson)
280
+ bin2 = doc2['bin']
281
+ assert_kind_of Binary, bin2
282
+ assert_equal 'somebinarystring', bin2.to_s
283
+ assert_equal Binary::SUBTYPE_BYTES, bin2.subtype
284
+ end
285
+
286
+ def test_binary_type
287
+ bin = Binary.new([1, 2, 3, 4, 5], Binary::SUBTYPE_USER_DEFINED)
288
+
289
+ doc = {'bin' => bin}
290
+ bson = @encoder.serialize(doc)
291
+ doc2 = @decoder.deserialize(bson)
292
+ bin2 = doc2['bin']
293
+ assert_kind_of Binary, bin2
294
+ assert_equal [1, 2, 3, 4, 5], bin2.to_a
295
+ assert_equal Binary::SUBTYPE_USER_DEFINED, bin2.subtype
296
+ end
297
+
298
+ # Java doesn't support binary subtype 0 yet
299
+ if !(RUBY_PLATFORM =~ /java/)
300
+ def test_binary_subtype_0
301
+ bin = Binary.new([1, 2, 3, 4, 5], Binary::SUBTYPE_SIMPLE)
302
+
303
+ doc = {'bin' => bin}
304
+ bson = @encoder.serialize(doc)
305
+ doc2 = @decoder.deserialize(bson)
306
+ bin2 = doc2['bin']
307
+ assert_kind_of Binary, bin2
308
+ assert_equal [1, 2, 3, 4, 5], bin2.to_a
309
+ assert_equal Binary::SUBTYPE_SIMPLE, bin2.subtype
310
+ end
311
+ end
312
+
313
+ def test_binary_byte_buffer
314
+ bb = Binary.new
315
+ 5.times { |i| bb.put(i + 1) }
316
+
317
+ doc = {'bin' => bb}
318
+ bson = @encoder.serialize(doc)
319
+ doc2 = @decoder.deserialize(bson)
320
+ bin2 = doc2['bin']
321
+ assert_kind_of Binary, bin2
322
+ assert_equal [1, 2, 3, 4, 5], bin2.to_a
323
+ assert_equal Binary::SUBTYPE_BYTES, bin2.subtype
324
+ end
325
+
326
+ def test_put_id_first
327
+ val = BSON::OrderedHash.new
328
+ val['not_id'] = 1
329
+ val['_id'] = 2
330
+ roundtrip = @decoder.deserialize(@encoder.serialize(val, false, true).to_s)
331
+ assert_kind_of BSON::OrderedHash, roundtrip
332
+ assert_equal '_id', roundtrip.keys.first
333
+
334
+ val = {'a' => 'foo', 'b' => 'bar', :_id => 42, 'z' => 'hello'}
335
+ roundtrip = @decoder.deserialize(@encoder.serialize(val, false, true).to_s)
336
+ assert_kind_of BSON::OrderedHash, roundtrip
337
+ assert_equal '_id', roundtrip.keys.first
338
+ end
339
+
340
+ def test_nil_id
341
+ doc = {"_id" => nil}
342
+ assert_doc_pass(doc)
343
+ end
344
+
345
+ if !(RUBY_PLATFORM =~ /java/)
346
+ def test_timestamp
347
+ val = {"test" => [4, 20]}
348
+ assert_equal val, @decoder.deserialize([0x13, 0x00, 0x00, 0x00,
349
+ 0x11, 0x74, 0x65, 0x73,
350
+ 0x74, 0x00, 0x04, 0x00,
351
+ 0x00, 0x00, 0x14, 0x00,
352
+ 0x00, 0x00, 0x00])
353
+
354
+ end
355
+ end
356
+
357
+ def test_overflow
358
+ doc = {"x" => 2**75}
359
+ assert_raise RangeError do
360
+ bson = @encoder.serialize(doc)
361
+ end
362
+
363
+ doc = {"x" => 9223372036854775}
364
+ assert_doc_pass(doc)
365
+
366
+ doc = {"x" => 9223372036854775807}
367
+ assert_doc_pass(doc)
368
+
369
+ doc["x"] = doc["x"] + 1
370
+ assert_raise RangeError do
371
+ bson = @encoder.serialize(doc)
372
+ end
373
+
374
+ doc = {"x" => -9223372036854775}
375
+ assert_doc_pass(doc)
376
+
377
+ doc = {"x" => -9223372036854775808}
378
+ assert_doc_pass(doc)
379
+
380
+ doc["x"] = doc["x"] - 1
381
+ assert_raise RangeError do
382
+ bson = BSON::BSON_CODER.serialize(doc)
383
+ end
384
+ end
385
+
386
+ def test_invalid_numeric_types
387
+ [BigDecimal.new("1.0"), Complex(0, 1), Rational(2, 3)].each do |type|
388
+ doc = {"x" => type}
389
+ begin
390
+ @encoder.serialize(doc)
391
+ rescue => e
392
+ ensure
393
+ assert_equal InvalidDocument, e.class
394
+ assert_match /Cannot serialize/, e.message
395
+ end
396
+ end
397
+ end
398
+
399
+ def test_do_not_change_original_object
400
+ val = BSON::OrderedHash.new
401
+ val['not_id'] = 1
402
+ val['_id'] = 2
403
+ assert val.keys.include?('_id')
404
+ @encoder.serialize(val)
405
+ assert val.keys.include?('_id')
406
+
407
+ val = {'a' => 'foo', 'b' => 'bar', :_id => 42, 'z' => 'hello'}
408
+ assert val.keys.include?(:_id)
409
+ @encoder.serialize(val)
410
+ assert val.keys.include?(:_id)
411
+ end
412
+
413
+ # note we only test for _id here because in the general case we will
414
+ # write duplicates for :key and "key". _id is a special case because
415
+ # we call has_key? to check for it's existence rather than just iterating
416
+ # over it like we do for the rest of the keys. thus, things like
417
+ # HashWithIndifferentAccess can cause problems for _id but not for other
418
+ # keys. rather than require rails to test with HWIA directly, we do this
419
+ # somewhat hacky test.
420
+ def test_no_duplicate_id
421
+ dup = {"_id" => "foo", :_id => "foo"}
422
+ one = {"_id" => "foo"}
423
+
424
+ assert_equal @encoder.serialize(one).to_a, @encoder.serialize(dup).to_a
425
+ end
426
+
427
+ def test_no_duplicate_id_when_moving_id
428
+ dup = {"_id" => "foo", :_id => "foo"}
429
+ one = {:_id => "foo"}
430
+
431
+ assert_equal @encoder.serialize(one, false, true).to_s, @encoder.serialize(dup, false, true).to_s
432
+ end
433
+
434
+ def test_null_character
435
+ doc = {"a" => "\x00"}
436
+
437
+ assert_doc_pass(doc)
438
+
439
+ assert_raise InvalidDocument do
440
+ @encoder.serialize({"\x00" => "a"})
441
+ end
442
+
443
+ assert_raise InvalidDocument do
444
+ @encoder.serialize({"a" => (Regexp.compile "ab\x00c")})
445
+ end
446
+ end
447
+
448
+ def test_max_key
449
+ doc = {"a" => MaxKey.new}
450
+ assert_doc_pass(doc)
451
+ end
452
+
453
+ def test_min_key
454
+ doc = {"a" => MinKey.new}
455
+ assert_doc_pass(doc)
456
+ end
457
+
458
+ def test_invalid_object
459
+ o = Object.new
460
+ assert_raise InvalidDocument do
461
+ @encoder.serialize({:foo => o})
462
+ end
463
+
464
+ assert_raise InvalidDocument do
465
+ @encoder.serialize({:foo => Date.today})
466
+ end
467
+ end
468
+
469
+ def test_move_id
470
+ a = BSON::OrderedHash.new
471
+ a['text'] = 'abc'
472
+ a['key'] = 'abc'
473
+ a['_id'] = 1
474
+
475
+
476
+ assert_equal ")\000\000\000\020_id\000\001\000\000\000\002text" +
477
+ "\000\004\000\000\000abc\000\002key\000\004\000\000\000abc\000\000",
478
+ @encoder.serialize(a, false, true).to_s
479
+
480
+ # Java doesn't support this. Isn't actually necessary.
481
+ if !(RUBY_PLATFORM =~ /java/)
482
+ assert_equal ")\000\000\000\002text\000\004\000\000\000abc\000\002key" +
483
+ "\000\004\000\000\000abc\000\020_id\000\001\000\000\000\000",
484
+ @encoder.serialize(a, false, false).to_s
485
+ end
486
+ end
487
+
488
+ def test_move_id_with_nested_doc
489
+ b = BSON::OrderedHash.new
490
+ b['text'] = 'abc'
491
+ b['_id'] = 2
492
+ c = BSON::OrderedHash.new
493
+ c['text'] = 'abc'
494
+ c['hash'] = b
495
+ c['_id'] = 3
496
+ assert_equal ">\000\000\000\020_id\000\003\000\000\000\002text" +
497
+ "\000\004\000\000\000abc\000\003hash\000\034\000\000" +
498
+ "\000\002text\000\004\000\000\000abc\000\020_id\000\002\000\000\000\000\000",
499
+ @encoder.serialize(c, false, true).to_s
500
+
501
+ # Java doesn't support this. Isn't actually necessary.
502
+ if !(RUBY_PLATFORM =~ /java/)
503
+ assert_equal ">\000\000\000\002text\000\004\000\000\000abc\000\003hash" +
504
+ "\000\034\000\000\000\002text\000\004\000\000\000abc\000\020_id" +
505
+ "\000\002\000\000\000\000\020_id\000\003\000\000\000\000",
506
+ @encoder.serialize(c, false, false).to_s
507
+ end
508
+ end
509
+
510
+ # Mocking this class for testing
511
+ class ::HashWithIndifferentAccess < Hash; end
512
+
513
+ def test_keep_id_with_hash_with_indifferent_access
514
+ doc = HashWithIndifferentAccess.new
515
+ embedded = HashWithIndifferentAccess.new
516
+ embedded['_id'] = ObjectId.new
517
+ doc['_id'] = ObjectId.new
518
+ doc['embedded'] = [embedded]
519
+ @encoder.serialize(doc, false, true).to_a
520
+ assert doc.has_key?("_id")
521
+ assert doc['embedded'][0].has_key?("_id")
522
+
523
+ doc['_id'] = ObjectId.new
524
+ @encoder.serialize(doc, false, true).to_a
525
+ assert doc.has_key?("_id")
526
+ end
527
+
528
+ end