re-require shoulda; gridfs decoupling
This commit is contained in:
parent
54a68c7438
commit
89fe06250e
|
@ -292,10 +292,11 @@ It's also possible to test replica pairs with connection pooling:
|
||||||
|
|
||||||
$ rake test:pooled_pair_insert
|
$ rake test:pooled_pair_insert
|
||||||
|
|
||||||
===Mocha
|
===Shoulda and Mocha
|
||||||
|
|
||||||
Running the test suite requires mocha. You can install it as follows:
|
Running the test suite requires shoulda and mocha. You can install them as follows:
|
||||||
|
|
||||||
|
$ gem install shoulda
|
||||||
$ gem install mocha
|
$ gem install mocha
|
||||||
|
|
||||||
The tests assume that the Mongo database is running on the default port. You
|
The tests assume that the Mongo database is running on the default port. You
|
||||||
|
|
|
@ -22,22 +22,25 @@ module Mongo
|
||||||
|
|
||||||
def initialize(db, fs_name=DEFAULT_FS_NAME)
|
def initialize(db, fs_name=DEFAULT_FS_NAME)
|
||||||
check_params(db)
|
check_params(db)
|
||||||
@db = db
|
@db = db
|
||||||
@files = @db["#{fs_name}.files"]
|
@files = @db["#{fs_name}.files"]
|
||||||
@chunks = @db["#{fs_name}.chunks"]
|
@chunks = @db["#{fs_name}.chunks"]
|
||||||
|
@fs_name = fs_name
|
||||||
|
|
||||||
@chunks.create_index([['files_id', Mongo::ASCENDING], ['n', Mongo::ASCENDING]])
|
@chunks.create_index([['files_id', Mongo::ASCENDING], ['n', Mongo::ASCENDING]])
|
||||||
end
|
end
|
||||||
|
|
||||||
def put(data, filename, opts={})
|
def put(data, filename, opts={})
|
||||||
file = GridIO.new(@files, @chunks, filename, 'w', false, opts=opts)
|
opts.merge!(default_grid_io_opts)
|
||||||
|
file = GridIO.new(@files, @chunks, filename, 'w', opts=opts)
|
||||||
file.write(data)
|
file.write(data)
|
||||||
file.close
|
file.close
|
||||||
file.files_id
|
file.files_id
|
||||||
end
|
end
|
||||||
|
|
||||||
def get(id)
|
def get(id)
|
||||||
GridIO.new(@files, @chunks, nil, 'r', false, :_id => id)
|
opts = {:query => {'_id' => id}}.merge!(default_grid_io_opts)
|
||||||
|
GridIO.new(@files, @chunks, nil, 'r', opts)
|
||||||
end
|
end
|
||||||
|
|
||||||
def delete(id)
|
def delete(id)
|
||||||
|
@ -47,6 +50,10 @@ module Mongo
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
def default_grid_io_opts
|
||||||
|
{:fs_name => @fs_name}
|
||||||
|
end
|
||||||
|
|
||||||
def check_params(db)
|
def check_params(db)
|
||||||
if !db.is_a?(Mongo::DB)
|
if !db.is_a?(Mongo::DB)
|
||||||
raise MongoArgumentError, "db must be an instance of Mongo::DB."
|
raise MongoArgumentError, "db must be an instance of Mongo::DB."
|
||||||
|
|
|
@ -23,10 +23,12 @@ module Mongo
|
||||||
super
|
super
|
||||||
|
|
||||||
@files.create_index([['filename', 1], ['uploadDate', -1]])
|
@files.create_index([['filename', 1], ['uploadDate', -1]])
|
||||||
|
@default_query_opts = {:sort => [['filename', 1], ['uploadDate', -1]], :limit => 1}
|
||||||
end
|
end
|
||||||
|
|
||||||
def open(filename, mode, opts={})
|
def open(filename, mode, opts={})
|
||||||
file = GridIO.new(@files, @chunks, filename, mode, true, opts)
|
opts.merge!(default_grid_io_opts(filename))
|
||||||
|
file = GridIO.new(@files, @chunks, filename, mode, opts)
|
||||||
return file unless block_given?
|
return file unless block_given?
|
||||||
result = nil
|
result = nil
|
||||||
begin
|
begin
|
||||||
|
@ -37,15 +39,31 @@ module Mongo
|
||||||
result
|
result
|
||||||
end
|
end
|
||||||
|
|
||||||
def put(data, filename)
|
def put(data, filename, opts={})
|
||||||
|
opts.merge!(default_grid_io_opts(filename))
|
||||||
|
file = GridIO.new(@files, @chunks, filename, 'w', opts)
|
||||||
|
file.write(data)
|
||||||
|
file.close
|
||||||
|
file.files_id
|
||||||
end
|
end
|
||||||
|
|
||||||
def get(id)
|
def get(filename, opts={})
|
||||||
|
opts.merge!(default_grid_io_opts(filename))
|
||||||
|
GridIO.new(@files, @chunks, filename, 'r', opts)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Deletes all files matching the given criteria.
|
def delete(filename, opts={})
|
||||||
def delete(criteria)
|
ids = @files.find({'filename' => filename}, ['_id'])
|
||||||
|
ids.each do |id|
|
||||||
|
@files.remove({'_id' => id})
|
||||||
|
@chunks.remove('files_id' => id)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def default_grid_io_opts(filename=nil)
|
||||||
|
{:fs_name => @fs_name, :query => {'filename' => filename}, :query_opts => @default_query_opts}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -23,20 +23,20 @@ module Mongo
|
||||||
|
|
||||||
attr_reader :content_type, :chunk_size, :upload_date, :files_id, :filename, :metadata
|
attr_reader :content_type, :chunk_size, :upload_date, :files_id, :filename, :metadata
|
||||||
|
|
||||||
def initialize(files, chunks, filename, mode, filesystem, opts={})
|
def initialize(files, chunks, filename, mode, opts={})
|
||||||
@files = files
|
@files = files
|
||||||
@chunks = chunks
|
@chunks = chunks
|
||||||
@filename = filename
|
@filename = filename
|
||||||
@mode = mode
|
@mode = mode
|
||||||
@content_type = opts[:content_type] || DEFAULT_CONTENT_TYPE
|
@query = opts[:query] || {}
|
||||||
@chunk_size = opts[:chunk_size] || DEFAULT_CHUNK_SIZE
|
@query_opts = opts[:query_opts] || {}
|
||||||
@files_id = opts[:_id]
|
@fs_name = opts[:fs_name] || Grid::DEFAULT_FS_NAME
|
||||||
|
|
||||||
case @mode
|
case @mode
|
||||||
when 'r' then init_read(filesystem, opts)
|
when 'r' then init_read(opts)
|
||||||
when 'w' then init_write(opts)
|
when 'w' then init_write(opts)
|
||||||
else
|
else
|
||||||
raise GridError, "Invalid file mode #{@mode}. Valid options include 'r' and 'w'."
|
raise GridError, "Invalid file mode #{@mode}. Mode should be 'r' or 'w'."
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -147,9 +147,7 @@ module Mongo
|
||||||
chunk
|
chunk
|
||||||
end
|
end
|
||||||
|
|
||||||
# TODO: Perhaps use an upsert here instead?
|
|
||||||
def save_chunk(chunk)
|
def save_chunk(chunk)
|
||||||
@chunks.remove('_id' => chunk['_id'])
|
|
||||||
@chunks.insert(chunk)
|
@chunks.insert(chunk)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -159,22 +157,17 @@ module Mongo
|
||||||
chunk
|
chunk
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_chunk_for_read(n)
|
|
||||||
chunk = get_chunk(n)
|
|
||||||
return nil unless chunk
|
|
||||||
end
|
|
||||||
|
|
||||||
def last_chunk_number
|
def last_chunk_number
|
||||||
(@file_length / @chunk_size).to_i
|
(@file_length / @chunk_size).to_i
|
||||||
end
|
end
|
||||||
|
|
||||||
# Read a file in its entirety (optimized).
|
# Read a file in its entirety.
|
||||||
def read_all
|
def read_all
|
||||||
buf = ''
|
buf = ''
|
||||||
while true
|
while true
|
||||||
buf << @current_chunk['data'].to_s
|
buf << @current_chunk['data'].to_s
|
||||||
break if @current_chunk['n'] == last_chunk_number
|
|
||||||
@current_chunk = get_chunk(@current_chunk['n'] + 1)
|
@current_chunk = get_chunk(@current_chunk['n'] + 1)
|
||||||
|
break unless @current_chunk
|
||||||
end
|
end
|
||||||
buf
|
buf
|
||||||
end
|
end
|
||||||
|
@ -232,15 +225,10 @@ module Mongo
|
||||||
string.length - to_write
|
string.length - to_write
|
||||||
end
|
end
|
||||||
|
|
||||||
# Initialize based on whether the supplied file exists.
|
# Initialize the class for reading a file.
|
||||||
def init_read(filesystem, opts)
|
def init_read(opts)
|
||||||
if filesystem
|
doc = @files.find(@query, @query_opts).next_document
|
||||||
doc = @files.find({'filename' => @filename}, :sort => [["uploadDate", -1]], :limit => 1).next_document
|
raise GridError, "Could not open file matching #{@query.inspect} #{@query_opts.inspect}" unless doc
|
||||||
raise GridError, "Could not open file with filename #{@filename}" unless doc
|
|
||||||
else
|
|
||||||
doc = @files.find({'_id' => @files_id}).next_document
|
|
||||||
raise GridError, "Could not open file with id #{@files_id}" unless doc
|
|
||||||
end
|
|
||||||
|
|
||||||
@files_id = doc['_id']
|
@files_id = doc['_id']
|
||||||
@content_type = doc['contentType']
|
@content_type = doc['contentType']
|
||||||
|
@ -251,11 +239,12 @@ module Mongo
|
||||||
@metadata = doc['metadata']
|
@metadata = doc['metadata']
|
||||||
@md5 = doc['md5']
|
@md5 = doc['md5']
|
||||||
@filename = doc['filename']
|
@filename = doc['filename']
|
||||||
|
|
||||||
@current_chunk = get_chunk(0)
|
@current_chunk = get_chunk(0)
|
||||||
@file_position = 0
|
@file_position = 0
|
||||||
end
|
end
|
||||||
|
|
||||||
# Validates and sets up the class for the given file mode.
|
# Initialize the class for writing a file.
|
||||||
def init_write(opts)
|
def init_write(opts)
|
||||||
@files_id = opts[:_id] || Mongo::ObjectID.new
|
@files_id = opts[:_id] || Mongo::ObjectID.new
|
||||||
@content_type = opts[:content_type] || @content_type || DEFAULT_CONTENT_TYPE
|
@content_type = opts[:content_type] || @content_type || DEFAULT_CONTENT_TYPE
|
||||||
|
@ -281,7 +270,7 @@ module Mongo
|
||||||
# Get a server-side md5.
|
# Get a server-side md5.
|
||||||
md5_command = OrderedHash.new
|
md5_command = OrderedHash.new
|
||||||
md5_command['filemd5'] = @files_id
|
md5_command['filemd5'] = @files_id
|
||||||
md5_command['root'] = 'fs'
|
md5_command['root'] = @fs_name
|
||||||
h['md5'] = @files.db.command(md5_command)['md5']
|
h['md5'] = @files.db.command(md5_command)['md5']
|
||||||
|
|
||||||
h
|
h
|
||||||
|
|
|
@ -1,13 +1,15 @@
|
||||||
# encoding:utf-8
|
# encoding:utf-8
|
||||||
require 'test/test_helper'
|
require 'test/test_helper'
|
||||||
|
|
||||||
context "Inspecting" do
|
class BinaryTest < Test::Unit::TestCase
|
||||||
setup do
|
context "Inspecting" do
|
||||||
@data = ("THIS IS BINARY " * 50).unpack("c*")
|
setup do
|
||||||
end
|
@data = ("THIS IS BINARY " * 50).unpack("c*")
|
||||||
|
end
|
||||||
|
|
||||||
should "not display actual data" do
|
should "not display actual data" do
|
||||||
binary = Mongo::Binary.new(@data)
|
binary = Mongo::Binary.new(@data)
|
||||||
assert_equal "<Mongo::Binary:#{binary.object_id}>", binary.inspect
|
assert_equal "<Mongo::Binary:#{binary.object_id}>", binary.inspect
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
require 'test/test_helper'
|
require 'test/test_helper'
|
||||||
|
|
||||||
class TestCollection < Test::Unit::TestCase
|
class TestCollection < Test::Unit::TestCase
|
||||||
@@connection = Connection.new(ENV['MONGO_RUBY_DRIVER_HOST'] || 'localhost', ENV['MONGO_RUBY_DRIVER_PORT'] || Connection::DEFAULT_PORT)
|
@@connection ||= Connection.new(ENV['MONGO_RUBY_DRIVER_HOST'] || 'localhost', ENV['MONGO_RUBY_DRIVER_PORT'] || Connection::DEFAULT_PORT)
|
||||||
@@db = @@connection.db('ruby-mongo-test')
|
@@db = @@connection.db('ruby-mongo-test')
|
||||||
@@test = @@db.collection("test")
|
@@test = @@db.collection("test")
|
||||||
@@version = @@connection.server_version
|
@@version = @@connection.server_version
|
||||||
|
@ -75,32 +75,31 @@ class TestCollection < Test::Unit::TestCase
|
||||||
end
|
end
|
||||||
|
|
||||||
if @@version > "1.1"
|
if @@version > "1.1"
|
||||||
context "distinct queries" do
|
def setup_for_distinct
|
||||||
setup do
|
@@test.remove
|
||||||
@@test.remove
|
@@test.insert([{:a => 0, :b => {:c => "a"}},
|
||||||
@@test.insert([{:a => 0, :b => {:c => "a"}},
|
{:a => 1, :b => {:c => "b"}},
|
||||||
{:a => 1, :b => {:c => "b"}},
|
{:a => 1, :b => {:c => "c"}},
|
||||||
{:a => 1, :b => {:c => "c"}},
|
{:a => 2, :b => {:c => "a"}},
|
||||||
{:a => 2, :b => {:c => "a"}},
|
{:a => 3},
|
||||||
{:a => 3},
|
{:a => 3}])
|
||||||
{:a => 3}])
|
end
|
||||||
|
|
||||||
|
def test_distinct_queries
|
||||||
|
setup_for_distinct
|
||||||
|
assert_equal [0, 1, 2, 3], @@test.distinct(:a).sort
|
||||||
|
assert_equal ["a", "b", "c"], @@test.distinct("b.c").sort
|
||||||
|
end
|
||||||
|
|
||||||
|
if @@version >= "1.2"
|
||||||
|
def test_filter_collection_with_query
|
||||||
|
setup_for_distinct
|
||||||
|
assert_equal [2, 3], @@test.distinct(:a, {:a => {"$gt" => 1}}).sort
|
||||||
end
|
end
|
||||||
|
|
||||||
should "return distinct values" do
|
def test_filter_nested_objects
|
||||||
assert_equal [0, 1, 2, 3], @@test.distinct(:a).sort
|
setup_for_distinct
|
||||||
assert_equal ["a", "b", "c"], @@test.distinct("b.c").sort
|
assert_equal ["a", "b"], @@test.distinct("b.c", {"b.c" => {"$ne" => "c"}}).sort
|
||||||
end
|
|
||||||
|
|
||||||
if @@version >= "1.2"
|
|
||||||
|
|
||||||
should "filter collection with query" do
|
|
||||||
assert_equal [2, 3], @@test.distinct(:a, {:a => {"$gt" => 1}}).sort
|
|
||||||
end
|
|
||||||
|
|
||||||
should "filter nested objects" do
|
|
||||||
assert_equal ["a", "b"], @@test.distinct("b.c", {"b.c" => {"$ne" => "c"}}).sort
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,184 +1,186 @@
|
||||||
require 'test/test_helper'
|
require 'test/test_helper'
|
||||||
include Mongo
|
include Mongo
|
||||||
|
|
||||||
context "GridFileSystem:" do
|
class GridFileSystemTest < Test::Unit::TestCase
|
||||||
setup do
|
context "GridFileSystem:" do
|
||||||
@db ||= Connection.new(ENV['MONGO_RUBY_DRIVER_HOST'] || 'localhost',
|
|
||||||
ENV['MONGO_RUBY_DRIVER_PORT'] || Connection::DEFAULT_PORT).db('ruby-mongo-test')
|
|
||||||
@files = @db.collection('fs.files')
|
|
||||||
@chunks = @db.collection('fs.chunks')
|
|
||||||
end
|
|
||||||
|
|
||||||
teardown do
|
|
||||||
@files.remove
|
|
||||||
@chunks.remove
|
|
||||||
end
|
|
||||||
|
|
||||||
context "When reading:" do
|
|
||||||
setup do
|
setup do
|
||||||
@data = "CHUNKS" * 50000
|
@con = Connection.new(ENV['MONGO_RUBY_DRIVER_HOST'] || 'localhost',
|
||||||
@grid = GridFileSystem.new(@db)
|
ENV['MONGO_RUBY_DRIVER_PORT'] || Connection::DEFAULT_PORT)
|
||||||
@grid.open('sample', 'w') do |f|
|
@db = @con.db('mongo-ruby-test')
|
||||||
f.write @data
|
|
||||||
end
|
|
||||||
|
|
||||||
@grid = GridFileSystem.new(@db)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
should "read sample data" do
|
teardown do
|
||||||
data = @grid.open('sample', 'r') { |f| f.read }
|
@db['fs.files'].remove
|
||||||
assert_equal data.length, @data.length
|
@db['fs.chunks'].remove
|
||||||
end
|
end
|
||||||
|
|
||||||
should "return an empty string if length is zero" do
|
context "When reading:" do
|
||||||
data = @grid.open('sample', 'r') { |f| f.read(0) }
|
|
||||||
assert_equal '', data
|
|
||||||
end
|
|
||||||
|
|
||||||
should "return the first n bytes" do
|
|
||||||
data = @grid.open('sample', 'r') {|f| f.read(288888) }
|
|
||||||
assert_equal 288888, data.length
|
|
||||||
assert_equal @data[0...288888], data
|
|
||||||
end
|
|
||||||
|
|
||||||
should "return the first n bytes even with an offset" do
|
|
||||||
data = @grid.open('sample', 'r') do |f|
|
|
||||||
f.seek(1000)
|
|
||||||
f.read(288888)
|
|
||||||
end
|
|
||||||
assert_equal 288888, data.length
|
|
||||||
assert_equal @data[1000...289888], data
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context "When writing:" do
|
|
||||||
setup do
|
|
||||||
@data = "BYTES" * 50000
|
|
||||||
@grid = GridFileSystem.new(@db)
|
|
||||||
@grid.open('sample', 'w') do |f|
|
|
||||||
f.write @data
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
should "read sample data" do
|
|
||||||
data = @grid.open('sample', 'r') { |f| f.read }
|
|
||||||
assert_equal data.length, @data.length
|
|
||||||
end
|
|
||||||
|
|
||||||
should "return the total number of bytes written" do
|
|
||||||
data = 'a' * 300000
|
|
||||||
assert_equal 300000, @grid.open('write', 'w') {|f| f.write(data) }
|
|
||||||
end
|
|
||||||
|
|
||||||
should "more read sample data" do
|
|
||||||
data = @grid.open('sample', 'r') { |f| f.read }
|
|
||||||
assert_equal data.length, @data.length
|
|
||||||
end
|
|
||||||
|
|
||||||
should "raise exception if not opened for write" do
|
|
||||||
assert_raise GridError do
|
|
||||||
@grid.open('io', 'r') { |f| f.write('hello') }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context "and when overwriting the file" do
|
|
||||||
setup do
|
setup do
|
||||||
@old = @grid.open('sample', 'r')
|
@chunks_data = "CHUNKS" * 50000
|
||||||
|
@grid = GridFileSystem.new(@db)
|
||||||
@new_data = "DATA" * 1000
|
@grid.open('sample.file', 'w') do |f|
|
||||||
@grid.open('sample', 'w') do |f|
|
f.write @chunks_data
|
||||||
f.write @new_data
|
|
||||||
end
|
end
|
||||||
|
|
||||||
@new = @grid.open('sample', 'r')
|
@grid = GridFileSystem.new(@db)
|
||||||
end
|
end
|
||||||
|
|
||||||
should "have a newer upload date" do
|
should "read sample data" do
|
||||||
assert @new.upload_date > @old.upload_date
|
data = @grid.open('sample.file', 'r') { |f| f.read }
|
||||||
|
assert_equal data.length, @chunks_data.length
|
||||||
end
|
end
|
||||||
|
|
||||||
should "have a different files_id" do
|
should "return an empty string if length is zero" do
|
||||||
assert_not_equal @new.files_id, @old.files_id
|
data = @grid.open('sample.file', 'r') { |f| f.read(0) }
|
||||||
|
assert_equal '', data
|
||||||
end
|
end
|
||||||
|
|
||||||
should "contain the new data" do
|
should "return the first n bytes" do
|
||||||
assert_equal @new_data, @new.read
|
data = @grid.open('sample.file', 'r') {|f| f.read(288888) }
|
||||||
|
assert_equal 288888, data.length
|
||||||
|
assert_equal @chunks_data[0...288888], data
|
||||||
end
|
end
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context "When writing chunks:" do
|
should "return the first n bytes even with an offset" do
|
||||||
setup do
|
data = @grid.open('sample.file', 'r') do |f|
|
||||||
data = "B" * 50000
|
f.seek(1000)
|
||||||
@grid = GridFileSystem.new(@db)
|
f.read(288888)
|
||||||
@grid.open('sample', 'w', :chunk_size => 1000) do |f|
|
end
|
||||||
f.write data
|
assert_equal 288888, data.length
|
||||||
|
assert_equal @chunks_data[1000...289888], data
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
should "write the correct number of chunks" do
|
context "When writing:" do
|
||||||
file = @files.find_one({:filename => 'sample'})
|
setup do
|
||||||
chunks = @chunks.find({'files_id' => file['_id']}).to_a
|
@data = "BYTES" * 50
|
||||||
assert_equal 50, chunks.length
|
@grid = GridFileSystem.new(@db)
|
||||||
end
|
@grid.open('sample', 'w') do |f|
|
||||||
end
|
f.write @data
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
context "Positioning:" do
|
should "read sample data" do
|
||||||
setup do
|
data = @grid.open('sample', 'r') { |f| f.read }
|
||||||
data = 'hello, world' + '1' * 5000 + 'goodbye!' + '2' * 1000 + '!'
|
assert_equal data.length, @data.length
|
||||||
@grid = GridFileSystem.new(@db)
|
end
|
||||||
@grid.open('hello', 'w', :chunk_size => 1000) do |f|
|
|
||||||
f.write data
|
should "return the total number of bytes written" do
|
||||||
|
data = 'a' * 300000
|
||||||
|
assert_equal 300000, @grid.open('sample', 'w') {|f| f.write(data) }
|
||||||
|
end
|
||||||
|
|
||||||
|
should "more read sample data" do
|
||||||
|
data = @grid.open('sample', 'r') { |f| f.read }
|
||||||
|
assert_equal data.length, @data.length
|
||||||
|
end
|
||||||
|
|
||||||
|
should "raise exception if not opened for write" do
|
||||||
|
assert_raise GridError do
|
||||||
|
@grid.open('io', 'r') { |f| f.write('hello') }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "and when overwriting the file" do
|
||||||
|
setup do
|
||||||
|
@old = @grid.open('sample', 'r')
|
||||||
|
|
||||||
|
@new_data = "DATA" * 10
|
||||||
|
sleep(2)
|
||||||
|
@grid.open('sample', 'w') do |f|
|
||||||
|
f.write @new_data
|
||||||
|
end
|
||||||
|
|
||||||
|
@new = @grid.open('sample', 'r')
|
||||||
|
end
|
||||||
|
|
||||||
|
should "have a newer upload date" do
|
||||||
|
assert @new.upload_date > @old.upload_date, "New data is not greater than old date."
|
||||||
|
end
|
||||||
|
|
||||||
|
should "have a different files_id" do
|
||||||
|
assert_not_equal @new.files_id, @old.files_id
|
||||||
|
end
|
||||||
|
|
||||||
|
should "contain the new data" do
|
||||||
|
assert_equal @new_data, @new.read, "Expected DATA"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "When writing chunks:" do
|
||||||
|
setup do
|
||||||
|
data = "B" * 50000
|
||||||
|
@grid = GridFileSystem.new(@db)
|
||||||
|
@grid.open('sample', 'w', :chunk_size => 1000) do |f|
|
||||||
|
f.write data
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
should "write the correct number of chunks" do
|
||||||
|
file = @db['fs.files'].find_one({:filename => 'sample'})
|
||||||
|
chunks = @db['fs.chunks'].find({'files_id' => file['_id']}).to_a
|
||||||
|
assert_equal 50, chunks.length
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
should "seek within chunks" do
|
context "Positioning:" do
|
||||||
@grid.open('hello', 'r') do |f|
|
setup do
|
||||||
f.seek(0)
|
data = 'hello, world' + '1' * 5000 + 'goodbye!' + '2' * 1000 + '!'
|
||||||
assert_equal 'h', f.read(1)
|
@grid = GridFileSystem.new(@db)
|
||||||
f.seek(7)
|
@grid.open('hello', 'w', :chunk_size => 1000) do |f|
|
||||||
assert_equal 'w', f.read(1)
|
f.write data
|
||||||
f.seek(4)
|
end
|
||||||
assert_equal 'o', f.read(1)
|
|
||||||
f.seek(0)
|
|
||||||
f.seek(7, IO::SEEK_CUR)
|
|
||||||
assert_equal 'w', f.read(1)
|
|
||||||
f.seek(-2, IO::SEEK_CUR)
|
|
||||||
assert_equal ' ', f.read(1)
|
|
||||||
f.seek(-4, IO::SEEK_CUR)
|
|
||||||
assert_equal 'l', f.read(1)
|
|
||||||
f.seek(3, IO::SEEK_CUR)
|
|
||||||
assert_equal 'w', f.read(1)
|
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
should "seek between chunks" do
|
should "seek within chunks" do
|
||||||
@grid.open('hello', 'r') do |f|
|
@grid.open('hello', 'r') do |f|
|
||||||
f.seek(1000)
|
f.seek(0)
|
||||||
assert_equal '11111', f.read(5)
|
assert_equal 'h', f.read(1)
|
||||||
|
f.seek(7)
|
||||||
f.seek(5009)
|
assert_equal 'w', f.read(1)
|
||||||
assert_equal '111goodbye!222', f.read(14)
|
f.seek(4)
|
||||||
|
assert_equal 'o', f.read(1)
|
||||||
f.seek(-1, IO::SEEK_END)
|
f.seek(0)
|
||||||
assert_equal '!', f.read(1)
|
f.seek(7, IO::SEEK_CUR)
|
||||||
f.seek(-6, IO::SEEK_END)
|
assert_equal 'w', f.read(1)
|
||||||
assert_equal '2', f.read(1)
|
f.seek(-2, IO::SEEK_CUR)
|
||||||
|
assert_equal ' ', f.read(1)
|
||||||
|
f.seek(-4, IO::SEEK_CUR)
|
||||||
|
assert_equal 'l', f.read(1)
|
||||||
|
f.seek(3, IO::SEEK_CUR)
|
||||||
|
assert_equal 'w', f.read(1)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
should "tell the current position" do
|
should "seek between chunks" do
|
||||||
@grid.open('hello', 'r') do |f|
|
@grid.open('hello', 'r') do |f|
|
||||||
assert_equal 0, f.tell
|
f.seek(1000)
|
||||||
|
assert_equal '11111', f.read(5)
|
||||||
|
|
||||||
f.seek(999)
|
f.seek(5009)
|
||||||
assert_equal 999, f.tell
|
assert_equal '111goodbye!222', f.read(14)
|
||||||
|
|
||||||
|
f.seek(-1, IO::SEEK_END)
|
||||||
|
assert_equal '!', f.read(1)
|
||||||
|
f.seek(-6, IO::SEEK_END)
|
||||||
|
assert_equal '2', f.read(1)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
should "seek only in read mode" do
|
should "tell the current position" do
|
||||||
assert_raise GridError do
|
@grid.open('hello', 'r') do |f|
|
||||||
@grid.open('hello', 'w') {|f| f.seek(0) }
|
assert_equal 0, f.tell
|
||||||
|
|
||||||
|
f.seek(999)
|
||||||
|
assert_equal 999, f.tell
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
should "seek only in read mode" do
|
||||||
|
assert_raise GridError do
|
||||||
|
@grid.open('hello', 'w') {|f| f.seek(0) }
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,33 +1,37 @@
|
||||||
require 'test/test_helper'
|
require 'test/test_helper'
|
||||||
include Mongo
|
include Mongo
|
||||||
|
|
||||||
context "" do
|
class GridIOTest < Test::Unit::TestCase
|
||||||
setup do
|
|
||||||
@db ||= Connection.new(ENV['MONGO_RUBY_DRIVER_HOST'] || 'localhost',
|
|
||||||
ENV['MONGO_RUBY_DRIVER_PORT'] || Connection::DEFAULT_PORT).db('ruby-mongo-test')
|
|
||||||
@files = @db.collection('fs.files')
|
|
||||||
@chunks = @db.collection('fs.chunks')
|
|
||||||
end
|
|
||||||
|
|
||||||
teardown do
|
context "GridIO" do
|
||||||
@files.remove
|
|
||||||
@chunks.remove
|
|
||||||
end
|
|
||||||
|
|
||||||
context "Options" do
|
|
||||||
setup do
|
setup do
|
||||||
@filename = 'test'
|
@db ||= Connection.new(ENV['MONGO_RUBY_DRIVER_HOST'] || 'localhost',
|
||||||
@mode = 'w'
|
ENV['MONGO_RUBY_DRIVER_PORT'] || Connection::DEFAULT_PORT).db('ruby-mongo-test')
|
||||||
|
@files = @db.collection('fs.files')
|
||||||
|
@chunks = @db.collection('fs.chunks')
|
||||||
end
|
end
|
||||||
|
|
||||||
should "set default 256k chunk size" do
|
teardown do
|
||||||
file = GridIO.new(@files, @chunks, @filename, @mode, false)
|
@files.remove
|
||||||
assert_equal 256 * 1024, file.chunk_size
|
@chunks.remove
|
||||||
end
|
end
|
||||||
|
|
||||||
should "set chunk size" do
|
context "Options" do
|
||||||
file = GridIO.new(@files, @chunks, @filename, @mode, false, :chunk_size => 1000)
|
setup do
|
||||||
assert_equal 1000, file.chunk_size
|
@filename = 'test'
|
||||||
|
@mode = 'w'
|
||||||
|
end
|
||||||
|
|
||||||
|
should "set default 256k chunk size" do
|
||||||
|
file = GridIO.new(@files, @chunks, @filename, @mode)
|
||||||
|
assert_equal 256 * 1024, file.chunk_size
|
||||||
|
end
|
||||||
|
|
||||||
|
should "set chunk size" do
|
||||||
|
file = GridIO.new(@files, @chunks, @filename, @mode, :chunk_size => 1000)
|
||||||
|
assert_equal 1000, file.chunk_size
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,84 +1,87 @@
|
||||||
require 'test/test_helper'
|
require 'test/test_helper'
|
||||||
include Mongo
|
include Mongo
|
||||||
|
|
||||||
context "Tests:" do
|
class GridTest < Test::Unit::TestCase
|
||||||
setup do
|
context "Tests:" do
|
||||||
@db ||= Connection.new(ENV['MONGO_RUBY_DRIVER_HOST'] || 'localhost',
|
|
||||||
ENV['MONGO_RUBY_DRIVER_PORT'] || Connection::DEFAULT_PORT).db('ruby-mongo-test')
|
|
||||||
@files = @db.collection('test-fs.files')
|
|
||||||
@chunks = @db.collection('test-fs.chunks')
|
|
||||||
end
|
|
||||||
|
|
||||||
teardown do
|
|
||||||
@files.remove
|
|
||||||
@chunks.remove
|
|
||||||
end
|
|
||||||
|
|
||||||
context "A basic grid-stored file" do
|
|
||||||
setup do
|
setup do
|
||||||
@data = "GRIDDATA" * 50000
|
@db ||= Connection.new(ENV['MONGO_RUBY_DRIVER_HOST'] || 'localhost',
|
||||||
@grid = Grid.new(@db, 'test-fs')
|
ENV['MONGO_RUBY_DRIVER_PORT'] || Connection::DEFAULT_PORT).db('ruby-mongo-test')
|
||||||
@id = @grid.put(@data, 'sample', :metadata => {'app' => 'photos'})
|
@files = @db.collection('test-fs.files')
|
||||||
|
@chunks = @db.collection('test-fs.chunks')
|
||||||
end
|
end
|
||||||
|
|
||||||
should "retrieve the stored data" do
|
teardown do
|
||||||
data = @grid.get(@id).data
|
@files.remove
|
||||||
assert_equal @data, data
|
@chunks.remove
|
||||||
end
|
end
|
||||||
|
|
||||||
should "store the filename" do
|
context "A basic grid-stored file" do
|
||||||
file = @grid.get(@id)
|
setup do
|
||||||
assert_equal 'sample', file.filename
|
@data = "GRIDDATA" * 50000
|
||||||
end
|
@grid = Grid.new(@db, 'test-fs')
|
||||||
|
@id = @grid.put(@data, 'sample', :metadata => {'app' => 'photos'})
|
||||||
should "store any relevant metadata" do
|
|
||||||
file = @grid.get(@id)
|
|
||||||
assert_equal 'photos', file.metadata['app']
|
|
||||||
end
|
|
||||||
|
|
||||||
should "delete the file and any chunks" do
|
|
||||||
@grid.delete(@id)
|
|
||||||
assert_raise GridError do
|
|
||||||
@grid.get(@id)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context "Streaming: " do
|
|
||||||
setup do
|
|
||||||
def read_and_write_stream(filename, read_length, opts={})
|
|
||||||
io = File.open(File.join(File.dirname(__FILE__), 'data', filename), 'r')
|
|
||||||
id = @grid.put(io, filename + read_length.to_s, opts)
|
|
||||||
file = @grid.get(id)
|
|
||||||
io.rewind
|
|
||||||
data = io.read
|
|
||||||
if data.respond_to?(:force_encoding)
|
|
||||||
data.force_encoding(:binary)
|
|
||||||
end
|
|
||||||
read_data = ""
|
|
||||||
while(chunk = file.read(read_length))
|
|
||||||
read_data << chunk
|
|
||||||
end
|
|
||||||
assert_equal data.length, read_data.length
|
|
||||||
end
|
end
|
||||||
|
|
||||||
@grid = Grid.new(@db, 'test-fs')
|
should "retrieve the stored data" do
|
||||||
|
data = @grid.get(@id).data
|
||||||
|
assert_equal @data, data
|
||||||
|
end
|
||||||
|
|
||||||
|
should "store the filename" do
|
||||||
|
file = @grid.get(@id)
|
||||||
|
assert_equal 'sample', file.filename
|
||||||
|
end
|
||||||
|
|
||||||
|
should "store any relevant metadata" do
|
||||||
|
file = @grid.get(@id)
|
||||||
|
assert_equal 'photos', file.metadata['app']
|
||||||
|
end
|
||||||
|
|
||||||
|
should "delete the file and any chunks" do
|
||||||
|
@grid.delete(@id)
|
||||||
|
assert_raise GridError do
|
||||||
|
@grid.get(@id)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
should "put and get a small io object with a small chunk size" do
|
context "Streaming: " do || {}
|
||||||
read_and_write_stream('small_data.txt', 1, :chunk_size => 2)
|
setup do
|
||||||
end
|
def read_and_write_stream(filename, read_length, opts={})
|
||||||
|
io = File.open(File.join(File.dirname(__FILE__), 'data', filename), 'r')
|
||||||
|
id = @grid.put(io, filename + read_length.to_s, opts)
|
||||||
|
file = @grid.get(id)
|
||||||
|
io.rewind
|
||||||
|
data = io.read
|
||||||
|
if data.respond_to?(:force_encoding)
|
||||||
|
data.force_encoding(:binary)
|
||||||
|
end
|
||||||
|
read_data = ""
|
||||||
|
while(chunk = file.read(read_length))
|
||||||
|
read_data << chunk
|
||||||
|
end
|
||||||
|
assert_equal data.length, read_data.length
|
||||||
|
assert_equal data, read_data, "Unequal!"
|
||||||
|
end
|
||||||
|
|
||||||
should "put and get a small io object" do
|
@grid = Grid.new(@db, 'test-fs')
|
||||||
read_and_write_stream('small_data.txt', 1)
|
end
|
||||||
end
|
|
||||||
|
|
||||||
should "put and get a large io object when reading smaller than the chunk size" do
|
should "put and get a small io object with a small chunk size" do
|
||||||
read_and_write_stream('sample_file.pdf', 256 * 1024)
|
read_and_write_stream('small_data.txt', 1, :chunk_size => 2)
|
||||||
end
|
end
|
||||||
|
|
||||||
should "put and get a large io object when reading larger than the chunk size" do
|
should "put and get a small io object" do
|
||||||
read_and_write_stream('sample_file.pdf', 300 * 1024)
|
read_and_write_stream('small_data.txt', 1)
|
||||||
|
end
|
||||||
|
|
||||||
|
should "put and get a large io object when reading smaller than the chunk size" do
|
||||||
|
read_and_write_stream('sample_file.pdf', 256 * 1024)
|
||||||
|
end
|
||||||
|
|
||||||
|
should "put and get a large io object when reading larger than the chunk size" do
|
||||||
|
read_and_write_stream('sample_file.pdf', 300 * 1024)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -5,12 +5,14 @@ require 'test/unit'
|
||||||
|
|
||||||
begin
|
begin
|
||||||
require 'rubygems'
|
require 'rubygems'
|
||||||
|
require 'shoulda'
|
||||||
require 'mocha'
|
require 'mocha'
|
||||||
rescue LoadError
|
rescue LoadError
|
||||||
puts <<MSG
|
puts <<MSG
|
||||||
|
|
||||||
This test suite requires mocha.
|
This test suite requires shoulda and mocha.
|
||||||
You can install it as follows:
|
You can install them as follows:
|
||||||
|
gem install shoulda
|
||||||
gem install mocha
|
gem install mocha
|
||||||
|
|
||||||
MSG
|
MSG
|
||||||
|
@ -38,31 +40,3 @@ class Test::Unit::TestCase
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# shoulda-mini
|
|
||||||
# based on test/spec/mini 5
|
|
||||||
# http://gist.github.com/307649
|
|
||||||
# chris@ozmm.org
|
|
||||||
#
|
|
||||||
def context(*args, &block)
|
|
||||||
return super unless (name = args.first) && block
|
|
||||||
require 'test/unit'
|
|
||||||
klass = Class.new(Test::Unit::TestCase) do
|
|
||||||
def self.should(name, &block)
|
|
||||||
define_method("test_#{name.to_s.gsub(/\W/,'_')}", &block) if block
|
|
||||||
end
|
|
||||||
def self.xshould(*args) end
|
|
||||||
def self.context(*args, &block) instance_eval(&block) end
|
|
||||||
def self.setup(&block)
|
|
||||||
define_method(:setup) { self.class.setups.each { |s| instance_eval(&s) } }
|
|
||||||
setups << block
|
|
||||||
end
|
|
||||||
def self.setups; @setups ||= [] end
|
|
||||||
def self.teardown(&block) define_method(:teardown, &block) end
|
|
||||||
end
|
|
||||||
(class << klass; self end).send(:define_method, :name) { name.gsub(/\W/,'_') }
|
|
||||||
klass.class_eval do
|
|
||||||
include Mongo
|
|
||||||
end
|
|
||||||
klass.class_eval &block
|
|
||||||
end
|
|
||||||
|
|
|
@ -1,58 +1,61 @@
|
||||||
require 'test/test_helper'
|
require 'test/test_helper'
|
||||||
|
|
||||||
context "Basic operations: " do
|
class CollectionTest < Test::Unit::TestCase
|
||||||
setup do
|
|
||||||
@logger = mock()
|
|
||||||
end
|
|
||||||
|
|
||||||
should "send update message" do
|
context "Basic operations: " do
|
||||||
@conn = Connection.new('localhost', 27017, :logger => @logger, :connect => false)
|
setup do
|
||||||
@db = @conn['testing']
|
@logger = mock()
|
||||||
@coll = @db.collection('books')
|
|
||||||
@conn.expects(:send_message).with do |op, msg, log|
|
|
||||||
op == 2001 && log.include?("db.books.update")
|
|
||||||
end
|
end
|
||||||
@coll.update({}, {:title => 'Moby Dick'})
|
|
||||||
end
|
|
||||||
|
|
||||||
should "send insert message" do
|
should "send update message" do
|
||||||
@conn = Connection.new('localhost', 27017, :logger => @logger, :connect => false)
|
@conn = Connection.new('localhost', 27017, :logger => @logger, :connect => false)
|
||||||
@db = @conn['testing']
|
@db = @conn['testing']
|
||||||
@coll = @db.collection('books')
|
@coll = @db.collection('books')
|
||||||
@conn.expects(:send_message).with do |op, msg, log|
|
@conn.expects(:send_message).with do |op, msg, log|
|
||||||
op == 2002 && log.include?("db.books.insert")
|
op == 2001 && log.include?("db.books.update")
|
||||||
|
end
|
||||||
|
@coll.update({}, {:title => 'Moby Dick'})
|
||||||
end
|
end
|
||||||
@coll.insert({:title => 'Moby Dick'})
|
|
||||||
end
|
|
||||||
|
|
||||||
should "not log binary data" do
|
should "send insert message" do
|
||||||
@conn = Connection.new('localhost', 27017, :logger => @logger, :connect => false)
|
@conn = Connection.new('localhost', 27017, :logger => @logger, :connect => false)
|
||||||
@db = @conn['testing']
|
@db = @conn['testing']
|
||||||
@coll = @db.collection('books')
|
@coll = @db.collection('books')
|
||||||
data = Mongo::Binary.new(("BINARY " * 1000).unpack("c*"))
|
@conn.expects(:send_message).with do |op, msg, log|
|
||||||
@conn.expects(:send_message).with do |op, msg, log|
|
op == 2002 && log.include?("db.books.insert")
|
||||||
op == 2002 && log.include?("Mongo::Binary")
|
end
|
||||||
|
@coll.insert({:title => 'Moby Dick'})
|
||||||
end
|
end
|
||||||
@coll.insert({:data => data})
|
|
||||||
end
|
|
||||||
|
|
||||||
should "send safe update message" do
|
should "not log binary data" do
|
||||||
@conn = Connection.new('localhost', 27017, :logger => @logger, :connect => false)
|
@conn = Connection.new('localhost', 27017, :logger => @logger, :connect => false)
|
||||||
@db = @conn['testing']
|
@db = @conn['testing']
|
||||||
@coll = @db.collection('books')
|
@coll = @db.collection('books')
|
||||||
@conn.expects(:send_message_with_safe_check).with do |op, msg, db_name, log|
|
data = Mongo::Binary.new(("BINARY " * 1000).unpack("c*"))
|
||||||
op == 2001 && log.include?("db.books.update")
|
@conn.expects(:send_message).with do |op, msg, log|
|
||||||
|
op == 2002 && log.include?("Mongo::Binary")
|
||||||
|
end
|
||||||
|
@coll.insert({:data => data})
|
||||||
end
|
end
|
||||||
@coll.update({}, {:title => 'Moby Dick'}, :safe => true)
|
|
||||||
end
|
|
||||||
|
|
||||||
should "send safe insert message" do
|
should "send safe update message" do
|
||||||
@conn = Connection.new('localhost', 27017, :logger => @logger, :connect => false)
|
@conn = Connection.new('localhost', 27017, :logger => @logger, :connect => false)
|
||||||
@db = @conn['testing']
|
@db = @conn['testing']
|
||||||
@coll = @db.collection('books')
|
@coll = @db.collection('books')
|
||||||
@conn.expects(:send_message_with_safe_check).with do |op, msg, db_name, log|
|
@conn.expects(:send_message_with_safe_check).with do |op, msg, db_name, log|
|
||||||
op == 2001 && log.include?("db.books.update")
|
op == 2001 && log.include?("db.books.update")
|
||||||
|
end
|
||||||
|
@coll.update({}, {:title => 'Moby Dick'}, :safe => true)
|
||||||
|
end
|
||||||
|
|
||||||
|
should "send safe insert message" do
|
||||||
|
@conn = Connection.new('localhost', 27017, :logger => @logger, :connect => false)
|
||||||
|
@db = @conn['testing']
|
||||||
|
@coll = @db.collection('books')
|
||||||
|
@conn.expects(:send_message_with_safe_check).with do |op, msg, db_name, log|
|
||||||
|
op == 2001 && log.include?("db.books.update")
|
||||||
|
end
|
||||||
|
@coll.update({}, {:title => 'Moby Dick'}, :safe => true)
|
||||||
end
|
end
|
||||||
@coll.update({}, {:title => 'Moby Dick'}, :safe => true)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,114 +1,116 @@
|
||||||
require 'test/test_helper'
|
require 'test/test_helper'
|
||||||
include Mongo
|
include Mongo
|
||||||
|
|
||||||
context "Initialization: " do
|
class ConnectionTest < Test::Unit::TestCase
|
||||||
setup do
|
context "Initialization: " do
|
||||||
def new_mock_socket
|
|
||||||
socket = Object.new
|
|
||||||
socket.stubs(:setsockopt).with(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
|
|
||||||
socket
|
|
||||||
end
|
|
||||||
|
|
||||||
def new_mock_db
|
|
||||||
db = Object.new
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context "given a single node" do
|
|
||||||
setup do
|
setup do
|
||||||
TCPSocket.stubs(:new).returns(new_mock_socket)
|
def new_mock_socket
|
||||||
@conn = Connection.new('localhost', 27017, :connect => false)
|
socket = Object.new
|
||||||
|
socket.stubs(:setsockopt).with(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
|
||||||
admin_db = new_mock_db
|
socket
|
||||||
admin_db.expects(:command).returns({'ok' => 1, 'ismaster' => 1})
|
|
||||||
@conn.expects(:[]).with('admin').returns(admin_db)
|
|
||||||
@conn.connect_to_master
|
|
||||||
end
|
|
||||||
|
|
||||||
should "set localhost and port to master" do
|
|
||||||
assert_equal 'localhost', @conn.host
|
|
||||||
assert_equal 27017, @conn.port
|
|
||||||
end
|
|
||||||
|
|
||||||
should "set connection pool to 1" do
|
|
||||||
assert_equal 1, @conn.size
|
|
||||||
end
|
|
||||||
|
|
||||||
should "default slave_ok to false" do
|
|
||||||
assert !@conn.slave_ok?
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context "initializing a paired connection" do
|
|
||||||
should "require left and right nodes" do
|
|
||||||
assert_raise MongoArgumentError do
|
|
||||||
Connection.paired(['localhost', 27018], :connect => false)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
assert_raise MongoArgumentError do
|
def new_mock_db
|
||||||
Connection.paired(['localhost', 27018], :connect => false)
|
db = Object.new
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
should "store both nodes" do
|
context "given a single node" do
|
||||||
@conn = Connection.paired([['localhost', 27017], ['localhost', 27018]], :connect => false)
|
setup do
|
||||||
|
TCPSocket.stubs(:new).returns(new_mock_socket)
|
||||||
|
@conn = Connection.new('localhost', 27017, :connect => false)
|
||||||
|
|
||||||
assert_equal ['localhost', 27017], @conn.nodes[0]
|
admin_db = new_mock_db
|
||||||
assert_equal ['localhost', 27018], @conn.nodes[1]
|
admin_db.expects(:command).returns({'ok' => 1, 'ismaster' => 1})
|
||||||
end
|
@conn.expects(:[]).with('admin').returns(admin_db)
|
||||||
end
|
@conn.connect_to_master
|
||||||
|
|
||||||
context "initializing with a mongodb uri" do
|
|
||||||
should "parse a simple uri" do
|
|
||||||
@conn = Connection.from_uri("mongodb://localhost", :connect => false)
|
|
||||||
assert_equal ['localhost', 27017], @conn.nodes[0]
|
|
||||||
end
|
|
||||||
|
|
||||||
should "parse a uri specifying multiple nodes" do
|
|
||||||
@conn = Connection.from_uri("mongodb://localhost:27017,mydb.com:27018", :connect => false)
|
|
||||||
assert_equal ['localhost', 27017], @conn.nodes[0]
|
|
||||||
assert_equal ['mydb.com', 27018], @conn.nodes[1]
|
|
||||||
end
|
|
||||||
|
|
||||||
should "parse a uri specifying multiple nodes with auth" do
|
|
||||||
@conn = Connection.from_uri("mongodb://kyle:s3cr3t@localhost:27017/app,mickey:m0u5e@mydb.com:27018/dsny", :connect => false)
|
|
||||||
assert_equal ['localhost', 27017], @conn.nodes[0]
|
|
||||||
assert_equal ['mydb.com', 27018], @conn.nodes[1]
|
|
||||||
assert_equal ['kyle', 's3cr3t', 'app'], @conn.auths[0]
|
|
||||||
assert_equal ['mickey', 'm0u5e', 'dsny'], @conn.auths[1]
|
|
||||||
end
|
|
||||||
|
|
||||||
should "attempt to connect" do
|
|
||||||
TCPSocket.stubs(:new).returns(new_mock_socket)
|
|
||||||
@conn = Connection.from_uri("mongodb://localhost", :connect => false)
|
|
||||||
|
|
||||||
admin_db = new_mock_db
|
|
||||||
admin_db.expects(:command).returns({'ok' => 1, 'ismaster' => 1})
|
|
||||||
@conn.expects(:[]).with('admin').returns(admin_db)
|
|
||||||
@conn.connect_to_master
|
|
||||||
end
|
|
||||||
|
|
||||||
should "raise an error on invalid uris" do
|
|
||||||
assert_raise MongoArgumentError do
|
|
||||||
Connection.from_uri("mongo://localhost", :connect => false)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
assert_raise MongoArgumentError do
|
should "set localhost and port to master" do
|
||||||
Connection.from_uri("mongodb://localhost:abc", :connect => false)
|
assert_equal 'localhost', @conn.host
|
||||||
|
assert_equal 27017, @conn.port
|
||||||
end
|
end
|
||||||
|
|
||||||
assert_raise MongoArgumentError do
|
should "set connection pool to 1" do
|
||||||
Connection.from_uri("mongodb://localhost:27017, my.db.com:27018, ", :connect => false)
|
assert_equal 1, @conn.size
|
||||||
|
end
|
||||||
|
|
||||||
|
should "default slave_ok to false" do
|
||||||
|
assert !@conn.slave_ok?
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
should "require all of username, password, and database if any one is specified" do
|
context "initializing a paired connection" do
|
||||||
assert_raise MongoArgumentError do
|
should "require left and right nodes" do
|
||||||
Connection.from_uri("mongodb://localhost/db", :connect => false)
|
assert_raise MongoArgumentError do
|
||||||
|
Connection.paired(['localhost', 27018], :connect => false)
|
||||||
|
end
|
||||||
|
|
||||||
|
assert_raise MongoArgumentError do
|
||||||
|
Connection.paired(['localhost', 27018], :connect => false)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
assert_raise MongoArgumentError do
|
should "store both nodes" do
|
||||||
Connection.from_uri("mongodb://kyle:password@localhost", :connect => false)
|
@conn = Connection.paired([['localhost', 27017], ['localhost', 27018]], :connect => false)
|
||||||
|
|
||||||
|
assert_equal ['localhost', 27017], @conn.nodes[0]
|
||||||
|
assert_equal ['localhost', 27018], @conn.nodes[1]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "initializing with a mongodb uri" do
|
||||||
|
should "parse a simple uri" do
|
||||||
|
@conn = Connection.from_uri("mongodb://localhost", :connect => false)
|
||||||
|
assert_equal ['localhost', 27017], @conn.nodes[0]
|
||||||
|
end
|
||||||
|
|
||||||
|
should "parse a uri specifying multiple nodes" do
|
||||||
|
@conn = Connection.from_uri("mongodb://localhost:27017,mydb.com:27018", :connect => false)
|
||||||
|
assert_equal ['localhost', 27017], @conn.nodes[0]
|
||||||
|
assert_equal ['mydb.com', 27018], @conn.nodes[1]
|
||||||
|
end
|
||||||
|
|
||||||
|
should "parse a uri specifying multiple nodes with auth" do
|
||||||
|
@conn = Connection.from_uri("mongodb://kyle:s3cr3t@localhost:27017/app,mickey:m0u5e@mydb.com:27018/dsny", :connect => false)
|
||||||
|
assert_equal ['localhost', 27017], @conn.nodes[0]
|
||||||
|
assert_equal ['mydb.com', 27018], @conn.nodes[1]
|
||||||
|
assert_equal ['kyle', 's3cr3t', 'app'], @conn.auths[0]
|
||||||
|
assert_equal ['mickey', 'm0u5e', 'dsny'], @conn.auths[1]
|
||||||
|
end
|
||||||
|
|
||||||
|
should "attempt to connect" do
|
||||||
|
TCPSocket.stubs(:new).returns(new_mock_socket)
|
||||||
|
@conn = Connection.from_uri("mongodb://localhost", :connect => false)
|
||||||
|
|
||||||
|
admin_db = new_mock_db
|
||||||
|
admin_db.expects(:command).returns({'ok' => 1, 'ismaster' => 1})
|
||||||
|
@conn.expects(:[]).with('admin').returns(admin_db)
|
||||||
|
@conn.connect_to_master
|
||||||
|
end
|
||||||
|
|
||||||
|
should "raise an error on invalid uris" do
|
||||||
|
assert_raise MongoArgumentError do
|
||||||
|
Connection.from_uri("mongo://localhost", :connect => false)
|
||||||
|
end
|
||||||
|
|
||||||
|
assert_raise MongoArgumentError do
|
||||||
|
Connection.from_uri("mongodb://localhost:abc", :connect => false)
|
||||||
|
end
|
||||||
|
|
||||||
|
assert_raise MongoArgumentError do
|
||||||
|
Connection.from_uri("mongodb://localhost:27017, my.db.com:27018, ", :connect => false)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
should "require all of username, password, and database if any one is specified" do
|
||||||
|
assert_raise MongoArgumentError do
|
||||||
|
Connection.from_uri("mongodb://localhost/db", :connect => false)
|
||||||
|
end
|
||||||
|
|
||||||
|
assert_raise MongoArgumentError do
|
||||||
|
Connection.from_uri("mongodb://kyle:password@localhost", :connect => false)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,91 +1,93 @@
|
||||||
require 'test/test_helper'
|
require 'test/test_helper'
|
||||||
|
|
||||||
context "Cursor options" do
|
class CursorTest < Test::Unit::TestCase
|
||||||
setup do
|
context "Cursor options" do
|
||||||
@connection = stub(:class => Connection)
|
setup do
|
||||||
@db = stub(:name => "testing", :slave_ok? => false, :connection => @connection)
|
@connection = stub(:class => Connection)
|
||||||
@collection = stub(:db => @db, :name => "items")
|
@db = stub(:name => "testing", :slave_ok? => false, :connection => @connection)
|
||||||
@cursor = Cursor.new(@collection)
|
@collection = stub(:db => @db, :name => "items")
|
||||||
|
@cursor = Cursor.new(@collection)
|
||||||
|
end
|
||||||
|
|
||||||
|
should "set admin to false" do
|
||||||
|
assert_equal false, @cursor.admin
|
||||||
|
|
||||||
|
@cursor = Cursor.new(@collection, :admin => true)
|
||||||
|
assert_equal true, @cursor.admin
|
||||||
|
end
|
||||||
|
|
||||||
|
should "set selector" do
|
||||||
|
assert @cursor.selector == {}
|
||||||
|
|
||||||
|
@cursor = Cursor.new(@collection, :selector => {:name => "Jones"})
|
||||||
|
assert @cursor.selector == {:name => "Jones"}
|
||||||
|
end
|
||||||
|
|
||||||
|
should "set fields" do
|
||||||
|
assert_nil @cursor.fields
|
||||||
|
|
||||||
|
@cursor = Cursor.new(@collection, :fields => [:name, :date])
|
||||||
|
assert @cursor.fields == {:name => 1, :date => 1}
|
||||||
|
end
|
||||||
|
|
||||||
|
should "set limit" do
|
||||||
|
assert_equal 0, @cursor.limit
|
||||||
|
|
||||||
|
@cursor = Cursor.new(@collection, :limit => 10)
|
||||||
|
assert_equal 10, @cursor.limit
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
should "set skip" do
|
||||||
|
assert_equal 0, @cursor.skip
|
||||||
|
|
||||||
|
@cursor = Cursor.new(@collection, :skip => 5)
|
||||||
|
assert_equal 5, @cursor.skip
|
||||||
|
end
|
||||||
|
|
||||||
|
should "set sort order" do
|
||||||
|
assert_nil @cursor.order
|
||||||
|
|
||||||
|
@cursor = Cursor.new(@collection, :order => "last_name")
|
||||||
|
assert_equal "last_name", @cursor.order
|
||||||
|
end
|
||||||
|
|
||||||
|
should "set hint" do
|
||||||
|
assert_nil @cursor.hint
|
||||||
|
|
||||||
|
@cursor = Cursor.new(@collection, :hint => "name")
|
||||||
|
assert_equal "name", @cursor.hint
|
||||||
|
end
|
||||||
|
|
||||||
|
should "cache full collection name" do
|
||||||
|
assert_equal "testing.items", @cursor.full_collection_name
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
should "set admin to false" do
|
context "Query fields" do
|
||||||
assert_equal false, @cursor.admin
|
setup do
|
||||||
|
@connection = stub(:class => Collection)
|
||||||
|
@db = stub(:slave_ok? => true, :name => "testing", :connection => @connection)
|
||||||
|
@collection = stub(:db => @db, :name => "items")
|
||||||
|
end
|
||||||
|
|
||||||
@cursor = Cursor.new(@collection, :admin => true)
|
should "when an array should return a hash with each key" do
|
||||||
assert_equal true, @cursor.admin
|
@cursor = Cursor.new(@collection, :fields => [:name, :age])
|
||||||
end
|
result = @cursor.fields
|
||||||
|
assert_equal result.keys.sort{|a,b| a.to_s <=> b.to_s}, [:age, :name].sort{|a,b| a.to_s <=> b.to_s}
|
||||||
|
assert result.values.all? {|v| v == 1}
|
||||||
|
end
|
||||||
|
|
||||||
should "set selector" do
|
should "when a string, return a hash with just the key" do
|
||||||
assert @cursor.selector == {}
|
@cursor = Cursor.new(@collection, :fields => "name")
|
||||||
|
result = @cursor.fields
|
||||||
|
assert_equal result.keys.sort, ["name"]
|
||||||
|
assert result.values.all? {|v| v == 1}
|
||||||
|
end
|
||||||
|
|
||||||
@cursor = Cursor.new(@collection, :selector => {:name => "Jones"})
|
should "return nil when neither hash nor string nor symbol" do
|
||||||
assert @cursor.selector == {:name => "Jones"}
|
@cursor = Cursor.new(@collection, :fields => 1234567)
|
||||||
end
|
assert_nil @cursor.fields
|
||||||
|
end
|
||||||
should "set fields" do
|
|
||||||
assert_nil @cursor.fields
|
|
||||||
|
|
||||||
@cursor = Cursor.new(@collection, :fields => [:name, :date])
|
|
||||||
assert @cursor.fields == {:name => 1, :date => 1}
|
|
||||||
end
|
|
||||||
|
|
||||||
should "set limit" do
|
|
||||||
assert_equal 0, @cursor.limit
|
|
||||||
|
|
||||||
@cursor = Cursor.new(@collection, :limit => 10)
|
|
||||||
assert_equal 10, @cursor.limit
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
should "set skip" do
|
|
||||||
assert_equal 0, @cursor.skip
|
|
||||||
|
|
||||||
@cursor = Cursor.new(@collection, :skip => 5)
|
|
||||||
assert_equal 5, @cursor.skip
|
|
||||||
end
|
|
||||||
|
|
||||||
should "set sort order" do
|
|
||||||
assert_nil @cursor.order
|
|
||||||
|
|
||||||
@cursor = Cursor.new(@collection, :order => "last_name")
|
|
||||||
assert_equal "last_name", @cursor.order
|
|
||||||
end
|
|
||||||
|
|
||||||
should "set hint" do
|
|
||||||
assert_nil @cursor.hint
|
|
||||||
|
|
||||||
@cursor = Cursor.new(@collection, :hint => "name")
|
|
||||||
assert_equal "name", @cursor.hint
|
|
||||||
end
|
|
||||||
|
|
||||||
should "cache full collection name" do
|
|
||||||
assert_equal "testing.items", @cursor.full_collection_name
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context "Query fields" do
|
|
||||||
setup do
|
|
||||||
@connection = stub(:class => Collection)
|
|
||||||
@db = stub(:slave_ok? => true, :name => "testing", :connection => @connection)
|
|
||||||
@collection = stub(:db => @db, :name => "items")
|
|
||||||
end
|
|
||||||
|
|
||||||
should "when an array should return a hash with each key" do
|
|
||||||
@cursor = Cursor.new(@collection, :fields => [:name, :age])
|
|
||||||
result = @cursor.fields
|
|
||||||
assert_equal result.keys.sort{|a,b| a.to_s <=> b.to_s}, [:age, :name].sort{|a,b| a.to_s <=> b.to_s}
|
|
||||||
assert result.values.all? {|v| v == 1}
|
|
||||||
end
|
|
||||||
|
|
||||||
should "when a string, return a hash with just the key" do
|
|
||||||
@cursor = Cursor.new(@collection, :fields => "name")
|
|
||||||
result = @cursor.fields
|
|
||||||
assert_equal result.keys.sort, ["name"]
|
|
||||||
assert result.values.all? {|v| v == 1}
|
|
||||||
end
|
|
||||||
|
|
||||||
should "return nil when neither hash nor string nor symbol" do
|
|
||||||
@cursor = Cursor.new(@collection, :fields => 1234567)
|
|
||||||
assert_nil @cursor.fields
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,98 +1,98 @@
|
||||||
require 'test/test_helper'
|
require 'test/test_helper'
|
||||||
|
|
||||||
context "DBTest: " do
|
class DBTest < Test::Unit::TestCase
|
||||||
setup do
|
context "DBTest: " do
|
||||||
def insert_message(db, documents)
|
|
||||||
documents = [documents] unless documents.is_a?(Array)
|
|
||||||
message = ByteBuffer.new
|
|
||||||
message.put_int(0)
|
|
||||||
BSON.serialize_cstr(message, "#{db.name}.test")
|
|
||||||
documents.each { |doc| message.put_array(BSON.new.serialize(doc, true).to_a) }
|
|
||||||
message = db.add_message_headers(Mongo::Constants::OP_INSERT, message)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context "DB commands" do
|
|
||||||
setup do
|
setup do
|
||||||
@conn = stub()
|
def insert_message(db, documents)
|
||||||
@db = DB.new("testing", @conn)
|
documents = [documents] unless documents.is_a?(Array)
|
||||||
@collection = mock()
|
message = ByteBuffer.new
|
||||||
@db.stubs(:system_command_collection).returns(@collection)
|
message.put_int(0)
|
||||||
end
|
BSON.serialize_cstr(message, "#{db.name}.test")
|
||||||
|
documents.each { |doc| message.put_array(BSON.new.serialize(doc, true).to_a) }
|
||||||
should "raise an error if given a hash with more than one key" do
|
message = db.add_message_headers(Mongo::Constants::OP_INSERT, message)
|
||||||
assert_raise MongoArgumentError do
|
|
||||||
@db.command(:buildinfo => 1, :somekey => 1)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
should "raise an error if the selector is omitted" do
|
context "DB commands" do
|
||||||
assert_raise MongoArgumentError do
|
setup do
|
||||||
@db.command({}, true)
|
@conn = stub()
|
||||||
|
@db = DB.new("testing", @conn)
|
||||||
|
@collection = mock()
|
||||||
|
@db.stubs(:system_command_collection).returns(@collection)
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
should "create the proper cursor" do
|
should "raise an error if given a hash with more than one key" do
|
||||||
@cursor = mock(:next_document => {"ok" => 1})
|
assert_raise MongoArgumentError do
|
||||||
Cursor.expects(:new).with(@collection, :admin => true,
|
@db.command(:buildinfo => 1, :somekey => 1)
|
||||||
:limit => -1, :selector => {:buildinfo => 1}, :socket => nil).returns(@cursor)
|
end
|
||||||
command = {:buildinfo => 1}
|
end
|
||||||
@db.command(command, true)
|
|
||||||
end
|
|
||||||
|
|
||||||
should "raise an error when the command fails" do
|
should "raise an error if the selector is omitted" do
|
||||||
@cursor = mock(:next_document => {"ok" => 0})
|
assert_raise MongoArgumentError do
|
||||||
Cursor.expects(:new).with(@collection, :admin => true,
|
@db.command({}, true)
|
||||||
:limit => -1, :selector => {:buildinfo => 1}, :socket => nil).returns(@cursor)
|
end
|
||||||
assert_raise OperationFailure do
|
end
|
||||||
|
|
||||||
|
should "create the proper cursor" do
|
||||||
|
@cursor = mock(:next_document => {"ok" => 1})
|
||||||
|
Cursor.expects(:new).with(@collection, :admin => true,
|
||||||
|
:limit => -1, :selector => {:buildinfo => 1}, :socket => nil).returns(@cursor)
|
||||||
command = {:buildinfo => 1}
|
command = {:buildinfo => 1}
|
||||||
@db.command(command, true, true)
|
@db.command(command, true)
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
should "raise an error if logging out fails" do
|
should "raise an error when the command fails" do
|
||||||
@db.expects(:command).returns({})
|
@cursor = mock(:next_document => {"ok" => 0})
|
||||||
assert_raise MongoDBError do
|
Cursor.expects(:new).with(@collection, :admin => true,
|
||||||
@db.logout
|
:limit => -1, :selector => {:buildinfo => 1}, :socket => nil).returns(@cursor)
|
||||||
|
assert_raise OperationFailure do
|
||||||
|
command = {:buildinfo => 1}
|
||||||
|
@db.command(command, true, true)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
should "raise an error if collection creation fails" do
|
should "raise an error if logging out fails" do
|
||||||
@db.expects(:collection_names).returns([])
|
@db.expects(:command).returns({})
|
||||||
@db.expects(:command).returns({})
|
assert_raise MongoDBError do
|
||||||
assert_raise MongoDBError do
|
@db.logout
|
||||||
@db.create_collection("foo")
|
end
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
should "raise an error if getlasterror fails" do
|
should "raise an error if collection creation fails" do
|
||||||
@db.expects(:command).returns({})
|
@db.expects(:collection_names).returns([])
|
||||||
assert_raise MongoDBError do
|
@db.expects(:command).returns({})
|
||||||
@db.error
|
assert_raise MongoDBError do
|
||||||
|
@db.create_collection("foo")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
should "raise an error if rename fails" do
|
should "raise an error if getlasterror fails" do
|
||||||
@db.expects(:command).returns({})
|
@db.expects(:command).returns({})
|
||||||
assert_raise MongoDBError do
|
assert_raise MongoDBError do
|
||||||
@db.rename_collection("foo", "bar")
|
@db.error
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
should "raise an error if drop_index fails" do
|
should "raise an error if rename fails" do
|
||||||
@db.expects(:command).returns({})
|
@db.expects(:command).returns({})
|
||||||
assert_raise MongoDBError do
|
assert_raise MongoDBError do
|
||||||
@db.drop_index("foo", "bar")
|
@db.rename_collection("foo", "bar")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
should "raise an error if set_profiling_level fails" do
|
should "raise an error if drop_index fails" do
|
||||||
@db.expects(:command).returns({})
|
@db.expects(:command).returns({})
|
||||||
assert_raise MongoDBError do
|
assert_raise MongoDBError do
|
||||||
@db.profiling_level = :slow_only
|
@db.drop_index("foo", "bar")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
should "raise an error if set_profiling_level fails" do
|
||||||
|
@db.expects(:command).returns({})
|
||||||
|
assert_raise MongoDBError do
|
||||||
|
@db.profiling_level = :slow_only
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue