fakettp 0.2.4.1 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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