pyrotest

package module
v0.6.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 22, 2025 License: Apache-2.0 Imports: 13 Imported by: 0

README

pyrotest

PkgGoDev License build and test Go Report Card Coverage

pyrotest provides Gomega matchers in combination with specially typed matchers for reasoning about Prometheus metrics in unit tests. This package is opinionated in that certain details about how the Prometheus module organizes metrics should not matter and tests should be authored from the perspective of a user working with Prometheus metrics, not necessarily from the perspective of the Prometheus developers.

In consequence, pyrotest conceals the somewhat fussy hierarchical differentiation of the Prometheus data model into metric families only then containing the individual metrics (that is, the individual “timeseries”) as a nasty implementation detail. Not least, as a prometheus user you deal with the (ultimate) metrics, not families.

This module supports counters, gauges, and the so-called “classic” histograms.

Example

package some_test

import (
	. "github.com/onsi/ginkgo/v2"
	. "github.com/onsi/gomega"
	. "github.com/thediveo/pyrotest"
)

var _ = Describe("example", func() {

    It("collects metrics, lints them, and reasons about them", func() {
        var coll prometheus.Collector = ...

        families := CollectAndLint(coll)
        Expect(families).To(ContainMetrics(
            Gauge(HaveName("bar_baz"),
                HaveHelp(Not(BeEmpty())),
                HaveLabel("foobar=baz")),
            Counter(HaveName(ContainSubstring("_total")),
                HaveHelp(ContainSubstring("no help")),
                HaveLabelWithValue("label", "scam")),
            Histogram(HaveName("barz"),
                HaveSomeFilledBuckets()),
        ))
    })

})

Contributing

Please see CONTRIBUTING.md.

DevContainer

[!CAUTION]

Do not use VSCode's "Dev Containers: Clone Repository in Container Volume" command, as it is utterly broken by design, ignoring .devcontainer/devcontainer.json.

  1. git clone https://github.com/thediveo/pyrotest
  2. in VSCode: Ctrl+Shift+P, "Dev Containers: Open Workspace in Container..."
  3. select pyrotest.code-workspace and off you go...

Go Version Support

pyrotest supports versions of Go that are noted by the Go release policy, that is, major versions N and N-1 (where N is the current major version).

pyrotest is Copyright 2025 Harald Albrecht, and licensed under the Apache License, Version 2.0.

Documentation

Overview

Package pyrotest provides a Prometheus datamodel-specific DSL for writing concise and easily readable assertions on metrics. In addition, it highly values clear and concise failure messages that are easy to understand, avoiding unnecessary and distracting matcher details (as Gomega is sometime fallible to).

Metric Families and Metrics

As a timeseries database user you don't care about the gory internal details of the Prometheus data model, you just work with “metrics” that have “labels”.

Consequently, package pyrotest conceals the confusing and fussy hierarchical differentiation of the Prometheus data model into metric families that only then contain individual metrics (that is, the individual “timeseries”).

Usage

A typical usage might look like this:

// import . "github.com/thediveo/pyrotest"

metfams := CollectAndLint(mycollector)
Expect(metfams).To(ContainMetrics(
    Counter(HaveName("foo_sprockets_total"),
        HaveUnit("sprockets"),
        HaveLabel("type=barz"), HaveLabel("anyway")),
    Gauge(HaveName(ContainsSubstring("rumpelpumpel")),
        HaveLabel("region=elsewhere")),
))

The basic types.GomegaMatcher for matching a single or multiple metrics are:

  • ContainMetrics succeeds if actual contains all expected metrics.
  • BeAMetric succeeds if actual is a metric (family) with the expected properties.

Both ContainMetrics and BeAMetric accept only the following metric-type specific matchers:

Each of these specific matchers then accept the following metric property-related matchers, where these matchers often accept either a simple value or alternatively types.GomegaMatcher (allowing for much more complex assertions):

Motivation

This package isn't strictly necessary, as Gomega's matcher toolchest already contains everything necessary. However, just sticking to these basics requires extensive use of gomega.HaveField matchers in combination with protobuf-originating accessor functions like prommodel.MetricFamily.GetName in order to correctly deal with the level of pointer indirection used in protobuf optional fields – which the Prometheus data model likes to use basically everywhere. pyrotest brings back concise matcher design, with build-time type-safety on top.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func BeAMetric

func BeAMetric(m MetricMatcher) types.GomegaMatcher

BeAMetric succeeds if actual is a Prometheus *prommodel.MetricFamily that matches the passed-in metric properties in form of a MetricMatcher.

func ContainMetrics

func ContainMetrics(ms ...MetricMatcher) types.GomegaMatcher

ContainMetrics succeeds if actual represents a MetricsFamilies map and contains the passed-in metrics described by MetricMatcher elements.

Types

type BeAMetricMatcher

type BeAMetricMatcher struct {
	Expected MetricMatcher
}

func (*BeAMetricMatcher) FailureMessage

func (m *BeAMetricMatcher) FailureMessage(actual any) string

func (*BeAMetricMatcher) Match

func (m *BeAMetricMatcher) Match(actual any) (bool, error)

func (*BeAMetricMatcher) NegatedFailureMessage

func (m *BeAMetricMatcher) NegatedFailureMessage(actual any) string

type ContainMetricsMatcher

type ContainMetricsMatcher struct {
	ExpectedMetrics []MetricMatcher
	// contains filtered or unexported fields
}

ContainMetricsMatcher is a types.GomegaMatcher that succeeds if an actual value is assignable to a MetricsFamilies map and that metric families map contains all expected metrics.

func (*ContainMetricsMatcher) FailureMessage

func (m *ContainMetricsMatcher) FailureMessage(actual any) string

func (*ContainMetricsMatcher) Match

func (m *ContainMetricsMatcher) Match(actual any) (bool, error)

func (*ContainMetricsMatcher) NegatedFailureMessage

func (m *ContainMetricsMatcher) NegatedFailureMessage(actual any) string

type HaveLabelMatcher

type HaveLabelMatcher struct {
	// contains filtered or unexported fields
}

HaveLabelMatcher succeeds if it matches an actual metric label by name and optionally by value. The HaveLabelMatcher supports describing the name and value matches as either verbatim string matches or alternatively using types.GomegaMatcher.

func (*HaveLabelMatcher) GomegaString

func (m *HaveLabelMatcher) GomegaString() string

GomegaString returns an optimized string representation for failure reporting, reducing visual clutter as much as possible. In case both the expected name and the value are plain string values. Otherwise, it falls back to reporting both name and value using Gomega's format.Object. In any case, it never reports useless private state, such as the name of the matcher constructor used, or the derived name and value matcher objects.

type HistoryBucketBoundariesMatcher added in v0.6.0

type HistoryBucketBoundariesMatcher struct {
	// contains filtered or unexported fields
}

HistoryBucketBoundariesMatcher matches the explicit bucket upper boundaries.

func (*HistoryBucketBoundariesMatcher) GomegaString added in v0.6.0

func (m *HistoryBucketBoundariesMatcher) GomegaString() string

type HistoryEmptyBucketsMatcher added in v0.6.0

type HistoryEmptyBucketsMatcher struct {
	// contains filtered or unexported fields
}

HistoryEmptyBucketsMatcher matches the empty buckets including the implicit “+Inf” empty bucket.

func (*HistoryEmptyBucketsMatcher) GomegaString added in v0.6.0

func (m *HistoryEmptyBucketsMatcher) GomegaString() string

type HistorySomeFilledBucketsMatcher added in v0.6.0

type HistorySomeFilledBucketsMatcher struct {
	// contains filtered or unexported fields
}

HistorySomeFilledBucketsMatcher matches any non-empty buckets including the implicit “+Inf” bucket.

func (*HistorySomeFilledBucketsMatcher) GomegaString added in v0.6.0

func (m *HistorySomeFilledBucketsMatcher) GomegaString() string

type MetricFamilyHelpMatcher

type MetricFamilyHelpMatcher struct {
	// contains filtered or unexported fields
}

MetricFamilyHelpMatcher matches the help property of a metric family.

func (*MetricFamilyHelpMatcher) GomegaString

func (m *MetricFamilyHelpMatcher) GomegaString() string

type MetricFamilyNameMatcher

type MetricFamilyNameMatcher struct {
	// contains filtered or unexported fields
}

MetricFamilyNameMatcher matches the name property of a metric family and optionally indicates a plain name for direct family lookup.

func (*MetricFamilyNameMatcher) GomegaString

func (m *MetricFamilyNameMatcher) GomegaString() string

GomegaString returns an optimized string representation of this metric family name matcher as to make failure reporting more useful. In particular it hides implementation detail state information from the representation.

type MetricFamilyPropertyMatcher

type MetricFamilyPropertyMatcher interface {
	// contains filtered or unexported methods
}

MetricFamilyPropertyMatcher matches properties of a metric family (as opposed to properties of individual metrics).

type MetricFamilyUnitMatcher

type MetricFamilyUnitMatcher struct {
	// contains filtered or unexported fields
}

MetricFamilyUnitMatcher matches the unit property of a metric family.

func (*MetricFamilyUnitMatcher) GomegaString

func (m *MetricFamilyUnitMatcher) GomegaString() string

type MetricMatcher

type MetricMatcher interface {
	// contains filtered or unexported methods
}

MetricMatcher succeeds if it matches any metric within a metric family, based on family and metric properties. All properties not explicitly specified are taken as “don't care”.

Note: when a MetricMatcher matches the name of a metric to a plain string, it can report so using the unexported indexname method in order to allow outer matchers to optimize finding a matching metric family using a direct dictionary lookup.

A MetricMatcher is a composite matcher that succeeds only if all its MetricPropertyMatcher succeed, such as matching metric family properties like name, unit, help, and labels.

func Counter

func Counter(props ...MetricPropertyMatcher) MetricMatcher

Counter succeeds if a metric (metric family) is a Prometheus Counter and additionally satisfies all optionally specified name, help, and labels matchers.

See also:

func Gauge

func Gauge(props ...MetricPropertyMatcher) MetricMatcher

Gauge succeeds if a metric (metric family) is a Prometheus Gauge and additionally satisfies all optionally specified name, help, and labels matchers.

See also:

func Histogram

func Histogram(props ...MetricPropertyMatcher) MetricMatcher

Histogram succeeds if a metric (metric family) is a Prometheus “conventional” Histogram and additionally satisfies all optionally specified name, help, and labels matchers.

See also:

type MetricPropertyMatcher

type MetricPropertyMatcher interface {
	// contains filtered or unexported methods
}

MetricPropertyMatcher is an interface marker for matchers of eiher a metric family property or a single metric property, depending on the level at which in the Prometheus data model the property is located on.

func HaveAllEmptyBuckets added in v0.6.0

func HaveAllEmptyBuckets() MetricPropertyMatcher

HaveAllEmptyBuckets succeeds if a metric is a Histogram and all its buckets as well as its count (the implicit cumulative “+Inf” bucket) are zero.

func HaveBucketBoundaries added in v0.6.0

func HaveBucketBoundaries(values any) MetricPropertyMatcher

HaveBucketBoundaries succeeds if a metric is a Histogram and has the matching (float64) bucket upper boundaries. Please note the “+Inf” boundary is implicit and thus must not be specified in the bucket boundaries slice passed to this matcher. If passed a types.GomegaMatcher, this matcher gets passed a slice of []uint64 explicit boundaries as the actual value.

func HaveHelp

func HaveHelp(help any) MetricPropertyMatcher

HaveHelp succeeds if a metric family has a help text that either equals the passed string or matches the passed types.GomegaMatcher.

func HaveLabel

func HaveLabel(label any) MetricPropertyMatcher

HaveLabel succeeds if a metric has a label with the specified name (and optional value).

The value passed into the label parameter can be either a string or GomegaMatcher:

  • a string in the form of “name” where it must match a label name, or in the “name=value” form where it must match both the label name and value.
  • a GomegaMatcher that matches the name only.
  • any other type of value is an error.

See also HaveLabelWithValue.

func HaveLabelWithValue

func HaveLabelWithValue(name, value any) MetricPropertyMatcher

HaveLabelWithValue succeeds if a metric has a label with the specified name and value.

The value passed into the name parameter can be either a string or a GomegaMatcher. Similarly, the value passed into the value parameter can also be either a string or a GomegaMatcher. Passing any other type of value to either the name or value parameter is an error.

See also HaveLabel.

func HaveMetricValue added in v0.6.0

func HaveMetricValue(value any) MetricPropertyMatcher

HaveMetricValue succeeds if a metric is a Counter or a Gauge and has a matching float64 value.

func HaveName

func HaveName(name any) MetricPropertyMatcher

HaveName succeeds if a metric family has a name that either equals the passed string or matches the passed GomegaMatcher.

func HaveSomeFilledBuckets added in v0.6.0

func HaveSomeFilledBuckets() MetricPropertyMatcher

HaveSomeFilledBuckets succeeds if a metric is a Histogram and at least one of its buckets, including the implicit “+Inf” bucket, is not empty.

func HaveUnit

func HaveUnit(unit any) MetricPropertyMatcher

HaveUnit succeeds if a metric family has a unit that either equals the passed string or matches the passed types.GomegaMatcher.

type MetricValueMatcher added in v0.6.0

type MetricValueMatcher struct {
	// contains filtered or unexported fields
}

MetricValueMatcher matches the value property of a Counter or Gauge metric.

func (*MetricValueMatcher) GomegaString added in v0.6.0

func (m *MetricValueMatcher) GomegaString() string

type MetricsFamilies

type MetricsFamilies = map[string]*prommodel.MetricFamily

MetricFamilies maps from a metric family name to its metric family.

func CollectAndLint

func CollectAndLint(coll prometheus.Collector, metricNames ...string) MetricsFamilies

CollectAndLint collects metrics from the passed-in prometheus.Collector, linting them, and finally returns them if there are neither errors nor linting issues. Otherwise, CollectAndLint will fail the current test with details about collecting or linting problems. CollectAndLint uses a newly created pedantic prometheus.Registry.

If any metric names are passed in, only metrics with those names are checked.

func GatherAndLint

func GatherAndLint(g prometheus.Gatherer, metricNames ...string) MetricsFamilies

GatherAndLint gathers all metrics from the passed-in prometheus.Gatherer, linting them, and finally returns them if there are neither errors nor linting erros. Otherwise, GatherAndLint will fail the current test with details about gathering or linting problems.

If any metric names are passed in, only metrics with those names are checked.

type TypedMetricFamilyMatcher

type TypedMetricFamilyMatcher struct {
	// contains filtered or unexported fields
}

TypedMetricFamilyMatcher implements MetricMatcher to match metrics within a metric family that satisfy a mandatory type, optional name, optional properties other than name and labels, and finally a set of labels.

func (*TypedMetricFamilyMatcher) GomegaString

func (m *TypedMetricFamilyMatcher) GomegaString() string

GomegaString returns an optimized string representation for failure reporting, reducing visual clutter compared to simply dumbing the matcher using Gomega's format.Object.

Directories

Path Synopsis
Package to provides convenience conversion functions when working with histogram buckets.
Package to provides convenience conversion functions when working with histogram buckets.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL