457 lines · 11.7 KB · Ruby
RawDownload
require_relative '../../../test_helper'require_relative '../../../../lib/docs' class DocsAbstractStoreTest < Minitest::Spec  InvalidPathError = Docs::AbstractStore::InvalidPathError  LockError = Docs::AbstractStore::LockError   let :path do    '/'  end   let :store do    Docs::AbstractStore.new(@path || path).tap do |store|      store.extend FakeInstrumentation    end  end   describe ".new" do    it "raises an error with a relative path" do      assert_raises ArgumentError do        Docs::AbstractStore.new 'path'      end    end     it "sets #root_path" do      @path = '/path'      assert_equal @path, store.root_path    end     it "expands #root_path" do      @path = '/path/..'      assert_equal '/', store.root_path    end     it "sets #working_path" do      assert_equal store.root_path, store.working_path    end  end   describe "#root_path" do    it "can't be overwritten" do      @path = '/path'      store.root_path << '/..'      assert_equal '/path', store.root_path    end  end   describe "#working_path" do    it "can't be overwritten" do      @path = '/path'      store.working_path << '/..'      assert_equal '/path', store.working_path    end  end   describe "#open" do    it "raises an error when the store is locked" do      assert_raises LockError do        store.send :lock, &-> { store.open 'dir' }      end    end     context "with a relative path" do      it "updates #working_path relative to #root_path" do        2.times { store.open 'dir' }        assert_equal File.join(path, 'dir'), store.working_path      end       it "expands the new #working_path" do        store.open './dir/../'        assert_equal path, store.working_path      end       it "raises an error when the new #working_path is outside of #root_path" do        @path = '/dir'        assert_raises InvalidPathError do          store.open '../dir2'        end      end    end     context "with an absolute path" do      it "updates #working_path" do        store.open File.join(path, 'dir')        assert_equal File.join(path, 'dir'), store.working_path      end       it "expands the new #working_path" do        store.open File.join(path, 'dir/..')        assert_equal path, store.working_path      end       it "raises an error when the new #working_path is outside of #root_path" do        @path = '/dir'        assert_raises InvalidPathError do          store.open '/dir2'        end      end    end     context "with a block" do      it "calls the block" do        store.open('dir') { @called = true }        assert @called      end       it "returns the block's return value" do        assert_equal 1, store.open('dir') { 1 }      end       it "updates #working_path while calling the block" do        store.open 'dir' do          assert_equal File.join(path, 'dir'), store.working_path        end      end       it "resets #working_path to its previous value afterward" do        store.open('dir')        store.open('dir2') {}        assert_equal File.join(path, 'dir'), store.working_path      end       it "resets #working_path even when the block fails" do        assert_raises RuntimeError do          store.open('dir') { raise }        end        assert_equal path, store.working_path      end    end  end   describe "#close" do    it "resets #working_path to #root_path" do      2.times { store.open 'dir' }      store.close      assert_equal path, store.working_path    end     it "raises an error when the store is locked" do      assert_raises LockError do        store.send :lock, &-> { store.close }      end    end  end   describe "#expand_path" do    context "when #working_path is '/'" do      before do        store.open '/'      end       it "returns '/path' with './path'" do        assert_equal '/path', store.expand_path('./path')      end       it "returns '/path' with '/path'" do        assert_equal '/path', store.expand_path('/path')      end    end     context "when #working_path is '/dir'" do      before do        store.open '/dir'      end       it "returns '/dir/path' with './path'" do        assert_equal '/dir/path', store.expand_path('./path')      end       it "returns '/dir/path' with 'path/../path'" do        assert_equal '/dir/path', store.expand_path('path/../path')      end       it "returns '/dir/path' with '/dir/path'" do        assert_equal '/dir/path', store.expand_path('/dir/path')      end       it "raises an error with '..'" do        assert_raises InvalidPathError do          store.expand_path '..'        end      end       it "raises an error with '/'" do        assert_raises InvalidPathError do          store.expand_path '/'        end      end    end  end   describe "#read" do    it "raises an error with a path outside of #working_path" do      @path = '/path'      assert_raises InvalidPathError do        store.read '../file'      end    end     it "returns nil when the file doesn't exist" do      dont_allow(store).read_file      stub(store).file_exist?('/file') { false }      assert_nil store.read('file')    end     it "returns #read_file when the file exists" do      stub(store).read_file('/file') { 1 }      stub(store).file_exist?('/file') { true }      assert_equal 1, store.read('file')    end  end   describe "#write" do    it "raises an error with a path outside of #working_path" do      @path = '/path'      assert_raises InvalidPathError do        store.write '../file', ''      end    end     context "when the file doesn't exist" do      before do        stub(store).file_exist?('/file') { false }        stub(store).create_file      end       it "returns #create_file" do        stub(store).create_file('/file', '') { 1 }        assert_equal 1, store.write('file', '')      end       it "instrument 'create'" do        store.write 'file', ''        assert store.last_instrumentation        assert_equal 'create.store', store.last_instrumentation[:event]        assert_equal '/file', store.last_instrumentation[:payload][:path]      end    end     context "when the file exists" do      before do        stub(store).file_exist?('/file') { true }        stub(store).update_file      end       it "returns #update_file" do        stub(store).update_file('/file', '') { 1 }        assert_equal 1, store.write('file', '')      end       it "instruments 'update'" do        store.write 'file', ''        assert store.last_instrumentation        assert_equal 'update.store', store.last_instrumentation[:event]        assert_equal '/file', store.last_instrumentation[:payload][:path]      end    end  end   describe "#delete" do    it "raises an error with a path outside og #working_path" do      @path = '/path'      assert_raises InvalidPathError do        store.delete '../file'      end    end     it "returns nil when the file doesn't exist" do      dont_allow(store).delete_file      stub(store).file_exist?('/file') { false }      assert_nil store.delete('file')    end     context "when the file exists" do      before do        stub(store).file_exist?('/file') { true }        stub(store).delete_file      end       it "calls #delete_file" do        mock(store).delete_file('/file')        store.delete 'file'      end       it "returns true" do        assert store.delete('file')      end       it "instruments 'destroy'" do        store.delete 'file'        assert store.last_instrumentation        assert_equal 'destroy.store', store.last_instrumentation[:event]        assert_equal '/file', store.last_instrumentation[:payload][:path]      end    end  end   describe "exist?" do    it "raises an error with a path outside of #working_path" do      @path = '/path'      assert_raises InvalidPathError do        store.exist? '../file'      end    end     it "returns #file_exist?" do      stub(store).file_exist?('/file') { 1 }      assert_equal 1, store.exist?('file')    end  end   describe "mtime" do    it "raises an error with a path outside of #working_path" do      @path = '/path'      assert_raises InvalidPathError do        store.mtime '../file'      end    end     it "returns nil when the file doesn't exist" do      stub(store).file_exist?('/file') { false }      dont_allow(store).file_mtime      assert_nil store.mtime('file')    end     it "returns #file_mtime when the file exists" do      stub(store).file_exist?('/file') { true }      stub(store).file_mtime('/file') { 1 }      assert_equal 1, store.mtime('file')    end  end   describe "#size" do    it "raises an error with a path outside of #working_path" do      @path = '/path'      assert_raises InvalidPathError do        store.size '../file'      end    end     it "returns nil when the file doesn't exist" do      stub(store).file_exist?('/file') { false }      dont_allow(store).file_size      assert_nil store.size('file')    end     it "returns #file_size when the file exists" do      stub(store).file_exist?('/file') { true }      stub(store).file_size('/file') { 1 }      assert_equal 1, store.size('file')    end  end   describe "#each" do    it "calls #list_files with #working_path" do      store.open 'dir'      block = Proc.new {}      mock(store).list_files(File.join(path, 'dir'), &block)      store.each(&block)    end  end   describe "#replace" do    before do      stub(store).file_exist?      stub(store).create_file      stub(store).delete_file    end     def stub_paths(*paths)      stub(store).each { |&block| paths.each(&block) }    end     it "calls the block" do      store.replace { @called = true }      assert @called    end     it "returns the block's return value" do      assert_equal 1, store.replace { 1 }    end     it "locks the store while calling the block" do      assert_raises LockError do        store.replace { store.open('dir') }      end      store.open 'dir'    end     context "with a path" do      it "opens the path while calling the block" do        store.replace 'dir' do          assert_equal File.join(path, 'dir'), store.working_path        end      end    end     context "when the block writes no files" do      it "doesn't delete files" do        stub_paths '/', '/file'        dont_allow(store).delete_file        store.replace {}      end    end     context "when the block writes files" do      it "deletes untouched files" do        stub_paths '/', '/dir', '/dir/file', '/dir/file2', '/dir2'        mock(store).delete_file('/dir/file2').then.delete_file('/dir2')        store.replace { store.write 'dir/file', '' }      end       it "doesn't delete touched files" do        stub_paths '/', '/dir', '/dir/(file)'        dont_allow(store).delete_file        store.replace { store.write 'dir/(file)', '' }      end    end     context "when the block fails" do      it "doesn't delete files" do        stub_paths '/', '/file'        dont_allow(store).delete_file        assert_raises RuntimeError do          store.replace { store.write 'file2', ''; raise }        end      end       it "unlocks the store afterward" do        assert_raises RuntimeError do          store.replace { raise }        end        store.open 'dir'      end    end     context "when called multiple times" do      before do        stub_paths '/', '/file'      end       it "deletes untouched files that were touched the previous time" do        store.replace { store.write 'file', '' }        mock(store).delete_file '/file'        store.replace { store.write 'file2', '' }      end       it "deletes untouched files that were touched and failed the previous time" do        assert_raises RuntimeError do          store.replace { store.write 'file', ''; raise }        end        mock(store).delete_file '/file'        store.replace { store.write 'file2', '' }      end    end  endend