1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package couchserver
16
17 import (
18 "context"
19 "encoding/json"
20 "errors"
21 "io/ioutil"
22 "net/http"
23 "net/http/httptest"
24 "testing"
25
26 "gitlab.com/flimzy/testy"
27
28 "github.com/go-kivik/kivik/v4"
29 internal "github.com/go-kivik/kivik/v4/int/errors"
30 )
31
32 type mockCreator struct {
33 backend
34 }
35
36 func (p *mockCreator) CreateDB(_ context.Context, _ string, _ ...kivik.Option) error {
37 return nil
38 }
39
40 type errCreator struct {
41 backend
42 }
43
44 func (p *errCreator) CreateDB(_ context.Context, _ string, _ ...kivik.Option) error {
45 return errors.New("failure")
46 }
47
48 func TestPutDB(t *testing.T) {
49 t.Run("Success", func(t *testing.T) {
50 h := &Handler{client: &mockCreator{}}
51 resp := callEndpoint(h, "PUT", "/foo")
52 if resp.StatusCode != http.StatusOK {
53 t.Errorf("Expected 200, got %s", resp.Status)
54 }
55 expected := map[string]interface{}{
56 "ok": true,
57 }
58 if d := testy.DiffAsJSON(expected, resp.Body); d != nil {
59 t.Error(d)
60 }
61 })
62 t.Run("Error", func(t *testing.T) {
63 h := &Handler{client: &errCreator{}}
64 resp := callEndpoint(h, "PUT", "/foo")
65 if resp.StatusCode != http.StatusInternalServerError {
66 t.Errorf("Expected 500, got %s", resp.Status)
67 }
68 })
69 }
70
71 type mockNotExists struct{ backend }
72
73 func (d *mockNotExists) DBExists(_ context.Context, _ string, _ ...kivik.Option) (bool, error) {
74 return false, nil
75 }
76
77 type mockExists struct{ backend }
78
79 func (d *mockExists) DBExists(_ context.Context, _ string, _ ...kivik.Option) (bool, error) {
80 return true, nil
81 }
82
83 type mockErrExists struct{ backend }
84
85 func (d *mockErrExists) DBExists(_ context.Context, _ string, _ ...kivik.Option) (bool, error) {
86 return false, errors.New("failure")
87 }
88
89 func TestHeadDB(t *testing.T) {
90 t.Run("NotExists", func(t *testing.T) {
91 h := &Handler{client: &mockNotExists{}}
92 resp := callEndpoint(h, "HEAD", "/notexist")
93 if resp.StatusCode != http.StatusNotFound {
94 t.Errorf("Expected 404/NotFound, got %s", resp.Status)
95 }
96 })
97
98 t.Run("Exists", func(t *testing.T) {
99 h := &Handler{client: &mockExists{}}
100 resp := callEndpoint(h, "HEAD", "/exists")
101 if resp.StatusCode != http.StatusOK {
102 t.Errorf("Expected 200/OK, got %s", resp.Status)
103 }
104 })
105
106 t.Run("Error", func(t *testing.T) {
107 h := &Handler{client: &mockErrExists{}}
108 resp := callEndpoint(h, "HEAD", "/exists")
109 if resp.StatusCode != http.StatusInternalServerError {
110 t.Errorf("Expected 500, got %s", resp.Status)
111 }
112 })
113 }
114
115 type mockFoundDB struct{ db }
116
117
118
119
120
121
122
123
124
125
126 var testStats = &kivik.DBStats{
127 Name: "receipts",
128 CompactRunning: false,
129 DocCount: 6146,
130 DeletedCount: 64637,
131 UpdateSeq: "292786",
132 DiskSize: 137433211,
133 ActiveSize: 1,
134 }
135
136 func (d *mockFoundDB) Stats(_ context.Context) (*kivik.DBStats, error) {
137 return testStats, nil
138 }
139
140 type mockGetFound struct{ backend }
141
142 func (c *mockGetFound) DB(_ context.Context, _ string, _ ...kivik.Option) (db, error) {
143 return &mockFoundDB{}, nil
144 }
145
146 type mockGetNotFound struct{ backend }
147
148 func (c *mockGetNotFound) DB(_ context.Context, _ string, _ ...kivik.Option) (db, error) {
149 return nil, &internal.Error{Status: http.StatusNotFound, Message: "database not found"}
150 }
151
152 type errClient struct{ backend }
153
154 func (c *errClient) DB(_ context.Context, _ string, _ ...kivik.Option) (db, error) {
155 return nil, errors.New("failure")
156 }
157
158 func TestGetDB(t *testing.T) {
159 t.Run("Endpoint exists for GET", func(t *testing.T) {
160 h := &Handler{client: &mockGetNotFound{}}
161 resp := callEndpointEndClose(h, "/exists")
162 if resp.StatusCode == http.StatusMethodNotAllowed {
163 t.Error("Expected another response than method not allowed")
164 }
165 })
166
167 t.Run("Not found", func(t *testing.T) {
168 h := &Handler{client: &mockGetNotFound{}}
169 resp := callEndpointEndClose(h, "/notexists")
170 if resp.StatusCode != http.StatusNotFound {
171 t.Errorf("Expected 404, got %s", resp.Status)
172 }
173 })
174
175 t.Run("Found", func(t *testing.T) {
176 h := &Handler{client: &mockGetFound{}}
177 resp := callEndpointEndClose(h, "/asdf")
178 if resp.StatusCode != http.StatusOK {
179 t.Errorf("Expected 200, got %s", resp.Status)
180 }
181 })
182
183 t.Run("Error", func(t *testing.T) {
184 h2 := &Handler{client: &errClient{}}
185 resp := callEndpointEndClose(h2, "/error")
186 if resp.StatusCode != http.StatusInternalServerError {
187 t.Errorf("Expected 500, got %s", resp.Status)
188 }
189 })
190
191 t.Run("Response", func(t *testing.T) {
192 h := &Handler{client: &mockGetFound{}}
193 resp := callEndpoint(h, "GET", "/asdf")
194 t.Cleanup(func() {
195 _ = resp.Body.Close()
196 })
197 if cc := resp.Header.Get("Cache-Control"); cc != "must-revalidate" {
198 t.Errorf("Cache-Control header doesn't match, got %s", cc)
199 }
200 if contentType := resp.Header.Get("Content-Type"); contentType != "application/json" {
201 t.Errorf("Content-Type header doesn't match, got %s", contentType)
202 }
203 var body interface{}
204 buf, err := ioutil.ReadAll(resp.Body)
205 if err != nil {
206 t.Fatal(err)
207 }
208 if err = json.Unmarshal(buf, &body); err != nil {
209 t.Errorf("JSON error, %s", err)
210 }
211 expected := testStats
212 if difftext := testy.DiffAsJSON(expected, body); difftext != nil {
213 t.Error(difftext)
214 }
215 })
216 }
217
218 func callEndpoint(h *Handler, method string, path string) *http.Response {
219 w := httptest.NewRecorder()
220 req := httptest.NewRequest(method, path, nil)
221 handler := h.Main()
222 handler.ServeHTTP(w, req)
223 resp := w.Result()
224 return resp
225 }
226
227 func callEndpointEndClose(h *Handler, path string) *http.Response {
228 resp := callEndpoint(h, http.MethodGet, path)
229 _ = resp.Body.Close()
230 return resp
231 }
232
View as plain text