Verified Commit 3908acfd authored by Hercules Merscher's avatar Hercules Merscher 🌴
Browse files

feat: Custom exceptions

parent 0b3aba50
Loading
Loading
Loading
Loading
Loading
+3 −2
Original line number Diff line number Diff line
# frozen_string_literal: true

require 'labkit/covered_experience/registry'
require 'labkit/covered_experience/error'
require 'labkit/covered_experience/experience'
require 'labkit/covered_experience/null'
require 'labkit/covered_experience/registry'

module Labkit
  # Module for loading and managing Covered Experiences.
@@ -31,7 +32,7 @@ module Labkit
      def raise_or_null(experience_id)
        return Null.instance unless %w[development test].include?(ENV['RAILS_ENV'])

        raise "Covered Experience #{experience_id} not found in the registry"
        raise(NotFoundError, "Covered Experience #{experience_id} not found in the registry")
      end
    end
  end
+9 −0
Original line number Diff line number Diff line
# frozen_string_literal: true

module Labkit
  module CoveredExperience
    CoveredExperienceError = Class.new(StandardError)
    UnstartedError = Class.new(CoveredExperienceError)
    NotFoundError = Class.new(StandardError)
  end
end
+39 −6
Original line number Diff line number Diff line
# frozen_string_literal: true

require 'labkit/logging/json_logger'
require 'labkit/covered_experience/error'

module Labkit
  module CoveredExperience
@@ -11,16 +12,36 @@ module Labkit
      async_slow: 300
    }.freeze

    # The `Experience` class represents a single Covered Experience
    # event to be measured and reported.
    class Experience
      attr_reader :error

      def initialize(definition)
        @definition = definition
        @has_error = false
      end

      # Start the Covered Experience.
      #
      # @yield [self] When a block is provided, the experience will be completed automatically.
      # @return [self]
      # @raise [CoveredExperienceError] If the block raises an error.
      #
      # Usage:
      #
      #  CoveredExperience.new(definition).start do |experience|
      #    experience.checkpoint
      #    experience.checkpoint
      #  end
      #
      #  experience = CoveredExperience.new(definition)
      #  experience.start
      #  experience.checkpoint
      #  experience.complete
      def start
        @started = Time.now.utc
        checkpoint_counter.increment(checkpoint: "start")
        # complete

        return self unless block_given?

@@ -29,12 +50,16 @@ module Labkit
          self
        rescue StandardError => e
          error!(e)
          raise
          raise(CoveredExperienceError, e)
        ensure
          complete
        end
      end

      # Checkpoint the Covered Experience.
      #
      # @raise [UnstartedError] If the experience has not been started and RAILS_ENV is development or test.
      # @return [self]
      def checkpoint
        return unless ensure_started!

@@ -42,6 +67,10 @@ module Labkit
        self
      end

      # Complete the Covered Experience.
      #
      # @raise [UnstartedError] If the experience has not been started and RAILS_ENV is development or test.
      # @return [self]
      def complete
        return unless ensure_started!

@@ -56,7 +85,11 @@ module Labkit
        self
      end

      def error!(error = nil)
      # Marks the experience as failed with an error
      #
      # @param error [StandardError, String] The error that caused the experience to fail.
      # @return [self]
      def error!(error)
        @has_error = true
        @error = error
        self
@@ -75,7 +108,7 @@ module Labkit
      def ensure_started!
        return @started unless @started.nil?

        err = "Covered Experience #{@definition.id} not started"
        err = UnstartedError.new("Covered Experience #{@definition.id} not started")

        warn(err)
        raise(err) if %w[development test].include?(ENV['RAILS_ENV'])
@@ -109,8 +142,8 @@ module Labkit
        )
      end

      def warn(message)
        logger.warn(component: self.class.name, message: message)
      def warn(exception)
        logger.warn(component: self.class.name, message: exception.message)
      end

      def logger
+12 −8
Original line number Diff line number Diff line
@@ -31,7 +31,7 @@ RSpec.describe Labkit::CoveredExperience::Experience, :with_metrics_config do
      it 'captures exceptions and marks as error' do
        expect do
          experience.start { raise 'Something went wrong' }
        end.to raise_error(RuntimeError, 'Something went wrong')
        end.to raise_error(Labkit::CoveredExperience::CoveredExperienceError, 'Something went wrong')
        .and complete_covered_experience(:testing_sample, error: true)
      end
    end
@@ -78,7 +78,7 @@ RSpec.describe Labkit::CoveredExperience::Experience, :with_metrics_config do
            stub_env('RAILS_ENV', env)
          end

          it { expect { checkpoint }.to raise_error(RuntimeError, "Covered Experience #{definition.id} not started") }
          it { expect { checkpoint }.to raise_error(Labkit::CoveredExperience::UnstartedError, "Covered Experience #{definition.id} not started") }
        end
      end
    end
@@ -97,7 +97,7 @@ RSpec.describe Labkit::CoveredExperience::Experience, :with_metrics_config do

      it 'ends with error when marked as error' do
        expect do
          experience.error!.complete
          experience.error!('boom!').complete
        end.to complete_covered_experience(:testing_sample, error: true)
      end

@@ -130,16 +130,20 @@ RSpec.describe Labkit::CoveredExperience::Experience, :with_metrics_config do
            stub_env('RAILS_ENV', env)
          end

          it { expect { complete }.to raise_error(RuntimeError, "Covered Experience #{definition.id} not started") }
          it { expect { complete }.to raise_error(Labkit::CoveredExperience::UnstartedError, "Covered Experience #{definition.id} not started") }
        end
      end
    end
  end

  describe '#error!' do
    it 'returns itself' do
      expect(experience.error!).to be(experience)
      expect(experience).to have_error
    end
    let(:exception) { Labkit::CoveredExperience::CoveredExperienceError.new('boom!') }

    subject!(:error) { experience.error!(exception) }

    it { is_expected.to be(experience) }
    it { is_expected.to have_error }
    it { expect(experience.error).to be(exception) }
    it { expect(experience.error!('BOOM!').error).to be('BOOM!') }
  end
end
+1 −1
Original line number Diff line number Diff line
@@ -35,7 +35,7 @@ RSpec.describe Labkit::CoveredExperience do
      it "raises error when RAILS_ENV is #{env}" do
        stub_env('RAILS_ENV', env)

        expect { described_class['nonexistent'] }.to raise_error(RuntimeError, "Covered Experience nonexistent not found in the registry")
        expect { described_class['nonexistent'] }.to raise_error(Labkit::CoveredExperience::NotFoundError, "Covered Experience nonexistent not found in the registry")
      end
    end
  end