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.

241 lines
6.6 KiB

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