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 }