Skip to content

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

Open
wants to merge 3 commits into
base: main
Choose a base branch
from

Conversation

ying-jeanne
Copy link
Contributor

@ying-jeanne ying-jeanne commented Apr 15, 2025

Fixes: Prometheus GitHub issue #1438

This PR addresses Prometheus GitHub issue #1438 and the related comment by @bwplotka here. It introduces a simple fake implementation of the Prometheus API to facilitate unit testing. This fake API simulates the behavior of the actual Prometheus API, enabling unit tests without the need for a live Prometheus server. By combining this with end-to-end (e2e) tests, users will be able to cover all test cases comprehensively.

As an example of a similar approach, see this Grafana project.

For simplicity, the fake implementation is placed within the same package to avoid any import complexity for now.

Signed-off-by: Ying WANG <[email protected]>
Signed-off-by: Ying WANG <[email protected]>
@ying-jeanne
Copy link
Contributor Author

@ArthurSens @bwplotka @kakkoyun @vesari could you please give this PR a look? thank you.

"github.com/prometheus/common/model"
)

func assertEqual(t *testing.T, a, b interface{}) {
Copy link
Member

@bwplotka bwplotka Apr 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cmp.Diff can be used (from github.com/google/go-cmp)

Copy link
Member

@bwplotka bwplotka left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a great start, thanks! I am not against this, but would love to explore alternatives before commit to this in our stable module.

  1. Should we decide what to do with API module first: proposal: Move API code to a separate module/repo #1649
  2. Should we auto-generate this instead of manual code.
  3. Should we explore closure/funcs approach. Currently with fake return params approach, tomorrow someone will complain that they want a specific result for a specific parameter or ensure some parameter invariance. Perhaps function based approach (See suggestion) might work better?

Also concerns from #1438 (comment) still apply.

"github.com/prometheus/common/model"
)

type FakeAPI struct {
Copy link
Member

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.

Suggested change
type FakeAPI struct {
var _ API = &FakeAPI{}
type FakeAPI struct {

// 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
Copy link
Member

Choose a reason for hiding this comment

The 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
package v1
package v1_test

Comment on lines +102 to +106
fakeAPI := &FakeAPI{
ExpectedLabelNamesResult: tt.expectedLabels,
ExpectedLabelNamesWarnings: tt.expectedWarnings,
ExpectedLabelNamesError: tt.expectedError,
}
Copy link
Member

Choose a reason for hiding this comment

The 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?

Comment on lines +41 to +43
ExpectedLabelNamesResult []string
ExpectedLabelNamesWarnings Warnings
ExpectedLabelNamesError error
Copy link
Member

Choose a reason for hiding this comment

The 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
ExpectedLabelNamesResult []string
ExpectedLabelNamesWarnings Warnings
ExpectedLabelNamesError error
LabelNamesFn(ctx context.Context, matches []string, startTime, endTime time.Time, opts ...Option) ([]string, Warnings, error)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

API Mocks
2 participants