fakettp 0.2.4.1 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. data/.gitignore +6 -0
  2. data/README.rdoc +201 -0
  3. data/Rakefile +113 -0
  4. data/VERSION +1 -0
  5. data/features/control.feature +12 -0
  6. data/features/dashboard.feature +38 -0
  7. data/features/expectations.feature +27 -0
  8. data/features/expectations/expect_get +3 -0
  9. data/features/expectations/get_foo +3 -0
  10. data/features/expectations/get_root +3 -0
  11. data/features/expectations/pass_and_fail +5 -0
  12. data/features/expectations/set_response +5 -0
  13. data/features/step_definitions/expectations.rb +5 -0
  14. data/features/step_definitions/http.rb +42 -0
  15. data/features/step_definitions/simulator.rb +25 -0
  16. data/features/step_definitions/webrat_steps.rb +99 -0
  17. data/features/support/env.rb +5 -0
  18. data/features/support/fakettp.rb +2 -0
  19. data/features/support/paths.rb +12 -0
  20. data/features/support/xpath.rb +10 -0
  21. data/features/verification.feature +49 -0
  22. data/lib/fakettp/commands/fakettp_command.rb +31 -19
  23. data/lib/fakettp/controller.rb +12 -0
  24. data/lib/fakettp/db.rb +7 -0
  25. data/lib/fakettp/error.rb +6 -18
  26. data/lib/fakettp/expectation.rb +28 -44
  27. data/lib/fakettp/fakettp.yml +3 -0
  28. data/lib/fakettp/public/fakettp.css +33 -0
  29. data/lib/fakettp/schema.rb +14 -0
  30. data/lib/fakettp/simulator.rb +16 -10
  31. data/lib/fakettp/views/index.erb +14 -0
  32. data/spec/fakettp/commands/fakettp_command_spec.rb +128 -0
  33. data/spec/fakettp/controller_spec.rb +250 -0
  34. data/spec/fakettp/error_spec.rb +40 -0
  35. data/spec/fakettp/expectation_helper_spec.rb +33 -0
  36. data/spec/fakettp/expectation_spec.rb +142 -0
  37. data/spec/fakettp/simulator_spec.rb +149 -0
  38. data/spec/spec.opts +1 -0
  39. data/spec/spec_helper.rb +18 -0
  40. metadata +73 -24
  41. data/README.html +0 -113
data/.gitignore ADDED
@@ -0,0 +1,6 @@
1
+ tmp/**
2
+ pkg
3
+ install
4
+ testinstall
5
+ coverage
6
+ tmtags
data/README.rdoc ADDED
@@ -0,0 +1,201 @@
1
+ = FakeTTP
2
+
3
+ == Purpose
4
+
5
+ When you are writing acceptance/integration tests for an application which
6
+ makes HTTP requests to a remote application, sometimes you need to be able
7
+ to test the interactions in different scenarios without talking to a real
8
+ instance of the remote application.
9
+
10
+ FakeTTP is a standalone web application that allows you to mock requests (ie
11
+ set and verify expectations on the requests your application makes, and
12
+ return suitable responses to those requests).
13
+
14
+ == Installation
15
+
16
+ === Install the gem
17
+
18
+ Add GemCutter as a gem source (you only need to do this once):
19
+
20
+ gem sources -a http://gemcutter.org
21
+
22
+ Then install FakeTTP:
23
+
24
+ sudo gem install fakettp
25
+
26
+ Alternatively, you can specify the source when you install the gem:
27
+
28
+ sudo gem install fakettp --source http://gemcutter.org
29
+
30
+ === Create a FakeTTP directory
31
+
32
+ You can install FakeTTP anywhere that your web server user can see it:
33
+
34
+ fakettp install <directory> <hostname>
35
+
36
+ Set _hostname_ to the host you want to use for FakeTTP control requests (the
37
+ examples below use <tt>fakettp.local</tt>). You can install multiple copies
38
+ and run them independently by using different directories and hostnames (for
39
+ example on a CI server to prevent clashes when building multiple projects
40
+ in parallel).
41
+
42
+ === Point your web server at the directory
43
+
44
+ FakeTTP should work with any Rack-compatible server: just point the server to
45
+ the correct directory. For example, using Passenger[http://www.modrails.com/]
46
+ (mod_rails) with Apache, create a virtual host along these lines:
47
+
48
+ <VirtualHost *:80>
49
+ ServerName fakettp.local
50
+ DocumentRoot "/path/to/fakettp/public"
51
+ <directory "/path/to/fakettp/public">
52
+ Order deny,allow
53
+ Deny from all
54
+ Allow from 127.0.0.1
55
+ </directory>
56
+ </VirtualHost>
57
+
58
+ Then make sure <tt>fakettp.local</tt> resolves to 127.0.0.1 (assuming you're
59
+ running the simulator on the same machine as the application under test), eg
60
+ by adding the following line to <tt>/etc/hosts</tt>:
61
+
62
+ 127.0.0.1 fakettp.local
63
+
64
+ === IMPORTANT: security note
65
+
66
+ Because expectations are set by posting Ruby code to be executed on the
67
+ server, you probably don't want any old Tom, Dick or Harry to be able to
68
+ connect. The security settings in the virtual host config example above
69
+ restrict access to clients running on the local machine.
70
+
71
+ == Usage
72
+
73
+ The examples below assume you've installed using the hostname
74
+ <tt>fakettp.local</tt>. If you've used a different host, adjust appropriately.
75
+
76
+ === Resetting
77
+
78
+ To reset FakeTTP (ie remove all expectations and errors), make an HTTP POST
79
+ request to <tt>http://fakettp.local/reset</tt>.
80
+
81
+ ==== Calling from curl
82
+
83
+ curl fakettp.local/reset -X POST
84
+
85
+ === Setting expectations
86
+
87
+ To create a new expectation, make an HTTP POST request to
88
+ <tt>http://fakettp.local/expect</tt>, with a <em>Content-Type</em> header of
89
+ 'text/plain' and the request data containing a Ruby block to execute.
90
+
91
+ The supplied code should be in the following format, and will generally
92
+ consist of a number of assertions on the request, followed by creation of
93
+ the response to return to the application under test.
94
+
95
+ expect "GET of /foo" do
96
+ request.host.should == 'fakettp.local'
97
+ request.path_info.should == '/foo'
98
+
99
+ content_type 'text/plain'
100
+ "All is well\n"
101
+ end
102
+
103
+ The label on the first line is used in error reporting.
104
+
105
+ The expectation code has access to the underlying Sinatra request and
106
+ response objects etc, as well as RSpec[http://rspec.info] matchers.
107
+
108
+ ==== Calling from curl
109
+
110
+ Assuming the expectation is in <tt>expectation_file</tt>:
111
+
112
+ curl -X POST fakettp.local/expect -X POST -H 'Content-Type:text/plain' --binary-data <expectation_file>
113
+
114
+ === Verifying
115
+
116
+ To verify that all expectations have been met, make an HTTP GET request
117
+ to <tt>http://fakettp.local/verify</tt>.
118
+
119
+ If all is well, the response will be a <em>200 OK</em> with a body of 'OK'. Otherwise
120
+ the status will be <em>400 Bad Request</em>, with a list of failures in the body. The
121
+ failure messages include the complete details of the unexpected request that
122
+ was received, to assist debugging.
123
+
124
+ ==== Calling from curl
125
+
126
+ curl fakettp.local/verify
127
+
128
+ === Web console
129
+
130
+ Point your browser at http://fakettp.local/
131
+
132
+ Currently this is very basic, just showing the expectations from the last run.
133
+
134
+ === Multiple faked hosts
135
+
136
+ To have FakeTTP respond to multiple hostnames, create the appropriate hosts
137
+ entries. If you're using name-based virtual hosts in Apache, add a
138
+ _ServerAlias_ entry to the virtual host config, under the _ServerName_ line, eg:
139
+
140
+ ServerAlias foo.com bar.com
141
+
142
+ == Change log
143
+
144
+ 0.3.0 (18 May 2009)
145
+
146
+ * Now uses SQLite and ActiveRecord to store expectations, instead of the filesystem.
147
+ * Supports specification of hostname on installation
148
+ * HTML page showing expectations from the last run
149
+
150
+ 0.2.4.1 (5 May 2009)
151
+
152
+ * Temporarily depend on edge version of Sinatra, to gain Rack 1.0 compatibility.
153
+
154
+ 0.2.4 (25 Mar 2009)
155
+
156
+ * Fixed a bug which caused expectations to be run in the wrong order if there
157
+ were more than nine of them.
158
+
159
+ 0.2.3 (19 Mar 2009)
160
+
161
+ * Fixed a bug where expectations were being overwritten on Linux due to
162
+ Dir.entries not returning results in the expected order
163
+
164
+ 0.2.2 (18 Mar 2009)
165
+
166
+ * Only accept control requests (reset, expect, verify) on fakettp.local
167
+
168
+ 0.2.1 (17 Mar 2009)
169
+
170
+ * Fixed issue where rspec matchers weren't available to expectations
171
+
172
+ 0.2 (14 Mar 2009)
173
+
174
+ * Complete rewrite, with tests and classes and stuff this time.
175
+
176
+ If you get an 'unexpected return' error, remove the return statement from your
177
+ expectation, and just put the return value on the last line of the _expect_ block.
178
+
179
+ 0.1.2 (13 Feb 2009)
180
+
181
+ * Make sure README.html appears in generated gem
182
+
183
+ 0.1.1 (13 Feb 2009)
184
+
185
+ * Fix permissions on installed tmp directory.
186
+
187
+ 0.1.0 (13 Feb 2009)
188
+
189
+ * First release as a gem.
190
+
191
+ == To Do
192
+
193
+ * Add examples
194
+ * Make control requests RESTful?
195
+ * Show label in verification error for expected requests that weren't received
196
+ * Add facility to stub as well as mock requests
197
+ * Allow more flexibility in request ordering
198
+ * Allow user-specific helper files in installation dir
199
+ * Provide Ruby API to set expectations etc
200
+ * Return the expected content type even if expectation fails
201
+ * Highlight failed lines in console
data/Rakefile ADDED
@@ -0,0 +1,113 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+ require 'spec/rake/spectask'
4
+ require 'spec/rake/verify_rcov'
5
+
6
+ desc 'run specs, create gem, install and test'
7
+ task :default => [:rcov, :verify_rcov, :gemspec, :build, :local_install, :test_install, :'cucumber:all', :ok]
8
+
9
+ begin
10
+ require 'jeweler'
11
+ Jeweler::Tasks.new do |gem|
12
+ gem.name = "fakettp"
13
+ gem.summary = %Q{HTTP server mocking tool}
14
+ gem.description = gem.summary
15
+ gem.email = "kerryjbuckley@gmail.com"
16
+ gem.homepage = "http://github.com/kerryb/fakettp"
17
+ gem.authors = ["Kerry Buckley"]
18
+ gem.add_dependency 'sinatra-sinatra'
19
+ gem.add_dependency 'sqlite3-ruby'
20
+ gem.add_development_dependency 'jeweler'
21
+ gem.add_development_dependency 'rcov'
22
+ gem.add_development_dependency "rspec"
23
+ gem.add_development_dependency "cucumber"
24
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
25
+ end
26
+ Jeweler::GemcutterTasks.new
27
+ rescue LoadError
28
+ puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
29
+ end
30
+
31
+ require 'spec/rake/spectask'
32
+ Spec::Rake::SpecTask.new(:spec) do |spec|
33
+ spec.libs << 'lib' << 'spec'
34
+ spec.spec_files = FileList['spec/**/*_spec.rb']
35
+ end
36
+
37
+ Spec::Rake::SpecTask.new(:rcov) do |spec|
38
+ spec.libs << 'lib' << 'spec'
39
+ spec.pattern = 'spec/**/*_spec.rb'
40
+ spec.rcov = true
41
+ spec.rcov_opts = ['--exclude', 'spec']
42
+ end
43
+
44
+ task :spec => :check_dependencies
45
+
46
+ desc 'Check spec coverage'
47
+ RCov::VerifyTask.new(:verify_rcov) do |t|
48
+ t.threshold = 100.0
49
+ t.index_html = 'coverage/index.html'
50
+ end
51
+
52
+ begin
53
+ require 'cucumber/rake/task'
54
+ namespace :cucumber do
55
+ Cucumber::Rake::Task.new({:ok => :check_dependencies}, 'Run features that should pass') do |t|
56
+ t.cucumber_opts = "--color --tags ~@wip --strict --format 'pretty'"
57
+ end
58
+
59
+ Cucumber::Rake::Task.new({:wip => :check_dependencies}, 'Run features that are being worked on') do |t|
60
+ t.cucumber_opts = "--color --tags @wip:2 --wip --format 'pretty'"
61
+ end
62
+
63
+ desc 'Run all features'
64
+ task :all => [:ok, :wip]
65
+ end
66
+ desc 'Alias for cucumber:ok'
67
+ task :cucumber => 'cucumber:ok'
68
+ rescue LoadError
69
+ task :cucumber do
70
+ abort "Cucumber is not available. In order to run features, you must: sudo gem install cucumber"
71
+ end
72
+ end
73
+
74
+ require 'rake/rdoctask'
75
+ Rake::RDocTask.new do |rdoc|
76
+ if File.exist?('VERSION')
77
+ version = File.read('VERSION')
78
+ else
79
+ version = ""
80
+ end
81
+
82
+ rdoc.rdoc_dir = 'rdoc'
83
+ rdoc.title = "fakettp2 #{version}"
84
+ rdoc.rdoc_files.include('README*')
85
+ rdoc.rdoc_files.include('lib/**/*.rb')
86
+ end
87
+
88
+ desc 'remove all build products'
89
+ task :clean do
90
+ FileUtils.rm_rf %w(tmp install coverage pkg)
91
+ end
92
+
93
+ task :local_install do
94
+ system 'sudo gem in -l pkg/*'
95
+ end
96
+
97
+ desc 'Install FakeTTP into local install directory'
98
+ task :test_install do
99
+ rm_rf 'install'
100
+ system 'fakettp install install fakettp.local'
101
+ touch 'install/tmp/restart.txt'
102
+ end
103
+
104
+ task :ok do
105
+ red = "\e[31m"
106
+ yellow = "\e[33m"
107
+ green = "\e[32m"
108
+ blue = "\e[34m"
109
+ purple = "\e[35m"
110
+ bold = "\e[1m"
111
+ normal = "\e[0m"
112
+ puts "", "#{bold}#{red}*#{yellow}*#{green}*#{blue}*#{purple}* #{green} ALL TESTS PASSED #{purple}*#{blue}*#{green}*#{yellow}*#{red}*#{normal}"
113
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.3.0
@@ -0,0 +1,12 @@
1
+ Feature: Controlling the simulator
2
+ Scenario: Attempting to reset the simulator using the wrong host
3
+ When we post to /reset on foo.fake.local
4
+ Then the response body should contain 'Simulator received mismatched request'
5
+
6
+ Scenario: Attempting to create an expectation using the wrong host
7
+ When we post to /expect on foo.fake.local
8
+ Then the response body should contain 'Simulator received mismatched request'
9
+
10
+ Scenario: Attempting to verify the simulator using the wrong host
11
+ When we get /verify on foo.fake.local
12
+ Then the response body should contain 'Simulator received mismatched request'
@@ -0,0 +1,38 @@
1
+ Feature: Dashboard for debugging failures
2
+ Scenario: Dashboard web page served from /
3
+ When we get / on fakettp.local
4
+ Then the response should have a content type of 'text/html'
5
+ And /html/head/title in the response should be 'FakeTTP'
6
+
7
+ Scenario: Expectations are listed
8
+ Given the simulator is reset
9
+ And there are 3 expectations
10
+ When we get / on fakettp.local
11
+ Then there should be 3 /html/body/div elements in the response
12
+
13
+ Scenario: Show expectation heading and contents
14
+ Given the simulator is reset
15
+ And we expect get_root
16
+ When we get / on fakettp.local
17
+ Then //h1[1] in the response should be '1'
18
+ And //div[1]/pre in the response should be:
19
+ """
20
+ expect "GET /" do
21
+ request.path_info.should == '/'
22
+ end
23
+ """
24
+
25
+ @wip
26
+ Scenario: Highlight passed and failed lines
27
+ Given the simulator is reset
28
+ And we expect pass_and_fail
29
+ And we get / on foo.fake.local
30
+ When we get / on fakettp.local
31
+ Then //div[1]/pre in the response should be:
32
+ """
33
+ <span class="pass">expect "pass and fail" do
34
+ (2 + 2).should == 4</span>
35
+ <span class="fail"> true.should be_false</span>
36
+ 'will never get here'
37
+ end
38
+ """
@@ -0,0 +1,27 @@
1
+ Feature: Expectations
2
+ Scenario: Using rspec matchers
3
+ Given the simulator is reset
4
+ And we expect expect_get
5
+ And we get / on foo.fake.local
6
+ Then verifying the simulator should report success
7
+
8
+ Scenario: Setting response headers
9
+ Given the simulator is reset
10
+ And we expect set_response
11
+ And we get / on foo.fake.local
12
+ Then the response should have a 'foo' header with a value of 'bar'
13
+ And verifying the simulator should report success
14
+
15
+ Scenario: Setting response content-type
16
+ Given the simulator is reset
17
+ And we expect set_response
18
+ And we get / on foo.fake.local
19
+ Then the response should have a content type of 'application/xml'
20
+ And verifying the simulator should report success
21
+
22
+ Scenario: Setting response code
23
+ Given the simulator is reset
24
+ And we expect set_response
25
+ And we get / on foo.fake.local
26
+ Then the response body should be '<foo />'
27
+ And verifying the simulator should report success
@@ -0,0 +1,3 @@
1
+ expect "GET" do
2
+ request.should be_get
3
+ end
@@ -0,0 +1,3 @@
1
+ expect "GET /foo" do
2
+ request.path_info.should == '/foo'
3
+ end
@@ -0,0 +1,3 @@
1
+ expect "GET /" do
2
+ request.path_info.should == '/'
3
+ end
@@ -0,0 +1,5 @@
1
+ expect "pass and fail" do
2
+ (2 + 2).should == 4
3
+ true.should be_false
4
+ 'will never get here'
5
+ end
@@ -0,0 +1,5 @@
1
+ expect "Set header" do
2
+ response['foo'] = 'bar'
3
+ content_type 'application/xml'
4
+ '<foo />'
5
+ end
@@ -0,0 +1,5 @@
1
+ Given /^there are (\d*) expectations$/ do |count|
2
+ count.to_i.times do
3
+ Fakettp::Expectation.create! :contents => 'foo'
4
+ end
5
+ end
@@ -0,0 +1,42 @@
1
+ require 'hpricot'
2
+
3
+ When /^we get (\S*) on (\S*)$/ do |path, host|
4
+ req = Net::HTTP::Get.new path
5
+ @@response = Net::HTTP.new(host).start {|http| http.request(req) }
6
+ end
7
+ When /^we post to (\S*) on (\S*)$/ do |path, host|
8
+ req = Net::HTTP::Post.new path
9
+ @@response = Net::HTTP.new(host).start {|http| http.request(req) }
10
+ end
11
+
12
+ Then /^the response should have a '(.*)' header with a value of '(.*)'$/ do |name, value|
13
+ @@response[name].should == value
14
+ end
15
+
16
+ Then /^the response should have a content type of '(.*)'$/ do |value|
17
+ @@response.content_type.should == value
18
+ end
19
+
20
+ Then /^the response body should be '(.*)'$/ do |value|
21
+ @@response.body.should == value
22
+ end
23
+
24
+ Then /^the response body should contain '(.*)'$/ do |value|
25
+ @@response.body.should include(value)
26
+ end
27
+
28
+ Then /^(\S*) in the response should be '(.*)'$/ do |locator, value|
29
+ check_value locator, value
30
+ end
31
+
32
+ Then /^(\S*) in the response should be:$/ do |locator, value|
33
+ check_value locator, value
34
+ end
35
+
36
+ def check_value locator, value
37
+ (Hpricot(@@response.body)/locator).inner_html.should == value
38
+ end
39
+
40
+ Then /^there should be (\d*) (.*) elements in the response$/ do |count, locator|
41
+ Hpricot(@@response.body).search(locator).size.should == count.to_i
42
+ end