Files
ubicloud/spec/lib/monitorable_resource_spec.rb
Jeremy Evans eff0bb2b60 Call original Clog.emit message when mocking in the tests
This makes sure to yield to the block and test that there aren't
errors inside of it.
2025-05-31 01:20:38 +09:00

168 lines
6.7 KiB
Ruby

# frozen_string_literal: true
require_relative "../model/spec_helper"
RSpec.describe MonitorableResource do
let(:postgres_server) { PostgresServer.new { it.id = "c068cac7-ed45-82db-bf38-a003582b36ee" } }
let(:r_w_event_loop) { described_class.new(postgres_server) }
let(:vm_host) { VmHost.new { it.id = "46683a25-acb1-4371-afe9-d39f303e44b4" } }
let(:r_without_event_loop) { described_class.new(vm_host) }
describe "#open_resource_session" do
it "returns if session is not nil and pulse reading is up" do
r_w_event_loop.instance_variable_set(:@session, "not nil")
r_w_event_loop.instance_variable_set(:@pulse, {reading: "up"})
expect(postgres_server).not_to receive(:reload)
r_w_event_loop.open_resource_session
end
it "sets session to resource's init_health_monitor_session" do
expect(postgres_server).to receive(:reload).and_return(postgres_server)
expect(postgres_server).to receive(:init_health_monitor_session).and_return("session")
expect { r_w_event_loop.open_resource_session }.to change { r_w_event_loop.instance_variable_get(:@session) }.from(nil).to("session")
end
it "sets deleted to true if resource is deleted" do
expect(postgres_server).to receive(:reload).and_raise(Sequel::NoExistingObject)
expect { r_w_event_loop.open_resource_session }.to change(r_w_event_loop, :deleted).from(false).to(true)
end
it "ignores exception if it is not Sequel::NoExistingObject" do
expect(postgres_server).to receive(:reload).and_raise(StandardError)
expect { r_w_event_loop.open_resource_session }.not_to raise_error
end
end
describe "#process_event_loop" do
before do
# We are monkeypatching the sleep method here to avoid the actual sleep.
# We also use it to flip the @run_event_loop flag to true, so that the
# loop in the process_event_loop method can exit.
def r_w_event_loop.sleep(duration)
@run_event_loop = true
end
end
it "returns if session is nil or resource does not need event loop" do
expect(Thread).not_to receive(:new)
# session is nil
r_w_event_loop.process_event_loop
# resource does not need event loop
r_without_event_loop.instance_variable_set(:@session, "not nil")
r_without_event_loop.process_event_loop
end
it "creates a new thread and runs the event loop" do
session = {ssh_session: instance_double(Net::SSH::Connection::Session)}
r_w_event_loop.instance_variable_set(:@session, session)
expect(Thread).to receive(:new).and_yield
expect(session[:ssh_session]).to receive(:loop)
r_w_event_loop.process_event_loop
end
it "swallows exception and logs it if event loop fails" do
session = {ssh_session: instance_double(Net::SSH::Connection::Session)}
r_w_event_loop.instance_variable_set(:@session, session)
expect(Thread).to receive(:new).and_yield
expect(session[:ssh_session]).to receive(:loop).and_raise(StandardError)
expect(Clog).to receive(:emit).and_call_original
expect(r_w_event_loop).to receive(:close_resource_session)
r_w_event_loop.process_event_loop
end
end
describe "#check_pulse" do
it "calls check_pulse on resource and sets pulse" do
expect(postgres_server).to receive(:check_pulse).and_return({reading: "up"})
expect { r_w_event_loop.check_pulse }.to change { r_w_event_loop.instance_variable_get(:@pulse) }.from({}).to({reading: "up"})
end
it "swallows exception and logs it if check_pulse fails" do
expect(vm_host).to receive(:check_pulse).and_raise(StandardError)
expect(Clog).to receive(:emit).and_call_original
expect { r_without_event_loop.check_pulse }.not_to raise_error
end
it "waits for the pulse thread to finish" do
pulse_thread = Thread.new {}
expect(pulse_thread).to receive(:join)
r_w_event_loop.instance_variable_set(:@pulse_thread, pulse_thread)
r_w_event_loop.check_pulse
end
it "logs the pulse if reading is not up" do
expect(postgres_server).to receive(:check_pulse).and_return({reading: "down", reading_rpt: 5})
expect(Clog).to receive(:emit).and_call_original
r_w_event_loop.check_pulse
end
it "does not log the pulse if reading is up and reading_rpt is not every 5th reading" do
expect(postgres_server).to receive(:check_pulse).and_return({reading: "up", reading_rpt: 3})
expect(Clog).not_to receive(:emit).and_call_original
r_w_event_loop.check_pulse
end
it "logs the pulse if reading is up and reading_rpt is every 5th reading" do
expect(postgres_server).to receive(:check_pulse).and_return({reading: "up", reading_rpt: 6})
expect(Clog).to receive(:emit).and_call_original
r_w_event_loop.check_pulse
end
end
describe "#close_resource_session" do
it "returns if session is nil" do
session = {ssh_session: instance_double(Net::SSH::Connection::Session)}
expect(session[:ssh_session]).not_to receive(:shutdown!)
expect(session).to receive(:nil?).and_return(true)
r_w_event_loop.instance_variable_set(:@session, session)
r_w_event_loop.close_resource_session
end
it "shuts down and closes the session" do
session = {ssh_session: instance_double(Net::SSH::Connection::Session)}
expect(session[:ssh_session]).to receive(:shutdown!)
expect(session[:ssh_session]).to receive(:close)
r_w_event_loop.instance_variable_set(:@session, session)
r_w_event_loop.close_resource_session
end
end
describe "#force_stop_if_stuck" do
it "does nothing if pulse check is not stuck" do
expect(Kernel).not_to receive(:exit!)
# not locked
r_w_event_loop.force_stop_if_stuck
# not timed out
r_w_event_loop.instance_variable_get(:@mutex).lock
r_w_event_loop.instance_variable_set(:@pulse_check_started_at, Time.now)
r_w_event_loop.force_stop_if_stuck
r_w_event_loop.instance_variable_get(:@mutex).unlock
end
it "triggers Kernel.exit if pulse check is stuck" do
r_w_event_loop.instance_variable_get(:@mutex).lock
r_w_event_loop.instance_variable_set(:@pulse_check_started_at, Time.now - 200)
expect(Clog).to receive(:emit).at_least(:once).and_call_original
r_w_event_loop.force_stop_if_stuck
r_w_event_loop.instance_variable_get(:@mutex).unlock
end
end
describe "#lock_no_wait" do
it "does not yield if mutex is locked" do
r_w_event_loop.instance_variable_get(:@mutex).lock
expect { |b| r_w_event_loop.lock_no_wait(&b) }.not_to yield_control
r_w_event_loop.instance_variable_get(:@mutex).unlock
end
it "yields if mutex is not locked" do
expect { |b| r_w_event_loop.lock_no_wait(&b) }.to yield_control
end
end
end