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.
- data/.gitignore +6 -0
- data/README.rdoc +201 -0
- data/Rakefile +113 -0
- data/VERSION +1 -0
- data/features/control.feature +12 -0
- data/features/dashboard.feature +38 -0
- data/features/expectations.feature +27 -0
- data/features/expectations/expect_get +3 -0
- data/features/expectations/get_foo +3 -0
- data/features/expectations/get_root +3 -0
- data/features/expectations/pass_and_fail +5 -0
- data/features/expectations/set_response +5 -0
- data/features/step_definitions/expectations.rb +5 -0
- data/features/step_definitions/http.rb +42 -0
- data/features/step_definitions/simulator.rb +25 -0
- data/features/step_definitions/webrat_steps.rb +99 -0
- data/features/support/env.rb +5 -0
- data/features/support/fakettp.rb +2 -0
- data/features/support/paths.rb +12 -0
- data/features/support/xpath.rb +10 -0
- data/features/verification.feature +49 -0
- data/lib/fakettp/commands/fakettp_command.rb +31 -19
- data/lib/fakettp/controller.rb +12 -0
- data/lib/fakettp/db.rb +7 -0
- data/lib/fakettp/error.rb +6 -18
- data/lib/fakettp/expectation.rb +28 -44
- data/lib/fakettp/fakettp.yml +3 -0
- data/lib/fakettp/public/fakettp.css +33 -0
- data/lib/fakettp/schema.rb +14 -0
- data/lib/fakettp/simulator.rb +16 -10
- data/lib/fakettp/views/index.erb +14 -0
- data/spec/fakettp/commands/fakettp_command_spec.rb +128 -0
- data/spec/fakettp/controller_spec.rb +250 -0
- data/spec/fakettp/error_spec.rb +40 -0
- data/spec/fakettp/expectation_helper_spec.rb +33 -0
- data/spec/fakettp/expectation_spec.rb +142 -0
- data/spec/fakettp/simulator_spec.rb +149 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +18 -0
- metadata +73 -24
- 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
|
data/lib/fakettp/simulator.rb
CHANGED
@@ -4,25 +4,31 @@ require 'fakettp/error'
|
|
4
4
|
module Fakettp
|
5
5
|
class Simulator
|
6
6
|
def self.reset
|
7
|
-
Expectation.
|
8
|
-
Error.
|
7
|
+
Expectation.delete_all
|
8
|
+
Error.delete_all
|
9
9
|
end
|
10
10
|
|
11
11
|
def self.verify
|
12
|
-
Error
|
13
|
-
Error.
|
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
|
17
|
+
Expectation.create! :contents => expectation
|
18
18
|
end
|
19
19
|
|
20
20
|
def self.handle_request binding
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
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
|