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.
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
                        If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With