我已经通过Ruby on Rails项目进行迁移,以便在CircleCi 2.0上运行测试。执行此操作后,与文件上载相关的某些规范将失败。我有以下型号:
SlideAudio:
class SlideAudio < ApplicationRecord
# == Extensions ============================================================
Paperclip.interpolates :locale do |attachment, style|
attachment.instance.locale
end
# Tell paperclip that :slide_id means SlideAudio#slide_id
# This is used in the custom path and url below when we define the attachment
Paperclip.interpolates :slide_id do |attachment, style|
attachment.instance.slide_id
end
has_attached_file :audio,
path: ":rails_root/public/slides/:attachment/:style/:locale/:slide_id.:extension",
url: "/slides/:attachment/:style/:locale/:slide_id.:extension",
processors: [:audio_compression],
styles: {
original: {},
compressed_96k: {bitrate: '96k'}
}
# == Associations ==========================================================
belongs_to :slide, optional: true
# == Validations ===========================================================
validates_attachment :audio, content_type: {
content_type: [
'audio/mpeg',
'audio/x-mpeg',
'audio/mp3',
'audio/x-mp3',
'audio/mpeg3',
'audio/x-mpeg3',
'audio/mpg',
'audio/x-mpg',
'audio/x-mpegaudio'
]
}
# == Instance Methods ======================================================
def has_a_valid_audio_file?
return false if audio.blank?
# Before a slide_audio is saved, the audio file lives in the tmp directory.
if audio.queued_for_write.present? && audio.queued_for_write[:original].present?
temporary_path = audio.queued_for_write[:original].path
File.exists?(temporary_path)
# After a slide is saved, Paperclip copies that file to "path".
elsif audio.path.present?
File.exists?(audio.path)
else
false
end
end
end
幻灯片:
class Slide < ApplicationRecord
has_many :audios, class_name: 'SlideAudio'
end
我还有一个负责压缩音频的课程:
module Paperclip
class AudioCompression < Processor
def make
original_filepath = @file.path
if should_skip_compression?
# If no compression is needed, we can't just return the original file (@file)
# because Paperclip will remove it during cleanup.
# That's why we need to copy it and return that copy.
new_filepath = original_filepath.gsub('.mp3', '-copy.mp3')
FileUtils.cp(original_filepath, new_filepath)
return File.open(new_filepath)
end
desired_bitrate = options[:bitrate] # This is formatted as a string that ffmpeg accepts, e.g. "96k"
Paperclip::AudioCompression.compress(original_filepath, desired_bitrate)
end
protected
# This method is not required by Paperclip processors - it is our "helper" method.
# If the original bitrate is not significantly higher than the desired bitrate,
# we won't try to compress, because we would lose sound quality and wouldn't gain much.
def should_skip_compression?
return true if options[:style] == :original # Do not compress original files.
desired_bitrate = options[:bitrate] # This is formatted as a string that ffmpeg accepts, e.g. "96k"
desired_bitrate_in_kbs = desired_bitrate.to_i # This is a number, e.g. 96.
original_audio_info = `ffmpeg -i #{@file.path} 2>&1`
original_bitrate = original_audio_info.match(/(?:bitrate: )(\d+) (?:kb\/s)$/)&.captures&.first
original_bitrate_in_kbs = original_bitrate.to_i
upper_acceptable_bitrate_limit_in_kbs = desired_bitrate_in_kbs + 5
original_bitrate_in_kbs < upper_acceptable_bitrate_limit_in_kbs
end
def self.compress(original_filepath, desired_bitrate)
# This file doesn't exist yet, we're just constructing the filepath.
compressed_filepath = original_filepath.gsub('.mp3', "-#{desired_bitrate}.mp3")
# Use ffmpeg to compress the original file.
# The files are usually uploaded to a temporary location, such as /var/folders.
# The flags used:
# -y: If there's a temporary file at compressed_filepath, overwrite it.
# -i: Specifies the input filepath.
# -ab: Specifies the desired bitrate.
Paperclip.run('ffmpeg', "-y -i #{original_filepath} -ab #{desired_bitrate} #{compressed_filepath}")
File.open(compressed_filepath)
end
end
end
我的测试看起来像这样:
require 'rails_helper'
RSpec.describe SlideAudio, type: :model do
describe 'Instance Methods' do
let(:slide) { create(:slide) }
let(:audio) { slide.audios.create(locale: 'en') }
let(:audio_file_128k) do
Rack::Test::UploadedFile.new(
'spec/fixtures/audios/test-128k.mp3',
'audio/mpeg')
end
let(:audio_file_96k) do
Rack::Test::UploadedFile.new(
'spec/fixtures/audios/test-96k.mp3',
'audio/mpeg')
end
describe '#audio=' do
it 'should compress mp3 files to 96 kbps if their bitrate is too high' do
audio.audio = audio_file_128k
audio.save!
audio_info = `ffmpeg -i #{audio.audio.path(:compressed_96k)} 2>&1`
bitrate_info = audio_info[/bitrate:.*$/]
expect(bitrate_info).to eq('bitrate: 96 kb/s')
end
end
end
end
并且输出失败:
Failure/Error: expect(bitrate_info).to eq('bitrate: 96 kb/s')
expected: "bitrate: 96 kb/s"
got: nil
(compared using ==)
任何想法我该如何解决这个问题?这是我的circle.yml文件:
version: 2
environment:
TZ: "/usr/share/zoneinfo/America/Los_Angeles"
jobs:
build:
parallelism: 2
working_directory: ~/circleci-survey-builder
docker:
- image: circleci/ruby:2.3.4
environment:
PGHOST: 127.0.0.1
PGUSER: ubuntu
RAILS_ENV: test
- image: circleci/postgres:9.6-alpine
environment:
POSTGRES_USER: ubuntu
POSTGRES_DB: circle_test
POSTGRES_PASSWORD: ''
steps:
- checkout
- run:
name: 'Install CircleCI dependencies'
command: bash deploy/circle-dependencies.sh
- type: cache-restore
key: survey-builder-{{ checksum "Gemfile.lock" }}
- run:
name: 'Install dependencies'
command: bundle install --path vendor/bundle
- type: cache-save
key: survey-builder-{{ checksum "Gemfile.lock" }}
paths:
- vendor/bundle
- run:
name: 'Create database.yml'
command: mv config/database.ci.yml config/database.yml
- run:
name: Set up SurveyBuilder database
command: bundle exec rake db:structure:load --trace
- run:
name: Set up Dashboard database
command: |
createdb dashboard_test
psql -p 5432 dashboard_test < db/dashboard_test.sql
- run:
name: Set up foreign data wrapper
command: psql -p 5432 circle_test < db/test_foreign_data_wrapper.sql
- run:
name: 'Run tests'
command: |
bundle exec rspec --format progress \
$(circleci tests glob "spec/**/*_spec.rb" | circleci tests split --split-by=timings)