-
Notifications
You must be signed in to change notification settings - Fork 1.2k
test: Implement fakes for prometheus api #1798
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||
---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,174 @@ | ||||||||||
// Copyright 2019 The Prometheus Authors | ||||||||||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||||||||||
// you may not use this file except in compliance with the License. | ||||||||||
// You may obtain a copy of the License at | ||||||||||
// | ||||||||||
// http://www.apache.org/licenses/LICENSE-2.0 | ||||||||||
// | ||||||||||
// Unless required by applicable law or agreed to in writing, software | ||||||||||
// distributed under the License is distributed on an "AS IS" BASIS, | ||||||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||||||
// See the License for the specific language governing permissions and | ||||||||||
// limitations under the License. | ||||||||||
package v1 | ||||||||||
|
||||||||||
import ( | ||||||||||
"context" | ||||||||||
"time" | ||||||||||
|
||||||||||
"github.com/prometheus/common/model" | ||||||||||
) | ||||||||||
|
||||||||||
type FakeAPI struct { | ||||||||||
// FakeAPI is a mock API for testing purposes. | ||||||||||
// It implements the API interface and provides fake data for testing. | ||||||||||
ExpectedAlertsResult []*Alert | ||||||||||
ExpectedAlertsError error | ||||||||||
|
||||||||||
ExpectedAlertManagersResult AlertManagersResult | ||||||||||
ExpectedAlertManagersError error | ||||||||||
|
||||||||||
ExpectedCleanTombstonesError error | ||||||||||
|
||||||||||
ExpectedConfigResult ConfigResult | ||||||||||
ExpectedConfigError error | ||||||||||
|
||||||||||
ExpectedDeleteSeriesError error | ||||||||||
|
||||||||||
ExpectedFlagsResult FlagsResult | ||||||||||
ExpectedFlagsError error | ||||||||||
|
||||||||||
ExpectedLabelNamesResult []string | ||||||||||
ExpectedLabelNamesWarnings Warnings | ||||||||||
ExpectedLabelNamesError error | ||||||||||
Comment on lines
+41
to
+43
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wonder, wouldn't function based approach be much more flexible?
Suggested change
|
||||||||||
|
||||||||||
ExpectedLabelValuesResult model.LabelValues | ||||||||||
ExpectedLabelValuesWarnings Warnings | ||||||||||
ExpectedLabelValuesError error | ||||||||||
|
||||||||||
ExpectedQueryResult model.Value | ||||||||||
ExpectedQueryWarnings Warnings | ||||||||||
ExpectedQueryError error | ||||||||||
|
||||||||||
ExpectedQueryRangeResult model.Value | ||||||||||
ExpectedQueryRangeWarnings Warnings | ||||||||||
ExpectedQueryRangeError error | ||||||||||
|
||||||||||
ExpectedQueryExemplarsResult []ExemplarQueryResult | ||||||||||
ExpectedQueryExemplarsError error | ||||||||||
|
||||||||||
ExpectedBuildinfoResult BuildinfoResult | ||||||||||
ExpectedBuildinfoError error | ||||||||||
|
||||||||||
ExpectedRuntimeinfoResult RuntimeinfoResult | ||||||||||
ExpectedRuntimeinfoError error | ||||||||||
|
||||||||||
ExpectedSeriesResult []model.LabelSet | ||||||||||
ExpectedSeriesWarnings Warnings | ||||||||||
ExpectedSeriesError error | ||||||||||
|
||||||||||
ExpectedSnapshotResult SnapshotResult | ||||||||||
ExpectedSnapshotError error | ||||||||||
|
||||||||||
ExpectedRulesResult RulesResult | ||||||||||
ExpectedRulesError error | ||||||||||
|
||||||||||
ExpectedTargetsResult TargetsResult | ||||||||||
ExpectedTargetsError error | ||||||||||
|
||||||||||
ExpectedTargetsMetadataResult []MetricMetadata | ||||||||||
ExpectedTargetsMetadataError error | ||||||||||
|
||||||||||
ExpectedMetadataResult map[string][]Metadata | ||||||||||
ExpectedMetadataError error | ||||||||||
|
||||||||||
ExpectedTSDBResult TSDBResult | ||||||||||
ExpectedTSDBError error | ||||||||||
|
||||||||||
ExpectedWalReplayResult WalReplayStatus | ||||||||||
ExpectedWalReplayError error | ||||||||||
} | ||||||||||
|
||||||||||
func (f *FakeAPI) Alerts(ctx context.Context) ([]*Alert, error) { | ||||||||||
return f.ExpectedAlertsResult, f.ExpectedAlertsError | ||||||||||
} | ||||||||||
|
||||||||||
func (f *FakeAPI) AlertManagers(ctx context.Context) (AlertManagersResult, error) { | ||||||||||
return f.ExpectedAlertManagersResult, f.ExpectedAlertManagersError | ||||||||||
} | ||||||||||
|
||||||||||
func (f *FakeAPI) CleanTombstones(ctx context.Context) error { | ||||||||||
return f.ExpectedCleanTombstonesError | ||||||||||
} | ||||||||||
|
||||||||||
func (f *FakeAPI) Config(ctx context.Context) (ConfigResult, error) { | ||||||||||
return f.ExpectedConfigResult, f.ExpectedConfigError | ||||||||||
} | ||||||||||
|
||||||||||
func (f *FakeAPI) DeleteSeries(ctx context.Context, matches []string, startTime, endTime time.Time) error { | ||||||||||
return f.ExpectedDeleteSeriesError | ||||||||||
} | ||||||||||
|
||||||||||
func (f *FakeAPI) Flags(ctx context.Context) (FlagsResult, error) { | ||||||||||
return f.ExpectedFlagsResult, f.ExpectedFlagsError | ||||||||||
} | ||||||||||
|
||||||||||
func (f *FakeAPI) LabelNames(ctx context.Context, matches []string, startTime, endTime time.Time, opts ...Option) ([]string, Warnings, error) { | ||||||||||
return f.ExpectedLabelNamesResult, f.ExpectedLabelNamesWarnings, f.ExpectedLabelNamesError | ||||||||||
} | ||||||||||
|
||||||||||
func (f *FakeAPI) LabelValues(ctx context.Context, label string, matches []string, startTime, endTime time.Time, opts ...Option) (model.LabelValues, Warnings, error) { | ||||||||||
return f.ExpectedLabelValuesResult, f.ExpectedLabelValuesWarnings, f.ExpectedLabelValuesError | ||||||||||
} | ||||||||||
|
||||||||||
func (f *FakeAPI) Query(ctx context.Context, query string, ts time.Time, opts ...Option) (model.Value, Warnings, error) { | ||||||||||
return f.ExpectedQueryResult, f.ExpectedQueryWarnings, f.ExpectedQueryError | ||||||||||
} | ||||||||||
|
||||||||||
func (f *FakeAPI) QueryRange(ctx context.Context, query string, r Range, opts ...Option) (model.Value, Warnings, error) { | ||||||||||
return f.ExpectedQueryRangeResult, f.ExpectedQueryRangeWarnings, f.ExpectedQueryRangeError | ||||||||||
} | ||||||||||
|
||||||||||
func (f *FakeAPI) QueryExemplars(ctx context.Context, query string, startTime, endTime time.Time) ([]ExemplarQueryResult, error) { | ||||||||||
return f.ExpectedQueryExemplarsResult, f.ExpectedQueryExemplarsError | ||||||||||
} | ||||||||||
|
||||||||||
func (f *FakeAPI) Buildinfo(ctx context.Context) (BuildinfoResult, error) { | ||||||||||
return f.ExpectedBuildinfoResult, f.ExpectedBuildinfoError | ||||||||||
} | ||||||||||
|
||||||||||
func (f *FakeAPI) Runtimeinfo(ctx context.Context) (RuntimeinfoResult, error) { | ||||||||||
return f.ExpectedRuntimeinfoResult, f.ExpectedRuntimeinfoError | ||||||||||
} | ||||||||||
|
||||||||||
func (f *FakeAPI) Series(ctx context.Context, matches []string, startTime, endTime time.Time, opts ...Option) ([]model.LabelSet, Warnings, error) { | ||||||||||
return f.ExpectedSeriesResult, f.ExpectedSeriesWarnings, f.ExpectedSeriesError | ||||||||||
} | ||||||||||
|
||||||||||
func (f *FakeAPI) Snapshot(ctx context.Context, skipHead bool) (SnapshotResult, error) { | ||||||||||
return f.ExpectedSnapshotResult, f.ExpectedSnapshotError | ||||||||||
} | ||||||||||
|
||||||||||
func (f *FakeAPI) Rules(ctx context.Context) (RulesResult, error) { | ||||||||||
return f.ExpectedRulesResult, f.ExpectedRulesError | ||||||||||
} | ||||||||||
|
||||||||||
func (f *FakeAPI) Targets(ctx context.Context) (TargetsResult, error) { | ||||||||||
return f.ExpectedTargetsResult, f.ExpectedTargetsError | ||||||||||
} | ||||||||||
|
||||||||||
func (f *FakeAPI) TargetsMetadata(ctx context.Context, matchTarget, metric, limit string) ([]MetricMetadata, error) { | ||||||||||
return f.ExpectedTargetsMetadataResult, f.ExpectedTargetsMetadataError | ||||||||||
} | ||||||||||
|
||||||||||
func (f *FakeAPI) Metadata(ctx context.Context, metric, limit string) (map[string][]Metadata, error) { | ||||||||||
return f.ExpectedMetadataResult, f.ExpectedMetadataError | ||||||||||
} | ||||||||||
|
||||||||||
func (f *FakeAPI) TSDB(ctx context.Context, opts ...Option) (TSDBResult, error) { | ||||||||||
return f.ExpectedTSDBResult, f.ExpectedTSDBError | ||||||||||
} | ||||||||||
|
||||||||||
func (f *FakeAPI) WalReplay(ctx context.Context) (WalReplayStatus, error) { | ||||||||||
return f.ExpectedWalReplayResult, f.ExpectedWalReplayError | ||||||||||
} |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,114 @@ | ||||||
// Copyright 2019 The Prometheus Authors | ||||||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
// you may not use this file except in compliance with the License. | ||||||
// You may obtain a copy of the License at | ||||||
// | ||||||
// http://www.apache.org/licenses/LICENSE-2.0 | ||||||
// | ||||||
// Unless required by applicable law or agreed to in writing, software | ||||||
// distributed under the License is distributed on an "AS IS" BASIS, | ||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
// See the License for the specific language governing permissions and | ||||||
// limitations under the License. | ||||||
package v1 | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If we do this, let's show/test how it can be used for importers.
Suggested change
|
||||||
|
||||||
import ( | ||||||
"context" | ||||||
"errors" | ||||||
"reflect" | ||||||
"testing" | ||||||
"time" | ||||||
|
||||||
"github.com/prometheus/common/model" | ||||||
) | ||||||
|
||||||
func assertEqual(t *testing.T, a, b interface{}) { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. cmp.Diff can be used (from github.com/google/go-cmp) |
||||||
if !reflect.DeepEqual(a, b) { | ||||||
t.Errorf("%v != %v", a, b) | ||||||
} | ||||||
} | ||||||
|
||||||
func TestFakeAPI_Query(t *testing.T) { | ||||||
tests := []struct { | ||||||
name string | ||||||
query string | ||||||
expectedResult model.Value | ||||||
expectedWarnings Warnings | ||||||
expectedError error | ||||||
}{ | ||||||
{ | ||||||
name: "Valid query", | ||||||
query: "up == 1", | ||||||
expectedResult: &model.String{Value: "1"}, | ||||||
}, | ||||||
{ | ||||||
name: "Query with no results, warning present", | ||||||
query: "up == 0", | ||||||
expectedResult: nil, | ||||||
expectedWarnings: Warnings{"Warning: No data found for query, check if the time range is correct"}, | ||||||
expectedError: nil, | ||||||
}, | ||||||
{ | ||||||
name: "Error query", | ||||||
query: "invalid_query", | ||||||
expectedError: errors.New("mock error"), | ||||||
}, | ||||||
} | ||||||
|
||||||
for _, tt := range tests { | ||||||
t.Run(tt.name, func(t *testing.T) { | ||||||
// Setup FakeAPI | ||||||
fakeAPI := &FakeAPI{ | ||||||
ExpectedQueryResult: tt.expectedResult, | ||||||
ExpectedQueryWarnings: tt.expectedWarnings, | ||||||
ExpectedQueryError: tt.expectedError, | ||||||
} | ||||||
|
||||||
result, warnings, err := fakeAPI.Query(context.Background(), tt.query, time.Now()) | ||||||
assertEqual(t, tt.expectedResult, result) | ||||||
assertEqual(t, tt.expectedWarnings, warnings) | ||||||
assertEqual(t, tt.expectedError, err) | ||||||
}) | ||||||
} | ||||||
} | ||||||
|
||||||
func TestFakeAPI_LabelNames(t *testing.T) { | ||||||
tests := []struct { | ||||||
name string | ||||||
matches []string | ||||||
expectedLabels []string | ||||||
expectedWarnings Warnings | ||||||
expectedError error | ||||||
}{ | ||||||
{ | ||||||
name: "Valid label names", | ||||||
matches: []string{"up"}, | ||||||
expectedLabels: []string{"label1", "label2"}, | ||||||
expectedWarnings: nil, | ||||||
expectedError: nil, | ||||||
}, | ||||||
{ | ||||||
name: "Error in label names", | ||||||
matches: []string{"error"}, | ||||||
expectedLabels: nil, | ||||||
expectedWarnings: nil, | ||||||
expectedError: errors.New("mock label error"), | ||||||
}, | ||||||
} | ||||||
|
||||||
for _, tt := range tests { | ||||||
t.Run(tt.name, func(t *testing.T) { | ||||||
// Setup FakeAPI | ||||||
fakeAPI := &FakeAPI{ | ||||||
ExpectedLabelNamesResult: tt.expectedLabels, | ||||||
ExpectedLabelNamesWarnings: tt.expectedWarnings, | ||||||
ExpectedLabelNamesError: tt.expectedError, | ||||||
} | ||||||
Comment on lines
+102
to
+106
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just to highlight what's currently possible. Instead of the referenced example one could write a following code // Somewhere before the test
type myFakeAPI struct {
v1.API
labelNamesFn(ctx context.Context, matches []string, startTime, endTime time.Time, opts ...Option) ([]string, Warnings, error)
}
func (f *myFakeAPI) LabelNames(ctx context.Context, matches []string, startTime, endTime time.Time, opts ...Option) ([]string, Warnings, error) {
return f.labelNamesFn()
}
// No more methods needs to implemented, if the test uses only LabelNames....
// In your test...
fakeAPI := &myFakeAPI{
labelNamesFn: func(ctx context.Context, matches []string, startTime, endTime time.Time, opts ...Option) ([]string, Warnings, error) { return tt.expectedLabels,tt.expectedWarnings, tt.expectedError}
} Then the question is... is it worth to host fake impl if it's as simple as this? |
||||||
|
||||||
result, warnings, err := fakeAPI.LabelNames(context.Background(), tt.matches, time.Now(), time.Now()) | ||||||
assertEqual(t, tt.expectedLabels, result) | ||||||
assertEqual(t, tt.expectedWarnings, warnings) | ||||||
assertEqual(t, tt.expectedError, err) | ||||||
}) | ||||||
} | ||||||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we follow this path we absolutely need to ensure this struct is not disconnected from API interface.