Skip to content

Commit

Permalink
Merge pull request #3 from senid231/add-send-metrics-matcher
Browse files Browse the repository at this point in the history
add send_metrics matcher, add base_stats instrumentation tests
  • Loading branch information
senid231 authored Nov 29, 2023
2 parents 1c704a2 + b247622 commit bad109f
Show file tree
Hide file tree
Showing 6 changed files with 194 additions and 3 deletions.
10 changes: 10 additions & 0 deletions lib/prometheus_exporter/ext/rspec.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,17 @@
# frozen_string_literal: true

require 'prometheus_exporter/client'
require_relative 'rspec/matchers'
require_relative 'rspec/test_client'

# Setups default client before it used anywhere.
# use `include_examples :observes_prometheus_metrics` in specs
PrometheusExporter::Client.default = PrometheusExporter::Ext::RSpec::TestClient.instance

RSpec.configure do |config|
config.include PrometheusExporter::Ext::RSpec::Matchers

config.before do
PrometheusExporter::Ext::RSpec::TestClient.instance.reset
end
end
5 changes: 5 additions & 0 deletions lib/prometheus_exporter/ext/rspec/matchers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,14 @@

require 'date'
require_relative 'metric_matcher'
require_relative 'send_metrics_matcher'

module PrometheusExporter::Ext::RSpec
module Matchers
def send_metrics(expected = nil)
PrometheusExporter::Ext::RSpec::SendMetricsMatcher.new(expected)
end

def a_prometheus_metric(klass, name)
MetricMatcher.new(klass, name)
end
Expand Down
86 changes: 86 additions & 0 deletions lib/prometheus_exporter/ext/rspec/send_metrics_matcher.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
# frozen_string_literal: true

module PrometheusExporter::Ext::RSpec
class SendMetricsMatcher
include RSpec::Matchers::DSL::DefaultImplementations
include RSpec::Matchers
include RSpec::Matchers::Composable

attr_reader :expected, :actual

def initialize(expected)
@expected = expected&.map { |metric| deep_stringify_keys(metric) }
@ordered = false
@times = nil
end

def name
'sends metrics to prometheus'
end

def supports_block_expectations?
true
end

def matches?(actual_proc)
raise ArgumentError, "#{name} matcher supports only block expectations" unless actual_proc.is_a?(Proc)

metrics_before = PrometheusExporter::Ext::RSpec::TestClient.instance.metrics
actual_proc.call
metrics_after = PrometheusExporter::Ext::RSpec::TestClient.instance.metrics - metrics_before
@actual = metrics_after.map { |metric| deep_stringify_keys(metric) }

if expected
expected_value = @ordered ? expected : match_array(expected)
values_match?(expected_value, actual)
elsif @times
values_match?(@times, actual.size)
else
actual.size >= 1
end
end

def failure_message
if expected
expected_value = @ordered ? expected : match_array(expected)
+"expected #{name} to receive #{description_of(expected_value)}, but got\n #{description_of(actual)}"
elsif @times
values_match?(@times, actual.size)
+"expected #{name} to receive #{@times} metrics, but got #{actual.size}\n #{description_of(actual)}"
else
actual.size
+"expected #{name} to receive more than 1 metric, but got #{actual.size}\n #{description_of(actual)}"
end
end

def ordered
raise ArgumentError, 'ordered cannot be when expected not provided' if expected.nil?
raise ArgumentError, 'ordered cannot be used with times' if @times

@ordered = true
self
end

def times(qty)
raise ArgumentError, 'times argument must be an integer' unless qty.is_a?(Integer)
raise ArgumentError, 'times argument must be >= 1' unless qty >= 1
raise ArgumentError, 'ordered cannot be when expected is provided' unless expected.nil?
raise ArgumentError, 'ordered cannot be used with times' if @ordered

@times = qty
self
end

def description_of(object)
RSpec::Support::ObjectFormatter.new(nil).format(object)
end

private

def deep_stringify_keys(hash)
hash.transform_keys(&:to_s).transform_values do |value|
value.is_a?(Hash) ? deep_stringify_keys(value) : value
end
end
end
end
30 changes: 30 additions & 0 deletions lib/prometheus_exporter/ext/rspec/test_client.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# frozen_string_literal: true

require 'singleton'

module PrometheusExporter::Ext::RSpec
class TestClient
include Singleton

def initialize
super
reset
end

def metrics
@metrics.dup
end

def send_json(data)
@metrics << data
end

def reset
@metrics = []
end

def stop
nil
end
end
end
50 changes: 50 additions & 0 deletions spec/prometheus_exporter/ext/instrumentation/base_stats_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# frozen_string_literal: true

RSpec.describe PrometheusExporter::Ext::Instrumentation::BaseStats do
subject do
instrumentation.collect(data_list)
end

let(:instrumentation) { TestInstrumentation.new }
let(:data_list) do
[
{ foo: 123, bar: 456, labels: { qwe: 'asd' } },
{ foo: 124, bar: 457, labels: { qwe: 'zxc' } }
]
end

it 'sends metrics' do
expect { subject }.to send_metrics
end

it 'sends exactly 2 metrics' do
expect { subject }.to send_metrics.times(2)
end

it 'sends correct metrics' do
expect { subject }.to send_metrics(
[
{
type: 'test',
foo: 123, bar: 456,
labels: { qwe: 'asd' },
metric_labels: {}
},
{
type: 'test',
foo: 124, bar: 457,
labels: { qwe: 'zxc' },
metric_labels: {}
}
]
)
end

context 'with empty data list' do
let(:data_list) { [] }

it 'sends correct metrics' do
expect { subject }.not_to send_metrics
end
end
end
16 changes: 13 additions & 3 deletions spec/spec_helper.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,19 @@
# frozen_string_literal: true

require 'prometheus_exporter'
require 'prometheus_exporter/ext'
require 'prometheus_exporter/ext/rspec/matchers'
require 'prometheus_exporter/ext/rspec'
require 'prometheus_exporter/ext/instrumentation/base_stats'

class TestInstrumentation < PrometheusExporter::Ext::Instrumentation::BaseStats
self.type = 'test'

def collect(data_list)
data_list.each do |data|
collect_data(data)
end
end
end

RSpec.configure do |config|
# Enable flags like --only-failures and --next-failure
Expand All @@ -13,6 +25,4 @@
config.expect_with :rspec do |c|
c.syntax = :expect
end

config.include PrometheusExporter::Ext::RSpec::Matchers
end

0 comments on commit bad109f

Please sign in to comment.