go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/logdog/server/config/datastore_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 config 16 17 import ( 18 "context" 19 "testing" 20 21 "go.chromium.org/luci/config" 22 "go.chromium.org/luci/config/cfgclient" 23 cfgmem "go.chromium.org/luci/config/impl/memory" 24 "go.chromium.org/luci/gae/impl/memory" 25 "go.chromium.org/luci/gae/service/datastore" 26 27 "go.chromium.org/luci/logdog/api/config/svcconfig" 28 29 . "github.com/smartystreets/goconvey/convey" 30 . "go.chromium.org/luci/common/testing/assertions" 31 ) 32 33 func TestSync(t *testing.T) { 34 t.Parallel() 35 36 Convey("With initial configs", t, func() { 37 configs := map[config.Set]cfgmem.Files{ 38 "services/${appid}": { 39 "services.cfg": `coordinator { admin_auth_group: "a" }`, 40 }, 41 "projects/proj1": { 42 "${appid}.cfg": `archive_gs_bucket: "a"`, 43 }, 44 } 45 46 ctx := context.Background() 47 ctx = memory.Use(ctx) 48 ctx = cfgclient.Use(ctx, cfgmem.New(configs)) 49 50 sync := func(expectedErr string) { 51 if expectedErr == "" { 52 So(Sync(ctx), ShouldBeNil) 53 } else { 54 So(Sync(ctx), ShouldErrLike, expectedErr) 55 } 56 datastore.GetTestable(ctx).CatchupIndexes() 57 } 58 59 serviceCfg := func() (string, error) { 60 var cfg svcconfig.Config 61 err := fromDatastore(ctx, serviceConfigKind, serviceConfigPath, &cfg) 62 return cfg.Coordinator.AdminAuthGroup, err 63 } 64 65 projectCfg := func(proj string) (string, error) { 66 var cfg svcconfig.ProjectConfig 67 err := fromDatastore(ctx, projectConfigKind, proj, &cfg) 68 return cfg.ArchiveGsBucket, err 69 } 70 71 sync("") 72 73 svc, err := serviceCfg() 74 So(err, ShouldBeNil) 75 So(svc, ShouldEqual, "a") 76 77 prj, err := projectCfg("proj1") 78 So(err, ShouldBeNil) 79 So(prj, ShouldEqual, "a") 80 81 Convey("No changes", func() { 82 sync("") 83 84 svc, err := serviceCfg() 85 So(err, ShouldBeNil) 86 So(svc, ShouldEqual, "a") 87 88 prj, err := projectCfg("proj1") 89 So(err, ShouldBeNil) 90 So(prj, ShouldEqual, "a") 91 }) 92 93 Convey("Service config change", func() { 94 configs["services/${appid}"]["services.cfg"] = `coordinator { admin_auth_group: "b" }` 95 96 sync("") 97 98 svc, err := serviceCfg() 99 So(err, ShouldBeNil) 100 So(svc, ShouldEqual, "b") 101 }) 102 103 Convey("Broken service config", func() { 104 configs["services/${appid}"]["services.cfg"] = `wat` 105 106 sync("bad service config") 107 108 svc, err := serviceCfg() 109 So(err, ShouldBeNil) 110 So(svc, ShouldEqual, "a") // unchanged 111 }) 112 113 Convey("Project config change", func() { 114 configs["projects/proj1"]["${appid}.cfg"] = `archive_gs_bucket: "b"` 115 116 sync("") 117 118 prj, err := projectCfg("proj1") 119 So(err, ShouldBeNil) 120 So(prj, ShouldEqual, "b") 121 }) 122 123 Convey("Broken project config", func() { 124 configs["projects/proj1"]["${appid}.cfg"] = `wat` 125 126 sync("bad project config") 127 128 prj, err := projectCfg("proj1") 129 So(err, ShouldBeNil) 130 So(prj, ShouldEqual, "a") // unchanged 131 }) 132 133 Convey("New project", func() { 134 configs["projects/proj2"] = cfgmem.Files{ 135 "${appid}.cfg": `archive_gs_bucket: "new"`, 136 } 137 138 sync("") 139 140 prj1, err := projectCfg("proj1") 141 So(err, ShouldBeNil) 142 So(prj1, ShouldEqual, "a") // still there 143 144 prj2, err := projectCfg("proj2") 145 So(err, ShouldBeNil) 146 So(prj2, ShouldEqual, "new") 147 }) 148 149 Convey("Removed project", func() { 150 delete(configs, "projects/proj1") 151 152 sync("") 153 154 _, err = projectCfg("proj1") 155 So(err, ShouldEqual, datastore.ErrNoSuchEntity) 156 }) 157 158 Convey("Broken project doesn't block updated", func() { 159 configs["projects/proj1"]["${appid}.cfg"] = `wat` 160 configs["projects/proj2"] = cfgmem.Files{ 161 "${appid}.cfg": `archive_gs_bucket: "new"`, 162 } 163 164 sync("bad project config") 165 166 prj1, err := projectCfg("proj1") 167 So(err, ShouldBeNil) 168 So(prj1, ShouldEqual, "a") // unchanged 169 170 prj2, err := projectCfg("proj2") 171 So(err, ShouldBeNil) 172 So(prj2, ShouldEqual, "new") 173 174 delete(configs, "projects/proj2") 175 176 sync("bad project config") 177 178 prj1, err = projectCfg("proj1") 179 So(err, ShouldBeNil) 180 So(prj1, ShouldEqual, "a") // still unchanged 181 182 _, err = projectCfg("proj2") 183 So(err, ShouldEqual, datastore.ErrNoSuchEntity) 184 }) 185 }) 186 }