go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/common/proto/paged/queries_test.go (about)

     1  // Copyright 2019 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 paged
    16  
    17  import (
    18  	"context"
    19  	"testing"
    20  
    21  	"go.chromium.org/luci/common/errors"
    22  	"go.chromium.org/luci/gae/impl/memory"
    23  	"go.chromium.org/luci/gae/service/datastore"
    24  
    25  	"go.chromium.org/luci/common/proto/examples"
    26  
    27  	. "github.com/smartystreets/goconvey/convey"
    28  	. "go.chromium.org/luci/common/testing/assertions"
    29  )
    30  
    31  func TestQueries(t *testing.T) {
    32  	t.Parallel()
    33  
    34  	Convey("Query", t, func() {
    35  		type Record struct {
    36  			_kind string `gae:"$kind,kind"`
    37  			ID    string `gae:"$id"`
    38  		}
    39  
    40  		c := memory.Use(context.Background())
    41  		datastore.GetTestable(c).AutoIndex(true)
    42  		datastore.GetTestable(c).Consistent(true)
    43  		rsp := &examples.ListResponse{}
    44  		q := datastore.NewQuery("kind")
    45  
    46  		Convey("invalid", func() {
    47  			Convey("function", func() {
    48  				Convey("nil", func() {
    49  					So(Query(c, 0, "", rsp, q, nil), ShouldErrLike, "callback must be a function")
    50  				})
    51  
    52  				Convey("no inputs", func() {
    53  					f := func() error {
    54  						return nil
    55  					}
    56  					So(Query(c, 0, "", rsp, q, f), ShouldErrLike, "callback function must accept one argument")
    57  				})
    58  
    59  				Convey("many inputs", func() {
    60  					f := func(any, datastore.CursorCB) error {
    61  						return nil
    62  					}
    63  					So(Query(c, 0, "", rsp, q, f), ShouldErrLike, "callback function must accept one argument")
    64  				})
    65  
    66  				Convey("no outputs", func() {
    67  					f := func(any) {
    68  					}
    69  					So(Query(c, 0, "", rsp, q, f), ShouldErrLike, "callback function must return one value")
    70  				})
    71  
    72  				Convey("many outputs", func() {
    73  					f := func(any) (any, error) {
    74  						return nil, nil
    75  					}
    76  					So(Query(c, 0, "", rsp, q, f), ShouldErrLike, "callback function must return one value")
    77  				})
    78  			})
    79  
    80  			Convey("token", func() {
    81  				f := func(any) error {
    82  					return nil
    83  				}
    84  				So(Query(c, 0, "tok", rsp, q, f), ShouldErrLike, "invalid page token")
    85  			})
    86  		})
    87  
    88  		Convey("valid", func() {
    89  			Convey("callback", func() {
    90  				Convey("error", func() {
    91  					f := func(r *Record) error {
    92  						return errors.New("error")
    93  					}
    94  					So(datastore.Put(c, &Record{ID: "id"}), ShouldBeNil)
    95  
    96  					So(Query(c, 0, "", rsp, q, f), ShouldErrLike, "error")
    97  				})
    98  
    99  				Convey("stop", func() {
   100  					f := func(*Record) error {
   101  						return datastore.Stop
   102  					}
   103  
   104  					Convey("first", func() {
   105  						So(datastore.Put(c, &Record{ID: "id1"}), ShouldBeNil)
   106  						So(datastore.Put(c, &Record{ID: "id2"}), ShouldBeNil)
   107  
   108  						Convey("limit", func() {
   109  							Convey("greater", func() {
   110  								So(Query(c, 10, "", rsp, q, f), ShouldBeNil)
   111  								So(rsp.NextPageToken, ShouldNotBeEmpty)
   112  
   113  								tok := rsp.NextPageToken
   114  								rsp.NextPageToken = ""
   115  								So(Query(c, 10, tok, rsp, q, f), ShouldBeNil)
   116  								So(rsp.NextPageToken, ShouldBeEmpty)
   117  							})
   118  
   119  							Convey("equal", func() {
   120  								So(Query(c, 1, "", rsp, q, f), ShouldBeNil)
   121  								So(rsp.NextPageToken, ShouldNotBeEmpty)
   122  
   123  								tok := rsp.NextPageToken
   124  								rsp.NextPageToken = ""
   125  								So(Query(c, 1, tok, rsp, q, f), ShouldBeNil)
   126  								So(rsp.NextPageToken, ShouldBeEmpty)
   127  							})
   128  						})
   129  
   130  						Convey("no limit", func() {
   131  							So(Query(c, 0, "", rsp, q, f), ShouldBeNil)
   132  							So(rsp.NextPageToken, ShouldNotBeEmpty)
   133  
   134  							tok := rsp.NextPageToken
   135  							rsp.NextPageToken = ""
   136  							So(Query(c, 0, tok, rsp, q, f), ShouldBeNil)
   137  							So(rsp.NextPageToken, ShouldBeEmpty)
   138  						})
   139  					})
   140  
   141  					Convey("intermediate", func() {
   142  						i := 0
   143  						f = func(*Record) error {
   144  							i++
   145  							if i == 2 {
   146  								return datastore.Stop
   147  							}
   148  							return nil
   149  						}
   150  						So(datastore.Put(c, &Record{ID: "id1"}), ShouldBeNil)
   151  						So(datastore.Put(c, &Record{ID: "id2"}), ShouldBeNil)
   152  						So(datastore.Put(c, &Record{ID: "id3"}), ShouldBeNil)
   153  
   154  						Convey("limit", func() {
   155  							Convey("greater", func() {
   156  								So(Query(c, 10, "", rsp, q, f), ShouldBeNil)
   157  								So(rsp.NextPageToken, ShouldNotBeEmpty)
   158  
   159  								tok := rsp.NextPageToken
   160  								rsp.NextPageToken = ""
   161  								So(Query(c, 10, tok, rsp, q, f), ShouldBeNil)
   162  								So(rsp.NextPageToken, ShouldBeEmpty)
   163  							})
   164  
   165  							Convey("equal", func() {
   166  								So(Query(c, 2, "", rsp, q, f), ShouldBeNil)
   167  								So(rsp.NextPageToken, ShouldNotBeEmpty)
   168  
   169  								tok := rsp.NextPageToken
   170  								rsp.NextPageToken = ""
   171  								So(Query(c, 2, tok, rsp, q, f), ShouldBeNil)
   172  								So(rsp.NextPageToken, ShouldBeEmpty)
   173  							})
   174  						})
   175  
   176  						Convey("no limit", func() {
   177  							So(Query(c, 0, "", rsp, q, f), ShouldBeNil)
   178  							So(rsp.NextPageToken, ShouldNotBeEmpty)
   179  
   180  							tok := rsp.NextPageToken
   181  							rsp.NextPageToken = ""
   182  							So(Query(c, 0, tok, rsp, q, f), ShouldBeNil)
   183  							So(rsp.NextPageToken, ShouldBeEmpty)
   184  						})
   185  					})
   186  
   187  					Convey("last", func() {
   188  						So(datastore.Put(c, &Record{ID: "id"}), ShouldBeNil)
   189  
   190  						Convey("limit", func() {
   191  							Convey("greater", func() {
   192  								So(Query(c, 10, "", rsp, q, f), ShouldBeNil)
   193  								So(rsp.NextPageToken, ShouldBeEmpty)
   194  							})
   195  
   196  							Convey("equal", func() {
   197  								So(Query(c, 1, "", rsp, q, f), ShouldBeNil)
   198  								So(rsp.NextPageToken, ShouldBeEmpty)
   199  							})
   200  						})
   201  
   202  						Convey("no limit", func() {
   203  							So(Query(c, 0, "", rsp, q, f), ShouldBeNil)
   204  							So(rsp.NextPageToken, ShouldBeEmpty)
   205  						})
   206  					})
   207  				})
   208  
   209  				Convey("ok", func() {
   210  					f := func(*Record) error {
   211  						return nil
   212  					}
   213  					So(datastore.Put(c, &Record{ID: "id"}), ShouldBeNil)
   214  
   215  					Convey("limit", func() {
   216  						So(Query(c, 10, "", rsp, q, f), ShouldBeNil)
   217  					})
   218  
   219  					Convey("no limit", func() {
   220  						So(Query(c, 0, "", rsp, q, f), ShouldBeNil)
   221  					})
   222  				})
   223  			})
   224  
   225  			Convey("query", func() {
   226  				rsp.Records = make([]string, 0)
   227  				f := func(r *Record) error {
   228  					rsp.Records = append(rsp.Records, r.ID)
   229  					return nil
   230  				}
   231  
   232  				Convey("limit", func() {
   233  					Convey("none", func() {
   234  						So(Query(c, 2, "", rsp, q, f), ShouldBeNil)
   235  						So(rsp.Records, ShouldBeEmpty)
   236  					})
   237  
   238  					Convey("one", func() {
   239  						So(datastore.Put(c, &Record{ID: "id"}), ShouldBeNil)
   240  
   241  						So(Query(c, 2, "", rsp, q, f), ShouldBeNil)
   242  						So(rsp.Records, ShouldResemble, []string{"id"})
   243  						So(rsp.NextPageToken, ShouldBeEmpty)
   244  					})
   245  
   246  					Convey("many", func() {
   247  						So(datastore.Put(c, &Record{ID: "id1"}), ShouldBeNil)
   248  						So(datastore.Put(c, &Record{ID: "id2"}), ShouldBeNil)
   249  						So(datastore.Put(c, &Record{ID: "id3"}), ShouldBeNil)
   250  
   251  						So(Query(c, 2, "", rsp, q, f), ShouldBeNil)
   252  						So(rsp.Records, ShouldResemble, []string{"id1", "id2"})
   253  						So(rsp.NextPageToken, ShouldNotBeEmpty)
   254  
   255  						tok := rsp.NextPageToken
   256  						rsp.NextPageToken = ""
   257  						rsp.Records = make([]string, 0)
   258  						So(Query(c, 2, tok, rsp, q, f), ShouldBeNil)
   259  						So(rsp.Records, ShouldResemble, []string{"id3"})
   260  						So(rsp.NextPageToken, ShouldBeEmpty)
   261  					})
   262  				})
   263  
   264  				Convey("no limit", func() {
   265  					Convey("none", func() {
   266  						So(Query(c, 0, "", rsp, q, f), ShouldBeNil)
   267  						So(rsp.Records, ShouldBeEmpty)
   268  					})
   269  
   270  					Convey("one", func() {
   271  						So(datastore.Put(c, &Record{ID: "id"}), ShouldBeNil)
   272  
   273  						So(Query(c, 0, "", rsp, q, f), ShouldBeNil)
   274  						So(rsp.Records, ShouldResemble, []string{"id"})
   275  					})
   276  
   277  					Convey("many", func() {
   278  						So(datastore.Put(c, &Record{ID: "id1"}), ShouldBeNil)
   279  						So(datastore.Put(c, &Record{ID: "id2"}), ShouldBeNil)
   280  						So(datastore.Put(c, &Record{ID: "id3"}), ShouldBeNil)
   281  
   282  						So(Query(c, 0, "", rsp, q, f), ShouldBeNil)
   283  						So(rsp.Records, ShouldResemble, []string{"id1", "id2", "id3"})
   284  					})
   285  
   286  					Convey("error", func() {
   287  						rsp.Records = make([]string, 0)
   288  						f := func(r *Record) error {
   289  							return errors.Reason("error").Err()
   290  						}
   291  						So(datastore.Put(c, &Record{ID: "id1"}), ShouldBeNil)
   292  						So(datastore.Put(c, &Record{ID: "id2"}), ShouldBeNil)
   293  						So(datastore.Put(c, &Record{ID: "id3"}), ShouldBeNil)
   294  
   295  						So(Query(c, 0, "", rsp, q, f), ShouldErrLike, "error")
   296  						So(rsp.Records, ShouldBeEmpty)
   297  						So(rsp.NextPageToken, ShouldBeEmpty)
   298  					})
   299  				})
   300  			})
   301  		})
   302  	})
   303  }