github.com/cornelk/go-cloud@v0.17.1/docstore/memdocstore/mem_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 memdocstore
    16  
    17  import (
    18  	"context"
    19  	"io/ioutil"
    20  	"os"
    21  	"path/filepath"
    22  	"testing"
    23  
    24  	"github.com/cornelk/go-cloud/docstore"
    25  	"github.com/cornelk/go-cloud/docstore/driver"
    26  	"github.com/cornelk/go-cloud/docstore/drivertest"
    27  	"github.com/google/go-cmp/cmp"
    28  )
    29  
    30  type harness struct{}
    31  
    32  func newHarness(ctx context.Context, t *testing.T) (drivertest.Harness, error) {
    33  	return &harness{}, nil
    34  }
    35  
    36  func (h *harness) MakeCollection(_ context.Context, kind drivertest.CollectionKind) (driver.Collection, error) {
    37  	switch kind {
    38  	case drivertest.SingleKey, drivertest.NoRev:
    39  		return newCollection(drivertest.KeyField, nil, nil)
    40  	case drivertest.TwoKey:
    41  		return newCollection("", drivertest.HighScoreKey, nil)
    42  	case drivertest.AltRev:
    43  		return newCollection(drivertest.KeyField, nil, &Options{RevisionField: drivertest.AlternateRevisionField})
    44  	default:
    45  		panic("bad kind")
    46  	}
    47  }
    48  
    49  func (*harness) BeforeDoTypes() []interface{}    { return nil }
    50  func (*harness) BeforeQueryTypes() []interface{} { return nil }
    51  
    52  func (*harness) RevisionsEqual(rev1, rev2 interface{}) bool { return rev1 == rev2 }
    53  
    54  func (*harness) Close() {}
    55  
    56  func TestConformance(t *testing.T) {
    57  	// CodecTester is nil because memdocstore has no native representation.
    58  	drivertest.RunConformanceTests(t, newHarness, nil, nil)
    59  }
    60  
    61  type docmap = map[string]interface{}
    62  
    63  // memdocstore-specific tests.
    64  
    65  // The following tests test memdocstore's backend implementation.
    66  
    67  func TestUpdateEncodesValues(t *testing.T) {
    68  	// Check that update encodes the values in mods.
    69  	ctx := context.Background()
    70  	dc, err := newCollection(drivertest.KeyField, nil, nil)
    71  	if err != nil {
    72  		t.Fatal(err)
    73  	}
    74  	coll := docstore.NewCollection(dc)
    75  	defer coll.Close()
    76  	doc := docmap{drivertest.KeyField: "testUpdateEncodes", "a": 1, dc.RevisionField(): nil}
    77  	if err := coll.Put(ctx, doc); err != nil {
    78  		t.Fatal(err)
    79  	}
    80  	if err := coll.Update(ctx, doc, docstore.Mods{"a": 2}); err != nil {
    81  		t.Fatal(err)
    82  	}
    83  	got := docmap{drivertest.KeyField: doc[drivertest.KeyField]}
    84  	// This Get will fail if the int value 2 in the above mod was not encoded to an int64.
    85  	if err := coll.Get(ctx, got); err != nil {
    86  		t.Fatal(err)
    87  	}
    88  	want := docmap{
    89  		drivertest.KeyField: doc[drivertest.KeyField],
    90  		"a":                 int64(2),
    91  		dc.RevisionField():  got[dc.RevisionField()],
    92  	}
    93  	if !cmp.Equal(got, want) {
    94  		t.Errorf("got %v, want %v", got, want)
    95  	}
    96  }
    97  
    98  func TestUpdateAtomic(t *testing.T) {
    99  	// Check that update is atomic.
   100  	ctx := context.Background()
   101  	dc, err := newCollection(drivertest.KeyField, nil, nil)
   102  	if err != nil {
   103  		t.Fatal(err)
   104  	}
   105  	coll := docstore.NewCollection(dc)
   106  	defer coll.Close()
   107  	doc := docmap{drivertest.KeyField: "testUpdateAtomic", "a": "A", "b": "B", dc.RevisionField(): nil}
   108  
   109  	mods := docstore.Mods{"a": "Y", "b.c": "Z"} // "b" is not a map, so "b.c" is an error
   110  	if err := coll.Put(ctx, doc); err != nil {
   111  		t.Fatal(err)
   112  	}
   113  	if errs := coll.Actions().Update(doc, mods).Do(ctx); errs == nil {
   114  		t.Fatal("got nil, want errors")
   115  	}
   116  	got := docmap{drivertest.KeyField: doc[drivertest.KeyField]}
   117  	if err := coll.Get(ctx, got); err != nil {
   118  		t.Fatal(err)
   119  	}
   120  	want := docmap{
   121  		drivertest.KeyField: doc[drivertest.KeyField],
   122  		dc.RevisionField():  got[dc.RevisionField()],
   123  		"a":                 "A",
   124  		"b":                 "B",
   125  	}
   126  	if !cmp.Equal(got, want) {
   127  		t.Errorf("got %v, want %v", got, want)
   128  	}
   129  }
   130  
   131  func TestSortDocs(t *testing.T) {
   132  	newDocs := func() []storedDoc {
   133  		return []storedDoc{
   134  			{"a": int64(1), "b": "1", "c": 3.0},
   135  			{"a": int64(2), "b": "2", "c": 4.0},
   136  			{"a": int64(3), "b": "3"}, // missing "c"
   137  		}
   138  	}
   139  	inorder := newDocs()
   140  	reversed := newDocs()
   141  	for i := 0; i < len(reversed)/2; i++ {
   142  		j := len(reversed) - i - 1
   143  		reversed[i], reversed[j] = reversed[j], reversed[i]
   144  	}
   145  
   146  	for _, test := range []struct {
   147  		field     string
   148  		ascending bool
   149  		want      []storedDoc
   150  	}{
   151  		{"a", true, inorder},
   152  		{"a", false, reversed},
   153  		{"b", true, inorder},
   154  		{"b", false, reversed},
   155  		{"c", true, inorder},
   156  		{"c", false, []storedDoc{inorder[1], inorder[0], inorder[2]}},
   157  	} {
   158  		got := newDocs()
   159  		sortDocs(got, test.field, test.ascending)
   160  		if diff := cmp.Diff(got, test.want); diff != "" {
   161  			t.Errorf("%q, asc=%t:\n%s", test.field, test.ascending, diff)
   162  		}
   163  	}
   164  }
   165  
   166  func TestSaveAndLoad(t *testing.T) {
   167  	// Save and then load into a file.
   168  	dir, err := ioutil.TempDir("", t.Name())
   169  	if err != nil {
   170  		t.Fatal(err)
   171  	}
   172  	defer os.RemoveAll(dir)
   173  
   174  	// Load from nonexistent file should return empty data.
   175  	f := filepath.Join(dir, "saveAndLoad")
   176  	got, err := loadDocs(f)
   177  	if err != nil {
   178  		t.Fatalf("loading from nonexistent file, got %v, want nil", err)
   179  	}
   180  	if len(got) != 0 {
   181  		t.Fatalf("loading from nonexistent file, got %v, want empty map", got)
   182  	}
   183  
   184  	// Save some data into the file.
   185  	docs := map[interface{}]storedDoc{
   186  		"k1": {"key": "k1", "a": 1},
   187  		"k2": {"key": "k2", "b": 2},
   188  	}
   189  	if err := saveDocs(f, docs); err != nil {
   190  		t.Fatal(err)
   191  	}
   192  	// File should exist now.
   193  	if _, err := os.Lstat(f); err != nil {
   194  		t.Fatal(err)
   195  	}
   196  
   197  	// Reload the data.
   198  	got, err = loadDocs(f)
   199  	if err != nil {
   200  		t.Fatal(err)
   201  	}
   202  	if !cmp.Equal(got, docs) {
   203  		t.Errorf("\ngot  %v\nwant %v", got, docs)
   204  	}
   205  }