require 'thwait' require 'net/http' require 'uri' require 'pact/logging' require 'pact/consumer/server' require 'singleton' require 'pact/mock_service/app' module Pact module MockService class AppManager include Pact::Logging include Singleton def initialize @apps_spawned = false @app_registrations = [] end 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( name: name, log_file: create_log_file(name), pact_dir: pact_dir, pact_specification_version: options[:pact_specification_version] ) register(app, uri.port) end def register(app, port = nil) if port existing = existing_app_on_port(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.spawn app_registration.port end def ports_of_mock_services app_registrations.find_all(&:is_a_mock_service?).collect(&:port) end def kill_all app_registrations.find_all(&:spawned?).collect(&:kill) @apps_spawned = false end def clear_all kill_all @app_registrations = [] end def spawn_all app_registrations.find_all(&:not_spawned?).collect(&:spawn) @apps_spawned = true end def app_registered_on?(port) app_registrations.any? { |app_registration| app_registration.port == port } end private def existing_app_on_port(port) app_registration = registration_on_port(port) app_registration ? app_registration.app : nil end def registration_on_port(port) @app_registrations.find { |app_registration| app_registration.port == port } end def pact_dir Pact.configuration.pact_dir end def create_log_file(service_name) FileUtils::mkdir_p(Pact.configuration.log_dir) log = File.open(log_file_path(service_name), 'w') log.sync = true log end def log_file_path(service_name) File.join(Pact.configuration.log_dir, "#{log_file_name(service_name)}.log") end def log_file_name(service_name) lower_case_name = service_name.downcase.gsub(/\s+/, '_') if lower_case_name.include?('_service') lower_case_name.gsub('_service', '_mock_service') else lower_case_name + '_mock_service' end end def app_registrations @app_registrations end def register_app(app, port) app_registration = AppRegistration.new(app: app, port: port) app_registrations << app_registration app_registration end end class AppRegistration include Pact::Logging attr_accessor :port, :app def initialize(opts) @max_wait = 10 @port = opts[:port] @app = opts[:app] @spawned = false end def kill logger.debug "Supposed to be stopping" @spawned = false end def not_spawned? !spawned? end def spawned? @spawned end def is_a_mock_service? app.is_a?(Pact::MockService::App) end def to_s "#{app} on port #{port}" end def spawn logger.info "Starting app #{self}..." @server = Pact::Server.new(app, port).boot @port = @server.port @spawned = true logger.info "Started on port #{port}" end end end end