github.com/line/ostracon@v1.0.10-0.20230328032236-7f20145f065d/rpc/client/mock/abci.go (about)

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