...

Source file src/github.com/go-kivik/kivik/v4/x/fsdb/cdb/document_test.go

Documentation: github.com/go-kivik/kivik/v4/x/fsdb/cdb

     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  package cdb
    14  
    15  import (
    16  	"context"
    17  	"net/http"
    18  	"os"
    19  	"regexp"
    20  	"runtime"
    21  	"testing"
    22  
    23  	"gitlab.com/flimzy/testy"
    24  
    25  	"github.com/go-kivik/kivik/v4"
    26  	internal "github.com/go-kivik/kivik/v4/int/errors"
    27  	"github.com/go-kivik/kivik/v4/x/fsdb/filesystem"
    28  )
    29  
    30  const isGopherJS117 = runtime.GOARCH == "js"
    31  
    32  func TestDocumentPersist(t *testing.T) {
    33  	if isGopherJS117 {
    34  		t.Skip("Tests broken for GopherJS 1.17")
    35  	}
    36  	type tt struct {
    37  		path   string
    38  		doc    *Document
    39  		status int
    40  		err    string
    41  	}
    42  	tests := testy.NewTable()
    43  	tests.Add("nil doc", func(t *testing.T) interface{} {
    44  		var tmpdir string
    45  		tests.Cleanup(testy.TempDir(t, &tmpdir))
    46  
    47  		return tt{
    48  			path:   tmpdir,
    49  			status: http.StatusBadRequest,
    50  			err:    "document has no revisions",
    51  		}
    52  	})
    53  	tests.Add("no revs", func(t *testing.T) interface{} {
    54  		var tmpdir string
    55  		tests.Cleanup(testy.TempDir(t, &tmpdir))
    56  
    57  		cdb := New(tmpdir, filesystem.Default())
    58  
    59  		return tt{
    60  			path:   tmpdir,
    61  			doc:    cdb.NewDocument("foo"),
    62  			status: http.StatusBadRequest,
    63  			err:    "document has no revisions",
    64  		}
    65  	})
    66  	tests.Add("new doc, one rev", func(t *testing.T) interface{} {
    67  		var tmpdir string
    68  		tests.Cleanup(testy.TempDir(t, &tmpdir))
    69  
    70  		cdb := New(tmpdir, filesystem.Default())
    71  		doc := cdb.NewDocument("foo")
    72  		rev, err := cdb.NewRevision(map[string]string{
    73  			"value": "bar",
    74  		})
    75  		if err != nil {
    76  			t.Fatal(err)
    77  		}
    78  		if _, err := doc.AddRevision(context.TODO(), rev, kivik.Params(nil)); err != nil {
    79  			t.Fatal(err)
    80  		}
    81  
    82  		return tt{
    83  			path: tmpdir,
    84  			doc:  doc,
    85  		}
    86  	})
    87  	tests.Add("update existing doc", func(t *testing.T) interface{} {
    88  		tmpdir := testy.CopyTempDir(t, "testdata/persist.update", 0)
    89  		tests.Cleanup(func() error {
    90  			return os.RemoveAll(tmpdir)
    91  		})
    92  
    93  		cdb := New(tmpdir)
    94  		doc, err := cdb.OpenDocID("foo", kivik.Params(nil))
    95  		if err != nil {
    96  			t.Fatal(err)
    97  		}
    98  		rev, err := cdb.NewRevision(map[string]interface{}{
    99  			"_rev":  "1-xxx",
   100  			"value": "bar",
   101  			"_revisions": map[string]interface{}{
   102  				"start": 2,
   103  				"ids":   []string{"yyy", "xxx"},
   104  			},
   105  		})
   106  		if err != nil {
   107  			t.Fatal(err)
   108  		}
   109  		if _, err := doc.AddRevision(context.TODO(), rev, kivik.Params(nil)); err != nil {
   110  			t.Fatal(err)
   111  		}
   112  
   113  		return tt{
   114  			path: tmpdir,
   115  			doc:  doc,
   116  		}
   117  	})
   118  	tests.Add("update existing doc with attachments", func(t *testing.T) interface{} {
   119  		tmpdir := testy.CopyTempDir(t, "testdata/persist.att", 0)
   120  		tests.Cleanup(func() error {
   121  			return os.RemoveAll(tmpdir)
   122  		})
   123  
   124  		cdb := New(tmpdir)
   125  		doc, err := cdb.OpenDocID("bar", kivik.Params(nil))
   126  		if err != nil {
   127  			t.Fatal(err)
   128  		}
   129  		rev, err := cdb.NewRevision(map[string]interface{}{
   130  			"_rev":  "1-xxx",
   131  			"value": "bar",
   132  			"_revisions": map[string]interface{}{
   133  				"start": 2,
   134  				"ids":   []string{"yyy", "xxx"},
   135  			},
   136  			"_attachments": map[string]interface{}{
   137  				"bar.txt": map[string]interface{}{
   138  					"content_type": "text/plain",
   139  					"data":         []byte("Additional content"),
   140  				},
   141  				"foo.txt": map[string]interface{}{
   142  					"content_type": "text/plain",
   143  					"stub":         true,
   144  				},
   145  			},
   146  		})
   147  		if err != nil {
   148  			t.Fatal(err)
   149  		}
   150  		if _, err := doc.addRevision(context.TODO(), rev, kivik.Params(nil)); err != nil {
   151  			t.Fatal(err)
   152  		}
   153  
   154  		return tt{
   155  			path: tmpdir,
   156  			doc:  doc,
   157  		}
   158  	})
   159  
   160  	tests.Run(t, func(t *testing.T, tt tt) {
   161  		err := tt.doc.persist(context.TODO())
   162  		if d := internal.StatusErrorDiff(tt.err, tt.status, err); d != "" {
   163  			t.Error(d)
   164  		}
   165  		if err != nil {
   166  			return
   167  		}
   168  		if d := testy.DiffInterface(testy.Snapshot(t), tt.doc, tmpdirRE(tt.path)); d != nil {
   169  			t.Error(d)
   170  		}
   171  		if d := testy.DiffAsJSON(testy.Snapshot(t, "fs"), testy.JSONDir{
   172  			Path:        tt.path,
   173  			NoMD5Sum:    true,
   174  			FileContent: true,
   175  		}); d != nil {
   176  			t.Error(d)
   177  		}
   178  	})
   179  }
   180  
   181  func tmpdirRE(path string) testy.Replacement {
   182  	return testy.Replacement{
   183  		Regexp:      regexp.MustCompile(`len=\d+\) "` + regexp.QuoteMeta(path)),
   184  		Replacement: `len=X) "<tmpdir>`,
   185  	}
   186  }
   187  
   188  func TestDocumentAddRevision(t *testing.T) {
   189  	if isGopherJS117 {
   190  		t.Skip("Tests broken for GopherJS 1.17")
   191  	}
   192  
   193  	type tt struct {
   194  		path     string
   195  		doc      *Document
   196  		rev      *Revision
   197  		options  kivik.Option
   198  		status   int
   199  		err      string
   200  		expected string
   201  	}
   202  	tests := testy.NewTable()
   203  	tests.Add("stub with bad digest", func(t *testing.T) interface{} {
   204  		tmpdir := testy.CopyTempDir(t, "testdata/persist.att", 0)
   205  		tests.Cleanup(func() error {
   206  			return os.RemoveAll(tmpdir)
   207  		})
   208  
   209  		cdb := New(tmpdir)
   210  		doc, err := cdb.OpenDocID("bar", kivik.Params(nil))
   211  		if err != nil {
   212  			t.Fatal(err)
   213  		}
   214  		rev, err := cdb.NewRevision(map[string]interface{}{
   215  			"_rev":  "1-xxx",
   216  			"value": "bar",
   217  			"_revisions": map[string]interface{}{
   218  				"start": 2,
   219  				"ids":   []string{"yyy", "xxx"},
   220  			},
   221  			"_attachments": map[string]interface{}{
   222  				"foo.txt": map[string]interface{}{
   223  					"content_type": "text/plain",
   224  					"stub":         true,
   225  					"digest":       "md5-asdf",
   226  				},
   227  			},
   228  		})
   229  		if err != nil {
   230  			t.Fatal(err)
   231  		}
   232  
   233  		return tt{
   234  			path:   tmpdir,
   235  			doc:    doc,
   236  			rev:    rev,
   237  			status: http.StatusBadRequest,
   238  			err:    "invalid attachment data for foo.txt",
   239  		}
   240  	})
   241  	tests.Add("stub with wrong revpos", func(t *testing.T) interface{} {
   242  		tmpdir := testy.CopyTempDir(t, "testdata/persist.att", 0)
   243  		tests.Cleanup(func() error {
   244  			return os.RemoveAll(tmpdir)
   245  		})
   246  
   247  		cdb := New(tmpdir)
   248  		doc, err := cdb.OpenDocID("bar", kivik.Params(nil))
   249  		if err != nil {
   250  			t.Fatal(err)
   251  		}
   252  		rev, err := cdb.NewRevision(map[string]interface{}{
   253  			"_rev":  "1-xxx",
   254  			"value": "bar",
   255  			"_revisions": map[string]interface{}{
   256  				"start": 2,
   257  				"ids":   []string{"yyy", "xxx"},
   258  			},
   259  			"_attachments": map[string]interface{}{
   260  				"foo.txt": map[string]interface{}{
   261  					"content_type": "text/plain",
   262  					"stub":         true,
   263  					"revpos":       6,
   264  				},
   265  			},
   266  		})
   267  		if err != nil {
   268  			t.Fatal(err)
   269  		}
   270  
   271  		return tt{
   272  			path:   tmpdir,
   273  			doc:    doc,
   274  			rev:    rev,
   275  			status: http.StatusBadRequest,
   276  			err:    "invalid attachment data for foo.txt",
   277  		}
   278  	})
   279  	tests.Add("stub with 0 revpos", func(t *testing.T) interface{} {
   280  		tmpdir := testy.CopyTempDir(t, "testdata/persist.att", 0)
   281  		tests.Cleanup(func() error {
   282  			return os.RemoveAll(tmpdir)
   283  		})
   284  
   285  		cdb := New(tmpdir)
   286  		doc, err := cdb.OpenDocID("bar", kivik.Params(nil))
   287  		if err != nil {
   288  			t.Fatal(err)
   289  		}
   290  		rev, err := cdb.NewRevision(map[string]interface{}{
   291  			"_rev":  "1-xxx",
   292  			"value": "bar",
   293  			"_revisions": map[string]interface{}{
   294  				"start": 2,
   295  				"ids":   []string{"yyy", "xxx"},
   296  			},
   297  			"_attachments": map[string]interface{}{
   298  				"foo.txt": map[string]interface{}{
   299  					"content_type": "text/plain",
   300  					"stub":         true,
   301  					"revpos":       0,
   302  				},
   303  			},
   304  		})
   305  		if err != nil {
   306  			t.Fatal(err)
   307  		}
   308  
   309  		return tt{
   310  			path:   tmpdir,
   311  			doc:    doc,
   312  			rev:    rev,
   313  			status: http.StatusBadRequest,
   314  			err:    "invalid attachment data for foo.txt",
   315  		}
   316  	})
   317  	tests.Add("upload attachment", func(t *testing.T) interface{} {
   318  		var tmpdir string
   319  		tests.Cleanup(testy.TempDir(t, &tmpdir))
   320  
   321  		cdb := New(tmpdir)
   322  		doc := cdb.NewDocument("foo")
   323  		rev, err := cdb.NewRevision(map[string]interface{}{
   324  			"value": "bar",
   325  			"_revisions": map[string]interface{}{
   326  				"start": 2,
   327  				"ids":   []string{"yyy", "xxx"},
   328  			},
   329  			"_attachments": map[string]interface{}{
   330  				"!foo.txt": map[string]interface{}{
   331  					"content_type": "text/plain",
   332  					"data":         []byte("some test content"),
   333  				},
   334  			},
   335  		})
   336  		if err != nil {
   337  			t.Fatal(err)
   338  		}
   339  
   340  		return tt{
   341  			path:     tmpdir,
   342  			doc:      doc,
   343  			rev:      rev,
   344  			expected: "1-1472ad25836971f236294ad7b19d9f65",
   345  		}
   346  	})
   347  	tests.Add("re-upload identical attachment", func(t *testing.T) interface{} {
   348  		tmpdir := testy.CopyTempDir(t, "testdata/persist.att", 0)
   349  		tests.Cleanup(func() error {
   350  			return os.RemoveAll(tmpdir)
   351  		})
   352  
   353  		cdb := New(tmpdir)
   354  		doc, err := cdb.OpenDocID("bar", kivik.Params(nil))
   355  		if err != nil {
   356  			t.Fatal(err)
   357  		}
   358  		rev, err := cdb.NewRevision(map[string]interface{}{
   359  			"_rev": "1-xxx",
   360  			"_attachments": map[string]interface{}{
   361  				"foo.txt": map[string]interface{}{
   362  					"content_type": "text/plain",
   363  					"data":         []byte("Test content\n"),
   364  				},
   365  			},
   366  		})
   367  		if err != nil {
   368  			t.Fatal(err)
   369  		}
   370  
   371  		return tt{
   372  			path:     tmpdir,
   373  			doc:      doc,
   374  			rev:      rev,
   375  			expected: "2-61afc657ebc34041a2568f5d5ab9fc71",
   376  		}
   377  	})
   378  
   379  	tests.Run(t, func(t *testing.T, tt tt) {
   380  		opts := tt.options
   381  		if opts == nil {
   382  			opts = kivik.Params(nil)
   383  		}
   384  		revid, err := tt.doc.addRevision(context.TODO(), tt.rev, opts)
   385  		if d := internal.StatusErrorDiff(tt.err, tt.status, err); d != "" {
   386  			t.Error(d)
   387  		}
   388  		if err != nil {
   389  			return
   390  		}
   391  		if revid != tt.expected {
   392  			t.Errorf("Unexpected revd: %s", revid)
   393  		}
   394  		if d := testy.DiffInterface(testy.Snapshot(t), tt.doc, tmpdirRE(tt.path)); d != nil {
   395  			t.Error(d)
   396  		}
   397  		if d := testy.DiffAsJSON(testy.Snapshot(t, "fs"), testy.JSONDir{
   398  			Path:        tt.path,
   399  			NoMD5Sum:    true,
   400  			FileContent: true,
   401  		}); d != nil {
   402  			t.Error(d)
   403  		}
   404  	})
   405  }
   406  

View as plain text