smoke_test
#!/usr/bin/env ruby
# Peoplefinder smoke test
# =======================
#
# Set the following environment variables:
#
# * HOST - the root of the server to be tested, e.g. http://example.com/
# * EMAIL_ADDRESS - the email address to sign in with
# * EMAIL_PASSWORD - the IMAP password
#
# Google Apps IMAP is assumed, and the account must have IMAP access
# configured. For the password, generate an app-specific password (this is
# necessary if two-factor authentication is enabled).
#
# Run the test:
#
# $ bundle exec ./smoke_test
#
# Successful output will look like this:
#
# Request token ... OK
# Fetch token email ... wait 2s ... OK
# Sign in ... OK
# Edit profile ... OK
# Search for profile ... wait 2s ... OK
#
# On success, the return code is zero. Failure will result in a non-zero return
# code.
require "bundler/setup"
require "capybara"
require "capybara/dsl"
require "capybara/poltergeist"
require "mail"
require "net/imap"
require "securerandom"
host = ENV.fetch("HOST")
email_address = ENV.fetch("EMAIL_ADDRESS")
email_password = ENV.fetch("EMAIL_PASSWORD")
search_name = ENV.fetch("SEARCH_NAME")
Capybara.register_driver :poltergeist do |app|
Capybara::Poltergeist::Driver.new(app, phantomjs_options: ["--ignore-ssl-errors=yes"])
end
Capybara.run_server = false
Capybara.current_driver = :poltergeist
Capybara.app_host = host
class SmokeTest
include Capybara::DSL
TIME_DRIFT_ALLOWANCE = 5
def initialize(email_address, imap, host, search_name)
@email_address = email_address
@imap = imap
@host = host
@search_name = search_name
@start_time = Time.zone.now.utc
end
def run
step "Request token" do
# 300 secs is the TTL for the DNS based maintenance page
with_retries(initial_delay: 15, max_delay: 60) { request_token }
# request_token
end
signin_path = nil
step "Fetch token email" do
token_mail = with_retries(initial_delay: 5, max_delay: 120) { fetch_token_mail }
raise "couldn't find token email" unless token_mail
text_part = token_mail.text_part.decoded
signin_path = text_part[%r{/tokens/[0-9a-f-]+}]
raise "couldn't find sign-in token link" unless signin_path
end
my_name = nil
step "Sign in" do
sign_in signin_path
my_name = find_own_name
end
unique_string = SecureRandom.uuid
step "Edit profile" do
update_profile my_name, unique_string
end
step "Search for own changed profile" do
found = with_retries { search_profile my_name, unique_string }
raise "couldn't find own profile via search" unless found
end
step "Search for profile: #{@search_name}" do
found = with_retries { search_profile @search_name, @search_name }
raise "couldn't find profile for #{@search_name} via search" unless found
end
rescue StandardError => e
puts page.text
raise e
end
private
attr_reader :imap, :email_address
def find_own_name
page.text[/Signed in as (.*?) Sign out/, 1].tap do |name|
raise "couldn't find own name" unless name
end
end
def search_profile(name, search_term)
visit "/"
fill_in "Enter the name of a person or role", with: search_term
click_button "Submit search"
result = nil
within "#search_results" do
result = page.has_text?(name)
end
result
end
def update_profile(name, uuid)
desc = "Smoke test started at #{@start_time.iso8601}\n\nUnique identifier: #{uuid}"
click_link name, match: :first
click_edit_profile
fill_in "Extra information", with: desc
click_button "Save", match: :first
raise "couldn't update profile" unless page.has_text?("Updated your profile")
end
def request_token
visit "/"
page_loaded = page.has_text?("Request link to access People Finder")
if page_loaded
fill_in "Email address", with: email_address
click_button "Request link"
end
page_loaded
end
def fetch_token_mail
imap.examine "INBOX"
candidate_ids = imap.search(["SINCE", @start_time.strftime("%-d-%b-%Y")])
mails = candidate_ids.map do |id|
msg = imap.fetch(id, "RFC822")[0].attr["RFC822"]
Mail.read_from_string(msg)
end
mails.reverse.find do |mail|
mail.subject =~ /access request/i &&
mail.date.to_time >= (@start_time - TIME_DRIFT_ALLOWANCE) &&
mail.text_part.decoded.include?(@host)
end
end
def sign_in(path)
visit path
raise "couldn't sign in via #{path}" unless page.body =~ /Signed in as/
end
def with_retries(tries: 5, initial_delay: 2, max_delay: 30)
delay = initial_delay
result = nil
tries.times do
result = yield
break if result
wait delay
delay = [max_delay, delay * 2].min
end
result
end
def wait(delay)
$stdout.print "wait #{delay} ... "
$stdout.flush
sleep delay
end
def step(string)
$stdout.print string, " ... "
$stdout.flush
yield.tap { $stdout.puts "OK" }
end
end
imap = Net::IMAP.new("imap.gmail.com", port: 993, ssl: true)
imap.login email_address, email_password
SmokeTest.new(email_address, imap, host, search_name).run
imap.logout
imap.disconnect