mirror of
https://github.com/Evolix/chexpire.git
synced 2024-05-04 01:35:10 +02:00
SSL Checks with check_http
This commit is contained in:
parent
db4e7d42b2
commit
8a9a7f6f22
43
app/jobs/ssl_sync_job.rb
Normal file
43
app/jobs/ssl_sync_job.rb
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
class SSLSyncJob < ApplicationJob
|
||||||
|
queue_as :default
|
||||||
|
|
||||||
|
rescue_from StandardError do |exception|
|
||||||
|
check_logger.log(:standard_error, exception) if check.present?
|
||||||
|
raise # rubocop:disable Style/SignalException
|
||||||
|
end
|
||||||
|
|
||||||
|
rescue_from ActiveRecord::RecordNotFound do; end
|
||||||
|
|
||||||
|
# parser error are already logged
|
||||||
|
rescue_from SSL::Error do; end
|
||||||
|
|
||||||
|
attr_reader :check
|
||||||
|
|
||||||
|
def perform(check_id)
|
||||||
|
prepare_check(check_id)
|
||||||
|
|
||||||
|
response = SSL.ask(check.domain, logger: check_logger)
|
||||||
|
return unless response.valid?
|
||||||
|
|
||||||
|
update_from_response(response)
|
||||||
|
end
|
||||||
|
|
||||||
|
def update_from_response(response)
|
||||||
|
check.domain_expires_at = response.expire_at
|
||||||
|
check.last_success_at = Time.now
|
||||||
|
|
||||||
|
check.save!
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def prepare_check(check_id)
|
||||||
|
@check = Check.find(check_id)
|
||||||
|
check.update_attribute(:last_run_at, Time.now)
|
||||||
|
end
|
||||||
|
|
||||||
|
# logger is a reserved ActiveJob method
|
||||||
|
def check_logger
|
||||||
|
@check_logger ||= CheckLogger.new(check)
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,5 +1,6 @@
|
||||||
class CheckDomainProcessor
|
class CheckDomainProcessor
|
||||||
include CheckProcessor
|
include CheckProcessor
|
||||||
|
|
||||||
protected
|
protected
|
||||||
|
|
||||||
def configuration_key
|
def configuration_key
|
||||||
|
|
|
@ -42,6 +42,10 @@ module CheckProcessor
|
||||||
scope.where("domain_expires_at IS NULL")
|
scope.where("domain_expires_at IS NULL")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def resolve_all
|
||||||
|
scope
|
||||||
|
end
|
||||||
|
|
||||||
protected
|
protected
|
||||||
|
|
||||||
def base_scope
|
def base_scope
|
||||||
|
@ -55,7 +59,7 @@ module CheckProcessor
|
||||||
fail NotImplementedError, "#{self.class.name} did not implemented method #{__callee__}"
|
fail NotImplementedError, "#{self.class.name} did not implemented method #{__callee__}"
|
||||||
end
|
end
|
||||||
|
|
||||||
def process(check)
|
def process(_check)
|
||||||
fail NotImplementedError, "#{self.class.name} did not implemented method #{__callee__}"
|
fail NotImplementedError, "#{self.class.name} did not implemented method #{__callee__}"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
23
app/services/check_ssl_processor.rb
Normal file
23
app/services/check_ssl_processor.rb
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
class CheckSSLProcessor
|
||||||
|
include CheckProcessor
|
||||||
|
|
||||||
|
protected
|
||||||
|
|
||||||
|
def configuration_key
|
||||||
|
"checks_ssl"
|
||||||
|
end
|
||||||
|
|
||||||
|
def resolvers
|
||||||
|
%i[
|
||||||
|
resolve_all
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
|
def scope
|
||||||
|
base_scope.ssl
|
||||||
|
end
|
||||||
|
|
||||||
|
def process(check)
|
||||||
|
SSLSyncJob.perform_now(check.id)
|
||||||
|
end
|
||||||
|
end
|
66
app/services/ssl.rb
Normal file
66
app/services/ssl.rb
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
require "null_logger"
|
||||||
|
require "system_command"
|
||||||
|
require_relative "ssl/parser"
|
||||||
|
require_relative "ssl/response"
|
||||||
|
require_relative "ssl/errors"
|
||||||
|
|
||||||
|
module SSL
|
||||||
|
class << self
|
||||||
|
def ask(domain, system_klass: SystemCommand, logger: NullLogger.new)
|
||||||
|
Service.new(domain, system_klass: system_klass, logger: logger).call
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class Service
|
||||||
|
attr_reader :domain
|
||||||
|
attr_reader :logger
|
||||||
|
attr_reader :system_klass
|
||||||
|
attr_reader :configuration
|
||||||
|
|
||||||
|
def initialize(domain, system_klass: SystemCommand, configuration: nil, logger: NullLogger.new)
|
||||||
|
@domain = domain
|
||||||
|
@logger = logger
|
||||||
|
@system_klass = system_klass
|
||||||
|
@configuration = configuration || default_configuration
|
||||||
|
end
|
||||||
|
|
||||||
|
def call
|
||||||
|
result = run_command
|
||||||
|
parse(result)
|
||||||
|
rescue StandardError => ex
|
||||||
|
logger.log :service_error, ex
|
||||||
|
raise
|
||||||
|
end
|
||||||
|
|
||||||
|
def run_command
|
||||||
|
command = system_klass.new(check_http_path, check_http_args, logger: logger)
|
||||||
|
result = command.execute
|
||||||
|
|
||||||
|
unless result.exit_status.zero?
|
||||||
|
fail SSLCommandError, "SSL command failed with status #{result.exit_status}"
|
||||||
|
end
|
||||||
|
|
||||||
|
result
|
||||||
|
end
|
||||||
|
|
||||||
|
def parse(result)
|
||||||
|
parser = Parser.new(domain, logger: logger)
|
||||||
|
parser.parse(result.stdout)
|
||||||
|
end
|
||||||
|
|
||||||
|
def check_http_path
|
||||||
|
configuration.check_http_path.presence || "check_http"
|
||||||
|
end
|
||||||
|
|
||||||
|
def check_http_args
|
||||||
|
[
|
||||||
|
configuration.check_http_args.presence,
|
||||||
|
"-H '#{domain}'",
|
||||||
|
].compact
|
||||||
|
end
|
||||||
|
|
||||||
|
def default_configuration
|
||||||
|
OpenStruct.new(Rails.configuration.chexpire.fetch("checks_ssl") { {} })
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
10
app/services/ssl/errors.rb
Normal file
10
app/services/ssl/errors.rb
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
module SSL
|
||||||
|
class Error < StandardError; end
|
||||||
|
|
||||||
|
class SSLCommandError < Error; end
|
||||||
|
|
||||||
|
class ParserError < Error; end
|
||||||
|
class DomainNotMatchError < ParserError; end
|
||||||
|
class InvalidResponseError < ParserError; end
|
||||||
|
class InvalidDateError < ParserError; end
|
||||||
|
end
|
52
app/services/ssl/parser.rb
Normal file
52
app/services/ssl/parser.rb
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
require "null_logger"
|
||||||
|
require "ssl/errors"
|
||||||
|
|
||||||
|
module SSL
|
||||||
|
class Parser
|
||||||
|
DATE_REGEX = /will expire on (.+)\./
|
||||||
|
# Several date formats possible:
|
||||||
|
# OK - Certificate 'domain.net' will expire on Sat 10 Jun 2028 09:14:18 AM GMT +0000.
|
||||||
|
# OK - Certificate 'domain.net' will expire on 2018-08-06 02:57 +0200/CEST.
|
||||||
|
|
||||||
|
attr_reader :logger
|
||||||
|
attr_reader :domain
|
||||||
|
|
||||||
|
def initialize(domain, logger: NullLogger.new)
|
||||||
|
@logger = logger
|
||||||
|
@domain = domain
|
||||||
|
end
|
||||||
|
|
||||||
|
def parse(raw)
|
||||||
|
fail DomainNotMatchError unless match_domain?(raw)
|
||||||
|
|
||||||
|
match = raw.match(DATE_REGEX)
|
||||||
|
|
||||||
|
fail InvalidResponseError unless match.present?
|
||||||
|
|
||||||
|
response = build_response(match)
|
||||||
|
|
||||||
|
logger.log :parsed_response, response
|
||||||
|
|
||||||
|
response
|
||||||
|
rescue ParserError => ex
|
||||||
|
logger.log :parser_error, ex
|
||||||
|
raise
|
||||||
|
end
|
||||||
|
|
||||||
|
def match_domain?(raw)
|
||||||
|
raw.match(/\b#{domain}\b/).present?
|
||||||
|
end
|
||||||
|
|
||||||
|
def build_response(match)
|
||||||
|
Response.new(domain).tap do |response|
|
||||||
|
response.expire_at = parse_datetime(match[1])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def parse_datetime(date_str)
|
||||||
|
Time.parse(date_str).utc
|
||||||
|
rescue StandardError => ex
|
||||||
|
raise InvalidDateError, ex.message
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
13
app/services/ssl/response.rb
Normal file
13
app/services/ssl/response.rb
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
module SSL
|
||||||
|
class Response
|
||||||
|
attr_accessor :expire_at
|
||||||
|
|
||||||
|
def initialize(domain)
|
||||||
|
@domain = domain
|
||||||
|
end
|
||||||
|
|
||||||
|
def valid?
|
||||||
|
expire_at.present?
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -7,6 +7,9 @@ default: &default
|
||||||
interval: 0.5
|
interval: 0.5
|
||||||
long_term: 60
|
long_term: 60
|
||||||
long_term_frequency: 10
|
long_term_frequency: 10
|
||||||
|
checks_ssl:
|
||||||
|
check_http_path: ""
|
||||||
|
check_http_args: ""
|
||||||
|
|
||||||
development:
|
development:
|
||||||
<<: *default
|
<<: *default
|
||||||
|
|
|
@ -8,3 +8,6 @@ test:
|
||||||
interval: 0.00
|
interval: 0.00
|
||||||
long_term: 60
|
long_term: 60
|
||||||
long_term_frequency: 10
|
long_term_frequency: 10
|
||||||
|
checks_ssl:
|
||||||
|
check_http_path: ""
|
||||||
|
check_http_args: ""
|
||||||
|
|
|
@ -1,11 +1,17 @@
|
||||||
namespace :checks do
|
namespace :checks do
|
||||||
namespace :sync_dates do
|
namespace :sync_dates do
|
||||||
task all: [:domain]
|
task all: [:domain, :ssl]
|
||||||
|
|
||||||
desc "Refresh domains expiry dates"
|
desc "Refresh domains expiry dates"
|
||||||
task domain: :environment do
|
task domain: :environment do
|
||||||
process = CheckDomainProcessor.new
|
process = CheckDomainProcessor.new
|
||||||
process.sync_dates
|
process.sync_dates
|
||||||
end
|
end
|
||||||
|
|
||||||
|
desc "Refresh SSL expiry dates"
|
||||||
|
task domain: :environment do
|
||||||
|
process = CheckSSLProcessor.new
|
||||||
|
process.sync_dates
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
1
test/fixtures/files/ssl/ssl0.domain.org.txt
vendored
Normal file
1
test/fixtures/files/ssl/ssl0.domain.org.txt
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
OK - Certificate 'ssl0.domain.org' will expire on Sat 10 Jun 2028 09:14:18 AM GMT +0000.
|
1
test/fixtures/files/ssl/ssl1.domain.org.txt
vendored
Normal file
1
test/fixtures/files/ssl/ssl1.domain.org.txt
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
SSL OK - Certificate 'ssl1.domain.org' will expire on 2022-08-06 02:57 +0200/CEST.
|
1
test/fixtures/files/ssl/ssl100.invalid.org.txt
vendored
Normal file
1
test/fixtures/files/ssl/ssl100.invalid.org.txt
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
check_http: Invalid hostname/address - ssl100.invalid.org
|
1
test/fixtures/files/ssl/ssl101.invalid.org.txt
vendored
Normal file
1
test/fixtures/files/ssl/ssl101.invalid.org.txt
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
SSL OK - Certificate 'ssl101.invalid.org' will expire on unknown date.
|
68
test/jobs/ssl_sync_job_test.rb
Normal file
68
test/jobs/ssl_sync_job_test.rb
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
require "test_helper"
|
||||||
|
|
||||||
|
class SSLSyncJobTest < ActiveJob::TestCase
|
||||||
|
test "calls whois database and update check with the response (domain.fr)" do
|
||||||
|
domain = "ssl0.domain.org"
|
||||||
|
check = create(:check, :nil_dates, domain: domain)
|
||||||
|
|
||||||
|
mock_system_command("check_http", expected_command_arg(domain), stdout: ssl_response(domain)) do
|
||||||
|
SSLSyncJob.perform_now(check.id)
|
||||||
|
end
|
||||||
|
|
||||||
|
check.reload
|
||||||
|
|
||||||
|
assert_just_now check.last_run_at
|
||||||
|
assert_just_now check.last_success_at
|
||||||
|
assert_equal Time.new(2028, 6, 10, 9, 14, 18, 0), check.domain_expires_at
|
||||||
|
assert_nil check.domain_updated_at
|
||||||
|
assert_nil check.domain_created_at
|
||||||
|
assert check.active?
|
||||||
|
end
|
||||||
|
|
||||||
|
test "ignore invalid response" do
|
||||||
|
domain = "domain.fr"
|
||||||
|
check = create(:check, :nil_dates, domain: domain)
|
||||||
|
original_updated_at = check.updated_at
|
||||||
|
|
||||||
|
mock_system_command("check_http", expected_command_arg(domain), stdout: "not a response") do
|
||||||
|
SSLSyncJob.perform_now(check.id)
|
||||||
|
end
|
||||||
|
|
||||||
|
check.reload
|
||||||
|
|
||||||
|
assert_just_now check.last_run_at
|
||||||
|
assert_nil check.last_success_at
|
||||||
|
assert_equal original_updated_at, check.updated_at
|
||||||
|
assert check.active?
|
||||||
|
end
|
||||||
|
|
||||||
|
test "should ignore not found (removed) checks" do
|
||||||
|
assert_nothing_raised do
|
||||||
|
SSLSyncJob.perform_now("9999999")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
test "should log and re-raise StandardError" do
|
||||||
|
check = create(:check)
|
||||||
|
|
||||||
|
assert_raise StandardError do
|
||||||
|
SSL.stub :ask, nil do
|
||||||
|
SSLSyncJob.perform_now(check.id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
assert_equal 1, check.logs.count
|
||||||
|
assert_match(/undefined method \W+valid\?/, check.logs.last.error)
|
||||||
|
assert check.logs.last.failed?
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def ssl_response(domain)
|
||||||
|
file_fixture("ssl/#{domain}.txt").read
|
||||||
|
end
|
||||||
|
|
||||||
|
def expected_command_arg(domain)
|
||||||
|
["-H '#{domain}'"]
|
||||||
|
end
|
||||||
|
end
|
|
@ -69,6 +69,14 @@ class CheckProcessorTest < ActiveSupport::TestCase
|
||||||
assert_not_includes checks, c4
|
assert_not_includes checks, c4
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "resolve_all include all eligible checks" do
|
||||||
|
create(:check, :expires_next_week)
|
||||||
|
create(:check, :expires_next_week, last_run_at: 4.hours.ago)
|
||||||
|
create(:check, :last_runs_failed)
|
||||||
|
|
||||||
|
assert_equal @processor.send(:scope), @processor.resolve_all
|
||||||
|
end
|
||||||
|
|
||||||
test "resolvers does not include checks recently executed" do
|
test "resolvers does not include checks recently executed" do
|
||||||
c1 = create(:check, :expires_next_week)
|
c1 = create(:check, :expires_next_week)
|
||||||
c2 = create(:check, :expires_next_week, last_run_at: 4.hours.ago)
|
c2 = create(:check, :expires_next_week, last_run_at: 4.hours.ago)
|
||||||
|
|
35
test/services/check_ssl_processor_test.rb
Normal file
35
test/services/check_ssl_processor_test.rb
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
require "test_helper"
|
||||||
|
|
||||||
|
class CheckSSLProcessorTest < ActiveSupport::TestCase
|
||||||
|
setup do
|
||||||
|
@processor = CheckSSLProcessor.new
|
||||||
|
end
|
||||||
|
|
||||||
|
test "process SSLSyncJob for ssl checks" do
|
||||||
|
domain = "ssl0.domain.org"
|
||||||
|
check = create(:check, :ssl, :nil_dates, domain: domain)
|
||||||
|
|
||||||
|
response = file_fixture("ssl/ssl0.domain.org.txt").read
|
||||||
|
mock_system_command("check_http", ["-H '#{domain}'"], stdout: response) do
|
||||||
|
@processor.send(:process, check)
|
||||||
|
end
|
||||||
|
|
||||||
|
check.reload
|
||||||
|
|
||||||
|
assert_equal Time.new(2028, 6, 10, 9, 14, 18, 0), check.domain_expires_at
|
||||||
|
end
|
||||||
|
|
||||||
|
test "scope concerns only checks of kind 'ssl'" do
|
||||||
|
domains = create_list(:check, 2, :ssl)
|
||||||
|
create_list(:check, 2, :domain)
|
||||||
|
|
||||||
|
assert_equal domains, @processor.send(:scope)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "resolvers returns an array of methods returning a scope" do
|
||||||
|
assert_not_empty @processor.send(:resolvers)
|
||||||
|
@processor.send(:resolvers).each do |method|
|
||||||
|
assert_kind_of ActiveRecord::Relation, @processor.public_send(method)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
53
test/services/ssl/parser_test.rb
Normal file
53
test/services/ssl/parser_test.rb
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
require "test_helper"
|
||||||
|
require "ssl/parser"
|
||||||
|
require "ssl/errors"
|
||||||
|
|
||||||
|
module SSL
|
||||||
|
class ParserTest < ActiveSupport::TestCase
|
||||||
|
test "should parse a SSL check response" do
|
||||||
|
parser = Parser.new("ssl0.domain.org")
|
||||||
|
domain = file_fixture("ssl/ssl0.domain.org.txt").read
|
||||||
|
response = parser.parse(domain)
|
||||||
|
assert_kind_of Response, response
|
||||||
|
|
||||||
|
assert_equal Time.new(2028, 6, 10, 9, 14, 18, 0), response.expire_at
|
||||||
|
assert response.expire_at.utc?
|
||||||
|
end
|
||||||
|
|
||||||
|
test "should parse a SSL check response in another format and convert it in UTC" do
|
||||||
|
parser = Parser.new("ssl1.domain.org")
|
||||||
|
domain = file_fixture("ssl/ssl1.domain.org.txt").read
|
||||||
|
response = parser.parse(domain)
|
||||||
|
assert_kind_of Response, response
|
||||||
|
|
||||||
|
assert_equal Time.new(2022, 8, 6, 0, 57, 0, 0), response.expire_at
|
||||||
|
assert response.expire_at.utc?
|
||||||
|
end
|
||||||
|
|
||||||
|
test "should raises DomainNotMatchError when parsed text does not match the domain" do
|
||||||
|
parser = Parser.new("anotherdomain.fr")
|
||||||
|
output = file_fixture("ssl/ssl1.domain.org.txt").read
|
||||||
|
|
||||||
|
assert_raises DomainNotMatchError do
|
||||||
|
parser.parse(output)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
test "should raises InvalidResponseError when check response is not matched" do
|
||||||
|
parser = Parser.new("ssl100.invalid.org")
|
||||||
|
output = file_fixture("ssl/ssl100.invalid.org.txt").read
|
||||||
|
|
||||||
|
assert_raises InvalidResponseError do
|
||||||
|
parser.parse(output)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
test "should raises InvalidDateError when a date is not in the expected format" do
|
||||||
|
parser = Parser.new("ssl101.invalid.org")
|
||||||
|
output = file_fixture("ssl/ssl101.invalid.org.txt").read
|
||||||
|
|
||||||
|
assert_raises InvalidDateError do
|
||||||
|
parser.parse(output)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
74
test/services/ssl_test.rb
Normal file
74
test/services/ssl_test.rb
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
require "test_helper"
|
||||||
|
require "ssl"
|
||||||
|
require "system_command"
|
||||||
|
|
||||||
|
module SSL
|
||||||
|
class ServiceTest < ActiveSupport::TestCase
|
||||||
|
test "should run the command, return the result" do
|
||||||
|
result = OpenStruct.new(exit_status: 0)
|
||||||
|
|
||||||
|
mock_system_klass("check_http", ["-H 'example.org'"], result) do |system_klass|
|
||||||
|
service = Service.new("example.org", system_klass: system_klass)
|
||||||
|
assert_equal result, service.run_command
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
test "should raise an exception if exit status > 0" do
|
||||||
|
result = OpenStruct.new(exit_status: 1)
|
||||||
|
|
||||||
|
mock_system_klass("check_http", ["-H 'example.org'"], result) do |system_klass|
|
||||||
|
service = Service.new("example.org", system_klass: system_klass)
|
||||||
|
|
||||||
|
assert_raises SSLCommandError do
|
||||||
|
service.run_command
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
test "should parse from a command result" do
|
||||||
|
result = OpenStruct.new(
|
||||||
|
exit_status: 0,
|
||||||
|
stdout: file_fixture("ssl/ssl0.domain.org.txt").read,
|
||||||
|
)
|
||||||
|
|
||||||
|
service = Service.new("ssl0.domain.org")
|
||||||
|
assert_kind_of Response, service.parse(result)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "should uses the command line arguments of the configuration" do
|
||||||
|
result = OpenStruct.new(exit_status: 0)
|
||||||
|
config = OpenStruct.new(check_http_args: "-f follow -I 127.0.0.1")
|
||||||
|
|
||||||
|
expected_args = ["-f follow -I 127.0.0.1", "-H 'example.org'"]
|
||||||
|
mock_system_klass("check_http", expected_args, result) do |system_klass|
|
||||||
|
service = Service.new("example.org", configuration: config, system_klass: system_klass)
|
||||||
|
assert_equal result, service.run_command
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
test "should uses the program path from the configuration" do
|
||||||
|
result = OpenStruct.new(exit_status: 0)
|
||||||
|
config = OpenStruct.new(check_http_path: "/usr/local/custom/path")
|
||||||
|
|
||||||
|
mock_system_klass("/usr/local/custom/path", ["-H 'example.org'"], result) do |system_klass|
|
||||||
|
service = Service.new("example.org", configuration: config, system_klass: system_klass)
|
||||||
|
assert_equal result, service.run_command
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def mock_system_klass(program, command_args, result)
|
||||||
|
system_klass = Minitest::Mock.new
|
||||||
|
system_command = Minitest::Mock.new.expect(:execute, result)
|
||||||
|
system_klass.expect(:new, system_command) do |arg1, arg2, logger:|
|
||||||
|
arg1 == program &&
|
||||||
|
arg2 == command_args &&
|
||||||
|
logger.class == NullLogger
|
||||||
|
end
|
||||||
|
|
||||||
|
yield system_klass
|
||||||
|
|
||||||
|
system_klass.verify
|
||||||
|
system_command.verify
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -6,6 +6,7 @@ if !ENV["NO_COVERAGE"] && (ARGV.empty? || ARGV.include?("test/test_helper.rb"))
|
||||||
SimpleCov.start "rails" do
|
SimpleCov.start "rails" do
|
||||||
add_group "Notifier", "app/services/notifier"
|
add_group "Notifier", "app/services/notifier"
|
||||||
add_group "Whois", "app/services/whois"
|
add_group "Whois", "app/services/whois"
|
||||||
|
add_group "SSL", "app/services/ssl"
|
||||||
add_group "Services", "app/services"
|
add_group "Services", "app/services"
|
||||||
add_group "Policies", "app/policies"
|
add_group "Policies", "app/policies"
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue