diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ccd5d3f..9799a8c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -29,7 +29,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - go-version: ['1.11', '1.12', '1.13', '1.14', '1.15', '1.16', '1.17'] + go-version: ['1.13', '1.14', '1.15', '1.16', '1.17'] steps: - name: Set up Go ${{ matrix.go-version }} uses: actions/setup-go@v2 diff --git a/messenger.go b/messenger.go index bc23cba..7e574df 100644 --- a/messenger.go +++ b/messenger.go @@ -198,7 +198,7 @@ func (m *Messenger) ProfileByID(id int64, profileFields []string) (Profile, erro err = json.Unmarshal(content, &p) if err != nil { - return p, err + return p, NewUnmarshalError(err).WithContent(content) } if p == *new(Profile) { diff --git a/response.go b/response.go index a606f4f..d2db26c 100644 --- a/response.go +++ b/response.go @@ -87,9 +87,10 @@ func checkFacebookError(r io.Reader) error { var err error qr := QueryResponse{} - err = json.NewDecoder(r).Decode(&qr) + decoder := json.NewDecoder(r) + err = decoder.Decode(&qr) if err != nil { - return xerrors.Errorf("json unmarshal error: %w", err) + return NewUnmarshalError(err).WithReader(decoder.Buffered()) } if qr.Error != nil { return xerrors.Errorf("facebook error: %w", qr.Error) @@ -100,9 +101,9 @@ func checkFacebookError(r io.Reader) error { func getFacebookQueryResponse(r io.Reader) (QueryResponse, error) { qr := QueryResponse{} - err := json.NewDecoder(r).Decode(&qr) - if err != nil { - return qr, xerrors.Errorf("json unmarshal error: %w", err) + decoder := json.NewDecoder(r) + if err := decoder.Decode(&qr); err != nil { + return qr, NewUnmarshalError(err).WithReader(decoder.Buffered()) } if qr.Error != nil { return qr, xerrors.Errorf("facebook error: %w", qr.Error) diff --git a/response_test.go b/response_test.go index cd0a26a..258b639 100644 --- a/response_test.go +++ b/response_test.go @@ -1,7 +1,9 @@ package messenger import ( + "bytes" "encoding/json" + "errors" "testing" "github.com/stretchr/testify/assert" @@ -15,3 +17,17 @@ func Test_MarshalStructuredMessageElement(t *testing.T) { require.NoError(t, err) assert.JSONEq(t, string(data), `{"image_url":"", "subtitle":"", "title": "Title"}`) } + +func TestResponse_checkFacebookError_UnmarshalError(t *testing.T) { + r := bytes.NewReader([]byte("test error text")) + err := checkFacebookError(r) + assert.True(t, errors.Is(err, ErrUnmarshal)) + assert.Contains(t, err.Error(), "test error text") +} + +func TestResponse_getFacebookQueryResponse_UnmarshalError(t *testing.T) { + r := bytes.NewReader([]byte("test error text")) + _, err := getFacebookQueryResponse(r) + assert.True(t, errors.Is(err, ErrUnmarshal)) + assert.Contains(t, err.Error(), "test error text") +} diff --git a/unmarshal_error.go b/unmarshal_error.go new file mode 100644 index 0000000..f782454 --- /dev/null +++ b/unmarshal_error.go @@ -0,0 +1,47 @@ +package messenger + +import ( + "errors" + "fmt" + "io" + "io/ioutil" +) + +var ErrUnmarshal = errors.New("unmarshal error") + +type UnmarshalError struct { + Content []byte + ErrorText string + Err error +} + +func (u *UnmarshalError) Error() string { + return fmt.Sprintf("can not unmarshal content: %s; error: %s", string(u.Content), u.ErrorText) +} + +func (u *UnmarshalError) Unwrap() error { + return u.Err +} + +func NewUnmarshalError(err error) *UnmarshalError { + return &UnmarshalError{ + Err: ErrUnmarshal, + ErrorText: err.Error(), + } +} + +func (u *UnmarshalError) WithReader(reader io.Reader) *UnmarshalError { + content, _ := ioutil.ReadAll(reader) + u.Content = content + return u +} + +func (u *UnmarshalError) WithContent(content []byte) *UnmarshalError { + u.Content = content + return u +} + +func (u *UnmarshalError) WithErr(err error) *UnmarshalError { + u.Err = err + return u +} diff --git a/unmarshal_error_test.go b/unmarshal_error_test.go new file mode 100644 index 0000000..f8e160c --- /dev/null +++ b/unmarshal_error_test.go @@ -0,0 +1,57 @@ +package messenger + +import ( + "bytes" + "errors" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestNewUnmarshalError(t *testing.T) { + err := errors.New("some error") + unmarshalError := NewUnmarshalError(err) + assert.True(t, errors.Is(unmarshalError, ErrUnmarshal)) +} + +func TestUnmarshalError_Error(t *testing.T) { + err := errors.New("some error") + content := []byte("test content") + actual := NewUnmarshalError(err).WithContent(content).Error() + expected := "can not unmarshal content: test content; error: some error" + assert.Equal(t, expected, actual) +} + +func TestUnmarshalError_Unwrap(t *testing.T) { + err := errors.New("some error") + actual := NewUnmarshalError(err).Unwrap() + expected := ErrUnmarshal + assert.Equal(t, expected, actual) +} + +func TestUnmarshalError_WithContent(t *testing.T) { + err := errors.New("some error") + content := []byte("test content") + + actual := NewUnmarshalError(err).WithContent(content) + expected := &UnmarshalError{Err: ErrUnmarshal, Content: content, ErrorText: err.Error()} + assert.Equal(t, expected, actual) +} + +func TestUnmarshalError_WithReader(t *testing.T) { + err := errors.New("some error") + content := []byte("test content") + reader := bytes.NewReader(content) + + actual := NewUnmarshalError(err).WithReader(reader) + expected := &UnmarshalError{Err: ErrUnmarshal, Content: content, ErrorText: err.Error()} + assert.Equal(t, expected, actual) +} + +func TestUnmarshalError_WithErr(t *testing.T) { + someError := errors.New("some error") + otherError := errors.New("other error") + actual := NewUnmarshalError(someError).WithErr(otherError) + expected := &UnmarshalError{Err: otherError, ErrorText: someError.Error()} + assert.Equal(t, expected, actual) +}