go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/config/validation/rules_test.go (about) 1 // Copyright 2018 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 validation 16 17 import ( 18 "context" 19 "fmt" 20 "testing" 21 22 "go.chromium.org/luci/common/errors" 23 24 . "github.com/smartystreets/goconvey/convey" 25 . "go.chromium.org/luci/common/testing/assertions" 26 ) 27 28 func TestRuleSet(t *testing.T) { 29 t.Parallel() 30 31 Convey("With validator callback", t, func() { 32 ctx := context.Background() 33 34 type call struct { 35 validatorID string 36 configSet string 37 path string 38 } 39 calls := []call{} 40 41 validator := func(id string) Func { 42 return func(ctx *Context, configSet, path string, content []byte) error { 43 calls = append(calls, call{id, configSet, path}) 44 return nil 45 } 46 } 47 48 Convey("Works", func() { 49 r := NewRuleSet() 50 51 r.Vars.Register("a", func(context.Context) (string, error) { return "a_val", nil }) 52 r.Vars.Register("b", func(context.Context) (string, error) { return "b_val", nil }) 53 54 r.Add("services/${a}", "paths/a", validator("rule_1")) 55 r.Add("services/${a}", "paths/${b}", validator("rule_2")) 56 r.Add("services/c", "paths/c", validator("rule_3")) 57 r.Add("regex:services/.*", "paths/c", validator("rule_4")) 58 59 patterns, err := r.ConfigPatterns(ctx) 60 So(err, ShouldBeNil) 61 62 type pair struct { 63 configSet string 64 path string 65 } 66 asPairs := make([]pair, len(patterns)) 67 for i, p := range patterns { 68 asPairs[i] = pair{p.ConfigSet.String(), p.Path.String()} 69 } 70 So(asPairs, ShouldResemble, []pair{ 71 {"exact:services/a_val", "exact:paths/a"}, 72 {"exact:services/a_val", "exact:paths/b_val"}, 73 {"exact:services/c", "exact:paths/c"}, 74 {"regex:^services/.*$", "exact:paths/c"}, 75 }) 76 77 callValidator := func(configSet, path string) { 78 c := Context{Context: ctx} 79 So(r.ValidateConfig(&c, configSet, path, []byte("body")), ShouldBeNil) 80 } 81 82 callValidator("services/unknown", "paths/a") 83 callValidator("services/a_val", "paths/a") 84 callValidator("services/a_val", "paths/b_val") 85 callValidator("services/a_val", "paths/c") 86 callValidator("services/c", "paths/c") 87 88 So(calls, ShouldResemble, []call{ 89 {"rule_1", "services/a_val", "paths/a"}, 90 {"rule_2", "services/a_val", "paths/b_val"}, 91 {"rule_4", "services/a_val", "paths/c"}, 92 {"rule_3", "services/c", "paths/c"}, 93 {"rule_4", "services/c", "paths/c"}, 94 }) 95 }) 96 97 Convey("Error in the var callback", func() { 98 r := NewRuleSet() 99 r.Vars.Register("a", func(context.Context) (string, error) { return "", fmt.Errorf("boom") }) 100 r.Add("services/${a}", "paths/a", validator("rule_1")) 101 err := r.ValidateConfig(&Context{Context: ctx}, "services/zzz", "some path", []byte("body")) 102 So(err, ShouldErrLike, "boom") 103 }) 104 105 Convey("Missing variables", func() { 106 r := NewRuleSet() 107 r.Vars.Register("a", func(context.Context) (string, error) { return "a_val", nil }) 108 r.Add("${zzz}", "a", validator("1")) 109 r.Add("a", "${zzz}", validator("1")) 110 111 _, err := r.ConfigPatterns(ctx) 112 merr := err.(errors.MultiError) 113 So(merr, ShouldHaveLength, 2) 114 So(merr[0], ShouldErrLike, 115 `config set pattern "exact:${zzz}": no placeholder named "zzz" is registered`) 116 So(merr[1], ShouldErrLike, 117 `path pattern "exact:${zzz}": no placeholder named "zzz" is registered`) 118 119 err = r.ValidateConfig(&Context{Context: ctx}, "set", "path", nil) 120 merr = err.(errors.MultiError) 121 So(merr, ShouldHaveLength, 2) 122 So(merr[0], ShouldErrLike, 123 `config set pattern "exact:${zzz}": no placeholder named "zzz" is registered`) 124 So(merr[1], ShouldErrLike, 125 `path pattern "exact:${zzz}": no placeholder named "zzz" is registered`) 126 }) 127 128 Convey("Pattern is validated", func() { 129 r := NewRuleSet() 130 r.Vars.Register("a", func(context.Context) (string, error) { return "a_val", nil }) 131 r.Add("unknown:${a}", "a", validator("1")) 132 r.Add("a", "unknown:${a}", validator("1")) 133 134 _, err := r.ConfigPatterns(ctx) 135 merr := err.(errors.MultiError) 136 So(merr, ShouldHaveLength, 2) 137 So(merr[0], ShouldErrLike, 138 `config set pattern "unknown:${a}": unknown pattern kind: "unknown"`) 139 So(merr[1], ShouldErrLike, 140 `path pattern "unknown:${a}": unknown pattern kind: "unknown"`) 141 142 err = r.ValidateConfig(&Context{Context: ctx}, "set", "path", nil) 143 merr = err.(errors.MultiError) 144 So(merr, ShouldHaveLength, 2) 145 So(merr[0], ShouldErrLike, 146 `config set pattern "unknown:${a}": unknown pattern kind: "unknown"`) 147 So(merr[1], ShouldErrLike, 148 `path pattern "unknown:${a}": unknown pattern kind: "unknown"`) 149 }) 150 }) 151 }