github.com/cornelk/go-cloud@v0.17.1/docstore/docstore_test.go (about)

     1  // Copyright 2019 The Go Cloud Development Kit Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     https://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package docstore
    16  
    17  import (
    18  	"context"
    19  	"reflect"
    20  	"testing"
    21  	"time"
    22  
    23  	"github.com/cornelk/go-cloud/docstore/driver"
    24  	"github.com/cornelk/go-cloud/gcerrors"
    25  	"github.com/google/go-cmp/cmp"
    26  )
    27  
    28  type Book struct {
    29  	Title            string `docstore:"key"`
    30  	Author           Name   `docstore:"author"`
    31  	PublicationYears []int  `docstore:"pub_years,omitempty"`
    32  	NumPublications  int    `docstore:"-"`
    33  }
    34  
    35  type Name struct {
    36  	First, Last string
    37  }
    38  
    39  func TestIsIncNumber(t *testing.T) {
    40  	for _, x := range []interface{}{int(1), 'x', uint(1), byte(1), float32(1), float64(1), time.Duration(1)} {
    41  		if !isIncNumber(x) {
    42  			t.Errorf("%v: got false, want true", x)
    43  		}
    44  	}
    45  	for _, x := range []interface{}{1 + 1i, "3", time.Time{}} {
    46  		if isIncNumber(x) {
    47  			t.Errorf("%v: got true, want false", x)
    48  		}
    49  	}
    50  }
    51  
    52  func TestActionsDo(t *testing.T) {
    53  	c := newCollection(fakeDriverCollection{})
    54  	defer c.Close()
    55  	dn := map[string]interface{}{"key": nil}
    56  	d1 := map[string]interface{}{"key": 1}
    57  	d2 := map[string]interface{}{"key": 2}
    58  	dsn := &Book{}
    59  	ds1 := &Book{Title: "The Master and Margarita"}
    60  	ds2 := &Book{Title: "The Martian"}
    61  
    62  	for _, test := range []struct {
    63  		alist *ActionList
    64  		want  []int // error indexes; nil if no error
    65  	}{
    66  		{c.Actions().Get(d1).Get(d2).Get(ds1).Get(ds2), nil},
    67  		{c.Actions().Get(d1).Put(d1).Put(ds1).Get(ds1), nil},
    68  		{c.Actions().Get(d2).Replace(d1).Put(d2).Get(d1), nil},
    69  		{c.Actions().Get(ds2).Replace(ds1).Put(ds2).Get(ds1), nil},
    70  		// Missing keys.
    71  		{c.Actions().Put(dn).Put(dsn), []int{0, 1}},
    72  		{c.Actions().Get(dn).Replace(dn).Create(dn).Update(dn, Mods{"a": 1}), []int{0, 1, 3}},
    73  		{c.Actions().Get(dsn).Replace(dsn).Create(dsn).Update(dsn, Mods{"a": 1}), []int{0, 1, 3}},
    74  		// Duplicate documents.
    75  		{c.Actions().Create(dn).Create(dn).Create(dsn).Create(dsn), nil}, // each Create without a key is a separate document
    76  		{c.Actions().Create(d2).Create(ds2).Get(d2).Get(ds2).Create(d2).Put(ds2), []int{4, 5}},
    77  		{c.Actions().Get(d1).Get(ds1).Get(d1).Get(ds1), []int{2, 3}},
    78  		{c.Actions().Put(d1).Put(ds1).Get(d1).Get(ds1).Get(d1).Get(ds1), []int{4, 5}},
    79  		{c.Actions().Get(d1).Get(ds1).Put(d1).Put(d2).Put(ds1).Put(ds2).Put(d1).Replace(ds1), []int{6, 7}},
    80  		{c.Actions().Create(dn).Create(d1).Create(dsn).Create(ds1).Get(d1).Get(ds1), nil},
    81  		// Get with field paths.
    82  		{c.Actions().Get(d1, "a.b", "c"), nil},
    83  		{c.Actions().Get(ds1, "name.Last", "pub_years"), nil},
    84  		{c.Actions().Get(d1, ".c").Get(ds1, "").Get(ds2, "\xa0\xa1"), []int{0, 1, 2}}, // bad field path
    85  		// Mods.
    86  		{c.Actions().Update(d1, nil).Update(ds1, nil), []int{0, 1}},                                                 // empty mod
    87  		{c.Actions().Update(d1, Mods{"a.b.c": 1, "a.b": 2, "a.b+c": 3}), []int{0}},                                  // a.b is a prefix of a.b.c
    88  		{c.Actions().Update(d1, Mods{"": 1}).Update(ds1, Mods{".f": 2}), []int{0, 1}},                               // invalid field path
    89  		{c.Actions().Update(d1, Mods{"a": Increment(true)}).Update(ds1, Mods{"name": Increment("b")}), []int{0, 1}}, // invalid incOp
    90  	} {
    91  		err := test.alist.Do(context.Background())
    92  		if err == nil {
    93  			if len(test.want) > 0 {
    94  				t.Errorf("%s: got nil, want error", test.alist)
    95  			}
    96  			continue
    97  		}
    98  		var got []int
    99  		for _, e := range err.(ActionListError) {
   100  			if gcerrors.Code(e.Err) != gcerrors.InvalidArgument {
   101  				t.Errorf("%s: got %v, want InvalidArgument", test.alist, e.Err)
   102  			}
   103  			got = append(got, e.Index)
   104  		}
   105  		if !cmp.Equal(got, test.want) {
   106  			t.Errorf("%s: got %v, want %v", test.alist, got, test.want)
   107  		}
   108  	}
   109  }
   110  
   111  func TestClosedErrors(t *testing.T) {
   112  	// Check that all collection methods return errClosed if the collection is closed.
   113  	ctx := context.Background()
   114  	c := NewCollection(fakeDriverCollection{})
   115  	if err := c.Close(); err != nil {
   116  		t.Fatalf("got %v, want nil", err)
   117  	}
   118  
   119  	check := func(err error) {
   120  		t.Helper()
   121  		if alerr, ok := err.(ActionListError); ok {
   122  			err = alerr.Unwrap()
   123  		}
   124  		if err != errClosed {
   125  			t.Errorf("got %v, want errClosed", err)
   126  		}
   127  	}
   128  
   129  	doc := map[string]interface{}{"key": "k"}
   130  	check(c.Close())
   131  	check(c.Actions().Create(doc).Do(ctx))
   132  	check(c.Create(ctx, doc))
   133  	check(c.Replace(ctx, doc))
   134  	check(c.Put(ctx, doc))
   135  	check(c.Get(ctx, doc))
   136  	check(c.Delete(ctx, doc))
   137  	check(c.Update(ctx, doc, Mods{"a": 1}))
   138  	iter := c.Query().Get(ctx)
   139  	check(iter.Next(ctx, doc))
   140  
   141  	// Check that DocumentIterator.Next returns errClosed if Close is called
   142  	// in the middle of the iteration.
   143  	c = NewCollection(fakeDriverCollection{})
   144  	iter = c.Query().Get(ctx)
   145  	c.Close()
   146  	check(iter.Next(ctx, doc))
   147  }
   148  
   149  func TestSerializeRevisionErrors(t *testing.T) {
   150  	c := NewCollection(fakeDriverCollection{})
   151  	_, err := c.RevisionToString(nil)
   152  	if got := gcerrors.Code(err); got != gcerrors.InvalidArgument {
   153  		t.Errorf("got %v, want InvalidArgument", got)
   154  	}
   155  	_, err = c.StringToRevision("")
   156  	if got := gcerrors.Code(err); got != gcerrors.InvalidArgument {
   157  		t.Errorf("got %v, want InvalidArgument", got)
   158  	}
   159  }
   160  
   161  type fakeDriverCollection struct {
   162  	driver.Collection
   163  }
   164  
   165  func (fakeDriverCollection) Key(doc driver.Document) (interface{}, error) {
   166  	key, err := doc.GetField("key")
   167  	// TODO(#2589): remove this check once we check for empty key.
   168  	if err != nil || driver.IsEmptyValue(reflect.ValueOf(key)) {
   169  		return nil, err
   170  	}
   171  	return key, nil
   172  }
   173  
   174  func (fakeDriverCollection) RevisionField() string { return DefaultRevisionField }
   175  
   176  func (fakeDriverCollection) Close() error { return nil }
   177  
   178  func (fakeDriverCollection) RunActions(ctx context.Context, actions []*driver.Action, opts *driver.RunActionsOptions) driver.ActionListError {
   179  	return nil
   180  }
   181  
   182  func (fakeDriverCollection) RunGetQuery(context.Context, *driver.Query) (driver.DocumentIterator, error) {
   183  	return fakeDriverDocumentIterator{}, nil
   184  }
   185  
   186  type fakeDriverDocumentIterator struct {
   187  	driver.DocumentIterator
   188  }
   189  
   190  func (fakeDriverDocumentIterator) Next(context.Context, driver.Document) error { return nil }