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