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
@@ -0,0 +1,33 @@
1
+ body {
2
+ font: 10pt Corbel, "Lucida Grande", "Lucida Sans Unicode",
3
+ "Lucida Sans", "DejaVu Sans", "Bitstream Vera Sans",
4
+ "Liberation Sans", Verdana, sans-serif;
5
+ }
6
+ div.expectation {
7
+ width: 95%;
8
+ border: 1px solid #888;
9
+ background-color: #eee;
10
+ margin: 0 auto 10px auto;
11
+ padding: 5px;
12
+ }
13
+ h1 {
14
+ width: 95%;
15
+ border: 1px solid #888;
16
+ background-color: #888;
17
+ margin: 0 auto;
18
+ padding: 2px 5px;
19
+ font-size: 110%;
20
+ color: #fff;
21
+ }
22
+ .expectation pre {
23
+ padding: 0;
24
+ margin: 0;
25
+ font: 75% Monaco, "Bitstream Vera Sans Mono", "Lucida Console",
26
+ Terminal, monospace;
27
+ }
28
+ .pass {
29
+ color: #0a0;
30
+ }
31
+ .fail {
32
+ color: #f00;
33
+ }
@@ -0,0 +1,14 @@
1
+ require File.dirname(__FILE__) + '/db'
2
+
3
+ ActiveRecord::Schema.define do
4
+ create_table :expectations, :force => true do |t|
5
+ t.text :contents
6
+ t.boolean :executed, :default => false
7
+ end
8
+
9
+ create_table :errors, :force => true do |t|
10
+ t.text :message
11
+ t.integer :line_number
12
+ t.integer :expectation_id
13
+ end
14
+ end
@@ -4,25 +4,31 @@ require 'fakettp/error'
4
4
  module Fakettp
5
5
  class Simulator
6
6
  def self.reset
7
- Expectation.clear_all
8
- Error.clear_all
7
+ Expectation.delete_all
8
+ Error.delete_all
9
9
  end
10
10
 
11
11
  def self.verify
12
- Error << 'Expected request not received' unless Expectation.empty?
13
- Error.empty?
12
+ Error.create!(:message => 'Expected request not received') unless Expectation.all_received?
13
+ return !Error.exists?
14
14
  end
15
15
 
16
16
  def self.<< expectation
17
- Expectation << expectation
17
+ Expectation.create! :contents => expectation
18
18
  end
19
19
 
20
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
21
+ expectation = Expectation.next
22
+ if expectation
23
+ begin
24
+ expectation.execute binding
25
+ rescue Fakettp::Expectation::Error => e
26
+ expectation.errors.create :message => e.message, :line_number => e.line_number
27
+ raise e
28
+ end
29
+ else
30
+ Error.create! :message => 'Received unexpected request'
31
+ raise Expectation::Error.new('Received unexpected request')
26
32
  end
27
33
  end
28
34
 
@@ -0,0 +1,14 @@
1
+ <html>
2
+ <head>
3
+ <link rel="stylesheet" type="text/css" href="fakettp.css" />
4
+ <title>FakeTTP</title>
5
+ </head>
6
+ <body>
7
+ <% @expectations.each_with_index do |expectation, index| %>
8
+ <h1><%= index + 1 %></h1>
9
+ <div class="expectation">
10
+ <pre><%= expectation.render %></pre>
11
+ </div>
12
+ <% end %>
13
+ </body>
14
+ </html>
@@ -0,0 +1,128 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
2
+
3
+ shared_examples_for 'incorrect usage' do
4
+ it 'completes with non-zero status' do
5
+ @command.run.should_not == 0
6
+ end
7
+
8
+ it 'prints a usage method to stderr' do
9
+ @command.run
10
+ @local_std_error.string.should =~ /Usage:/
11
+ end
12
+ end
13
+
14
+ describe Fakettp::Commands::FakettpCommand do
15
+ before do
16
+ @orig_stderr = $stderr
17
+ @local_std_error = StringIO.new
18
+ $stderr = @local_std_error
19
+ end
20
+
21
+ after do
22
+ $stderr = @orig_stderr
23
+ @local_std_error.close
24
+ end
25
+
26
+ describe 'with no arguments' do
27
+ before do
28
+ @command = Fakettp::Commands::FakettpCommand.new([])
29
+ end
30
+
31
+ it_should_behave_like 'incorrect usage'
32
+ end
33
+
34
+ describe "with 'install'" do
35
+ describe 'but no directory' do
36
+ before do
37
+ @command = Fakettp::Commands::FakettpCommand.new(['install'])
38
+ end
39
+
40
+ it_should_behave_like 'incorrect usage'
41
+ end
42
+
43
+ describe 'and a directory' do
44
+ before :all do
45
+ @dir = File.expand_path(File.dirname(__FILE__) + '../../../tmp/install_test')
46
+ end
47
+
48
+ describe 'but no hostname' do
49
+ before do
50
+ @command = Fakettp::Commands::FakettpCommand.new(['install', @dir])
51
+ end
52
+
53
+ it_should_behave_like 'incorrect usage'
54
+ end
55
+
56
+ describe 'and a hostname' do
57
+ before :all do
58
+ @hostname = 'fakettp.local'
59
+ FileUtils.rm_rf @dir
60
+ @command = Fakettp::Commands::FakettpCommand.new(['install', @dir, @hostname])
61
+ end
62
+
63
+ after :all do
64
+ FileUtils.rm_rf @dir
65
+ end
66
+
67
+ describe 'when the directory already exists' do
68
+ before :all do
69
+ FileUtils.mkdir_p @dir
70
+ end
71
+
72
+ it 'completes with non-zero status' do
73
+ @command.run.should_not == 0
74
+ end
75
+
76
+ it 'prints an error message to stderr' do
77
+ @command.run
78
+ @local_std_error.string.should =~ /already exists/
79
+ end
80
+ end
81
+
82
+ describe 'when the directory does not exist' do
83
+ before :all do
84
+ @status = @command.run
85
+ end
86
+
87
+ it 'creates the directory' do
88
+ File.should be_a_directory(@dir)
89
+ end
90
+
91
+ it 'copies the correct files to the directory' do
92
+ Dir.glob(@dir + '/**/*').reject {|f| f =~ /sqlite3$/}.sort.should ==
93
+ ['config.ru', 'fakettp.yml', 'public', 'public/fakettp.css',
94
+ 'tmp'].sort.map {|f| "#{@dir}/#{f}"}
95
+ end
96
+
97
+ it 'writes the database details and hostname to fakettp.yml' do
98
+ YAML.load(File.read(@dir + '/fakettp.yml')).should == {
99
+ 'database' => {'adapter' => 'sqlite3', 'database' => 'fakettp.sqlite3'},
100
+ 'hostname' => @hostname
101
+ }
102
+ end
103
+
104
+ it "makes the tmp directory world-read/writeable" do
105
+ (File.stat(@dir + "/tmp").mode & 0777).should == 0777
106
+ end
107
+
108
+ it 'creates the database' do
109
+ run_from = File.dirname(__FILE__) + '/../../../lib'
110
+ `cd #{run_from};ruby -e "FAKETTP_BASE = '#{File.expand_path(@dir)}';require 'fakettp/expectation';p Fakettp::Expectation.new"`.should =~ /executed: false/
111
+ end
112
+
113
+ it 'completes with zero status' do
114
+ @status.should == 0
115
+ end
116
+ end
117
+ end
118
+ end
119
+ end
120
+
121
+ describe 'with an unrecognised command' do
122
+ before do
123
+ @command = Fakettp::Commands::FakettpCommand.new(['wibble'])
124
+ end
125
+
126
+ it_should_behave_like 'incorrect usage'
127
+ end
128
+ end
@@ -0,0 +1,250 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+ require 'hpricot'
3
+
4
+ describe 'Controller' do
5
+ include Sinatra::Test
6
+ before :all do
7
+ @host = YAML.load(File.read(FAKETTP_BASE + '/fakettp.yml'))['hostname']
8
+ end
9
+
10
+ before do
11
+ Fakettp::Simulator.reset
12
+ end
13
+
14
+ it 'mixes in Fakettp::ExpectationHelper' do
15
+ Sinatra::Application.included_modules.should include(Fakettp::ExpectationHelper)
16
+ end
17
+
18
+ describe 'posting to /reset' do
19
+ before do
20
+ Fakettp::Simulator.stub! :reset
21
+ end
22
+
23
+ describe 'on the fakettp host' do
24
+ def do_post
25
+ post '/reset', nil, :host => @host
26
+ end
27
+
28
+ it 'resets the simulator' do
29
+ Fakettp::Simulator.should_receive :reset
30
+ do_post
31
+ end
32
+
33
+ it 'is successful' do
34
+ do_post
35
+ @response.should be_ok
36
+ end
37
+
38
+ it 'returns a plain text response' do
39
+ do_post
40
+ @response.content_type.should == 'text/plain'
41
+ end
42
+
43
+ it 'returns an acknowledgement message' do
44
+ do_post
45
+ @response.body.should == "Reset OK\n"
46
+ end
47
+ end
48
+
49
+ describe 'on another host' do
50
+ it 'acts like any other simulated request' do
51
+ post '/reset', nil, :host => 'foo.fake.local'
52
+ response.body.should == "Simulator received mismatched request\n"
53
+ end
54
+ end
55
+ end
56
+
57
+ describe 'posting an expectation to /expect' do
58
+ before do
59
+ @body = 'foo'
60
+ Fakettp::Simulator.stub! :<<
61
+ end
62
+
63
+ describe 'on the fakettp host' do
64
+ def do_post
65
+ post '/expect', @body, :host => @host
66
+ end
67
+
68
+ it 'sets a simulator expectation using the request body' do
69
+ Fakettp::Simulator.should_receive(:<<).with @body
70
+ do_post
71
+ end
72
+
73
+ it 'is successful' do
74
+ do_post
75
+ @response.should be_ok
76
+ end
77
+
78
+ it 'returns a plain text response' do
79
+ do_post
80
+ @response.content_type.should == 'text/plain'
81
+ end
82
+
83
+ it 'returns an acknowledgement message' do
84
+ do_post
85
+ @response.body.should == "Expect OK\n"
86
+ end
87
+ end
88
+
89
+ describe 'on another host' do
90
+ it 'acts like any other simulated request' do
91
+ post '/expect', @body, :host => 'foo.fake.local'
92
+ response.body.should == "Simulator received mismatched request\n"
93
+ end
94
+ end
95
+ end
96
+
97
+ %w(GET POST PUT DELETE HEAD).each do |verb|
98
+ describe "receiving an arbitrary #{verb} on a faked host" do
99
+ before do
100
+ Fakettp::Simulator.stub! :record_error
101
+ end
102
+
103
+ define_method :do_request do
104
+ send verb.downcase.to_sym, '/foo/bar'
105
+ end
106
+
107
+ it 'simulates handling the request' do
108
+ Fakettp::Simulator.should_receive(:handle_request).with(
109
+ an_instance_of(Binding)).and_return ' '
110
+ do_request
111
+ end
112
+
113
+ describe 'when the simulator returns an error' do
114
+ before do
115
+ @error = Fakettp::Expectation::Error.new('foo')
116
+ Fakettp::Simulator.stub!(:handle_request).and_raise @error
117
+ end
118
+
119
+ it 'returns a 500 status' do
120
+ do_request
121
+ @response.status.should == 500
122
+ end
123
+
124
+ it 'returns a plain text response' do
125
+ do_request
126
+ @response.content_type.should == 'text/plain'
127
+ end
128
+
129
+ unless verb == 'HEAD' # No body for that one
130
+ it 'returns an error message' do
131
+ do_request
132
+ @response.body.should == "Simulator received mismatched request\n"
133
+ end
134
+ end
135
+ end
136
+ end
137
+ end
138
+
139
+ describe 'getting /verify' do
140
+ before do
141
+ Fakettp::Simulator.stub! :verify
142
+ @errors = 'bar'
143
+ Fakettp::Simulator.stub!(:list_errors).and_return @errors
144
+ end
145
+
146
+ describe 'on the fakettp host' do
147
+ def do_get
148
+ get '/verify', nil, :host => @host
149
+ end
150
+
151
+ it 'verifies the simulator' do
152
+ Fakettp::Simulator.should_receive :verify
153
+ do_get
154
+ end
155
+
156
+ it 'returns a plain text response' do
157
+ do_get
158
+ @response.content_type.should == 'text/plain'
159
+ end
160
+
161
+ describe 'when verification succeeds' do
162
+ before do
163
+ Fakettp::Simulator.stub!(:verify).and_return true
164
+ end
165
+
166
+ it 'is successful' do
167
+ do_get
168
+ @response.should be_ok
169
+ end
170
+
171
+ it 'returns an acknowledgement message' do
172
+ do_get
173
+ @response.body.should == "Verify OK\n"
174
+ end
175
+ end
176
+
177
+ describe 'when verification fails' do
178
+ before do
179
+ Fakettp::Simulator.stub!(:verify).and_return false
180
+ end
181
+
182
+ it 'returns 500 Internal Error' do
183
+ do_get
184
+ @response.status.should == 500
185
+ end
186
+
187
+ it 'lists the errors' do
188
+ do_get
189
+ @response.body.should == @errors
190
+ end
191
+ end
192
+ end
193
+
194
+ describe 'on another host' do
195
+ it 'acts like any other simulated request' do
196
+ get '/verify', nil, :host => 'foo.fake.local'
197
+ response.body.should == "Simulator received mismatched request\n"
198
+ end
199
+ end
200
+ end
201
+
202
+ describe 'getting /' do
203
+ describe 'on the fakettp host' do
204
+ before do
205
+ expectation_1 = stub :expectation, :id => 1, :render => 'foo'
206
+ expectation_2 = stub :expectation, :id => 2, :render => 'bar'
207
+ Fakettp::Expectation.stub!(:all).and_return [expectation_1, expectation_2]
208
+ end
209
+
210
+ def do_get
211
+ get '/', nil, :host => @host
212
+ @response_doc = Hpricot(@response.body)
213
+ end
214
+
215
+ it 'returns an html response' do
216
+ do_get
217
+ response.content_type.should == 'text/html'
218
+ end
219
+
220
+ it 'sets the title' do
221
+ do_get
222
+ (@response_doc/'head/title').inner_html.should == 'FakeTTP'
223
+ end
224
+
225
+ it 'renders a div for each expectation' do
226
+ do_get
227
+ @response_doc.search("//div[@class='expectation']").size.should == 2
228
+ end
229
+
230
+ it 'numbers the expectations' do
231
+ do_get
232
+ (@response_doc/"//h1[1]").inner_html.should == '1'
233
+ (@response_doc/"//h1[2]").inner_html.should == '2'
234
+ end
235
+
236
+ it 'displays the expectation contents' do
237
+ do_get
238
+ (@response_doc/"//div[@class='expectation'][1]/pre").inner_html.should == 'foo'
239
+ (@response_doc/"//div[@class='expectation'][2]/pre").inner_html.should == 'bar'
240
+ end
241
+ end
242
+
243
+ describe 'on another host' do
244
+ it 'acts like any other simulated request' do
245
+ get '/', nil, :host => 'foo.fake.local'
246
+ response.body.should == "Simulator received mismatched request\n"
247
+ end
248
+ end
249
+ end
250
+ end