bson 4.5.0-java → 4.6.0-java

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,758 @@
1
+ require 'spec_helper'
2
+
3
+ describe BSON::ByteBuffer do
4
+
5
+ let(:buffer) do
6
+ described_class.new
7
+ end
8
+
9
+ shared_examples_for 'does not write' do
10
+ it 'raises ArgumentError' do
11
+ expect do
12
+ modified
13
+ end.to raise_error(ArgumentError)
14
+ end
15
+
16
+ it 'does not change write position' do
17
+ expect do
18
+ modified
19
+ end.to raise_error(ArgumentError)
20
+
21
+ expect(buffer.write_position).to eq(0)
22
+ end
23
+ end
24
+
25
+ describe '#put_byte' do
26
+
27
+ let(:modified) do
28
+ buffer.put_byte(BSON::Int32::BSON_TYPE)
29
+ end
30
+
31
+ it 'appends the byte to the byte buffer' do
32
+ expect(modified.to_s).to eq(BSON::Int32::BSON_TYPE.chr)
33
+ end
34
+
35
+ it 'increments the write position by 1' do
36
+ expect(modified.write_position).to eq(1)
37
+ end
38
+
39
+ context 'when it receives a numeric value' do
40
+ it 'raises the ArgumentError exception' do
41
+ expect{buffer.put_byte(1)}.to raise_error(ArgumentError)
42
+ end
43
+ end
44
+
45
+ context 'when it receives a nil value' do
46
+ it 'raises the ArgumentError exception' do
47
+ expect{buffer.put_byte(nil)}.to raise_error(ArgumentError)
48
+ end
49
+ end
50
+
51
+ context 'when given a string of length > 1' do
52
+
53
+ let(:modified) do
54
+ buffer.put_byte('xx')
55
+ end
56
+
57
+ it_behaves_like 'does not write'
58
+ end
59
+
60
+ context 'when given a string of length 0' do
61
+
62
+ let(:modified) do
63
+ buffer.put_byte('')
64
+ end
65
+
66
+ it_behaves_like 'does not write'
67
+ end
68
+
69
+ context 'when byte is not valid utf-8' do
70
+ let(:string) do
71
+ Utils.make_byte_string([254]).freeze
72
+ end
73
+
74
+ let(:modified) do
75
+ buffer.put_byte(string)
76
+ end
77
+
78
+ it 'writes the byte' do
79
+ expect(modified.to_s).to eq(string)
80
+ end
81
+ end
82
+ end
83
+
84
+ describe '#put_bytes' do
85
+
86
+ let(:modified) do
87
+ buffer.put_bytes(BSON::Int32::BSON_TYPE)
88
+ buffer
89
+ end
90
+
91
+ it 'increments the write position by 1' do
92
+ expect(modified.write_position).to eq(1)
93
+ end
94
+
95
+ context 'when it receives a numeric value' do
96
+ it 'raises the ArgumentError exception' do
97
+ expect{buffer.put_bytes(1)}.to raise_error(ArgumentError)
98
+ end
99
+ end
100
+
101
+ context 'when it receives a nil value' do
102
+ it 'raises the ArgumentError exception' do
103
+ expect{buffer.put_bytes(nil)}.to raise_error(ArgumentError)
104
+ end
105
+ end
106
+
107
+ context 'when given a string with null bytes' do
108
+ let(:byte_str) { [0, 239, 254, 0].map(&:chr).join }
109
+
110
+ let(:modified) do
111
+ buffer.put_bytes(byte_str)
112
+ end
113
+
114
+ before do
115
+ expect(buffer.write_position).to eq(0)
116
+ expect(byte_str.length).to eq(4)
117
+ end
118
+
119
+ it 'writes the string' do
120
+ expect(modified.write_position).to eq(4)
121
+ end
122
+ end
123
+
124
+ context 'when bytes are not valid utf-8' do
125
+ let(:string) do
126
+ Utils.make_byte_string([254, 0, 255]).freeze
127
+ end
128
+
129
+ let(:modified) do
130
+ buffer.put_bytes(string)
131
+ end
132
+
133
+ it 'writes the bytes' do
134
+ expect(modified.to_s).to eq(string)
135
+ end
136
+ end
137
+ end
138
+
139
+ shared_examples_for 'bson string writer' do
140
+
141
+ context 'given empty string' do
142
+ let(:modified) do
143
+ buffer.put_string('')
144
+ end
145
+
146
+ it 'writes length and null terminator' do
147
+ expect(modified.write_position).to eq(5)
148
+ end
149
+ end
150
+
151
+ context 'when string is not valid utf-8 in utf-8 encoding' do
152
+ let(:string) do
153
+ Utils.make_byte_string([254, 253, 255], 'utf-8')
154
+ end
155
+
156
+ before do
157
+ expect(string.encoding.name).to eq('UTF-8')
158
+ end
159
+
160
+ it 'raises EncodingError' do
161
+ # Precise exception classes and messages differ between MRI and JRuby
162
+ expect do
163
+ modified
164
+ end.to raise_error(EncodingError)
165
+ end
166
+ end
167
+
168
+ context 'when string is in binary encoding and cannot be encoded in utf-8' do
169
+ let(:string) do
170
+ Utils.make_byte_string([254, 253, 255], 'binary')
171
+ end
172
+
173
+ before do
174
+ expect(string.encoding.name).to eq('ASCII-8BIT')
175
+ end
176
+
177
+ it 'raises Encoding::UndefinedConversionError' do
178
+ expect do
179
+ modified
180
+ end.to raise_error(Encoding::UndefinedConversionError, /from ASCII-8BIT to UTF-8/)
181
+ end
182
+ end
183
+ end
184
+
185
+ describe '#put_string' do
186
+
187
+ let(:modified) do
188
+ buffer.put_string(string)
189
+ end
190
+
191
+ it_behaves_like 'bson string writer'
192
+
193
+ context 'when the buffer does not need to be expanded' do
194
+
195
+ context 'when the string is UTF-8' do
196
+
197
+ let!(:modified) do
198
+ buffer.put_string('testing')
199
+ end
200
+
201
+ it 'appends the string to the byte buffer' do
202
+ expect(modified.to_s).to eq("#{8.to_bson.to_s}testing#{BSON::NULL_BYTE}")
203
+ end
204
+
205
+ it 'increments the write position by length + 5' do
206
+ expect(modified.write_position).to eq(12)
207
+ end
208
+ end
209
+ end
210
+
211
+ context 'when the buffer needs to be expanded' do
212
+
213
+ let(:string) do
214
+ 300.times.inject(""){ |s, i| s << "#{i}" }
215
+ end
216
+
217
+ context 'when no bytes exist in the buffer' do
218
+
219
+ let!(:modified) do
220
+ buffer.put_string(string)
221
+ end
222
+
223
+ it 'appends the string to the byte buffer' do
224
+ expect(modified.to_s).to eq("#{(string.bytesize + 1).to_bson.to_s}#{string}#{BSON::NULL_BYTE}")
225
+ end
226
+
227
+ it 'increments the write position by length + 5' do
228
+ expect(modified.write_position).to eq(string.bytesize + 5)
229
+ end
230
+ end
231
+
232
+ context 'when bytes exist in the buffer' do
233
+
234
+ let!(:modified) do
235
+ buffer.put_int32(4).put_string(string)
236
+ end
237
+
238
+ it 'appends the string to the byte buffer' do
239
+ expect(modified.to_s).to eq(
240
+ "#{[ 4 ].pack(BSON::Int32::PACK)}#{(string.bytesize + 1).to_bson.to_s}#{string}#{BSON::NULL_BYTE}"
241
+ )
242
+ end
243
+
244
+ it 'increments the write position by length + 5' do
245
+ expect(modified.write_position).to eq(string.bytesize + 9)
246
+ end
247
+ end
248
+ end
249
+
250
+ context 'when string is in an encoding other than utf-8' do
251
+ let(:string) do
252
+ # "\xfe"
253
+ Utils.make_byte_string([254], 'iso-8859-1')
254
+ end
255
+
256
+ let(:expected) do
257
+ # \xc3\xbe == \u00fe
258
+ Utils.make_byte_string([3, 0, 0, 0, 0xc3, 0xbe, 0])
259
+ end
260
+
261
+ it 'is written as utf-8' do
262
+ expect(modified.to_s).to eq(expected)
263
+ end
264
+ end
265
+ end
266
+
267
+ describe '#put_cstring' do
268
+
269
+ let(:modified) do
270
+ buffer.put_cstring(string)
271
+ end
272
+
273
+ it_behaves_like 'bson string writer'
274
+
275
+ context 'when argument is a string' do
276
+ context 'when the string is valid' do
277
+
278
+ let!(:modified) do
279
+ buffer.put_cstring('testing')
280
+ end
281
+
282
+ it 'appends the string plus null byte to the byte buffer' do
283
+ expect(modified.to_s).to eq("testing#{BSON::NULL_BYTE}")
284
+ end
285
+
286
+ it 'increments the write position by the length + 1' do
287
+ expect(modified.write_position).to eq(8)
288
+ end
289
+
290
+ it 'mutates receiver' do
291
+ modified
292
+ expect(buffer.write_position).to eq(8)
293
+ end
294
+ end
295
+
296
+ context "when the string contains a null byte" do
297
+
298
+ let(:string) do
299
+ "test#{BSON::NULL_BYTE}ing"
300
+ end
301
+
302
+ it 'raises ArgumentError' do
303
+ expect {
304
+ modified
305
+ }.to raise_error(ArgumentError, /String .* contains null bytes/)
306
+ end
307
+ end
308
+
309
+ context 'when string is in an encoding other than utf-8' do
310
+ let(:string) do
311
+ # "\xfe"
312
+ Utils.make_byte_string([254], 'iso-8859-1')
313
+ end
314
+
315
+ let(:expected) do
316
+ # \xc3\xbe == \u00fe
317
+ # No length prefix
318
+ Utils.make_byte_string([0xc3, 0xbe, 0])
319
+ end
320
+
321
+ it 'is written as utf-8' do
322
+ expect(modified.to_s).to eq(expected)
323
+ end
324
+ end
325
+ end
326
+
327
+ context 'when argument is a symbol' do
328
+ let(:modified) do
329
+ buffer.put_cstring(:testing)
330
+ end
331
+
332
+ it 'writes' do
333
+ expect(modified.to_s).to eq("testing#{BSON::NULL_BYTE}")
334
+ end
335
+
336
+ it 'increments the write position by the length + 1' do
337
+ expect(modified.write_position).to eq(8)
338
+ end
339
+
340
+ it 'mutates receiver' do
341
+ modified
342
+ expect(buffer.write_position).to eq(8)
343
+ end
344
+
345
+ context 'when symbol includes a null byte' do
346
+ let(:modified) do
347
+ buffer.put_cstring(:"tes\x00ing")
348
+ end
349
+
350
+ it 'raises ArgumentError' do
351
+ expect {
352
+ modified
353
+ }.to raise_error(ArgumentError, /String .* contains null bytes/)
354
+ end
355
+
356
+ it 'does not change write position' do
357
+ begin
358
+ buffer.put_cstring(:"tes\x00ing")
359
+ rescue ArgumentError
360
+ end
361
+
362
+ expect(buffer.write_position).to eq(0)
363
+ end
364
+ end
365
+ end
366
+
367
+ context 'when argument is a Fixnum' do
368
+ let(:modified) do
369
+ buffer.put_cstring(1234)
370
+ end
371
+
372
+ it 'writes' do
373
+ expect(modified.to_s).to eq("1234#{BSON::NULL_BYTE}")
374
+ end
375
+
376
+ it 'increments the write position by the length + 1' do
377
+ expect(modified.write_position).to eq(5)
378
+ end
379
+ end
380
+
381
+ context 'when argument is of an unsupported type' do
382
+ let(:modified) do
383
+ buffer.put_cstring(1234.0)
384
+ end
385
+
386
+ it 'raises TypeError' do
387
+ expect do
388
+ modified
389
+ end.to raise_error(TypeError, /Invalid type for put_cstring/)
390
+ end
391
+
392
+ it 'does not change write position' do
393
+ begin
394
+ buffer.put_cstring(1234.0)
395
+ rescue TypeError
396
+ end
397
+
398
+ expect(buffer.write_position).to eq(0)
399
+ end
400
+ end
401
+ end
402
+
403
+ describe '#put_symbol' do
404
+ context 'normal symbol' do
405
+ let(:modified) do
406
+ buffer.put_symbol(:hello)
407
+ end
408
+
409
+ it 'writes the symbol as string' do
410
+ expect(modified.to_s).to eq("\x06\x00\x00\x00hello\x00")
411
+ end
412
+
413
+ it 'advances write position' do
414
+ # 4 byte length + 5 byte string + null byte
415
+ expect(modified.write_position).to eq(10)
416
+ end
417
+ end
418
+
419
+ context 'symbol with null byte' do
420
+ let(:modified) do
421
+ buffer.put_symbol(:"he\x00lo")
422
+ end
423
+
424
+ it 'writes the symbol as string' do
425
+ expect(modified.to_s).to eq("\x06\x00\x00\x00he\x00lo\x00")
426
+ end
427
+
428
+ it 'advances write position' do
429
+ # 4 byte length + 5 byte string + null byte
430
+ expect(modified.write_position).to eq(10)
431
+ end
432
+ end
433
+
434
+ context 'when symbol is not valid utf-8' do
435
+ let(:symbol) do
436
+ Utils.make_byte_string([254, 0, 255]).to_sym
437
+ end
438
+
439
+ let(:modified) do
440
+ buffer.put_symbol(symbol)
441
+ end
442
+
443
+ it 'raises EncodingError' do
444
+ # Precise exception classes and messages differ between MRI and JRuby
445
+ expect do
446
+ modified
447
+ end.to raise_error(EncodingError)
448
+ end
449
+ end
450
+ end
451
+
452
+ describe '#put_double' do
453
+
454
+ let(:modified) do
455
+ buffer.put_double(1.2332)
456
+ end
457
+
458
+ it 'appends the double to the buffer' do
459
+ expect(modified.to_s).to eq([ 1.2332 ].pack(Float::PACK))
460
+ end
461
+
462
+ it 'increments the write position by 8' do
463
+ expect(modified.write_position).to eq(8)
464
+ end
465
+
466
+ context 'when argument is an integer' do
467
+
468
+ let(:modified) do
469
+ buffer.put_double(3)
470
+ end
471
+
472
+ it 'writes a double' do
473
+ expect(modified.to_s).to eq([ 3 ].pack(Float::PACK))
474
+ end
475
+
476
+ it 'increments the write position by 8' do
477
+ expect(modified.write_position).to eq(8)
478
+ end
479
+ end
480
+
481
+ context 'when argument is a BigNum' do
482
+ let(:value) { 123456789012345678901234567890 }
483
+
484
+ let(:modified) do
485
+ buffer.put_double(value)
486
+ end
487
+
488
+ let(:actual) do
489
+ described_class.new(modified.to_s).get_double
490
+ end
491
+
492
+ it 'writes a double' do
493
+ expect(actual).to be_within(1).of(value)
494
+ end
495
+
496
+ it 'increments the write position by 8' do
497
+ expect(modified.write_position).to eq(8)
498
+ end
499
+ end
500
+
501
+ context 'when argument is a string' do
502
+
503
+ let(:modified) do
504
+ buffer.put_double("hello")
505
+ end
506
+
507
+ it 'raises TypeError' do
508
+ expect do
509
+ modified
510
+ end.to raise_error(TypeError, /no implicit conversion to float from string|ClassCastException:.*RubyString cannot be cast to.*RubyFloat/)
511
+ expect(buffer.write_position).to eq(0)
512
+ end
513
+ end
514
+ end
515
+
516
+ describe '#put_int32' do
517
+
518
+ context 'when the integer is 32 bit' do
519
+
520
+ context 'when the integer is positive' do
521
+
522
+ let!(:modified) do
523
+ buffer.put_int32(Integer::MAX_32BIT - 1)
524
+ end
525
+
526
+ let(:expected) do
527
+ [ Integer::MAX_32BIT - 1 ].pack(BSON::Int32::PACK)
528
+ end
529
+
530
+ it 'appends the int32 to the byte buffer' do
531
+ expect(modified.to_s).to eq(expected)
532
+ end
533
+
534
+ it 'increments the write position by 4' do
535
+ expect(modified.write_position).to eq(4)
536
+ end
537
+ end
538
+
539
+ context 'when the integer is negative' do
540
+
541
+ let!(:modified) do
542
+ buffer.put_int32(Integer::MIN_32BIT + 1)
543
+ end
544
+
545
+ let(:expected) do
546
+ [ Integer::MIN_32BIT + 1 ].pack(BSON::Int32::PACK)
547
+ end
548
+
549
+ it 'appends the int32 to the byte buffer' do
550
+ expect(modified.to_s).to eq(expected)
551
+ end
552
+
553
+ it 'increments the write position by 4' do
554
+ expect(modified.write_position).to eq(4)
555
+ end
556
+ end
557
+
558
+ context 'when the integer is not 32 bit' do
559
+
560
+ it 'raises an exception' do
561
+ expect {
562
+ buffer.put_int32(Integer::MAX_64BIT - 1)
563
+ }.to raise_error(RangeError)
564
+ end
565
+ end
566
+ end
567
+
568
+ context 'when argument is a float' do
569
+
570
+ let(:modified) do
571
+ buffer.put_int32(4.934)
572
+ end
573
+
574
+ let(:expected) do
575
+ [ 4 ].pack(BSON::Int32::PACK)
576
+ end
577
+
578
+ it 'appends the int32 to the byte buffer' do
579
+ expect(modified.to_s).to eq(expected)
580
+ end
581
+
582
+ it 'increments the write position by 4' do
583
+ expect(modified.write_position).to eq(4)
584
+ end
585
+ end
586
+ end
587
+
588
+ describe '#put_int64' do
589
+
590
+ context 'when the integer is 64 bit' do
591
+
592
+ context 'when the integer is positive' do
593
+
594
+ let!(:modified) do
595
+ buffer.put_int64(Integer::MAX_64BIT - 1)
596
+ end
597
+
598
+ let(:expected) do
599
+ [ Integer::MAX_64BIT - 1 ].pack(BSON::Int64::PACK)
600
+ end
601
+
602
+ it 'appends the int64 to the byte buffer' do
603
+ expect(modified.to_s).to eq(expected)
604
+ end
605
+
606
+ it 'increments the write position by 8' do
607
+ expect(modified.write_position).to eq(8)
608
+ end
609
+ end
610
+
611
+ context 'when the integer is negative' do
612
+
613
+ let!(:modified) do
614
+ buffer.put_int64(Integer::MIN_64BIT + 1)
615
+ end
616
+
617
+ let(:expected) do
618
+ [ Integer::MIN_64BIT + 1 ].pack(BSON::Int64::PACK)
619
+ end
620
+
621
+ it 'appends the int64 to the byte buffer' do
622
+ expect(modified.to_s).to eq(expected)
623
+ end
624
+
625
+ it 'increments the write position by 8' do
626
+ expect(modified.write_position).to eq(8)
627
+ end
628
+ end
629
+
630
+ context 'when the integer is larger than 64 bit' do
631
+
632
+ it 'raises an exception' do
633
+ expect {
634
+ buffer.put_int64(Integer::MAX_64BIT + 1)
635
+ }.to raise_error(RangeError)
636
+ end
637
+ end
638
+ end
639
+
640
+ context 'when integer fits in 32 bits' do
641
+ let(:modified) do
642
+ buffer.put_int64(1)
643
+ end
644
+
645
+ it 'increments the write position by 8' do
646
+ expect(modified.write_position).to eq(8)
647
+ end
648
+ end
649
+
650
+ context 'when argument is a float' do
651
+
652
+ let(:modified) do
653
+ buffer.put_int64(4.934)
654
+ end
655
+
656
+ let(:expected) do
657
+ [ 4 ].pack(BSON::Int64::PACK)
658
+ end
659
+
660
+ it 'appends the int64 to the byte buffer' do
661
+ expect(modified.to_s).to eq(expected)
662
+ end
663
+
664
+ it 'increments the write position by 8' do
665
+ expect(modified.write_position).to eq(8)
666
+ end
667
+ end
668
+ end
669
+
670
+ describe '#replace_int32' do
671
+
672
+ let(:exp_0) do
673
+ [ 0 ].pack(BSON::Int32::PACK)
674
+ end
675
+
676
+ let(:exp_first) do
677
+ [ 5 ].pack(BSON::Int32::PACK)
678
+ end
679
+
680
+ let(:exp_second) do
681
+ [ 4 ].pack(BSON::Int32::PACK)
682
+ end
683
+
684
+ let(:exp_42) do
685
+ [ 42 ].pack(BSON::Int32::PACK)
686
+ end
687
+
688
+ let(:modified) do
689
+ buffer.replace_int32(0, 5)
690
+ end
691
+
692
+ context 'when there is sufficient data in buffer' do
693
+
694
+ before do
695
+ buffer.put_int32(0).put_int32(4)
696
+ end
697
+
698
+ it 'replaces the int32 at the location' do
699
+ expect(modified.to_s).to eq("#{exp_first}#{exp_second}")
700
+ end
701
+
702
+ context 'when the position is negative' do
703
+
704
+ let(:modified) do
705
+ buffer.replace_int32(-1, 5)
706
+ end
707
+
708
+ it 'raises ArgumentError' do
709
+ expect do
710
+ modified
711
+ end.to raise_error(ArgumentError, /Position.*cannot be negative/)
712
+ end
713
+ end
714
+
715
+ context 'when the position is 4 bytes prior to write position' do
716
+
717
+ let(:modified) do
718
+ buffer.replace_int32(4, 42)
719
+ end
720
+
721
+ it 'replaces the int32 at the location' do
722
+ expect(modified.to_s).to eq("#{exp_0}#{exp_42}")
723
+ end
724
+ end
725
+
726
+ context 'when the position exceeds allowed range' do
727
+
728
+ let(:modified) do
729
+ # Buffer has 8 bytes but we can only write up to position 4
730
+ buffer.replace_int32(5, 42)
731
+ end
732
+
733
+ it 'raises ArgumentError' do
734
+ expect do
735
+ modified
736
+ end.to raise_error(ArgumentError, /Position.*is out of bounds/)
737
+ end
738
+ end
739
+ end
740
+
741
+ context 'when there is insufficient data in buffer' do
742
+
743
+ before do
744
+ buffer.put_bytes("aa")
745
+ end
746
+
747
+ let(:modified) do
748
+ buffer.replace_int32(1, 42)
749
+ end
750
+
751
+ it 'raises ArgumentError' do
752
+ expect do
753
+ modified
754
+ end.to raise_error(ArgumentError, /Buffer does not have enough data/)
755
+ end
756
+ end
757
+ end
758
+ end