brew/Library/Homebrew/test/cache_store_spec.rb
Mike McQuaid a11fe57cd2
cache_store: handle corrupt DBM database.
When the DBM database cannot be read by the current version of Ruby's
DBM library (due to corruption or another incompatibility) it segfaults
or freezes which takes down the entire Homebrew Ruby process.

This isn't desirable so instead perform a shell out with the Homebrew
Ruby to see if it can read the DBM database before we try to use the
information. If this hangs or crashes: silently delete the database and
recreate it.
2018-09-20 10:57:27 +01:00

148 lines
4.1 KiB
Ruby

require "cache_store"
describe CacheStoreDatabase do
subject { CacheStoreDatabase.new(:sample) }
describe "self.use" do
let(:type) { :test }
it "creates a new `DatabaseCache` instance" do
cache_store = double("cache_store", close_if_open!: nil)
expect(CacheStoreDatabase).to receive(:new).with(type).and_return(cache_store)
expect(cache_store).to receive(:close_if_open!)
CacheStoreDatabase.use(type) { |_db| }
end
end
describe "#set" do
let(:db) { double("db", :[]= => nil) }
before(:each) do
allow(File).to receive(:write)
allow(subject).to receive(:created?).and_return(true)
expect(db).to receive(:has_key?).with(:foo).and_return(false)
allow(subject).to receive(:db).and_return(db)
end
it "sets the value in the `CacheStoreDatabase`" do
expect(db).to_not have_key(:foo)
subject.set(:foo, "bar")
end
end
describe "#get" do
context "database created" do
let(:db) { double("db", :[] => "bar") }
before(:each) do
allow(subject).to receive(:created?).and_return(true)
expect(db).to receive(:has_key?).with(:foo).and_return(true)
allow(subject).to receive(:db).and_return(db)
end
it "gets value in the `CacheStoreDatabase` corresponding to the key" do
expect(db).to have_key(:foo)
expect(subject.get(:foo)).to eq("bar")
end
end
context "database not created" do
let(:db) { double("db", :[] => nil) }
before(:each) do
allow(subject).to receive(:created?).and_return(false)
allow(subject).to receive(:db).and_return(db)
end
it "does not get value in the `CacheStoreDatabase` corresponding to key" do
expect(subject.get(:foo)).to_not be("bar")
end
it "does not call `db[]` if `CacheStoreDatabase.created?` is `false`" do
expect(db).not_to receive(:[])
subject.get(:foo)
end
end
end
describe "#delete" do
context "database created" do
let(:db) { double("db", :[] => { foo: "bar" }) }
before(:each) do
allow(subject).to receive(:created?).and_return(true)
allow(subject).to receive(:db).and_return(db)
end
it "deletes value in the `CacheStoreDatabase` corresponding to the key" do
expect(db).to receive(:delete).with(:foo)
subject.delete(:foo)
end
end
context "database not created" do
let(:db) { double("db", delete: nil) }
before(:each) do
allow(subject).to receive(:created?).and_return(false)
allow(subject).to receive(:db).and_return(db)
end
it "does not call `db.delete` if `CacheStoreDatabase.created?` is `false`" do
expect(db).not_to receive(:delete)
subject.delete(:foo)
end
end
end
describe "#close_if_open!" do
context "database open" do
before(:each) do
subject.instance_variable_set(:@db, instance_double(DBM, close: nil))
end
it "does not raise an error when `close` is called on the database" do
expect { subject.close_if_open! }.to_not raise_error(NoMethodError)
end
end
context "database not open" do
before(:each) do
subject.instance_variable_set(:@db, nil)
end
it "does not raise an error when `close` is called on the database" do
expect { subject.close_if_open! }.to_not raise_error(NoMethodError)
end
end
end
describe "#created?" do
let(:cache_path) { Pathname("path/to/homebrew/cache/sample.db") }
before(:each) do
allow(subject).to receive(:cache_path).and_return(cache_path)
end
context "`cache_path.exist?` returns `true`" do
before(:each) do
allow(cache_path).to receive(:exist?).and_return(true)
end
it "returns `true`" do
expect(subject.created?).to be(true)
end
end
context "`cache_path.exist?` returns `false`" do
before(:each) do
allow(cache_path).to receive(:exist?).and_return(false)
end
it "returns `false`" do
expect(subject.created?).to be(false)
end
end
end
end