package protoanalysis import ( "context" "testing" "github.com/stretchr/testify/require" ) func TestNestedMessages(t *testing.T) { packages, err := Parse(context.Background(), nil, "testdata/nested_messages") require.NoError(t, err) pkg := packages[0] require.Equal(t, "A", pkg.Messages[0].Name) require.Equal(t, "A_B", pkg.Messages[1].Name) require.Equal(t, "A_B_C", pkg.Messages[2].Name) } func TestQualifiedServiceTypes(t *testing.T) { packages, err := Parse(context.Background(), nil, "testdata/qualified_service") require.NoError(t, err) require.Len(t, packages, 1) require.Len(t, packages[0].Services, 1) require.Len(t, packages[0].Services[0].RPCFuncs, 2) require.Equal(t, ".qualified_service.PingRequest", packages[0].Services[0].RPCFuncs[0].RequestType) require.Equal(t, ".qualified_service.Outer.NestedRequest", packages[0].Services[0].RPCFuncs[1].RequestType) message, err := packages[0].MessageByName(".qualified_service.Outer.NestedRequest") require.NoError(t, err) require.Equal(t, "Outer_NestedRequest", message.Name) } func TestLiquidity(t *testing.T) { packages, err := Parse(context.Background(), nil, "testdata/liquidity") require.NoError(t, err) expected := Packages{ { Name: "tendermint.liquidity", Path: "testdata/liquidity", Files: Files{ { Path: "testdata/liquidity/genesis.proto", Dependencies: []string{"liquidity.proto", "gogoproto/gogo.proto"}, }, { Path: "testdata/liquidity/liquidity.proto", Dependencies: []string{"tx.proto", "gogoproto/gogo.proto", "cosmos_proto/coin.proto", "protoc-gen-openapiv2/options/annotations.proto"}, }, { Path: "testdata/liquidity/msg.proto", Dependencies: []string{"google/api/annotations.proto", "protoc-gen-openapiv2/options/annotations.proto", "tx.proto"}, }, { Path: "testdata/liquidity/query.proto", Dependencies: []string{"gogoproto/gogo.proto", "liquidity.proto", "google/api/annotations.proto", "cosmos_proto/pagination.proto", "protoc-gen-openapiv2/options/annotations.proto"}, }, { Path: "testdata/liquidity/tx.proto", Dependencies: []string{"gogoproto/gogo.proto", "cosmos_proto/coin.proto", "protoc-gen-openapiv2/options/annotations.proto"}, }, }, GoImportName: "github.com/tendermint/liquidity/x/liquidity/types", Messages: []Message{ {Name: "PoolRecord", Path: "testdata/liquidity/genesis.proto", HighestFieldNumber: 6, Fields: map[string]string{ "deposit_msg_states": "DepositMsgState", "pool": "Pool", "pool_batch": "PoolBatch", "pool_metadata": "PoolMetadata", "swap_msg_states": "SwapMsgState", "withdraw_msg_states": "WithdrawMsgState", }}, {Name: "GenesisState", Path: "testdata/liquidity/genesis.proto", HighestFieldNumber: 2, Fields: map[string]string{ "params": "Params", "pool_records": "PoolRecord", }}, {Name: "PoolType", Path: "testdata/liquidity/liquidity.proto", HighestFieldNumber: 5, Fields: map[string]string{ "description": "string", "id": "uint32", "max_reserve_coin_num": "uint32", "min_reserve_coin_num": "uint32", "name": "string", }}, {Name: "Params", Path: "testdata/liquidity/liquidity.proto", HighestFieldNumber: 9, Fields: map[string]string{ "init_pool_coin_mint_amount": "string", "max_order_amount_ratio": "bytes", "max_reserve_coin_amount": "string", "min_init_deposit_amount": "string", "pool_creation_fee": "cosmos.base.v1beta1.Coin", "pool_types": "PoolType", "swap_fee_rate": "bytes", "unit_batch_height": "uint32", "withdraw_fee_rate": "bytes", }}, {Name: "Pool", Path: "testdata/liquidity/liquidity.proto", HighestFieldNumber: 5, Fields: map[string]string{ "id": "uint64", "pool_coin_denom": "string", "reserve_account_address": "string", "reserve_coin_denoms": "string", "type_id": "uint32", }}, {Name: "PoolMetadata", Path: "testdata/liquidity/liquidity.proto", HighestFieldNumber: 3, Fields: map[string]string{ "pool_coin_total_supply": "cosmos.base.v1beta1.Coin", "pool_id": "uint64", "reserve_coins": "cosmos.base.v1beta1.Coin", }}, {Name: "PoolMetadataResponse", Path: "testdata/liquidity/liquidity.proto", HighestFieldNumber: 2, Fields: map[string]string{ "pool_coin_total_supply": "cosmos.base.v1beta1.Coin", "reserve_coins": "cosmos.base.v1beta1.Coin", }}, {Name: "PoolBatch", Path: "testdata/liquidity/liquidity.proto", HighestFieldNumber: 7, Fields: map[string]string{ "begin_height": "int64", "deposit_msg_index": "uint64", "executed": "bool", "index": "uint64", "pool_id": "uint64", "swap_msg_index": "uint64", "withdraw_msg_index": "uint64", }}, {Name: "PoolBatchResponse", Path: "testdata/liquidity/liquidity.proto", HighestFieldNumber: 6, Fields: map[string]string{ "begin_height": "int64", "deposit_msg_index": "uint64", "executed": "bool", "index": "uint64", "swap_msg_index": "uint64", "withdraw_msg_index": "uint64", }}, {Name: "DepositMsgState", Path: "testdata/liquidity/liquidity.proto", HighestFieldNumber: 6, Fields: map[string]string{ "executed": "bool", "msg": "MsgDepositWithinBatch", "msg_height": "int64", "msg_index": "uint64", "succeeded": "bool", "to_be_deleted": "bool", }}, {Name: "WithdrawMsgState", Path: "testdata/liquidity/liquidity.proto", HighestFieldNumber: 6, Fields: map[string]string{ "executed": "bool", "msg": "MsgWithdrawWithinBatch", "msg_height": "int64", "msg_index": "uint64", "succeeded": "bool", "to_be_deleted": "bool", }}, {Name: "SwapMsgState", Path: "testdata/liquidity/liquidity.proto", HighestFieldNumber: 10, Fields: map[string]string{ "exchanged_offer_coin": "cosmos.base.v1beta1.Coin", "executed": "bool", "msg": "MsgSwapWithinBatch", "msg_height": "int64", "msg_index": "uint64", "order_expiry_height": "int64", "remaining_offer_coin": "cosmos.base.v1beta1.Coin", "reserved_offer_coin_fee": "cosmos.base.v1beta1.Coin", "succeeded": "bool", "to_be_deleted": "bool", }}, {Name: "QueryLiquidityPoolRequest", Path: "testdata/liquidity/query.proto", HighestFieldNumber: 1, Fields: map[string]string{ "pool_id": "uint64", }}, {Name: "QueryLiquidityPoolResponse", Path: "testdata/liquidity/query.proto", HighestFieldNumber: 1, Fields: map[string]string{ "pool": "Pool", }}, {Name: "QueryLiquidityPoolBatchRequest", Path: "testdata/liquidity/query.proto", HighestFieldNumber: 1, Fields: map[string]string{ "pool_id": "uint64", }}, {Name: "QueryLiquidityPoolBatchResponse", Path: "testdata/liquidity/query.proto", HighestFieldNumber: 1, Fields: map[string]string{ "batch": "PoolBatch", }}, {Name: "QueryLiquidityPoolsRequest", Path: "testdata/liquidity/query.proto", HighestFieldNumber: 1, Fields: map[string]string{ "pagination": "cosmos.base.query.v1beta1.PageRequest", }}, {Name: "QueryLiquidityPoolsResponse", Path: "testdata/liquidity/query.proto", HighestFieldNumber: 2, Fields: map[string]string{ "pagination": "cosmos.base.query.v1beta1.PageResponse", "pools": "Pool", }}, {Name: "QueryParamsRequest", Path: "testdata/liquidity/query.proto", HighestFieldNumber: 0, Fields: map[string]string{}}, {Name: "QueryParamsResponse", Path: "testdata/liquidity/query.proto", HighestFieldNumber: 1, Fields: map[string]string{ "params": "Params", }}, {Name: "QueryPoolBatchSwapMsgsRequest", Path: "testdata/liquidity/query.proto", HighestFieldNumber: 2, Fields: map[string]string{ "pagination": "cosmos.base.query.v1beta1.PageRequest", "pool_id": "uint64", }}, {Name: "QueryPoolBatchSwapMsgRequest", Path: "testdata/liquidity/query.proto", HighestFieldNumber: 2, Fields: map[string]string{ "msg_index": "uint64", "pool_id": "uint64", }}, {Name: "QueryPoolBatchSwapMsgsResponse", Path: "testdata/liquidity/query.proto", HighestFieldNumber: 2, Fields: map[string]string{ "pagination": "cosmos.base.query.v1beta1.PageResponse", "swaps": "SwapMsgState", }}, {Name: "QueryPoolBatchSwapMsgResponse", Path: "testdata/liquidity/query.proto", HighestFieldNumber: 1, Fields: map[string]string{ "swap": "SwapMsgState", }}, {Name: "QueryPoolBatchDepositMsgsRequest", Path: "testdata/liquidity/query.proto", HighestFieldNumber: 2, Fields: map[string]string{ "pagination": "cosmos.base.query.v1beta1.PageRequest", "pool_id": "uint64", }}, {Name: "QueryPoolBatchDepositMsgRequest", Path: "testdata/liquidity/query.proto", HighestFieldNumber: 2, Fields: map[string]string{ "msg_index": "uint64", "pool_id": "uint64", }}, {Name: "QueryPoolBatchDepositMsgsResponse", Path: "testdata/liquidity/query.proto", HighestFieldNumber: 2, Fields: map[string]string{ "deposits": "DepositMsgState", "pagination": "cosmos.base.query.v1beta1.PageResponse", }}, {Name: "QueryPoolBatchDepositMsgResponse", Path: "testdata/liquidity/query.proto", HighestFieldNumber: 1, Fields: map[string]string{ "deposit": "DepositMsgState", }}, {Name: "QueryPoolBatchWithdrawMsgsRequest", Path: "testdata/liquidity/query.proto", HighestFieldNumber: 2, Fields: map[string]string{ "pagination": "cosmos.base.query.v1beta1.PageRequest", "pool_id": "uint64", }}, {Name: "QueryPoolBatchWithdrawMsgRequest", Path: "testdata/liquidity/query.proto", HighestFieldNumber: 2, Fields: map[string]string{ "msg_index": "uint64", "pool_id": "uint64", }}, {Name: "QueryPoolBatchWithdrawMsgsResponse", Path: "testdata/liquidity/query.proto", HighestFieldNumber: 2, Fields: map[string]string{ "pagination": "cosmos.base.query.v1beta1.PageResponse", "withdraws": "WithdrawMsgState", }}, {Name: "QueryPoolBatchWithdrawMsgResponse", Path: "testdata/liquidity/query.proto", HighestFieldNumber: 1, Fields: map[string]string{ "withdraw": "WithdrawMsgState", }}, {Name: "MsgCreatePool", Path: "testdata/liquidity/tx.proto", HighestFieldNumber: 4, Fields: map[string]string{ "deposit_coins": "cosmos.base.v1beta1.Coin", "pool_creator_address": "string", "pool_type_id": "uint32", }}, {Name: "MsgCreatePoolRequest", Path: "testdata/liquidity/tx.proto", HighestFieldNumber: 2, Fields: map[string]string{ "base_req": "BaseReq", "msg": "MsgCreatePool", }}, {Name: "MsgCreatePoolResponse", Path: "testdata/liquidity/tx.proto", HighestFieldNumber: 1, Fields: map[string]string{ "std_tx": "StdTx", }}, {Name: "MsgDepositWithinBatch", Path: "testdata/liquidity/tx.proto", HighestFieldNumber: 3, Fields: map[string]string{ "deposit_coins": "cosmos.base.v1beta1.Coin", "depositor_address": "string", "pool_id": "uint64", }}, {Name: "MsgDepositWithinBatchRequest", Path: "testdata/liquidity/tx.proto", HighestFieldNumber: 3, Fields: map[string]string{ "base_req": "BaseReq", "msg": "MsgDepositWithinBatch", "pool_id": "uint64", }}, {Name: "MsgDepositWithinBatchResponse", Path: "testdata/liquidity/tx.proto", HighestFieldNumber: 1, Fields: map[string]string{ "std_tx": "StdTx", }}, {Name: "MsgWithdrawWithinBatch", Path: "testdata/liquidity/tx.proto", HighestFieldNumber: 3, Fields: map[string]string{ "pool_coin": "cosmos.base.v1beta1.Coin", "pool_id": "uint64", "withdrawer_address": "string", }}, {Name: "MsgWithdrawWithinBatchRequest", Path: "testdata/liquidity/tx.proto", HighestFieldNumber: 3, Fields: map[string]string{ "base_req": "BaseReq", "msg": "MsgWithdrawWithinBatch", "pool_id": "uint64", }}, {Name: "MsgWithdrawWithinBatchResponse", Path: "testdata/liquidity/tx.proto", HighestFieldNumber: 1, Fields: map[string]string{ "std_tx": "StdTx", }}, {Name: "MsgSwapWithinBatch", Path: "testdata/liquidity/tx.proto", HighestFieldNumber: 7, Fields: map[string]string{ "demand_coin_denom": "string", "offer_coin": "cosmos.base.v1beta1.Coin", "offer_coin_fee": "cosmos.base.v1beta1.Coin", "order_price": "bytes", "pool_id": "uint64", "swap_requester_address": "string", "swap_type_id": "uint32", }}, {Name: "MsgSwapWithinBatchRequest", Path: "testdata/liquidity/tx.proto", HighestFieldNumber: 3, Fields: map[string]string{ "base_req": "BaseReq", "msg": "MsgSwapWithinBatch", "pool_id": "uint64", }}, {Name: "MsgSwapWithinBatchResponse", Path: "testdata/liquidity/tx.proto", HighestFieldNumber: 1, Fields: map[string]string{ "std_tx": "StdTx", }}, {Name: "BaseReq", Path: "testdata/liquidity/tx.proto", HighestFieldNumber: 11, Fields: map[string]string{ "account_number": "uint64", "chain_id": "string", "fees": "cosmos.base.v1beta1.Coin", "from": "string", "gas": "uint64", "gas_adjustment": "string", "gas_prices": "cosmos.base.v1beta1.DecCoin", "memo": "string", "sequence": "uint64", "simulate": "bool", "timeout_height": "uint64", }}, {Name: "Fee", Path: "testdata/liquidity/tx.proto", HighestFieldNumber: 2, Fields: map[string]string{ "amount": "cosmos.base.v1beta1.Coin", "gas": "uint64", }}, {Name: "PubKey", Path: "testdata/liquidity/tx.proto", HighestFieldNumber: 2, Fields: map[string]string{ "type": "string", "value": "string", }}, {Name: "Signature", Path: "testdata/liquidity/tx.proto", HighestFieldNumber: 4, Fields: map[string]string{ "account_number": "uint64", "pub_key": "PubKey", "sequence": "uint64", "signature": "string", }}, {Name: "StdTx", Path: "testdata/liquidity/tx.proto", HighestFieldNumber: 4, Fields: map[string]string{ "fee": "Fee", "memo": "string", "msg": "string", "signature": "Signature", }}, }, Services: []Service{ { Name: "MsgApi", RPCFuncs: []RPCFunc{ { Name: "CreatePoolApi", RequestType: "MsgCreatePoolRequest", ReturnsType: "MsgCreatePoolResponse", HTTPRules: []HTTPRule{ { Endpoint: "/liquidity/pools/{test}", Params: []string{"test"}, HasBody: true, BodyFields: map[string]string{ "base_req": "BaseReq", "msg": "MsgCreatePool", }, }, }, }, { Name: "DepositWithinBatchApi", RequestType: "MsgDepositWithinBatchRequest", ReturnsType: "MsgDepositWithinBatchResponse", HTTPRules: []HTTPRule{ { Endpoint: "/liquidity/pools/{pool_id}/batch/deposits", Params: []string{"pool_id"}, HasBody: true, BodyFields: map[string]string{ "base_req": "BaseReq", "msg": "MsgDepositWithinBatch", }, }, }, }, { Name: "WithdrawWithinBatchApi", RequestType: "MsgWithdrawWithinBatchRequest", ReturnsType: "MsgWithdrawWithinBatchResponse", HTTPRules: []HTTPRule{ { Endpoint: "/liquidity/pools/{pool_id}/batch/withdraws", Params: []string{"pool_id"}, HasBody: true, BodyFields: map[string]string{ "base_req": "BaseReq", "msg": "MsgWithdrawWithinBatch", }, }, }, }, { Name: "SwapApi", RequestType: "MsgSwapWithinBatchRequest", ReturnsType: "MsgSwapWithinBatchResponse", HTTPRules: []HTTPRule{ { Endpoint: "/liquidity/pools/{pool_id}/batch/swaps", Params: []string{"pool_id"}, HasQuery: true, // NOTE: this should never happen in this case, but this was done to test protoanalysis detection in SwapApi proto definition. HasBody: true, BodyFields: map[string]string{ "base_req": "BaseReq", "msg": "MsgSwapWithinBatch", }, }, }, }, }, }, { Name: "Query", RPCFuncs: []RPCFunc{ { Name: "LiquidityPools", RequestType: "QueryLiquidityPoolsRequest", ReturnsType: "QueryLiquidityPoolsResponse", HTTPRules: []HTTPRule{ { Endpoint: "/liquidity/pools", HasQuery: true, QueryFields: map[string]string{ "pagination": "cosmos.base.query.v1beta1.PageRequest", }, }, }, }, { Name: "LiquidityPool", RequestType: "QueryLiquidityPoolRequest", ReturnsType: "QueryLiquidityPoolResponse", HTTPRules: []HTTPRule{ { Endpoint: "/liquidity/pools/{pool_id}", Params: []string{"pool_id"}, }, }, }, { Name: "LiquidityPoolBatch", RequestType: "QueryLiquidityPoolBatchRequest", ReturnsType: "QueryLiquidityPoolBatchResponse", HTTPRules: []HTTPRule{ { Endpoint: "/liquidity/pools/{pool_id}/batch", Params: []string{"pool_id"}, }, }, }, { Name: "PoolBatchSwapMsgs", RequestType: "QueryPoolBatchSwapMsgsRequest", ReturnsType: "QueryPoolBatchSwapMsgsResponse", HTTPRules: []HTTPRule{ { Endpoint: "/liquidity/pools/{pool_id}/batch/swaps", Params: []string{"pool_id"}, HasQuery: true, QueryFields: map[string]string{ "pagination": "cosmos.base.query.v1beta1.PageRequest", }, }, }, }, { Name: "PoolBatchSwapMsg", RequestType: "QueryPoolBatchSwapMsgRequest", ReturnsType: "QueryPoolBatchSwapMsgResponse", HTTPRules: []HTTPRule{ { Endpoint: "/liquidity/pools/{pool_id}/batch/swaps/{msg_index}", Params: []string{"pool_id", "msg_index"}, }, }, }, { Name: "PoolBatchDepositMsgs", RequestType: "QueryPoolBatchDepositMsgsRequest", ReturnsType: "QueryPoolBatchDepositMsgsResponse", HTTPRules: []HTTPRule{ { Endpoint: "/liquidity/pools/{pool_id}/batch/deposits", Params: []string{"pool_id"}, HasQuery: true, QueryFields: map[string]string{ "pagination": "cosmos.base.query.v1beta1.PageRequest", }, }, }, }, { Name: "PoolBatchDepositMsg", RequestType: "QueryPoolBatchDepositMsgRequest", ReturnsType: "QueryPoolBatchDepositMsgResponse", HTTPRules: []HTTPRule{ { Endpoint: "/liquidity/pools/{pool_id}/batch/deposits/{msg_index}", Params: []string{"pool_id", "msg_index"}, }, }, }, { Name: "PoolBatchWithdrawMsgs", RequestType: "QueryPoolBatchWithdrawMsgsRequest", ReturnsType: "QueryPoolBatchWithdrawMsgsResponse", HTTPRules: []HTTPRule{ { Endpoint: "/liquidity/pools/{pool_id}/batch/withdraws", Params: []string{"pool_id"}, HasQuery: true, QueryFields: map[string]string{ "pagination": "cosmos.base.query.v1beta1.PageRequest", }, }, }, }, { Name: "PoolBatchWithdrawMsg", RequestType: "QueryPoolBatchWithdrawMsgRequest", ReturnsType: "QueryPoolBatchWithdrawMsgResponse", HTTPRules: []HTTPRule{ { Endpoint: "/liquidity/pools/{pool_id}/batch/withdraws/{msg_index}", Params: []string{"pool_id", "msg_index"}, }, }, }, { Name: "Params", RequestType: "QueryParamsRequest", ReturnsType: "QueryParamsResponse", HTTPRules: []HTTPRule{ { Endpoint: "/liquidity/params", }, }, }, }, }, { Name: "Msg", RPCFuncs: []RPCFunc{ { Name: "CreatePool", RequestType: "MsgCreatePool", ReturnsType: "MsgCreatePoolResponse", }, { Name: "DepositWithinBatch", RequestType: "MsgDepositWithinBatch", ReturnsType: "MsgDepositWithinBatchResponse", }, { Name: "WithdrawWithinBatch", RequestType: "MsgWithdrawWithinBatch", ReturnsType: "MsgWithdrawWithinBatchResponse", }, { Name: "Swap", RequestType: "MsgSwapWithinBatch", ReturnsType: "MsgSwapWithinBatchResponse", }, }, }, }, }, } require.Equal(t, expected, packages) } func TestSkipInternalPath(t *testing.T) { packages, err := Parse(context.Background(), nil, "testdata/internal") require.NoError(t, err) require.Len(t, packages, 0) }