Files
traces/integration/util/harness_api_reads.go
Joe ElliottandGitHub 5ee93d3cf8 Tempo 3.0: Integration Tests Rewrite (#6088)
* First pass test harness

Signed-off-by: Joe Elliott <[email protected]>

* limits - passed

Signed-off-by: Joe Elliott <[email protected]>

* metrics generator - passed

Signed-off-by: Joe Elliott <[email protected]>

* harness cleanup/fix target info test

Signed-off-by: Joe Elliott <[email protected]>

* deployments - passed

Signed-off-by: Joe Elliott <[email protected]>

* receivers - passed

Signed-off-by: Joe Elliott <[email protected]>

* harness cleanup

Signed-off-by: Joe Elliott <[email protected]>

* storage - passed

Signed-off-by: Joe Elliott <[email protected]>

* api - passing

Signed-off-by: Joe Elliott <[email protected]>

* cleanup and CI

Signed-off-by: Joe Elliott <[email protected]>

* Additional tests

Signed-off-by: Joe Elliott <[email protected]>

* it's me and you linter. how many times are you going to do this to me?

Signed-off-by: Joe Elliott <[email protected]>

* lint and tests

Signed-off-by: Joe Elliott <[email protected]>

* restored live store partition tests

Signed-off-by: Joe Elliott <[email protected]>

* massive lint

Signed-off-by: Joe Elliott <[email protected]>

* cleanup

Signed-off-by: Joe Elliott <[email protected]>

* lint >.<

Signed-off-by: Joe Elliott <[email protected]>

* wups

Signed-off-by: Joe Elliott <[email protected]>

* cleanup and stabilizing

Signed-off-by: Joe Elliott <[email protected]>

* cleanup

Signed-off-by: Joe Elliott <[email protected]>

* omg

Signed-off-by: Joe Elliott <[email protected]>

* added config file. removed empty file. deleted some debug code

Signed-off-by: Joe Elliott <[email protected]>

* even more fixes

Signed-off-by: Joe Elliott <[email protected]>

* maybe this works? maybe it was all local networking issues

Signed-off-by: Joe Elliott <[email protected]>

* review

Signed-off-by: Joe Elliott <[email protected]>

* backed off huge write timeout. added missing config file

Signed-off-by: Joe Elliott <[email protected]>

* restore disable parallelism setting. restore beefy write timeout

Signed-off-by: Joe Elliott <[email protected]>

* apparently another test relied on the http receiver

Signed-off-by: Joe Elliott <[email protected]>

* livestore avail zone!

Signed-off-by: Joe Elliott <[email protected]>

* add support for v1 traces and tags endpoints

Signed-off-by: Joe Elliott <[email protected]>

* review

Signed-off-by: Joe Elliott <[email protected]>

* changed the location of the changelog entry to avoid conflicts

Signed-off-by: Joe Elliott <[email protected]>

---------

Signed-off-by: Joe Elliott <[email protected]>
2026-01-07 08:31:11 -05:00

160 lines
4.5 KiB
Go

package util
import (
"context"
"errors"
"fmt"
"io"
"testing"
"github.com/grafana/dskit/user"
"github.com/grafana/tempo/pkg/httpclient"
"github.com/grafana/tempo/pkg/model/trace"
"github.com/grafana/tempo/pkg/tempopb"
tempoUtil "github.com/grafana/tempo/pkg/util"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/credentials/insecure"
)
// BaseURL returns the base URL for the Tempo queryAPI. Some tests construct requests manually if
// they need to do something such as set custom headers or bad requests bodies. If you don't have
// a special need, use the client returned from APIClientHTTP or APIClientGRPC instead.
func (h *TempoHarness) BaseURL() string {
return "http://" + h.Services[ServiceQueryFrontend].Endpoint(3200)
}
func (h *TempoHarness) APIClientHTTP(tenant string) *httpclient.Client {
return httpclient.New(h.BaseURL(), tenant)
}
func (h *TempoHarness) APIClientGRPC(tenant string) (tempopb.StreamingQuerierClient, context.Context, error) {
endpoint := h.Services[ServiceQueryFrontend].Endpoint(3200)
ctx := context.Background()
if tenant != "" {
ctx = user.InjectOrgID(ctx, tenant)
var err error
ctx, err = user.InjectIntoGRPCRequest(ctx)
if err != nil {
return nil, nil, err
}
}
client, err := NewSearchGRPCClient(endpoint, insecure.NewCredentials())
if err != nil {
return nil, nil, err
}
return client, ctx, nil
}
func NewSearchGRPCClient(endpoint string, creds credentials.TransportCredentials) (tempopb.StreamingQuerierClient, error) {
clientConn, err := grpc.NewClient(endpoint, grpc.WithTransportCredentials(creds))
if err != nil {
return nil, err
}
return tempopb.NewStreamingQuerierClient(clientConn), nil
}
func SearchTraceQLAndAssertTrace(t *testing.T, client *httpclient.Client, info *tempoUtil.TraceInfo) {
expected, err := info.ConstructTraceFromEpoch()
require.NoError(t, err)
attr := tempoUtil.RandomAttrFromTrace(expected)
query := fmt.Sprintf(`{ .%s = "%s"}`, attr.GetKey(), attr.GetValue().GetStringValue())
resp, err := client.SearchTraceQL(query)
require.NoError(t, err)
require.True(t, traceIDInResults(t, info.HexID(), resp))
}
func SearchTraceQLAndAssertTraceWithRange(t *testing.T, client *httpclient.Client, info *tempoUtil.TraceInfo, start, end int64) {
expected, err := info.ConstructTraceFromEpoch()
require.NoError(t, err)
attr := tempoUtil.RandomAttrFromTrace(expected)
query := fmt.Sprintf(`{ .%s = "%s"}`, attr.GetKey(), attr.GetValue().GetStringValue())
resp, err := client.SearchTraceQLWithRange(query, start, end)
require.NoError(t, err)
require.True(t, traceIDInResults(t, info.HexID(), resp))
}
// SearchStreamAndAssertTrace will search and assert that the trace is present in the streamed results.
// nolint: revive
func SearchStreamAndAssertTrace(t *testing.T, ctx context.Context, client tempopb.StreamingQuerierClient, info *tempoUtil.TraceInfo, start, end int64) {
expected, err := info.ConstructTraceFromEpoch()
require.NoError(t, err)
attr := tempoUtil.RandomAttrFromTrace(expected)
query := fmt.Sprintf(`{ .%s = "%s"}`, attr.GetKey(), attr.GetValue().GetStringValue())
// -- assert search
resp, err := client.Search(ctx, &tempopb.SearchRequest{
Query: query,
Start: uint32(start),
End: uint32(end),
})
require.NoError(t, err)
// drain the stream until everything is returned while watching for the trace in question
found := false
for {
resp, err := resp.Recv()
if resp != nil {
found = traceIDInResults(t, info.HexID(), resp)
if found {
break
}
}
if errors.Is(err, io.EOF) {
break
}
require.NoError(t, err)
}
require.True(t, found)
}
func traceIDInResults(t *testing.T, hexID string, resp *tempopb.SearchResponse) bool {
for _, s := range resp.Traces {
equal, err := tempoUtil.EqualHexStringTraceIDs(s.TraceID, hexID)
require.NoError(t, err)
if equal {
return true
}
}
return false
}
func QueryAndAssertTrace(t *testing.T, client *httpclient.Client, info *tempoUtil.TraceInfo) {
// v2
respV2, err := client.QueryTraceV2(info.HexID())
require.NoError(t, err)
expected, err := info.ConstructTraceFromEpoch()
require.NoError(t, err)
AssertEqualTrace(t, respV2.Trace, expected)
// v1
respV1, err := client.QueryTrace(info.HexID())
require.NoError(t, err)
AssertEqualTrace(t, respV1, expected)
}
func AssertEqualTrace(t *testing.T, a, b *tempopb.Trace) {
t.Helper()
trace.SortTraceAndAttributes(a)
trace.SortTraceAndAttributes(b)
assert.Equal(t, a, b)
}