From 7e2d810c53cffeed848a687d9241e04a4c068073 Mon Sep 17 00:00:00 2001 From: Beth Skurrie Date: Thu, 21 Feb 2019 19:46:13 +1100 Subject: [PATCH] feat: allow mock service host to be configured https://github.com/pact-foundation/pact-ruby/issues/186 --- lib/pact/consumer/server.rb | 11 +++---- lib/pact/mock_service/app_manager.rb | 30 +++++++++---------- lib/pact/mock_service/cli.rb | 6 ++-- lib/pact/mock_service/client.rb | 9 +++--- .../control_server/mock_service_creator.rb | 3 +- lib/pact/mock_service/control_server/run.rb | 1 + lib/pact/mock_service/run.rb | 2 +- lib/pact/mock_service/spawn.rb | 13 ++++---- spec/lib/pact/consumer/server_spec.rb | 2 +- .../lib/pact/mock_service/app_manager_spec.rb | 8 ----- spec/lib/pact/mock_service/client_spec.rb | 2 +- 11 files changed, 40 insertions(+), 47 deletions(-) diff --git a/lib/pact/consumer/server.rb b/lib/pact/consumer/server.rb index 615b92f..2e66db5 100644 --- a/lib/pact/consumer/server.rb +++ b/lib/pact/consumer/server.rb @@ -34,12 +34,13 @@ def ports end end - attr_reader :app, :port, :options + attr_reader :app, :host, :port, :options - def initialize(app, port, options = {}) + def initialize(app, host, port, options = {}) @app = app @middleware = Middleware.new(@app) @server_thread = nil + @host = host @port = port @options = options end @@ -52,10 +53,6 @@ def error @middleware.error end - def host - "localhost" - end - def responsive? return false if @server_thread && @server_thread.join(0) res = get_identity @@ -96,7 +93,7 @@ def webrick_opts end def ssl_opts - { SSLEnable: true, SSLCertName: [ %w[CN localhost] ] } + { SSLEnable: true, SSLCertName: [ ["CN", host] ] } end def boot diff --git a/lib/pact/mock_service/app_manager.rb b/lib/pact/mock_service/app_manager.rb index 560b048..223bd46 100644 --- a/lib/pact/mock_service/app_manager.rb +++ b/lib/pact/mock_service/app_manager.rb @@ -23,7 +23,6 @@ def initialize def register_mock_service_for(name, url, options = {}) uri = URI(url) raise "Currently only http is supported" unless uri.scheme == 'http' - raise "Currently only services on localhost are supported" unless uri.host == 'localhost' uri.port = nil if options[:find_available_port] app = Pact::MockService.new( @@ -32,21 +31,21 @@ def register_mock_service_for(name, url, options = {}) pact_dir: pact_dir, pact_specification_version: options.fetch(:pact_specification_version) ) - register(app, uri.port) + register(app, uri.host, uri.port) end - def register(app, port = nil) + def register(app, host, port = nil) if port - existing = existing_app_on_port(port) + existing = existing_app_on_host_and_port(host, port) raise "Port #{port} is already being used by #{existing}" if existing and not existing == app end - app_registration = register_app(app, port) + app_registration = register_app(app, host, port) app_registration.spawn app_registration.port end - def ports_of_mock_services - app_registrations.find_all(&:is_a_mock_service?).collect(&:port) + def urls_of_mock_services + app_registrations.find_all(&:is_a_mock_service?).collect{ |ar| "http://#{ar.host}:#{ar.port}" } end def kill_all @@ -70,13 +69,13 @@ def app_registered_on?(port) private - def existing_app_on_port(port) - app_registration = registration_on_port(port) + def existing_app_on_host_and_port(host, port) + app_registration = registration_on_host_and_port(host, port) app_registration ? app_registration.app : nil end - def registration_on_port(port) - @app_registrations.find { |app_registration| app_registration.port == port } + def registration_on_host_and_port(host, port) + @app_registrations.find { |app_registration| app_registration.port == port && app_registration.host == host } end def pact_dir @@ -107,8 +106,8 @@ def app_registrations @app_registrations end - def register_app(app, port) - app_registration = AppRegistration.new(app: app, port: port) + def register_app(app, host, port) + app_registration = AppRegistration.new(app: app, host: host, port: port) app_registrations << app_registration app_registration end @@ -116,11 +115,12 @@ def register_app(app, port) class AppRegistration include Pact::Logging - attr_accessor :port, :app + attr_accessor :host, :port, :app def initialize(opts) @max_wait = 10 @port = opts[:port] + @host = opts[:host] @app = opts[:app] @spawned = false end @@ -148,7 +148,7 @@ def to_s def spawn logger.info "Starting app #{self}..." - @server = Pact::Server.new(app, port).boot + @server = Pact::Server.new(app, host, port).boot @port = @server.port @spawned = true logger.info "Started on port #{port}" diff --git a/lib/pact/mock_service/cli.rb b/lib/pact/mock_service/cli.rb index d709d96..29dfd05 100755 --- a/lib/pact/mock_service/cli.rb +++ b/lib/pact/mock_service/cli.rb @@ -107,6 +107,7 @@ def restart desc 'control-start', "Start a Pact mock service control server." method_option :port, aliases: "-p", desc: "Port on which to run the service", default: '1234' + method_option :host, aliases: "-h", desc: "Host on which to bind the service", default: 'localhost' method_option :log_dir, aliases: "-l", desc: "File to which to log output", default: "log" method_option :log_level, desc: "Log level. Options are DEBUG INFO WARN ERROR", default: "DEBUG" method_option :pact_file_write_mode, aliases: "-m", desc: PACT_FILE_WRITE_MODE_DESC, type: :string, default: 'overwrite' @@ -134,6 +135,7 @@ def control_stop desc 'control-restart', "Start a Pact mock service control server." method_option :port, aliases: "-p", desc: "Port on which to run the service", default: '1234' + method_option :host, aliases: "-h", desc: "Host on which to bind the service", default: 'localhost' method_option :log_dir, aliases: "-l", desc: "File to which to log output", default: "log" method_option :log_level, desc: "Log level. Options are DEBUG INFO WARN ERROR", default: "DEBUG" method_option :pact_dir, aliases: "-d", desc: "Directory to which the pacts will be written", default: "." @@ -180,14 +182,14 @@ def control_pidfile_name def start_server pidfile require 'pact/mock_service/server/spawn' - Pact::MockService::Server::Spawn.(pidfile, options[:port], options[:ssl]) do + Pact::MockService::Server::Spawn.(pidfile, options[:host], options[:port], options[:ssl]) do yield end end def restart_server pidfile require 'pact/mock_service/server/respawn' - Pact::MockService::Server::Respawn.(pidfile, options[:port], options[:ssl]) do + Pact::MockService::Server::Respawn.(pidfile, options[:host], options[:port], options[:ssl]) do yield end end diff --git a/lib/pact/mock_service/client.rb b/lib/pact/mock_service/client.rb index b699ea8..cb1e137 100644 --- a/lib/pact/mock_service/client.rb +++ b/lib/pact/mock_service/client.rb @@ -13,8 +13,8 @@ class Client MOCK_SERVICE_ADMINISTRATON_HEADERS = {'X-Pact-Mock-Service' => 'true'} - def initialize port - @http = Net::HTTP.new('localhost', port) + def initialize port, host = 'localhost' + @http = Net::HTTP.new(host, port) end def verify example_description @@ -46,8 +46,9 @@ def add_expected_interaction interaction raise AddInteractionError.new("\e[31m#{response.body}\e[m") unless response.is_a? Net::HTTPSuccess end - def self.clear_interactions port, example_description - Net::HTTP.new("localhost", port).delete("/interactions?example_description=#{CGI.escape(example_description)}", MOCK_SERVICE_ADMINISTRATON_HEADERS) + def self.clear_interactions mock_service_base_url, example_description + uri = URI(mock_service_base_url) + Net::HTTP.new(uri.host, uri.port).delete("/interactions?example_description=#{CGI.escape(example_description)}", MOCK_SERVICE_ADMINISTRATON_HEADERS) end def write_pact pacticipant_details diff --git a/lib/pact/mock_service/control_server/mock_service_creator.rb b/lib/pact/mock_service/control_server/mock_service_creator.rb index 56b708b..9dc6564 100644 --- a/lib/pact/mock_service/control_server/mock_service_creator.rb +++ b/lib/pact/mock_service/control_server/mock_service_creator.rb @@ -8,7 +8,6 @@ module Pact module MockService module ControlServer - class MockServiceCreator attr_reader :options @@ -22,7 +21,7 @@ def call env consumer_name = env['HTTP_X_PACT_CONSUMER'] provider_name = env['HTTP_X_PACT_PROVIDER'] port = FindAPort.available_port - mock_service = Pact::MockService::Spawn.(consumer_name, provider_name, port, options) + mock_service = Pact::MockService::Spawn.(consumer_name, provider_name, options[:host] || 'localhost', port, options) delegator = Delegator.new(mock_service, consumer_name, provider_name) @mock_services.add(delegator) delegator.call(env) diff --git a/lib/pact/mock_service/control_server/run.rb b/lib/pact/mock_service/control_server/run.rb index 1184408..cf1cd76 100644 --- a/lib/pact/mock_service/control_server/run.rb +++ b/lib/pact/mock_service/control_server/run.rb @@ -55,6 +55,7 @@ def control_server_options unique_pact_file_names: options[:unique_pact_file_names], cors_enabled: options[:cors] || false, ssl: options[:ssl], + host: options[:host], pact_specification_version: options[:pact_specification_version] } end diff --git a/lib/pact/mock_service/run.rb b/lib/pact/mock_service/run.rb index 1dd9bb5..b021845 100644 --- a/lib/pact/mock_service/run.rb +++ b/lib/pact/mock_service/run.rb @@ -98,7 +98,7 @@ def webbrick_opts def ssl_opts { :SSLEnable => true, - :SSLCertName => [ %w[CN localhost] ] + :SSLCertName => [ ["CN", host] ] } end diff --git a/lib/pact/mock_service/spawn.rb b/lib/pact/mock_service/spawn.rb index ce1aa40..369841c 100644 --- a/lib/pact/mock_service/spawn.rb +++ b/lib/pact/mock_service/spawn.rb @@ -8,15 +8,16 @@ module Pact module MockService class Spawn - def self.call consumer, provider, port, options - new(consumer, provider, port, options).call + def self.call consumer, provider, host, port, options + new(consumer, provider, host, port, options).call end - attr_reader :consumer, :provider, :port, :options + attr_reader :consumer, :provider, :host, :port, :options - def initialize consumer, provider, port, options + def initialize consumer, provider, host, port, options @consumer = consumer @provider = provider + @host = host @port = port @options = options end @@ -49,7 +50,7 @@ def mock_service end def start_mock_service app, port - Pact::Server.new(app, port, ssl: options[:ssl]).boot + Pact::Server.new(app, host, port, ssl: options[:ssl]).boot end def create_log_file @@ -73,7 +74,7 @@ def log_file_path end def base_url - options[:ssl] ? "https://localhost:#{port}" : "http://localhost:#{port}" + options[:ssl] ? "https://#{host}:#{port}" : "http://#{host}:#{port}" end def name diff --git a/spec/lib/pact/consumer/server_spec.rb b/spec/lib/pact/consumer/server_spec.rb index 19f6685..d2c3226 100644 --- a/spec/lib/pact/consumer/server_spec.rb +++ b/spec/lib/pact/consumer/server_spec.rb @@ -4,7 +4,7 @@ describe 'booting' do context 'with `nil` port' do let(:app) { -> (env) { [200, {}, ['OK']] } } - let(:server) { described_class.new(app, nil) } + let(:server) { described_class.new(app, 'localhost', nil) } it 'boots server with port 0 trick' do expect(server.port).to be_nil diff --git a/spec/lib/pact/mock_service/app_manager_spec.rb b/spec/lib/pact/mock_service/app_manager_spec.rb index e8f70f2..5435b34 100644 --- a/spec/lib/pact/mock_service/app_manager_spec.rb +++ b/spec/lib/pact/mock_service/app_manager_spec.rb @@ -50,14 +50,6 @@ module Pact::MockService end end - context "for a host other than localhost" do - let(:url) { 'http://aserver:1234'} - - it "should throw an unsupported error" do - expect { AppManager.instance.register_mock_service_for name, url, options }.to raise_error "Currently only services on localhost are supported" - end - end - describe "find_a_port option" do let(:url) { 'http://localhost' } diff --git a/spec/lib/pact/mock_service/client_spec.rb b/spec/lib/pact/mock_service/client_spec.rb index 744231c..d75508f 100644 --- a/spec/lib/pact/mock_service/client_spec.rb +++ b/spec/lib/pact/mock_service/client_spec.rb @@ -53,7 +53,7 @@ module MockService end it "deletes the interactions" do - Pact::MockService::Client.clear_interactions 4444, "some example" + Pact::MockService::Client.clear_interactions "http://localhost:4444", "some example" expect(delete_verifications).to have_been_made end end