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
@@ -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