spoofer 0.1.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.
- checksums.yaml +7 -0
- data/LICENSE.txt +20 -0
- data/README.md +93 -0
- data/Rakefile +25 -0
- data/features/checking_requests_were_made.feature +39 -0
- data/features/configuring_spoofer_via_http.feature +175 -0
- data/features/echoing_request_in_response.feature +76 -0
- data/features/logging_requests.feature +13 -0
- data/features/resetting_stubs.feature +16 -0
- data/features/steps/http_client_steps.rb +60 -0
- data/features/steps/logging_steps.rb +3 -0
- data/features/steps/shell_steps.rb +10 -0
- data/features/steps/spoofer_steps.rb +21 -0
- data/features/stubbing_requests_by_method.feature +25 -0
- data/features/stubbing_requests_by_path.feature +60 -0
- data/features/stubbing_requests_from_a_file.feature +33 -0
- data/features/stubbing_requests_with_parameters.feature +28 -0
- data/features/support/env.rb +15 -0
- data/features/support/hash_key_path.rb +7 -0
- data/features/support/http_client.rb +50 -0
- data/features/support/spoofer_runner.rb +7 -0
- data/features/using_rack_middlewares.feature +31 -0
- data/lib/spoofer.rb +104 -0
- data/lib/spoofer/api.rb +50 -0
- data/lib/spoofer/api/api_request.rb +49 -0
- data/lib/spoofer/api/stub.rb +39 -0
- data/lib/spoofer/fake_host.rb +102 -0
- data/lib/spoofer/fake_host/helpers.rb +11 -0
- data/lib/spoofer/fake_host/request_echo.rb +42 -0
- data/lib/spoofer/fake_host/stubbed_request.rb +73 -0
- data/lib/spoofer/version.rb +3 -0
- data/spec/fixtures/import_stubs.spoof +3 -0
- data/spec/spec_helper.rb +21 -0
- data/spec/spoofer/fake_host_spec.rb +74 -0
- data/spec/spoofer/stubbed_request_spec.rb +16 -0
- data/spec/support/.keep +0 -0
- data/spec/support/matchers.rb +7 -0
- data/spec/support/pry.rb +1 -0
- data/spec/support/request_helper.rb +7 -0
- metadata +233 -0
@@ -0,0 +1,16 @@
|
|
1
|
+
Feature: Resetting stubs
|
2
|
+
In order to create a deterministic clean slate at the beginning of my specs
|
3
|
+
As a developer
|
4
|
+
I want to be able to reset all previously configured request stubs
|
5
|
+
|
6
|
+
Scenario: Clearing a stubbed request
|
7
|
+
Given I have a spoofer specification with:
|
8
|
+
"""
|
9
|
+
Spoofer.mimic(:port => 11988).get("/some/path").returning("Hello World", 201)
|
10
|
+
"""
|
11
|
+
When I evaluate the code:
|
12
|
+
"""
|
13
|
+
Spoofer.reset_all!
|
14
|
+
"""
|
15
|
+
And I make an HTTP GET request to "http://localhost:11988/some/path"
|
16
|
+
Then I should receive an HTTP 404 response with an empty body
|
@@ -0,0 +1,60 @@
|
|
1
|
+
Before do
|
2
|
+
@httpclient = HttpClient.new
|
3
|
+
end
|
4
|
+
|
5
|
+
def headers_from_string(string)
|
6
|
+
string.split("\n").inject({}) do |headers, header_string|
|
7
|
+
headers.tap do |h|
|
8
|
+
components = header_string.split(":")
|
9
|
+
h[components[0].strip] = components[1].strip
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
When /^I make an HTTP (POST|PUT) request to "([^\"]*)" with the payload:$/ do |http_method, url, payload|
|
15
|
+
@httpclient.perform_request_with_payload(url, http_method, payload)
|
16
|
+
end
|
17
|
+
|
18
|
+
When /^I make an HTTP (POST|PUT) request with a "([^\"]*)" content-type to "([^\"]*)" and the payload:$/ do |http_method, content_type, url, payload|
|
19
|
+
@httpclient.perform_request_with_payload(url, http_method, payload, :content_type => content_type)
|
20
|
+
end
|
21
|
+
|
22
|
+
When /^I make an HTTP (GET|POST|PUT|DELETE|HEAD) request to "([^\"]*)"$/ do |http_method, url|
|
23
|
+
@httpclient.perform_request(url, http_method)
|
24
|
+
end
|
25
|
+
|
26
|
+
When /^I make an HTTP (GET|POST|PUT|DELETE|HEAD) request to "([^\"]*)" with the header "([^\"]*)"$/ do |http_method, url, header|
|
27
|
+
@httpclient.perform_request(url, http_method, nil, headers_from_string(header))
|
28
|
+
end
|
29
|
+
|
30
|
+
Then /^I should receive an HTTP (\d+) response with an empty body$/ do |status_code|
|
31
|
+
steps %Q{
|
32
|
+
Then I should receive an HTTP #{status_code} response with a body matching ""
|
33
|
+
}
|
34
|
+
end
|
35
|
+
|
36
|
+
Then /^I should receive an HTTP (\d+) response with a body matching "([^\"]*)"$/ do |status_code, http_body|
|
37
|
+
@httpclient.should have_response_with_code_and_body(status_code.to_i, http_body)
|
38
|
+
end
|
39
|
+
|
40
|
+
Then /^I should receive an HTTP (\d+) response with a body containing:$/ do |status_code, http_body|
|
41
|
+
@httpclient.should have_response_with_code_and_body(status_code.to_i, http_body)
|
42
|
+
end
|
43
|
+
|
44
|
+
Then /^I should receive an HTTP (\d+) response$/ do |status_code|
|
45
|
+
@httpclient.should have_response_with_code(status_code.to_i)
|
46
|
+
end
|
47
|
+
|
48
|
+
Then /^I should receive an HTTP (\d+) response with the value "([^\"]*)" for the header "([^\"]*)"$/ do |status_code, header_value, header_key|
|
49
|
+
@httpclient.should have_response_with_code_and_header(status_code.to_i, header_key, header_value)
|
50
|
+
end
|
51
|
+
|
52
|
+
Then /^I should receive an HTTP (\d+) response with the JSON value "([^\"]*)" for the key path "([^\"]*)"$/ do |status, json_value, key_path|
|
53
|
+
json = JSON.parse(@httpclient.last_response.to_s)
|
54
|
+
json.value_for_key_path(key_path).should == json_value
|
55
|
+
end
|
56
|
+
|
57
|
+
Then /^I should receive an HTTP (\d+) response with the Plist value "([^\"]*)" for the key path "([^\"]*)"$/ do |status, json_value, key_path|
|
58
|
+
plist = Plist.parse_xml(@httpclient.last_response.to_s)
|
59
|
+
plist.value_for_key_path(key_path).should == json_value
|
60
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
TEMP_FILES = []
|
2
|
+
|
3
|
+
Given /^the file "([^\"]*)" exists with the contents:$/ do |file_path, string|
|
4
|
+
File.open(file_path, "w") { |io| io.write(string) }
|
5
|
+
TEMP_FILES << file_path
|
6
|
+
end
|
7
|
+
|
8
|
+
After do
|
9
|
+
TEMP_FILES.each { |path| FileUtils.rm(path) if File.exist?(path) }
|
10
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
Given /^I have a spoofer specification with:$/ do |string|
|
2
|
+
SpooferRunner.new.evaluate(string)
|
3
|
+
end
|
4
|
+
|
5
|
+
Given /^that Spoofer is running and accepting remote configuration on "([^\"]*)"$/ do |api_endpoint|
|
6
|
+
Spoofer.mimic(:port => 11988, :remote_configuration_path => api_endpoint)
|
7
|
+
end
|
8
|
+
|
9
|
+
Given /^that Spoofer is running and accepting remote configuration on "([^\"]*)" with the existing stubs:$/ do |api_endpoint, existing_stubs|
|
10
|
+
Spoofer.mimic(:port => 11988, :remote_configuration_path => api_endpoint) do
|
11
|
+
eval(existing_stubs)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
When /^I evaluate the code:$/ do |string|
|
16
|
+
eval(string)
|
17
|
+
end
|
18
|
+
|
19
|
+
After do
|
20
|
+
Spoofer.cleanup!
|
21
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
Feature: Stubbing requests by path
|
2
|
+
In order to test a range of API endpoints and HTTP verbs
|
3
|
+
As a developer
|
4
|
+
I want to be able to stub requests to return specific responses depending on the request method
|
5
|
+
|
6
|
+
Scenario: Stubbing a POST request to return a 201 response
|
7
|
+
Given I have a spoofer specification with:
|
8
|
+
"""
|
9
|
+
Spoofer.mimic(:port => 11988).post("/some/path").returning("Hello World", 201)
|
10
|
+
"""
|
11
|
+
When I make an HTTP POST request to "http://localhost:11988/some/path"
|
12
|
+
Then I should receive an HTTP 201 response with a body matching "Hello World"
|
13
|
+
|
14
|
+
Scenario: Stubbing the same path with different responses for GET and POST
|
15
|
+
Given I have a spoofer specification with:
|
16
|
+
"""
|
17
|
+
Spoofer.mimic(:port => 11988) do
|
18
|
+
get("/some/path").returning("Some Record", 200)
|
19
|
+
post("/some/path").returning("Created", 201)
|
20
|
+
end
|
21
|
+
"""
|
22
|
+
When I make an HTTP GET request to "http://localhost:11988/some/path"
|
23
|
+
Then I should receive an HTTP 200 response with a body matching "Some Record"
|
24
|
+
When I make an HTTP POST request to "http://localhost:11988/some/path"
|
25
|
+
Then I should receive an HTTP 201 response with a body matching "Created"
|
@@ -0,0 +1,60 @@
|
|
1
|
+
Feature: Stubbing requests by path
|
2
|
+
In order to test my app through its entire stack without depending on an external API
|
3
|
+
As a developer
|
4
|
+
I want to be able to stub requests to specific paths to return a canned response
|
5
|
+
|
6
|
+
Scenario: Stubbing a GET request to /some/path and return an empty response
|
7
|
+
Given I have a spoofer specification with:
|
8
|
+
"""
|
9
|
+
Spoofer.mimic(:port => 11988).get("/some/path")
|
10
|
+
"""
|
11
|
+
When I make an HTTP GET request to "http://localhost:11988/some/path"
|
12
|
+
Then I should receive an HTTP 200 response with an empty body
|
13
|
+
|
14
|
+
Scenario: Stubbing a GET request to /some/path and returning a non-empty response
|
15
|
+
Given I have a spoofer specification with:
|
16
|
+
"""
|
17
|
+
Spoofer.mimic(:port => 11988).get("/some/path").returning("Hello World")
|
18
|
+
"""
|
19
|
+
When I make an HTTP GET request to "http://localhost:11988/some/path"
|
20
|
+
Then I should receive an HTTP 200 response with a body matching "Hello World"
|
21
|
+
|
22
|
+
Scenario: Requesting an un-stubbed path and getting a 404 response
|
23
|
+
Given I have a spoofer specification with:
|
24
|
+
"""
|
25
|
+
Spoofer.mimic(:port => 11988).get("/some/path")
|
26
|
+
"""
|
27
|
+
When I make an HTTP GET request to "http://localhost:11988/some/other/path"
|
28
|
+
Then I should receive an HTTP 404 response with an empty body
|
29
|
+
|
30
|
+
Scenario: Stubbing a POST request to /some/path and return an empty response
|
31
|
+
Given I have a spoofer specification with:
|
32
|
+
"""
|
33
|
+
Spoofer.mimic(:port => 11988).post("/some/path")
|
34
|
+
"""
|
35
|
+
When I make an HTTP POST request to "http://localhost:11988/some/path"
|
36
|
+
Then I should receive an HTTP 200 response with an empty body
|
37
|
+
|
38
|
+
Scenario: Stubbing a PUT request to /some/path and return an empty response
|
39
|
+
Given I have a spoofer specification with:
|
40
|
+
"""
|
41
|
+
Spoofer.mimic(:port => 11988).put("/some/path")
|
42
|
+
"""
|
43
|
+
When I make an HTTP PUT request to "http://localhost:11988/some/path"
|
44
|
+
Then I should receive an HTTP 200 response with an empty body
|
45
|
+
|
46
|
+
Scenario: Stubbing a DELETE request to /some/path and return an empty response
|
47
|
+
Given I have a spoofer specification with:
|
48
|
+
"""
|
49
|
+
Spoofer.mimic(:port => 11988).delete("/some/path")
|
50
|
+
"""
|
51
|
+
When I make an HTTP DELETE request to "http://localhost:11988/some/path"
|
52
|
+
Then I should receive an HTTP 200 response with an empty body
|
53
|
+
|
54
|
+
Scenario: Stubbing a HEAD request to /some/path and return an empty response
|
55
|
+
Given I have a spoofer specification with:
|
56
|
+
"""
|
57
|
+
Spoofer.mimic(:port => 11988).head("/some/path")
|
58
|
+
"""
|
59
|
+
When I make an HTTP HEAD request to "http://localhost:11988/some/path"
|
60
|
+
Then I should receive an HTTP 200 response with an empty body
|
@@ -0,0 +1,33 @@
|
|
1
|
+
Feature: Stubbing requests from a file
|
2
|
+
In order to pre-load Spoofer with a set of stubs
|
3
|
+
As a developer
|
4
|
+
I want to be able to store my stub configuration in a separate file and load it in at runtime
|
5
|
+
|
6
|
+
Scenario: Stubbing requests using a file
|
7
|
+
Given the file "/tmp/test.spoof" exists with the contents:
|
8
|
+
"""
|
9
|
+
get("/ping") { "pong" }
|
10
|
+
"""
|
11
|
+
And I have a spoofer specification with:
|
12
|
+
"""
|
13
|
+
Spoofer.mimic(:port => 11988) do
|
14
|
+
import "/tmp/test.mimic"
|
15
|
+
end
|
16
|
+
"""
|
17
|
+
When I make an HTTP GET request to "http://localhost:11988/ping"
|
18
|
+
Then I should receive an HTTP 200 response with a body matching "pong"
|
19
|
+
|
20
|
+
Scenario: Stubbed requests from a file persist even when Spoofer is cleared
|
21
|
+
Given the file "/tmp/test.spoof" exists with the contents:
|
22
|
+
"""
|
23
|
+
get("/ping") { "pong" }
|
24
|
+
"""
|
25
|
+
And I have a spoofer specification with:
|
26
|
+
"""
|
27
|
+
Spoofer.mimic(:port => 11988) do
|
28
|
+
import "/tmp/test.mimic"
|
29
|
+
end
|
30
|
+
Spoofer.reset_all!
|
31
|
+
"""
|
32
|
+
When I make an HTTP GET request to "http://localhost:11988/ping"
|
33
|
+
Then I should receive an HTTP 200 response with a body matching "pong"
|
@@ -0,0 +1,28 @@
|
|
1
|
+
Feature: Stubbing requests by path
|
2
|
+
In order to test requests that use specific query parameters
|
3
|
+
As a developer
|
4
|
+
I want to be able to only stub requests that have the correct parameters
|
5
|
+
|
6
|
+
Scenario: Accepting any parameters to a stubbed path
|
7
|
+
Given I have a spoofer specification with:
|
8
|
+
"""
|
9
|
+
Spoofer.mimic(:port => 11988).get("/some/path")
|
10
|
+
"""
|
11
|
+
When I make an HTTP GET request to "http://localhost:11988/some/path?foo=bar"
|
12
|
+
Then I should receive an HTTP 200 response with an empty body
|
13
|
+
|
14
|
+
Scenario: Accepting specific parameters and matching
|
15
|
+
Given I have a spoofer specification with:
|
16
|
+
"""
|
17
|
+
Spoofer.mimic(:port => 11988).get("/some/path").with_query_parameters("foo" => "bar")
|
18
|
+
"""
|
19
|
+
When I make an HTTP GET request to "http://localhost:11988/some/path?foo=bar"
|
20
|
+
Then I should receive an HTTP 200 response with an empty body
|
21
|
+
|
22
|
+
Scenario: Accepting specific parameters and matching
|
23
|
+
Given I have a spoofer specification with:
|
24
|
+
"""
|
25
|
+
Spoofer.mimic(:port => 11988).get("/some/path").with_query_parameters("foo" => "bar")
|
26
|
+
"""
|
27
|
+
When I make an HTTP GET request to "http://localhost:11988/some/path?foo=baz"
|
28
|
+
Then I should receive an HTTP 404 response
|
@@ -0,0 +1,15 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), *%w[.. .. lib]))
|
2
|
+
|
3
|
+
TEST_STDOUT = StringIO.new
|
4
|
+
|
5
|
+
Before do
|
6
|
+
if test_proxy = ENV["MIMIC_TEST_PROXY"]
|
7
|
+
HttpClient.use_proxy(test_proxy)
|
8
|
+
end
|
9
|
+
|
10
|
+
$stdout = TEST_STDOUT
|
11
|
+
end
|
12
|
+
|
13
|
+
After do
|
14
|
+
$stdout = STDOUT
|
15
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'rest_client'
|
2
|
+
|
3
|
+
class HttpClient
|
4
|
+
attr_reader :last_response
|
5
|
+
|
6
|
+
def self.use_proxy(proxy)
|
7
|
+
RestClient.proxy = proxy
|
8
|
+
end
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
@last_response = nil
|
12
|
+
end
|
13
|
+
|
14
|
+
def perform_request(url, method, payload = nil, options={})
|
15
|
+
RestClient.send(method.downcase, url, options) do |response, request|
|
16
|
+
@last_response = response
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def perform_request_with_payload(url, method, payload, options={})
|
21
|
+
RestClient.send(method.downcase, url, payload, options) do |response, request|
|
22
|
+
@last_response = response
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def has_response_with_code_and_body?(status_code, response_body)
|
27
|
+
if @last_response
|
28
|
+
return @last_response.code.to_i == status_code && @last_response.to_s == response_body
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def has_response_with_code?(status_code)
|
33
|
+
if @last_response
|
34
|
+
@last_response.code.to_i == status_code
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def has_response_with_code_and_header?(status_code, header_key, header_value)
|
39
|
+
if @last_response
|
40
|
+
@last_response.code.to_i == status_code &&
|
41
|
+
@last_response.headers[beautify_header(header_key)] == header_value
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def beautify_header(header_key)
|
48
|
+
header_key.downcase.gsub(/-/, '_').to_sym
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
Feature: Injecting Rack middleware into the request chain for a stub
|
2
|
+
In order to test common scenarios (e.g. authentication)
|
3
|
+
As a developer
|
4
|
+
I want to be able to use Rack middlewares for certain responses
|
5
|
+
|
6
|
+
Scenario: Using Rack::Auth to simulate failed authentication
|
7
|
+
Given I have a spoofer specification with:
|
8
|
+
"""
|
9
|
+
Spoofer.mimic(:port => 11988) do
|
10
|
+
use Rack::Auth::Basic do |username, password|
|
11
|
+
end
|
12
|
+
|
13
|
+
get("/some/path")
|
14
|
+
end
|
15
|
+
"""
|
16
|
+
When I make an HTTP GET request to "http://localhost:11988/some/path"
|
17
|
+
Then I should receive an HTTP 401 response
|
18
|
+
|
19
|
+
Scenario: Using Rack::Auth to simulate successful authentication
|
20
|
+
Given I have a spoofer specification with:
|
21
|
+
"""
|
22
|
+
Spoofer.mimic(:port => 11988) do
|
23
|
+
use Rack::Auth::Basic do |username, password|
|
24
|
+
username == 'test' && password == 'pass'
|
25
|
+
end
|
26
|
+
|
27
|
+
get("/some/path")
|
28
|
+
end
|
29
|
+
"""
|
30
|
+
When I make an HTTP GET request to "http://test:pass@localhost:11988/some/path"
|
31
|
+
Then I should receive an HTTP 200 response
|
data/lib/spoofer.rb
ADDED
@@ -0,0 +1,104 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
require 'rack'
|
3
|
+
require 'logger'
|
4
|
+
require 'json'
|
5
|
+
require 'plist'
|
6
|
+
require 'socket'
|
7
|
+
require 'digest'
|
8
|
+
require 'sinatra'
|
9
|
+
|
10
|
+
module Spoofer
|
11
|
+
autoload :API, 'spoofer/api'
|
12
|
+
autoload :FakeHost, 'spoofer/fake_host'
|
13
|
+
|
14
|
+
SPOOFER_DEFAULT_PORT = 11988
|
15
|
+
|
16
|
+
SPOOFER_DEFAULT_OPTIONS = {
|
17
|
+
hostname: 'localhost',
|
18
|
+
port: SPOOFER_DEFAULT_PORT,
|
19
|
+
remote_configuration_path: nil,
|
20
|
+
fork: true,
|
21
|
+
log: nil
|
22
|
+
}
|
23
|
+
|
24
|
+
def self.poser(options = {}, &block)
|
25
|
+
options = SPOOFER_DEFAULT_OPTIONS.merge(options)
|
26
|
+
|
27
|
+
host = FakeHost.new(options).tap do |h|
|
28
|
+
h.instance_eval(&block) if block_given?
|
29
|
+
Server.instance.serve(h, options)
|
30
|
+
end
|
31
|
+
add_host(host)
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.cleanup!
|
35
|
+
Spoofer::Server.instance.shutdown
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.reset_all!
|
39
|
+
@hosts.each { |h| h.clear }
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def self.add_host(host)
|
45
|
+
host.tap { |h| (@hosts ||= []) << h }
|
46
|
+
end
|
47
|
+
|
48
|
+
class Server
|
49
|
+
include Singleton
|
50
|
+
|
51
|
+
def logger
|
52
|
+
@logger ||= Logger.new(StringIO.new)
|
53
|
+
end
|
54
|
+
|
55
|
+
def serve(app, options)
|
56
|
+
if options[:fork]
|
57
|
+
@thread = Thread.fork do
|
58
|
+
start_service(app, options)
|
59
|
+
end
|
60
|
+
|
61
|
+
wait_for_service(app.hostname, options[:port])
|
62
|
+
|
63
|
+
else
|
64
|
+
start_service(app, options)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def start_service(app, options)
|
69
|
+
Rack::Handler::Thin.run(app.url_map, {
|
70
|
+
:Port => options[:port],
|
71
|
+
:Logger => logger,
|
72
|
+
:AccessLog => logger,
|
73
|
+
})
|
74
|
+
end
|
75
|
+
|
76
|
+
def shutdown
|
77
|
+
Thread.kill(@thread) if @thread
|
78
|
+
end
|
79
|
+
|
80
|
+
# courtesy of http://is.gd/eoYho
|
81
|
+
|
82
|
+
def listening?(host, port)
|
83
|
+
begin
|
84
|
+
socket = TCPSocket.new(host, port)
|
85
|
+
socket.close unless socket.nil?
|
86
|
+
true
|
87
|
+
rescue Errno::ECONNREFUSED, SocketError,
|
88
|
+
Errno::EBADF, # Windows
|
89
|
+
Errno::EADDRNOTAVAIL # Windows
|
90
|
+
false
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def wait_for_service(host, port, timeout = 5)
|
95
|
+
start_time = Time.now
|
96
|
+
|
97
|
+
until listening?(host, port)
|
98
|
+
if timeout && (Time.now > (start_time + timeout))
|
99
|
+
raise SocketError.new("Socket did not open within #{timeout} seconds")
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|