Rspec STDIN gets - ruby

How can I use gets method inside a test?
I want to write an interactive spec, in my spec I login to a site, which asks confirmation sms. I don't know that sms code before I run spec, that's why I enter sms code during test run.
When I try to do something like sms = gets.chomp
I get the following error:
Errno::ENOENT:
No such file or directory - spec/login/login_spec.rb

In your spec you want to use $stdin. Your code should look something like this:
it "sends an SMS and verifies it" do
SMSVerifier.send_verification_code(test_phone_number)
print "Enter the sms code you received: "
code = $stdin.gets.chomp
SMSVerifier.check_verification_code(test_phone_number, code).should == true
end

As a matter of principle, rspec, and unit-tests in general should not be interactive. By actually sending an SMS in your test you need to:
Have the relevant phone at hand
Wait for the SMS to arrive (how long before you decide it fails?)
Read the SMS and fill it (correctly!) back in your running test.
This means that your spec is not automated, and that instead of running it tens of times a day (as unit-tests were meant to be run), you will run it once a week, if at all, because it would be such a pain to run.
Leave live SMS tests to system tests, and unit-test this functionality by stubbing the actual send behavior, and check the received parameters:
it "sends an SMS and verifies it" do
sent_text = nil
expect(SMSSender).to receive(:send).with(test_phone_number, an_instance_of(String)) do |num, text|
sent_text = text
end
SMSVerifier.send_verification_code(test_phone_number)
SMSVerifier.check_verification_code(test_phone_number, sent_text).should be_true
end

Related

RSpec testing of a multiprocess library

I'm trying to test a gem I'm creating with RSpec. The gem's purpose is to create queues (using 'bunny'). It will serve to communicate between processes on several servers.
But I cannot find documentation on how to safely create processes inside RSpec running environment without spawning several testing processes (all displaying example failures and successes).
Here is what I wanted the tests to do :
Spawn children processes, waiting on the queue
Push messages from the main RSpec process
Consumes the queue on the children processes
Wait for children to stop and get the number of messages received from each child.
For now I implemented a simple case where child is consuming only one message and then stops.
Here is my code currently :
module Queues
# Basic CR accepting only jobs of type cmd_line
class CR
attr_reader :nb_jobs
def initialize
# opening communication pipes
#rout, #wout = IO.pipe
#nb_jobs = nil # not yet available.
end
def main
#todo = JobPipe.instance
job = #todo.pop do |j|
# accept only CMD_LINE type of jobs.
j.type == Messages::Job::CMD_LINE
end
# run command
%x{#{job.cmd}}
#wout.puts "1" # saying that we did one job
end
def run
#pid = Process.fork
if #pid.nil? then
# we are in the child
self.main
#rout.close
#wout.close
exit
end
end
def wait
#nb_jobs = #rout.gets(nil).to_i
Process.wait(#pid)
#rout.close
#wout.close
#nb_jobs
end
end
#job = Messages::Job.new({:type => Messages::Job::CMD_LINE, :cmd => "sleep 1" })
RSpec.describe JobPipe do
context "one Orchestrator and one CR" do
before(:each) do
indalo_queue_pre_configure
end
it "can send a job with Orchestrator and be received by CR" do
cr = CR.new
cr.run # execute the C.R. process
todo = JobPipe.instance
todo.push(#job)
nb_jobs = cr.wait
expect(nb_jobs).to eql(1)
end
end
context "one Orchestrator and severals CR" do
it 'can send one job per CR and get all back' do
crs = Array.new(rand(2..10)) { CR.new }
crs.each do |cr|
cr.run
end
todo = JobPipe.instance
crs.each do |_|
todo.push(#job)
end
nb_jobs = 0
crs.each do |cr|
nb_jobs += cr.wait
end
expect(nb_jobs).to eql(crs.length)
end
end
end
end
Edit: The question is (sorry not putting it right away, this was a mistake):
Is there a way to use correctly RSpec on a multi-process environment ?
I'm not looking for a code review, just wanted to display a clear example of what I wanted to do. Here I used fork, but this duplicate all the process (including RSpec part) and got numerous RSpec outputs which is not what we would expect in a test suite.
I would expect that only the main program states the RSpec outputs/stats and the subprocesses just interact with it.
The only way I see to do that correctly is not fork, but call subprocesses through an other mean. Maybe I answer alone to this question...
But not knowing well RSpec, I was wondering if someone knew how to do it within RSpec without writing external code. It seems to me that having separate codes linked to a single test example is not a good idea.
What I found about multi-process testing is this plugin to RSpec. The only thing is I don't know about the mock concept, but maybe I have to learn about it...
Ok, I found an answer which is to use the &block argument of the Process.fork method. In this case, you don't really duplicate all the process, but just execute the block of code in an other process and then return 0 (like said in the Ruby doc).
This prevent the children to get all the RSpec environment and displaying plenty of times the states of your tests.
PS : Be careful not to forget to redirect STDOUT/STDERR of child process if you don't want them to pollute the STDOUT/STDERR of the test.
PS2: don't forget to close #wout on the parent side if you call #rout.gets(nil) in it, because having it opened on the parent prevent EOF from happening (a bug in the code I presented) even if you close it in the child.
PS3: Use two pipes instead of one to prevent child/parent to talk and listen in the same. Childhood error but I did it again.
PS4: Use exit statement (at the end of the &block) to prevent zombie state of the child and usure parent not waiting too long that the rest of the child process dies.
Sorry for that long post, but it's good it stays also for me ^^

How to test when the end of an SMS message has been reached

I am writing an application which acts on commands received via SMS (in Python, but the problem occurs in other languages).
This page on the CMGL command shows the following command and response:
AT+CMGL="ALL"
+CMGL: 1,"REC UNREAD","+31628870634",,"11/01/09,10:26:26+04"
This is text message 1
+CMGL: 2,"REC UNREAD","+31628870634",,"11/01/09,10:26:49+04"
This is text message 2
OK
Most implementations I have looked at look for a valid status such as OK or ERROR to detect the end of the response.
I am trying to find a way to determine the difference between this status code and the end of the message.
Otherwise it would be possible for a malicious sender to send a message of
Hello<CR><LF>OK<CR><LF>+CMGL: 1,(rest of fake message...)
Because I am receiving data on the serial port, there is no clear way to tell when I have reached the end of one message from the GSM board.
Could anyone point me to some information on the best practice here please? Other implementations I have seen do not seem to cover this issue.
The only idea I have had so far (but feels quite messy) is to keep reading until a timeout has been hit. Then check the number of OK / ERROR lines in the message and only accept the last.

How Can I Use xmpp4r To Detect The Online/Offline Status Of A Given Jabber ID?

What is the proper xmpp4r way to know if a given contact is online before sending them a message?
Can you post sample xmpp4r code for doing this?
Here is my use case:
If contact online, send :normal message
Else, email contact
Here are things I have working code for:
Send messages of various types
Get a roster/contact list
Register a call back to detect changes in presence
However, I can't find a place that directly addresses a work flow like this:
Loop through each JID in your roster
If jid.is_online? == true, send IM
Else, send email
I've read that you should send a JID a message of type :headline and if that fails, you know the user is offline. In my tests, if the user is ONLINE, they'll receive a message of type headline. This is suboptimal, as users should only receive messages to read, not noise to determine online status.
I've read that on sign on, all of your contacts will bounce a presence status back at you, and that status is the sole indication that they are online - assuming that there isn't a disconnect or presence change you've yet to receive. So you should register a presence call back, record the initial users who ping you back, and then add or remove from the list based on your running roster presence callback.
If this is truly the way to do it:
Can I get some example code of how to collect all the "I'm here" presence confirmations on sign on via xmpp4r?
Why, oh why, was xmpp designed this way and why is this better than offering an "is_online_and_available" method?
So the answer here is adding a message call back and checking inside the block for the type:
m = Message.new(to, body)
cl.send(m)
cl.add_message_callback do |m|
if m.type == :error
puts "type: #{m.type}"
else
puts "not an error"
end
end
This requires threading as you have to be listening for the response.

Using Ruby on Rails app to control console application

The console application I would like to control is Bluesoleil. It's a Bluetooth software/driver, but details of the software isn't that important I think. What I want to do is basically, type console commands in Windows or Linux terminal environment using web browser running Ruby on Rails app.
So high level design of the Ruby on Rails app would be something like this.
Web browser showing a page with UI for Bluesoleil
Ruby on Rails app render the page for UI, takes in commands from the user and displays result through web browser, just like regular Ruby on Rails app
On the backend, Ruby on Rails types in commands in the console that is running Bluesoleil. And the result shown in the console is grabbed as string by Ruby on Rails.
Is something like this possible with Ruby on Rails?
Just to clear possible confusion, when I say console and console application here, I don't mean Rails console or Ruby console. Console here is just a terminal environment running console applications and so on.
Thank you.
If you only need to run a "one-off" command, just use backticks. If you need to maintain a long-running background process, which accepts commands and returns responses, you can do something like this (some of the details have been edited out, since this code is from a proprietary application):
class Backend
def initialize
#running = false
#server = nil
# if we forget to call "stop", make sure to close down background process on exit
ObjectSpace.define_finalizer(self,lambda { stop if #running })
end
def start
stop if #running
#server = IO.popen("backend","w+")
#running = true
end
def stop
return if not #running
#server << "exit\n"
#server.flush
#running = false
end
def query(*args)
raise "backend not running" if not #running
#server << "details edited out\n"
#server.flush
loop do
response = parse_response
# handle response
# break loop when backend is finished sending data
end
end
private
def parse_response
# details edited out, uses c = #server.getc to read data from backend
# getc will block if there is nothing to read,
# so there needs to be an unambiguous terminator telling you where
# to stop reading
end
end
end
You can adapt this to your own needs. Beware of situations where the background process dies and leaves the main process hanging.
Although it doesn't apply to your situation, if you are designing the background program yourself: Build the background process so that if ANYTHING makes it crash, it will send an unambiguous message like "PANIC" or something which tells the main process to either exit with an error message, or try starting another background process. Also, make sure it is completely unambiguous where each "message" begins and ends, and test the coordination between main/background process well -- if there are bugs on either end, it is very easy to get a situation where both processes get stuck waiting for each other. One more thing: design the "protocol" which the 2 processes speak to each other in a way which makes it easy to maintain synchronization between the 2.

Using the xmpp4r Ruby gem, how can I synchronously discover if a contact is online?

I'm new to XMPP and the xmpp4r library, so please forgive my noob question if this is obviously documented somewhere.
What's the most straightforward way, in a synchronous manner, to find out if a given JID is online? (so that I can call something like is_online?(jid) in an if statement)
My details:
I'm writing a Sinatra app that will attempt to send a message to a user when a particular url gets requested on the web server, but it should only try to send the message to the user if that user is currently online. Figuring out if a given JID is online is my problem.
Now, I know that if I connect and wait a few seconds for all the initial presence probe responses to come back to the Roster helper, then I can inspect any of those presences from my Roster and call #online? on them to get the correct value. But, I don't know when all of the presence updates have been sent, so there's a race condition there and sometimes calling #online? on a presence from my roster will return false if I just haven't received that presence probe response yet.
So, my current thinking is that the most straightforward way to find out if someone is online is to construct a new Presence message of type :probe and send that out to the JID that I'm interested in. Here's how I'm doing it right now:
#jabber is the result of Client::new
#email is the jid I'm interested in polling
def is_online?(jabber, email)
online = false
p = Presence.new
p.set_to(email)
p.set_from(jabber.jid)
p.set_type(:probe)
pres = jabber.send(p) do |returned_presence|
online = returned_presence.nil?
end
return online
end
Now, this works in cases where the user is actually online, but when the user is offline, it looks like the presence probe message that comes back is being caught by some other presence_callback handler that doesn't know what to do with it, and my is_online? function never finishes returning a value.
Can anyone help me by providing a simple example is_online? function that I can call, or point me in the right direction for how I can detect when the roster is done getting all the initial presence updates before I try checking a presence for #online?
As it turns out, there's not a synchronous way to ask for a JID presence. You've just got to ask for what you want, then wait for your response handler to fire when the response arrives.

Resources