- Fix some C rdoc so it is parsed correctly. - Fill out transaction testing. - Populate docs for DB options. FossilOrigin-Name: f54dbfacf2dda100a116fdcc856ca5231e249f23238ca9d4355618e3a380a8f8
227 lines
5 KiB
Ruby
227 lines
5 KiB
Ruby
#!/usr/bin/env rspec -cfd
|
|
# vim: set nosta noet ts=4 sw=4 ft=ruby:
|
|
|
|
require_relative '../lib/helper'
|
|
|
|
|
|
RSpec.describe( MDBX::Database ) do
|
|
|
|
after( :all ) do
|
|
db = described_class.open( TEST_DATABASE.to_s )
|
|
db.clear
|
|
db.close
|
|
end
|
|
|
|
it "disallows direct calls to #new" do
|
|
expect{ described_class.new }.
|
|
to raise_exception( NoMethodError, /private/ )
|
|
end
|
|
|
|
it "knows the env handle open/close state" do
|
|
db = described_class.open( TEST_DATABASE.to_s )
|
|
expect( db.closed? ).to be_falsey
|
|
db.close
|
|
expect( db.closed? ).to be_truthy
|
|
end
|
|
|
|
it "closes itself automatically when used in block form" do
|
|
db = described_class.open( TEST_DATABASE.to_s ) do |db|
|
|
expect( db.closed? ).to be_falsey
|
|
end
|
|
expect( db.closed? ).to be_truthy
|
|
end
|
|
|
|
|
|
context 'an opened database' do
|
|
|
|
let!( :db ) { described_class.open( TEST_DATABASE.to_s ) }
|
|
|
|
after( :each ) do
|
|
db.close
|
|
end
|
|
|
|
it "can be reopened" do
|
|
db.close
|
|
expect( db ).to be_closed
|
|
db.reopen
|
|
expect( db ).to_not be_closed
|
|
end
|
|
|
|
it "knows its own path" do
|
|
expect( db.path ).to match( %r|tmp/testdb$| )
|
|
end
|
|
|
|
it "fails if opened again within the same process" do
|
|
# This is a function of libmdbx internals, just testing
|
|
# here for behavior.
|
|
expect {
|
|
described_class.open( TEST_DATABASE.to_s )
|
|
}.
|
|
to raise_exception( MDBX::DatabaseError, /environment is already used/ )
|
|
end
|
|
|
|
it "can remove a key by setting its value to nil" do
|
|
db[ 'test' ] = "hi"
|
|
expect( db['test'] ).to eq( 'hi' )
|
|
|
|
db[ 'test' ] = nil
|
|
expect( db['test'] ).to be_nil
|
|
end
|
|
|
|
it "can return an array of its keys" do
|
|
db[ 'key1' ] = true
|
|
db[ 'key2' ] = true
|
|
db[ 'key3' ] = true
|
|
expect( db.keys ).to include( 'key1', 'key2', 'key3' )
|
|
end
|
|
end
|
|
|
|
|
|
context 'collections' do
|
|
|
|
let!( :db ) { described_class.open( TEST_DATABASE.to_s, max_collections: 5 ) }
|
|
|
|
after( :each ) do
|
|
db.close
|
|
end
|
|
|
|
it "fail if the max_collections option is not specified when opening" do
|
|
db.close
|
|
db = described_class.open( TEST_DATABASE.to_s )
|
|
expect{
|
|
db.collection( 'bucket' )
|
|
} .to raise_exception( /not enabled/ )
|
|
end
|
|
|
|
it "disallows regular key/val storage for namespace keys" do
|
|
db.collection( 'bucket' )
|
|
db[ 'okay' ] = 1
|
|
db.collection( nil )
|
|
|
|
expect{ db['bucket'] = 1 }.to raise_exception( /MDBX_INCOMPATIBLE/ )
|
|
end
|
|
|
|
it "defaults to the top-level namespace" do
|
|
expect( db.collection ).to be_nil
|
|
end
|
|
|
|
it "can set a collection namespace" do
|
|
expect( db.collection ).to be_nil
|
|
db[ 'key' ] = true
|
|
|
|
db.collection( 'bucket' )
|
|
expect( db.collection ).to eq( 'bucket' )
|
|
db[ 'key' ] = false
|
|
|
|
db.main
|
|
expect( db.collection ).to be_nil
|
|
expect( db['key'] ).to be_truthy
|
|
end
|
|
|
|
it "revert back to the previous collection when used in a block" do
|
|
expect( db.collection ).to be_nil
|
|
db.collection( 'bucket' ) { 'no-op' }
|
|
expect( db.collection ).to be_nil
|
|
end
|
|
|
|
it "can be cleared of contents" do
|
|
db.collection( 'bucket' )
|
|
10.times {|i| db[i] = true }
|
|
expect( db[2] ).to be_truthy
|
|
|
|
db.clear
|
|
db.main
|
|
expect( db['bucket'] ).to be_nil
|
|
end
|
|
end
|
|
|
|
|
|
context 'transactions' do
|
|
|
|
let!( :db ) { described_class.open( TEST_DATABASE.to_s, max_collections: 5 ) }
|
|
|
|
after( :each ) do
|
|
db.close
|
|
end
|
|
|
|
it "knows when a transaction is currently open" do
|
|
expect( db.in_transaction? ).to be_falsey
|
|
db.snapshot
|
|
expect( db.in_transaction? ).to be_truthy
|
|
db.abort
|
|
expect( db.in_transaction? ).to be_falsey
|
|
|
|
db.snapshot do
|
|
expect( db.in_transaction? ).to be_truthy
|
|
end
|
|
expect( db.in_transaction? ).to be_falsey
|
|
end
|
|
|
|
it "throws an error if changing collection mid-transaction" do
|
|
db.snapshot do
|
|
expect{ db.collection('nope') }.
|
|
to raise_exception( MDBX::DatabaseError, /transaction open/ )
|
|
end
|
|
end
|
|
|
|
it "are re-entrant" do
|
|
3.times { db.snapshot }
|
|
expect( db.in_transaction? ).to be_truthy
|
|
end
|
|
|
|
it "refuse to allow writes for read-only snapshots" do
|
|
db.snapshot
|
|
expect{ db[1] = true }.
|
|
to raise_exception( MDBX::DatabaseError, /permission denied/i )
|
|
end
|
|
|
|
it "revert changes via explicit rollbacks" do
|
|
db[ 1 ] = true
|
|
db.transaction
|
|
db[ 1 ] = false
|
|
expect( db[ 1 ] ).to be_falsey
|
|
db.rollback
|
|
expect( db[ 1 ] ).to be_truthy
|
|
end
|
|
|
|
it "revert changes via uncaught exceptions" do
|
|
db[ 1 ] = true
|
|
expect {
|
|
db.transaction do
|
|
db[ 1 ] = false
|
|
raise "boom"
|
|
end
|
|
}.to raise_exception( RuntimeError, "boom" )
|
|
expect( db[ 1 ] ).to be_truthy
|
|
end
|
|
|
|
it "revert changes via explicit exceptions" do
|
|
db[ 1 ] = true
|
|
expect {
|
|
db.transaction do
|
|
db[ 1 ] = false
|
|
raise MDBX::Rollback, "boom!"
|
|
end
|
|
}.to_not raise_exception
|
|
expect( db[ 1 ] ).to be_truthy
|
|
end
|
|
|
|
it "write changes after commit" do
|
|
db[ 1 ] = true
|
|
db.transaction
|
|
db[ 1 ] = false
|
|
expect( db[ 1 ] ).to be_falsey
|
|
db.commit
|
|
expect( db[ 1 ] ).to be_falsey
|
|
end
|
|
|
|
it "automatically write changes after block" do
|
|
db[ 1 ] = true
|
|
db.transaction do
|
|
db[ 1 ] = false
|
|
end
|
|
expect( db[ 1 ] ).to be_falsey
|
|
end
|
|
end
|
|
end
|
|
|