Vikrant Gupta c322657666
feat(organization): schema changes for the organizations entity (#7684)
* feat(organization): add hname and alias for organization

* fix: boolean values are not shown in the list panel's column

* fix: moved logic to component level

* fix: added type

* fix: added test cases

* fix: added test cases

* chore: update copy webpack plugin

* Revert "fix: display same key with multiple data types in filter suggestions by enhancing the deduping logic (#7255)"

This reverts commit 1e85981a17a8e715e948308d3e85072d976907d3.

* fix: use query search v2 for traces data source to handle multiple data types for the same key

* fix(QueryBuilderSearchV2): add user typed option if it doesn't exist in the payload

* fix(QueryBuilderSearchV2): increase the height of search dropdown for non-logs data sources

* fix: display span scope selector for trace data source

* chore: remove the span scope selector from qb search v1 and move the component to search v2

* fix: write test to ensure that we display span scope selector for traces data source

* fix: limit converting  ->   only to log data source

* fix: don't display empty suggestion if only spaces are typed

* chore: tests for span scope selector

* chore: qb search flow (key, operator, value) test cases

* refactor: fix the Maximum update depth reached issue while running tests

* chore: overall improvements to span scope selector tests

Resource attr filter: style fix and quick filter changes (#7691)

* chore: resource attr filter init

* chore: resource attr filter api integration

* chore: operator config updated

* chore: fliter show hide logic and styles

* chore: add support for custom operator list to qb

* chore: minor refactor

* chore: minor code refactor

* test: quick filters test suite added

* test: quick filters test suite added

* test: all errors test suite added

* chore: style fix

* test: all errors mock fix

* chore: test case fix and mixpanel update

* chore: color update

* chore: minor refactor

* chore: style fix

* chore: set default query in exceptions tab

* chore: style fix

* chore: minor refactor

* chore: minor refactor

* chore: minor refactor

* chore: test update

* chore: fix filter header with no query name

* fix: scroll fix

* chore: add data source traces to quick filters

* chore: replace div with fragment

---------

Co-authored-by: Aditya Singh <adityasingh@Adityas-MacBook-Pro.local>

fix: handle rate operators for table panel (#7695)

* fix: handle rate operators for table panel

chore: fix error rate (#7701)

Signed-off-by: Shivanshu Raj Shrivastava <shivanshu1333@gmail.com>

* feat(organization): minor cleanups

* feat(organization): better naming for api and usecase

* feat(organization): better packaging for modules

* feat(organization): change hname to displayName

* feat(organization): update the migration to use dialect

* feat(organization): update the migration to use dialect

* feat(organization): update the migration to use dialect

* feat(organization): revert back to impl

* feat(organization): remove DI from organization

* feat(organization): address review comments

* feat(organization): address review comments

* feat(organization): address review comments

---------

Signed-off-by: Shivanshu Raj Shrivastava <shivanshu1333@gmail.com>
2025-04-25 19:38:15 +05:30

245 lines
8.1 KiB
Go

package tests
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"net/http/httptest"
"runtime/debug"
"testing"
"time"
"github.com/DATA-DOG/go-sqlmock"
"github.com/SigNoz/signoz/pkg/instrumentation/instrumentationtest"
"github.com/SigNoz/signoz/pkg/modules/organization"
"github.com/SigNoz/signoz/pkg/prometheus"
"github.com/SigNoz/signoz/pkg/prometheus/prometheustest"
"github.com/SigNoz/signoz/pkg/query-service/app"
"github.com/SigNoz/signoz/pkg/query-service/app/clickhouseReader"
"github.com/SigNoz/signoz/pkg/query-service/auth"
"github.com/SigNoz/signoz/pkg/query-service/constants"
"github.com/SigNoz/signoz/pkg/query-service/dao"
"github.com/SigNoz/signoz/pkg/query-service/model"
"github.com/SigNoz/signoz/pkg/sqlstore"
"github.com/SigNoz/signoz/pkg/telemetrystore"
"github.com/SigNoz/signoz/pkg/telemetrystore/telemetrystoretest"
"github.com/SigNoz/signoz/pkg/types"
"github.com/SigNoz/signoz/pkg/types/authtypes"
"github.com/google/uuid"
"github.com/open-telemetry/opentelemetry-collector-contrib/pkg/stanza/entry"
mockhouse "github.com/srikanthccv/ClickHouse-go-mock"
"github.com/stretchr/testify/require"
"golang.org/x/exp/maps"
)
var jwt = authtypes.NewJWT("secret", 1*time.Hour, 2*time.Hour)
func NewMockClickhouseReader(t *testing.T, testDB sqlstore.SQLStore) (*clickhouseReader.ClickHouseReader, mockhouse.ClickConnMockCommon) {
require.NotNil(t, testDB)
telemetryStore := telemetrystoretest.New(telemetrystore.Config{Provider: "clickhouse"}, sqlmock.QueryMatcherRegexp)
reader := clickhouseReader.NewReaderFromClickhouseConnection(
clickhouseReader.NewOptions("", ""),
testDB,
telemetryStore,
prometheustest.New(instrumentationtest.New().Logger(), prometheus.Config{}),
"",
true,
true,
time.Duration(time.Second),
nil,
)
return reader, telemetryStore.Mock()
}
func addLogsQueryExpectation(
mockClickhouse mockhouse.ClickConnMockCommon,
logsToReturn []model.SignozLog,
) {
cols := []mockhouse.ColumnType{}
cols = append(cols, mockhouse.ColumnType{Type: "UInt64", Name: "timestamp"})
cols = append(cols, mockhouse.ColumnType{Type: "UInt64", Name: "observed_timestamp"})
cols = append(cols, mockhouse.ColumnType{Type: "String", Name: "id"})
cols = append(cols, mockhouse.ColumnType{Type: "String", Name: "trace_id"})
cols = append(cols, mockhouse.ColumnType{Type: "String", Name: "span_id"})
cols = append(cols, mockhouse.ColumnType{Type: "UInt32", Name: "trace_flags"})
cols = append(cols, mockhouse.ColumnType{Type: "String", Name: "severity_text"})
cols = append(cols, mockhouse.ColumnType{Type: "UInt8", Name: "severity_number"})
cols = append(cols, mockhouse.ColumnType{Type: "String", Name: "body"})
cols = append(cols, mockhouse.ColumnType{Type: "Array(String)", Name: "resources_string_key"})
cols = append(cols, mockhouse.ColumnType{Type: "Array(String)", Name: "resources_string_value"})
cols = append(cols, mockhouse.ColumnType{Type: "Array(String)", Name: "attributes_string_key"})
cols = append(cols, mockhouse.ColumnType{Type: "Array(String)", Name: "attributes_string_value"})
cols = append(cols, mockhouse.ColumnType{Type: "Array(String)", Name: "attributes_int64_key"})
cols = append(cols, mockhouse.ColumnType{Type: "Array(Int64)", Name: "attributes_int64_value"})
cols = append(cols, mockhouse.ColumnType{Type: "Array(String)", Name: "attributes_float64_key"})
cols = append(cols, mockhouse.ColumnType{Type: "Array(Float64)", Name: "attributes_float64_value"})
cols = append(cols, mockhouse.ColumnType{Type: "Array(String)", Name: "attributes_bool_key"})
cols = append(cols, mockhouse.ColumnType{Type: "Array(Bool)", Name: "attributes_bool_value"})
values := [][]any{}
for _, l := range logsToReturn {
rowValues := []any{}
rowValues = append(rowValues, l.Timestamp)
rowValues = append(rowValues, l.Timestamp)
rowValues = append(rowValues, l.ID)
rowValues = append(rowValues, l.TraceID)
rowValues = append(rowValues, l.SpanID)
rowValues = append(rowValues, l.TraceFlags)
rowValues = append(rowValues, l.SeverityText)
rowValues = append(rowValues, l.SeverityNumber)
rowValues = append(rowValues, l.Body)
rowValues = append(rowValues, maps.Keys(l.Resources_string))
rowValues = append(rowValues, maps.Values(l.Resources_string))
rowValues = append(rowValues, maps.Keys(l.Attributes_string))
rowValues = append(rowValues, maps.Values(l.Attributes_string))
rowValues = append(rowValues, maps.Keys(l.Attributes_int64))
rowValues = append(rowValues, maps.Values(l.Attributes_int64))
rowValues = append(rowValues, maps.Keys(l.Attributes_float64))
rowValues = append(rowValues, maps.Values(l.Attributes_float64))
rowValues = append(rowValues, maps.Keys(l.Attributes_bool))
rowValues = append(rowValues, maps.Values(l.Attributes_bool))
values = append(values, rowValues)
}
rows := mockhouse.NewRows(cols, values)
mockClickhouse.ExpectQuery(
"SELECT .*? from signoz_logs.distributed_logs.*",
).WillReturnRows(rows)
}
func makeTestSignozLog(
body string,
attributes map[string]interface{},
) model.SignozLog {
testLog := model.SignozLog{
Timestamp: uint64(time.Now().UnixNano()),
Body: body,
Attributes_bool: map[string]bool{},
Attributes_string: map[string]string{},
Attributes_int64: map[string]int64{},
Attributes_float64: map[string]float64{},
Resources_string: map[string]string{},
SeverityText: entry.Info.String(),
SeverityNumber: uint8(entry.Info),
SpanID: uuid.New().String(),
TraceID: uuid.New().String(),
}
for k, v := range attributes {
switch v := v.(type) {
case bool:
testLog.Attributes_bool[k] = v
case string:
testLog.Attributes_string[k] = v
case int:
testLog.Attributes_int64[k] = int64(v)
case float64:
testLog.Attributes_float64[k] = v
default:
panic(fmt.Sprintf("found attribute value of unsupported type %T in test log", v))
}
}
return testLog
}
func createTestUser(organizationModule organization.Module) (*types.User, *model.ApiError) {
// Create a test user for auth
ctx := context.Background()
organization := types.NewOrganization("test")
err := organizationModule.Create(ctx, organization)
if err != nil {
return nil, model.InternalError(err)
}
group, apiErr := dao.DB().GetGroupByName(ctx, constants.AdminGroup)
if apiErr != nil {
return nil, model.InternalError(apiErr)
}
auth.InitAuthCache(ctx)
userId := uuid.NewString()
return dao.DB().CreateUser(
ctx,
&types.User{
ID: userId,
Name: "test",
Email: userId[:8] + "test@test.com",
Password: "test",
OrgID: organization.ID.StringValue(),
GroupID: group.ID,
},
true,
)
}
func AuthenticatedRequestForTest(
user *types.User,
path string,
postData interface{},
) (*http.Request, error) {
userJwt, err := auth.GenerateJWTForUser(user, jwt)
if err != nil {
return nil, err
}
var req *http.Request
if postData != nil {
var body bytes.Buffer
err = json.NewEncoder(&body).Encode(postData)
if err != nil {
return nil, err
}
req = httptest.NewRequest(http.MethodPost, path, &body)
} else {
req = httptest.NewRequest(http.MethodGet, path, nil)
}
req.Header.Add("Authorization", "Bearer "+userJwt.AccessJwt)
ctx, err := jwt.ContextFromRequest(req.Context(), req.Header.Get("Authorization"))
if err != nil {
return nil, err
}
req = req.WithContext(ctx)
return req, nil
}
func HandleTestRequest(handler http.Handler, req *http.Request, expectedStatus int) (*app.ApiResponse, error) {
respWriter := httptest.NewRecorder()
handler.ServeHTTP(respWriter, req)
response := respWriter.Result()
responseBody, err := io.ReadAll(response.Body)
if err != nil {
return nil, fmt.Errorf("couldn't read response body received from QS: %w", err)
}
if response.StatusCode != expectedStatus {
return nil, fmt.Errorf(
"unexpected response status from query service for path %s. status: %d, body: %v\n%v",
req.URL.Path, response.StatusCode, string(responseBody), string(debug.Stack()),
)
}
var result app.ApiResponse
err = json.Unmarshal(responseBody, &result)
if err != nil {
return nil, fmt.Errorf(
"Could not unmarshal QS response into an ApiResponse.\nResponse body: %s",
string(responseBody),
)
}
return &result, nil
}