Verified Commit 21c37799 authored by Hercules Merscher's avatar Hercules Merscher 🌴
Browse files

feat: Labkit::RSpec matchers

parent 3908acfd
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@ LabKit-Ruby provides functionality in a number of areas:
1. `Labkit::FIPS` for checking for FIPS mode and using FIPS-compliant algorithms.
1. `Labkit::Logging` for sanitizing log messages.
1. `Labkit::Metrics` for metrics. More on the [README](./lib/labkit/metrics/README.md).
1. `Labkit::RSpec` for RSpec matchers to test Labkit functionality (requires selective loading). More on the [README](./lib/labkit/rspec/README.md).
1. `Labkit::Tracing` for handling and propagating distributed traces.

## Developing
+121 −0
Original line number Diff line number Diff line
# Labkit RSpec Support

This module provides RSpec matchers for testing Labkit functionality in your Rails applications.

## Setup

You must explicitly require the RSpec matchers in your test files:

```ruby
# In your spec_helper.rb or rails_helper.rb
require 'labkit/rspec/matchers'
```

This approach ensures that:
- Test dependencies are not loaded in production environments
- You have explicit control over which matchers are available
- The gem remains lightweight for non-testing use cases


## Available Matchers

### Covered Experience Matchers

These matchers help you test that your code properly instruments covered experiences with the expected metrics.

#### `start_covered_experience`

Tests that a covered experience is started (checkpoint=start metric is incremented).

```ruby
expect { subject }.to start_covered_experience('rails_request')

# Test that it does NOT start
expect { subject }.not_to start_covered_experience('rails_request')
```

#### `checkpoint_covered_experience`

Tests that a covered experience checkpoint is recorded (checkpoint=intermediate metric is incremented).

```ruby
expect { subject }.to checkpoint_covered_experience('rails_request')

# Test that it does NOT checkpoint
expect { subject }.not_to checkpoint_covered_experience('rails_request')
```

#### `complete_covered_experience`

Tests that a covered experience is completed with the expected metrics:
- `gitlab_covered_experience_checkpoint_total` (with checkpoint=end)
- `gitlab_covered_experience_total` (with error flag)
- `gitlab_covered_experience_apdex_total` (with success flag)

```ruby
# Test successful completion
expect { subject }.to complete_covered_experience('rails_request')

# Test completion with error
expect { subject }.to complete_covered_experience('rails_request', error: true, success: false)

# Test that it does NOT complete
expect { subject }.not_to complete_covered_experience('rails_request')
```

## Example Usage

### In your spec_helper.rb or rails_helper.rb:

```ruby
# spec/spec_helper.rb or spec/rails_helper.rb
require 'gitlab-labkit'

# Explicitly require the RSpec matchers
require 'labkit/rspec/matchers'

RSpec.configure do |config|
  # Your other RSpec configuration...
end
```

### In your test files:

```ruby
RSpec.describe MyController, type: :controller do
  describe '#index' do
    it 'instruments the request properly' do
      expect { get :index }.to start_covered_experience('rails_request')
        .and complete_covered_experience('rails_request')
    end

    context 'when an error occurs' do
      before do
        allow(MyService).to receive(:call).and_raise(StandardError)
      end

      it 'records the error in metrics' do
        expect { get :index }.to complete_covered_experience('rails_request', error: true, success: false)
      end
    end
  end
end
```

### For individual spec files (alternative approach):

```ruby
# spec/controllers/my_controller_spec.rb
require 'spec_helper'
require 'labkit/rspec/matchers' # Can also be required per-file if needed

RSpec.describe MyController do
  # Your tests using the matchers...
end
```

## Requirements

- The covered experience must be registered in `Labkit::CoveredExperience::Registry`
- Metrics must be properly configured in your test environment
- The code under test must use Labkit's covered experience instrumentation
+10 −0
Original line number Diff line number Diff line
# frozen_string_literal: true

# RSpec matchers loader for Labkit
#
# This file loads all available RSpec matchers for Labkit.
# It must be explicitly required in your test setup.

raise LoadError, "RSpec is not loaded. Please require 'rspec' before requiring 'labkit/rspec/matchers'" unless defined?(RSpec)

require_relative 'matchers/covered_experience_matchers'
+21 −7
Original line number Diff line number Diff line
# frozen_string_literal: true

# RSpec matchers for testing Labkit CoveredExperience functionality
#
# This file must be explicitly required in your test setup:
#   require 'labkit/rspec/matchers'

raise LoadError, "RSpec is not loaded. Please require 'rspec' before requiring 'labkit/rspec/matchers'" unless defined?(RSpec)

module Labkit
  module RSpec
    module Matchers
      # Helper module for CoveredExperience metrics access
      module CoveredExperienceMetrics
        def checkpoint_counter
          Labkit::Metrics::Client.get(:gitlab_covered_experience_checkpoint_total)
@@ -20,6 +31,9 @@ module CoveredExperienceMetrics
          definition.to_h.slice(:id, :feature_category, :urgency)
        end
      end
    end
  end
end

# Matcher for verifying CoveredExperience start metrics instrumentation.
#
@@ -32,7 +46,7 @@ end
# Parameters:
# - covered_experience_id: Required. The ID of the covered experience (e.g., 'rails_request')
RSpec::Matchers.define :start_covered_experience do |covered_experience_id|
  include CoveredExperienceMetrics
  include Labkit::RSpec::Matchers::CoveredExperienceMetrics

  description { "start covered experience '#{covered_experience_id}'" }
  supports_block_expectations
@@ -68,7 +82,7 @@ end
# Parameters:
# - covered_experience_id: Required. The ID of the covered experience (e.g., 'rails_request')
RSpec::Matchers.define :checkpoint_covered_experience do |covered_experience_id|
  include CoveredExperienceMetrics
  include Labkit::RSpec::Matchers::CoveredExperienceMetrics

  description { "checkpoint covered experience '#{covered_experience_id}'" }
  supports_block_expectations
@@ -101,7 +115,7 @@ RSpec::Matchers.define :checkpoint_covered_experience do |covered_experience_id|
    checkpoint_after = checkpoint_counter&.get(labels.merge(checkpoint: "intermediate")).to_i
    @checkpoint_change = checkpoint_after - checkpoint_before

    @checkpoint_change == 0
    @checkpoint_change.zero?
  end

  failure_message_when_negated do
@@ -113,7 +127,7 @@ end
# Matcher for verifying CoveredExperience completion metrics instrumentation.
#
# Usage:
#   expect { subject }.to covered_experience_completed('rails_request')
#   expect { subject }.to complete_covered_experience('rails_request')
#
# This matcher verifies that the following metrics are incremented with specific labels:
# - gitlab_covered_experience_checkpoint_total (with checkpoint=end)
@@ -123,9 +137,9 @@ end
# Parameters:
# - covered_experience_id: Required. The ID of the covered experience (e.g., 'rails_request')
# - error: Optional. The expected error flag for gitlab_covered_experience_total (false by default)
# - error: Optional. The expected success flag for gitlab_covered_experience_apdex_total (true by default)
# - success: Optional. The expected success flag for gitlab_covered_experience_apdex_total (true by default)
RSpec::Matchers.define :complete_covered_experience do |covered_experience_id, error: false, success: true|
  include CoveredExperienceMetrics
  include Labkit::RSpec::Matchers::CoveredExperienceMetrics

  description { "complete covered experience '#{covered_experience_id}'" }
  supports_block_expectations
@@ -172,7 +186,7 @@ RSpec::Matchers.define :complete_covered_experience do |covered_experience_id, e
    @total_change = total_after - total_before
    @apdex_change = apdex_after - apdex_before

    @checkpoint_change == 0 && @total_change == 0 && @apdex_change == (error ? 1 : 0)
    @checkpoint_change.zero? && @total_change.zero? && @apdex_change == (error ? 1 : 0)
  end

  failure_message_when_negated do
+1 −1
Original line number Diff line number Diff line
@@ -3,7 +3,7 @@
require 'spec_helper'
require 'labkit/covered_experience/registry'
require 'labkit/covered_experience/experience'
require_relative '../../support/covered_experience/matchers'
require 'labkit/rspec/matchers/covered_experience_matchers'

RSpec.describe Labkit::CoveredExperience::Experience, :with_metrics_config do
  include StubENV