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.

232 lines
6.2 KiB

8 years ago
  1. package mock
  2. import (
  3. abci "github.com/tendermint/tendermint/abci/types"
  4. "github.com/tendermint/tendermint/libs/bytes"
  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 bytes.HexBytes) (*ctypes.ResultABCIQuery, error) {
  25. return a.ABCIQueryWithOptions(path, data, client.DefaultABCIQueryOptions)
  26. }
  27. func (a ABCIApp) ABCIQueryWithOptions(
  28. path string,
  29. data bytes.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}) }()
  57. }
  58. return &ctypes.ResultBroadcastTx{
  59. Code: c.Code,
  60. Data: c.Data,
  61. Log: c.Log,
  62. Codespace: c.Codespace,
  63. Hash: tx.Hash(),
  64. }, nil
  65. }
  66. func (a ABCIApp) BroadcastTxSync(tx types.Tx) (*ctypes.ResultBroadcastTx, error) {
  67. c := a.App.CheckTx(abci.RequestCheckTx{Tx: tx})
  68. // and this gets written in a background thread...
  69. if !c.IsErr() {
  70. go func() { a.App.DeliverTx(abci.RequestDeliverTx{Tx: tx}) }()
  71. }
  72. return &ctypes.ResultBroadcastTx{
  73. Code: c.Code,
  74. Data: c.Data,
  75. Log: c.Log,
  76. Codespace: c.Codespace,
  77. Hash: tx.Hash(),
  78. }, nil
  79. }
  80. // ABCIMock will send all abci related request to the named app,
  81. // so you can test app behavior from a client without needing
  82. // an entire tendermint node
  83. type ABCIMock struct {
  84. Info Call
  85. Query Call
  86. BroadcastCommit Call
  87. Broadcast Call
  88. }
  89. func (m ABCIMock) ABCIInfo() (*ctypes.ResultABCIInfo, error) {
  90. res, err := m.Info.GetResponse(nil)
  91. if err != nil {
  92. return nil, err
  93. }
  94. return &ctypes.ResultABCIInfo{Response: res.(abci.ResponseInfo)}, nil
  95. }
  96. func (m ABCIMock) ABCIQuery(path string, data bytes.HexBytes) (*ctypes.ResultABCIQuery, error) {
  97. return m.ABCIQueryWithOptions(path, data, client.DefaultABCIQueryOptions)
  98. }
  99. func (m ABCIMock) ABCIQueryWithOptions(
  100. path string,
  101. data bytes.HexBytes,
  102. opts client.ABCIQueryOptions) (*ctypes.ResultABCIQuery, error) {
  103. res, err := m.Query.GetResponse(QueryArgs{path, data, opts.Height, opts.Prove})
  104. if err != nil {
  105. return nil, err
  106. }
  107. resQuery := res.(abci.ResponseQuery)
  108. return &ctypes.ResultABCIQuery{Response: resQuery}, nil
  109. }
  110. func (m ABCIMock) BroadcastTxCommit(tx types.Tx) (*ctypes.ResultBroadcastTxCommit, error) {
  111. res, err := m.BroadcastCommit.GetResponse(tx)
  112. if err != nil {
  113. return nil, err
  114. }
  115. return res.(*ctypes.ResultBroadcastTxCommit), nil
  116. }
  117. func (m ABCIMock) BroadcastTxAsync(tx types.Tx) (*ctypes.ResultBroadcastTx, error) {
  118. res, err := m.Broadcast.GetResponse(tx)
  119. if err != nil {
  120. return nil, err
  121. }
  122. return res.(*ctypes.ResultBroadcastTx), nil
  123. }
  124. func (m ABCIMock) BroadcastTxSync(tx types.Tx) (*ctypes.ResultBroadcastTx, error) {
  125. res, err := m.Broadcast.GetResponse(tx)
  126. if err != nil {
  127. return nil, err
  128. }
  129. return res.(*ctypes.ResultBroadcastTx), nil
  130. }
  131. // ABCIRecorder can wrap another type (ABCIApp, ABCIMock, or Client)
  132. // and record all ABCI related calls.
  133. type ABCIRecorder struct {
  134. Client client.ABCIClient
  135. Calls []Call
  136. }
  137. func NewABCIRecorder(client client.ABCIClient) *ABCIRecorder {
  138. return &ABCIRecorder{
  139. Client: client,
  140. Calls: []Call{},
  141. }
  142. }
  143. type QueryArgs struct {
  144. Path string
  145. Data bytes.HexBytes
  146. Height int64
  147. Prove bool
  148. }
  149. func (r *ABCIRecorder) addCall(call Call) {
  150. r.Calls = append(r.Calls, call)
  151. }
  152. func (r *ABCIRecorder) ABCIInfo() (*ctypes.ResultABCIInfo, error) {
  153. res, err := r.Client.ABCIInfo()
  154. r.addCall(Call{
  155. Name: "abci_info",
  156. Response: res,
  157. Error: err,
  158. })
  159. return res, err
  160. }
  161. func (r *ABCIRecorder) ABCIQuery(path string, data bytes.HexBytes) (*ctypes.ResultABCIQuery, error) {
  162. return r.ABCIQueryWithOptions(path, data, client.DefaultABCIQueryOptions)
  163. }
  164. func (r *ABCIRecorder) ABCIQueryWithOptions(
  165. path string,
  166. data bytes.HexBytes,
  167. opts client.ABCIQueryOptions) (*ctypes.ResultABCIQuery, error) {
  168. res, err := r.Client.ABCIQueryWithOptions(path, data, opts)
  169. r.addCall(Call{
  170. Name: "abci_query",
  171. Args: QueryArgs{path, data, opts.Height, opts.Prove},
  172. Response: res,
  173. Error: err,
  174. })
  175. return res, err
  176. }
  177. func (r *ABCIRecorder) BroadcastTxCommit(tx types.Tx) (*ctypes.ResultBroadcastTxCommit, error) {
  178. res, err := r.Client.BroadcastTxCommit(tx)
  179. r.addCall(Call{
  180. Name: "broadcast_tx_commit",
  181. Args: tx,
  182. Response: res,
  183. Error: err,
  184. })
  185. return res, err
  186. }
  187. func (r *ABCIRecorder) BroadcastTxAsync(tx types.Tx) (*ctypes.ResultBroadcastTx, error) {
  188. res, err := r.Client.BroadcastTxAsync(tx)
  189. r.addCall(Call{
  190. Name: "broadcast_tx_async",
  191. Args: tx,
  192. Response: res,
  193. Error: err,
  194. })
  195. return res, err
  196. }
  197. func (r *ABCIRecorder) BroadcastTxSync(tx types.Tx) (*ctypes.ResultBroadcastTx, error) {
  198. res, err := r.Client.BroadcastTxSync(tx)
  199. r.addCall(Call{
  200. Name: "broadcast_tx_sync",
  201. Args: tx,
  202. Response: res,
  203. Error: err,
  204. })
  205. return res, err
  206. }