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 }