//go:build unit
package tools
import (
"context"
"encoding/json"
"net/http"
"net/http/httptest"
"net/url"
"testing"
"github.com/grafana/grafana-openapi-client-go/client"
"github.com/grafana/grafana-openapi-client-go/models"
mcpgrafana "github.com/grafana/mcp-grafana"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func mockDatasourcesCtx(server *httptest.Server) context.Context {
u, _ := url.Parse(server.URL)
cfg := client.DefaultTransportConfig()
cfg.Host = u.Host
cfg.Schemes = []string{"http"}
cfg.APIKey = "test"
c := client.NewHTTPClientWithConfig(nil, cfg)
return mcpgrafana.WithGrafanaClient(context.Background(), c)
}
func createMockDatasources(count int) []*models.DataSource {
datasources := make([]*models.DataSource, count)
for i := 0; i < count; i++ {
datasources[i] = &models.DataSource{
ID: int64(i + 1),
UID: "ds-" + string(rune('a'+i)),
Name: "Datasource " + string(rune('A'+i)),
Type: "prometheus",
IsDefault: i == 0,
}
}
return datasources
}
func TestListDatasources_Pagination(t *testing.T) {
// Create 10 mock datasources
mockDS := createMockDatasources(10)
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "/api/datasources", r.URL.Path)
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
_ = json.NewEncoder(w).Encode(mockDS)
}))
defer server.Close()
ctx := mockDatasourcesCtx(server)
t.Run("default pagination returns first 50 (all 10)", func(t *testing.T) {
result, err := listDatasources(ctx, ListDatasourcesParams{})
require.NoError(t, err)
assert.Len(t, result.Datasources, 10)
assert.Equal(t, 10, result.Total)
assert.False(t, result.HasMore)
})
t.Run("limit restricts results", func(t *testing.T) {
result, err := listDatasources(ctx, ListDatasourcesParams{Limit: 3})
require.NoError(t, err)
assert.Len(t, result.Datasources, 3)
assert.Equal(t, 10, result.Total)
assert.True(t, result.HasMore)
})
t.Run("offset skips results", func(t *testing.T) {
result, err := listDatasources(ctx, ListDatasourcesParams{Limit: 3, Offset: 2})
require.NoError(t, err)
assert.Len(t, result.Datasources, 3)
assert.Equal(t, 10, result.Total)
assert.True(t, result.HasMore)
assert.Equal(t, "ds-c", result.Datasources[0].UID)
})
t.Run("offset beyond total returns empty", func(t *testing.T) {
result, err := listDatasources(ctx, ListDatasourcesParams{Offset: 20})
require.NoError(t, err)
assert.Len(t, result.Datasources, 0)
assert.Equal(t, 10, result.Total)
assert.False(t, result.HasMore)
})
t.Run("limit capped at 100", func(t *testing.T) {
result, err := listDatasources(ctx, ListDatasourcesParams{Limit: 200})
require.NoError(t, err)
// Since we only have 10 datasources, we get all 10
assert.Len(t, result.Datasources, 10)
assert.Equal(t, 10, result.Total)
assert.False(t, result.HasMore)
})
t.Run("last page has hasMore=false", func(t *testing.T) {
result, err := listDatasources(ctx, ListDatasourcesParams{Limit: 3, Offset: 9})
require.NoError(t, err)
assert.Len(t, result.Datasources, 1)
assert.Equal(t, 10, result.Total)
assert.False(t, result.HasMore)
})
}
func TestListDatasources_TypeFilter(t *testing.T) {
// Create mixed type datasources
mockDS := []*models.DataSource{
{ID: 1, UID: "prom-1", Name: "Prometheus 1", Type: "prometheus"},
{ID: 2, UID: "loki-1", Name: "Loki 1", Type: "loki"},
{ID: 3, UID: "prom-2", Name: "Prometheus 2", Type: "prometheus"},
{ID: 4, UID: "tempo-1", Name: "Tempo 1", Type: "tempo"},
}
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
_ = json.NewEncoder(w).Encode(mockDS)
}))
defer server.Close()
ctx := mockDatasourcesCtx(server)
t.Run("filter by type with pagination", func(t *testing.T) {
result, err := listDatasources(ctx, ListDatasourcesParams{Type: "prometheus", Limit: 1})
require.NoError(t, err)
assert.Len(t, result.Datasources, 1)
assert.Equal(t, 2, result.Total) // 2 prometheus datasources total
assert.True(t, result.HasMore)
assert.Equal(t, "prom-1", result.Datasources[0].UID)
})
t.Run("filter by type second page", func(t *testing.T) {
result, err := listDatasources(ctx, ListDatasourcesParams{Type: "prometheus", Limit: 1, Offset: 1})
require.NoError(t, err)
assert.Len(t, result.Datasources, 1)
assert.Equal(t, 2, result.Total)
assert.False(t, result.HasMore)
assert.Equal(t, "prom-2", result.Datasources[0].UID)
})
}