File: dbstats_test.go

package info (click to toggle)
golang-gitlab-gitlab-org-labkit 1.17.0-4
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 1,092 kB
  • sloc: sh: 210; javascript: 49; makefile: 4
file content (124 lines) | stat: -rw-r--r-- 3,619 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
package sqlmetrics

import (
	"bytes"
	"database/sql"
	"fmt"
	"html/template"
	"testing"

	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/testutil"
	"github.com/stretchr/testify/require"
)

type dbMock sql.DBStats

func (db dbMock) Stats() sql.DBStats {
	return sql.DBStats(db)
}

func TestNewDBStatsCollector(t *testing.T) {
	db := &dbMock{
		MaxOpenConnections: 100,
		OpenConnections:    10,
		InUse:              3,
		Idle:               7,
		WaitCount:          5,
		WaitDuration:       12,
		MaxIdleClosed:      7,
		MaxIdleTimeClosed:  6,
		MaxLifetimeClosed:  8,
	}

	dbName := "foo"
	defaultLabels := prometheus.Labels{dbNameLabel: dbName}

	tests := []struct {
		name           string
		opts           []DBStatsCollectorOption
		expectedLabels prometheus.Labels
	}{
		{
			name:           "default",
			expectedLabels: defaultLabels,
		},
		{
			name: "with custom labels",
			opts: []DBStatsCollectorOption{
				WithExtraLabels(map[string]string{"x": "y"}),
			},
			expectedLabels: prometheus.Labels{
				dbNameLabel: dbName,
				"x":         "y",
			},
		},
		{
			name: "does not override db_name label",
			opts: []DBStatsCollectorOption{
				WithExtraLabels(map[string]string{"db_name": "bar"}),
			},
			expectedLabels: defaultLabels,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			collector := NewDBStatsCollector(dbName, db, tt.opts...)

			validateMetric(t, collector, maxOpenConnectionsName, maxOpenConnectionsDesc, "gauge", float64(db.MaxOpenConnections), tt.expectedLabels)
			validateMetric(t, collector, openConnectionsName, openConnectionsDesc, "gauge", float64(db.OpenConnections), tt.expectedLabels)
			validateMetric(t, collector, inUseName, inUseDesc, "gauge", float64(db.InUse), tt.expectedLabels)
			validateMetric(t, collector, idleName, idleDesc, "gauge", float64(db.Idle), tt.expectedLabels)
			validateMetric(t, collector, waitCountName, waitCountDesc, "counter", float64(db.WaitCount), tt.expectedLabels)
			validateMetric(t, collector, waitDurationName, waitDurationDesc, "counter", db.WaitDuration.Seconds(), tt.expectedLabels)
			validateMetric(t, collector, maxIdleClosedName, maxIdleClosedDesc, "counter", float64(db.MaxIdleClosed), tt.expectedLabels)
			validateMetric(t, collector, maxIdleTimeClosedName, maxIdleTimeClosedDesc, "counter", float64(db.MaxIdleTimeClosed), tt.expectedLabels)
			validateMetric(t, collector, maxLifetimeClosedName, maxLifetimeClosedDesc, "counter", float64(db.MaxLifetimeClosed), tt.expectedLabels)
		})
	}
}

type labelsIter struct {
	Dict    prometheus.Labels
	Counter int
}

func (l *labelsIter) HasMore() bool {
	l.Counter++
	return l.Counter < len(l.Dict)
}

func validateMetric(t *testing.T, collector prometheus.Collector, name string, desc string, valueType string, value float64, labels prometheus.Labels) {
	t.Helper()

	tmpl := template.New("")
	tmpl.Delims("[[", "]]")
	txt := `
# HELP [[.Name]] [[.Desc]]
# TYPE [[.Name]] [[.Type]]
[[.Name]]{[[range $k, $v := .Labels.Dict]][[$k]]="[[$v]]"[[if $.Labels.HasMore]],[[end]][[end]]} [[.Value]]
`
	_, err := tmpl.Parse(txt)
	require.NoError(t, err)

	var expected bytes.Buffer
	fullName := fmt.Sprintf("%s_%s_%s", namespace, subsystem, name)

	err = tmpl.Execute(&expected, struct {
		Name   string
		Desc   string
		Type   string
		Value  float64
		Labels *labelsIter
	}{
		Name:   fullName,
		Desc:   desc,
		Labels: &labelsIter{Dict: labels},
		Value:  value,
		Type:   valueType,
	})
	require.NoError(t, err)

	err = testutil.CollectAndCompare(collector, &expected, fullName)
	require.NoError(t, err)
}