Back to Repositories

Testing Cloud Storage Integration in CarrierWave

This test suite validates CarrierWave’s Fog storage integration, focusing on file handling and cloud storage provider compatibility. It ensures reliable file uploads across different storage services while maintaining proper filename and extension handling.

Test Coverage Overview

The test suite provides comprehensive coverage of CarrierWave’s Fog storage implementation, focusing on:
  • Cloud storage provider integration testing
  • Connection caching and eager loading functionality
  • Filename parsing and handling across different scenarios
  • Extension processing for various file types
  • Multi-byte character support in filenames

Implementation Analysis

The testing approach utilizes RSpec’s shared examples to validate storage functionality across different cloud providers. The implementation employs mock objects for local testing while supporting remote testing through environment variables. Key patterns include isolation of provider-specific tests and comprehensive filename parsing logic.

Technical Details

Testing tools and configuration:
  • RSpec as the testing framework
  • Fog gem for cloud storage abstraction
  • Support for AWS, Google, and Rackspace providers
  • Mock/Live testing toggle via environment variables
  • Dedicated credential management

Best Practices Demonstrated

The test suite exemplifies several testing best practices:
  • Isolation of provider-specific credentials
  • Comprehensive edge case handling
  • Clear test organization using RSpec contexts
  • Proper mocking and stubbing techniques
  • Modular test structure with shared examples

carrierwaveuploader/carrierwave

spec/storage/fog_spec.rb

            
require 'spec_helper'
require 'fog/aws'
require 'fog/google'
require 'fog/local'
begin
  require 'fog/rackspace'
rescue LoadError
  # Can be missing in CI
end

unless ENV['REMOTE'] == 'true'
  Fog.mock!
end

require_relative './fog_credentials' # after Fog.mock!
require_relative './fog_helper'

describe CarrierWave::Storage::Fog do
  FOG_CREDENTIALS.each do |credential|
    include_examples 'Fog storage', credential
  end

  describe '.eager_load' do
    after do
      CarrierWave::Storage::Fog.connection_cache.clear
      CarrierWave::Uploader::Base.fog_credentials = nil
    end

    it "caches Fog::Storage instance" do
      CarrierWave::Uploader::Base.fog_credentials = {
        provider: 'AWS', aws_access_key_id: 'foo', aws_secret_access_key: 'bar'
      }
      expect { CarrierWave::Storage::Fog.eager_load }.
        to change { CarrierWave::Storage::Fog.connection_cache }
    end

    it "does nothing when fog_credentials is empty" do
      CarrierWave::Uploader::Base.fog_credentials = {}
      expect { CarrierWave::Storage::Fog.eager_load }.
        not_to change { CarrierWave::Storage::Fog.connection_cache }
    end
  end

  describe CarrierWave::Storage::Fog::File do
    subject(:file) { CarrierWave::Storage::Fog::File.new(nil, nil, nil) }

    describe "#filename" do
      subject(:filename) { file.filename }

      before { allow(file).to receive(:url).and_return(url) }

      context "with normal url" do
        let(:url) { 'http://example.com/path/to/foo.txt' }

        it "extracts filename from url" do
          is_expected.to eq('foo.txt')
        end
      end

      context "when url contains '/' in query string" do
        let(:url){ 'http://example.com/path/to/foo.txt?bar=baz/fubar' }

        it "extracts correct part" do
          is_expected.to eq('foo.txt')
        end
      end

      context "when url contains multi-byte characters" do
        let(:url) { 'http://example.com/path/to/%E6%97%A5%E6%9C%AC%E8%AA%9E.txt' }

        it "decodes multi-byte characters" do
          is_expected.to eq('日本語.txt')
        end
      end
    end

    describe "#basename" do
      subject(:basename) { file.basename }

      before { allow(file).to receive(:filename).and_return(filename) }

      context "when file has complicated extensions" do
        let(:filename) { "complex.filename.tar.gz" }

        it "return correct basename" do
          is_expected.to eq("complex.filename")
        end
      end

      context "when file has simple extension" do
        let(:filename) { "simple.extension" }

        it "return correct basename" do
          is_expected.to eq("simple")
        end
      end

      context "when file has no extension" do
        let(:filename) { "filename" }

        it "return correct basename" do
          is_expected.to eq("filename")
        end
      end
    end

    describe "#extension" do
      subject(:extension) { file.extension }

      before { allow(file).to receive(:filename).and_return(filename) }

      %w[gz bz2 z lz xz].each do |ext|
        context "when file has complicated extensions (tar.#{ext})" do
          let(:filename) { "complex.filename.tar.#{ext}" }

          it "return correct extension" do
            is_expected.to eq("tar.#{ext}")
          end
        end
      end

      context "when file has simple extension" do
        let(:filename) { "simple.extension" }

        it "return correct extension" do
          is_expected.to eq("extension")
        end
      end

      context "when file has no extension" do
        let(:filename) { "filename" }

        it "return correct extension" do
          is_expected.to eq("")
        end
      end
    end
  end
end