Skip to content

Commit

Permalink
Run test with and without race detector (#91)
Browse files Browse the repository at this point in the history
Some tests reassigns the global logger. These tests may fail due to race
condition.
It's known zerolog issue: rs/zerolog#242

![Screenshot 2024-05-13 at 12 21
06](https://github.com/QuesmaOrg/quesma/assets/1474/cff627d2-977d-43ae-b239-38c526a5cb75)


Changes 
1. We moved these test to separate file, and exclude from running under
race detector.
2. We need to run these tests anyway. So we run tests twice with and
without race detector enabled.


Reference:
https://go.dev/doc/articles/race_detector
  • Loading branch information
nablaone authored May 15, 2024
1 parent b9798f2 commit d9e3b26
Show file tree
Hide file tree
Showing 3 changed files with 121 additions and 99 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/e2e-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -89,5 +89,5 @@ jobs:

- name: Run e2e tests
working-directory: quesma
run: go test --tags=integration -v ./...
run: go test -race --tags=integration -v ./...

120 changes: 120 additions & 0 deletions quesma/quesma/search_norace_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
//go:build !race

/*
This file contains tests which can raise a race condition.
*/

package quesma

import (
"context"
"github.com/DATA-DOG/go-sqlmock"
"github.com/stretchr/testify/assert"
"math/rand"
"mitmproxy/quesma/clickhouse"
"mitmproxy/quesma/concurrent"
"mitmproxy/quesma/logger"
"mitmproxy/quesma/model"
"mitmproxy/quesma/quesma/config"
"mitmproxy/quesma/quesma/ui"
"mitmproxy/quesma/telemetry"
"mitmproxy/quesma/testdata"
"mitmproxy/quesma/tracing"
"testing"
"time"
)

// TestAllUnsupportedQueryTypesAreProperlyRecorded tests if all unsupported query types are properly recorded.
// It runs |testdata.UnsupportedAggregationsTests| tests, each of them sends one query of unsupported type.
// It ensures that this query type is recorded in the management console, and that all other query types are not.
func TestAllUnsupportedQueryTypesAreProperlyRecorded(t *testing.T) {
for _, tt := range testdata.UnsupportedQueriesTests {
t.Run(tt.TestName, func(t *testing.T) {
if tt.QueryType == "script" {
t.Skip("Only 1 test. We can't deal with scripts inside queries yet. It fails very early, during JSON unmarshalling, so we can't even know the type of aggregation.")
}
db, _, err := sqlmock.New()
if err != nil {
t.Fatal(err)
}
defer db.Close()
assert.NoError(t, err)

lm := clickhouse.NewLogManagerWithConnection(db, table)
cfg := config.QuesmaConfiguration{IndexConfig: map[string]config.IndexConfiguration{tableName: {Enabled: true}}}
logChan := logger.InitOnlyChannelLoggerForTests(cfg, &tracing.AsyncTraceLogger{AsyncQueryTrace: concurrent.NewMap[string, tracing.TraceCtx]()})
managementConsole := ui.NewQuesmaManagementConsole(cfg, nil, nil, logChan, telemetry.NewPhoneHomeEmptyAgent())
go managementConsole.RunOnlyChannelProcessor()

queryRunner := NewQueryRunner(lm, cfg, nil, managementConsole)
newCtx := context.WithValue(ctx, tracing.RequestIdCtxKey, tracing.GetRequestId())
_, _ = queryRunner.handleSearch(newCtx, tableName, []byte(tt.QueryRequestJson))

for _, queryType := range model.AllQueryTypes {
if queryType != tt.QueryType {
assert.Len(t, managementConsole.QueriesWithUnsupportedType(queryType), 0)
}
}

// Update of the count below is done asynchronously in another goroutine
// (go managementConsole.RunOnlyChannelProcessor() above), so we might need to wait a bit
assert.Eventually(t, func() bool {
return len(managementConsole.QueriesWithUnsupportedType(tt.QueryType)) == 1
}, 250*time.Millisecond, 1*time.Millisecond)
assert.Equal(t, 1, managementConsole.GetTotalUnsupportedQueries())
assert.Equal(t, 1, managementConsole.GetSavedUnsupportedQueries())
assert.Equal(t, 1, len(managementConsole.GetUnsupportedTypesWithCount()))
})
}
}

// TestDifferentUnsupportedQueries tests if different unsupported queries are properly recorded.
// I randomly select requestsNr queries from testdata.UnsupportedAggregationsTests, run them, and check
// if all of them are properly recorded in the management console.
func TestDifferentUnsupportedQueries(t *testing.T) {
const maxSavedQueriesPerQueryType = 10
const requestsNr = 50

// generate random |requestsNr| queries to send
testNrs := make([]int, 0, requestsNr)
testCounts := make([]int, len(testdata.UnsupportedQueriesTests))
for range requestsNr {
randInt := rand.Intn(len(testdata.UnsupportedQueriesTests))
if testdata.UnsupportedQueriesTests[randInt].QueryType == "script" {
// We can't deal with scripts inside queries yet. It fails very early, during JSON unmarshalling, so we can't even know the type of aggregation.
continue
}
testNrs = append(testNrs, randInt)
testCounts[randInt]++
}

db, _, err := sqlmock.New()
if err != nil {
t.Fatal(err)
}
defer db.Close()
assert.NoError(t, err)

lm := clickhouse.NewLogManagerWithConnection(db, table)
cfg := config.QuesmaConfiguration{IndexConfig: map[string]config.IndexConfiguration{tableName: {Enabled: true}}}
logChan := logger.InitOnlyChannelLoggerForTests(cfg, &tracing.AsyncTraceLogger{AsyncQueryTrace: concurrent.NewMap[string, tracing.TraceCtx]()})
managementConsole := ui.NewQuesmaManagementConsole(cfg, nil, nil, logChan, telemetry.NewPhoneHomeEmptyAgent())
go managementConsole.RunOnlyChannelProcessor()

queryRunner := NewQueryRunner(lm, cfg, nil, managementConsole)
for _, testNr := range testNrs {
newCtx := context.WithValue(ctx, tracing.RequestIdCtxKey, tracing.GetRequestId())
_, _ = queryRunner.handleSearch(newCtx, tableName, []byte(testdata.UnsupportedQueriesTests[testNr].QueryRequestJson))
}

for i, tt := range testdata.UnsupportedQueriesTests {
// Update of the count below is done asynchronously in another goroutine
// (go managementConsole.RunOnlyChannelProcessor() above), so we might need to wait a bit
assert.Eventually(t, func() bool {
return len(managementConsole.QueriesWithUnsupportedType(tt.QueryType)) == min(testCounts[i], maxSavedQueriesPerQueryType)
}, 600*time.Millisecond, 1*time.Millisecond,
tt.TestName+": wanted: %d, got: %d", min(testCounts[i], maxSavedQueriesPerQueryType),
len(managementConsole.QueriesWithUnsupportedType(tt.QueryType)),
)
}
}
98 changes: 0 additions & 98 deletions quesma/quesma/search_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,8 @@ import (
"fmt"
"github.com/DATA-DOG/go-sqlmock"
"github.com/stretchr/testify/assert"
"math/rand"
"mitmproxy/quesma/clickhouse"
"mitmproxy/quesma/concurrent"
"mitmproxy/quesma/logger"
"mitmproxy/quesma/model"
"mitmproxy/quesma/queryparser"
"mitmproxy/quesma/quesma/config"
Expand All @@ -20,7 +18,6 @@ import (
"strconv"
"strings"
"testing"
"time"
)

const defaultAsyncSearchTimeout = 1000
Expand Down Expand Up @@ -407,98 +404,3 @@ func TestNumericFacetsQueries(t *testing.T) {
}
}
}

// TestAllUnsupportedQueryTypesAreProperlyRecorded tests if all unsupported query types are properly recorded.
// It runs |testdata.UnsupportedAggregationsTests| tests, each of them sends one query of unsupported type.
// It ensures that this query type is recorded in the management console, and that all other query types are not.
func TestAllUnsupportedQueryTypesAreProperlyRecorded(t *testing.T) {
for _, tt := range testdata.UnsupportedQueriesTests {
t.Run(tt.TestName, func(t *testing.T) {
if tt.QueryType == "script" {
t.Skip("Only 1 test. We can't deal with scripts inside queries yet. It fails very early, during JSON unmarshalling, so we can't even know the type of aggregation.")
}
db, _, err := sqlmock.New()
if err != nil {
t.Fatal(err)
}
defer db.Close()
assert.NoError(t, err)

lm := clickhouse.NewLogManagerWithConnection(db, table)
cfg := config.QuesmaConfiguration{IndexConfig: map[string]config.IndexConfiguration{tableName: {Enabled: true}}}
logChan := logger.InitOnlyChannelLoggerForTests(cfg, &tracing.AsyncTraceLogger{AsyncQueryTrace: concurrent.NewMap[string, tracing.TraceCtx]()})
managementConsole := ui.NewQuesmaManagementConsole(cfg, nil, nil, logChan, telemetry.NewPhoneHomeEmptyAgent())
go managementConsole.RunOnlyChannelProcessor()

queryRunner := NewQueryRunner(lm, cfg, nil, managementConsole)
newCtx := context.WithValue(ctx, tracing.RequestIdCtxKey, tracing.GetRequestId())
_, _ = queryRunner.handleSearch(newCtx, tableName, []byte(tt.QueryRequestJson))

for _, queryType := range model.AllQueryTypes {
if queryType != tt.QueryType {
assert.Len(t, managementConsole.QueriesWithUnsupportedType(queryType), 0)
}
}

// Update of the count below is done asynchronously in another goroutine
// (go managementConsole.RunOnlyChannelProcessor() above), so we might need to wait a bit
assert.Eventually(t, func() bool {
return len(managementConsole.QueriesWithUnsupportedType(tt.QueryType)) == 1
}, 250*time.Millisecond, 1*time.Millisecond)
assert.Equal(t, 1, managementConsole.GetTotalUnsupportedQueries())
assert.Equal(t, 1, managementConsole.GetSavedUnsupportedQueries())
assert.Equal(t, 1, len(managementConsole.GetUnsupportedTypesWithCount()))
})
}
}

// TestDifferentUnsupportedQueries tests if different unsupported queries are properly recorded.
// I randomly select requestsNr queries from testdata.UnsupportedAggregationsTests, run them, and check
// if all of them are properly recorded in the management console.
func TestDifferentUnsupportedQueries(t *testing.T) {
const maxSavedQueriesPerQueryType = 10
const requestsNr = 50

// generate random |requestsNr| queries to send
testNrs := make([]int, 0, requestsNr)
testCounts := make([]int, len(testdata.UnsupportedQueriesTests))
for range requestsNr {
randInt := rand.Intn(len(testdata.UnsupportedQueriesTests))
if testdata.UnsupportedQueriesTests[randInt].QueryType == "script" {
// We can't deal with scripts inside queries yet. It fails very early, during JSON unmarshalling, so we can't even know the type of aggregation.
continue
}
testNrs = append(testNrs, randInt)
testCounts[randInt]++
}

db, _, err := sqlmock.New()
if err != nil {
t.Fatal(err)
}
defer db.Close()
assert.NoError(t, err)

lm := clickhouse.NewLogManagerWithConnection(db, table)
cfg := config.QuesmaConfiguration{IndexConfig: map[string]config.IndexConfiguration{tableName: {Enabled: true}}}
logChan := logger.InitOnlyChannelLoggerForTests(cfg, &tracing.AsyncTraceLogger{AsyncQueryTrace: concurrent.NewMap[string, tracing.TraceCtx]()})
managementConsole := ui.NewQuesmaManagementConsole(cfg, nil, nil, logChan, telemetry.NewPhoneHomeEmptyAgent())
go managementConsole.RunOnlyChannelProcessor()

queryRunner := NewQueryRunner(lm, cfg, nil, managementConsole)
for _, testNr := range testNrs {
newCtx := context.WithValue(ctx, tracing.RequestIdCtxKey, tracing.GetRequestId())
_, _ = queryRunner.handleSearch(newCtx, tableName, []byte(testdata.UnsupportedQueriesTests[testNr].QueryRequestJson))
}

for i, tt := range testdata.UnsupportedQueriesTests {
// Update of the count below is done asynchronously in another goroutine
// (go managementConsole.RunOnlyChannelProcessor() above), so we might need to wait a bit
assert.Eventually(t, func() bool {
return len(managementConsole.QueriesWithUnsupportedType(tt.QueryType)) == min(testCounts[i], maxSavedQueriesPerQueryType)
}, 600*time.Millisecond, 1*time.Millisecond,
tt.TestName+": wanted: %d, got: %d", min(testCounts[i], maxSavedQueriesPerQueryType),
len(managementConsole.QueriesWithUnsupportedType(tt.QueryType)),
)
}
}

0 comments on commit d9e3b26

Please sign in to comment.