Back to Repositories

Testing Rack Integration and File Upload Handling in Grape

This integration test suite validates core Rack functionality in the Grape API framework, focusing on file upload handling and API endpoint mounting. The tests ensure proper handling of temporary file uploads and verify correct namespace mounting behavior.

Test Coverage Overview

The test suite covers two critical integration aspects of Grape’s Rack implementation:

  • File upload processing through Rack’s multipart handling
  • API mounting within namespaced contexts
  • Response validation for file metadata and content
  • Proper cleanup of temporary resources

Implementation Analysis

The testing approach utilizes RSpec’s behavior-driven development patterns with context-specific test organization. The implementation leverages Grape’s API class inheritance and configuration features, demonstrating both file handling and routing capabilities.

Key patterns include:
  • Subject/let block usage for test setup
  • Dynamic class creation for isolated test contexts
  • Rack::Test::UploadedFile for multipart testing

Technical Details

Testing tools and configuration:
  • RSpec for test framework
  • Rack::Test for HTTP request simulation
  • Tempfile for temporary file management
  • JSON format for response handling
  • Grape API class inheritance
  • Namespace-based routing

Best Practices Demonstrated

The test suite exemplifies several testing best practices:

  • Proper resource cleanup after file operations
  • Isolated test contexts using dynamic class creation
  • Clear separation of setup and assertions
  • Comprehensive parameter validation
  • Structured test organization using describe/context blocks

ruby-grape/grape

spec/grape/integration/rack_spec.rb

            
# frozen_string_literal: true

describe Rack do
  describe 'from a Tempfile' do
    subject { last_response.body }

    let(:app) do
      Class.new(Grape::API) do
        format :json

        params do
          requires :file, type: File
        end

        post do
          params[:file].then do |file|
            {
              filename: file[:filename],
              type: file[:type],
              content: file[:tempfile].read
            }
          end
        end
      end
    end

    let(:response_body) do
      {
        filename: File.basename(tempfile.path),
        type: 'text/plain',
        content: 'rubbish'
      }.to_json
    end

    let(:tempfile) do
      Tempfile.new.tap do |t|
        t.write('rubbish')
        t.rewind
      end
    end

    before do
      post '/', file: Rack::Test::UploadedFile.new(tempfile.path, 'text/plain')
    end

    it 'correctly populates params from a Tempfile' do
      expect(subject).to eq(response_body)
    ensure
      tempfile.close!
    end
  end

  context 'when the app is mounted' do
    let(:ping_mount) do
      Class.new(Grape::API) do
        get 'ping'
      end
    end

    let(:app) do
      app_to_mount = ping_mount
      Class.new(Grape::API) do
        namespace 'namespace' do
          mount app_to_mount
        end
      end
    end

    it 'finds the app on the namespace' do
      get '/namespace/ping'
      expect(last_response).to be_successful
    end
  end
end