Skip to content

Commit

Permalink
Get one basic end-to-end test working
Browse files Browse the repository at this point in the history
  • Loading branch information
mattwynne committed Apr 18, 2011
1 parent 392a29f commit 96e66c3
Show file tree
Hide file tree
Showing 9 changed files with 262 additions and 70 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
.bundle

*.swp
5 changes: 5 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
source :rubygems
source 'http://localhost:8808'

gem 'thin'
gem 'sinatra'
gem 'httparty'
gem 'uuid'
gem 'haml'
gem 'rspec'
gem 'cucumber'
gem 'rack-test'
gem 'capybara'

44 changes: 44 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,15 +1,47 @@
GEM
remote: http://rubygems.org/
remote: http://localhost:8808/
specs:
builder (3.0.0)
capybara (0.4.1.2)
celerity (>= 0.7.9)
culerity (>= 0.2.4)
mime-types (>= 1.16)
nokogiri (>= 1.3.3)
rack (>= 1.0.0)
rack-test (>= 0.5.4)
selenium-webdriver (>= 0.0.27)
xpath (~> 0.1.3)
celerity (0.8.9)
childprocess (0.1.8)
ffi (~> 1.0.6)
crack (0.1.8)
cucumber (0.10.2)
builder (>= 2.1.2)
diff-lcs (>= 1.1.2)
gherkin (>= 2.3.5)
json (>= 1.4.6)
term-ansicolor (>= 1.0.5)
culerity (0.2.15)
daemons (1.1.2)
diff-lcs (1.1.2)
eventmachine (0.12.10)
ffi (1.0.7)
rake (>= 0.8.7)
gherkin (2.3.5)
json (>= 1.4.6)
haml (3.0.25)
httparty (0.7.4)
crack (= 0.1.8)
json (1.5.1)
json_pure (1.5.1)
macaddr (1.0.0)
mime-types (1.16)
nokogiri (1.4.4)
rack (1.2.2)
rack-test (0.5.7)
rack (>= 1.0)
rake (0.8.7)
rspec (2.5.0)
rspec-core (~> 2.5.0)
rspec-expectations (~> 2.5.0)
Expand All @@ -18,23 +50,35 @@ GEM
rspec-expectations (2.5.0)
diff-lcs (~> 1.1.2)
rspec-mocks (2.5.0)
rubyzip (0.9.4)
selenium-webdriver (0.1.4)
childprocess (>= 0.1.7)
ffi (>= 1.0.7)
json_pure
rubyzip
sinatra (1.2.1)
rack (~> 1.1)
tilt (>= 1.2.2, < 2.0)
term-ansicolor (1.0.5)
thin (1.2.11)
daemons (>= 1.0.9)
eventmachine (>= 0.12.6)
rack (>= 1.0.0)
tilt (1.2.2)
uuid (2.3.1)
macaddr (~> 1.0)
xpath (0.1.3)
nokogiri (~> 1.3)

PLATFORMS
ruby

DEPENDENCIES
capybara
cucumber
haml
httparty
rack-test
rspec
sinatra
thin
Expand Down
22 changes: 22 additions & 0 deletions features/compete.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
Feature: Complete

Scenario: One player plays another
Given a player "dumbo" who plays like this:
"""
get '/' do
'huh?'
end
"""
And a player "always-4" who plays like this:
"""
get '/' do
'4'
end
"""
And the correct answer to every question is '4'
When the two players are entered
And the game is played for 1 second
Then the scores should be:
| player | score |
| dumbo | 0 |
| always-4 | 1 |
102 changes: 102 additions & 0 deletions features/step_definitions/steps.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
require 'thin'
Thin::Logging.silent = true # uncomment this if things are going wrong on the player

module ExtremeStartup
module TestingApi

class TestablePlayer
attr_reader :url, :name

def initialize(name, content)
@name, @content = name, content
@port = next_free_port
@app = Class.new(Sinatra::Base) do
eval content

get('/ping') { 'OK' }
end
end

def start
app = @app
port = @port
@thread = Thread.new do
Thin::Server.start('localhost', port) do
map('/') { run app.new }
end
end
@url = "http://localhost:#{port}"
Timeout.timeout(2) do
until responsive?;end
end
end

private

def responsive?
response = Net::HTTP.start('localhost', @port) { |http| http.get('/ping') }
response.body == 'OK'
rescue Errno::ECONNREFUSED, Errno::EBADF
false
end

def next_free_port
server = TCPServer.new('127.0.0.1', 0)
server.addr[1]
ensure
server.close if server
end
end

attr_reader :players

def create_player(name, content)
@players ||= []
@players << TestablePlayer.new(name, content)
end

def enter_player(player)
post '/players', :name => player.name, :url => player.url
end

def stub_correct_answer_to_be(correct_answer)
::ExtremeStartup::AdditionQuestion.class_eval do
define_method(:answered_correctly?) do |actual_answer|
actual_answer.to_s == correct_answer
end
end
end

def score_for(player_name)
visit '/'
find('.player .name', :text => player_name).find(:xpath, '..').find('.score').text.to_i
end
end
end

World(ExtremeStartup::TestingApi)

Given /^a player "([^"]*)" who plays like this:$/ do |player_name, content|
create_player player_name, content
end

Given /^the correct answer to every question is '(\d+)'$/ do |answer|
stub_correct_answer_to_be answer
end

When /^the two players are entered$/ do
players.each do |player|
player.start
enter_player player
end
end

When /^the game is played for (\d+) second$/ do |num_seconds|
sleep num_seconds.to_i
end

Then /^the scores should be:$/ do |table|
table.hashes.each do |row|
score_for(row['player']).should == row['score'].to_i
end
end
15 changes: 15 additions & 0 deletions features/support/env.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
$: << File.expand_path(File.dirname(__FILE__) + '/../../lib')
require 'extreme_startup/web_server'

require 'rack/test'
require 'capybara/cucumber'
Capybara.app = ExtremeStartup::WebServer

module RackTestWorld
include Rack::Test::Methods

def app
ExtremeStartup::WebServer
end
end
World(RackTestWorld)
68 changes: 68 additions & 0 deletions lib/extreme_startup/web_server.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
require 'sinatra/base'
require 'httparty'
require 'uuid'
require 'haml'
require "socket"
require_relative 'scoreboard'
require_relative 'player'
require_relative 'shopper'

Thread.abort_on_exception = true

module ExtremeStartup
class WebServer < Sinatra::Base

set :port, 3000
set :players, Hash.new
set :scoreboard, Scoreboard.new
set :question_factory, QuestionFactory.new

get '/' do
haml :leaderboard, :locals => {
:leaderboard => scoreboard.leaderboard,
:players => players }
end

get %r{/players/([\w]+)} do |uuid|
haml :personal_page, :locals => { :name => players[uuid].name, :score => scoreboard.scores[uuid] }
end

get '/players' do
haml :add_player
end

get '/advance_round' do
question_factory.advance_round
redirect to('/round')
end

get '/round' do
question_factory.round.to_s
end

post '/players' do
player = Player.new(params)
scoreboard.new_player(player)
players[player.uuid] = player

Thread.new { Shopper.new(player, scoreboard, question_factory).start }

personal_page = "http://#{local_ip}:#{@env["SERVER_PORT"]}/players/#{player.uuid}"
haml :player_added, :locals => { :url => personal_page }
end

private

def local_ip
UDPSocket.open {|s| s.connect("64.233.187.99", 1); s.addr.last}
end

[:players, :scoreboard, :question_factory].each do |setting|
define_method(setting) do
settings.send(setting)
end
end

end
end

6 changes: 4 additions & 2 deletions views/leaderboard.haml
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,7 @@
%body
%h1 Leaderboard
%ul
- leaderboard.each do |entry|
%li= "#{players[entry[0]].name} #{entry[1]}"
- leaderboard.each do |entry|
%li.player
%span.name #{players[entry[0]].name}
%span.score #{entry[1]}
68 changes: 1 addition & 67 deletions web_server.rb
Original file line number Diff line number Diff line change
@@ -1,70 +1,4 @@
require 'rubygems'
require 'sinatra/base'
require 'httparty'
require 'uuid'
require 'haml'
require "socket"
require_relative 'lib/extreme_startup/scoreboard'
require_relative 'lib/extreme_startup/player'
require_relative 'lib/extreme_startup/shopper'

Thread.abort_on_exception = true

module ExtremeStartup
class WebServer < Sinatra::Base

set :port, 3000
set :players, Hash.new
set :scoreboard, Scoreboard.new
set :question_factory, QuestionFactory.new

get '/' do
haml :leaderboard, :locals => {
:leaderboard => scoreboard.leaderboard,
:players => players }
end

get %r{/players/([\w]+)} do |uuid|
haml :personal_page, :locals => { :name => players[uuid].name, :score => scoreboard.scores[uuid] }
end

get '/players' do
haml :add_player
end

get '/advance_round' do
question_factory.advance_round
redirect to('/round')
end

get '/round' do
question_factory.round.to_s
end

post '/players' do
player = Player.new(params)
scoreboard.new_player(player)
players[player.uuid] = player

Thread.new { Shopper.new(player, scoreboard, question_factory).start }

personal_page = "http://#{local_ip}:#{@env["SERVER_PORT"]}/players/#{player.uuid}"
haml :player_added, :locals => { :url => personal_page }
end

private

def local_ip
UDPSocket.open {|s| s.connect("64.233.187.99", 1); s.addr.last}
end

[:players, :scoreboard, :question_factory].each do |setting|
define_method(setting) do
settings.send(setting)
end
end

end
end

require_relative 'lib/extreme_startup/web_server'
ExtremeStartup::WebServer.run!

0 comments on commit 96e66c3

Please sign in to comment.