go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/gae/service/datastore/datastore_integration_test.go (about) 1 // Copyright 2020 The LUCI 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 // http://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 datastore_test 16 17 import ( 18 "context" 19 "testing" 20 21 "go.chromium.org/luci/common/errors" 22 "go.chromium.org/luci/common/logging/memlogger" 23 24 "go.chromium.org/luci/gae/impl/memory" 25 "go.chromium.org/luci/gae/service/datastore" 26 27 . "github.com/smartystreets/goconvey/convey" 28 . "go.chromium.org/luci/common/testing/assertions" 29 ) 30 31 type Foo struct { 32 _kind string `gae:"$kind,Foo"` 33 ID int64 `gae:"$id"` 34 35 MultiVals []string `gae:"multi_vals"` 36 SingleVal string `gae:"single_val"` 37 Status bool `gae:"status"` 38 } 39 40 func TestRunMulti(t *testing.T) { 41 t.Parallel() 42 43 Convey("RunMulti", t, func() { 44 ctx := memory.Use(context.Background()) 45 ctx = memlogger.Use(ctx) 46 datastore.GetTestable(ctx).AutoIndex(true) 47 datastore.GetTestable(ctx).Consistent(true) 48 49 foos := []*Foo{ 50 { 51 ID: 1, 52 MultiVals: []string{"m1", "m2"}, 53 SingleVal: "s1", 54 Status: true, 55 }, 56 { 57 ID: 2, 58 MultiVals: []string{"m2", "m3"}, 59 SingleVal: "s1", 60 Status: false, 61 }, 62 { 63 ID: 3, 64 MultiVals: []string{"m3", "m4"}, 65 SingleVal: "s2", 66 Status: true, 67 }, 68 { 69 ID: 4, 70 MultiVals: []string{"m4", "m5"}, 71 SingleVal: "s2", 72 Status: false, 73 }, 74 } 75 76 So(datastore.Put(ctx, foos), ShouldBeNil) 77 78 Convey("ok", func() { 79 Convey("default ordering", func() { 80 queries := []*datastore.Query{ 81 datastore.NewQuery("Foo").Gte("__key__", datastore.KeyForObj(ctx, &Foo{ID: 1})), 82 } 83 var res []*Foo 84 err := datastore.RunMulti(ctx, queries, func(foo *Foo) error { 85 res = append(res, foo) 86 return nil 87 }) 88 So(err, ShouldBeNil) 89 So(res, ShouldResemble, foos) 90 }) 91 92 Convey("querying key only", func() { 93 queries := []*datastore.Query{ 94 datastore.NewQuery("Foo").Eq("multi_vals", "m2"), 95 datastore.NewQuery("Foo").Eq("multi_vals", "m3"), 96 } 97 var keys []*datastore.Key 98 err := datastore.RunMulti(ctx, queries, func(k *datastore.Key) error { 99 keys = append(keys, k) 100 return nil 101 }) 102 So(err, ShouldBeNil) 103 So(keys, ShouldResemble, []*datastore.Key{ 104 datastore.KeyForObj(ctx, foos[0]), 105 datastore.KeyForObj(ctx, foos[1]), 106 datastore.KeyForObj(ctx, foos[2]), 107 }) 108 }) 109 110 Convey("overlapped results with non-default orders", func() { 111 queries := []*datastore.Query{ 112 datastore.NewQuery("Foo").Eq("multi_vals", "m2").Order("-single_val", "status"), 113 datastore.NewQuery("Foo").Eq("multi_vals", "m3").Order("-single_val", "status"), 114 } 115 var res []*Foo 116 err := datastore.RunMulti(ctx, queries, func(foo *Foo) error { 117 res = append(res, foo) 118 return nil 119 }) 120 So(err, ShouldBeNil) 121 So(res, ShouldResemble, []*Foo{foos[2], foos[1], foos[0]}) 122 }) 123 124 Convey("send Stop in cb", func() { 125 queries := []*datastore.Query{ 126 datastore.NewQuery("Foo").Eq("multi_vals", "m2"), 127 datastore.NewQuery("Foo").Eq("multi_vals", "m3"), 128 } 129 var res []*Foo 130 err := datastore.RunMulti(ctx, queries, func(foo *Foo) error { 131 if len(res) == 2 { 132 return datastore.Stop 133 } 134 res = append(res, foo) 135 return nil 136 }) 137 So(err, ShouldBeNil) 138 So(res, ShouldResemble, []*Foo{foos[0], foos[1]}) 139 }) 140 141 Convey("not found", func() { 142 queries := []*datastore.Query{ 143 datastore.NewQuery("Foo").Eq("single_val", "non-existent"), 144 } 145 var res []*Foo 146 err := datastore.RunMulti(ctx, queries, func(foo *Foo) error { 147 res = append(res, foo) 148 return nil 149 }) 150 So(err, ShouldBeNil) 151 So(res, ShouldBeNil) 152 }) 153 Convey("cb with cursorCB", func() { 154 queries := []*datastore.Query{ 155 datastore.NewQuery("Foo").Eq("single_val", "s1"), 156 datastore.NewQuery("Foo").Eq("single_val", "s2"), 157 } 158 var cur datastore.Cursor 159 var err error 160 var fooses []*Foo 161 err = datastore.RunMulti(ctx, queries, func(foo *Foo, c datastore.CursorCB) error { 162 fooses = append(fooses, foo) 163 if len(fooses) == 1 { 164 cur, err = c() 165 if err != nil { 166 return err 167 } 168 return datastore.Stop 169 } 170 return nil 171 }) 172 So(err, ShouldBeNil) 173 So(fooses, ShouldNotBeNil) 174 So(fooses, ShouldResemble, []*Foo{foos[0]}) 175 // Apply the cursor to the queries 176 queries, err = datastore.ApplyCursors(ctx, queries, cur) 177 So(err, ShouldBeNil) 178 So(queries, ShouldNotBeNil) 179 err = datastore.RunMulti(ctx, queries, func(foo *Foo, c datastore.CursorCB) error { 180 fooses = append(fooses, foo) 181 if len(fooses) == 3 { 182 cur, err = c() 183 if err != nil { 184 return err 185 } 186 return datastore.Stop 187 } 188 return nil 189 }) 190 So(err, ShouldBeNil) 191 So(fooses, ShouldNotBeNil) 192 So(fooses, ShouldResemble, []*Foo{foos[0], foos[1], foos[2]}) 193 // Apply the cursor to the queries 194 queries, err = datastore.ApplyCursors(ctx, queries, cur) 195 So(err, ShouldBeNil) 196 So(queries, ShouldNotBeNil) 197 err = datastore.RunMulti(ctx, queries, func(foo *Foo, c datastore.CursorCB) error { 198 fooses = append(fooses, foo) 199 return nil 200 }) 201 So(err, ShouldBeNil) 202 So(fooses, ShouldNotBeNil) 203 So(fooses, ShouldResemble, []*Foo{foos[0], foos[1], foos[2], foos[3]}) 204 }) 205 Convey("cb with cursorCB, repeat entities", func() { 206 queries := []*datastore.Query{ 207 datastore.NewQuery("Foo").Eq("multi_vals", "m2"), 208 datastore.NewQuery("Foo").Eq("multi_vals", "m3"), 209 datastore.NewQuery("Foo").Eq("multi_vals", "m4"), 210 } 211 var cur datastore.Cursor 212 var err error 213 var fooses []*Foo 214 err = datastore.RunMulti(ctx, queries, func(foo *Foo, c datastore.CursorCB) error { 215 fooses = append(fooses, foo) 216 if len(fooses) == 2 { 217 cur, err = c() 218 if err != nil { 219 return err 220 } 221 return datastore.Stop 222 } 223 return nil 224 }) 225 So(err, ShouldBeNil) 226 So(fooses, ShouldNotBeNil) 227 So(fooses, ShouldResemble, []*Foo{foos[0], foos[1]}) 228 // Apply the cursor to the queries 229 queries, err = datastore.ApplyCursors(ctx, queries, cur) 230 So(err, ShouldBeNil) 231 So(queries, ShouldNotBeNil) 232 err = datastore.RunMulti(ctx, queries, func(foo *Foo, c datastore.CursorCB) error { 233 fooses = append(fooses, foo) 234 return nil 235 }) 236 So(err, ShouldBeNil) 237 So(fooses, ShouldNotBeNil) 238 // RunMulti returns only the unique entities for that run. If there q1 and q2 are in q, 239 // which is a slice of query. And if x is a valid response to both. If the callback reads 240 // one of the x and then stops and retrieves the cursor. It is possible to repeat the same 241 // set of queries with the cursor applied and get x again. This happens because RunMulti 242 // doesn't have any knowledge of the previous call and it doesn't see that x has already 243 // been returned in a previous run. 244 So(fooses, ShouldResemble, []*Foo{foos[0], foos[1], foos[1], foos[2], foos[3]}) 245 }) 246 }) 247 248 Convey("bad", func() { 249 Convey("Queries with more than one kind", func() { 250 queries := []*datastore.Query{ 251 datastore.NewQuery("Foo"), 252 datastore.NewQuery("Foo1"), 253 } 254 255 err := datastore.RunMulti(ctx, queries, func(foo *Foo, c datastore.CursorCB) error { 256 return nil 257 }) 258 So(err, ShouldErrLike, "should query the same kind") 259 }) 260 261 Convey("Queries with different order", func() { 262 queries := []*datastore.Query{ 263 datastore.NewQuery("Foo").Order("field1"), 264 datastore.NewQuery("Foo").Order("-field1"), 265 } 266 267 err := datastore.RunMulti(ctx, queries, func(foo *Foo, c datastore.CursorCB) error { 268 return nil 269 }) 270 So(err, ShouldErrLike, "should use the same order") 271 }) 272 }) 273 274 Convey("context cancelation", func() { 275 ctx, cancel := context.WithCancel(ctx) 276 277 queries := []*datastore.Query{ 278 datastore.NewQuery("Foo").Eq("multi_vals", "m2"), 279 datastore.NewQuery("Foo").Eq("multi_vals", "m3"), 280 } 281 282 err := datastore.RunMulti(ctx, queries, func(k *datastore.Key) error { 283 cancel() 284 <-ctx.Done() // make sure it "propagates" everywhere 285 return nil 286 }) 287 So(err, ShouldEqual, context.Canceled) 288 }) 289 290 Convey("callback error", func() { 291 customErr := errors.New("boo") 292 293 queries := []*datastore.Query{ 294 datastore.NewQuery("Foo").Eq("multi_vals", "m2"), 295 datastore.NewQuery("Foo").Eq("multi_vals", "m3"), 296 } 297 298 var keys []*datastore.Key 299 err := datastore.RunMulti(ctx, queries, func(k *datastore.Key) error { 300 keys = append(keys, k) 301 return customErr 302 }) 303 So(err, ShouldEqual, customErr) 304 So(keys, ShouldHaveLength, 1) 305 }) 306 }) 307 }