describe CC::Service::Invocation do it "success" do service = FakeService.new(:some_result) result = CC::Service::Invocation.invoke(service) expect(service.receive_count).to eq(1) expect(result).to eq(:some_result) end it "success with return values" do service = FakeService.new(:some_result) result = CC::Service::Invocation.invoke(service) do |i| i.with :return_values, "error" end expect(service.receive_count).to eq(1) expect(result).to eq(:some_result) end it "failure with return values" do service = FakeService.new(nil) result = CC::Service::Invocation.invoke(service) do |i| i.with :return_values, "error" end expect(service.receive_count).to eq(1) expect({ ok: false, message: "error" }).to eq(result) end it "retries" do service = FakeService.new service.fake_error = RuntimeError.new error_occurred = false begin CC::Service::Invocation.invoke(service) do |i| i.with :retries, 3 end rescue error_occurred = true end expect(error_occurred).not_to be_nil expect(service.receive_count).to eq(1 + 3) end it "metrics" do statsd = FakeStatsd.new CC::Service::Invocation.invoke(FakeService.new) do |i| i.with :metrics, statsd, "a_prefix" end expect(statsd.incremented_keys.length).to eq(1) expect(statsd.incremented_keys.first).to eq("services.invocations.a_prefix") end it "metrics on errors" do statsd = FakeStatsd.new service = FakeService.new service.fake_error = RuntimeError.new error_occurred = false begin CC::Service::Invocation.invoke(service) do |i| i.with :metrics, statsd, "a_prefix" end rescue error_occurred = true end expect(error_occurred).not_to be_nil expect(statsd.incremented_keys.length).to eq(1) expect(statsd.incremented_keys.first).to match(/^services\.errors\.a_prefix/) end it "user message" do service = FakeService.new service.fake_error = CC::Service::HTTPError.new("Boom", {}) service.override_user_message = "Hey do this" logger = FakeLogger.new result = CC::Service::Invocation.invoke(service) do |i| i.with :error_handling, logger, "a_prefix" end expect(result[:message]).to eq("Hey do this") expect(result[:log_message]).to match(/Boom/) end it "error handling" do service = FakeService.new service.fake_error = RuntimeError.new("Boom") logger = FakeLogger.new result = CC::Service::Invocation.invoke(service) do |i| i.with :error_handling, logger, "a_prefix" end expect({ ok: false, message: "Boom", log_message: "Exception invoking service: [a_prefix] (RuntimeError) Boom" }).to eq(result) expect(logger.logged_errors.length).to eq(1) expect(logger.logged_errors.first).to match(/^Exception invoking service: \[a_prefix\]/) end it "multiple middleware" do service = FakeService.new service.fake_error = RuntimeError.new("Boom") logger = FakeLogger.new result = CC::Service::Invocation.invoke(service) do |i| i.with :retries, 3 i.with :error_handling, logger end expect({ ok: false, message: "Boom", log_message: "Exception invoking service: (RuntimeError) Boom" }).to eq(result) expect(service.receive_count).to eq(1 + 3) expect(logger.logged_errors.length).to eq(1) end private class FakeService attr_reader :receive_count attr_accessor :fake_error, :override_user_message def initialize(result = nil) @result = result @receive_count = 0 end def receive @receive_count += 1 begin raise fake_error if fake_error rescue => e if override_user_message e.user_message = override_user_message end raise e end @result end end class FakeStatsd attr_reader :incremented_keys def initialize @incremented_keys = Set.new end def increment(key) @incremented_keys << key end def timing(key, value) end end end