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.

250 lines
6.6 KiB

  1. package common_test
  2. import (
  3. "bytes"
  4. "encoding/json"
  5. "errors"
  6. "io"
  7. "io/ioutil"
  8. "net/http"
  9. "net/http/httptest"
  10. "reflect"
  11. "strings"
  12. "sync"
  13. "testing"
  14. "github.com/stretchr/testify/assert"
  15. "github.com/stretchr/testify/require"
  16. "github.com/tendermint/tmlibs/common"
  17. )
  18. func TestWriteSuccess(t *testing.T) {
  19. w := httptest.NewRecorder()
  20. common.WriteSuccess(w, "foo")
  21. assert.Equal(t, w.Code, 200, "should get a 200")
  22. }
  23. var blankErrResponse = new(common.ErrorResponse)
  24. func TestWriteError(t *testing.T) {
  25. tests := [...]struct {
  26. msg string
  27. code int
  28. }{
  29. 0: {
  30. msg: "this is a message",
  31. code: 419,
  32. },
  33. }
  34. for i, tt := range tests {
  35. w := httptest.NewRecorder()
  36. msg := tt.msg
  37. // First check without a defined code, should send back a 400
  38. common.WriteError(w, errors.New(msg))
  39. assert.Equal(t, w.Code, http.StatusBadRequest, "#%d: should get a 400", i)
  40. blob, err := ioutil.ReadAll(w.Body)
  41. if err != nil {
  42. assert.Fail(t, "expecting a successful ioutil.ReadAll", "#%d", i)
  43. continue
  44. }
  45. recv := new(common.ErrorResponse)
  46. if err := json.Unmarshal(blob, recv); err != nil {
  47. assert.Fail(t, "expecting a successful json.Unmarshal", "#%d", i)
  48. continue
  49. }
  50. assert.Equal(t, reflect.DeepEqual(recv, blankErrResponse), false, "expecting a non-blank error response")
  51. // Now test with an error that's .HTTPCode() int conforming
  52. // Reset w
  53. w = httptest.NewRecorder()
  54. common.WriteError(w, common.ErrorWithCode(errors.New("foo"), tt.code))
  55. assert.Equal(t, w.Code, tt.code, "case #%d", i)
  56. }
  57. }
  58. type marshalFailer struct{}
  59. var errFooFailed = errors.New("foo failed here")
  60. func (mf *marshalFailer) MarshalJSON() ([]byte, error) {
  61. return nil, errFooFailed
  62. }
  63. func TestWriteCode(t *testing.T) {
  64. codes := [...]int{
  65. 0: http.StatusOK,
  66. 1: http.StatusBadRequest,
  67. 2: http.StatusUnauthorized,
  68. 3: http.StatusInternalServerError,
  69. }
  70. for i, code := range codes {
  71. w := httptest.NewRecorder()
  72. common.WriteCode(w, "foo", code)
  73. assert.Equal(t, w.Code, code, "#%d", i)
  74. // Then for the failed JSON marshaling
  75. w = httptest.NewRecorder()
  76. common.WriteCode(w, &marshalFailer{}, code)
  77. wantCode := http.StatusBadRequest
  78. assert.Equal(t, w.Code, wantCode, "#%d", i)
  79. assert.True(t, strings.Contains(string(w.Body.Bytes()), errFooFailed.Error()),
  80. "#%d: expected %q in the error message", i, errFooFailed)
  81. }
  82. }
  83. type saver struct {
  84. Foo int `json:"foo" validate:"min=10"`
  85. Bar string `json:"bar"`
  86. }
  87. type rcloser struct {
  88. closeOnce sync.Once
  89. body *bytes.Buffer
  90. closeChan chan bool
  91. }
  92. var errAlreadyClosed = errors.New("already closed")
  93. func (rc *rcloser) Close() error {
  94. var err = errAlreadyClosed
  95. rc.closeOnce.Do(func() {
  96. err = nil
  97. rc.closeChan <- true
  98. close(rc.closeChan)
  99. })
  100. return err
  101. }
  102. func (rc *rcloser) Read(b []byte) (int, error) {
  103. return rc.body.Read(b)
  104. }
  105. var _ io.ReadCloser = (*rcloser)(nil)
  106. func makeReq(strBody string) (*http.Request, <-chan bool) {
  107. closeChan := make(chan bool, 1)
  108. buf := new(bytes.Buffer)
  109. buf.Write([]byte(strBody))
  110. req := &http.Request{
  111. Header: make(http.Header),
  112. Body: &rcloser{body: buf, closeChan: closeChan},
  113. }
  114. return req, closeChan
  115. }
  116. func TestParseRequestJSON(t *testing.T) {
  117. tests := [...]struct {
  118. body string
  119. wantErr bool
  120. useNil bool
  121. }{
  122. 0: {wantErr: true, body: ``},
  123. 1: {body: `{}`},
  124. 2: {body: `{"foo": 2}`}, // Not that the validate tags don't matter here since we are just parsing
  125. 3: {body: `{"foo": "abcd"}`, wantErr: true},
  126. 4: {useNil: true, wantErr: true},
  127. }
  128. for i, tt := range tests {
  129. req, closeChan := makeReq(tt.body)
  130. if tt.useNil {
  131. req.Body = nil
  132. }
  133. sav := new(saver)
  134. err := common.ParseRequestJSON(req, sav)
  135. if tt.wantErr {
  136. assert.NotEqual(t, err, nil, "#%d: want non-nil error", i)
  137. continue
  138. }
  139. assert.Equal(t, err, nil, "#%d: want nil error", i)
  140. wasClosed := <-closeChan
  141. assert.Equal(t, wasClosed, true, "#%d: should have invoked close", i)
  142. }
  143. }
  144. func TestFparseJSON(t *testing.T) {
  145. r1 := strings.NewReader(`{"foo": 1}`)
  146. sav := new(saver)
  147. require.Equal(t, common.FparseJSON(r1, sav), nil, "expecting successful parsing")
  148. r2 := strings.NewReader(`{"bar": "blockchain"}`)
  149. require.Equal(t, common.FparseJSON(r2, sav), nil, "expecting successful parsing")
  150. require.Equal(t, reflect.DeepEqual(sav, &saver{Foo: 1, Bar: "blockchain"}), true, "should have parsed both")
  151. // Now with a nil body
  152. require.NotEqual(t, nil, common.FparseJSON(nil, sav), "expecting a nil error report")
  153. }
  154. func TestFparseAndValidateJSON(t *testing.T) {
  155. r1 := strings.NewReader(`{"foo": 1}`)
  156. sav := new(saver)
  157. require.NotEqual(t, common.FparseAndValidateJSON(r1, sav), nil, "expecting validation to fail")
  158. r1 = strings.NewReader(`{"foo": 100}`)
  159. require.Equal(t, common.FparseJSON(r1, sav), nil, "expecting successful parsing")
  160. r2 := strings.NewReader(`{"bar": "blockchain"}`)
  161. require.Equal(t, common.FparseAndValidateJSON(r2, sav), nil, "expecting successful parsing")
  162. require.Equal(t, reflect.DeepEqual(sav, &saver{Foo: 100, Bar: "blockchain"}), true, "should have parsed both")
  163. // Now with a nil body
  164. require.NotEqual(t, nil, common.FparseJSON(nil, sav), "expecting a nil error report")
  165. }
  166. var blankSaver = new(saver)
  167. func TestParseAndValidateRequestJSON(t *testing.T) {
  168. tests := [...]struct {
  169. body string
  170. wantErr bool
  171. useNil bool
  172. }{
  173. 0: {wantErr: true, body: ``},
  174. 1: {body: `{}`, wantErr: true}, // Here it should fail since Foo doesn't meet the minimum value
  175. 2: {body: `{"foo": 2}`, wantErr: true}, // Here validation should fail
  176. 3: {body: `{"foo": "abcd"}`, wantErr: true},
  177. 4: {useNil: true, wantErr: true},
  178. 5: {body: `{"foo": 100}`}, // Must succeed
  179. }
  180. for i, tt := range tests {
  181. req, closeChan := makeReq(tt.body)
  182. if tt.useNil {
  183. req.Body = nil
  184. }
  185. sav := new(saver)
  186. err := common.ParseRequestAndValidateJSON(req, sav)
  187. if tt.wantErr {
  188. assert.NotEqual(t, err, nil, "#%d: want non-nil error", i)
  189. continue
  190. }
  191. assert.Equal(t, err, nil, "#%d: want nil error", i)
  192. assert.False(t, reflect.DeepEqual(blankSaver, sav), "#%d: expecting a set saver", i)
  193. wasClosed := <-closeChan
  194. assert.Equal(t, wasClosed, true, "#%d: should have invoked close", i)
  195. }
  196. }
  197. func TestErrorWithCode(t *testing.T) {
  198. tests := [...]struct {
  199. code int
  200. err error
  201. }{
  202. 0: {code: 500, err: errors.New("funky")},
  203. 1: {code: 406, err: errors.New("purist")},
  204. }
  205. for i, tt := range tests {
  206. errRes := common.ErrorWithCode(tt.err, tt.code)
  207. assert.Equal(t, errRes.Error(), tt.err.Error(), "#%d: expecting the error values to be equal", i)
  208. assert.Equal(t, errRes.Code, tt.code, "expecting the same status code", i)
  209. assert.Equal(t, errRes.HTTPCode(), tt.code, "expecting the same status code", i)
  210. }
  211. }