blob: d2d0634d98d7641ef562e0885275f0466415717c [file] [log] [blame]
// Copyright 2020 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 prjcfg
import (
"context"
"testing"
"google.golang.org/protobuf/encoding/prototext"
"go.chromium.org/luci/config"
"go.chromium.org/luci/config/cfgclient"
cfgmemory "go.chromium.org/luci/config/impl/memory"
gaememory "go.chromium.org/luci/gae/impl/memory"
"go.chromium.org/luci/gae/service/datastore"
cfgpb "go.chromium.org/luci/cv/api/config/v2"
. "github.com/smartystreets/goconvey/convey"
. "go.chromium.org/luci/common/testing/assertions"
)
func TestLoadingConfigs(t *testing.T) {
t.Parallel()
Convey("Load project config works", t, func() {
ctx := gaememory.Use(context.Background())
datastore.GetTestable(ctx).AutoIndex(true)
datastore.GetTestable(ctx).Consistent(true)
const project = "chromium"
Convey("Not existing project", func() {
m, err := GetLatestMeta(ctx, project)
So(err, ShouldBeNil)
So(m.Exists(), ShouldBeFalse)
So(m.EVersion, ShouldEqual, 0)
So(func() { m.Hash() }, ShouldPanic)
})
cfg := &cfgpb.Config{
ConfigGroups: []*cfgpb.ConfigGroup{
{
Name: "branch_m100",
Gerrit: []*cfgpb.ConfigGroup_Gerrit{
{
Url: "https://chromium-review.googlesource.com/",
Projects: []*cfgpb.ConfigGroup_Gerrit_Project{
{
Name: "chromium/src",
RefRegexp: []string{"refs/heads/branch_m100"},
},
},
},
},
},
{
Fallback: cfgpb.Toggle_YES,
Name: "catch_all",
Gerrit: []*cfgpb.ConfigGroup_Gerrit{
{
Url: "https://chromium-review.googlesource.com/",
Projects: []*cfgpb.ConfigGroup_Gerrit_Project{
{
Name: "chromium/src",
RefRegexp: []string{"refs/heads/main"},
},
},
},
},
},
},
}
ctx = cfgclient.Use(ctx, cfgmemory.New(map[config.Set]cfgmemory.Files{
config.ProjectSet(project): {ConfigFileName: prototext.Format(cfg)},
}))
So(UpdateProject(ctx, project, func(context.Context) error { return nil }), ShouldBeNil)
Convey("Enabled project", func() {
m, err := GetLatestMeta(ctx, project)
So(err, ShouldBeNil)
So(m.Exists(), ShouldBeTrue)
So(m.Status, ShouldEqual, StatusEnabled)
So(m.EVersion, ShouldEqual, 1)
So(m.ConfigGroupNames, ShouldResemble, []string{"branch_m100", "catch_all"})
h := m.Hash()
So(h, ShouldStartWith, "sha256:")
So(m.ConfigGroupIDs, ShouldResemble, []ConfigGroupID{
ConfigGroupID(h + "/branch_m100"),
ConfigGroupID(h + "/catch_all"),
})
m2, err := GetHashMeta(ctx, project, h)
So(err, ShouldBeNil)
So(m2, ShouldResemble, m)
cgs, err := m.GetConfigGroups(ctx)
So(err, ShouldBeNil)
So(len(cgs), ShouldEqual, 2)
So(cgs[0].Content, ShouldResembleProto, cfg.ConfigGroups[0])
So(cgs[1].Content, ShouldResembleProto, cfg.ConfigGroups[1])
})
cfg.ConfigGroups = append(cfg.ConfigGroups, &cfgpb.ConfigGroup{
Name: "branch_m200",
Gerrit: []*cfgpb.ConfigGroup_Gerrit{
{
Url: "https://chromium-review.googlesource.com/",
Projects: []*cfgpb.ConfigGroup_Gerrit_Project{
{
Name: "chromium/src",
RefRegexp: []string{"refs/heads/branch_m200"},
},
},
},
},
})
ctx = cfgclient.Use(ctx, cfgmemory.New(map[config.Set]cfgmemory.Files{
config.ProjectSet(project): {ConfigFileName: prototext.Format(cfg)},
}))
So(UpdateProject(ctx, project, func(context.Context) error { return nil }), ShouldBeNil)
Convey("Updated project", func() {
m, err := GetLatestMeta(ctx, project)
So(err, ShouldBeNil)
So(m.Exists(), ShouldBeTrue)
So(m.Status, ShouldEqual, StatusEnabled)
So(m.EVersion, ShouldEqual, 2)
h := m.Hash()
So(h, ShouldStartWith, "sha256:")
So(m.ConfigGroupIDs, ShouldResemble, []ConfigGroupID{
ConfigGroupID(h + "/branch_m100"),
ConfigGroupID(h + "/catch_all"),
ConfigGroupID(h + "/branch_m200"),
})
cgs, err := m.GetConfigGroups(ctx)
So(err, ShouldBeNil)
So(len(cgs), ShouldEqual, 3)
Convey("reading ConfigGroup directly works", func() {
cg, err := GetConfigGroup(ctx, project, m.ConfigGroupIDs[2])
So(err, ShouldBeNil)
So(cg.Content, ShouldResembleProto, cfg.ConfigGroups[2])
})
})
So(DisableProject(ctx, project, func(context.Context) error { return nil }), ShouldBeNil)
Convey("Disabled project", func() {
m, err := GetLatestMeta(ctx, project)
So(err, ShouldBeNil)
So(m.Exists(), ShouldBeTrue)
So(m.Status, ShouldEqual, StatusDisabled)
So(m.EVersion, ShouldEqual, 3)
So(len(m.ConfigGroupIDs), ShouldEqual, 3)
cgs, err := m.GetConfigGroups(ctx)
So(err, ShouldBeNil)
So(len(cgs), ShouldEqual, 3)
})
// Re-enable the project.
So(UpdateProject(ctx, project, func(context.Context) error { return nil }), ShouldBeNil)
Convey("Re-enabled project", func() {
m, err := GetLatestMeta(ctx, project)
So(err, ShouldBeNil)
So(m.Exists(), ShouldBeTrue)
So(m.Status, ShouldEqual, StatusEnabled)
})
m, err := GetLatestMeta(ctx, project)
So(err, ShouldBeNil)
cgs, err := m.GetConfigGroups(ctx)
So(err, ShouldBeNil)
Convey("Deleted project", func() {
So(datastore.Delete(ctx, &ProjectConfig{Project: project}, cgs), ShouldBeNil)
m, err = GetLatestMeta(ctx, project)
So(err, ShouldBeNil)
So(m.Exists(), ShouldBeFalse)
})
Convey("reading partially deleted project", func() {
So(datastore.Delete(ctx, cgs[1]), ShouldBeNil)
_, err = m.GetConfigGroups(ctx)
So(err, ShouldErrLike, "ConfigGroups for")
So(err, ShouldErrLike, "not found")
So(datastore.IsErrNoSuchEntity(err), ShouldBeTrue)
// Can still read individual ConfigGroups.
cg, err := GetConfigGroup(ctx, project, m.ConfigGroupIDs[0])
So(err, ShouldBeNil)
So(cg.Content, ShouldResembleProto, cfg.ConfigGroups[0])
cg, err = GetConfigGroup(ctx, project, m.ConfigGroupIDs[2])
So(err, ShouldBeNil)
So(cg.Content, ShouldResembleProto, cfg.ConfigGroups[2])
// ... except the deleted one.
_, err = GetConfigGroup(ctx, project, m.ConfigGroupIDs[1])
So(datastore.IsErrNoSuchEntity(err), ShouldBeTrue)
})
})
}