1package chttp
2
3import (
4	"io/ioutil"
5	"net/http"
6	"strings"
7	"testing"
8
9	"github.com/flimzy/diff"
10
11	"github.com/go-kivik/kivik"
12)
13
14func TestResponseError(t *testing.T) {
15	tests := []struct {
16		name     string
17		resp     *http.Response
18		expected interface{}
19	}{
20		{
21			name:     "non error",
22			resp:     &http.Response{StatusCode: 200},
23			expected: nil,
24		},
25		{
26			name: "HEAD error",
27			resp: &http.Response{
28				StatusCode: 404,
29				Request:    &http.Request{Method: "HEAD"},
30				Body:       Body(""),
31			},
32			expected: &HTTPError{Code: 404},
33		},
34		{
35			name: "2.0.0 error",
36			resp: &http.Response{
37				StatusCode: 400,
38				Header: http.Header{
39					"Cache-Control":       {"must-revalidate"},
40					"Content-Length":      {"194"},
41					"Content-Type":        {"application/json"},
42					"Date":                {"Fri, 27 Oct 2017 15:34:07 GMT"},
43					"Server":              {"CouchDB/2.0.0 (Erlang OTP/17)"},
44					"X-Couch-Request-ID":  {"92d05bd015"},
45					"X-CouchDB-Body-Time": {"0"},
46				},
47				ContentLength: 194,
48				Body:          Body(`{"error":"illegal_database_name","reason":"Name: '_foo'. Only lowercase characters (a-z), digits (0-9), and any of the characters _, $, (, ), +, -, and / are allowed. Must begin with a letter."}`),
49				Request:       &http.Request{Method: "PUT"},
50			},
51			expected: &HTTPError{
52				Code:   400,
53				Reason: "Name: '_foo'. Only lowercase characters (a-z), digits (0-9), and any of the characters _, $, (, ), +, -, and / are allowed. Must begin with a letter.",
54			},
55		},
56		{
57			name: "invalid json error",
58			resp: &http.Response{
59				StatusCode: 400,
60				Header: http.Header{
61					"Server":         {"CouchDB/1.6.1 (Erlang OTP/17)"},
62					"Date":           {"Fri, 27 Oct 2017 15:42:34 GMT"},
63					"Content-Type":   {"application/json"},
64					"Content-Length": {"194"},
65					"Cache-Control":  {"must-revalidate"},
66				},
67				ContentLength: 194,
68				Body:          Body("invalid json"),
69				Request:       &http.Request{Method: "PUT"},
70			},
71			expected: &HTTPError{Code: 400},
72		},
73	}
74	for _, test := range tests {
75		t.Run(test.name, func(t *testing.T) {
76			err := ResponseError(test.resp)
77			if d := diff.Interface(test.expected, err); d != nil {
78				t.Error(d)
79			}
80		})
81	}
82}
83
84func xTestErrors(t *testing.T) {
85	type errTest struct {
86		Name           string
87		Func           func() error
88		ExpectedStatus int
89		ExpectedMsg    string
90	}
91	tests := []errTest{
92		{
93			Name: "200Response",
94			Func: func() error { return ResponseError(&http.Response{StatusCode: 200}) },
95		},
96		{
97			Name: "HEADError",
98			Func: func() error {
99				return ResponseError(&http.Response{
100					StatusCode: 400,
101					Request: &http.Request{
102						Method: "HEAD",
103					},
104					Body: ioutil.NopCloser(strings.NewReader("")),
105				})
106			},
107			ExpectedStatus: 400,
108			ExpectedMsg:    "Bad Request",
109		},
110		{
111			Name: "WithReason",
112			Func: func() error {
113				return ResponseError(&http.Response{
114					StatusCode: 404,
115					Request: &http.Request{
116						Method: "GET",
117					},
118					Header:        map[string][]string{"Content-Type": {"application/json"}},
119					ContentLength: 1, // Just non-zero for this test
120					Body:          ioutil.NopCloser(strings.NewReader(`{"code":404,"reason":"db_not_found"}`)),
121				})
122			},
123			ExpectedStatus: 404,
124			ExpectedMsg:    "Not Found: db_not_found",
125		},
126		{
127			Name: "WithoutReason",
128			Func: func() error {
129				return ResponseError(&http.Response{
130					StatusCode: 404,
131					Request: &http.Request{
132						Method: "GET",
133					},
134					Header:        map[string][]string{"Content-Type": {"application/json"}},
135					ContentLength: 1, // Just non-zero for this test
136					Body:          ioutil.NopCloser(strings.NewReader(`{"code":404}`)),
137				})
138			},
139			ExpectedStatus: 404,
140			ExpectedMsg:    "Not Found",
141		},
142		{
143			Name: "BadJSON",
144			Func: func() error {
145				return ResponseError(&http.Response{
146					StatusCode: 404,
147					Request: &http.Request{
148						Method: "GET",
149					},
150					Header:        map[string][]string{"Content-Type": {"application/json"}},
151					ContentLength: 1, // Just non-zero for this test
152					Body:          ioutil.NopCloser(strings.NewReader(`asdf`)),
153				})
154			},
155			ExpectedStatus: 404,
156			ExpectedMsg:    "Not Found: unknown (failed to decode error response: invalid character 'a' looking for beginning of value)",
157		},
158	}
159	for _, test := range tests {
160		func(test errTest) {
161			t.Run(test.Name, func(t *testing.T) {
162				err := test.Func()
163				if err == nil {
164					if test.ExpectedStatus == 0 {
165						return
166					}
167					t.Errorf("Got an error when none expected: %s", err)
168				}
169				if status := kivik.StatusCode(err); status != test.ExpectedStatus {
170					t.Errorf("Status. Expected %d, Actual %d", test.ExpectedStatus, status)
171				}
172				if msg := err.Error(); msg != test.ExpectedMsg {
173					t.Errorf("Error. Expected '%s', Actual '%s'", test.ExpectedMsg, msg)
174				}
175			})
176		}(test)
177	}
178}
179