...

Source file src/github.com/go-kivik/kivik/v4/x/kivikd/couchserver/db_test.go

Documentation: github.com/go-kivik/kivik/v4/x/kivikd/couchserver

     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  }
   232  

View as plain text