go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/swarming/server/cfg/settings_test.go (about) 1 // Copyright 2023 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 cfg 16 17 import ( 18 "context" 19 "testing" 20 21 "go.chromium.org/luci/common/errors" 22 "go.chromium.org/luci/config/validation" 23 24 configpb "go.chromium.org/luci/swarming/proto/config" 25 26 . "github.com/smartystreets/goconvey/convey" 27 ) 28 29 func TestSettingsValidation(t *testing.T) { 30 t.Parallel() 31 32 call := func(cfg *configpb.SettingsCfg) []string { 33 ctx := validation.Context{Context: context.Background()} 34 ctx.SetFile("settings.cfg") 35 validateSettingsCfg(&ctx, cfg) 36 if err := ctx.Finalize(); err != nil { 37 var verr *validation.Error 38 errors.As(err, &verr) 39 out := make([]string, len(verr.Errors)) 40 for i, err := range verr.Errors { 41 out[i] = err.Error() 42 } 43 return out 44 } 45 return nil 46 } 47 48 Convey("Empty", t, func() { 49 So(call(&configpb.SettingsCfg{}), ShouldBeNil) 50 }) 51 52 Convey("Good", t, func() { 53 So(call(withDefaultSettings(&configpb.SettingsCfg{ 54 DisplayServerUrlTemplate: "https://something.example.com/swarming/task/%s", 55 ExtraChildSrcCspUrl: []string{"https://something.example.com/raw/build/"}, 56 Cipd: &configpb.CipdSettings{ 57 DefaultServer: "https://something.example.com", 58 DefaultClientPackage: &configpb.CipdPackage{ 59 PackageName: "some/pkg/${platform}", 60 Version: "some:version", 61 }, 62 }, 63 })), ShouldBeNil) 64 }) 65 66 Convey("Errors", t, func() { 67 testCases := []struct { 68 cfg *configpb.SettingsCfg 69 err string 70 }{ 71 { 72 cfg: &configpb.SettingsCfg{BotDeathTimeoutSecs: -100}, 73 err: "(bot_death_timeout_secs): must be non-negative, got -100", 74 }, 75 { 76 cfg: &configpb.SettingsCfg{ReusableTaskAgeSecs: -100}, 77 err: "(reusable_task_age_secs): must be non-negative, got -100", 78 }, 79 { 80 cfg: &configpb.SettingsCfg{DisplayServerUrlTemplate: "example.com"}, 81 err: "(display_server_url_template): must have exactly one `%s` term", 82 }, 83 { 84 cfg: &configpb.SettingsCfg{DisplayServerUrlTemplate: "example.com/%s/%s"}, 85 err: "(display_server_url_template): must have exactly one `%s` term", 86 }, 87 { 88 cfg: &configpb.SettingsCfg{DisplayServerUrlTemplate: "example.com/%s"}, 89 err: "(display_server_url_template): must be an https:// URL", 90 }, 91 { 92 cfg: &configpb.SettingsCfg{DisplayServerUrlTemplate: "https://example.com/%s/%%"}, 93 err: `(display_server_url_template): parse "https://example.com/.../%": invalid URL escape "%"`, 94 }, 95 { 96 cfg: &configpb.SettingsCfg{ExtraChildSrcCspUrl: []string{"example.com"}}, 97 err: "(extra_child_src_csp_url): must be an https:// URL", 98 }, 99 { 100 cfg: &configpb.SettingsCfg{ 101 Cipd: &configpb.CipdSettings{ 102 DefaultClientPackage: &configpb.CipdPackage{ 103 PackageName: "some/pkg/${platform}", 104 Version: "some:version", 105 }, 106 }, 107 }, 108 err: "(cipd / default_server): this is a required field", 109 }, 110 { 111 cfg: &configpb.SettingsCfg{ 112 Cipd: &configpb.CipdSettings{ 113 DefaultServer: "https://something.example.com", 114 DefaultClientPackage: &configpb.CipdPackage{ 115 PackageName: "some/pkg/${zzz}", 116 Version: "some:version", 117 }, 118 }, 119 }, 120 err: `(cipd / default_client_package / package_name): bad package name template "some/pkg/${zzz}": unknown variable "${zzz}"`, 121 }, 122 { 123 cfg: &configpb.SettingsCfg{ 124 Cipd: &configpb.CipdSettings{ 125 DefaultServer: "https://something.example.com", 126 DefaultClientPackage: &configpb.CipdPackage{ 127 PackageName: "some/pkg//pkg", 128 Version: "some:version", 129 }, 130 }, 131 }, 132 err: `(cipd / default_client_package / package_name): invalid package name "some/pkg//pkg": must be a slash-separated path where each component matches "[a-z0-9_\-\.]+"`, 133 }, 134 { 135 cfg: &configpb.SettingsCfg{ 136 Cipd: &configpb.CipdSettings{ 137 DefaultServer: "https://something.example.com", 138 DefaultClientPackage: &configpb.CipdPackage{ 139 PackageName: "some/pkg/${platform}", 140 Version: "???", 141 }, 142 }, 143 }, 144 err: `(cipd / default_client_package / version): bad version "???": not an instance ID, a ref or a tag`, 145 }, 146 { 147 cfg: &configpb.SettingsCfg{ 148 Resultdb: &configpb.ResultDBSettings{ 149 Server: "example.com", 150 }, 151 }, 152 err: "(resultdb / server): must be an https:// URL", 153 }, 154 { 155 cfg: &configpb.SettingsCfg{ 156 Cas: &configpb.CASSettings{ 157 ViewerServer: "example.com", 158 }, 159 }, 160 err: "(cas / viewer_server): must be an https:// URL", 161 }, 162 } 163 for _, cs := range testCases { 164 So(call(cs.cfg), ShouldResemble, []string{`in "settings.cfg" ` + cs.err}) 165 } 166 }) 167 }