cosmos-sdk/baseapp/grpcrouter_test.go
Aaron Craelius 9ce0844847
feat(baseapp): support pulsar gRPC query servers (#11192)
## Description

This adds support for registering gRPC query server implementations that were generated using pulsar. It should make integration with the ORM easier.

This should be backportable.

This does not enable support for pulsar msg servers or tx's.



---

### Author Checklist

*All items are required. Please add a note to the item if the item is not applicable and
please add links to any relevant follow up issues.*

I have...

- [ ] included the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title
- [ ] added `!` to the type prefix if API or client breaking change
- [ ] targeted the correct branch (see [PR Targeting](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#pr-targeting))
- [ ] provided a link to the relevant issue or specification
- [ ] followed the guidelines for [building modules](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules)
- [ ] included the necessary unit and integration [tests](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#testing)
- [ ] added a changelog entry to `CHANGELOG.md`
- [ ] included comments for [documenting Go code](https://blog.golang.org/godoc)
- [ ] updated the relevant documentation or specification
- [ ] reviewed "Files changed" and left comments if necessary
- [ ] confirmed all CI checks have passed

### Reviewers Checklist

*All items are required. Please add a note if the item is not applicable and please add
your handle next to the items reviewed if you only reviewed selected items.*

I have...

- [ ] confirmed the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title
- [ ] confirmed `!` in the type prefix if API or client breaking change
- [ ] confirmed all author checklist items have been addressed 
- [ ] reviewed state machine logic
- [ ] reviewed API design and naming
- [ ] reviewed documentation is accurate
- [ ] reviewed tests and test coverage
- [ ] manually tested (if applicable)
2022-02-17 12:48:45 +00:00

175 lines
5.2 KiB
Go

package baseapp_test
import (
"context"
"sync"
"testing"
"github.com/cosmos/cosmos-sdk/testutil/testdata_pulsar"
"github.com/stretchr/testify/require"
"github.com/tendermint/tendermint/libs/log"
dbm "github.com/tendermint/tm-db"
"github.com/cosmos/cosmos-sdk/baseapp"
"github.com/cosmos/cosmos-sdk/codec/types"
"github.com/cosmos/cosmos-sdk/simapp"
"github.com/cosmos/cosmos-sdk/testutil/testdata"
sdk "github.com/cosmos/cosmos-sdk/types"
)
func TestGRPCQueryRouter(t *testing.T) {
qr := baseapp.NewGRPCQueryRouter()
interfaceRegistry := testdata.NewTestInterfaceRegistry()
qr.SetInterfaceRegistry(interfaceRegistry)
testdata_pulsar.RegisterQueryServer(qr, testdata_pulsar.QueryImpl{})
helper := &baseapp.QueryServiceTestHelper{
GRPCQueryRouter: qr,
Ctx: sdk.Context{}.WithContext(context.Background()),
}
client := testdata.NewQueryClient(helper)
res, err := client.Echo(context.Background(), &testdata.EchoRequest{Message: "hello"})
require.Nil(t, err)
require.NotNil(t, res)
require.Equal(t, "hello", res.Message)
require.Panics(t, func() {
_, _ = client.Echo(context.Background(), nil)
})
res2, err := client.SayHello(context.Background(), &testdata.SayHelloRequest{Name: "Foo"})
require.Nil(t, err)
require.NotNil(t, res)
require.Equal(t, "Hello Foo!", res2.Greeting)
spot := &testdata.Dog{Name: "Spot", Size_: "big"}
any, err := types.NewAnyWithValue(spot)
require.NoError(t, err)
res3, err := client.TestAny(context.Background(), &testdata.TestAnyRequest{AnyAnimal: any})
require.NoError(t, err)
require.NotNil(t, res3)
require.Equal(t, spot, res3.HasAnimal.Animal.GetCachedValue())
}
func TestRegisterQueryServiceTwice(t *testing.T) {
// Setup baseapp.
db := dbm.NewMemDB()
encCfg := simapp.MakeTestEncodingConfig()
logger, _ := log.NewDefaultLogger("plain", "info", false)
app := baseapp.NewBaseApp("test", logger, db)
app.SetInterfaceRegistry(encCfg.InterfaceRegistry)
testdata.RegisterInterfaces(encCfg.InterfaceRegistry)
// First time registering service shouldn't panic.
require.NotPanics(t, func() {
testdata.RegisterQueryServer(
app.GRPCQueryRouter(),
testdata.QueryImpl{},
)
})
// Second time should panic.
require.Panics(t, func() {
testdata.RegisterQueryServer(
app.GRPCQueryRouter(),
testdata.QueryImpl{},
)
})
}
// Tests that we don't have data races per
// https://github.com/cosmos/cosmos-sdk/issues/10324
// but with the same client connection being used concurrently.
func TestQueryDataRaces_sameConnectionToSameHandler(t *testing.T) {
var mu sync.Mutex
var helper *baseapp.QueryServiceTestHelper
makeClientConn := func(qr *baseapp.GRPCQueryRouter) *baseapp.QueryServiceTestHelper {
mu.Lock()
defer mu.Unlock()
if helper == nil {
helper = &baseapp.QueryServiceTestHelper{
GRPCQueryRouter: qr,
Ctx: sdk.Context{}.WithContext(context.Background()),
}
}
return helper
}
testQueryDataRacesSameHandler(t, makeClientConn)
}
// Tests that we don't have data races per
// https://github.com/cosmos/cosmos-sdk/issues/10324
// but with unique client connections requesting from the same handler concurrently.
func TestQueryDataRaces_uniqueConnectionsToSameHandler(t *testing.T) {
// Return a new handler for every single call.
testQueryDataRacesSameHandler(t, func(qr *baseapp.GRPCQueryRouter) *baseapp.QueryServiceTestHelper {
return &baseapp.QueryServiceTestHelper{
GRPCQueryRouter: qr,
Ctx: sdk.Context{}.WithContext(context.Background()),
}
})
}
func testQueryDataRacesSameHandler(t *testing.T, makeClientConn func(*baseapp.GRPCQueryRouter) *baseapp.QueryServiceTestHelper) {
t.Parallel()
qr := baseapp.NewGRPCQueryRouter()
interfaceRegistry := testdata.NewTestInterfaceRegistry()
qr.SetInterfaceRegistry(interfaceRegistry)
testdata.RegisterQueryServer(qr, testdata.QueryImpl{})
// The goal is to invoke the router concurrently and check for any data races.
// 0. Run with: go test -race
// 1. Synchronize every one of the 1,000 goroutines waiting to all query at the
// same time.
// 2. Once the greenlight is given, perform a query through the router.
var wg sync.WaitGroup
defer wg.Wait()
greenlight := make(chan bool)
n := 1000
ready := make(chan bool, n)
go func() {
for i := 0; i < n; i++ {
<-ready
}
close(greenlight)
}()
for i := 0; i < n; i++ {
wg.Add(1)
go func() {
defer wg.Done()
// Wait until we get the green light to start.
ready <- true
<-greenlight
client := testdata.NewQueryClient(makeClientConn(qr))
res, err := client.Echo(context.Background(), &testdata.EchoRequest{Message: "hello"})
require.Nil(t, err)
require.NotNil(t, res)
require.Equal(t, "hello", res.Message)
require.Panics(t, func() {
_, _ = client.Echo(context.Background(), nil)
})
res2, err := client.SayHello(context.Background(), &testdata.SayHelloRequest{Name: "Foo"})
require.Nil(t, err)
require.NotNil(t, res)
require.Equal(t, "Hello Foo!", res2.Greeting)
spot := &testdata.Dog{Name: "Spot", Size_: "big"}
any, err := types.NewAnyWithValue(spot)
require.NoError(t, err)
res3, err := client.TestAny(context.Background(), &testdata.TestAnyRequest{AnyAnimal: any})
require.NoError(t, err)
require.NotNil(t, res3)
require.Equal(t, spot, res3.HasAnimal.Animal.GetCachedValue())
}()
}
}