You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

220 lines
6.1 KiB

8 years ago
  1. package mock
  2. import (
  3. abci "github.com/tendermint/tendermint/abci/types"
  4. cmn "github.com/tendermint/tendermint/libs/common"
  5. "github.com/tendermint/tendermint/proxy"
  6. "github.com/tendermint/tendermint/rpc/client"
  7. ctypes "github.com/tendermint/tendermint/rpc/core/types"
  8. "github.com/tendermint/tendermint/types"
  9. )
  10. // ABCIApp will send all abci related request to the named app,
  11. // so you can test app behavior from a client without needing
  12. // an entire tendermint node
  13. type ABCIApp struct {
  14. App abci.Application
  15. }
  16. var (
  17. _ client.ABCIClient = ABCIApp{}
  18. _ client.ABCIClient = ABCIMock{}
  19. _ client.ABCIClient = (*ABCIRecorder)(nil)
  20. )
  21. func (a ABCIApp) ABCIInfo() (*ctypes.ResultABCIInfo, error) {
  22. return &ctypes.ResultABCIInfo{Response: a.App.Info(proxy.RequestInfo)}, nil
  23. }
  24. func (a ABCIApp) ABCIQuery(path string, data cmn.HexBytes) (*ctypes.ResultABCIQuery, error) {
  25. return a.ABCIQueryWithOptions(path, data, client.DefaultABCIQueryOptions)
  26. }
  27. func (a ABCIApp) ABCIQueryWithOptions(
  28. path string,
  29. data cmn.HexBytes,
  30. opts client.ABCIQueryOptions) (*ctypes.ResultABCIQuery, error) {
  31. q := a.App.Query(abci.RequestQuery{
  32. Data: data,
  33. Path: path,
  34. Height: opts.Height,
  35. Prove: opts.Prove,
  36. })
  37. return &ctypes.ResultABCIQuery{Response: q}, nil
  38. }
  39. // NOTE: Caller should call a.App.Commit() separately,
  40. // this function does not actually wait for a commit.
  41. // TODO: Make it wait for a commit and set res.Height appropriately.
  42. func (a ABCIApp) BroadcastTxCommit(tx types.Tx) (*ctypes.ResultBroadcastTxCommit, error) {
  43. res := ctypes.ResultBroadcastTxCommit{}
  44. res.CheckTx = a.App.CheckTx(abci.RequestCheckTx{Tx: tx})
  45. if res.CheckTx.IsErr() {
  46. return &res, nil
  47. }
  48. res.DeliverTx = a.App.DeliverTx(abci.RequestDeliverTx{Tx: tx})
  49. res.Height = -1 // TODO
  50. return &res, nil
  51. }
  52. func (a ABCIApp) BroadcastTxAsync(tx types.Tx) (*ctypes.ResultBroadcastTx, error) {
  53. c := a.App.CheckTx(abci.RequestCheckTx{Tx: tx})
  54. // and this gets written in a background thread...
  55. if !c.IsErr() {
  56. go func() { a.App.DeliverTx(abci.RequestDeliverTx{Tx: tx}) }() // nolint: errcheck
  57. }
  58. return &ctypes.ResultBroadcastTx{Code: c.Code, Data: c.Data, Log: c.Log, Hash: tx.Hash()}, nil
  59. }
  60. func (a ABCIApp) BroadcastTxSync(tx types.Tx) (*ctypes.ResultBroadcastTx, error) {
  61. c := a.App.CheckTx(abci.RequestCheckTx{Tx: tx})
  62. // and this gets written in a background thread...
  63. if !c.IsErr() {
  64. go func() { a.App.DeliverTx(abci.RequestDeliverTx{Tx: tx}) }() // nolint: errcheck
  65. }
  66. return &ctypes.ResultBroadcastTx{Code: c.Code, Data: c.Data, Log: c.Log, Hash: tx.Hash()}, nil
  67. }
  68. // ABCIMock will send all abci related request to the named app,
  69. // so you can test app behavior from a client without needing
  70. // an entire tendermint node
  71. type ABCIMock struct {
  72. Info Call
  73. Query Call
  74. BroadcastCommit Call
  75. Broadcast Call
  76. }
  77. func (m ABCIMock) ABCIInfo() (*ctypes.ResultABCIInfo, error) {
  78. res, err := m.Info.GetResponse(nil)
  79. if err != nil {
  80. return nil, err
  81. }
  82. return &ctypes.ResultABCIInfo{Response: res.(abci.ResponseInfo)}, nil
  83. }
  84. func (m ABCIMock) ABCIQuery(path string, data cmn.HexBytes) (*ctypes.ResultABCIQuery, error) {
  85. return m.ABCIQueryWithOptions(path, data, client.DefaultABCIQueryOptions)
  86. }
  87. func (m ABCIMock) ABCIQueryWithOptions(
  88. path string,
  89. data cmn.HexBytes,
  90. opts client.ABCIQueryOptions) (*ctypes.ResultABCIQuery, error) {
  91. res, err := m.Query.GetResponse(QueryArgs{path, data, opts.Height, opts.Prove})
  92. if err != nil {
  93. return nil, err
  94. }
  95. resQuery := res.(abci.ResponseQuery)
  96. return &ctypes.ResultABCIQuery{Response: resQuery}, nil
  97. }
  98. func (m ABCIMock) BroadcastTxCommit(tx types.Tx) (*ctypes.ResultBroadcastTxCommit, error) {
  99. res, err := m.BroadcastCommit.GetResponse(tx)
  100. if err != nil {
  101. return nil, err
  102. }
  103. return res.(*ctypes.ResultBroadcastTxCommit), nil
  104. }
  105. func (m ABCIMock) BroadcastTxAsync(tx types.Tx) (*ctypes.ResultBroadcastTx, error) {
  106. res, err := m.Broadcast.GetResponse(tx)
  107. if err != nil {
  108. return nil, err
  109. }
  110. return res.(*ctypes.ResultBroadcastTx), nil
  111. }
  112. func (m ABCIMock) BroadcastTxSync(tx types.Tx) (*ctypes.ResultBroadcastTx, error) {
  113. res, err := m.Broadcast.GetResponse(tx)
  114. if err != nil {
  115. return nil, err
  116. }
  117. return res.(*ctypes.ResultBroadcastTx), nil
  118. }
  119. // ABCIRecorder can wrap another type (ABCIApp, ABCIMock, or Client)
  120. // and record all ABCI related calls.
  121. type ABCIRecorder struct {
  122. Client client.ABCIClient
  123. Calls []Call
  124. }
  125. func NewABCIRecorder(client client.ABCIClient) *ABCIRecorder {
  126. return &ABCIRecorder{
  127. Client: client,
  128. Calls: []Call{},
  129. }
  130. }
  131. type QueryArgs struct {
  132. Path string
  133. Data cmn.HexBytes
  134. Height int64
  135. Prove bool
  136. }
  137. func (r *ABCIRecorder) addCall(call Call) {
  138. r.Calls = append(r.Calls, call)
  139. }
  140. func (r *ABCIRecorder) ABCIInfo() (*ctypes.ResultABCIInfo, error) {
  141. res, err := r.Client.ABCIInfo()
  142. r.addCall(Call{
  143. Name: "abci_info",
  144. Response: res,
  145. Error: err,
  146. })
  147. return res, err
  148. }
  149. func (r *ABCIRecorder) ABCIQuery(path string, data cmn.HexBytes) (*ctypes.ResultABCIQuery, error) {
  150. return r.ABCIQueryWithOptions(path, data, client.DefaultABCIQueryOptions)
  151. }
  152. func (r *ABCIRecorder) ABCIQueryWithOptions(
  153. path string,
  154. data cmn.HexBytes,
  155. opts client.ABCIQueryOptions) (*ctypes.ResultABCIQuery, error) {
  156. res, err := r.Client.ABCIQueryWithOptions(path, data, opts)
  157. r.addCall(Call{
  158. Name: "abci_query",
  159. Args: QueryArgs{path, data, opts.Height, opts.Prove},
  160. Response: res,
  161. Error: err,
  162. })
  163. return res, err
  164. }
  165. func (r *ABCIRecorder) BroadcastTxCommit(tx types.Tx) (*ctypes.ResultBroadcastTxCommit, error) {
  166. res, err := r.Client.BroadcastTxCommit(tx)
  167. r.addCall(Call{
  168. Name: "broadcast_tx_commit",
  169. Args: tx,
  170. Response: res,
  171. Error: err,
  172. })
  173. return res, err
  174. }
  175. func (r *ABCIRecorder) BroadcastTxAsync(tx types.Tx) (*ctypes.ResultBroadcastTx, error) {
  176. res, err := r.Client.BroadcastTxAsync(tx)
  177. r.addCall(Call{
  178. Name: "broadcast_tx_async",
  179. Args: tx,
  180. Response: res,
  181. Error: err,
  182. })
  183. return res, err
  184. }
  185. func (r *ABCIRecorder) BroadcastTxSync(tx types.Tx) (*ctypes.ResultBroadcastTx, error) {
  186. res, err := r.Client.BroadcastTxSync(tx)
  187. r.addCall(Call{
  188. Name: "broadcast_tx_sync",
  189. Args: tx,
  190. Response: res,
  191. Error: err,
  192. })
  193. return res, err
  194. }