Skip to content

Commit 60357ba

Browse files
committed
Add unit tests
1 parent af07b32 commit 60357ba

40 files changed

Lines changed: 5849 additions & 83 deletions

internal/cmd/monitor_test.go

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
package cmd
2+
3+
import (
4+
"net/url"
5+
"testing"
6+
7+
"github.com/stretchr/testify/assert"
8+
"github.com/stretchr/testify/require"
9+
)
10+
11+
func TestMonitor(t *testing.T) {
12+
ctx, flags := NewTestContext()
13+
ctx.App.Version = "1.2.3"
14+
flags.String("internal-monitoring-listener-address", "", "")
15+
require.NoError(t, flags.Set("internal-monitoring-listener-address", "http://127.0.0.1:8081"))
16+
17+
var (
18+
called bool
19+
gotVersion string
20+
gotListenerURL *url.URL
21+
)
22+
23+
previousStart := startMonitorUI
24+
startMonitorUI = func(version string, listenerAddress *url.URL) {
25+
called = true
26+
gotVersion = version
27+
gotListenerURL = listenerAddress
28+
}
29+
t.Cleanup(func() {
30+
startMonitorUI = previousStart
31+
})
32+
33+
exitCode, err := Monitor(ctx)
34+
require.NoError(t, err)
35+
assert.Equal(t, 0, exitCode)
36+
assert.True(t, called)
37+
assert.Equal(t, "1.2.3", gotVersion)
38+
require.NotNil(t, gotListenerURL)
39+
assert.Equal(t, "http://127.0.0.1:8081", gotListenerURL.String())
40+
}
41+
42+
func TestMonitorReturnsErrorWhenInternalMonitoringAddressIsInvalid(t *testing.T) {
43+
ctx, flags := NewTestContext()
44+
flags.String("internal-monitoring-listener-address", "", "")
45+
require.NoError(t, flags.Set("internal-monitoring-listener-address", "://bad-url"))
46+
47+
called := false
48+
previousStart := startMonitorUI
49+
startMonitorUI = func(version string, listenerAddress *url.URL) {
50+
called = true
51+
}
52+
t.Cleanup(func() {
53+
startMonitorUI = previousStart
54+
})
55+
56+
exitCode, err := Monitor(ctx)
57+
require.Error(t, err)
58+
assert.Equal(t, 1, exitCode)
59+
assert.False(t, called)
60+
}
61+
62+
func TestMonitorWithoutInternalMonitoringAddress(t *testing.T) {
63+
ctx, flags := NewTestContext()
64+
ctx.App.Version = "dev"
65+
flags.String("internal-monitoring-listener-address", "", "")
66+
67+
called := false
68+
var gotListenerURL *url.URL
69+
previousStart := startMonitorUI
70+
startMonitorUI = func(version string, listenerAddress *url.URL) {
71+
called = true
72+
gotListenerURL = listenerAddress
73+
}
74+
t.Cleanup(func() {
75+
startMonitorUI = previousStart
76+
})
77+
78+
exitCode, err := Monitor(ctx)
79+
require.NoError(t, err)
80+
assert.Equal(t, 0, exitCode)
81+
assert.True(t, called)
82+
assert.Nil(t, gotListenerURL)
83+
}
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
package collectors
2+
3+
import (
4+
"io"
5+
"log/slog"
6+
"strings"
7+
"testing"
8+
9+
dto "github.com/prometheus/client_model/go"
10+
"github.com/prometheus/client_golang/prometheus"
11+
"github.com/stretchr/testify/assert"
12+
"github.com/stretchr/testify/require"
13+
)
14+
15+
func newTestLogger() *slog.Logger {
16+
return slog.New(slog.NewTextHandler(io.Discard, nil))
17+
}
18+
19+
func descString(t *testing.T, desc *prometheus.Desc) string {
20+
t.Helper()
21+
require.NotNil(t, desc)
22+
23+
return desc.String()
24+
}
25+
26+
func TestNewMetrics(t *testing.T) {
27+
m := NewMetrics()
28+
29+
require.NotNil(t, m)
30+
assert.Contains(t, descString(t, m.sampleMetric1), `fqName: "gitlab-ci-exporter_sampleMetric1"`)
31+
assert.Contains(t, descString(t, m.sampleMetric1), `variableLabels: {label1}`)
32+
assert.Contains(t, descString(t, m.sampleMetric2), `fqName: "gitlab-ci-exporter_sampleMetric2"`)
33+
assert.Contains(t, descString(t, m.sampleMetric2), `variableLabels: {label2}`)
34+
}
35+
36+
func TestNewExporter(t *testing.T) {
37+
settings := &Settings{
38+
LogLevel: "info",
39+
LogFormat: "text",
40+
MetricsPath: "/metrics",
41+
ListenPort: "8080",
42+
Address: "0.0.0.0",
43+
}
44+
logger := newTestLogger()
45+
46+
exporter, err := NewExporter(settings, logger)
47+
require.NoError(t, err)
48+
require.NotNil(t, exporter)
49+
require.NotNil(t, exporter.metrics)
50+
assert.Same(t, settings, exporter.Settings)
51+
assert.Same(t, logger, exporter.Logger)
52+
}
53+
54+
func TestExporterDescribe(t *testing.T) {
55+
exporter, err := NewExporter(&Settings{}, newTestLogger())
56+
require.NoError(t, err)
57+
58+
ch := make(chan *prometheus.Desc, 2)
59+
exporter.Describe(ch)
60+
close(ch)
61+
62+
var descs []string
63+
for desc := range ch {
64+
descs = append(descs, desc.String())
65+
}
66+
67+
require.Len(t, descs, 2)
68+
assert.True(t, strings.Contains(descs[0], "sampleMetric1") || strings.Contains(descs[1], "sampleMetric1"))
69+
assert.True(t, strings.Contains(descs[0], "sampleMetric2") || strings.Contains(descs[1], "sampleMetric2"))
70+
}
71+
72+
func TestExporterCollect(t *testing.T) {
73+
exporter, err := NewExporter(&Settings{}, newTestLogger())
74+
require.NoError(t, err)
75+
76+
registry := prometheus.NewRegistry()
77+
require.NoError(t, registry.Register(exporter))
78+
79+
families, err := registry.Gather()
80+
require.NoError(t, err)
81+
require.Len(t, families, 2)
82+
83+
familyByName := make(map[string]*dto.MetricFamily, len(families))
84+
for _, family := range families {
85+
familyByName[family.GetName()] = family
86+
}
87+
88+
sample1 := familyByName["gitlab-ci-exporter_sampleMetric1"]
89+
require.NotNil(t, sample1)
90+
require.Len(t, sample1.GetMetric(), 1)
91+
assert.Equal(t, dto.MetricType_GAUGE, sample1.GetType())
92+
require.Len(t, sample1.GetMetric()[0].GetLabel(), 1)
93+
assert.Equal(t, "label1", sample1.GetMetric()[0].GetLabel()[0].GetName())
94+
assert.Equal(t, "labelValue", sample1.GetMetric()[0].GetLabel()[0].GetValue())
95+
assert.GreaterOrEqual(t, sample1.GetMetric()[0].GetGauge().GetValue(), float64(0))
96+
assert.Less(t, sample1.GetMetric()[0].GetGauge().GetValue(), float64(1))
97+
98+
sample2 := familyByName["gitlab-ci-exporter_sampleMetric2"]
99+
require.NotNil(t, sample2)
100+
require.Len(t, sample2.GetMetric(), 1)
101+
assert.Equal(t, dto.MetricType_GAUGE, sample2.GetType())
102+
require.Len(t, sample2.GetMetric()[0].GetLabel(), 1)
103+
assert.Equal(t, "label2", sample2.GetMetric()[0].GetLabel()[0].GetName())
104+
assert.Equal(t, "labelValue", sample2.GetMetric()[0].GetLabel()[0].GetValue())
105+
assert.GreaterOrEqual(t, sample2.GetMetric()[0].GetGauge().GetValue(), float64(0))
106+
assert.Less(t, sample2.GetMetric()[0].GetGauge().GetValue(), float64(1))
107+
}
108+
109+
func TestSampleMetricsReturnValueBetweenZeroAndOne(t *testing.T) {
110+
exporter, err := NewExporter(&Settings{}, newTestLogger())
111+
require.NoError(t, err)
112+
113+
sample1 := exporter.sampleMetric1()
114+
sample2 := exporter.sampleMetric2()
115+
116+
assert.GreaterOrEqual(t, sample1, float64(0))
117+
assert.Less(t, sample1, float64(1))
118+
assert.GreaterOrEqual(t, sample2, float64(0))
119+
assert.Less(t, sample2, float64(1))
120+
}

internal/httpServer/server_test.go

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
package httpServer
2+
3+
import (
4+
"io"
5+
"log/slog"
6+
"net/http"
7+
"net/http/httptest"
8+
"testing"
9+
10+
"github.com/stretchr/testify/assert"
11+
"github.com/stretchr/testify/require"
12+
13+
"github.com/helvethink/gitlab-ci-exporter/internal/collectors"
14+
)
15+
16+
func newTestExporter(t *testing.T) *collectors.Exporter {
17+
t.Helper()
18+
19+
exporter, err := collectors.NewExporter(&collectors.Settings{
20+
MetricsPath: "metrics",
21+
ListenPort: "9191",
22+
Address: "0.0.0.0",
23+
}, slog.New(slog.NewTextHandler(io.Discard, nil)))
24+
require.NoError(t, err)
25+
26+
return exporter
27+
}
28+
29+
func withFreshServeMux(t *testing.T) {
30+
t.Helper()
31+
32+
previousMux := http.DefaultServeMux
33+
http.DefaultServeMux = http.NewServeMux()
34+
t.Cleanup(func() {
35+
http.DefaultServeMux = previousMux
36+
})
37+
}
38+
39+
func TestNewServerSetsExpectedAddr(t *testing.T) {
40+
withFreshServeMux(t)
41+
42+
server := NewServer(newTestExporter(t))
43+
44+
require.NotNil(t, server)
45+
assert.Equal(t, ":9191", server.Addr)
46+
assert.Nil(t, server.Handler)
47+
}
48+
49+
func TestNewServerServesRootPage(t *testing.T) {
50+
withFreshServeMux(t)
51+
52+
_ = NewServer(newTestExporter(t))
53+
54+
req := httptest.NewRequest(http.MethodGet, "/", nil)
55+
rr := httptest.NewRecorder()
56+
http.DefaultServeMux.ServeHTTP(rr, req)
57+
58+
require.Equal(t, http.StatusOK, rr.Code)
59+
assert.Contains(t, rr.Body.String(), "Gitlab CI Exporter")
60+
assert.Contains(t, rr.Body.String(), "Metrics at:")
61+
assert.Contains(t, rr.Body.String(), "href='metrics'")
62+
assert.Contains(t, rr.Body.String(), "github.com/Helvethink/gitlab-ci-exporter")
63+
}
64+
65+
func TestNewServerServesMetricsEndpoint(t *testing.T) {
66+
withFreshServeMux(t)
67+
68+
_ = NewServer(newTestExporter(t))
69+
70+
req := httptest.NewRequest(http.MethodGet, "/metrics", nil)
71+
rr := httptest.NewRecorder()
72+
http.DefaultServeMux.ServeHTTP(rr, req)
73+
74+
require.Equal(t, http.StatusOK, rr.Code)
75+
assert.Contains(t, rr.Body.String(), "gitlab_ci_exporter_sampleMetric1")
76+
assert.Contains(t, rr.Body.String(), "gitlab_ci_exporter_sampleMetric2")
77+
}

internal/logging/logger_test.go

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
package logger
2+
3+
import (
4+
"os"
5+
"testing"
6+
7+
log "github.com/sirupsen/logrus"
8+
"github.com/stretchr/testify/assert"
9+
"github.com/stretchr/testify/require"
10+
)
11+
12+
func preserveLogrusState(t *testing.T) {
13+
t.Helper()
14+
15+
prevLevel := log.GetLevel()
16+
prevFormatter := log.StandardLogger().Formatter
17+
prevReportCaller := log.StandardLogger().ReportCaller
18+
prevOutput := log.StandardLogger().Out
19+
20+
t.Cleanup(func() {
21+
log.SetLevel(prevLevel)
22+
log.SetFormatter(prevFormatter)
23+
log.SetReportCaller(prevReportCaller)
24+
log.SetOutput(prevOutput)
25+
})
26+
}
27+
28+
func TestConfigureTextFormat(t *testing.T) {
29+
preserveLogrusState(t)
30+
31+
err := Configure(Config{
32+
Level: "debug",
33+
Format: "text",
34+
ReportCaller: true,
35+
})
36+
require.NoError(t, err)
37+
38+
assert.Equal(t, log.DebugLevel, log.GetLevel())
39+
_, ok := log.StandardLogger().Formatter.(*log.TextFormatter)
40+
assert.True(t, ok)
41+
assert.True(t, log.StandardLogger().ReportCaller)
42+
assert.Same(t, os.Stdout, log.StandardLogger().Out)
43+
}
44+
45+
func TestConfigureJSONFormat(t *testing.T) {
46+
preserveLogrusState(t)
47+
48+
err := Configure(Config{
49+
Level: "info",
50+
Format: "json",
51+
ReportCaller: false,
52+
})
53+
require.NoError(t, err)
54+
55+
assert.Equal(t, log.InfoLevel, log.GetLevel())
56+
_, ok := log.StandardLogger().Formatter.(*log.JSONFormatter)
57+
assert.True(t, ok)
58+
assert.False(t, log.StandardLogger().ReportCaller)
59+
assert.Same(t, os.Stdout, log.StandardLogger().Out)
60+
}
61+
62+
func TestConfigureInvalidLevelReturnsError(t *testing.T) {
63+
preserveLogrusState(t)
64+
65+
err := Configure(Config{
66+
Level: "not-a-level",
67+
Format: "text",
68+
})
69+
require.Error(t, err)
70+
}
71+
72+
func TestConfigureInvalidFormatReturnsError(t *testing.T) {
73+
preserveLogrusState(t)
74+
75+
err := Configure(Config{
76+
Level: "info",
77+
Format: "xml",
78+
})
79+
require.Error(t, err)
80+
assert.Contains(t, err.Error(), "invalid log format")
81+
}

0 commit comments

Comments
 (0)