-
Notifications
You must be signed in to change notification settings - Fork 12
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Testing, Tooling] Expose integration app via gRPC/HTTP/WS #1017
base: main
Are you sure you want to change the base?
Changes from 2 commits
3cf4cd3
3d3e3f4
9a5fc9d
1714822
f9091a3
c76a958
423067b
2b76ef1
0b1ff7f
b975758
a1bbe17
2777eaa
5de0cea
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
package e2e | ||
|
||
import ( | ||
"context" | ||
"errors" | ||
"fmt" | ||
"net" | ||
"net/http" | ||
"sync" | ||
"testing" | ||
|
||
"github.com/cosmos/cosmos-sdk/crypto/keyring" | ||
"github.com/cosmos/cosmos-sdk/types/module" | ||
"github.com/gorilla/websocket" | ||
"github.com/grpc-ecosystem/grpc-gateway/runtime" | ||
"github.com/stretchr/testify/require" | ||
"google.golang.org/grpc" | ||
"google.golang.org/grpc/credentials/insecure" | ||
|
||
coretypes "github.com/cometbft/cometbft/rpc/core/types" | ||
|
||
"github.com/pokt-network/poktroll/testutil/integration" | ||
"github.com/pokt-network/poktroll/testutil/testclient" | ||
) | ||
|
||
// E2EApp wraps an integration.App and provides both gRPC and WebSocket servers for end-to-end testing | ||
type E2EApp struct { | ||
*integration.App | ||
grpcServer *grpc.Server | ||
grpcListener net.Listener | ||
wsServer *http.Server | ||
wsListener net.Listener | ||
wsUpgrader websocket.Upgrader | ||
wsConnMutex sync.RWMutex | ||
wsConnections map[*websocket.Conn]map[string]struct{} // maps connections to their subscribed event queries | ||
blockEventChan chan *coretypes.ResultEvent | ||
} | ||
|
||
// NewE2EApp creates a new E2EApp instance with integration.App, gRPC, and WebSocket servers | ||
func NewE2EApp(t *testing.T, opts ...integration.IntegrationAppOptionFn) *E2EApp { | ||
t.Helper() | ||
|
||
// Initialize and start gRPC server | ||
creds := insecure.NewCredentials() | ||
grpcServer := grpc.NewServer(grpc.Creds(creds)) | ||
mux := runtime.NewServeMux() | ||
|
||
// Create the integration app | ||
app := integration.NewCompleteIntegrationApp(t, opts...) | ||
app.RegisterGRPCServer(grpcServer) | ||
//app.RegisterGRPCServer(e2eApp.grpcServer) | ||
|
||
flagSet := testclient.NewFlagSet(t, "tcp://127.0.0.1:42070") | ||
keyRing := keyring.NewInMemory(app.GetCodec()) | ||
clientCtx := testclient.NewLocalnetClientCtx(t, flagSet).WithKeyring(keyRing) | ||
|
||
for moduleName, mod := range app.GetModuleManager().Modules { | ||
fmt.Printf(">>> %s\n", moduleName) | ||
mod.(module.AppModuleBasic).RegisterGRPCGatewayRoutes(clientCtx, mux) | ||
} | ||
|
||
// Create listeners for gRPC, WebSocket, and HTTP | ||
grpcListener, err := net.Listen("tcp", "localhost:42069") | ||
require.NoError(t, err, "failed to create gRPC listener") | ||
|
||
wsListener, err := net.Listen("tcp", "localhost:6969") | ||
require.NoError(t, err, "failed to create WebSocket listener") | ||
|
||
e2eApp := &E2EApp{ | ||
App: app, | ||
grpcListener: grpcListener, | ||
grpcServer: grpcServer, | ||
wsListener: wsListener, | ||
wsConnections: make(map[*websocket.Conn]map[string]struct{}), | ||
wsUpgrader: websocket.Upgrader{}, | ||
blockEventChan: make(chan *coretypes.ResultEvent, 1), | ||
} | ||
|
||
go func() { | ||
if err := e2eApp.grpcServer.Serve(grpcListener); err != nil { | ||
panic(err) | ||
} | ||
}() | ||
|
||
// Initialize and start WebSocket server | ||
e2eApp.wsServer = newWebSocketServer(e2eApp) | ||
go func() { | ||
if err := e2eApp.wsServer.Serve(wsListener); err != nil && errors.Is(err, http.ErrServerClosed) { | ||
panic(err) | ||
} | ||
}() | ||
|
||
// Initialize and start HTTP server | ||
go func() { | ||
if err := http.ListenAndServe("localhost:42070", mux); err != nil { | ||
panic(err) | ||
} | ||
}() | ||
|
||
// Start event handling | ||
go e2eApp.handleBlockEvents(t) | ||
|
||
return e2eApp | ||
} | ||
|
||
// Close gracefully shuts down the E2EApp and its servers | ||
func (app *E2EApp) Close() error { | ||
app.grpcServer.GracefulStop() | ||
if err := app.wsServer.Close(); err != nil { | ||
return err | ||
} | ||
|
||
close(app.blockEventChan) | ||
|
||
return nil | ||
} | ||
|
||
// GetClientConn returns a gRPC client connection to the E2EApp's gRPC server | ||
func (app *E2EApp) GetClientConn(ctx context.Context) (*grpc.ClientConn, error) { | ||
return grpc.DialContext( | ||
ctx, | ||
app.grpcListener.Addr().String(), | ||
grpc.WithTransportCredentials(insecure.NewCredentials()), | ||
) | ||
} | ||
|
||
// GetWSEndpoint returns the WebSocket endpoint URL | ||
func (app *E2EApp) GetWSEndpoint() string { | ||
return "ws://" + app.wsListener.Addr().String() + "/websocket" | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,237 @@ | ||
package e2e | ||
|
||
import ( | ||
"context" | ||
"encoding/hex" | ||
"net/http" | ||
"testing" | ||
"time" | ||
|
||
"cosmossdk.io/depinject" | ||
"cosmossdk.io/math" | ||
abci "github.com/cometbft/cometbft/abci/types" | ||
cometrpctypes "github.com/cometbft/cometbft/rpc/core/types" | ||
"github.com/cometbft/cometbft/types" | ||
cosmostx "github.com/cosmos/cosmos-sdk/client/tx" | ||
"github.com/cosmos/cosmos-sdk/crypto/hd" | ||
"github.com/cosmos/cosmos-sdk/crypto/keyring" | ||
cosmostypes "github.com/cosmos/cosmos-sdk/types" | ||
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" | ||
"github.com/golang/mock/gomock" | ||
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime" | ||
"github.com/stretchr/testify/require" | ||
"google.golang.org/grpc" | ||
|
||
"github.com/pokt-network/poktroll/app/volatile" | ||
"github.com/pokt-network/poktroll/pkg/client/block" | ||
"github.com/pokt-network/poktroll/pkg/client/events" | ||
"github.com/pokt-network/poktroll/pkg/client/query" | ||
"github.com/pokt-network/poktroll/pkg/client/tx" | ||
txtypes "github.com/pokt-network/poktroll/pkg/client/tx/types" | ||
"github.com/pokt-network/poktroll/testutil/integration" | ||
"github.com/pokt-network/poktroll/testutil/mockclient" | ||
"github.com/pokt-network/poktroll/testutil/testclient" | ||
gatewaytypes "github.com/pokt-network/poktroll/x/gateway/types" | ||
) | ||
|
||
func TestGRPCServer2(t *testing.T) { | ||
grpcServer := grpc.NewServer() | ||
//gwKeeper := gatewaykeeper.NewKeeper() | ||
//gwSvc := gatewaykeeper.NewMsgServerImpl(gwKeeper) | ||
|
||
app := integration.NewCompleteIntegrationApp(t) | ||
app.RegisterGRPCServer(grpcServer) | ||
|
||
mux := runtime.NewServeMux() | ||
err := http.ListenAndServe(":42070", mux) | ||
require.NoError(t, err) | ||
//gatewaytypes.RegisterMsgServer(grpcServer, gwSvc) | ||
//gatewaytypes.RegisterMsgServer(app.MsgServiceRouter(), gwSvc) | ||
|
||
//gatewaytypes.RegisterQueryHandlerFromEndpoint() | ||
|
||
//reflectionService, err := services.NewReflectionService() | ||
//require.NoError(t, err) | ||
|
||
//desc, err := reflectionService.FileDescriptors(nil, nil) | ||
//require.NoError(t, err) | ||
|
||
//app := integration.NewCompleteIntegrationApp(t) | ||
//grpcServer.RegisterService(desc, app.MsgServiceRouter()) | ||
} | ||
|
||
func TestSanity(t *testing.T) { | ||
app := integration.NewCompleteIntegrationApp(t) | ||
|
||
//app.Query(nil, &authtypes.QueryAccountRequest{ | ||
// Address: "pokt1h04g6njyuv03dhd74a73pyzeadmd8dk7l9tsk8", | ||
//}) | ||
|
||
//app.Query(nil, types2.RequestQuery{ | ||
// Data: nil, | ||
// Path: "", | ||
// Height: 0, | ||
// Prove: false, | ||
//}) | ||
|
||
ctrl := gomock.NewController(t) | ||
blockQueryClient := mockclient.NewMockBlockQueryClient(ctrl) | ||
blockQueryClient.EXPECT(). | ||
Block(gomock.Any(), gomock.Any()). | ||
DoAndReturn( | ||
func(ctx context.Context, height *int64) (*cometrpctypes.ResultBlock, error) { | ||
blockResultMock := &cometrpctypes.ResultBlock{ | ||
Block: &types.Block{ | ||
Header: types.Header{ | ||
Height: 1, | ||
}, | ||
}, | ||
} | ||
return blockResultMock, nil | ||
}, | ||
).AnyTimes() | ||
deps := depinject.Supply(app.QueryHelper(), blockQueryClient) | ||
sharedClient, err := query.NewSharedQuerier(deps) | ||
require.NoError(t, err) | ||
|
||
params, err := sharedClient.GetParams(app.GetSdkCtx()) | ||
require.NoError(t, err) | ||
|
||
t.Logf("shared params: %+v", params) | ||
} | ||
|
||
func TestNewE2EApp(t *testing.T) { | ||
initialHeight := int64(7553) | ||
// TODO_IN_THIS_COMMIT: does this 👆 need to be reconciled with the internal height of app? | ||
|
||
app := NewE2EApp(t) | ||
|
||
keyRing := keyring.NewInMemory(app.GetCodec()) | ||
rec, err := keyRing.NewAccount( | ||
"gateway2", | ||
"suffer wet jelly furnace cousin flip layer render finish frequent pledge feature economy wink like water disease final erase goat include apple state furnace", | ||
"", | ||
cosmostypes.FullFundraiserPath, | ||
hd.Secp256k1, | ||
) | ||
require.NoError(t, err) | ||
|
||
gateway2Addr, err := rec.GetAddress() | ||
require.NoError(t, err) | ||
|
||
// TODO_IN_THIS_COMMOT: fund gateway2 account. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [linter-name (fail-on-found)] reported by reviewdog 🐶 |
||
_, err = app.RunMsg(t, &banktypes.MsgSend{ | ||
FromAddress: app.GetFaucetBech32(), | ||
ToAddress: gateway2Addr.String(), | ||
Amount: cosmostypes.NewCoins(cosmostypes.NewInt64Coin(volatile.DenomuPOKT, 100000000)), | ||
}) | ||
require.NoError(t, err) | ||
|
||
ctrl := gomock.NewController(t) | ||
blockQueryClient := mockclient.NewMockBlockQueryClient(ctrl) | ||
blockQueryClient.EXPECT(). | ||
Block(gomock.Any(), gomock.Any()). | ||
DoAndReturn( | ||
func(ctx context.Context, height *int64) (*cometrpctypes.ResultBlock, error) { | ||
//time.Sleep(time.Second * 100) | ||
blockResultMock := &cometrpctypes.ResultBlock{ | ||
Block: &types.Block{ | ||
Header: types.Header{ | ||
Height: initialHeight, | ||
}, | ||
}, | ||
} | ||
return blockResultMock, nil | ||
}, | ||
).AnyTimes() | ||
//blockQueryClient, err := sdkclient.NewClientFromNode("tcp://127.0.0.1:42070") | ||
//blockQueryClient, err := sdkclient.NewClientFromNode("tcp://127.0.0.1:26657") | ||
//require.NoError(t, err) | ||
|
||
deps := depinject.Supply(app.QueryHelper(), blockQueryClient) | ||
|
||
sharedQueryClient, err := query.NewSharedQuerier(deps) | ||
require.NoError(t, err) | ||
|
||
sharedParams, err := sharedQueryClient.GetParams(app.GetSdkCtx()) | ||
require.NoError(t, err) | ||
|
||
t.Logf("shared params: %+v", sharedParams) | ||
|
||
eventsQueryClient := events.NewEventsQueryClient("ws://127.0.0.1:6969/websocket") | ||
//eventsQueryClient := events.NewEventsQueryClient("ws://127.0.0.1:26657/websocket") | ||
deps = depinject.Configs(deps, depinject.Supply(eventsQueryClient)) | ||
blockClient, err := block.NewBlockClient(app.GetSdkCtx(), deps) | ||
require.NoError(t, err) | ||
|
||
// TODO_IN_THIS_COMMIT: NOT localnet flagset NOR context, should be | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [linter-name (fail-on-found)] reported by reviewdog 🐶 |
||
// configured to match the E2E app listeners. | ||
flagSet := testclient.NewFlagSet(t, "127.0.0.1:42069") | ||
clientCtx := testclient.NewLocalnetClientCtx(t, flagSet).WithKeyring(keyRing) | ||
|
||
txFactory, err := cosmostx.NewFactoryCLI(clientCtx, flagSet) | ||
require.NoError(t, err) | ||
|
||
deps = depinject.Configs(deps, depinject.Supply(txtypes.Context(clientCtx), txFactory)) | ||
|
||
//_, txContext := testtx.NewE2ETxContext(t, keyRing, flagSet) | ||
txContext, err := tx.NewTxContext(deps) | ||
require.NoError(t, err) | ||
|
||
deps = depinject.Configs(deps, depinject.Supply(blockClient, txContext)) | ||
txClient, err := tx.NewTxClient(app.GetSdkCtx(), deps, tx.WithSigningKeyName("gateway2")) | ||
require.NoError(t, err) | ||
|
||
time.Sleep(time.Second * 1) | ||
|
||
eitherErr := txClient.SignAndBroadcast( | ||
app.GetSdkCtx(), | ||
gatewaytypes.NewMsgStakeGateway( | ||
"pokt15w3fhfyc0lttv7r585e2ncpf6t2kl9uh8rsnyz", | ||
cosmostypes.NewCoin(volatile.DenomuPOKT, math.NewInt(100000001)), | ||
), | ||
) | ||
|
||
// TODO_IN_THIS_COMMIT: signal to the WS server to send another block result event... | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [linter-name (fail-on-found)] reported by reviewdog 🐶 |
||
//app.NextBlock(t) | ||
|
||
err, errCh := eitherErr.SyncOrAsyncError() | ||
require.NoError(t, err) | ||
require.NoError(t, <-errCh) | ||
} | ||
|
||
func TestGRPCServer(t *testing.T) { | ||
app := NewE2EApp(t) | ||
t.Cleanup(func() { | ||
app.Close() | ||
}) | ||
|
||
grpcConn, err := grpc.NewClient("tcp://127.0.0.1:42069", grpc.WithInsecure()) | ||
require.NoError(t, err) | ||
|
||
dataHex, err := hex.DecodeString("0A2B706F6B74313577336668667963306C747476377235383565326E6370663674326B6C3975683872736E797A") | ||
require.NoError(t, err) | ||
|
||
req := &abci.RequestQuery{ | ||
Data: dataHex, | ||
Path: "/cosmos.auth.v1beta1.Query/Account", | ||
Height: 0, | ||
Prove: false, | ||
} | ||
res := &abci.ResponseQuery{} | ||
|
||
grpcConn.Invoke(context.Background(), "abci_query", req, res) | ||
|
||
//"method" : "abci_query", | ||
//"params" : { | ||
// "data" : "0A2B706F6B74313577336668667963306C747476377235383565326E6370663674326B6C3975683872736E797A", | ||
// "height" : "0", | ||
// "path" : "/cosmos.auth.v1beta1.Query/Account", | ||
// "prove" : false | ||
//} | ||
|
||
//"method" : "broadcast_tx_async", | ||
//"params" : { | ||
// "tx" : "CmsKZgohL3Bva3Ryb2xsLmdhdGV3YXkuTXNnU3Rha2VHYXRld2F5EkEKK3Bva3QxNXczZmhmeWMwbHR0djdyNTg1ZTJuY3BmNnQya2w5dWg4cnNueXoSEgoFdXBva3QSCTEwMDAwMDAwMRiGOxJYCk4KRgofL2Nvc21vcy5jcnlwdG8uc2VjcDI1NmsxLlB1YktleRIjCiEDZo2bY9XquUsFljtW/OKWVCDhYFf7NbidN4Y99VQ9438SBAoCCAESBhCqoYLJAhpAw5e7iJN5SpFit3fftxnZY7EDiFqupi7XEL3sUyeV0IBSQv2JZ7Cdu0dCG0yEVgj0xarkPi7dR10pNDL1gcUJxw==" | ||
//} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[linter-name (fail-on-found)] reported by reviewdog 🐶
// TODO_IN_THIS_COMMIT: does this 👆 need to be reconciled with the internal height of app?