pact-mock_service 0.2.3 → 0.2.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +8 -0
- data/lib/pact/consumer/app_manager.rb +23 -35
- data/lib/pact/consumer/mock_service/app.rb +11 -75
- data/lib/pact/consumer/mock_service/error_handler.rb +28 -0
- data/lib/pact/consumer/mock_service/index_get.rb +22 -0
- data/lib/pact/consumer/mock_service/interaction_delete.rb +3 -3
- data/lib/pact/consumer/mock_service/interaction_post.rb +2 -2
- data/lib/pact/consumer/mock_service/log_get.rb +2 -2
- data/lib/pact/consumer/mock_service/missing_interactions_get.rb +2 -3
- data/lib/pact/consumer/mock_service/mock_service_administration_endpoint.rb +11 -13
- data/lib/pact/consumer/mock_service/pact_post.rb +2 -2
- data/lib/pact/consumer/mock_service/request_handlers.rb +41 -0
- data/lib/pact/consumer/mock_service/verification_get.rb +2 -2
- data/lib/pact/consumer/mock_service.rb +41 -0
- data/lib/pact/consumer/server.rb +28 -7
- data/lib/pact/mock_service/cli/pidfile.rb +97 -0
- data/lib/pact/mock_service/cli.rb +109 -59
- data/lib/pact/mock_service/control_server/app.rb +42 -0
- data/lib/pact/mock_service/control_server/delegator.rb +55 -0
- data/lib/pact/mock_service/control_server/index.rb +25 -0
- data/lib/pact/mock_service/control_server/mock_service_creator.rb +34 -0
- data/lib/pact/mock_service/control_server/mock_services.rb +26 -0
- data/lib/pact/mock_service/control_server/require_pacticipant_headers.rb +20 -0
- data/lib/pact/mock_service/control_server/run.rb +68 -0
- data/lib/pact/mock_service/run.rb +81 -0
- data/lib/pact/mock_service/server/restart.rb +20 -0
- data/lib/pact/mock_service/server/spawn.rb +37 -0
- data/lib/pact/mock_service/server/wait_for_server_up.rb +35 -0
- data/lib/pact/mock_service/spawn.rb +68 -0
- data/lib/pact/mock_service/version.rb +1 -1
- metadata +18 -3
- data/lib/pact/consumer/mock_service/web_request_administration.rb +0 -27
@@ -0,0 +1,97 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
|
3
|
+
module Pact
|
4
|
+
module MockService
|
5
|
+
class CLI < Thor
|
6
|
+
|
7
|
+
class Pidfile
|
8
|
+
|
9
|
+
attr_accessor :pid_dir, :name, :pid
|
10
|
+
|
11
|
+
def initialize options
|
12
|
+
@pid_dir = options[:pid_dir] || 'tmp/pids'
|
13
|
+
@name = options[:name] || default_name
|
14
|
+
@pid = options[:pid] || Process.pid
|
15
|
+
end
|
16
|
+
|
17
|
+
def file_exists?
|
18
|
+
File.exist?(pidfile_path)
|
19
|
+
end
|
20
|
+
|
21
|
+
def process_running?
|
22
|
+
process_exists? pid_from_file
|
23
|
+
end
|
24
|
+
|
25
|
+
def pidfile_path
|
26
|
+
File.join(pid_dir, name)
|
27
|
+
end
|
28
|
+
|
29
|
+
def pid_from_file
|
30
|
+
File.read(pidfile_path).to_i
|
31
|
+
end
|
32
|
+
|
33
|
+
def default_name
|
34
|
+
File.basename($0, File.extname($0)) + ".pid"
|
35
|
+
end
|
36
|
+
|
37
|
+
def process_exists? pid
|
38
|
+
Process.kill 0, pid
|
39
|
+
true
|
40
|
+
rescue Errno::ESRCH
|
41
|
+
false
|
42
|
+
end
|
43
|
+
|
44
|
+
def file_exists_and_process_running?
|
45
|
+
file_exists? && process_running?
|
46
|
+
end
|
47
|
+
|
48
|
+
def can_start?
|
49
|
+
if file_exists? && process_running?
|
50
|
+
$stderr.puts "Server already running."
|
51
|
+
false
|
52
|
+
elsif file_exists?
|
53
|
+
$stderr.puts "WARN: PID file #{pidfile_path} already exists, but process is not running. Overwriting pidfile."
|
54
|
+
true
|
55
|
+
else
|
56
|
+
true
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def write
|
61
|
+
FileUtils.mkdir_p pid_dir
|
62
|
+
File.open(pidfile_path, "w") { |file| file << pid }
|
63
|
+
end
|
64
|
+
|
65
|
+
def delete
|
66
|
+
FileUtils.rm pidfile_path
|
67
|
+
end
|
68
|
+
|
69
|
+
def waitpid
|
70
|
+
tries = 0
|
71
|
+
sleep_time = 0.1
|
72
|
+
while process_running? && tries < 100
|
73
|
+
sleep sleep_time
|
74
|
+
tries += 1
|
75
|
+
end
|
76
|
+
raise "Process #{pid_from_file} not stopped after {100 * sleep_time} seconds." if process_running?
|
77
|
+
end
|
78
|
+
|
79
|
+
def kill_process
|
80
|
+
if file_exists?
|
81
|
+
begin
|
82
|
+
`ps -ef | grep pact`
|
83
|
+
Process.kill 2, pid_from_file
|
84
|
+
waitpid
|
85
|
+
delete
|
86
|
+
rescue Errno::ESRCH
|
87
|
+
$stderr.puts "Process in PID file #{pidfile_path} not running. Deleting PID file."
|
88
|
+
delete
|
89
|
+
end
|
90
|
+
else
|
91
|
+
$stderr.puts "No PID file found at #{pidfile_path}, server probably not running. Use `ps -ef | grep pact` if you suspect the process is still running."
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
@@ -1,100 +1,150 @@
|
|
1
|
-
require 'find_a_port'
|
2
1
|
require 'thor'
|
3
|
-
require 'thwait'
|
4
2
|
require 'webrick/https'
|
5
3
|
require 'rack/handler/webrick'
|
6
4
|
require 'fileutils'
|
5
|
+
require 'pact/mock_service/server/wait_for_server_up'
|
6
|
+
require 'pact/mock_service/cli/pidfile'
|
7
|
+
require 'socket'
|
7
8
|
|
8
9
|
module Pact
|
9
10
|
module MockService
|
10
11
|
class CLI < Thor
|
11
12
|
|
12
|
-
desc '
|
13
|
+
desc 'service', "Start a mock service. If the consumer, provider and pact-dir options are provided, the pact will be written automatically on shutdown."
|
13
14
|
method_option :port, aliases: "-p", desc: "Port on which to run the service"
|
14
|
-
method_option :ssl, desc: "Use a self-signed SSL cert to run the service over HTTPS"
|
15
15
|
method_option :log, aliases: "-l", desc: "File to which to log output"
|
16
|
+
method_option :ssl, desc: "Use a self-signed SSL cert to run the service over HTTPS"
|
16
17
|
method_option :cors, aliases: "-o", desc: "Support browser security in tests by responding to OPTIONS requests and adding CORS headers to mocked responses"
|
17
18
|
method_option :pact_dir, aliases: "-d", desc: "Directory to which the pacts will be written"
|
18
19
|
method_option :consumer, desc: "Consumer name"
|
19
20
|
method_option :provider, desc: "Provider name"
|
20
21
|
|
21
|
-
def
|
22
|
-
|
22
|
+
def service
|
23
|
+
require 'pact/mock_service/run'
|
24
|
+
Run.(options)
|
23
25
|
end
|
24
26
|
|
25
|
-
|
27
|
+
desc 'control', "Run a Pact mock service control server."
|
28
|
+
method_option :port, aliases: "-p", desc: "Port on which to run the service"
|
29
|
+
method_option :log_dir, aliases: "-l", desc: "File to which to log output"
|
30
|
+
method_option :pact_dir, aliases: "-d", desc: "Directory to which the pacts will be written"
|
31
|
+
method_option :ssl, desc: "Use a self-signed SSL cert to run the service over HTTPS"
|
32
|
+
method_option :cors, aliases: "-o", desc: "Support browser security in tests by responding to OPTIONS requests and adding CORS headers to mocked responses"
|
26
33
|
|
27
|
-
|
34
|
+
def control
|
35
|
+
require 'pact/mock_service/control_server/run'
|
36
|
+
ControlServer::Run.(options)
|
37
|
+
end
|
28
38
|
|
29
|
-
|
39
|
+
desc 'start', "Start a mock service. If the consumer, provider and pact-dir options are provided, the pact will be written automatically on shutdown."
|
40
|
+
method_option :port, aliases: "-p", default: '1234', desc: "Port on which to run the service"
|
41
|
+
method_option :log, aliases: "-l", desc: "File to which to log output"
|
42
|
+
method_option :ssl, desc: "Use a self-signed SSL cert to run the service over HTTPS"
|
43
|
+
method_option :cors, aliases: "-o", desc: "Support browser security in tests by responding to OPTIONS requests and adding CORS headers to mocked responses"
|
44
|
+
method_option :pact_dir, aliases: "-d", desc: "Directory to which the pacts will be written"
|
45
|
+
method_option :consumer, desc: "Consumer name"
|
46
|
+
method_option :provider, desc: "Provider name"
|
47
|
+
method_option :pid_dir, desc: "PID dir", default: 'tmp/pids'
|
30
48
|
|
31
|
-
def
|
32
|
-
|
49
|
+
def start
|
50
|
+
start_server(mock_service_pidfile) do
|
51
|
+
service
|
52
|
+
end
|
33
53
|
end
|
34
54
|
|
35
|
-
|
36
|
-
|
37
|
-
|
55
|
+
desc 'stop', "Stop a Pact mock service"
|
56
|
+
method_option :port, aliases: "-p", desc: "Port of the service to stop", default: '1234', required: true
|
57
|
+
method_option :pid_dir, desc: "PID dir, defaults to tmp/pids", default: "tmp/pids"
|
38
58
|
|
39
|
-
def
|
40
|
-
|
59
|
+
def stop
|
60
|
+
mock_service_pidfile.kill_process
|
61
|
+
end
|
41
62
|
|
42
|
-
|
43
|
-
|
63
|
+
desc 'restart', "Start or restart a mock service. If the consumer, provider and pact-dir options are provided, the pact will be written automatically on shutdown."
|
64
|
+
method_option :port, aliases: "-p", default: '1234', desc: "Port on which to run the service"
|
65
|
+
method_option :log, aliases: "-l", desc: "File to which to log output"
|
66
|
+
method_option :ssl, desc: "Use a self-signed SSL cert to run the service over HTTPS"
|
67
|
+
method_option :cors, aliases: "-o", desc: "Support browser security in tests by responding to OPTIONS requests and adding CORS headers to mocked responses"
|
68
|
+
method_option :pact_dir, aliases: "-d", desc: "Directory to which the pacts will be written"
|
69
|
+
method_option :consumer, desc: "Consumer name"
|
70
|
+
method_option :provider, desc: "Provider name"
|
71
|
+
method_option :pid_dir, desc: "PID dir", default: 'tmp/pids'
|
44
72
|
|
45
|
-
|
73
|
+
def restart
|
74
|
+
restart_server(mock_service_pidfile) do
|
75
|
+
service
|
76
|
+
end
|
46
77
|
end
|
47
78
|
|
48
|
-
|
79
|
+
desc 'control-start', "Start a Pact mock service control server."
|
80
|
+
method_option :port, aliases: "-p", desc: "Port on which to run the service", default: '1234'
|
81
|
+
method_option :log_dir, aliases: "-l", desc: "File to which to log output", default: "log"
|
82
|
+
method_option :ssl, desc: "Use a self-signed SSL cert to run the service over HTTPS"
|
83
|
+
method_option :cors, aliases: "-o", desc: "Support browser security in tests by responding to OPTIONS requests and adding CORS headers to mocked responses"
|
84
|
+
method_option :pact_dir, aliases: "-d", desc: "Directory to which the pacts will be written", default: "."
|
85
|
+
method_option :pid_dir, desc: "PID dir", default: "tmp/pids"
|
86
|
+
|
87
|
+
def control_start
|
88
|
+
start_server(control_server_pidfile) do
|
89
|
+
control
|
90
|
+
end
|
91
|
+
end
|
49
92
|
|
50
|
-
|
93
|
+
desc 'control-stop', "Stop a Pact mock service control server."
|
94
|
+
method_option :port, aliases: "-p", desc: "Port of control server to stop", default: "1234"
|
95
|
+
method_option :pid_dir, desc: "PID dir, defaults to tmp/pids", default: "tmp/pids"
|
51
96
|
|
52
|
-
def
|
53
|
-
|
97
|
+
def control_stop
|
98
|
+
control_server_pidfile.kill_process
|
54
99
|
end
|
55
100
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
101
|
+
desc 'control-restart', "Start a Pact mock service control server."
|
102
|
+
method_option :port, aliases: "-p", desc: "Port on which to run the service", default: '1234'
|
103
|
+
method_option :log_dir, aliases: "-l", desc: "File to which to log output", default: "log"
|
104
|
+
method_option :ssl, desc: "Use a self-signed SSL cert to run the service over HTTPS"
|
105
|
+
method_option :cors, aliases: "-o", desc: "Support browser security in tests by responding to OPTIONS requests and adding CORS headers to mocked responses"
|
106
|
+
method_option :pact_dir, aliases: "-d", desc: "Directory to which the pacts will be written", default: "."
|
107
|
+
method_option :pid_dir, desc: "PID dir", default: "tmp/pids"
|
108
|
+
|
109
|
+
def control_restart
|
110
|
+
restart_server(control_server_pidfile) do
|
111
|
+
control
|
62
112
|
end
|
63
|
-
Rack::Handler::WEBrick.shutdown
|
64
113
|
end
|
65
114
|
|
66
|
-
|
67
|
-
service_options = {
|
68
|
-
pact_dir: options[:pact_dir],
|
69
|
-
consumer: options[:consumer],
|
70
|
-
provider: options[:provider],
|
71
|
-
cors_enabled: options[:cors]
|
72
|
-
}
|
73
|
-
service_options[:log_file] = open_log_file if options[:log]
|
74
|
-
service_options
|
75
|
-
end
|
115
|
+
default_task :service
|
76
116
|
|
77
|
-
|
78
|
-
FileUtils.mkdir_p File.dirname(options[:log])
|
79
|
-
log = File.open(options[:log], 'w')
|
80
|
-
log.sync = true
|
81
|
-
log
|
82
|
-
end
|
117
|
+
no_commands do
|
83
118
|
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
119
|
+
def control_server_pidfile
|
120
|
+
Pidfile.new(pid_dir: options[:pid_dir], name: control_pidfile_name)
|
121
|
+
end
|
122
|
+
|
123
|
+
def mock_service_pidfile
|
124
|
+
Pidfile.new(pid_dir: options[:pid_dir], name: mock_service_pidfile_name)
|
125
|
+
end
|
126
|
+
|
127
|
+
def mock_service_pidfile_name
|
128
|
+
"mock-service-#{options[:port]}.pid"
|
129
|
+
end
|
92
130
|
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
131
|
+
def control_pidfile_name
|
132
|
+
"mock-service-control-#{options[:port]}.pid"
|
133
|
+
end
|
134
|
+
|
135
|
+
def start_server pidfile
|
136
|
+
require 'pact/mock_service/server/spawn'
|
137
|
+
Pact::MockService::Server::Spawn.(pidfile, options[:port]) do
|
138
|
+
yield
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
def restart_server pidfile
|
143
|
+
require 'pact/mock_service/server/respawn'
|
144
|
+
Pact::MockService::Server::Respawn.(pidfile, options[:port]) do
|
145
|
+
yield
|
146
|
+
end
|
147
|
+
end
|
98
148
|
end
|
99
149
|
end
|
100
150
|
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'pact/consumer/mock_service/rack_request_helper'
|
2
|
+
require 'pact/mock_service/control_server/require_pacticipant_headers'
|
3
|
+
require 'pact/mock_service/control_server/index'
|
4
|
+
require 'pact/mock_service/control_server/mock_services'
|
5
|
+
require 'pact/mock_service/control_server/mock_service_creator'
|
6
|
+
require 'rack'
|
7
|
+
require 'rack/cascade'
|
8
|
+
|
9
|
+
module Pact
|
10
|
+
module MockService
|
11
|
+
module ControlServer
|
12
|
+
class App
|
13
|
+
|
14
|
+
include Pact::Consumer::RackRequestHelper
|
15
|
+
|
16
|
+
def initialize options = {}
|
17
|
+
@mock_services = mock_services = MockServices.new([])
|
18
|
+
@app = Rack::Builder.new {
|
19
|
+
run Rack::Cascade.new([
|
20
|
+
Index.new,
|
21
|
+
Rack::Builder.new {
|
22
|
+
use RequirePacticipantHeaders
|
23
|
+
run Rack::Cascade.new([
|
24
|
+
mock_services,
|
25
|
+
MockServiceCreator.new(mock_services, options)
|
26
|
+
])
|
27
|
+
}
|
28
|
+
])
|
29
|
+
}
|
30
|
+
end
|
31
|
+
|
32
|
+
def call env
|
33
|
+
@app.call(env)
|
34
|
+
end
|
35
|
+
|
36
|
+
def shutdown
|
37
|
+
@mock_services.shutdown
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# Delegates the incoming request that was sent to the control server
|
2
|
+
# to the underlying MockService
|
3
|
+
# if the X-Pact-Consumer and X-Pact-Provider headers match
|
4
|
+
# the consumer and provider for this MockService.
|
5
|
+
|
6
|
+
module Pact
|
7
|
+
module MockService
|
8
|
+
module ControlServer
|
9
|
+
|
10
|
+
class Delegator
|
11
|
+
|
12
|
+
HTTP_X_PACT_CONSUMER = 'HTTP_X_PACT_CONSUMER'.freeze
|
13
|
+
HTTP_X_PACT_PROVIDER = 'HTTP_X_PACT_PROVIDER'.freeze
|
14
|
+
LOCATION = 'X-Pact-Mock-Service-Location'.freeze
|
15
|
+
PACT_MOCK_SERVICE_HEADER = {'HTTP_X_PACT_MOCK_SERVICE' => 'true'}.freeze
|
16
|
+
NOT_FOUND_RESPONSE = [404, {}, []].freeze
|
17
|
+
|
18
|
+
def initialize app, base_url, consumer_name, provider_name
|
19
|
+
@app = app
|
20
|
+
@base_url = base_url
|
21
|
+
@consumer_name = consumer_name
|
22
|
+
@provider_name = provider_name
|
23
|
+
@location_header = {LOCATION => @base_url}.freeze
|
24
|
+
end
|
25
|
+
|
26
|
+
def call env
|
27
|
+
return NOT_FOUND_RESPONSE unless consumer_and_provider_headers_match?(env)
|
28
|
+
delegate env
|
29
|
+
end
|
30
|
+
|
31
|
+
def shutdown
|
32
|
+
@app.shutdown
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def consumer_and_provider_headers_match? env
|
38
|
+
env[HTTP_X_PACT_CONSUMER] == @consumer_name && env[HTTP_X_PACT_PROVIDER] == @provider_name
|
39
|
+
end
|
40
|
+
|
41
|
+
def delegate env
|
42
|
+
add_location_header_to_response(call_app(env))
|
43
|
+
end
|
44
|
+
|
45
|
+
def call_app env
|
46
|
+
@app.call(env.merge(PACT_MOCK_SERVICE_HEADER))
|
47
|
+
end
|
48
|
+
|
49
|
+
def add_location_header_to_response response
|
50
|
+
[response.first, response[1].merge(@location_header), response.last]
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Pact
|
2
|
+
module MockService
|
3
|
+
module ControlServer
|
4
|
+
class Index
|
5
|
+
|
6
|
+
HTTP_X_PACT_MOCK_SERVICE = 'HTTP_X_PACT_MOCK_SERVICE'
|
7
|
+
PATH_INFO = 'PATH_INFO'
|
8
|
+
INDEX_RESPONSE = [200, {'Content-Type' => 'text/plain'}, ['Control server running']].freeze
|
9
|
+
NOT_FOUND_RESPONSE = [404, {}, []].freeze
|
10
|
+
|
11
|
+
def call env
|
12
|
+
if is_index_request_with_mock_service_header? env
|
13
|
+
INDEX_RESPONSE
|
14
|
+
else
|
15
|
+
NOT_FOUND_RESPONSE
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def is_index_request_with_mock_service_header? env
|
20
|
+
env[HTTP_X_PACT_MOCK_SERVICE] && env[PATH_INFO].chomp("/").size == 0
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'pact/mock_service/spawn'
|
2
|
+
require 'pact/mock_service/control_server/delegator'
|
3
|
+
require 'find_a_port'
|
4
|
+
require 'pact/mock_service/server/wait_for_server_up'
|
5
|
+
|
6
|
+
# Create a new MockService on a random port and delegate the incoming request to it
|
7
|
+
|
8
|
+
module Pact
|
9
|
+
module MockService
|
10
|
+
module ControlServer
|
11
|
+
|
12
|
+
class MockServiceCreator
|
13
|
+
|
14
|
+
attr_reader :options
|
15
|
+
|
16
|
+
def initialize mock_services, options
|
17
|
+
@mock_services = mock_services
|
18
|
+
@options = options
|
19
|
+
end
|
20
|
+
|
21
|
+
def call env
|
22
|
+
consumer_name = env['HTTP_X_PACT_CONSUMER']
|
23
|
+
provider_name = env['HTTP_X_PACT_PROVIDER']
|
24
|
+
port = FindAPort.available_port
|
25
|
+
mock_service = Pact::MockService::Spawn.(consumer_name, provider_name, port, options)
|
26
|
+
delegator = Delegator.new(mock_service, "http://localhost:#{port}", consumer_name, provider_name)
|
27
|
+
@mock_services.add(delegator)
|
28
|
+
response = delegator.call(env)
|
29
|
+
response
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'rack'
|
2
|
+
require 'rack/cascade'
|
3
|
+
|
4
|
+
module Pact
|
5
|
+
module MockService
|
6
|
+
module ControlServer
|
7
|
+
class MockServices < Rack::Cascade
|
8
|
+
|
9
|
+
def add app
|
10
|
+
mock_services << app
|
11
|
+
super
|
12
|
+
end
|
13
|
+
|
14
|
+
def shutdown
|
15
|
+
mock_services.each(&:shutdown)
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def mock_services
|
21
|
+
@mock_services ||= []
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Pact
|
2
|
+
module MockService
|
3
|
+
module ControlServer
|
4
|
+
class RequirePacticipantHeaders
|
5
|
+
|
6
|
+
def initialize app
|
7
|
+
@app = app
|
8
|
+
end
|
9
|
+
|
10
|
+
def call env
|
11
|
+
if env['HTTP_X_PACT_CONSUMER'] && env['HTTP_X_PACT_PROVIDER']
|
12
|
+
@app.call(env)
|
13
|
+
else
|
14
|
+
[500, {}, ["Please specify the consumer name and the provider name by setting the X-Pact-Consumer and X-Pact-Provider headers"]]
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'pact/mock_service/control_server/app'
|
2
|
+
|
3
|
+
module Pact
|
4
|
+
module MockService
|
5
|
+
module ControlServer
|
6
|
+
class Run
|
7
|
+
|
8
|
+
def self.call options
|
9
|
+
new(options).call
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize options
|
13
|
+
@options = options
|
14
|
+
end
|
15
|
+
|
16
|
+
def call
|
17
|
+
trap(:INT) { shutdown }
|
18
|
+
trap(:TERM) { shutdown }
|
19
|
+
|
20
|
+
# https://github.com/rack/rack/blob/ae78184e5c1fcf4ac133171ed4b47b0548cd9b44/lib/rack/handler/webrick.rb#L32
|
21
|
+
# Rack adapter for webrick uses class variable for the server which contains the port,
|
22
|
+
# so if we use it more than once in the same process, we lose the reference to the first
|
23
|
+
# server, and can't shut it down. So, keep a manual reference to the Webrick server, and
|
24
|
+
# shut it down directly rather than use Rack::Handler::WEBrick.shutdown
|
25
|
+
# Ruby!
|
26
|
+
Rack::Handler::WEBrick.run(control_server, webbrick_opts) do | server |
|
27
|
+
@webrick_server = server
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
attr_reader :options
|
34
|
+
|
35
|
+
def control_server
|
36
|
+
@control_server ||= Pact::MockService::ControlServer::App.new control_server_options
|
37
|
+
end
|
38
|
+
|
39
|
+
def shutdown
|
40
|
+
unless @shutting_down
|
41
|
+
@shutting_down = true
|
42
|
+
begin
|
43
|
+
@control_server.shutdown
|
44
|
+
ensure
|
45
|
+
@webrick_server.shutdown
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def control_server_options
|
51
|
+
{
|
52
|
+
log_dir: options[:log_dir] || "log",
|
53
|
+
pact_dir: options[:pact_dir] || ".",
|
54
|
+
cors_enabled: options[:cors] || false,
|
55
|
+
ssl: options[:ssl]
|
56
|
+
}
|
57
|
+
end
|
58
|
+
|
59
|
+
def webbrick_opts
|
60
|
+
{
|
61
|
+
:Port => options[:port],
|
62
|
+
:AccessLog => []
|
63
|
+
}
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
require 'find_a_port'
|
2
|
+
require 'pact/consumer/mock_service'
|
3
|
+
|
4
|
+
module Pact
|
5
|
+
module MockService
|
6
|
+
class Run
|
7
|
+
|
8
|
+
def self.call options
|
9
|
+
new(options).call
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize options
|
13
|
+
@options = options
|
14
|
+
end
|
15
|
+
|
16
|
+
def call
|
17
|
+
require 'pact/consumer/mock_service'
|
18
|
+
|
19
|
+
trap(:INT) { call_shutdown_hooks }
|
20
|
+
trap(:TERM) { call_shutdown_hooks }
|
21
|
+
|
22
|
+
Rack::Handler::WEBrick.run(mock_service, webbrick_opts)
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
attr_reader :options
|
28
|
+
|
29
|
+
def mock_service
|
30
|
+
@mock_service ||= begin
|
31
|
+
Pact::Consumer::MockService.new(service_options)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def call_shutdown_hooks
|
36
|
+
unless @shutting_down
|
37
|
+
@shutting_down = true
|
38
|
+
begin
|
39
|
+
mock_service.shutdown
|
40
|
+
ensure
|
41
|
+
Rack::Handler::WEBrick.shutdown
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def service_options
|
47
|
+
service_options = {
|
48
|
+
pact_dir: options[:pact_dir],
|
49
|
+
consumer: options[:consumer],
|
50
|
+
provider: options[:provider],
|
51
|
+
cors_enabled: options[:cors]
|
52
|
+
}
|
53
|
+
service_options[:log_file] = open_log_file if options[:log]
|
54
|
+
service_options
|
55
|
+
end
|
56
|
+
|
57
|
+
def open_log_file
|
58
|
+
FileUtils.mkdir_p File.dirname(options[:log])
|
59
|
+
log = File.open(options[:log], 'w')
|
60
|
+
log.sync = true
|
61
|
+
log
|
62
|
+
end
|
63
|
+
|
64
|
+
def webbrick_opts
|
65
|
+
opts = {
|
66
|
+
:Port => options[:port] || FindAPort.available_port,
|
67
|
+
:AccessLog => []
|
68
|
+
}
|
69
|
+
opts.merge!(ssl_opts) if options[:ssl]
|
70
|
+
opts
|
71
|
+
end
|
72
|
+
|
73
|
+
def ssl_opts
|
74
|
+
{
|
75
|
+
:SSLEnable => true,
|
76
|
+
:SSLCertName => [ %w[CN localhost] ]
|
77
|
+
}
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'pact/mock_service/server/spawn'
|
2
|
+
|
3
|
+
module Pact
|
4
|
+
module MockService
|
5
|
+
module Server
|
6
|
+
class Respawn
|
7
|
+
|
8
|
+
def self.call pidfile, port
|
9
|
+
if pidfile.file_exists_and_process_running?
|
10
|
+
pidfile.kill_process
|
11
|
+
end
|
12
|
+
|
13
|
+
Spawn.(pidfile, port) do
|
14
|
+
yield
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|