jmongo 1.0.3 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (96) hide show
  1. data/Gemfile +8 -0
  2. data/Gemfile.lock +43 -0
  3. data/Rakefile +72 -0
  4. data/jmongo.gemspec +84 -6
  5. data/lib/jmongo.rb +6 -14
  6. data/lib/jmongo/collection.rb +196 -114
  7. data/lib/jmongo/connection.rb +39 -13
  8. data/lib/jmongo/cursor.rb +161 -63
  9. data/lib/jmongo/db.rb +119 -30
  10. data/lib/jmongo/exceptions.rb +39 -0
  11. data/lib/jmongo/mongo-2.6.5.gb1.jar +0 -0
  12. data/lib/jmongo/mongo/bson.rb +130 -0
  13. data/lib/jmongo/mongo/collection.rb +185 -0
  14. data/lib/jmongo/mongo/connection.rb +45 -0
  15. data/lib/jmongo/mongo/db.rb +31 -0
  16. data/lib/jmongo/mongo/jmongo.rb +44 -0
  17. data/lib/jmongo/mongo/mongo.rb +98 -0
  18. data/lib/jmongo/mongo/ruby_ext.rb +38 -0
  19. data/lib/jmongo/mongo/utils.rb +136 -0
  20. data/lib/jmongo/version.rb +1 -1
  21. data/test-results.txt +98 -0
  22. data/test/auxillary/1.4_features.rb +166 -0
  23. data/test/auxillary/authentication_test.rb +68 -0
  24. data/test/auxillary/autoreconnect_test.rb +41 -0
  25. data/test/auxillary/fork_test.rb +30 -0
  26. data/test/auxillary/repl_set_auth_test.rb +58 -0
  27. data/test/auxillary/slave_connection_test.rb +36 -0
  28. data/test/auxillary/threaded_authentication_test.rb +101 -0
  29. data/test/bson/binary_test.rb +15 -0
  30. data/test/bson/bson_test.rb +657 -0
  31. data/test/bson/byte_buffer_test.rb +208 -0
  32. data/test/bson/hash_with_indifferent_access_test.rb +38 -0
  33. data/test/bson/json_test.rb +17 -0
  34. data/test/bson/object_id_test.rb +138 -0
  35. data/test/bson/ordered_hash_test.rb +245 -0
  36. data/test/bson/test_helper.rb +46 -0
  37. data/test/bson/timestamp_test.rb +46 -0
  38. data/test/collection_test.rb +933 -0
  39. data/test/connection_test.rb +325 -0
  40. data/test/conversions_test.rb +121 -0
  41. data/test/cursor_fail_test.rb +75 -0
  42. data/test/cursor_message_test.rb +43 -0
  43. data/test/cursor_test.rb +547 -0
  44. data/test/data/empty_data +0 -0
  45. data/test/data/sample_data +0 -0
  46. data/test/data/sample_file.pdf +0 -0
  47. data/test/data/small_data.txt +1 -0
  48. data/test/db_api_test.rb +739 -0
  49. data/test/db_connection_test.rb +15 -0
  50. data/test/db_test.rb +325 -0
  51. data/test/grid_file_system_test.rb +260 -0
  52. data/test/grid_io_test.rb +210 -0
  53. data/test/grid_test.rb +259 -0
  54. data/test/load/thin/config.ru +6 -0
  55. data/test/load/thin/config.yml.template +6 -0
  56. data/test/load/thin/load.rb +24 -0
  57. data/test/load/unicorn/config.ru +6 -0
  58. data/test/load/unicorn/load.rb +23 -0
  59. data/test/load/unicorn/unicorn.rb.template +29 -0
  60. data/test/replica_sets/connect_test.rb +111 -0
  61. data/test/replica_sets/connection_string_test.rb +29 -0
  62. data/test/replica_sets/count_test.rb +36 -0
  63. data/test/replica_sets/insert_test.rb +54 -0
  64. data/test/replica_sets/pooled_insert_test.rb +58 -0
  65. data/test/replica_sets/query_secondaries.rb +109 -0
  66. data/test/replica_sets/query_test.rb +52 -0
  67. data/test/replica_sets/read_preference_test.rb +43 -0
  68. data/test/replica_sets/refresh_test.rb +123 -0
  69. data/test/replica_sets/replication_ack_test.rb +71 -0
  70. data/test/replica_sets/rs_test_helper.rb +27 -0
  71. data/test/safe_test.rb +68 -0
  72. data/test/support/hash_with_indifferent_access.rb +186 -0
  73. data/test/support/keys.rb +45 -0
  74. data/test/support_test.rb +19 -0
  75. data/test/test_helper.rb +111 -0
  76. data/test/threading/threading_with_large_pool_test.rb +90 -0
  77. data/test/threading_test.rb +88 -0
  78. data/test/tools/auth_repl_set_manager.rb +14 -0
  79. data/test/tools/keyfile.txt +1 -0
  80. data/test/tools/repl_set_manager.rb +377 -0
  81. data/test/unit/collection_test.rb +128 -0
  82. data/test/unit/connection_test.rb +85 -0
  83. data/test/unit/cursor_test.rb +127 -0
  84. data/test/unit/db_test.rb +96 -0
  85. data/test/unit/grid_test.rb +51 -0
  86. data/test/unit/node_test.rb +73 -0
  87. data/test/unit/pool_manager_test.rb +47 -0
  88. data/test/unit/pool_test.rb +9 -0
  89. data/test/unit/read_test.rb +101 -0
  90. data/test/unit/safe_test.rb +125 -0
  91. data/test/uri_test.rb +92 -0
  92. metadata +170 -99
  93. data/lib/jmongo/ajrb.rb +0 -189
  94. data/lib/jmongo/jmongo_jext.rb +0 -302
  95. data/lib/jmongo/mongo-2.6.3.jar +0 -0
  96. data/lib/jmongo/utils.rb +0 -61
@@ -0,0 +1,68 @@
1
+ $:.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
+ require 'mongo'
3
+ require 'test/unit'
4
+ require './test/test_helper'
5
+
6
+ # NOTE: This test requires bouncing the server.
7
+ # It also requires that a user exists on the admin database.
8
+ class AuthenticationTest < Test::Unit::TestCase
9
+ include Mongo
10
+
11
+ def setup
12
+ @conn = Mongo::Connection.new
13
+ @db1 = @conn.db('mongo-ruby-test-auth1')
14
+ @db2 = @conn.db('mongo-ruby-test-auth2')
15
+ @admin = @conn.db('admin')
16
+ end
17
+
18
+ def teardown
19
+ @db1.authenticate('user1', 'secret')
20
+ @db2.authenticate('user2', 'secret')
21
+ @conn.drop_database('mongo-ruby-test-auth1')
22
+ @conn.drop_database('mongo-ruby-test-auth2')
23
+ end
24
+
25
+ def test_authenticate
26
+ @admin.authenticate('bob', 'secret')
27
+ @db1.add_user('user1', 'secret')
28
+ @db2.add_user('user2', 'secret')
29
+ @admin.logout
30
+
31
+ assert_raise Mongo::OperationFailure do
32
+ @db1['stuff'].insert({:a => 2}, :safe => true)
33
+ end
34
+
35
+ assert_raise Mongo::OperationFailure do
36
+ @db2['stuff'].insert({:a => 2}, :safe => true)
37
+ end
38
+
39
+ @db1.authenticate('user1', 'secret')
40
+ @db2.authenticate('user2', 'secret')
41
+
42
+ assert @db1['stuff'].insert({:a => 2}, :safe => true)
43
+ assert @db2['stuff'].insert({:a => 2}, :safe => true)
44
+
45
+ puts "Please bounce the server."
46
+ gets
47
+
48
+ # Here we reconnect.
49
+ begin
50
+ @db1['stuff'].find.to_a
51
+ rescue Mongo::ConnectionFailure
52
+ end
53
+
54
+ assert @db1['stuff'].insert({:a => 2}, :safe => true)
55
+ assert @db2['stuff'].insert({:a => 2}, :safe => true)
56
+
57
+ @db1.logout
58
+ assert_raise Mongo::OperationFailure do
59
+ @db1['stuff'].insert({:a => 2}, :safe => true)
60
+ end
61
+
62
+ @db2.logout
63
+ assert_raise Mongo::OperationFailure do
64
+ assert @db2['stuff'].insert({:a => 2}, :safe => true)
65
+ end
66
+ end
67
+
68
+ end
@@ -0,0 +1,41 @@
1
+ $:.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
+ require 'mongo'
3
+ require 'test/unit'
4
+ require './test/test_helper'
5
+
6
+ # NOTE: This test requires bouncing the server
7
+ class AutoreconnectTest < Test::Unit::TestCase
8
+ include Mongo
9
+
10
+ def setup
11
+ @conn = Mongo::Connection.new
12
+ @db = @conn.db('mongo-ruby-test')
13
+ @db.drop_collection("test-connect")
14
+ @coll = @db.collection("test-connect")
15
+ end
16
+
17
+ def test_query
18
+ @coll.save({:a => 20})
19
+ @coll.save({:a => 30})
20
+ @coll.save({:a => 40})
21
+ results = []
22
+ @coll.find.each {|r| results << r}
23
+ [20, 30, 40].each do |a|
24
+ assert results.any? {|r| r['a'] == a}, "Could not find record for a => #{a}"
25
+ end
26
+
27
+ puts "Please disconnect and then reconnect the current master."
28
+ gets
29
+
30
+ begin
31
+ @coll.find.to_a
32
+ rescue Mongo::ConnectionFailure
33
+ end
34
+
35
+ results = []
36
+ @coll.find.each {|r| results << r}
37
+ [20, 30, 40].each do |a|
38
+ assert results.any? {|r| r['a'] == a}, "Could not find record for a => #{a}"
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,30 @@
1
+ $:.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
+ require 'mongo'
3
+ require 'test/unit'
4
+ require './test/test_helper'
5
+
6
+ class ForkTest < Test::Unit::TestCase
7
+ include Mongo
8
+
9
+ def setup
10
+ @conn = standard_connection
11
+ end
12
+
13
+ def test_fork
14
+ # Now insert some data
15
+ 10.times do |n|
16
+ @conn[MONGO_TEST_DB]['nums'].insert({:a => n})
17
+ end
18
+
19
+ # Now fork. You'll almost always see an exception here.
20
+ if !Kernel.fork
21
+ 10.times do
22
+ assert @conn[MONGO_TEST_DB]['nums'].find_one
23
+ end
24
+ else
25
+ 10.times do
26
+ assert @conn[MONGO_TEST_DB]['nums'].find_one
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,58 @@
1
+ $:.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
+ require './test/test_helper'
3
+ require './test/tools/auth_repl_set_manager'
4
+
5
+ class AuthTest < Test::Unit::TestCase
6
+ include Mongo
7
+
8
+ def setup
9
+ @manager = AuthReplSetManager.new(:start_port => 40000)
10
+ @manager.start_set
11
+ end
12
+
13
+ def teardown
14
+ @manager.cleanup_set
15
+ end
16
+
17
+ def test_repl_set_auth
18
+ @conn = ReplSetConnection.new([@manager.host, @manager.ports[0]], [@manager.host, @manager.ports[1]],
19
+ [@manager.host, @manager.ports[2]], :name => @manager.name)
20
+
21
+ # Add an admin user
22
+ @conn['admin'].add_user("me", "secret")
23
+
24
+ # Ensure that insert fails
25
+ assert_raise_error Mongo::OperationFailure, "unauthorized" do
26
+ @conn['foo']['stuff'].insert({:a => 2}, :safe => {:w => 3})
27
+ end
28
+
29
+ # Then authenticate
30
+ assert @conn['admin'].authenticate("me", "secret")
31
+
32
+ # Insert should succeed now
33
+ assert @conn['foo']['stuff'].insert({:a => 2}, :safe => {:w => 3})
34
+
35
+ # So should a query
36
+ assert @conn['foo']['stuff'].find_one
37
+
38
+ # But not when we logout
39
+ @conn['admin'].logout
40
+
41
+ assert_raise_error Mongo::OperationFailure, "unauthorized" do
42
+ @conn['foo']['stuff'].find_one
43
+ end
44
+
45
+ # Same should apply to a random secondary
46
+ @slave1 = Connection.new(@conn.secondary_pools[0].host,
47
+ @conn.secondary_pools[0].port, :slave_ok => true)
48
+
49
+ # Find should fail
50
+ assert_raise_error Mongo::OperationFailure, "unauthorized" do
51
+ @slave1['foo']['stuff'].find_one
52
+ end
53
+
54
+ # But not when authenticated
55
+ @slave1['admin'].authenticate("me", "secret")
56
+ assert @slave1['foo']['stuff'].find_one
57
+ end
58
+ end
@@ -0,0 +1,36 @@
1
+ require './test/test_helper'
2
+
3
+ # NOTE: these tests are run only if we can connect to a single MongoDB in slave mode.
4
+ class SlaveConnectionTest < Test::Unit::TestCase
5
+ include Mongo
6
+
7
+ def self.connect_to_slave
8
+ @@host = ENV['MONGO_RUBY_DRIVER_HOST'] || 'localhost'
9
+ @@port = ENV['MONGO_RUBY_DRIVER_PORT'] || Connection::DEFAULT_PORT
10
+ conn = Connection.new(@@host, @@port, :slave_ok => true)
11
+ response = conn['admin'].command(:ismaster => 1)
12
+ Mongo::Support.ok?(response) && response['ismaster'] != 1
13
+ end
14
+
15
+ if self.connect_to_slave
16
+ puts "Connected to slave; running slave tests."
17
+
18
+ def test_connect_to_slave
19
+ assert_raise Mongo::ConnectionFailure do
20
+ @db = Connection.new(@@host, @@port, :slave_ok => false).db('ruby-mongo-demo')
21
+ end
22
+ end
23
+
24
+ def test_slave_ok_sent_to_queries
25
+ @con = Connection.new(@@host, @@port, :slave_ok => true)
26
+ assert_equal true, @con.slave_ok?
27
+ end
28
+ else
29
+ puts "Not connected to slave; skipping slave connection tests."
30
+
31
+ def test_slave_ok_false_on_queries
32
+ @conn = Connection.new(@@host, @@port)
33
+ assert !@conn.slave_ok?
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,101 @@
1
+ $:.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
+ require 'mongo'
3
+ require 'thread'
4
+ require 'test/unit'
5
+ require './test/test_helper'
6
+
7
+ # NOTE: This test requires bouncing the server.
8
+ # It also requires that a user exists on the admin database.
9
+ class AuthenticationTest < Test::Unit::TestCase
10
+ include Mongo
11
+
12
+ def setup
13
+ @conn = standard_connection(:pool_size => 10)
14
+ @db1 = @conn.db('mongo-ruby-test-auth1')
15
+ @db2 = @conn.db('mongo-ruby-test-auth2')
16
+ @admin = @conn.db('admin')
17
+ end
18
+
19
+ def teardown
20
+ @db1.authenticate('user1', 'secret')
21
+ @db2.authenticate('user2', 'secret')
22
+ @conn.drop_database('mongo-ruby-test-auth1')
23
+ @conn.drop_database('mongo-ruby-test-auth2')
24
+ end
25
+
26
+ def threaded_exec
27
+ threads = []
28
+
29
+ 100.times do
30
+ threads << Thread.new do
31
+ yield
32
+ end
33
+ end
34
+
35
+ 100.times do |n|
36
+ threads[n].join
37
+ end
38
+ end
39
+
40
+ def test_authenticate
41
+ @admin.authenticate('bob', 'secret')
42
+ @db1.add_user('user1', 'secret')
43
+ @db2.add_user('user2', 'secret')
44
+ @admin.logout
45
+
46
+ threaded_exec do
47
+ assert_raise Mongo::OperationFailure do
48
+ @db1['stuff'].insert({:a => 2}, :safe => true)
49
+ end
50
+ end
51
+
52
+ threaded_exec do
53
+ assert_raise Mongo::OperationFailure do
54
+ @db2['stuff'].insert({:a => 2}, :safe => true)
55
+ end
56
+ end
57
+
58
+ @db1.authenticate('user1', 'secret')
59
+ @db2.authenticate('user2', 'secret')
60
+
61
+ threaded_exec do
62
+ assert @db1['stuff'].insert({:a => 2}, :safe => true)
63
+ end
64
+
65
+ threaded_exec do
66
+ assert @db2['stuff'].insert({:a => 2}, :safe => true)
67
+ end
68
+
69
+ puts "Please bounce the server."
70
+ gets
71
+
72
+ # Here we reconnect.
73
+ begin
74
+ @db1['stuff'].find.to_a
75
+ rescue Mongo::ConnectionFailure
76
+ end
77
+
78
+ threaded_exec do
79
+ assert @db1['stuff'].insert({:a => 2}, :safe => true)
80
+ end
81
+
82
+ threaded_exec do
83
+ assert @db2['stuff'].insert({:a => 2}, :safe => true)
84
+ end
85
+
86
+ @db1.logout
87
+ threaded_exec do
88
+ assert_raise Mongo::OperationFailure do
89
+ @db1['stuff'].insert({:a => 2}, :safe => true)
90
+ end
91
+ end
92
+
93
+ @db2.logout
94
+ threaded_exec do
95
+ assert_raise Mongo::OperationFailure do
96
+ assert @db2['stuff'].insert({:a => 2}, :safe => true)
97
+ end
98
+ end
99
+ end
100
+
101
+ end
@@ -0,0 +1,15 @@
1
+ # encoding:utf-8
2
+ require './test/bson/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,657 @@
1
+ # encoding:utf-8
2
+ require './test/bson/test_helper'
3
+ require 'set'
4
+
5
+ if RUBY_VERSION < '1.9'
6
+ require 'complex'
7
+ require 'rational'
8
+ end
9
+ require 'bigdecimal'
10
+
11
+ begin
12
+ require 'tzinfo'
13
+ require 'active_support/core_ext'
14
+ Time.zone = "Pacific Time (US & Canada)"
15
+ Zone = Time.zone.now
16
+ rescue LoadError
17
+ warn 'Mocking time with zone'
18
+ module ActiveSupport
19
+ class TimeWithZone
20
+ def initialize(utc_time, zone)
21
+ end
22
+ end
23
+ end
24
+ Zone = ActiveSupport::TimeWithZone.new(Time.now.utc, 'EST')
25
+ end
26
+
27
+ class BSONTest < Test::Unit::TestCase
28
+
29
+ include BSON
30
+
31
+ def setup
32
+ @encoder = BSON::BSON_CODER
33
+ end
34
+
35
+ def assert_doc_pass(doc, options={})
36
+ bson = @encoder.serialize(doc)
37
+ if options[:debug]
38
+ puts "DEBUGGING DOC:"
39
+ p bson.to_a
40
+ puts "DESERIALIZES TO:"
41
+ end
42
+ assert_equal @encoder.serialize(doc).to_a, bson.to_a
43
+ assert_equal doc, @encoder.deserialize(bson)
44
+ end
45
+
46
+ def test_require_hash
47
+ assert_raise_error InvalidDocument, "takes a Hash" do
48
+ BSON.serialize('foo')
49
+ end
50
+
51
+ assert_raise_error InvalidDocument, "takes a Hash" do
52
+ BSON.serialize(Object.new)
53
+ end
54
+
55
+ assert_raise_error InvalidDocument, "takes a Hash" do
56
+ BSON.serialize(Set.new)
57
+ end
58
+ end
59
+
60
+ def test_string
61
+ doc = {'doc' => 'hello, world'}
62
+ assert_doc_pass(doc)
63
+ end
64
+
65
+ def test_valid_utf8_string
66
+ doc = {'doc' => 'aé'}
67
+ assert_doc_pass(doc)
68
+ end
69
+
70
+ def test_valid_utf8_key
71
+ doc = {'aé' => 'hello'}
72
+ assert_doc_pass(doc)
73
+ end
74
+
75
+ def test_limit_max_bson_size
76
+ doc = {'name' => 'a' * BSON_CODER.max_bson_size}
77
+ assert_raise InvalidDocument do
78
+ assert @encoder.serialize(doc)
79
+ end
80
+ end
81
+
82
+ def test_max_bson_size
83
+ assert BSON_CODER.max_bson_size >= BSON::DEFAULT_MAX_BSON_SIZE
84
+ end
85
+
86
+ def test_update_max_bson_size
87
+ require 'ostruct'
88
+ mock_conn = OpenStruct.new
89
+ size = 7 * 1024 * 1024
90
+ mock_conn.max_bson_size = size
91
+ assert_equal size, BSON_CODER.update_max_bson_size(mock_conn)
92
+ assert_equal size, BSON_CODER.max_bson_size
93
+ end
94
+
95
+ def test_round_trip
96
+ doc = {'doc' => 123}
97
+ @encoder.deserialize(@encoder.serialize(doc))
98
+ end
99
+
100
+ # In 1.8 we test that other string encodings raise an exception.
101
+ # In 1.9 we test that they get auto-converted.
102
+ if RUBY_VERSION < '1.9'
103
+ if ! RUBY_PLATFORM =~ /java/
104
+ require 'iconv'
105
+ def test_non_utf8_string
106
+ string = Iconv.conv('iso-8859-1', 'utf-8', 'aé')
107
+ doc = {'doc' => string}
108
+ assert_doc_pass(doc)
109
+ assert_raise InvalidStringEncoding do
110
+ @encoder.serialize(doc)
111
+ end
112
+ end
113
+
114
+ def test_non_utf8_key
115
+ key = Iconv.conv('iso-8859-1', 'utf-8', 'aé')
116
+ doc = {key => 'hello'}
117
+ assert_raise InvalidStringEncoding do
118
+ @encoder.serialize(doc)
119
+ end
120
+ end
121
+ end
122
+ else
123
+ def test_non_utf8_string
124
+ assert_raise BSON::InvalidStringEncoding do
125
+ BSON::BSON_CODER.serialize({'str' => 'aé'.encode('iso-8859-1')})
126
+ end
127
+ end
128
+
129
+ def test_invalid_utf8_string
130
+ str = "123\xD9"
131
+ assert !str.valid_encoding?
132
+ assert_raise BSON::InvalidStringEncoding do
133
+ BSON::BSON_CODER.serialize({'str' => str})
134
+ end
135
+ end
136
+
137
+ def test_non_utf8_key
138
+ assert_raise BSON::InvalidStringEncoding do
139
+ BSON::BSON_CODER.serialize({'aé'.encode('iso-8859-1') => 'hello'})
140
+ end
141
+ end
142
+
143
+ # Based on a test from sqlite3-ruby
144
+ def test_default_internal_is_honored
145
+ before_enc = Encoding.default_internal
146
+
147
+ str = "壁に耳あり、障子に目あり"
148
+ bson = BSON::BSON_CODER.serialize("x" => str)
149
+
150
+ silently { Encoding.default_internal = 'EUC-JP' }
151
+ out = BSON::BSON_CODER.deserialize(bson)["x"]
152
+
153
+ assert_equal Encoding.default_internal, out.encoding
154
+ assert_equal str.encode('EUC-JP'), out
155
+ assert_equal str, out.encode(str.encoding)
156
+ ensure
157
+ silently { Encoding.default_internal = before_enc }
158
+ end
159
+ end
160
+
161
+ def test_code
162
+ doc = {'$where' => Code.new('this.a.b < this.b')}
163
+ assert_doc_pass(doc)
164
+ end
165
+
166
+ def test_code_with_symbol
167
+ assert_raise_error ArgumentError, "BSON::Code must be in the form of a String" do
168
+ Code.new(:fubar)
169
+ end
170
+ end
171
+
172
+ def test_code_with_scope
173
+ doc = {'$where' => Code.new('this.a.b < this.b', {'foo' => 1})}
174
+ assert_doc_pass(doc)
175
+ end
176
+
177
+ def test_double
178
+ doc = {'doc' => 41.25}
179
+ assert_doc_pass(doc)
180
+ end
181
+
182
+ def test_int
183
+ doc = {'doc' => 42}
184
+ assert_doc_pass(doc)
185
+
186
+ doc = {"doc" => -5600}
187
+ assert_doc_pass(doc)
188
+
189
+ doc = {"doc" => 2147483647}
190
+ assert_doc_pass(doc)
191
+
192
+ doc = {"doc" => -2147483648}
193
+ assert_doc_pass(doc)
194
+ end
195
+
196
+ def test_ordered_hash
197
+ doc = BSON::OrderedHash.new
198
+ doc["b"] = 1
199
+ doc["a"] = 2
200
+ doc["c"] = 3
201
+ doc["d"] = 4
202
+ assert_doc_pass(doc)
203
+ end
204
+
205
+ def test_object
206
+ doc = {'doc' => {'age' => 42, 'name' => 'Spongebob', 'shoe_size' => 9.5}}
207
+ assert_doc_pass(doc)
208
+ end
209
+
210
+ def test_embedded_document_with_nil
211
+ doc = {'doc' => {'age' => 42, 'name' => nil, 'shoe_size' => 9.5}}
212
+ assert_doc_pass(doc)
213
+ end
214
+
215
+ def test_embedded_document_with_date
216
+ doc = {'doc' => {'age' => 42, 'date' => Time.now.utc, 'shoe_size' => 9.5}}
217
+ bson = @encoder.serialize(doc)
218
+ doc2 = @encoder.deserialize(bson)
219
+ assert doc['doc']
220
+ assert_equal 42, doc['doc']['age']
221
+ assert_equal 9.5, doc['doc']['shoe_size']
222
+ assert_in_delta Time.now, doc['doc']['date'], 1
223
+ end
224
+
225
+ def test_oid
226
+ doc = {'doc' => ObjectId.new}
227
+ assert_doc_pass(doc)
228
+ end
229
+
230
+ def test_array
231
+ doc = {'doc' => [1, 2, 'a', 'b']}
232
+ assert_doc_pass(doc)
233
+ end
234
+
235
+ def test_array_keys
236
+ doc = {'doc' => [1, 2, 'a', 'b']}
237
+ bson = @encoder.serialize(doc).to_a
238
+ assert_equal 48, bson[14]
239
+ assert_equal 49, bson[21]
240
+ assert_equal 50, bson[28]
241
+ assert_equal 51, bson[37]
242
+ end
243
+
244
+ def test_regex
245
+ doc = {'doc' => /foobar/i}
246
+ assert_doc_pass(doc)
247
+ end
248
+
249
+ def test_regex_multiline
250
+ doc = {'doc' => /foobar/m}
251
+ assert_doc_pass(doc)
252
+ end
253
+
254
+ def test_boolean
255
+ doc = {'doc' => true}
256
+ assert_doc_pass(doc)
257
+ end
258
+
259
+ def test_date
260
+ doc = {'date' => Time.now}
261
+ bson = @encoder.serialize(doc)
262
+ doc2 = @encoder.deserialize(bson)
263
+ # Mongo only stores up to the millisecond
264
+ assert_in_delta doc['date'], doc2['date'], 0.001
265
+ end
266
+
267
+ def test_date_in_array
268
+ doc = {'date' => [Time.now.utc]}
269
+ bson = @encoder.serialize(doc)
270
+ doc2 = @encoder.deserialize(bson)
271
+ end
272
+
273
+ def test_date_returns_as_utc
274
+ doc = {'date' => Time.now.utc}
275
+ bson = @encoder.serialize(doc)
276
+ doc2 = @encoder.deserialize(bson)
277
+ assert doc2['date'].utc?
278
+ end
279
+
280
+ def test_date_before_epoch
281
+ begin
282
+ doc = {'date' => Time.utc(1600)}
283
+ bson = @encoder.serialize(doc)
284
+ doc2 = @encoder.deserialize(bson)
285
+ # Mongo only stores up to the millisecond
286
+ assert_in_delta doc['date'], doc2['date'], 2
287
+ rescue ArgumentError
288
+ # some versions of Ruby won't let you create pre-epoch Time instances
289
+ #
290
+ # TODO figure out how that will work if somebady has saved data
291
+ # w/ early dates already and is just querying for it.
292
+ end
293
+ end
294
+
295
+ def test_exeption_on_using_unsupported_date_class
296
+ [DateTime.now, Date.today, Zone].each do |invalid_date|
297
+ doc = {:date => invalid_date}
298
+ begin
299
+ bson = BSON::BSON_CODER.serialize(doc)
300
+ rescue => e
301
+ ensure
302
+ if !invalid_date.is_a? Time
303
+ assert_equal InvalidDocument, e.class
304
+ assert_match(/UTC Time/, e.message)
305
+ end
306
+ end
307
+ end
308
+ end
309
+
310
+ def test_dbref
311
+ oid = ObjectId.new
312
+ doc = {}
313
+ doc['dbref'] = DBRef.new('namespace', oid)
314
+ bson = @encoder.serialize(doc)
315
+ doc2 = @encoder.deserialize(bson)
316
+
317
+ # Java doesn't deserialize to DBRefs
318
+ if RUBY_PLATFORM =~ /java/
319
+ assert_equal 'namespace', doc2['dbref']['$ns']
320
+ assert_equal oid, doc2['dbref']['$id']
321
+ else
322
+ assert_equal 'namespace', doc2['dbref'].namespace
323
+ assert_equal oid, doc2['dbref'].object_id
324
+ end
325
+ end
326
+
327
+ def test_symbol
328
+ doc = {'sym' => :foo}
329
+ bson = @encoder.serialize(doc)
330
+ doc2 = @encoder.deserialize(bson)
331
+ assert_equal :foo, doc2['sym']
332
+ end
333
+
334
+ def test_binary
335
+ bin = Binary.new
336
+ 'binstring'.each_byte { |b| bin.put(b) }
337
+
338
+ doc = {'bin' => bin}
339
+ bson = @encoder.serialize(doc)
340
+ doc2 = @encoder.deserialize(bson)
341
+ bin2 = doc2['bin']
342
+ assert_kind_of Binary, bin2
343
+ assert_equal 'binstring', bin2.to_s
344
+ assert_equal Binary::SUBTYPE_SIMPLE, bin2.subtype
345
+ end
346
+
347
+ def test_binary_with_deprecated_subtype
348
+ bin = Binary.new
349
+ 'binstring'.each_byte { |b| bin.put(b) }
350
+ bin.subtype = Binary::SUBTYPE_BYTES
351
+
352
+ doc = {'bin' => bin}
353
+ bson = @encoder.serialize(doc)
354
+ doc2 = @encoder.deserialize(bson)
355
+ bin2 = doc2['bin']
356
+ assert_kind_of Binary, bin2
357
+ assert_equal 'binstring', bin2.to_s
358
+ assert_equal Binary::SUBTYPE_BYTES, bin2.subtype
359
+ end
360
+
361
+ def test_binary_with_string
362
+ b = Binary.new('somebinarystring')
363
+ doc = {'bin' => b}
364
+ bson = @encoder.serialize(doc)
365
+ doc2 = @encoder.deserialize(bson)
366
+ bin2 = doc2['bin']
367
+ assert_kind_of Binary, bin2
368
+ assert_equal 'somebinarystring', bin2.to_s
369
+ assert_equal Binary::SUBTYPE_SIMPLE, bin2.subtype
370
+ end
371
+
372
+ def test_binary_type
373
+ bin = Binary.new([1, 2, 3, 4, 5], Binary::SUBTYPE_USER_DEFINED)
374
+
375
+ doc = {'bin' => bin}
376
+ bson = @encoder.serialize(doc)
377
+ doc2 = @encoder.deserialize(bson)
378
+ bin2 = doc2['bin']
379
+ assert_kind_of Binary, bin2
380
+ assert_equal [1, 2, 3, 4, 5], bin2.to_a
381
+ assert_equal Binary::SUBTYPE_USER_DEFINED, bin2.subtype
382
+ end
383
+
384
+ # Java doesn't support binary subtype 0 yet
385
+ if !(RUBY_PLATFORM =~ /java/)
386
+ def test_binary_subtype_0
387
+ bin = Binary.new([1, 2, 3, 4, 5], Binary::SUBTYPE_SIMPLE)
388
+
389
+ doc = {'bin' => bin}
390
+ bson = @encoder.serialize(doc)
391
+ doc2 = @encoder.deserialize(bson)
392
+ bin2 = doc2['bin']
393
+ assert_kind_of Binary, bin2
394
+ assert_equal [1, 2, 3, 4, 5], bin2.to_a
395
+ assert_equal Binary::SUBTYPE_SIMPLE, bin2.subtype
396
+ end
397
+ end
398
+
399
+ def test_binary_byte_buffer
400
+ bb = Binary.new
401
+ 5.times { |i| bb.put(i + 1) }
402
+
403
+ doc = {'bin' => bb}
404
+ bson = @encoder.serialize(doc)
405
+ doc2 = @encoder.deserialize(bson)
406
+ bin2 = doc2['bin']
407
+ assert_kind_of Binary, bin2
408
+ assert_equal [1, 2, 3, 4, 5], bin2.to_a
409
+ assert_equal Binary::SUBTYPE_SIMPLE, bin2.subtype
410
+ end
411
+
412
+ def test_put_id_first
413
+ val = BSON::OrderedHash.new
414
+ val['not_id'] = 1
415
+ val['_id'] = 2
416
+ roundtrip = @encoder.deserialize(@encoder.serialize(val, false, true).to_s)
417
+ assert_kind_of BSON::OrderedHash, roundtrip
418
+ assert_equal '_id', roundtrip.keys.first
419
+
420
+ val = {'a' => 'foo', 'b' => 'bar', :_id => 42, 'z' => 'hello'}
421
+ roundtrip = @encoder.deserialize(@encoder.serialize(val, false, true).to_s)
422
+ assert_kind_of BSON::OrderedHash, roundtrip
423
+ assert_equal '_id', roundtrip.keys.first
424
+ end
425
+
426
+ def test_nil_id
427
+ doc = {"_id" => nil}
428
+ assert_doc_pass(doc)
429
+ end
430
+
431
+ if !(RUBY_PLATFORM =~ /java/)
432
+ def test_timestamp
433
+ val = {"test" => [4, 20]}
434
+ result = @encoder.deserialize([0x13, 0x00, 0x00, 0x00,
435
+ 0x11, 0x74, 0x65, 0x73,
436
+ 0x74, 0x00, 0x04, 0x00,
437
+ 0x00, 0x00, 0x14, 0x00,
438
+ 0x00, 0x00, 0x00])
439
+
440
+ silently do
441
+ assert_equal 4, result["test"][0]
442
+ assert_equal 20, result["test"][1]
443
+ end
444
+ end
445
+ end
446
+
447
+ def test_timestamp_type
448
+ ts = Timestamp.new(5000, 100)
449
+ doc = {:ts => ts}
450
+ bson = @encoder.serialize(doc)
451
+ assert_equal ts, @encoder.deserialize(bson)["ts"]
452
+ end
453
+
454
+ def test_overflow
455
+ doc = {"x" => 2**75}
456
+ assert_raise RangeError do
457
+ bson = @encoder.serialize(doc)
458
+ end
459
+
460
+ doc = {"x" => 9223372036854775}
461
+ assert_doc_pass(doc)
462
+
463
+ doc = {"x" => 9223372036854775807}
464
+ assert_doc_pass(doc)
465
+
466
+ doc["x"] = doc["x"] + 1
467
+ assert_raise RangeError do
468
+ bson = @encoder.serialize(doc)
469
+ end
470
+
471
+ doc = {"x" => -9223372036854775}
472
+ assert_doc_pass(doc)
473
+
474
+ doc = {"x" => -9223372036854775808}
475
+ assert_doc_pass(doc)
476
+
477
+ doc["x"] = doc["x"] - 1
478
+ assert_raise RangeError do
479
+ bson = BSON::BSON_CODER.serialize(doc)
480
+ end
481
+ end
482
+
483
+ def test_invalid_numeric_types
484
+ [BigDecimal.new("1.0"), Complex(0, 1), Rational(2, 3)].each do |type|
485
+ doc = {"x" => type}
486
+ begin
487
+ @encoder.serialize(doc)
488
+ rescue => e
489
+ ensure
490
+ assert_equal InvalidDocument, e.class
491
+ assert_match(/Cannot serialize/, e.message)
492
+ end
493
+ end
494
+ end
495
+
496
+ def test_do_not_change_original_object
497
+ val = BSON::OrderedHash.new
498
+ val['not_id'] = 1
499
+ val['_id'] = 2
500
+ assert val.keys.include?('_id')
501
+ @encoder.serialize(val)
502
+ assert val.keys.include?('_id')
503
+
504
+ val = {'a' => 'foo', 'b' => 'bar', :_id => 42, 'z' => 'hello'}
505
+ assert val.keys.include?(:_id)
506
+ @encoder.serialize(val)
507
+ assert val.keys.include?(:_id)
508
+ end
509
+
510
+ # note we only test for _id here because in the general case we will
511
+ # write duplicates for :key and "key". _id is a special case because
512
+ # we call has_key? to check for it's existence rather than just iterating
513
+ # over it like we do for the rest of the keys. thus, things like
514
+ # HashWithIndifferentAccess can cause problems for _id but not for other
515
+ # keys. rather than require rails to test with HWIA directly, we do this
516
+ # somewhat hacky test.
517
+ #
518
+ # Note that the driver only eliminates duplicate ids when move_id is true.
519
+ def test_no_duplicate_id
520
+ dup = {"_id" => "foo", :_id => "foo"}
521
+ one = {"_id" => "foo"}
522
+
523
+ assert_equal @encoder.serialize(one, false, true).to_a, @encoder.serialize(dup, false, true).to_a
524
+ end
525
+
526
+ def test_duplicate_keys
527
+ #dup = {"_foo" => "foo", :_foo => "foo"}
528
+ #one = {"_foo" => "foo"}
529
+
530
+ #assert_equal @encoder.serialize(one).to_a, @encoder.serialize(dup).to_a
531
+ warn "Pending test for duplicate keys"
532
+ end
533
+
534
+ def test_no_duplicate_id_when_moving_id
535
+ dup = {"_id" => "foo", :_id => "foo"}
536
+ one = {:_id => "foo"}
537
+
538
+ assert_equal @encoder.serialize(one, false, true).to_s, @encoder.serialize(dup, false, true).to_s
539
+ end
540
+
541
+ def test_null_character
542
+ doc = {"a" => "\x00"}
543
+
544
+ assert_doc_pass(doc)
545
+
546
+ assert_raise InvalidDocument do
547
+ @encoder.serialize({"\x00" => "a"})
548
+ end
549
+
550
+ assert_raise InvalidDocument do
551
+ @encoder.serialize({"a" => (Regexp.compile "ab\x00c")})
552
+ end
553
+ end
554
+
555
+ def test_max_key
556
+ doc = {"a" => MaxKey.new}
557
+ assert_doc_pass(doc)
558
+ end
559
+
560
+ def test_min_key
561
+ doc = {"a" => MinKey.new}
562
+ assert_doc_pass(doc)
563
+ end
564
+
565
+ def test_invalid_object
566
+ o = Object.new
567
+ assert_raise InvalidDocument do
568
+ @encoder.serialize({:foo => o})
569
+ end
570
+
571
+ assert_raise InvalidDocument do
572
+ @encoder.serialize({:foo => Date.today})
573
+ end
574
+ end
575
+
576
+ def test_move_id
577
+ a = BSON::OrderedHash.new
578
+ a['text'] = 'abc'
579
+ a['key'] = 'abc'
580
+ a['_id'] = 1
581
+
582
+
583
+ assert_equal ")\000\000\000\020_id\000\001\000\000\000\002text" +
584
+ "\000\004\000\000\000abc\000\002key\000\004\000\000\000abc\000\000",
585
+ @encoder.serialize(a, false, true).to_s
586
+
587
+ assert_equal ")\000\000\000\002text\000\004\000\000\000abc\000\002key" +
588
+ "\000\004\000\000\000abc\000\020_id\000\001\000\000\000\000",
589
+ @encoder.serialize(a, false, false).to_s
590
+ end
591
+
592
+ def test_move_id_with_nested_doc
593
+ b = BSON::OrderedHash.new
594
+ b['text'] = 'abc'
595
+ b['_id'] = 2
596
+ c = BSON::OrderedHash.new
597
+ c['text'] = 'abc'
598
+ c['hash'] = b
599
+ c['_id'] = 3
600
+ assert_equal ">\000\000\000\020_id\000\003\000\000\000\002text" +
601
+ "\000\004\000\000\000abc\000\003hash\000\034\000\000" +
602
+ "\000\002text\000\004\000\000\000abc\000\020_id\000\002\000\000\000\000\000",
603
+ @encoder.serialize(c, false, true).to_s
604
+
605
+ # Java doesn't support this. Isn't actually necessary.
606
+ if !(RUBY_PLATFORM =~ /java/)
607
+ assert_equal ">\000\000\000\002text\000\004\000\000\000abc\000\003hash" +
608
+ "\000\034\000\000\000\002text\000\004\000\000\000abc\000\020_id" +
609
+ "\000\002\000\000\000\000\020_id\000\003\000\000\000\000",
610
+ @encoder.serialize(c, false, false).to_s
611
+ end
612
+ end
613
+
614
+ def test_invalid_key_names
615
+ assert @encoder.serialize({"hello" => "world"}, true)
616
+ assert @encoder.serialize({"hello" => {"hello" => "world"}}, true)
617
+
618
+ assert @encoder.serialize({"he$llo" => "world"}, true)
619
+ assert @encoder.serialize({"hello" => {"hell$o" => "world"}}, true)
620
+
621
+ assert_raise BSON::InvalidDocument do
622
+ @encoder.serialize({"he\0llo" => "world"}, true)
623
+ end
624
+
625
+ assert_raise BSON::InvalidKeyName do
626
+ @encoder.serialize({"$hello" => "world"}, true)
627
+ end
628
+
629
+ assert_raise BSON::InvalidKeyName do
630
+ @encoder.serialize({"hello" => {"$hello" => "world"}}, true)
631
+ end
632
+
633
+ assert_raise BSON::InvalidKeyName do
634
+ @encoder.serialize({".hello" => "world"}, true)
635
+ end
636
+
637
+ assert_raise BSON::InvalidKeyName do
638
+ @encoder.serialize({"hello" => {".hello" => "world"}}, true)
639
+ end
640
+
641
+ assert_raise BSON::InvalidKeyName do
642
+ @encoder.serialize({"hello." => "world"}, true)
643
+ end
644
+
645
+ assert_raise BSON::InvalidKeyName do
646
+ @encoder.serialize({"hello" => {"hello." => "world"}}, true)
647
+ end
648
+
649
+ assert_raise BSON::InvalidKeyName do
650
+ @encoder.serialize({"hel.lo" => "world"}, true)
651
+ end
652
+
653
+ assert_raise BSON::InvalidKeyName do
654
+ @encoder.serialize({"hello" => {"hel.lo" => "world"}}, true)
655
+ end
656
+ end
657
+ end