Files
grafana/pkg/storage/unified/testing/search_and_storage.go
Arati R. 0982cfd9a0 Unified Storage/Search: Add max count config for indexing (#107255)
* Add max count config for indexing
* Build empty index when max count is exceeded
* Address linting
* Refactor buildIndexes
* Add test for max count threshold
* Update test doc comments
* Refactor TestBuildIndexes_MaxCountThreshold to not use mock framework
* Rename mocks used in TestBuildIndexes_MaxCountThreshold

* Refactor mockResourceIndex

* Test setting of indexing threshold configs

* Tweak comments, log

* Fix logging in buildEmptyIndex

* Export and reuse TestDocumentBuilderSupplier

* Reuse MockResourceIndex
2025-06-27 14:00:39 +02:00

255 lines
6.2 KiB
Go

package test
import (
"context"
"testing"
"github.com/google/uuid"
"github.com/stretchr/testify/require"
claims "github.com/grafana/authlib/types"
"github.com/grafana/grafana/pkg/apimachinery/identity"
"github.com/grafana/grafana/pkg/apimachinery/utils"
"github.com/grafana/grafana/pkg/storage/unified/resource"
"github.com/grafana/grafana/pkg/storage/unified/resourcepb"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
)
func RunTestSearchAndStorage(t *testing.T, ctx context.Context, backend resource.StorageBackend, searchBackend resource.SearchBackend) {
// Create a test user with admin permissions
testUser := &identity.StaticRequester{
Type: claims.TypeUser,
Login: "testuser",
UserID: 123,
UserUID: "u123",
OrgRole: identity.RoleAdmin,
IsGrafanaAdmin: true,
}
ctx = claims.WithAuthInfo(ctx, testUser)
nsPrefix := "test-ns"
var server resource.ResourceServer
t.Run("Create initial resources in storage", func(t *testing.T) {
initialResources := []struct {
name string
title string
tags []string
}{
{
name: "initial1",
title: "First Initial Document",
tags: []string{"tag0", "initial"},
},
{
name: "initial2",
title: "Second Initial Document",
tags: []string{"tag0", "initial"},
},
}
for _, doc := range initialResources {
key := &resourcepb.ResourceKey{
Group: "test.grafana.app",
Resource: "testresources",
Namespace: nsPrefix,
Name: doc.name,
}
// Create document using unstructured
obj := &unstructured.Unstructured{
Object: map[string]interface{}{
"apiVersion": "test.grafana.app/v1",
"kind": "testresources",
"metadata": map[string]interface{}{
"name": doc.name,
"namespace": nsPrefix,
},
"spec": map[string]interface{}{
"title": doc.title,
"tags": doc.tags,
},
},
}
meta, err := utils.MetaAccessor(obj)
require.NoError(t, err)
// Convert unstructured to bytes
value, err := obj.MarshalJSON()
require.NoError(t, err)
// Create document
rv, err := backend.WriteEvent(ctx, resource.WriteEvent{
Type: resourcepb.WatchEvent_ADDED,
Key: key,
Value: value,
Object: meta,
GUID: uuid.New().String(),
})
require.NoError(t, err)
require.Greater(t, rv, int64(0))
}
})
ch := make(chan *resource.IndexEvent)
t.Run("Create a resource server with both backends", func(t *testing.T) {
// Create a resource server with both backends
var err error
server, err = resource.NewResourceServer(resource.ResourceServerOptions{
Backend: backend,
Search: resource.SearchOptions{
Backend: searchBackend,
Resources: &resource.TestDocumentBuilderSupplier{
GroupsResources: map[string]string{
"test.grafana.app": "testresources",
},
},
IndexEventsChan: ch,
},
})
require.NoError(t, err)
})
t.Run("Search for initial resources", func(t *testing.T) {
// Test 1: Search for initial resources
searchResp, err := server.Search(ctx, &resourcepb.ResourceSearchRequest{
Options: &resourcepb.ListOptions{
Key: &resourcepb.ResourceKey{
Group: "test.grafana.app",
Resource: "testresources",
Namespace: nsPrefix,
},
},
Query: "initial",
Limit: 10,
})
require.NoError(t, err)
require.NotNil(t, searchResp)
require.Nil(t, searchResp.Error)
require.Equal(t, int64(2), searchResp.TotalHits)
})
t.Run("Add search documents", func(t *testing.T) {
testDocs := []struct {
name string
title string
tags []string
}{
{
name: "doc1",
title: "First Test Document",
tags: []string{"hello", "tag1"},
},
{
name: "doc2",
title: "Second Test Document",
tags: []string{"hello", "tag2"},
},
{
name: "doc3",
title: "Third Test Document",
tags: []string{"hello", "tag3"},
},
}
for _, doc := range testDocs {
key := &resourcepb.ResourceKey{
Group: "test.grafana.app",
Resource: "testresources",
Namespace: nsPrefix,
Name: doc.name,
}
// Create document using unstructured
obj := &unstructured.Unstructured{
Object: map[string]interface{}{
"apiVersion": "test.grafana.app/v1",
"kind": "testresources",
"metadata": map[string]interface{}{
"name": doc.name,
"namespace": nsPrefix,
},
"spec": map[string]interface{}{
"title": doc.title,
"tags": doc.tags,
},
},
}
// Convert unstructured to bytes
value, err := obj.MarshalJSON()
require.NoError(t, err)
// Create document
createResp, err := server.Create(ctx, &resourcepb.CreateRequest{
Key: key,
Value: value,
})
require.NoError(t, err)
require.NotNil(t, createResp)
require.Nil(t, createResp.Error)
ev := <-ch
require.NotNil(t, ev)
}
})
t.Run("Search for documents", func(t *testing.T) {
searchResp, err := server.Search(ctx, &resourcepb.ResourceSearchRequest{
Options: &resourcepb.ListOptions{
Key: &resourcepb.ResourceKey{
Group: "test.grafana.app",
Resource: "testresources",
Namespace: nsPrefix,
},
},
Query: "Document",
Limit: 10,
})
require.NoError(t, err)
require.NotNil(t, searchResp)
require.Nil(t, searchResp.Error)
require.Equal(t, int64(5), searchResp.TotalHits)
})
t.Run("Search with tags", func(t *testing.T) {
searchResp, err := server.Search(ctx, &resourcepb.ResourceSearchRequest{
Options: &resourcepb.ListOptions{
Key: &resourcepb.ResourceKey{
Group: "test.grafana.app",
Resource: "testresources",
Namespace: nsPrefix,
},
},
Query: "hello",
Limit: 10,
})
require.NoError(t, err)
require.NotNil(t, searchResp)
require.Nil(t, searchResp.Error)
require.Equal(t, int64(3), searchResp.TotalHits)
})
t.Run("Search with specific tag", func(t *testing.T) {
searchResp, err := server.Search(ctx, &resourcepb.ResourceSearchRequest{
Options: &resourcepb.ListOptions{
Key: &resourcepb.ResourceKey{
Group: "test.grafana.app",
Resource: "testresources",
Namespace: nsPrefix,
},
},
Query: "tag1",
Limit: 10,
})
require.NoError(t, err)
require.NotNil(t, searchResp)
require.Nil(t, searchResp.Error)
require.Equal(t, int64(1), searchResp.TotalHits)
})
}