Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Testing a controller which includes ActionController::Live

I have a controller which includes the Rails ActionController::Live module. I am displaying the contents of a logfile, which is being read using FileTail gem, and using SSE from ActionController::Live like so:

class LogsController < ApplicationController
  include ActionController::Live

  def live
    response.headers['Content-Type'] = 'text/event-stream'
    sse = SSE.new(response.stream, event: 'queries')
    File.open(logfile_location) do |log|
      log.extend(File::Tail)
      log.interval = 1
      log.backward(10)
      log.tail {|line| sse.write line}
    end
  rescue => e
    Rails.logger.info "Error Message:: #{e.message}"
  ensure
    sse.close
  end
end

I want to test the live action using Rspec. This is what I currently have:

before { get :live }
it { expect(response.headers['Content-Type']).to eq("text/event-stream") }
after {response.stream.close unless response.stream.closed? }

if I don't have the line with after in place, the spec passes but it just keeps on listening and the connection is never closed, hence the specs never finish and you have to kill them manually.

If I have the after line, it passes sometimes, but most of the times it throws an exception, and the specs fail.

fatal:
   No live threads left. Deadlock?

Is there a way I can make this work? Maybe there's a specific way this has to be tested which am unable to find anywhere.

like image 817
Gaurav Manchanda Avatar asked Oct 07 '16 19:10

Gaurav Manchanda


1 Answers

There are potentially two problems in your application that stops you to test your code.

1) You are using old version of rails that does not include this fix(rails 5+ includes it)

2) Obviously, you are using File::Tail in the way when your code runs without point of exit. I mean that this:

File.open(logfile_location) do |log|
  log.extend(File::Tail)
  log.interval = 1
  log.backward(10)
  log.tail {|line| sse.write line}
end

runs forever.

So, you need simply stub File::Tail part that is responsible for running the code forever or modify your code accordingly(log.return_if_eof = Rails.env.test? will help).

Tested on rails 5.2.2. This code works:

describe "#live" do
  subject { process :live, method: :get }  

  its(:body) { is_expected.to include(file_content) } # file_content is content of your file
  its(:body) { is_expected.to include('event: queries') }
  its(:headers) { is_expected.to match(hash_including('Content-Type' => 'text/event-stream')) }
end
like image 143
intale Avatar answered Nov 01 '22 14:11

intale