github.com/go-kivik/kivik/v4@v4.3.2/x/kivikd/couchserver/db_test.go (about) 1 // Licensed under the Apache License, Version 2.0 (the "License"); you may not 2 // use this file except in compliance with the License. You may obtain a copy of 3 // the License at 4 // 5 // http://www.apache.org/licenses/LICENSE-2.0 6 // 7 // Unless required by applicable law or agreed to in writing, software 8 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 9 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 10 // License for the specific language governing permissions and limitations under 11 // the License. 12 13 //go:build !js 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 // expected := map[string]interface{}{ 118 // "committed_update_seq": 292786, 119 // "compact_running": false, 120 // "data_size": 65031503, 121 // "disk_format_version": 6, 122 // "instance_start_time": "1376269325408900", 123 // "purge_seq": 1, 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 }