pact-mock_service 0.2.3 → 0.2.4
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 +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
|