Skip to content

Instantly share code, notes, and snippets.

@rokumatsumoto
Last active September 14, 2023 22:00
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rokumatsumoto/e5f2e9ac027e2c8e2f88a3e6a5a5065d to your computer and use it in GitHub Desktop.
Save rokumatsumoto/e5f2e9ac027e2c8e2f88a3e6a5a5065d to your computer and use it in GitHub Desktop.
Stubbing the AWS SDK S3 (bucket.object, bucket.object.exists? methods and bucket.object.exists? responses)
# Gemfile
gem 'aws-sdk-s3'
# config/initializers/aws.rb
credentials = Aws::Credentials.new(
Boyutluseyler.config[:direct_upload_access_key_id],
Boyutluseyler.config[:direct_upload_secret_access_key]
)
# https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/S3/Resource.html
DIRECT_UPLOAD_RESOURCE = Aws::S3::Resource.new(
stub_responses: Rails.env.test?,
region: Boyutluseyler.config[:direct_upload_region],
credentials: credentials
)
DIRECT_UPLOAD_BUCKET = DIRECT_UPLOAD_RESOURCE.bucket(Boyutluseyler.config[:direct_upload_bucket_name])
# spec/support/helpers/stub_aws_responses.rb
# https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/S3/Resource.html
# https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/S3/Client.html
# Stubbing the AWS SDK
# https://docs.aws.amazon.com/sdk-for-ruby/v3/developer-guide/stubbing.html
# https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/ClientStubs.html
# https://aws.amazon.com/blogs/developer/advanced-client-stubbing-in-the-aws-sdk-for-ruby-version-3/
# https://stelligent.com/2016/04/14/using-stubs-for-the-aws-sdk-for-ruby-2/
# https://semaphoreci.com/community/tutorials/stubbing-the-aws-sdk
module StubAWSResponses
def stub_direct_upload_bucket_object_is_not_exist
DIRECT_UPLOAD_RESOURCE.client.stub_responses(:head_object,
status_code: 404,
headers: {},
body: '')
end
def stub_direct_upload_bucket_object_exists
DIRECT_UPLOAD_RESOURCE.client.stub_responses(:head_object,
status_code: 200,
headers: {},
body: '')
end
end
# spec/spec_helper.rb
RSpec.configure do |config|
config.include StubAWSResponses
end
# app/services/blueprints/build_service.rb
# frozen_string_literal: true
module Blueprints
class BuildService < Blueprints::BaseService
prepend ValidatesRemoteObject
def execute
validate!
Blueprint.new.tap do |b|
b.url = remote_object.public_url
b.url_path = remote_object.key
b.size = remote_object.size
b.content_type = remote_object.content_type
b.thumb_url = ''
end
end
private
def validate!
raise ValidationError, 'File not found' unless remote_object_exists?
end
def bucket
ObjectStorage::DirectUpload::Bucket
end
end
end
# app/services/concerns/validates_remote_object.rb
# frozen_string_literal: true
module ValidatesRemoteObject
# https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/S3/Object.html#exists%3F-instance_method
delegate :exists?, to: :remote_object, prefix: true
def remote_object
@remote_object ||= bucket.object(key)
end
def bucket
super || (raise NotImplementedError,
'"bucket" must be implemented and return an Aws::S3::Bucket')
end
def key
params[:key] || (raise ArgumentError, 'Key needs to be specified')
end
end
# lib/object_storage/direct_upload/bucket.rb
# frozen_string_literal: true
# Wrapper class for AWS::S3::Bucket
require_relative 'presigned_post'
module ObjectStorage
module DirectUpload
class Bucket
class << self
delegate :object, to: :bucket
def bucket
@bucket ||= DIRECT_UPLOAD_BUCKET
end
def presigned_post(policy)
PresignedPost.new(bucket, policy).create
end
end
end
end
end
# spec/services/blueprints/build_service_spec.rb
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Blueprints::BuildService do
describe 'modules' do
it { is_expected.to include_module(ValidatesRemoteObject) }
end
describe '#execute' do
subject(:service) { described_class.new(user, params) }
let(:user) { build_stubbed(:user) }
let(:params) { { key: 'model.stl' } }
let(:validation_error) { described_class::ValidationError }
context 'when the remote object is not exist' do
before do
stub_direct_upload_bucket_object_is_not_exist
end
after do
stub_direct_upload_bucket_object_exists # default stub behavior
end
it 'raises a file not found error' do
expect { service.execute }.to raise_error(validation_error, 'File not found')
end
end
context 'when the remote object exists' do
it 'builds a blueprint without saving it' do
direct_upload_bucket = class_double(ObjectStorage::DirectUpload::Bucket).as_stubbed_const
# stubbing Aws::S3::Object
remote_object = double(key: 'model.stl', size: 1, content_type: 'model/stl',
public_url: 'http://foo.com/model.stl', exists?: true)
allow(direct_upload_bucket).to receive(:object).and_return(remote_object)
result = service.execute
expect(result).to be_valid
expect(result).not_to be_persisted
end
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment