fakettp 0.2.4.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.html +113 -0
- data/bin/fakettp +5 -0
- data/lib/fakettp/commands/fakettp_command.rb +53 -0
- data/lib/fakettp/config.ru +6 -0
- data/lib/fakettp/controller.rb +42 -0
- data/lib/fakettp/error.rb +23 -0
- data/lib/fakettp/expectation.rb +56 -0
- data/lib/fakettp/expectation_helper.rb +17 -0
- data/lib/fakettp/simulator.rb +33 -0
- data/lib/fakettp.rb +3 -0
- metadata +113 -0
data/README.html
ADDED
@@ -0,0 +1,113 @@
|
|
1
|
+
<h1>FakeTTP</h1>
|
2
|
+
<h2>Purpose</h2>
|
3
|
+
<p>When you are writing acceptance/integration tests for an application which makes <span class="caps">HTTP</span> requests to a remote application, sometimes you need to be able to test the interactions in different scenarios without talking to a real instance of the remote application.</p>
|
4
|
+
<p>FakeTTP is a standalone web application that allows you to mock requests (ie set and verify expectations on the requests your application makes, and return suitable responses to those requests).</p>
|
5
|
+
<h2>Installation</h2>
|
6
|
+
<h3>Install the gem</h3>
|
7
|
+
<p>Add GitHub as a gem source (you only need to do this once):</p>
|
8
|
+
<p><code>gem sources -a http://gems.github.com</code></p>
|
9
|
+
<p>Then install FakeTTP:</p>
|
10
|
+
<p><code>sudo gem install kerryb-fakettp</code></p>
|
11
|
+
<p>Alternatively, you can specify the source when you install the gem:</p>
|
12
|
+
<p><code>sudo gem install kerryb-fakettp --source http://gems.github.com</code></p>
|
13
|
+
<h3>Create a FakeTTP directory</h3>
|
14
|
+
<p>You can install FakeTTP anywhere that your web server user can see it:</p>
|
15
|
+
<p><code>fakettp install <directory></code></p>
|
16
|
+
<h3>Point your web server at the directory</h3>
|
17
|
+
<p>FakeTTP should work with any Rack-compatible server: just point the server to the correct directory. For example, using <a href="http://www.modrails.com/">Passenger</a> (mod_rails) with Apache, create a virtual host along these lines:</p>
|
18
|
+
<pre><code>
|
19
|
+
<VirtualHost *:80>
|
20
|
+
ServerName fakettp.local
|
21
|
+
DocumentRoot "/path/to/fakettp/public"
|
22
|
+
<directory "/path/to/fakettp/public">
|
23
|
+
Order deny,allow
|
24
|
+
Deny from all
|
25
|
+
Allow from 127.0.0.1
|
26
|
+
</directory>
|
27
|
+
</VirtualHost>
|
28
|
+
</code></pre>
|
29
|
+
<p>Then make sure <code>fakettp.local</code> resolves to 127.0.0.1 (assuming you’re running the simulator on the same machine as the application under test), eg by adding the following line to <code>/etc/hosts</code>:</p>
|
30
|
+
<p><code>127.0.0.1 fakettp.local</code></p>
|
31
|
+
<h3><span class="caps">IMPORTANT</span>: security note</h3>
|
32
|
+
<p>Because expectations are set by posting Ruby code to be executed on the server, you probably don’t want any old Tom, Dick or Harry to be able to connect. The security settings in the virtual host config example above restrict access to clients running on the local machine.</p>
|
33
|
+
<h2>Usage</h2>
|
34
|
+
<h3>Resetting</h3>
|
35
|
+
<p>To reset FakeTTP (ie remove all expectations and errors), make an <span class="caps">HTTP</span> <span class="caps">POST</span> request to <code>http://fakettp.local/reset</code>.</p>
|
36
|
+
<h4>Calling from curl</h4>
|
37
|
+
<p><code>curl fakettp.local/reset -X POST</code></p>
|
38
|
+
<h3>Setting expectations</h3>
|
39
|
+
<p>To create a new expectation, make an <span class="caps">HTTP</span> <span class="caps">POST</span> request to <code>http://fakettp.local/expect</code>, with a <em>Content-Type</em> header of ‘text/plain’ and the request data containing a Ruby block to execute.</p>
|
40
|
+
<p>The supplied code should be in the following format, and will generally consist of a number of assertions on the request, followed by creation of the response to return to the application under test.</p>
|
41
|
+
<pre><code>
|
42
|
+
expect "GET of /foo" do
|
43
|
+
request.host.should == 'fakettp.local'
|
44
|
+
request.path_info.should == '/foo'
|
45
|
+
|
46
|
+
content_type 'text/plain'
|
47
|
+
"All is well\n"
|
48
|
+
end
|
49
|
+
</code></pre>
|
50
|
+
<p>The label on the first line is used in error reporting.</p>
|
51
|
+
<p>The expectation code has access to the underlying Sinatra request and response objects etc, as well as <a href="http://rspec.info">RSpec</a> matchers.</p>
|
52
|
+
<h4>Calling from curl</h4>
|
53
|
+
<p>Assuming the expectation is in <code>expectation_file</code>:</p>
|
54
|
+
<p><code>curl -X POST fakettp.local/expect -X POST -H 'Content-Type:text/plain' --binary-data @expectation_file</code></p>
|
55
|
+
<h3>Verifying</h3>
|
56
|
+
<p>To verify that all expectations have been met, make an <span class="caps">HTTP</span> <span class="caps">GET</span> request to <code>http://fakettp.local/verify</code>.</p>
|
57
|
+
<p>If all is well, the response will be a <em>200 OK</em> with a body of ‘OK’. Otherwise the status will be <em>400 Bad Request</em>, with a list of failures in the body. The failure messages include the complete details of the unexpected request that was received, to assist debugging.</p>
|
58
|
+
<h4>Calling from curl</h4>
|
59
|
+
<p><code>curl fakettp.local/verify</code></p>
|
60
|
+
<h3>Multiple hosts</h3>
|
61
|
+
<p>To have FakeTTP respond to multiple hostnames, create the appropriate hosts entries. If you’re using name-based virtual hosts in Apache, add a <em>ServerAlias</em> entry to the virtual host config, under the <em>ServerName</em> line, eg:</p>
|
62
|
+
<p><code>ServerAlias foo.com bar.com</code></p>
|
63
|
+
<h2>Change log</h2>
|
64
|
+
<p>0.2.4.1 (5 May 2009)</p>
|
65
|
+
<ul>
|
66
|
+
<li>Temporarily depend on edge version of Sinatra, to gain Rack 1.0 compatibility.</li>
|
67
|
+
</ul>
|
68
|
+
<p>0.2.4 (25 Mar 2009)</p>
|
69
|
+
<ul>
|
70
|
+
<li>Fixed a bug which caused expectations to be run in the wrong order if there were more than nine of them.</li>
|
71
|
+
</ul>
|
72
|
+
<p>0.2.3 (19 Mar 2009)</p>
|
73
|
+
<ul>
|
74
|
+
<li>Fixed a bug where expectations were being overwritten on Linux due to Dir.entries not returning results in the expected order</li>
|
75
|
+
</ul>
|
76
|
+
<p>0.2.2 (18 Mar 2009)</p>
|
77
|
+
<ul>
|
78
|
+
<li>Only accept control requests (reset, expect, verify) on fakettp.local</li>
|
79
|
+
</ul>
|
80
|
+
<p>0.2.1 (17 Mar 2009)</p>
|
81
|
+
<ul>
|
82
|
+
<li>Fixed issue where rspec matchers weren’t available to expectations</li>
|
83
|
+
</ul>
|
84
|
+
<p>0.2 (14 Mar 2009)</p>
|
85
|
+
<ul>
|
86
|
+
<li>Complete rewrite, with tests and classes and stuff this time.</li>
|
87
|
+
</ul>
|
88
|
+
<p>If you get an ‘unexpected return’ error, remove the return statement from your expectation, and just put the return value on the last line of the <em>expect</em> block.</p>
|
89
|
+
<p>0.1.2 (13 Feb 2009)</p>
|
90
|
+
<ul>
|
91
|
+
<li>Make sure <span class="caps">README</span>.html appears in generated gem</li>
|
92
|
+
</ul>
|
93
|
+
<p>0.1.1 (13 Feb 2009)</p>
|
94
|
+
<ul>
|
95
|
+
<li>Fix permissions on installed tmp directory.</li>
|
96
|
+
</ul>
|
97
|
+
<p>0.1.0 (13 Feb 2009)</p>
|
98
|
+
<ul>
|
99
|
+
<li>First release as a gem.</li>
|
100
|
+
</ul>
|
101
|
+
<h2>To Do</h2>
|
102
|
+
<ul>
|
103
|
+
<li>Add examples</li>
|
104
|
+
<li>Make control requests RESTful?</li>
|
105
|
+
<li>Make main hostname configurable?</li>
|
106
|
+
<li>Show label in verification error for expected requests that weren’t received</li>
|
107
|
+
<li>Add facility to stub as well as mock requests</li>
|
108
|
+
<li>Allow more flexibility in request ordering</li>
|
109
|
+
<li>Allow user-specific helper files in installation dir</li>
|
110
|
+
<li>Check install/run behaviour when development dependencies are not installed</li>
|
111
|
+
<li>Provide Ruby <span class="caps">API</span> to set expectations etc</li>
|
112
|
+
<li>Return the expected content type even if expectation fails</li>
|
113
|
+
</ul>
|
data/bin/fakettp
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
module Fakettp
|
2
|
+
module Commands
|
3
|
+
class FakettpCommand
|
4
|
+
def initialize args
|
5
|
+
@args = args
|
6
|
+
end
|
7
|
+
|
8
|
+
def run
|
9
|
+
command = get_command
|
10
|
+
return usage unless command
|
11
|
+
case command
|
12
|
+
when 'install' then
|
13
|
+
return install
|
14
|
+
else
|
15
|
+
return usage
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def get_command
|
22
|
+
@args[0]
|
23
|
+
end
|
24
|
+
|
25
|
+
def install
|
26
|
+
dir = get_dir
|
27
|
+
return usage unless dir
|
28
|
+
if File.exist? dir
|
29
|
+
$stderr.puts "File or directory #{dir} already exists."
|
30
|
+
return 1
|
31
|
+
end
|
32
|
+
FileUtils.mkdir_p dir + '/tmp/expectations', :mode => 0777
|
33
|
+
FileUtils.mkdir_p dir + '/public'
|
34
|
+
FileUtils.cp File.dirname(__FILE__) + '/../config.ru', dir
|
35
|
+
FileUtils.cp File.dirname(__FILE__) + '/../../../README.html', dir
|
36
|
+
return 0
|
37
|
+
end
|
38
|
+
|
39
|
+
def get_dir
|
40
|
+
@args[1]
|
41
|
+
end
|
42
|
+
|
43
|
+
def usage
|
44
|
+
$stderr.puts <<-EOF
|
45
|
+
Usage:
|
46
|
+
|
47
|
+
[TODO]
|
48
|
+
EOF
|
49
|
+
return 1
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'sinatra'
|
2
|
+
require 'spec'
|
3
|
+
Sinatra::Default.set :run, false
|
4
|
+
Sinatra::Default.set :environment, ENV['RACK_ENV']
|
5
|
+
|
6
|
+
require 'fakettp/expectation_helper'
|
7
|
+
require 'fakettp/simulator'
|
8
|
+
require 'fakettp/expectation'
|
9
|
+
|
10
|
+
include Fakettp::ExpectationHelper
|
11
|
+
|
12
|
+
post '/expect', :host => 'fakettp.local' do
|
13
|
+
Fakettp::Simulator << request.body.read
|
14
|
+
content_type 'text/plain'
|
15
|
+
"Expect OK\n"
|
16
|
+
end
|
17
|
+
|
18
|
+
post '/reset', :host => 'fakettp.local' do
|
19
|
+
Fakettp::Simulator.reset
|
20
|
+
content_type 'text/plain'
|
21
|
+
"Reset OK\n"
|
22
|
+
end
|
23
|
+
|
24
|
+
get '/verify', :host => 'fakettp.local' do
|
25
|
+
content_type 'text/plain'
|
26
|
+
if Fakettp::Simulator.verify
|
27
|
+
"Verify OK\n"
|
28
|
+
else
|
29
|
+
throw :halt, [500, Fakettp::Simulator.list_errors]
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
[:get, :post, :put, :delete, :head].each do |method|
|
34
|
+
send method, '/**' do
|
35
|
+
begin
|
36
|
+
Fakettp::Simulator.handle_request binding
|
37
|
+
rescue Fakettp::Expectation::Error => e
|
38
|
+
content_type 'text/plain'
|
39
|
+
throw :halt, [500, "Simulator received mismatched request\n"]
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Fakettp
|
2
|
+
class Error
|
3
|
+
ERROR_FILE = File.join FAKETTP_BASE, 'tmp', 'errors'
|
4
|
+
|
5
|
+
def self.clear_all
|
6
|
+
FileUtils.rm_rf ERROR_FILE
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.<< message
|
10
|
+
File.open ERROR_FILE, 'a' do |f|
|
11
|
+
f.puts message
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.empty?
|
16
|
+
!File.exists? ERROR_FILE
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.list
|
20
|
+
empty? ? '' : File.read(ERROR_FILE)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module Fakettp
|
2
|
+
class Expectation
|
3
|
+
class Error < Exception; end
|
4
|
+
|
5
|
+
EXPECTATION_DIR = File.join FAKETTP_BASE, 'tmp', 'expectations'
|
6
|
+
|
7
|
+
def initialize contents
|
8
|
+
@contents = contents
|
9
|
+
end
|
10
|
+
|
11
|
+
def execute binding
|
12
|
+
eval @contents, binding
|
13
|
+
# TODO: Include context of expectation file
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.clear_all
|
17
|
+
FileUtils.rm_rf Dir.glob(File.join(EXPECTATION_DIR, '*'))
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.empty?
|
21
|
+
files.empty?
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.<< expectation
|
25
|
+
File.open next_file_to_create, 'w' do |f|
|
26
|
+
f.write expectation
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.next
|
31
|
+
file = next_file_to_read
|
32
|
+
contents = File.read file
|
33
|
+
FileUtils.rm file
|
34
|
+
Expectation.new contents
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def self.next_file_to_create
|
40
|
+
name = (files.last.to_i + 1).to_s
|
41
|
+
File.join EXPECTATION_DIR, name
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.next_file_to_read
|
45
|
+
name = files.first
|
46
|
+
raise Error.new('Received unexpected request') unless name
|
47
|
+
File.join EXPECTATION_DIR, name
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.files
|
51
|
+
(Dir.entries(EXPECTATION_DIR) - ['.', '..']).sort_by {|a| a.to_i}
|
52
|
+
end
|
53
|
+
|
54
|
+
private_class_method :next_file_to_create, :next_file_to_read, :files
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'fakettp/expectation'
|
2
|
+
|
3
|
+
module Fakettp
|
4
|
+
module ExpectationHelper
|
5
|
+
def self.included(cls)
|
6
|
+
cls.send :include, Spec::Matchers
|
7
|
+
end
|
8
|
+
|
9
|
+
def expect label
|
10
|
+
begin
|
11
|
+
yield
|
12
|
+
rescue Exception => e
|
13
|
+
raise Fakettp::Expectation::Error.new("Error in #{label}: #{e.message}")
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'fakettp/expectation'
|
2
|
+
require 'fakettp/error'
|
3
|
+
|
4
|
+
module Fakettp
|
5
|
+
class Simulator
|
6
|
+
def self.reset
|
7
|
+
Expectation.clear_all
|
8
|
+
Error.clear_all
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.verify
|
12
|
+
Error << 'Expected request not received' unless Expectation.empty?
|
13
|
+
Error.empty?
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.<< expectation
|
17
|
+
Expectation << expectation
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.handle_request binding
|
21
|
+
begin
|
22
|
+
Expectation.next.execute binding
|
23
|
+
rescue Fakettp::Expectation::Error => e
|
24
|
+
Error << e.message
|
25
|
+
raise e
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.list_errors
|
30
|
+
Error.list
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
data/lib/fakettp.rb
ADDED
metadata
ADDED
@@ -0,0 +1,113 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: fakettp
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.2.4.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Kerry Buckley
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-05-13 00:00:00 +01:00
|
13
|
+
default_executable: fakettp
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: sinatra-sinatra
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 0.9.1.3
|
24
|
+
version:
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: rspec
|
27
|
+
type: :development
|
28
|
+
version_requirement:
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 1.1.12
|
34
|
+
version:
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: spicycode-rcov
|
37
|
+
type: :development
|
38
|
+
version_requirement:
|
39
|
+
version_requirements: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: 0.8.0
|
44
|
+
version:
|
45
|
+
- !ruby/object:Gem::Dependency
|
46
|
+
name: cucumber
|
47
|
+
type: :development
|
48
|
+
version_requirement:
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: 0.1.16
|
54
|
+
version:
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: RedCloth
|
57
|
+
type: :development
|
58
|
+
version_requirement:
|
59
|
+
version_requirements: !ruby/object:Gem::Requirement
|
60
|
+
requirements:
|
61
|
+
- - ">="
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
version: 4.1.1
|
64
|
+
version:
|
65
|
+
description:
|
66
|
+
email: kerryjbuckley@gmail.com
|
67
|
+
executables:
|
68
|
+
- fakettp
|
69
|
+
extensions: []
|
70
|
+
|
71
|
+
extra_rdoc_files: []
|
72
|
+
|
73
|
+
files:
|
74
|
+
- lib/fakettp/commands/fakettp_command.rb
|
75
|
+
- lib/fakettp/config.ru
|
76
|
+
- lib/fakettp/controller.rb
|
77
|
+
- lib/fakettp/error.rb
|
78
|
+
- lib/fakettp/expectation.rb
|
79
|
+
- lib/fakettp/expectation_helper.rb
|
80
|
+
- lib/fakettp/simulator.rb
|
81
|
+
- lib/fakettp.rb
|
82
|
+
- bin/fakettp
|
83
|
+
- README.html
|
84
|
+
has_rdoc: true
|
85
|
+
homepage: http://github.com/kerryb/fakettp/
|
86
|
+
licenses: []
|
87
|
+
|
88
|
+
post_install_message:
|
89
|
+
rdoc_options: []
|
90
|
+
|
91
|
+
require_paths:
|
92
|
+
- lib
|
93
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
94
|
+
requirements:
|
95
|
+
- - ">="
|
96
|
+
- !ruby/object:Gem::Version
|
97
|
+
version: "0"
|
98
|
+
version:
|
99
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: "0"
|
104
|
+
version:
|
105
|
+
requirements: []
|
106
|
+
|
107
|
+
rubyforge_project: fakettp
|
108
|
+
rubygems_version: 1.3.2
|
109
|
+
signing_key:
|
110
|
+
specification_version: 3
|
111
|
+
summary: HTTP server mocking tool
|
112
|
+
test_files: []
|
113
|
+
|