go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/gae/filter/readonly/filter_test.go (about)

     1  // Copyright 2017 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 readonly
    16  
    17  import (
    18  	"context"
    19  	"testing"
    20  
    21  	"go.chromium.org/luci/common/errors"
    22  
    23  	"go.chromium.org/luci/gae/impl/memory"
    24  	ds "go.chromium.org/luci/gae/service/datastore"
    25  
    26  	. "github.com/smartystreets/goconvey/convey"
    27  )
    28  
    29  func TestReadOnly(t *testing.T) {
    30  	t.Parallel()
    31  
    32  	Convey("Test datastore filter", t, func() {
    33  		c := memory.Use(context.Background())
    34  
    35  		type Tester struct {
    36  			ID    int `gae:"$id"`
    37  			Value string
    38  		}
    39  		type MutableTester struct {
    40  			ID    int `gae:"$id"`
    41  			Value string
    42  		}
    43  
    44  		// Add values to the datastore before applying the filter.
    45  		ds.Put(c, &Tester{ID: 1, Value: "exists 1"})
    46  		ds.Put(c, &MutableTester{ID: 1, Value: "exists 2"})
    47  		ds.GetTestable(c).CatchupIndexes()
    48  
    49  		// Apply the read-only filter.
    50  		c = FilterRDS(c, func(k *ds.Key) (ro bool) {
    51  			ro = k.Kind() != "MutableTester"
    52  			return
    53  		})
    54  		So(c, ShouldNotBeNil)
    55  
    56  		Convey("Get works.", func() {
    57  			v := Tester{ID: 1}
    58  			So(ds.Get(c, &v), ShouldBeNil)
    59  			So(v.Value, ShouldEqual, "exists 1")
    60  		})
    61  
    62  		Convey("Count works.", func() {
    63  			q := ds.NewQuery("Tester")
    64  			cnt, err := ds.Count(c, q)
    65  			So(err, ShouldBeNil)
    66  			So(cnt, ShouldEqual, 1)
    67  		})
    68  
    69  		Convey("Put fails with read-only error", func() {
    70  			err := ds.Put(c, &Tester{ID: 1}, &MutableTester{ID: 1, Value: "new"})
    71  			So(err, ShouldResemble, errors.MultiError{
    72  				ErrReadOnly,
    73  				nil,
    74  			})
    75  			// The second put actually worked.
    76  			v := MutableTester{ID: 1}
    77  			So(ds.Get(c, &v), ShouldBeNil)
    78  			So(v.Value, ShouldEqual, "new")
    79  		})
    80  
    81  		Convey("Delete fails with read-only error", func() {
    82  			err := ds.Delete(c, &Tester{ID: 1}, &MutableTester{ID: 1})
    83  			So(err, ShouldResemble, errors.MultiError{
    84  				ErrReadOnly,
    85  				nil,
    86  			})
    87  		})
    88  
    89  		Convey("AllocateIDs fails with read-only error", func() {
    90  			t1 := Tester{}
    91  			t2 := MutableTester{ID: -1}
    92  			err := ds.AllocateIDs(c, &t1, &t2)
    93  			So(err, ShouldResemble, errors.MultiError{
    94  				ErrReadOnly,
    95  				nil,
    96  			})
    97  			So(t2.ID, ShouldEqual, 0) // allocated
    98  		})
    99  
   100  		Convey("In a transaction", func() {
   101  			Convey("Get works.", func() {
   102  				v := Tester{ID: 1}
   103  
   104  				err := ds.RunInTransaction(c, func(c context.Context) error {
   105  					return ds.Get(c, &v)
   106  				}, nil)
   107  				So(err, ShouldBeNil)
   108  				So(v.Value, ShouldEqual, "exists 1")
   109  			})
   110  
   111  			Convey("Count works.", func() {
   112  				// (Need ancestor filter for transaction query)
   113  				q := ds.NewQuery("Tester").Ancestor(ds.KeyForObj(c, &Tester{ID: 1}))
   114  
   115  				var cnt int64
   116  				err := ds.RunInTransaction(c, func(c context.Context) (err error) {
   117  					cnt, err = ds.Count(c, q)
   118  					return
   119  				}, nil)
   120  				So(err, ShouldBeNil)
   121  				So(cnt, ShouldEqual, 1)
   122  			})
   123  
   124  			Convey("Put fails with read-only error", func() {
   125  				err := ds.RunInTransaction(c, func(c context.Context) error {
   126  					return ds.Put(c, &Tester{ID: 1})
   127  				}, nil)
   128  				So(err, ShouldEqual, ErrReadOnly)
   129  			})
   130  
   131  			Convey("Delete fails with read-only error", func() {
   132  				err := ds.RunInTransaction(c, func(c context.Context) error {
   133  					return ds.Delete(c, &Tester{ID: 1})
   134  				}, nil)
   135  				So(err, ShouldEqual, ErrReadOnly)
   136  			})
   137  
   138  			Convey("AllocateIDs fails with read-only error", func() {
   139  				err := ds.RunInTransaction(c, func(c context.Context) error {
   140  					return ds.AllocateIDs(c, make([]Tester, 10))
   141  				}, nil)
   142  				So(err, ShouldNotBeNil)
   143  				So(errors.SingleError(err), ShouldEqual, ErrReadOnly)
   144  			})
   145  		})
   146  	})
   147  }