Rails RSpec 第二个 ssh 请求的 net-ssh mock return

Rails RSpec net-ssh mock return of second ssh request

我正在开发一个经常访问远程服务器上的模拟数据的 Web 应用程序。我想为这些请求期间可能发生的错误处理创建测试。

我目前遇到的问题是我似乎无法使用 ssh_with_stderr 方法模拟请求。 ssh 方法工作正常。

这是我要测试的代码:

# app/jobs/zip_files_sync_job.rb

class ZipFilesSyncJob < ApplicationJob
  queue_as :default
  discard_on ActiveJob::DeserializationError

  def perform(simulation)
    simulation.zip_files.each do |f|
      if f.path.nil? && f.created_at < 6.hours.ago
        f.state = 'error'
        f.save!
        next
      end
      next if f.path.nil?

      _, errors = simulation.server.ssh_with_stderr("ls #{f.path.shellescape}")
      if errors.blank?
        f.size = f.simulation.server.ssh("stat -c %s #{f.path.shellescape}")
        f.state = 'ready' if f.size.to_i.positive?
      elsif f.state == 'ready' && errors.present?
        f.state = 'error'
      elsif f.state == 'zipping' && errors.present? && f.created_at < 6.hours.ago
        f.state = 'error'
      end
      f.save!
    end
  end
end

这就是我要测试的内容:

# spec/jobs/zip_files_sync_job_spec.rb

require 'rails_helper'

RSpec.describe ZipFilesSyncJob, type: :job do
  let(:private_group) { Group::PRIVATE }
  let(:user) { FactoryBot.create :user }
  let(:server) { FactoryBot.create :server, user: user, external_storage: false }
  let(:simulation) { FactoryBot.create :simulation, user: user, group: private_group, server: server }
  let(:zip_file) { FactoryBot.create :zip_file, simulation: simulation, path: 'test/zip_file', state: 'pending', size: '100' }
  let(:zip_file_no_path) { FactoryBot.create :zip_file, simulation: simulation, path: nil, created_at: 10.hours.ago, state: 'pending' }
  let(:ssh_connection) { double('net-ssh') }

  before do
    zip_file_no_path
    allow(Net::SSH).to receive(:start).and_yield(ssh_connection)
  end

  def perform_zip_file_sync(zip_file)
    perform_enqueued_jobs do
      ZipFilesSyncJob.perform_now(simulation)
    end
    zip_file.reload

    yield

    allow(Net::SSH).to receive(:start).and_call_original
  end

  describe '#perform' do
    include ActiveJob::TestHelper

   #################################
   ##### This test works fine #####
   #################################

    context 'with no errors' do
      before do
        zip_file
      end
      it 'it will change the state to ready' do
        allow(Net::SSH).to receive(:start).and_return('144371201')
        perform_zip_file_sync(zip_file) do
          expect(zip_file.state).to eq 'ready'
        end
      end
    end

   #############################################################################
   ##### This test fails because it does not return on the ssh_with_stderr #####
   #############################################################################

    context 'with errors' do
      it 'will change the state to error' do
        allow(Net::SSH).to receive(:start).and_return("[' ', 'Error with connection']")
        perform_enqueued_jobs do
          ZipFilesSyncJob.perform_now(simulation)
        end
        zip_file.reload
        expect(zip_file.state).to eq 'error'
      end
    end
  end
end

这是服务器连接的代码。它使用 net-ssh gem

# app/models/server.rb

Class Server < ApplicationRecord

  def ssh(command, storage = true, &block)
    Net::SSH.start(hostname, username, port: port, keys: ["key"], non_interactive: true, timeout: 1) do |ssh|
      ssh.exec! "cd #{folder.shellescape}; #{command}", &block
    end
  end

  def ssh_with_stderr(command)
    @output = ""
    @errors = ""
    begin
      Net::SSH.start(hostname, username, port: port, keys: ["key"], non_interactive: true, timeout: 1) do |ssh|
        ssh.exec! "cd #{folder.shellescape}; #{command}" do |_ch, stream, data|
          if stream == :stderr
            @errors += data
          else
            @output += data
          end
        end
      end
    rescue Net::SSH::Exception, Errno::ECONNREFUSED, Errno::EINVAL, Errno::EADDRNOTAVAIL => e
      @output = nil
      @errors = e.message
     end
    [@output, @errors]
  end

有了这个模拟

allow(Net::SSH).to receive(:start).and_return("[' ', 'Error with connection']")

ssh_with_stderr看起来像

def ssh_with_stderr(command)
  @output = ""
  @errors = ""
  begin
    [' ', 'Error with connection']
  rescue Net::SSH::Exception, Errno::ECONNREFUSED, Errno::EINVAL, Errno::EADDRNOTAVAIL => e
    @output = nil
    @errors = e.message
   end
  [@output, @errors]
end 

所以它总是 returns ["",""] ,并且检查 errors.blank? 总是积极的。

尝试用 and_raise 而不是 and_return 模拟 Net::SSH,比如

allow(Net::SSH).to receive(:start).and_raise(Errno::ECONNREFUSED, "Error with connection")