Skip to content

Commit d2b61b7

Browse files
committed
add unit tests for prometheus
1 parent 7e27cca commit d2b61b7

File tree

2 files changed

+320
-9
lines changed

2 files changed

+320
-9
lines changed

wasp/benchspy/prometheus.go

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import (
1717
type PrometheusQueryExecutor struct {
1818
KindName string `json:"kind"`
1919
startTime, endTime time.Time `json:"-"`
20-
client *client.Prometheus `json:"-"`
20+
client v1.API `json:"-"`
2121
Queries map[string]string `json:"queries"`
2222
QueryResults map[string]interface{} `json:"query_results"`
2323
warnings map[string]v1.Warnings `json:"-"`
@@ -46,18 +46,21 @@ func NewStandardPrometheusQueryExecutor(url string, startTime, endTime time.Time
4646
return nil, errors.Wrapf(err, "failed to create Prometheus client")
4747
}
4848

49-
standardQueries, queryErr := (&PrometheusQueryExecutor{client: c}).generateStandardQueries(nameRegexPattern, startTime, endTime)
50-
if queryErr != nil {
51-
return nil, errors.Wrapf(queryErr, "failed to generate standard queries for %s", nameRegexPattern)
52-
}
53-
54-
return &PrometheusQueryExecutor{
49+
pr := &PrometheusQueryExecutor{
5550
client: c,
56-
Queries: standardQueries,
5751
startTime: startTime,
5852
endTime: endTime,
5953
QueryResults: make(map[string]interface{}),
60-
}, nil
54+
}
55+
56+
standardQueries, queryErr := pr.generateStandardQueries(nameRegexPattern, startTime, endTime)
57+
if queryErr != nil {
58+
return nil, errors.Wrapf(queryErr, "failed to generate standard queries for %s", nameRegexPattern)
59+
}
60+
61+
pr.Queries = standardQueries
62+
63+
return pr, nil
6164
}
6265

6366
func (r *PrometheusQueryExecutor) Execute(ctx context.Context) error {

wasp/benchspy/prometheus_test.go

Lines changed: 308 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,308 @@
1+
package benchspy
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"testing"
7+
"time"
8+
9+
v1 "github.com/prometheus/client_golang/api/prometheus/v1"
10+
"github.com/prometheus/common/model"
11+
"github.com/stretchr/testify/assert"
12+
"github.com/stretchr/testify/require"
13+
)
14+
15+
func TestNewPrometheusQueryExecutor(t *testing.T) {
16+
t.Run("successful creation", func(t *testing.T) {
17+
startTime := time.Now().Add(-1 * time.Hour)
18+
endTime := time.Now()
19+
queries := map[string]string{"test": "query"}
20+
21+
executor, err := NewPrometheusQueryExecutor("http://localhost:9090", startTime, endTime, queries)
22+
23+
require.NoError(t, err)
24+
assert.NotNil(t, executor)
25+
assert.Equal(t, queries, executor.Queries)
26+
assert.Equal(t, startTime, executor.startTime)
27+
assert.Equal(t, endTime, executor.endTime)
28+
})
29+
}
30+
31+
func TestNewStandardPrometheusQueryExecutor(t *testing.T) {
32+
t.Run("successful creation", func(t *testing.T) {
33+
startTime := time.Now().Add(-1 * time.Hour)
34+
endTime := time.Now()
35+
36+
executor, err := NewStandardPrometheusQueryExecutor("http://localhost:9090", startTime, endTime, "test.*")
37+
38+
require.NoError(t, err)
39+
assert.NotNil(t, executor)
40+
assert.NotEmpty(t, executor.Queries)
41+
assert.Equal(t, 4, len(executor.Queries))
42+
43+
queries := []string{}
44+
for name := range executor.Queries {
45+
queries = append(queries, name)
46+
}
47+
48+
assert.Contains(t, queries, string(MedianCPUUsage))
49+
assert.Contains(t, queries, string(MedianMemUsage))
50+
assert.Contains(t, queries, string(P95MemUsage))
51+
assert.Contains(t, queries, string(P95CPUUsage))
52+
})
53+
}
54+
55+
func TestPrometheusQueryExecutor_Execute(t *testing.T) {
56+
t.Run("successful execution", func(t *testing.T) {
57+
// Create test data
58+
expectedQuery := "rate(test_metric[5m])"
59+
expectedTime := time.Now()
60+
expectedValue := &model.Vector{
61+
&model.Sample{
62+
Timestamp: model.Time(expectedTime.Unix()),
63+
Value: 42,
64+
},
65+
}
66+
67+
// Create mock client with custom query behavior
68+
mockClient := &mockPrometheusClient{
69+
queryFn: func(ctx context.Context, query string, ts time.Time, opts ...v1.Option) (model.Value, v1.Warnings, error) {
70+
assert.Equal(t, expectedQuery, query)
71+
return expectedValue, nil, nil
72+
},
73+
}
74+
75+
executor := &PrometheusQueryExecutor{
76+
client: mockClient,
77+
Queries: map[string]string{"test_metric": expectedQuery},
78+
warnings: make(map[string]v1.Warnings),
79+
QueryResults: make(map[string]interface{}),
80+
startTime: expectedTime.Add(-1 * time.Hour),
81+
endTime: expectedTime,
82+
}
83+
84+
err := executor.Execute(context.Background())
85+
86+
require.NoError(t, err)
87+
assert.NotEmpty(t, executor.QueryResults)
88+
assert.Contains(t, executor.QueryResults, "test_metric")
89+
90+
// Verify the stored result
91+
result, ok := executor.QueryResults["test_metric"]
92+
require.True(t, ok)
93+
assert.Equal(t, expectedValue, result)
94+
})
95+
96+
t.Run("handles query error", func(t *testing.T) {
97+
mockClient := &mockPrometheusClient{
98+
queryFn: func(ctx context.Context, query string, ts time.Time, opts ...v1.Option) (model.Value, v1.Warnings, error) {
99+
return nil, nil, fmt.Errorf("query failed")
100+
},
101+
}
102+
103+
executor := &PrometheusQueryExecutor{
104+
client: mockClient,
105+
Queries: map[string]string{"test_metric": "invalid_query"},
106+
warnings: make(map[string]v1.Warnings),
107+
QueryResults: make(map[string]interface{}),
108+
startTime: time.Now().Add(-1 * time.Hour),
109+
endTime: time.Now(),
110+
}
111+
112+
err := executor.Execute(context.Background())
113+
require.Error(t, err)
114+
assert.Empty(t, executor.QueryResults)
115+
})
116+
}
117+
118+
func TestPrometheusQueryExecutor_Validate(t *testing.T) {
119+
t.Run("valid configuration", func(t *testing.T) {
120+
executor := &PrometheusQueryExecutor{
121+
client: &mockPrometheusClient{},
122+
Queries: map[string]string{"test": "query"},
123+
startTime: time.Now(),
124+
endTime: time.Now(),
125+
}
126+
127+
err := executor.Validate()
128+
129+
require.NoError(t, err)
130+
})
131+
132+
t.Run("missing client", func(t *testing.T) {
133+
executor := &PrometheusQueryExecutor{
134+
Queries: map[string]string{"test": "query"},
135+
startTime: time.Now(),
136+
endTime: time.Now(),
137+
}
138+
139+
err := executor.Validate()
140+
141+
require.Error(t, err)
142+
assert.Contains(t, err.Error(), "client is nil")
143+
})
144+
145+
t.Run("empty queries", func(t *testing.T) {
146+
executor := &PrometheusQueryExecutor{
147+
client: &mockPrometheusClient{},
148+
startTime: time.Now(),
149+
endTime: time.Now(),
150+
}
151+
152+
err := executor.Validate()
153+
154+
require.Error(t, err)
155+
assert.Contains(t, err.Error(), "no queries provided")
156+
})
157+
}
158+
159+
func TestPrometheusQueryExecutor_IsComparable(t *testing.T) {
160+
t.Run("same queries", func(t *testing.T) {
161+
queries := map[string]string{"test": "query"}
162+
executor1 := &PrometheusQueryExecutor{Queries: queries}
163+
executor2 := &PrometheusQueryExecutor{Queries: queries}
164+
165+
err := executor1.IsComparable(executor2)
166+
167+
require.NoError(t, err)
168+
})
169+
170+
t.Run("different types", func(t *testing.T) {
171+
executor := &PrometheusQueryExecutor{}
172+
other := &mockQueryExecutor{
173+
kindName: "mock",
174+
queries: make(map[string]string),
175+
results: make(map[string]interface{}),
176+
}
177+
178+
err := executor.IsComparable(other)
179+
180+
require.Error(t, err)
181+
assert.Contains(t, err.Error(), "expected type")
182+
})
183+
}
184+
185+
type mockQueryExecutor struct {
186+
kindName string
187+
queries map[string]string
188+
results map[string]interface{}
189+
}
190+
191+
func (m *mockQueryExecutor) Execute(ctx context.Context) error {
192+
return nil
193+
}
194+
195+
func (r *mockQueryExecutor) IsComparable(other QueryExecutor) error {
196+
return nil
197+
}
198+
199+
func (m *mockQueryExecutor) Validate() error {
200+
return nil
201+
}
202+
203+
func (m *mockQueryExecutor) Kind() string {
204+
return m.kindName
205+
}
206+
207+
func (r *mockQueryExecutor) TimeRange(startTime, endTime time.Time) {
208+
return
209+
}
210+
211+
func (m *mockQueryExecutor) Results() map[string]interface{} {
212+
return m.results
213+
}
214+
215+
// Mock Prometheus client for testing
216+
type mockPrometheusClient struct {
217+
// Add test control fields
218+
queryFn func(ctx context.Context, query string, ts time.Time, opts ...v1.Option) (model.Value, v1.Warnings, error)
219+
}
220+
221+
// Default query implementation
222+
func (m *mockPrometheusClient) Query(ctx context.Context, query string, ts time.Time, opts ...v1.Option) (model.Value, v1.Warnings, error) {
223+
if m.queryFn != nil {
224+
return m.queryFn(ctx, query, ts, opts...)
225+
}
226+
return &model.Vector{}, v1.Warnings{}, nil
227+
}
228+
229+
// Minimal implementations for remaining interface methods
230+
func (m *mockPrometheusClient) Alerts(ctx context.Context) (v1.AlertsResult, error) {
231+
return v1.AlertsResult{}, nil
232+
}
233+
234+
func (m *mockPrometheusClient) AlertManagers(ctx context.Context) (v1.AlertManagersResult, error) {
235+
return v1.AlertManagersResult{}, nil
236+
}
237+
238+
func (m *mockPrometheusClient) CleanTombstones(ctx context.Context) error {
239+
return nil
240+
}
241+
242+
func (m *mockPrometheusClient) Config(ctx context.Context) (v1.ConfigResult, error) {
243+
return v1.ConfigResult{}, nil
244+
}
245+
246+
func (m *mockPrometheusClient) DeleteSeries(ctx context.Context, matches []string, startTime, endTime time.Time) error {
247+
return nil
248+
}
249+
250+
func (m *mockPrometheusClient) Flags(ctx context.Context) (v1.FlagsResult, error) {
251+
return v1.FlagsResult{}, nil
252+
}
253+
254+
func (m *mockPrometheusClient) LabelNames(ctx context.Context, matches []string, startTime, endTime time.Time, opts ...v1.Option) ([]string, v1.Warnings, error) {
255+
return []string{}, v1.Warnings{}, nil
256+
}
257+
258+
func (m *mockPrometheusClient) LabelValues(ctx context.Context, label string, matches []string, startTime, endTime time.Time, opts ...v1.Option) (model.LabelValues, v1.Warnings, error) {
259+
return model.LabelValues{}, v1.Warnings{}, nil
260+
}
261+
262+
func (m *mockPrometheusClient) QueryRange(ctx context.Context, query string, r v1.Range, opts ...v1.Option) (model.Value, v1.Warnings, error) {
263+
return &model.Vector{}, v1.Warnings{}, nil
264+
}
265+
266+
func (m *mockPrometheusClient) QueryExemplars(ctx context.Context, query string, startTime, endTime time.Time) ([]v1.ExemplarQueryResult, error) {
267+
return []v1.ExemplarQueryResult{}, nil
268+
}
269+
270+
func (m *mockPrometheusClient) Buildinfo(ctx context.Context) (v1.BuildinfoResult, error) {
271+
return v1.BuildinfoResult{}, nil
272+
}
273+
274+
func (m *mockPrometheusClient) Runtimeinfo(ctx context.Context) (v1.RuntimeinfoResult, error) {
275+
return v1.RuntimeinfoResult{}, nil
276+
}
277+
278+
func (m *mockPrometheusClient) Series(ctx context.Context, matches []string, startTime, endTime time.Time, opts ...v1.Option) ([]model.LabelSet, v1.Warnings, error) {
279+
return []model.LabelSet{}, v1.Warnings{}, nil
280+
}
281+
282+
func (m *mockPrometheusClient) Snapshot(ctx context.Context, skipHead bool) (v1.SnapshotResult, error) {
283+
return v1.SnapshotResult{}, nil
284+
}
285+
286+
func (m *mockPrometheusClient) Rules(ctx context.Context) (v1.RulesResult, error) {
287+
return v1.RulesResult{}, nil
288+
}
289+
290+
func (m *mockPrometheusClient) Targets(ctx context.Context) (v1.TargetsResult, error) {
291+
return v1.TargetsResult{}, nil
292+
}
293+
294+
func (m *mockPrometheusClient) TargetsMetadata(ctx context.Context, matchTarget, metric, limit string) ([]v1.MetricMetadata, error) {
295+
return []v1.MetricMetadata{}, nil
296+
}
297+
298+
func (m *mockPrometheusClient) Metadata(ctx context.Context, metric, limit string) (map[string][]v1.Metadata, error) {
299+
return map[string][]v1.Metadata{}, nil
300+
}
301+
302+
func (m *mockPrometheusClient) TSDB(ctx context.Context, opts ...v1.Option) (v1.TSDBResult, error) {
303+
return v1.TSDBResult{}, nil
304+
}
305+
306+
func (m *mockPrometheusClient) WalReplay(ctx context.Context) (v1.WalReplayStatus, error) {
307+
return v1.WalReplayStatus{}, nil
308+
}

0 commit comments

Comments
 (0)