blob: 0711c6c973019ed1f7e762f0862208310a95d9ef [file] [log] [blame]
// Copyright 2015 The LUCI Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package gaesettings
import (
"context"
"encoding/json"
"testing"
"time"
"go.chromium.org/luci/common/clock"
"go.chromium.org/luci/common/clock/testclock"
"go.chromium.org/luci/gae/filter/count"
"go.chromium.org/luci/gae/filter/dscache"
"go.chromium.org/luci/gae/filter/txnBuf"
"go.chromium.org/luci/gae/impl/memory"
ds "go.chromium.org/luci/gae/service/datastore"
"go.chromium.org/luci/gae/service/info"
. "github.com/smartystreets/goconvey/convey"
)
func TestWorks(t *testing.T) {
Convey("Works", t, func() {
ctx := memory.Use(context.Background())
ctx = dscache.FilterRDS(ctx, nil)
ctx, tc := testclock.UseTime(ctx, time.Unix(1444945245, 0))
// Record access to memcache. There should be none.
ctx, mcOps := count.FilterMC(ctx)
s := Storage{}
// Nothing's there yet.
bundle, exp, err := s.FetchAllSettings(ctx)
So(err, ShouldBeNil)
So(exp, ShouldEqual, time.Second)
So(len(bundle.Values), ShouldEqual, 0)
conTime, err := s.GetConsistencyTime(ctx)
So(conTime.IsZero(), ShouldBeTrue)
So(err, ShouldBeNil)
// Produce a bunch of versions.
tc.Add(time.Minute)
So(s.UpdateSetting(ctx, "key", json.RawMessage(`"val1"`), "who1", "why1"), ShouldBeNil)
tc.Add(time.Minute)
So(s.UpdateSetting(ctx, "key", json.RawMessage(`"val2"`), "who2", "why2"), ShouldBeNil)
tc.Add(time.Minute)
So(s.UpdateSetting(ctx, "key", json.RawMessage(`"val3"`), "who3", "why3"), ShouldBeNil)
bundle, exp, err = s.FetchAllSettings(ctx)
So(err, ShouldBeNil)
So(exp, ShouldEqual, time.Second)
So(*bundle.Values["key"], ShouldResemble, json.RawMessage(`"val3"`))
conTime, err = s.GetConsistencyTime(ctx)
So(conTime, ShouldResemble, clock.Now(ctx).UTC().Add(time.Second))
So(err, ShouldBeNil)
// Check all log entities is there.
ds.GetTestable(ctx).CatchupIndexes()
entities := []settingsEntity{}
So(ds.GetAll(ctx, ds.NewQuery("gaesettings.SettingsLog"), &entities), ShouldBeNil)
So(len(entities), ShouldEqual, 2)
asMap := map[string]settingsEntity{}
for _, e := range entities {
So(e.Kind, ShouldEqual, "gaesettings.SettingsLog")
So(e.Parent.Kind(), ShouldEqual, "gaesettings.Settings")
// Clear some fields to simplify assert below.
e.Kind = ""
e.Parent = nil
e.When = time.Time{}
asMap[e.ID] = e
}
So(asMap, ShouldResemble, map[string]settingsEntity{
"1": {
ID: "1",
Version: 1,
Value: "{\n \"key\": \"val1\"\n}",
Who: "who1",
Why: "why1",
},
"2": {
ID: "2",
Version: 2,
Value: "{\n \"key\": \"val2\"\n}",
Who: "who2",
Why: "why2",
},
})
// Memcache must not be used even if dscache is installed in the context.
So(mcOps.AddMulti.Total(), ShouldEqual, 0)
So(mcOps.GetMulti.Total(), ShouldEqual, 0)
So(mcOps.SetMulti.Total(), ShouldEqual, 0)
So(mcOps.DeleteMulti.Total(), ShouldEqual, 0)
})
Convey("Handles namespace switch", t, func() {
ctx := memory.Use(context.Background())
ctx = dscache.FilterRDS(ctx, nil)
namespaced := info.MustNamespace(ctx, "blah")
s := Storage{}
// Put something using default namespace.
So(s.UpdateSetting(ctx, "key", json.RawMessage(`"val1"`), "who1", "why1"), ShouldBeNil)
// Works when using default namespace.
bundle, _, err := s.FetchAllSettings(ctx)
So(err, ShouldBeNil)
So(*bundle.Values["key"], ShouldResemble, json.RawMessage(`"val1"`))
// Works when using non-default namespace too.
bundle, _, err = s.FetchAllSettings(namespaced)
So(err, ShouldBeNil)
So(*bundle.Values["key"], ShouldResemble, json.RawMessage(`"val1"`))
// Update using non-default namespace.
So(s.UpdateSetting(namespaced, "key", json.RawMessage(`"val2"`), "who2", "why2"), ShouldBeNil)
// Works when using default namespace.
bundle, _, err = s.FetchAllSettings(ctx)
So(err, ShouldBeNil)
So(*bundle.Values["key"], ShouldResemble, json.RawMessage(`"val2"`))
})
Convey("Ignores transactions", t, func() {
ctx := memory.Use(context.Background())
s := Storage{}
// Put something.
So(s.UpdateSetting(ctx, "key", json.RawMessage(`"val1"`), "who1", "why1"), ShouldBeNil)
// Works when fetching outside of a transaction.
bundle, _, err := s.FetchAllSettings(ctx)
So(err, ShouldBeNil)
So(len(bundle.Values), ShouldEqual, 1)
// Works when fetching from inside of a transaction.
ds.RunInTransaction(ctx, func(ctx context.Context) error {
bundle, _, err := s.FetchAllSettings(ctx)
So(err, ShouldBeNil)
So(len(bundle.Values), ShouldEqual, 1)
return nil
}, nil)
})
Convey("Ignores transactions and namespaces", t, func() {
ctx := memory.Use(context.Background())
s := Storage{}
// Put something.
So(s.UpdateSetting(ctx, "key", json.RawMessage(`"val1"`), "who1", "why1"), ShouldBeNil)
// Works when fetching outside of a transaction.
bundle, _, err := s.FetchAllSettings(ctx)
So(err, ShouldBeNil)
So(len(bundle.Values), ShouldEqual, 1)
// Works when fetching from inside of a transaction.
namespaced := info.MustNamespace(ctx, "blah")
ds.RunInTransaction(namespaced, func(ctx context.Context) error {
bundle, _, err := s.FetchAllSettings(ctx)
So(err, ShouldBeNil)
So(len(bundle.Values), ShouldEqual, 1)
return nil
}, nil)
})
Convey("Ignores transactions and txnBuf", t, func() {
ctx := memory.Use(context.Background())
s := Storage{}
// Put something.
So(s.UpdateSetting(ctx, "key", json.RawMessage(`"val1"`), "who1", "why1"), ShouldBeNil)
// Works when fetching outside of a transaction.
bundle, _, err := s.FetchAllSettings(ctx)
So(err, ShouldBeNil)
So(len(bundle.Values), ShouldEqual, 1)
// Works when fetching from inside of a transaction.
ds.RunInTransaction(txnBuf.FilterRDS(ctx), func(ctx context.Context) error {
bundle, _, err := s.FetchAllSettings(ctx)
So(err, ShouldBeNil)
So(len(bundle.Values), ShouldEqual, 1)
return nil
}, nil)
})
Convey("Ignores transactions and namespaces and txnBuf", t, func() {
ctx := memory.Use(context.Background())
s := Storage{}
// Put something.
So(s.UpdateSetting(ctx, "key", json.RawMessage(`"val1"`), "who1", "why1"), ShouldBeNil)
// Works when fetching outside of a transaction.
bundle, _, err := s.FetchAllSettings(ctx)
So(err, ShouldBeNil)
So(len(bundle.Values), ShouldEqual, 1)
// Works when fetching from inside of a transaction.
namespaced := info.MustNamespace(ctx, "blah")
ds.RunInTransaction(txnBuf.FilterRDS(namespaced), func(ctx context.Context) error {
bundle, _, err := s.FetchAllSettings(ctx)
So(err, ShouldBeNil)
So(len(bundle.Values), ShouldEqual, 1)
return nil
}, nil)
})
}