|
3 | 3 | package rest |
4 | 4 |
|
5 | 5 | import ( |
| 6 | + "encoding/json" |
6 | 7 | "io" |
| 8 | + "io/ioutil" |
7 | 9 | "net/http" |
8 | 10 | "net/http/httptest" |
| 11 | + "strconv" |
9 | 12 | "testing" |
10 | 13 |
|
11 | | - "github.com/cosmos/cosmos-sdk/client/context" |
12 | 14 | "github.com/stretchr/testify/require" |
| 15 | + "github.com/tendermint/tendermint/crypto" |
| 16 | + "github.com/tendermint/tendermint/crypto/secp256k1" |
13 | 17 |
|
| 18 | + "github.com/cosmos/cosmos-sdk/client/context" |
| 19 | + "github.com/cosmos/cosmos-sdk/codec" |
14 | 20 | "github.com/cosmos/cosmos-sdk/types" |
15 | 21 | ) |
16 | 22 |
|
@@ -138,6 +144,108 @@ func TestParseQueryHeight(t *testing.T) { |
138 | 144 | } |
139 | 145 | } |
140 | 146 |
|
| 147 | +func TestProcessPostResponse(t *testing.T) { |
| 148 | + // mock account |
| 149 | + // PubKey field ensures amino encoding is used first since standard |
| 150 | + // JSON encoding will panic on crypto.PubKey |
| 151 | + type mockAccount struct { |
| 152 | + Address types.AccAddress `json:"address"` |
| 153 | + Coins types.Coins `json:"coins"` |
| 154 | + PubKey crypto.PubKey `json:"public_key"` |
| 155 | + AccountNumber uint64 `json:"account_number"` |
| 156 | + Sequence uint64 `json:"sequence"` |
| 157 | + } |
| 158 | + |
| 159 | + // setup |
| 160 | + ctx := context.NewCLIContext() |
| 161 | + height := int64(194423) |
| 162 | + |
| 163 | + privKey := secp256k1.GenPrivKey() |
| 164 | + pubKey := privKey.PubKey() |
| 165 | + addr := types.AccAddress(pubKey.Address()) |
| 166 | + coins := types.NewCoins(types.NewCoin("atom", types.NewInt(100)), types.NewCoin("tree", types.NewInt(125))) |
| 167 | + accNumber := uint64(104) |
| 168 | + sequence := uint64(32) |
| 169 | + |
| 170 | + acc := mockAccount{addr, coins, pubKey, accNumber, sequence} |
| 171 | + cdc := codec.New() |
| 172 | + codec.RegisterCrypto(cdc) |
| 173 | + cdc.RegisterConcrete(&mockAccount{}, "cosmos-sdk/mockAccount", nil) |
| 174 | + ctx = ctx.WithCodec(cdc) |
| 175 | + |
| 176 | + // setup expected json responses with zero height |
| 177 | + jsonNoHeight, err := cdc.MarshalJSON(acc) |
| 178 | + require.Nil(t, err) |
| 179 | + require.NotNil(t, jsonNoHeight) |
| 180 | + jsonIndentNoHeight, err := cdc.MarshalJSONIndent(acc, "", " ") |
| 181 | + require.Nil(t, err) |
| 182 | + require.NotNil(t, jsonIndentNoHeight) |
| 183 | + |
| 184 | + // decode into map to order alphabetically |
| 185 | + m := make(map[string]interface{}) |
| 186 | + err = json.Unmarshal(jsonNoHeight, &m) |
| 187 | + require.Nil(t, err) |
| 188 | + jsonMap, err := json.Marshal(m) |
| 189 | + require.Nil(t, err) |
| 190 | + jsonWithHeight := append(append([]byte(`{"height":`), []byte(strconv.Itoa(int(height))+",")...), jsonMap[1:]...) |
| 191 | + jsonIndentMap, err := json.MarshalIndent(m, "", " ") |
| 192 | + jsonIndentWithHeight := append(append([]byte(`{`+"\n "+` "height": `), []byte(strconv.Itoa(int(height))+",")...), jsonIndentMap[1:]...) |
| 193 | + |
| 194 | + // check that negative height writes an error |
| 195 | + w := httptest.NewRecorder() |
| 196 | + ctx = ctx.WithHeight(-1) |
| 197 | + PostProcessResponse(w, ctx, acc) |
| 198 | + require.Equal(t, http.StatusInternalServerError, w.Code) |
| 199 | + |
| 200 | + // check that zero height returns expected response |
| 201 | + ctx = ctx.WithHeight(0) |
| 202 | + runPostProcessResponse(t, ctx, acc, jsonNoHeight, false) |
| 203 | + // check zero height with indent |
| 204 | + runPostProcessResponse(t, ctx, acc, jsonIndentNoHeight, true) |
| 205 | + // check that height returns expected response |
| 206 | + ctx = ctx.WithHeight(height) |
| 207 | + runPostProcessResponse(t, ctx, acc, jsonWithHeight, false) |
| 208 | + // check height with indent |
| 209 | + runPostProcessResponse(t, ctx, acc, jsonIndentWithHeight, true) |
| 210 | +} |
| 211 | + |
| 212 | +// asserts that ResponseRecorder returns the expected code and body |
| 213 | +// runs PostProcessResponse on the objects regular interface and on |
| 214 | +// the marshalled struct. |
| 215 | +func runPostProcessResponse(t *testing.T, ctx context.CLIContext, obj interface{}, |
| 216 | + expectedBody []byte, indent bool, |
| 217 | +) { |
| 218 | + if indent { |
| 219 | + ctx.Indent = indent |
| 220 | + } |
| 221 | + |
| 222 | + // test using regular struct |
| 223 | + w := httptest.NewRecorder() |
| 224 | + PostProcessResponse(w, ctx, obj) |
| 225 | + require.Equal(t, http.StatusOK, w.Code, w.Body) |
| 226 | + resp := w.Result() |
| 227 | + body, err := ioutil.ReadAll(resp.Body) |
| 228 | + require.Nil(t, err) |
| 229 | + require.Equal(t, expectedBody, body) |
| 230 | + |
| 231 | + var marshalled []byte |
| 232 | + if indent { |
| 233 | + marshalled, err = ctx.Codec.MarshalJSONIndent(obj, "", " ") |
| 234 | + } else { |
| 235 | + marshalled, err = ctx.Codec.MarshalJSON(obj) |
| 236 | + } |
| 237 | + require.Nil(t, err) |
| 238 | + |
| 239 | + // test using marshalled struct |
| 240 | + w = httptest.NewRecorder() |
| 241 | + PostProcessResponse(w, ctx, marshalled) |
| 242 | + require.Equal(t, http.StatusOK, w.Code, w.Body) |
| 243 | + resp = w.Result() |
| 244 | + body, err = ioutil.ReadAll(resp.Body) |
| 245 | + require.Nil(t, err) |
| 246 | + require.Equal(t, expectedBody, body) |
| 247 | +} |
| 248 | + |
141 | 249 | func mustNewRequest(t *testing.T, method, url string, body io.Reader) *http.Request { |
142 | 250 | req, err := http.NewRequest(method, url, body) |
143 | 251 | require.NoError(t, err) |
|
0 commit comments